[PATCH] mtd: spi-nor: Parse SFDP Command Sequence to change to Octal DDR(8D-8D-8D) mode
JaimeLiao
jaimeliao.tw at gmail.com
Thu Mar 10 07:59:09 CET 2022
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.tw at 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
More information about the U-Boot
mailing list