
Double Transfer Rate (DTR) is SPI protocol in which data is transferred on each clock edge as opposed to on each clock cycle. Make framework-level changes to allow supporting flashes in DTR mode.
Right now, mixed DTR modes are not supported. So, for example a mode like 4S-4D-4D will not work. All phases need to be either DTR or STR.
Signed-off-by: Pratyush Yadav p.yadav@ti.com --- drivers/mtd/spi/sf_internal.h | 1 + drivers/mtd/spi/spi-nor-core.c | 90 ++++++++++++++++++++++++++++------ include/linux/mtd/spi-nor.h | 32 +++++++----- 3 files changed, 95 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index d689b673ce..5780c81287 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -67,6 +67,7 @@ struct flash_info { #define USE_CLSR BIT(14) /* use CLSR command */ #define SPI_NOR_HAS_SST26LOCK BIT(15) /* Flash supports lock/unlock via BPR */ #define SPI_NOR_OCTAL_READ BIT(16) /* Flash supports Octal Read */ +#define SPI_NOR_OCTAL_DTR_READ BIT(17) /* Flash supports Octal DTR Read */
/* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 94e99eb99f..2915d0b721 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -176,6 +176,45 @@ struct spi_nor_fixups {
#endif /* SPI_FLASH_SFDP_SUPPORT */
+/** + * spi_nor_setup_op() - Set up common properties of a spi-mem op. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * @proto: the protocol from which the properties need to be set. + */ +static void spi_nor_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto) +{ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto); + + if (op->addr.nbytes) + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->dummy.nbytes) + op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->data.buf.in || op->data.buf.out) + op->data.buswidth = spi_nor_get_protocol_data_nbits(proto); + + if (spi_nor_protocol_is_dtr(proto)) { + /* + * spi-mem supports mixed DTR modes, but right now we can only + * have all phases either DTR or STR. IOW, spi-mem can have + * something like 4S-4D-4D, but spi-nor can't. So, set all 4 + * phases to either DTR or STR. + */ + op->cmd.is_dtr = op->addr.is_dtr = op->dummy.is_dtr = + op->data.is_dtr = true; + + /* 2 bytes per clock cycle in DTR mode. */ + op->dummy.nbytes *= 2; + + op->cmd.ext_type = nor->cmd_ext; + } +} + static int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op *op, void *buf) { @@ -194,6 +233,8 @@ static int spi_nor_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) SPI_MEM_OP_DATA_IN(len, NULL, 1)); int ret;
+ spi_nor_setup_op(nor, &op, nor->reg_proto); + ret = spi_nor_read_write_reg(nor, &op, val); if (ret < 0) dev_dbg(&flash->spimem->spi->dev, "error %d reading %x\n", ret, @@ -209,6 +250,8 @@ static int spi_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, NULL, 1));
+ spi_nor_setup_op(nor, &op, nor->reg_proto); + return spi_nor_read_write_reg(nor, &op, buf); }
@@ -223,14 +266,12 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, size_t remaining = len; int ret;
- /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op.dummy.buswidth = op.addr.buswidth; - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + spi_nor_setup_op(nor, &op, nor->read_proto);
/* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op.dummy.nbytes *= 2;
while (remaining) { op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; @@ -260,14 +301,11 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, SPI_MEM_OP_DATA_OUT(len, buf, 1)); int ret;
- /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0;
+ spi_nor_setup_op(nor, &op, nor->write_proto); + ret = spi_mem_adjust_op_size(nor->spi, &op); if (ret) return ret; @@ -669,6 +707,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA);
+ spi_nor_setup_op(nor, &op, nor->write_proto); + if (nor->erase) return nor->erase(nor, addr);
@@ -2186,6 +2226,13 @@ static int spi_nor_init_params(struct spi_nor *nor, spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+ /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR); + if (info->flags & SPI_NOR_QUAD_READ) { params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], @@ -2219,7 +2266,8 @@ static int spi_nor_init_params(struct spi_nor *nor, /* Override the parameters with data read from SFDP tables. */ nor->addr_width = 0; nor->mtd.erasesize = 0; - if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && + if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_OCTAL_DTR_READ)) && !(info->flags & SPI_NOR_SKIP_SFDP)) { struct spi_nor_flash_parameter sfdp_params;
@@ -2266,6 +2314,7 @@ static int spi_nor_hwcaps_read2cmd(u32 hwcaps) { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, + { SNOR_HWCAPS_READ_8_8_8_DTR, SNOR_CMD_READ_8_8_8_DTR }, };
return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, @@ -2282,6 +2331,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, + { SNOR_HWCAPS_PP_8_8_8_DTR, SNOR_CMD_PP_8_8_8_DTR }, };
return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, @@ -2381,12 +2431,10 @@ static int spi_nor_default_setup(struct spi_nor *nor, */ shared_mask = hwcaps->mask & params->hwcaps.mask;
- /* SPI n-n-n protocols are not supported yet. */ + /* Some SPI n-n-n protocols are not supported yet. */ ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | SNOR_HWCAPS_READ_4_4_4 | - SNOR_HWCAPS_READ_8_8_8 | - SNOR_HWCAPS_PP_4_4_4 | - SNOR_HWCAPS_PP_8_8_8); + SNOR_HWCAPS_PP_4_4_4); if (shared_mask & ignored_mask) { dev_dbg(nor->dev, "SPI n-n-n protocols are not supported yet.\n"); @@ -2464,6 +2512,7 @@ static int spi_nor_init(struct spi_nor *nor) }
if (nor->addr_width == 4 && + !(nor->info->flags & SPI_NOR_OCTAL_DTR_READ) && (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && !(nor->info->flags & SPI_NOR_4B_OPCODES)) { /* @@ -2508,10 +2557,16 @@ int spi_nor_scan(struct spi_nor *nor) if (spi->mode & SPI_RX_OCTAL) { hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
- if (spi->mode & SPI_TX_OCTAL) + if (spi->mode & SPI_TX_OCTAL) { hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 | SNOR_HWCAPS_PP_1_1_8 | SNOR_HWCAPS_PP_1_8_8); + + if ((spi->mode & SPI_TX_DTR) && + (spi->mode & SPI_RX_DTR)) + hwcaps.mask |= (SNOR_HWCAPS_READ_8_8_8_DTR | + SNOR_HWCAPS_PP_8_8_8_DTR); + } } else if (spi->mode & SPI_RX_QUAD) { hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
@@ -2607,6 +2662,9 @@ int spi_nor_scan(struct spi_nor *nor)
if (nor->addr_width) { /* already configured from SFDP */ + } else if (spi_nor_protocol_is_dtr(nor->read_proto)) { + /* Always use 4-byte addresses in DTR mode. */ + nor->addr_width = 4; } else if (info->addr_width) { nor->addr_width = info->addr_width; } else if (mtd->size > SZ_16M) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index eb5d2794f3..9a41e5b25b 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> +#include <spi-mem.h>
/* * Manufacturer IDs @@ -199,6 +200,7 @@ enum spi_nor_protocol { SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2), SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4), SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8), + SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_DTR(8, 8, 8), };
static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) @@ -266,7 +268,7 @@ struct spi_nor_hwcaps { * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly * (Slow) Read. */ -#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0) +#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0) #define SNOR_HWCAPS_READ BIT(0) #define SNOR_HWCAPS_READ_FAST BIT(1) #define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2) @@ -283,11 +285,12 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_READ_4_4_4 BIT(9) #define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10)
-#define SNOR_HWCPAS_READ_OCTO GENMASK(14, 11) +#define SNOR_HWCPAS_READ_OCTO GENMASK(15, 11) #define SNOR_HWCAPS_READ_1_1_8 BIT(11) #define SNOR_HWCAPS_READ_1_8_8 BIT(12) #define SNOR_HWCAPS_READ_8_8_8 BIT(13) #define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14) +#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15)
/* * Page Program capabilities. @@ -298,18 +301,19 @@ struct spi_nor_hwcaps { * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory * implements such commands. */ -#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16) -#define SNOR_HWCAPS_PP BIT(16) +#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16) +#define SNOR_HWCAPS_PP BIT(16)
-#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) -#define SNOR_HWCAPS_PP_1_1_4 BIT(17) -#define SNOR_HWCAPS_PP_1_4_4 BIT(18) -#define SNOR_HWCAPS_PP_4_4_4 BIT(19) +#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) +#define SNOR_HWCAPS_PP_1_1_4 BIT(17) +#define SNOR_HWCAPS_PP_1_4_4 BIT(18) +#define SNOR_HWCAPS_PP_4_4_4 BIT(19)
-#define SNOR_HWCAPS_PP_OCTO GENMASK(22, 20) -#define SNOR_HWCAPS_PP_1_1_8 BIT(20) -#define SNOR_HWCAPS_PP_1_8_8 BIT(21) -#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_OCTO GENMASK(23, 20) +#define SNOR_HWCAPS_PP_1_1_8 BIT(20) +#define SNOR_HWCAPS_PP_1_8_8 BIT(21) +#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23)
struct spi_nor_read_command { u8 num_mode_clocks; @@ -345,6 +349,7 @@ enum spi_nor_read_command_index { SNOR_CMD_READ_1_8_8, SNOR_CMD_READ_8_8_8, SNOR_CMD_READ_1_8_8_DTR, + SNOR_CMD_READ_8_8_8_DTR,
SNOR_CMD_READ_MAX }; @@ -361,6 +366,7 @@ enum spi_nor_pp_command_index { SNOR_CMD_PP_1_1_8, SNOR_CMD_PP_1_8_8, SNOR_CMD_PP_8_8_8, + SNOR_CMD_PP_8_8_8_DTR,
SNOR_CMD_PP_MAX }; @@ -413,6 +419,7 @@ struct flash_info; * @write_proto: the SPI protocol for write operations * @reg_proto the SPI protocol for read_reg/write_reg/erase operations * @cmd_buf: used by the write_reg + * @cmd_ext: the command opcode extension for DTR mode. * @prepare: [OPTIONAL] do some preparations for the * read/write/erase/lock/unlock operations * @unprepare: [OPTIONAL] do some post work after the @@ -454,6 +461,7 @@ struct spi_nor { bool sst_write_second; u32 flags; u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + enum spi_mem_cmd_ext cmd_ext;
int (*setup)(struct spi_nor *nor, const struct flash_info *info, const struct spi_nor_flash_parameter *params,