Parcing table of SFDP Command Sequence to change to Octal
DDR(8D-8D-8D) mode(CSODDR) and following SPI protocol to
judge opcode, address, dummy and data.
Enabling Octal DDR mode when nor->octal_dtr_enable didn't
hook.

Signed-off-by: JaimeLiao <jaimeliao...@gmail.com>
---
 drivers/mtd/spi/spi-nor-core.c | 114 ++++++++++++++++++++++++++++++++-
 include/linux/mtd/spi-nor.h    |   8 ++-
 2 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index d5d905fa5a..058dd8e889 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -63,6 +63,9 @@ struct sfdp_parameter_header {
 #define SFDP_SECTOR_MAP_ID     0xff81  /* Sector Map Table */
 #define SFDP_SST_ID            0x01bf  /* Manufacturer specific Table */
 #define SFDP_PROFILE1_ID       0xff05  /* xSPI Profile 1.0 Table */
+#define SFDP_CSODDR_ID         0xff0a  /* Command Sequences to Change to Octal
+                                        * DDR (8D-8D-8D) mode
+                                        */
 
 #define SFDP_SIGNATURE         0x50444653U
 #define SFDP_JESD216_MAJOR     1
@@ -172,6 +175,11 @@ struct sfdp_header {
 #define PROFILE1_DWORD5_DUMMY_100MHZ           GENMASK(11, 7)
 #define PROFILE1_DUMMY_DEFAULT                 20
 
+/* Command Sequences to Change to Octal DDR (8D-8D-8D) mode */
+#define CSODDR_LENGTH_OF_CMD           GENMASK(31, 24)
+#define CSODDR_DWORD1_OF_CMD           GENMASK(31, 0)
+#define CSODDR_DWORD2_OF_CMD           GENMASK(31, 0)
+
 struct sfdp_bfpt {
        u32     dwords[BFPT_DWORD_MAX];
 };
@@ -2434,6 +2442,49 @@ out:
        return ret;
 }
 
+/**
+ * spi_nor_parse_csoddr() - Parse Command Sequences to Change to Octal DDR 
(8D-8D-8D)
+ *                       mode.
+ * @nor:                 pointer to a 'struct spi_nor'
+ * @csoddr_header:       pointer to the 'struct sfdp_parameter_header' 
describing
+ *                       the CSODDR table length and version.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_csoddr(struct spi_nor *nor,
+                             const struct sfdp_parameter_header *csoddr_header)
+{
+       u32 *table, addr;
+       size_t len;
+       int ret, i;
+
+       len = csoddr_header->length * sizeof(*table);
+       table = kmalloc(len, GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       addr = SFDP_PARAM_HEADER_PTP(csoddr_header);
+       ret = spi_nor_read_sfdp(nor, addr, len, table);
+       if (ret)
+               goto out;
+
+       /* Fix endianness of the table DWORDs. */
+       for (i = 0; i < csoddr_header->length; i++)
+               table[i] = le32_to_cpu(table[i]);
+
+       /* Each command sequence consists of 2DWORDs(8 bytes) */
+       for(i = 0; i < csoddr_header->length/2; i++) {
+               if(FIELD_GET(CSODDR_LENGTH_OF_CMD, table[2*i])) {
+                       memcpy(&nor->csoddr_cmd[i].cmd_byte[0], &table[2*i], 4);
+                       memcpy(&nor->csoddr_cmd[i].cmd_byte[4], &table[2*i+1], 
4);
+               }
+       }
+
+out:
+       kfree(table);
+       return ret;
+}
+
 /**
  * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
  * @nor:               pointer to a 'struct spi_nor'
@@ -2540,6 +2591,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
                        err = spi_nor_parse_profile1(nor, param_header, params);
                        break;
 
+               case SFDP_CSODDR_ID:
+                       err = spi_nor_parse_csoddr(nor, param_header);
+                       break;
+
                default:
                        break;
                }
@@ -3489,6 +3544,63 @@ static struct spi_nor_fixups mt35xu512aba_fixups = {
 };
 #endif /* CONFIG_SPI_FLASH_MT35XU */
 
+/**
+ * spi_nor_generall_octal_dtr_enable() - execute command sequence to enable 
octal dtr mode.
+ * @nor:       pointer to a 'struct spi_nor'
+ *
+ * Command Sequence to change to Octal DTR mode is parcing from SFDP table.
+ * Executing commands to enable Octal DTR mode.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_generall_octal_dtr_enable(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       int ret, i;
+       u8 opcode, buf, word_buf[2];
+       u32 addr;
+
+       /* According SPI protocol for judging combination of commands */
+       for(i = 0; i < MAX_COMMAND_SEQUENCE_DTR; i++) {
+               if(nor->csoddr_cmd[i].cmd_byte[3]) {
+                       if(nor->csoddr_cmd[i].cmd_byte[3] == 1) {
+                               opcode = nor->csoddr_cmd[i].cmd_byte[2];
+                               ret = nor->write_reg(nor, opcode, NULL, 0);
+                       } else if(nor->csoddr_cmd[i].cmd_byte[3] == 2 ) {
+                               opcode = nor->csoddr_cmd[i].cmd_byte[2];
+                               buf = nor->csoddr_cmd[i].cmd_byte[1];
+                               ret = nor->write_reg(nor, opcode, &buf, 1);
+                       } else if(nor->csoddr_cmd[i].cmd_byte[3] == 3 ) {
+                               opcode = nor->csoddr_cmd[i].cmd_byte[2];
+                               word_buf[0] = nor->csoddr_cmd[i].cmd_byte[1];
+                               word_buf[1] = nor->csoddr_cmd[i].cmd_byte[0];
+                               ret = nor->write_reg(nor, opcode, word_buf, 2);
+                       } else if(nor->csoddr_cmd[i].cmd_byte[3] > 5) {
+                               opcode = nor->csoddr_cmd[i].cmd_byte[2];
+                               addr = nor->csoddr_cmd[i].cmd_byte[1] << 24 | 
nor->csoddr_cmd[i].cmd_byte[0] << 16 |
+                                      nor->csoddr_cmd[i].cmd_byte[7] << 8 | 
nor->csoddr_cmd[i].cmd_byte[7];
+                               buf = nor->csoddr_cmd[i].cmd_byte[5];
+                               op = (struct spi_mem_op)
+                                       SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+                                                  SPI_MEM_OP_ADDR(4, addr, 1),
+                                                  SPI_MEM_OP_NO_DUMMY,
+                                                  SPI_MEM_OP_DATA_OUT(1, &buf, 
1));
+
+                               ret = spi_mem_exec_op(nor->spi, &op);
+                       }
+
+                       if (ret) {
+                               dev_err(nor->dev, "Failed to enable octal DTR 
mode\n");
+                               return ret;
+                       }
+               }
+       }
+
+       nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
+
+       return 0;
+}
+
 /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
  * @nor:                 pointer to a 'struct spi_nor'
  *
@@ -3499,7 +3611,7 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor)
        int ret;
 
        if (!nor->octal_dtr_enable)
-               return 0;
+               nor->octal_dtr_enable = spi_nor_generall_octal_dtr_enable;
 
        if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR &&
              nor->write_proto == SNOR_PROTO_8_8_8_DTR))
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 7ddc4ba2bf..7c4b982bf3 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -261,7 +261,8 @@ static inline u8 spi_nor_get_protocol_width(enum 
spi_nor_protocol proto)
        return spi_nor_get_protocol_data_nbits(proto);
 }
 
-#define SPI_NOR_MAX_CMD_SIZE   8
+#define SPI_NOR_MAX_CMD_SIZE    8
+#define MAX_COMMAND_SEQUENCE_DTR 4
 enum spi_nor_ops {
        SPI_NOR_OPS_READ = 0,
        SPI_NOR_OPS_WRITE,
@@ -375,6 +376,10 @@ struct spi_nor_pp_command {
        enum spi_nor_protocol   proto;
 };
 
+struct csoddr {
+       u8                      cmd_byte[8];
+};
+
 enum spi_nor_read_command_index {
        SNOR_CMD_READ,
        SNOR_CMD_READ_FAST,
@@ -563,6 +568,7 @@ struct spi_nor {
        void *priv;
 /* Compatibility for spi_flash, remove once sf layer is merged with mtd */
        const char *name;
+       struct csoddr csoddr_cmd[MAX_COMMAND_SEQUENCE_DTR];
        u32 size;
        u32 sector_size;
        u32 erase_size;
-- 
2.17.1

Reply via email to