256 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/debugfs.h>4#include <linux/mtd/spi-nor.h>5#include <linux/spi/spi.h>6#include <linux/spi/spi-mem.h>7 8#include "core.h"9 10#define SPI_NOR_DEBUGFS_ROOT "spi-nor"11 12#define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name13static const char *const snor_f_names[] = {14 SNOR_F_NAME(HAS_SR_TB),15 SNOR_F_NAME(NO_OP_CHIP_ERASE),16 SNOR_F_NAME(BROKEN_RESET),17 SNOR_F_NAME(4B_OPCODES),18 SNOR_F_NAME(HAS_4BAIT),19 SNOR_F_NAME(HAS_LOCK),20 SNOR_F_NAME(HAS_16BIT_SR),21 SNOR_F_NAME(NO_READ_CR),22 SNOR_F_NAME(HAS_SR_TB_BIT6),23 SNOR_F_NAME(HAS_4BIT_BP),24 SNOR_F_NAME(HAS_SR_BP3_BIT6),25 SNOR_F_NAME(IO_MODE_EN_VOLATILE),26 SNOR_F_NAME(SOFT_RESET),27 SNOR_F_NAME(SWP_IS_VOLATILE),28 SNOR_F_NAME(RWW),29 SNOR_F_NAME(ECC),30 SNOR_F_NAME(NO_WP),31};32#undef SNOR_F_NAME33 34static const char *spi_nor_protocol_name(enum spi_nor_protocol proto)35{36 switch (proto) {37 case SNOR_PROTO_1_1_1: return "1S-1S-1S";38 case SNOR_PROTO_1_1_2: return "1S-1S-2S";39 case SNOR_PROTO_1_1_4: return "1S-1S-4S";40 case SNOR_PROTO_1_1_8: return "1S-1S-8S";41 case SNOR_PROTO_1_2_2: return "1S-2S-2S";42 case SNOR_PROTO_1_4_4: return "1S-4S-4S";43 case SNOR_PROTO_1_8_8: return "1S-8S-8S";44 case SNOR_PROTO_2_2_2: return "2S-2S-2S";45 case SNOR_PROTO_4_4_4: return "4S-4S-4S";46 case SNOR_PROTO_8_8_8: return "8S-8S-8S";47 case SNOR_PROTO_1_1_1_DTR: return "1D-1D-1D";48 case SNOR_PROTO_1_2_2_DTR: return "1D-2D-2D";49 case SNOR_PROTO_1_4_4_DTR: return "1D-4D-4D";50 case SNOR_PROTO_1_8_8_DTR: return "1D-8D-8D";51 case SNOR_PROTO_8_8_8_DTR: return "8D-8D-8D";52 }53 54 return "<unknown>";55}56 57static void spi_nor_print_flags(struct seq_file *s, unsigned long flags,58 const char *const *names, int names_len)59{60 bool sep = false;61 int i;62 63 for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) {64 if (!(flags & BIT(i)))65 continue;66 if (sep)67 seq_puts(s, " | ");68 sep = true;69 if (i < names_len && names[i])70 seq_puts(s, names[i]);71 else72 seq_printf(s, "1<<%d", i);73 }74}75 76static int spi_nor_params_show(struct seq_file *s, void *data)77{78 struct spi_nor *nor = s->private;79 struct spi_nor_flash_parameter *params = nor->params;80 struct spi_nor_erase_map *erase_map = ¶ms->erase_map;81 struct spi_nor_erase_region *region = erase_map->regions;82 const struct flash_info *info = nor->info;83 char buf[16], *str;84 unsigned int i;85 86 seq_printf(s, "name\t\t%s\n", info->name);87 seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);88 string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));89 seq_printf(s, "size\t\t%s\n", buf);90 seq_printf(s, "write size\t%u\n", params->writesize);91 seq_printf(s, "page size\t%u\n", params->page_size);92 seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes);93 94 seq_puts(s, "flags\t\t");95 spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names));96 seq_puts(s, "\n");97 98 seq_puts(s, "\nopcodes\n");99 seq_printf(s, " read\t\t0x%02x\n", nor->read_opcode);100 seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy);101 seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode);102 seq_printf(s, " program\t0x%02x\n", nor->program_opcode);103 104 switch (nor->cmd_ext_type) {105 case SPI_NOR_EXT_NONE:106 str = "none";107 break;108 case SPI_NOR_EXT_REPEAT:109 str = "repeat";110 break;111 case SPI_NOR_EXT_INVERT:112 str = "invert";113 break;114 default:115 str = "<unknown>";116 break;117 }118 seq_printf(s, " 8D extension\t%s\n", str);119 120 seq_puts(s, "\nprotocols\n");121 seq_printf(s, " read\t\t%s\n",122 spi_nor_protocol_name(nor->read_proto));123 seq_printf(s, " write\t\t%s\n",124 spi_nor_protocol_name(nor->write_proto));125 seq_printf(s, " register\t%s\n",126 spi_nor_protocol_name(nor->reg_proto));127 128 seq_puts(s, "\nerase commands\n");129 for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {130 struct spi_nor_erase_type *et = &erase_map->erase_type[i];131 132 if (et->size) {133 string_get_size(et->size, 1, STRING_UNITS_2, buf,134 sizeof(buf));135 seq_printf(s, " %02x (%s) [%d]\n", et->opcode, buf, i);136 }137 }138 139 if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {140 string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));141 seq_printf(s, " %02x (%s)\n", nor->params->die_erase_opcode, buf);142 }143 144 seq_puts(s, "\nsector map\n");145 seq_puts(s, " region (in hex) | erase mask | overlaid\n");146 seq_puts(s, " ------------------+------------+----------\n");147 for (i = 0; i < erase_map->n_regions; i++) {148 u64 start = region[i].offset;149 u64 end = start + region[i].size - 1;150 u8 erase_mask = region[i].erase_mask;151 152 seq_printf(s, " %08llx-%08llx | [%c%c%c%c] | %s\n",153 start, end,154 erase_mask & BIT(0) ? '0' : ' ',155 erase_mask & BIT(1) ? '1' : ' ',156 erase_mask & BIT(2) ? '2' : ' ',157 erase_mask & BIT(3) ? '3' : ' ',158 region[i].overlaid ? "yes" : "no");159 }160 161 return 0;162}163DEFINE_SHOW_ATTRIBUTE(spi_nor_params);164 165static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap,166 struct spi_nor_read_command *cmd)167{168 seq_printf(s, " %s%s\n", spi_nor_protocol_name(cmd->proto),169 cap == SNOR_HWCAPS_READ_FAST ? " (fast read)" : "");170 seq_printf(s, " opcode\t0x%02x\n", cmd->opcode);171 seq_printf(s, " mode cycles\t%u\n", cmd->num_mode_clocks);172 seq_printf(s, " dummy cycles\t%u\n", cmd->num_wait_states);173}174 175static void spi_nor_print_pp_cmd(struct seq_file *s,176 struct spi_nor_pp_command *cmd)177{178 seq_printf(s, " %s\n", spi_nor_protocol_name(cmd->proto));179 seq_printf(s, " opcode\t0x%02x\n", cmd->opcode);180}181 182static int spi_nor_capabilities_show(struct seq_file *s, void *data)183{184 struct spi_nor *nor = s->private;185 struct spi_nor_flash_parameter *params = nor->params;186 u32 hwcaps = params->hwcaps.mask;187 int i, cmd;188 189 seq_puts(s, "Supported read modes by the flash\n");190 for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {191 if (!(hwcaps & BIT(i)))192 continue;193 194 cmd = spi_nor_hwcaps_read2cmd(BIT(i));195 if (cmd < 0)196 continue;197 198 spi_nor_print_read_cmd(s, BIT(i), ¶ms->reads[cmd]);199 hwcaps &= ~BIT(i);200 }201 202 seq_puts(s, "\nSupported page program modes by the flash\n");203 for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {204 if (!(hwcaps & BIT(i)))205 continue;206 207 cmd = spi_nor_hwcaps_pp2cmd(BIT(i));208 if (cmd < 0)209 continue;210 211 spi_nor_print_pp_cmd(s, ¶ms->page_programs[cmd]);212 hwcaps &= ~BIT(i);213 }214 215 if (hwcaps)216 seq_printf(s, "\nunknown hwcaps 0x%x\n", hwcaps);217 218 return 0;219}220DEFINE_SHOW_ATTRIBUTE(spi_nor_capabilities);221 222static void spi_nor_debugfs_unregister(void *data)223{224 struct spi_nor *nor = data;225 226 debugfs_remove(nor->debugfs_root);227 nor->debugfs_root = NULL;228}229 230static struct dentry *rootdir;231 232void spi_nor_debugfs_register(struct spi_nor *nor)233{234 struct dentry *d;235 int ret;236 237 if (!rootdir)238 rootdir = debugfs_create_dir(SPI_NOR_DEBUGFS_ROOT, NULL);239 240 ret = devm_add_action(nor->dev, spi_nor_debugfs_unregister, nor);241 if (ret)242 return;243 244 d = debugfs_create_dir(dev_name(nor->dev), rootdir);245 nor->debugfs_root = d;246 247 debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops);248 debugfs_create_file("capabilities", 0444, d, nor,249 &spi_nor_capabilities_fops);250}251 252void spi_nor_debugfs_shutdown(void)253{254 debugfs_remove(rootdir);255}256