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.ya...@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(&params->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(&params->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(&params->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,
-- 
2.25.0

Reply via email to