lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 21 Apr 2020 14:39:45 +0800
From:   Mason Yang <masonccyang@...c.com.tw>
To:     broonie@...nel.org, tudor.ambarus@...rochip.com,
        miquel.raynal@...tlin.com, richard@....at, vigneshr@...com,
        boris.brezillon@...labora.com
Cc:     juliensu@...c.com.tw, linux-kernel@...r.kernel.org,
        linux-mtd@...ts.infradead.org, linux-spi@...r.kernel.org,
        Mason Yang <masonccyang@...c.com.tw>
Subject: [PATCH v2 3/5] mtd: spi-nor: Parse BFPT DWORD-18,19 and 20 for Octal 8D-8D-8D mode

Based on JESD216C BFPT(Basic Flash Parameter Table) to add:
18th DWORD:
Octal DTR(8D-8D-8D) command and command extension (00b: same, 01b: inverse)
- Get extension command type

19th DWORD:
Octal mode enable sequences by two instructions or write CFG Reg2
- Get enable Octal mode sequences
- implemented Read/write CFG Reg 2 function
- setup the call-back function for octal mode enable

20th DWORD:
Maximum operation speed of device in Octal mode
- Get the Octal maximum operation speed

Also defined the relevant macros and enum to add such modes and make sure
op->xxx.dtr fields, command nbytes and extension command are properly
filled and unmask DTR and X-X-X modes in spi_nor_spimem_adjust_hwcaps()
so that DTR and X-X-X support detection is done through
spi_mem_supports_op().

Signed-off-by: Mason Yang <masonccyang@...c.com.tw>
---
 drivers/mtd/spi-nor/core.c  | 220 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/mtd/spi-nor/core.h  |  17 ++++
 drivers/mtd/spi-nor/sfdp.c  | 116 +++++++++++++++++++++++
 drivers/mtd/spi-nor/sfdp.h  |  16 +++-
 include/linux/mtd/spi-nor.h |  51 +++++++++-
 5 files changed, 404 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index cc68ea8..b67c65d 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -40,6 +40,47 @@
 
 #define SPI_NOR_MAX_ADDR_WIDTH	4
 
+#define SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH()	\
+	{						\
+		op.cmd.buswidth = 8;			\
+		op.addr.buswidth = 8;			\
+		op.dummy.buswidth = 8;			\
+		op.data.buswidth = 8;			\
+		op.cmd.nbytes = 2;			\
+	}
+
+#define SET_SPIMEM_OP_FULL_OCTAL_WRITE_BUSWIDTH()	\
+	{						\
+		op.cmd.buswidth = 8;			\
+		op.addr.buswidth = 8;			\
+		op.data.buswidth = 8;			\
+		op.cmd.nbytes = 2;			\
+	}
+
+#define SET_SPIMEM_OP_DTR_READ()			\
+	{						\
+		op.dummy.nbytes *= 2;			\
+		op.cmd.dtr = true;			\
+		op.addr.dtr = true;			\
+		op.dummy.dtr = true;			\
+		op.data.dtr = true;			\
+	}
+
+#define SET_SPIMEM_OP_DTR_WRITE()			\
+	{						\
+		op.cmd.dtr = true;			\
+		op.addr.dtr = true;			\
+		op.data.dtr = true;			\
+	}
+
+#define SET_OCTAL_EXTION_OPCODE()				\
+	{							\
+		if (nor->ext_cmd_mode == EXT_CMD_IS_INVERSE)	\
+			op.cmd.ext_opcode = ~op.cmd.opcode;	\
+		else						\
+			op.cmd.ext_opcode = op.cmd.opcode;	\
+	}
+
 /**
  * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data
  *                           transfer
@@ -113,6 +154,16 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
 	/* convert the dummy cycles to the number of bytes */
 	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
+	if (spi_nor_protocol_is_8_8_8(nor->read_proto)) {
+		op.cmd.nbytes = 2;
+		SET_OCTAL_EXTION_OPCODE();
+
+		if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto))
+			SET_SPIMEM_OP_DTR_READ();
+
+		memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op, sizeof(op));
+	}
+
 	usebouncebuf = spi_nor_spimem_bounce(nor, &op);
 
 	if (nor->dirmap.rdesc) {
@@ -176,6 +227,16 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
 		op.addr.nbytes = 0;
 
+	if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+		op.cmd.nbytes = 2;
+		SET_OCTAL_EXTION_OPCODE();
+
+		if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto))
+			SET_SPIMEM_OP_DTR_WRITE();
+
+		memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op, sizeof(op));
+	}
+
 	if (spi_nor_spimem_bounce(nor, &op))
 		memcpy(nor->bouncebuf, buf, op.data.nbytes);
 
@@ -227,6 +288,15 @@ int spi_nor_write_enable(struct spi_nor *nor)
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_NO_DATA);
 
+		if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+			op.cmd.buswidth = 8;
+			op.cmd.nbytes = 2;
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto))
+				op.cmd.dtr = true;
+		}
+
 		ret = spi_mem_exec_op(nor->spimem, &op);
 	} else {
 		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN,
@@ -256,6 +326,15 @@ int spi_nor_write_disable(struct spi_nor *nor)
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_NO_DATA);
 
+		if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+			op.cmd.buswidth = 8;
+			op.cmd.nbytes = 2;
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto))
+				op.cmd.dtr = true;
+		}
+
 		ret = spi_mem_exec_op(nor->spimem, &op);
 	} else {
 		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI,
@@ -287,6 +366,16 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_DATA_IN(1, sr, 1));
 
+		if (spi_nor_protocol_is_8_8_8(nor->read_proto)) {
+			SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH();
+			op.addr.nbytes = nor->params->rdsr_addr_nbytes;
+			op.dummy.nbytes = nor->params->rdsr_dummy_cycles;
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto))
+				SET_SPIMEM_OP_DTR_READ();
+		}
+
 		ret = spi_mem_exec_op(nor->spimem, &op);
 	} else {
 		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR,
@@ -300,6 +389,92 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
 }
 
 /**
+ * spi_nor_read_cr2() - Read the Configuration Register 2.
+ * @nor:	pointer to 'struct spi_nor'.
+ * @addr:	offset address to read.
+ * @cr2:	pointer to a DMA-able buffer where the value of the
+ *              Configuration Register 2  will be written.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
+{
+	int ret;
+
+	if (nor->spimem) {
+		struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->params->rd_reg_cmd, 1),
+				   SPI_MEM_OP_ADDR(4, addr, 1),
+				   SPI_MEM_OP_DUMMY(4, 1),
+				   SPI_MEM_OP_DATA_IN(1, cr2, 1));
+
+		if (spi_nor_protocol_is_8_8_8(nor->read_proto)) {
+			SET_SPIMEM_OP_FULL_OCTAL_READ_BUSWIDTH();
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->read_proto))
+				SET_SPIMEM_OP_DTR_READ();
+		}
+
+		ret = spi_mem_exec_op(nor->spimem, &op);
+	} else {
+		ret = nor->controller_ops->read_reg(nor,
+						    nor->params->rd_reg_cmd,
+						    cr2, 1);
+	}
+
+	if (ret)
+		dev_dbg(nor->dev, "error %d reading CR2\n", ret);
+
+	return ret;
+}
+
+/**
+ * spi_nor_write_cr2() - Write the Configuration Register 2.
+ * @nor:	pointer to 'struct spi_nor'.
+ * @addr:	offset address to write.
+ * @cr2:	pointer to a DMA-able buffer where the value of the
+ *              Configuratin Register 2 will be read.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
+{
+	int ret;
+
+	ret = spi_nor_write_enable(nor);
+	if (ret)
+		return ret;
+
+	if (nor->spimem) {
+		struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->params->wr_reg_cmd, 1),
+				   SPI_MEM_OP_ADDR(4, addr, 1),
+				   SPI_MEM_OP_NO_DUMMY,
+				   SPI_MEM_OP_DATA_OUT(1, cr2, 1));
+
+		if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+			SET_SPIMEM_OP_FULL_OCTAL_WRITE_BUSWIDTH();
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto))
+				SET_SPIMEM_OP_DTR_WRITE();
+		}
+
+		ret = spi_mem_exec_op(nor->spimem, &op);
+	} else {
+		ret = nor->controller_ops->write_reg(nor,
+						     nor->params->wr_reg_cmd,
+						     cr2, 1);
+	}
+
+	if (ret)
+		dev_dbg(nor->dev, "error %d write CFG Reg 2\n", ret);
+
+	return ret;
+}
+
+/**
  * spi_nor_read_fsr() - Read the Flag Status Register.
  * @nor:	pointer to 'struct spi_nor'
  * @fsr:	pointer to a DMA-able buffer where the value of the
@@ -1002,6 +1177,15 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_NO_DATA);
 
+		if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+			op.cmd.buswidth = 8;
+			op.cmd.nbytes = 2;
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto))
+				op.cmd.dtr = true;
+		}
+
 		ret = spi_mem_exec_op(nor->spimem, &op);
 	} else {
 		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE,
@@ -1144,6 +1328,18 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_NO_DATA);
 
+		if (spi_nor_protocol_is_8_8_8(nor->write_proto)) {
+			op.cmd.buswidth = 8;
+			op.addr.buswidth = 8;
+			op.cmd.nbytes = 2;
+			SET_OCTAL_EXTION_OPCODE();
+
+			if (spi_nor_protocol_is_8D_8D_8D(nor->write_proto)) {
+				op.cmd.dtr = true;
+				op.addr.dtr = true;
+			}
+		}
+
 		return spi_mem_exec_op(nor->spimem, &op);
 	} else if (nor->controller_ops->erase) {
 		return nor->controller_ops->erase(nor, addr);
@@ -2204,7 +2400,7 @@ static int spi_nor_check(struct spi_nor *nor)
 	return 0;
 }
 
-static void
+void
 spi_nor_set_read_settings(struct spi_nor_read_command *read,
 			  u8 num_mode_clocks,
 			  u8 num_wait_states,
@@ -2253,6 +2449,7 @@ 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,
@@ -2269,6 +2466,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,
@@ -2368,12 +2566,6 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor,
 	struct spi_nor_flash_parameter *params = nor->params;
 	unsigned int cap;
 
-	/* DTR modes are not supported yet, mask them all. */
-	*hwcaps &= ~SNOR_HWCAPS_DTR;
-
-	/* X-X-X modes are not supported yet, mask them all. */
-	*hwcaps &= ~SNOR_HWCAPS_X_X_X;
-
 	for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
 		int rdidx, ppidx;
 
@@ -2614,7 +2806,6 @@ static int spi_nor_default_setup(struct spi_nor *nor,
 	 * controller and the SPI flash memory.
 	 */
 	shared_mask = hwcaps->mask & params->hwcaps.mask;
-
 	if (nor->spimem) {
 		/*
 		 * When called from spi_nor_probe(), all caps are set and we
@@ -2831,12 +3022,21 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
  */
 static void spi_nor_late_init_params(struct spi_nor *nor)
 {
+	struct spi_nor_flash_parameter *params = nor->params;
+	int ret = 0;
+
 	/*
 	 * NOR protection support. When locking_ops are not provided, we pick
 	 * the default ones.
 	 */
 	if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops)
 		nor->params->locking_ops = &spi_nor_sr_locking_ops;
+
+	if (nor->params->xspi_enable)
+		ret = nor->params->xspi_enable(nor,
+					       (params->dtr_read_cmd) ? 1 : 0);
+	if (ret)
+		dev_err(nor->dev, " enable xSPI mode failed %d\n", ret);
 }
 
 /**
@@ -2886,8 +3086,8 @@ static int spi_nor_init_params(struct spi_nor *nor)
 
 	spi_nor_manufacturer_init_params(nor);
 
-	if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
-	    !(nor->info->flags & SPI_NOR_SKIP_SFDP))
+	if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+	     SPI_NOR_OCTAL_RD_WR)) && !(nor->info->flags & SPI_NOR_SKIP_SFDP))
 		spi_nor_sfdp_init_params(nor);
 
 	spi_nor_post_sfdp_fixups(nor);
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 2ea11fa..ef32b86 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -62,6 +62,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
 };
@@ -78,6 +79,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
 };
@@ -208,6 +210,8 @@ struct spi_nor_locking_ops {
  *                      e.g. different opcodes, specific address calculation,
  *                      page size, etc.
  * @locking_ops:	SPI NOR locking methods.
+ * @xspi_enable:	enables xSPI Octal mode for 8S-8S-8S/8D-8D-8D.
+ * @octal_max_speed:	xSPI Octal maximum operation speed (MHz).
  * @dtr_read_cmd:	xSPI Octal DTR Read Fast command.
  * @rdsr_addr_nbytes:	xSPI Octal address bytes for read status register.
  * @rdsr_dummy_cycles:	xSPI Octal dummy cycles for read status register.
@@ -232,6 +236,11 @@ struct spi_nor_flash_parameter {
 
 	const struct spi_nor_locking_ops *locking_ops;
 
+	/* BFPT DWORD19 Octal mode enable sequences */
+	int (*xspi_enable)(struct spi_nor *nor, bool dtr);
+	/* BFPT DWORD20 Maximum operation speed of device in Octal mode */
+	u16 octal_max_speed;
+
 	/* xSPI profile 1.0 parameter for Octal 8S-8S-8S/8D-8D-8D */
 	u8 dtr_read_cmd;
 	u8 rdsr_addr_nbytes;
@@ -325,6 +334,7 @@ struct flash_info {
 					 * BP3 is bit 6 of status register.
 					 * Must be used with SPI_NOR_4BIT_BP.
 					 */
+#define SPI_NOR_OCTAL_RD_WR	BIT(19)	/* Flash supports Octal Read & Write */
 
 	/* Part specific fixup hooks. */
 	const struct spi_nor_fixups *fixups;
@@ -413,6 +423,8 @@ struct spi_nor_manufacturer {
 extern const struct spi_nor_manufacturer spi_nor_xilinx;
 extern const struct spi_nor_manufacturer spi_nor_xmc;
 
+int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
+int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
 int spi_nor_write_enable(struct spi_nor *nor);
 int spi_nor_write_disable(struct spi_nor *nor);
 int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
@@ -432,6 +444,11 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
 
 int spi_nor_hwcaps_read2cmd(u32 hwcaps);
 u8 spi_nor_convert_3to4_read(u8 opcode);
+void spi_nor_set_read_settings(struct spi_nor_read_command *read,
+			       u8 num_mode_clocks,
+			       u8 num_wait_states,
+			       u8 opcode,
+			       enum spi_nor_protocol proto);
 void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode,
 			     enum spi_nor_protocol proto);
 
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 26814a1..85a8509 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -50,6 +50,12 @@ struct xspi_dummy_cycles {
 	u8 shift;	/* Bit shift */
 };
 
+/* Basic Flash Parameter Table 20th DWORD, Max operation speed of device */
+struct octal_max_speed {
+	u8 idx;	/* Bits value */
+	u16 hz;	/* MHz */
+};
+
 struct sfdp_header {
 	u32		signature; /* Ox50444653U <=> "SFDP" */
 	u8		minor;
@@ -423,6 +429,64 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
 }
 
 /**
+ * spi_nor_cfg_reg2_octal_enable() - enable octal mode by CFG Reg 2
+ * @nor:               pointer to a 'struct spi_nor'
+ * @dtr:               true for DTR mode
+ *
+ * The Basic Flash Parameter Table 19th DWORD defined the Octal mode
+ * enable sequences by writing CFG Reg 2, 1: 8S-8S-8S, 2: 8D-8D-8D.
+ *
+ * Set Octal mode read/PP command, protocol and read dummy cycles.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_cfg_reg2_octal_enable(struct spi_nor *nor, bool dtr)
+{
+	struct spi_nor_flash_parameter *p = nor->params;
+	int ret;
+	u8 cr2 = 0;
+
+	if (!(nor->spimem->spi->mode & (SPI_RX_OCTAL | SPI_TX_OCTAL)))
+		return -ENOTSUPP;
+
+	if (dtr) {
+		cr2 |= CR2_OCTAL_MODE_DTR;
+		ret = spi_nor_write_cr2(nor, CR2_OCTAL_MODE_ADDR, &cr2);
+		if (ret)
+			return ret;
+
+		/* Octal 8D-8D-8D mode */
+		p->hwcaps.mask |= SNOR_HWCAPS_OPI_FULL_DTR;
+		spi_nor_set_read_settings(&p->reads[SNOR_CMD_READ_8_8_8_DTR],
+					  0, p->dummy_cycles,
+					  p->dtr_read_cmd,
+					  SNOR_PROTO_8_8_8_DTR);
+
+		spi_nor_set_pp_settings(&p->page_programs
+					[SNOR_CMD_PP_8_8_8_DTR],
+					SPINOR_OP_PP_8D_8D_8D,
+					SNOR_PROTO_8_8_8_DTR);
+	} else {
+		cr2 |= CR2_OCTAL_MODE_STR;
+		ret = spi_nor_write_cr2(nor, CR2_OCTAL_MODE_ADDR, &cr2);
+		if (ret)
+			return ret;
+
+		/* Octal 8S-8S-8S mode */
+		p->hwcaps.mask |= SNOR_HWCAPS_OPI_FULL_STR;
+		spi_nor_set_read_settings(&p->reads[SNOR_CMD_READ_8_8_8],
+					  0, p->dummy_cycles,
+					  SPINOR_OP_READ_8_8_8,
+					  SNOR_PROTO_8_8_8);
+
+		spi_nor_set_pp_settings(&p->page_programs[SNOR_CMD_PP_8_8_8],
+					SPINOR_OP_PP_8_8_8,
+					SNOR_PROTO_8_8_8);
+	}
+	return 0;
+}
+
+/**
  * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
  * @nor:		pointer to a 'struct spi_nor'
  * @bfpt_header:	pointer to the 'struct sfdp_parameter_header' describing
@@ -464,6 +528,22 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
 	u32 addr;
 	u16 half;
 	u8 erase_mask;
+	static const struct octal_max_speed max_hz[] = {
+		/* Bits value, MHz */
+		{ 0x0c, 400 },
+		{ 0x0b, 333 },
+		{ 0x0a, 266 },
+		{ 0x09, 250 },
+		{ 0x08, 200 },
+		{ 0x07, 166 },
+		{ 0x06, 133 },
+		{ 0x05, 100 },
+		{ 0x04,  80 },
+		{ 0x03,  66 },
+		{ 0x02,  50 },
+		{ 0x01,  33 },
+	};
+	u8 idx;
 
 	/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
 	if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
@@ -628,6 +708,42 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
 		return -EINVAL;
 	}
 
+	/* 8D-8D-8D command extension. */
+	switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
+	case BFPT_DWORD18_CMD_EXT_REP:
+		nor->ext_cmd_mode = EXT_CMD_IS_REPEAT;
+		break;
+	case BFPT_DWORD18_CMD_EXT_INV:
+		nor->ext_cmd_mode = EXT_CMD_IS_INVERSE;
+		break;
+	default:
+		break;
+	}
+
+	/* Octal mode enable sequences. */
+	switch (bfpt.dwords[BFPT_DWORD(19)] & BFPT_DWORD19_OCTAL_SEQ_MASK) {
+	case BFPT_DWORD19_TWO_INST:
+		break;
+	case BFPT_DWORD19_CFG_REG2:
+		params->xspi_enable = spi_nor_cfg_reg2_octal_enable;
+		break;
+	default:
+		break;
+	}
+
+	/* Octal mode max speed */
+	idx = max(FIELD_GET(BFPT_DWORD20_OCTAL_DTR_MAX_SPEED,
+			    bfpt.dwords[BFPT_DWORD(20)]),
+		  FIELD_GET(BFPT_DWORD20_OCTAL_STR_MAX_SPEED,
+			    bfpt.dwords[BFPT_DWORD(20)]));
+
+	for (i = 0; i < ARRAY_SIZE(max_hz); i++) {
+		if (max_hz[i].idx == idx) {
+			params->octal_max_speed = max_hz[i].hz;
+			break;
+		}
+	}
+
 	return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params);
 }
 
diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h
index e0a8ded..7a55b4d 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -10,11 +10,11 @@
 /* Basic Flash Parameter Table */
 
 /*
- * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs.
+ * JESD216 rev D defines a Basic Flash Parameter Table of 20 DWORDs.
  * They are indexed from 1 but C arrays are indexed from 0.
  */
 #define BFPT_DWORD(i)		((i) - 1)
-#define BFPT_DWORD_MAX		16
+#define BFPT_DWORD_MAX		20
 
 struct sfdp_bfpt {
 	u32	dwords[BFPT_DWORD_MAX];
@@ -83,6 +83,18 @@ struct sfdp_bfpt {
 #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD		(0x4UL << 20)
 #define BFPT_DWORD15_QER_SR2_BIT1		(0x5UL << 20) /* Spansion */
 
+#define BFPT_DWORD18_CMD_EXT_MASK		GENMASK(30, 29)
+#define BFPT_DWORD18_CMD_EXT_REP		(0x0UL << 29) /* Repeat */
+#define BFPT_DWORD18_CMD_EXT_INV		(0x1UL << 29) /* Invert */
+
+#define BFPT_DWORD19_OCTAL_SEQ_MASK		GENMASK(6, 5)
+#define BFPT_DWORD19_TWO_INST			(0x1UL << 5) /* Two Inst. */
+#define BFPT_DWORD19_CFG_REG2			(0x2UL << 5) /* CFG Reg2  */
+
+#define BFPT_DWORD20_OCTAL_MAX_SPEED_MASK	GENMASK(31, 16)
+#define BFPT_DWORD20_OCTAL_DTR_MAX_SPEED	GENMASK(31, 28)
+#define BFPT_DWORD20_OCTAL_STR_MAX_SPEED	GENMASK(19, 16)
+
 struct sfdp_parameter_header {
 	u8		id_lsb;
 	u8		minor;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 1e2af0e..778b52e 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -68,6 +68,9 @@
 #define SPINOR_OP_BE_4K_4B	0x21	/* Erase 4KiB block */
 #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
+#define SPINOR_OP_READ_8_8_8	SPINOR_OP_READ_1_4_4_4B
+#define SPINOR_OP_PP_8_8_8	SPINOR_OP_PP_4B
+#define SPINOR_OP_PP_8D_8D_8D	SPINOR_OP_PP_4B
 
 /* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
 #define SPINOR_OP_READ_1_1_1_DTR	0x0d
@@ -91,7 +94,6 @@
 #define XSR_PAGESIZE		BIT(0)	/* Page size in Po2 or Linear */
 #define XSR_RDY			BIT(7)	/* Ready */
 
-
 /* Used for Macronix and Winbond flashes. */
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
@@ -137,6 +139,13 @@
 #define SR2_QUAD_EN_BIT1	BIT(1)
 #define SR2_QUAD_EN_BIT7	BIT(7)
 
+/* Configuration Register 2, JEDEC216-D */
+#define CR2_OCTAL_MODE_ADDR	0x0
+#define CR2_OCTAL_MODE_MASK	GENMASK(1, 0)
+#define CR2_SPI_MODE		0
+#define CR2_OCTAL_MODE_STR	1
+#define CR2_OCTAL_MODE_DTR	2
+
 /* Supported SPI protocols */
 #define SNOR_PROTO_INST_MASK	GENMASK(23, 16)
 #define SNOR_PROTO_INST_SHIFT	16
@@ -157,15 +166,21 @@
 	 SNOR_PROTO_DATA_MASK)
 
 #define SNOR_PROTO_IS_DTR	BIT(24)	/* Double Transfer Rate */
+#define SNOR_PROTO_IS_FULL_DTR	BIT(25)	/* Full Double Transfer Rate */
 
 #define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits)	\
 	(SNOR_PROTO_INST(_inst_nbits) |				\
 	 SNOR_PROTO_ADDR(_addr_nbits) |				\
 	 SNOR_PROTO_DATA(_data_nbits))
+
 #define SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits)	\
 	(SNOR_PROTO_IS_DTR |					\
 	 SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits))
 
+#define SNOR_PROTO_8D_8D_8D(_inst_nbits, _addr_nbits, _data_nbits)	\
+	(SNOR_PROTO_IS_FULL_DTR |					\
+	SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits))
+
 enum spi_nor_protocol {
 	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),
 	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2),
@@ -182,6 +197,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_8D_8D_8D(8, 8, 8),
 };
 
 static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
@@ -189,6 +205,16 @@ static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
 	return !!(proto & SNOR_PROTO_IS_DTR);
 }
 
+static inline bool spi_nor_protocol_is_8_8_8(enum spi_nor_protocol proto)
+{
+	return !!(proto & SNOR_PROTO_8_8_8);
+}
+
+static inline bool spi_nor_protocol_is_8D_8D_8D(enum spi_nor_protocol proto)
+{
+	return !!(proto & SNOR_PROTO_IS_FULL_DTR);
+}
+
 static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto)
 {
 	return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >>
@@ -228,7 +254,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)
@@ -250,6 +276,7 @@ struct spi_nor_hwcaps {
 #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.
@@ -260,7 +287,7 @@ 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_MASK	GENMASK(23, 16)
 #define SNOR_HWCAPS_PP		BIT(16)
 
 #define SNOR_HWCAPS_PP_QUAD	GENMASK(19, 17)
@@ -272,6 +299,13 @@ struct spi_nor_hwcaps {
 #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)
+
+#define SNOR_HWCAPS_OPI_FULL_STR	(SNOR_HWCAPS_READ_8_8_8 | \
+					 SNOR_HWCAPS_PP_8_8_8)
+
+#define SNOR_HWCAPS_OPI_FULL_DTR	(SNOR_HWCAPS_READ_8_8_8_DTR | \
+					 SNOR_HWCAPS_PP_8_8_8_DTR)
 
 #define SNOR_HWCAPS_X_X_X	(SNOR_HWCAPS_READ_2_2_2 |	\
 				 SNOR_HWCAPS_READ_4_4_4 |	\
@@ -282,7 +316,9 @@ struct spi_nor_hwcaps {
 #define SNOR_HWCAPS_DTR		(SNOR_HWCAPS_READ_1_1_1_DTR |	\
 				 SNOR_HWCAPS_READ_1_2_2_DTR |	\
 				 SNOR_HWCAPS_READ_1_4_4_DTR |	\
-				 SNOR_HWCAPS_READ_1_8_8_DTR)
+				 SNOR_HWCAPS_READ_1_8_8_DTR |	\
+				 SNOR_HWCAPS_READ_8_8_8_DTR |	\
+				 SNOR_HWCAPS_PP_8_8_8_DTR)
 
 #define SNOR_HWCAPS_ALL		(SNOR_HWCAPS_READ_MASK |	\
 				 SNOR_HWCAPS_PP_MASK)
@@ -326,6 +362,11 @@ struct spi_nor_controller_ops {
 struct spi_nor_manufacturer;
 struct spi_nor_flash_parameter;
 
+enum extension_cmd_mode {
+	EXT_CMD_IS_REPEAT,
+	EXT_CMD_IS_INVERSE,
+};
+
 /**
  * struct spi_nor - Structure for defining a the SPI NOR layer
  * @mtd:		point to a mtd_info structure
@@ -335,6 +376,7 @@ struct spi_nor_controller_ops {
  * @bouncebuf:		bounce buffer used when the buffer passed by the MTD
  *                      layer is not DMA-able
  * @bouncebuf_size:	size of the bounce buffer
+ * @ext_cmd_mode:	xspi extension command mode, 0: repeat, 1:inverse
  * @info:		spi-nor part JDEC MFR id and other info
  * @manufacturer:	spi-nor manufacturer
  * @page_size:		the page size of the SPI NOR
@@ -363,6 +405,7 @@ struct spi_nor {
 	struct spi_mem		*spimem;
 	u8			*bouncebuf;
 	size_t			bouncebuf_size;
+	enum extension_cmd_mode ext_cmd_mode;
 	const struct flash_info	*info;
 	const struct spi_nor_manufacturer *manufacturer;
 	u32			page_size;
-- 
1.9.1

Powered by blists - more mailing lists