[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