All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures
@ 2023-12-06  9:31 Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR Tejas Bhumkar
                   ` (30 more replies)
  0 siblings, 31 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git

A set of patches has been developed to resolve concerns regarding data 
integrity failures in QSPI and OSPI for the Versal, Versal NET, Zynq, 
and ZynqMP platforms.
 
The series has undergone testing with flashes on the default setup, 
and comprehensive testing is currently underway to test the series 
with all available flash parts.
 
These patches are built upon the v5 series, which can be found at the 
following link:
https://lore.kernel.org/all/20231201031839.239567-1-venkatesh.abbarapu@amd.com/

Changes in v2:
- Removed the SPI_NOR_HAS_TB flag for gd25lx256e and is25wx256 flashes 
  since it already exists in a tree.

Algapally Santosh Sagar (1):
  mtd: spi-nor-ids: Add support for W25Q02NW

Ashok Reddy Soma (10):
  mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes
  mtd: spi-nor: Add support for cross die read in dual flash
    configuration
  mtd: spi-nor: Enable DTR octal flash program
  mtd: spi-nor: Send write disable cmd after every write enable
  mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled
  spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns
  spi: cadence_qspi: Clean up registers in init
  spi: cadence_qspi: Initialize read and write watermark registers
  spi: cadence_qspi: Enable ECO bit for higher frequencies
  spi: cadence_qspi: Write aligned byte length to ahbbase

T Karthik Reddy (9):
  mtd: spi-nor: Add config to enable flash DTR
  mtd: spi-nor-core: Set dummy buswidth equal to data buswidth
  spi: mtd: Use split reads if multi-die flag is set
  mtd: spi-nor: program quad enable bit for winbond flashes
  spi: cadence_qspi: Setup ddr mode in cadence qspi driver
  spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config
  spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal
    driver
  spi: cadence_qspi: Add spi mem dtr support ops
  mtd: spi-nor: Add block protection support for micron flashes

Tejas Bhumkar (5):
  arm64: versal: Enable defconfig for Micron octal flashes
  mtd: spi-nor: Update erase operation function
  spi: cadence_qspi: Fix versal ospi indirect write timed out issue
  arm64: versal: Enable soft reset support for xspi flashes
  arm64: versal: Enable octal DTR mode

Venkatesh Yadav Abbarapu (5):
  mtd: spi-nor: Update block protection flags for flash parts
  mtd: spi-nor: Add support for locking on Macronix nor flashes
  mtd: spi-nor: Add support for locking on ISSI nor flashes
  mtd: spi-nor: Add support for locking on GIGADEVICE nor flashes
  mtd: spi-nor: Add support for locking on Spansion nor flashes

 configs/xilinx_versal_virt_defconfig |    4 +
 drivers/mtd/spi/Kconfig              |    7 +
 drivers/mtd/spi/sf_internal.h        |    8 +
 drivers/mtd/spi/spi-nor-core.c       | 2028 +++++++++++++++++++++++---
 drivers/mtd/spi/spi-nor-ids.c        |   34 +-
 drivers/spi/cadence_ospi_versal.c    |   77 +-
 drivers/spi/cadence_qspi.c           |  403 ++++-
 drivers/spi/cadence_qspi.h           |   71 +
 drivers/spi/cadence_qspi_apb.c       |  107 +-
 include/linux/mtd/spi-nor.h          |   22 +
 include/spi.h                        |    4 +-
 11 files changed, 2541 insertions(+), 224 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-20  7:29   ` Jagan Teki
  2023-12-06  9:31 ` [PATCH v2 02/30] mtd: spi-nor-core: Set dummy buswidth equal to data buswidth Tejas Bhumkar
                   ` (29 subsequent siblings)
  30 siblings, 1 reply; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

From: T Karthik Reddy <t.karthik.reddy@amd.com>

The spi-nor framework will set up the flash parameters by
reading the flash id table flags, which include cmd opcodes,
address width, dummy bytes, and bus width. In case, flash
supports octal DTR mode and the controller does not support
the DTR. There is no process to switch back to SDR mode.
To avoid this issue, create a Kconfig option SPI_FLASH_DTR_ENABLE
to explicitly specify to enable/disable flash DTR support.
This config is disabled by default.
Do not initialize the mt35xu512aba post sfdp fixups unless
SPI_FLASH_DTR_ENABLE is enabled to avoid configuring DTR.

Signed-off-by: T Karthik Reddy <t.karthik.reddy@amd.com>
Co-developed-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/Kconfig        |  7 +++++++
 drivers/mtd/spi/spi-nor-core.c | 12 +++++++++---
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index 732b076045..e0898074a2 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -202,6 +202,13 @@ config SPI_FLASH_MT35XU
 	 because the fixup hooks for this flash add extra size overhead. Boards
 	 that don't use the flash can disable this to save space.
 
+config SPI_FLASH_DTR_ENABLE
+	bool "Enable Flash DTR support"
+	help
+	  Select this config to enable DTR mode by spi-nor framework.
+	  This config provides an option to explicitly enable/disable the DTR
+	  support even though the flash id flags specify flash supports DTR mode.
+
 config SPI_FLASH_SST
 	bool "SST SPI flash support"
 	help
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 5c3ffc80eb..473d9f41f3 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2962,7 +2962,8 @@ static int spi_nor_init_params(struct spi_nor *nor,
 					  SNOR_PROTO_1_1_8);
 	}
 
-	if (info->flags & SPI_NOR_OCTAL_DTR_READ) {
+	if (CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE) &&
+	    info->flags & SPI_NOR_OCTAL_DTR_READ) {
 		params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
 		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
 					  0, 20, SPINOR_OP_READ_FAST,
@@ -2978,8 +2979,10 @@ static int spi_nor_init_params(struct spi_nor *nor,
 	 * 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 (CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE)) {
+		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;
@@ -4011,6 +4014,9 @@ static void mt35xu512aba_default_init(struct spi_nor *nor)
 static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor,
 					 struct spi_nor_flash_parameter *params)
 {
+	if (!CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE))
+		return;
+
 	/* Set the Fast Read settings. */
 	params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
 	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 02/30] mtd: spi-nor-core: Set dummy buswidth equal to data buswidth
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 03/30] arm64: versal: Enable defconfig for Micron octal flashes Tejas Bhumkar
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy, Ashok Reddy Soma

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

In current implementation dummy buswidth is set equal to address
buswidth. In case of quad spi (mode 1-1-4), where address width is 1
the dummy bytes will be calculated to 1(8 dummy cycles) and dummy
buswidth is set to 1. Due to this, the controller driver will introduce
8 dummy cycles on data line(D0) during read operation.

But since we are using 4 data lines in case of qspi, we need to change
this dummy bus width to 4. This will make dummy bytes to 4 inplace of 1.
This will be taken care in controller driver by dividing with dummy
buswidth again as in below code, which makes dummy cycles to 8 as
earlier.

dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;

So with this change dummy cycles will be on all data lines(D0-D3) and it
is taken care for all the configurations(single, dual, quad and octal).

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 473d9f41f3..8949dab548 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -260,7 +260,7 @@ void spi_nor_setup_op(const struct spi_nor *nor,
 		op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto);
 
 	if (op->dummy.nbytes)
-		op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto);
+		op->dummy.buswidth = spi_nor_get_protocol_data_nbits(proto);
 
 	if (op->data.nbytes)
 		op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 03/30] arm64: versal: Enable defconfig for Micron octal flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 02/30] mtd: spi-nor-core: Set dummy buswidth equal to data buswidth Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 04/30] mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes Tejas Bhumkar
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git

The Micron MT35 series octal flashes can be activated
through the configuration option CONFIG_SPI_FLASH_MT35XU.
To ensure their detection, enable this option in the
default defconfig for octal flashes.

Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 configs/xilinx_versal_virt_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configs/xilinx_versal_virt_defconfig b/configs/xilinx_versal_virt_defconfig
index 6a2c03ccdd..ec7caacca0 100644
--- a/configs/xilinx_versal_virt_defconfig
+++ b/configs/xilinx_versal_virt_defconfig
@@ -95,6 +95,7 @@ CONFIG_MMC_SDHCI_ZYNQ=y
 CONFIG_ZYNQ_SDHCI_MIN_FREQ=100000
 CONFIG_MTD=y
 CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_MT35XU=y
 CONFIG_SPI_FLASH_GIGADEVICE=y
 CONFIG_SPI_FLASH_ISSI=y
 CONFIG_SPI_FLASH_MACRONIX=y
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 04/30] mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (2 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 03/30] arm64: versal: Enable defconfig for Micron octal flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 05/30] mtd: spi-nor: Add support for cross die read in dual flash configuration Tejas Bhumkar
                   ` (26 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

Enable mt35xu512aba_fixups for all mt35 series flashes to work
in DTR mode, and return after nor->fixups is updated, otherwise
it will get overwritten with macronix_octal_fixups.
This flash works in DTR mode only if CONFIG_SPI_FLASH_MT35XU
is enabled and SPI_NOR_OCTAL_DTR_READ flag is set in id table.

Additionally, a new flag, "SPI_XFER_SET_DDR," has been introduced
to instruct the Ospi controller driver to switch to DDR mode.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 8 +++++++-
 include/spi.h                  | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 8949dab548..e505648e5d 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -3991,6 +3991,7 @@ static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor)
 	if (ret)
 		return ret;
 
+	nor->spi->flags |= SPI_XFER_SET_DDR;
 	buf = SPINOR_MT_OCT_DTR;
 	op = (struct spi_mem_op)
 		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
@@ -4305,8 +4306,13 @@ void spi_nor_set_fixups(struct spi_nor *nor)
 #endif
 
 #ifdef CONFIG_SPI_FLASH_MT35XU
-	if (!strcmp(nor->info->name, "mt35xu512aba"))
+	if (!strcmp(nor->info->name, "mt35xu512aba") ||
+	    !strcmp(nor->info->name, "mt35xl512aba") ||
+	    !strcmp(nor->info->name, "mt35xu01g") ||
+	    !strcmp(nor->info->name, "mt35xu02g")) {
 		nor->fixups = &mt35xu512aba_fixups;
+		return;
+	}
 #endif
 
 #if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
diff --git a/include/spi.h b/include/spi.h
index 6bc8808bb9..eb015ecbf5 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -171,6 +171,7 @@ struct spi_slave {
 #define SPI_XFER_ONCE		(SPI_XFER_BEGIN | SPI_XFER_END)
 #define SPI_XFER_U_PAGE		BIT(4)
 #define SPI_XFER_STACKED	BIT(5)
+#define SPI_XFER_SET_DDR	BIT(6)
 	/*
 	 * Flag indicating that the spi-controller has multi chip select
 	 * capability and can assert/de-assert more than one chip select
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 05/30] mtd: spi-nor: Add support for cross die read in dual flash configuration
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (3 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 04/30] mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 06/30] mtd: spi-nor: Enable DTR octal flash program Tejas Bhumkar
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma, Michal Simek

From: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>

In a dual parallel configuration, halve the read offset.
Determine whether the read offset points to the lower or
upper flash in a dual stacked configuration and set the
corresponding flags accordingly.

Include support for cases where the read involves an odd
number of bytes.

Extend support for cross-die reads in flash memory devices
that contain multiple dies within them.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 61 ++++++++++++++++++++++++++++++----
 include/spi.h                  |  3 +-
 2 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e505648e5d..f6e7592458 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -1503,11 +1503,8 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
 	int ret;
 	u32 offset = from;
-	u32 stack_shift = 0;
-	u32 read_len = 0;
-	u32 rem_bank_len = 0;
-	u8 bank;
-	u8 is_ofst_odd = 0;
+	u32 bank_size, stack_shift = 0, read_len = 0, rem_bank_len = 0;
+	u8 bank, cur_bank, nxt_bank, is_ofst_odd = 0;
 
 	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
 
@@ -1541,6 +1538,40 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 			}
 		}
 
+		if (nor->addr_width == 4) {
+			/*
+			 * Some flash devices like N25Q512 have multiple dies
+			 * in it. Read operation in these devices is bounded
+			 * by its die segment. In a continuous read, across
+			 * multiple dies, when the last byte of the selected
+			 * die segment is read, the next byte read is the
+			 * first byte of the same die segment. This is Die
+			 * cross over issue. So to handle this issue, split
+			 * a read transaction, that spans across multiple
+			 * banks, into one read per bank. Bank size is 16MB
+			 * for single and dual stacked mode and 32MB for dual
+			 * parallel mode.
+			 */
+			if (nor->spi && nor->spi->multi_die) {
+				bank_size = SZ_16M;
+				if (nor->flags & SNOR_F_HAS_PARALLEL)
+					bank_size <<= 1;
+				cur_bank = offset / bank_size;
+				nxt_bank = (offset + len) / bank_size;
+				if (cur_bank != nxt_bank)
+					rem_bank_len = (bank_size *
+							(cur_bank + 1)) -
+							offset;
+				else
+					rem_bank_len = (mtd->size >>
+							stack_shift) -
+							offset;
+			} else {
+				rem_bank_len = (mtd->size >> stack_shift) -
+						offset;
+			}
+		}
+
 		if (nor->flags & SNOR_F_HAS_PARALLEL)
 			offset /= 2;
 
@@ -1552,6 +1583,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 #endif
 		}
 
+		if (len < rem_bank_len)
+			read_len = len;
+		else
+			read_len = rem_bank_len;
+
+		ret = spi_nor_wait_till_ready(nor);
+		if (ret)
+			goto read_err;
+
 		ret = nor->read(nor, offset, read_len, buf);
 		if (ret == 0) {
 			/* We shouldn't see 0-length reads */
@@ -1561,8 +1601,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 		if (ret < 0)
 			goto read_err;
 
-		*retlen += ret;
-		buf += ret;
+		if (is_ofst_odd == 1) {
+			memcpy(buf, (buf + 1), (len - 1));
+			*retlen += (ret - 1);
+			buf += ret - 1;
+			is_ofst_odd = 0;
+		} else {
+			*retlen += ret;
+			buf += ret;
+		}
 		from += ret;
 		len -= ret;
 	}
diff --git a/include/spi.h b/include/spi.h
index eb015ecbf5..9014066ee3 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -165,7 +165,7 @@ struct spi_slave {
 	unsigned int max_write_size;
 	void *memory_map;
 
-	u8 flags;
+	u32 flags;
 #define SPI_XFER_BEGIN		BIT(0)	/* Assert CS before transfer */
 #define SPI_XFER_END		BIT(1)	/* Deassert CS after transfer */
 #define SPI_XFER_ONCE		(SPI_XFER_BEGIN | SPI_XFER_END)
@@ -179,6 +179,7 @@ struct spi_slave {
 	 */
 	bool multi_cs_cap;
 	u32 bytemode;
+	bool multi_die;			/* flash with multiple dies */
 };
 
 /**
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 06/30] mtd: spi-nor: Enable DTR octal flash program
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (4 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 05/30] mtd: spi-nor: Add support for cross die read in dual flash configuration Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 07/30] spi: mtd: Use split reads if multi-die flag is set Tejas Bhumkar
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

Define a flag SPI_NOR_OCTAL_DTR_PP and if enabled in spi-nor-ids table,
enable octal DTR page program in the framework.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/sf_internal.h  | 1 +
 drivers/mtd/spi/spi-nor-core.c | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index d3ef69ec74..9c09f97ce2 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -69,6 +69,7 @@ struct flash_info {
 #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 */
+#define SPI_NOR_OCTAL_DTR_PP	BIT(18) /* Flash supports Octal DTR page program */
 };
 
 extern const struct flash_info spi_nor_ids[];
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index f6e7592458..63f78baaf4 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -3026,7 +3026,8 @@ static int spi_nor_init_params(struct spi_nor *nor,
 	 * Since xSPI Page Program opcode is backward compatible with
 	 * Legacy SPI, use Legacy SPI opcode there as well.
 	 */
-	if (CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE)) {
+	if (CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE) && info->flags & SPI_NOR_OCTAL_DTR_PP) {
+		params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
 		spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
 					SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR);
 	}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 07/30] spi: mtd: Use split reads if multi-die flag is set
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (5 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 06/30] mtd: spi-nor: Enable DTR octal flash program Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 08/30] mtd: spi-nor: Update block protection flags for flash parts Tejas Bhumkar
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy, Ashok Reddy Soma

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

Some flash devices have multiple dies in it & has die cross over
issue. When SPI_NOR_MULTI_DIE flag is set in flash id table use
it to enable split reads to avoid above issue. Define SPI_NOR_MULTI_DIE
new flag to flash id flags. Remove SPI_FLASH_SPLIT_READ config and
related code from the zynq and zynqmp qspi drivers as it is redundant.

Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/sf_internal.h  | 1 +
 drivers/mtd/spi/spi-nor-core.c | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 9c09f97ce2..2cbdea60b0 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -70,6 +70,7 @@ struct flash_info {
 #define SPI_NOR_OCTAL_READ	BIT(16)	/* Flash supports Octal Read */
 #define SPI_NOR_OCTAL_DTR_READ	BIT(17)	/* Flash supports Octal DTR Read */
 #define SPI_NOR_OCTAL_DTR_PP	BIT(18) /* Flash supports Octal DTR page program */
+#define SPI_NOR_MULTI_DIE	BIT(19) /* Flash has multi dies & need split reads*/
 };
 
 extern const struct flash_info spi_nor_ids[];
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 63f78baaf4..ace5da9591 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -4485,6 +4485,9 @@ int spi_nor_scan(struct spi_nor *nor)
 	if (info->flags & SPI_NOR_NO_ERASE)
 		mtd->flags |= MTD_NO_ERASE;
 
+	if (info->flags & SPI_NOR_MULTI_DIE)
+		nor->spi->multi_die = true;
+
 	nor->page_size = params.page_size;
 	mtd->writebufsize = nor->page_size;
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 08/30] mtd: spi-nor: Update block protection flags for flash parts
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (6 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 07/30] spi: mtd: Use split reads if multi-die flag is set Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 09/30] mtd: spi-nor-ids: Add support for W25Q02NW Tejas Bhumkar
                   ` (22 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Venkatesh Yadav Abbarapu

From: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>

The block protection flags for Gigadevice, Spansion, and ISSI flash
memory have been modified. Additionally, new flags for
SPI_NOR_OCTAL_DTR_READ and octal DTR page programming have been
introduced for Micron OSPI flashes. Furthermore, the flashes mt35xu01g
and mt35xu02g have been incorporated into the CONFIG_SPI_FLASH_MT35XU
configuration, so that in driver mt35xu512aba_fixups will be applied.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-ids.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 3cb132dcff..0dbd385265 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -122,9 +122,9 @@ const struct flash_info spi_nor_ids[] = {
 	{INFO("gd25b256", 0xc84019, 0, 64 * 1024, 512,	SECT_4K |
 	SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES)	},
 	{INFO("gd25b512", 0xc8471A, 0, 64 * 1024, 1024,	SECT_4K |
-	SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES)},
+	SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_TB)},
 	{INFO("gd55b01g", 0xc8471B, 0, 64 * 1024, 2048,	SECT_4K |
-	SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES)},
+	SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_TB)},
 	{INFO("gd55b02g", 0xc8471C, 0, 64 * 1024, 4096,	SECT_4K |
 	SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_4B_OPCODES)},
 	{INFO("gd25f64", 0xc84317, 0, 64 * 1024, 128,	SECT_4K |
@@ -212,11 +212,11 @@ const struct flash_info spi_nor_ids[] = {
 	{ INFO("is25lp128",  0x9d6018, 0, 64 * 1024, 256,
 			SECT_4K | SPI_NOR_DUAL_READ) },
 	{ INFO("is25lp256",  0x9d6019, 0, 64 * 1024, 512,
-			SECT_4K | SPI_NOR_DUAL_READ) },
+			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_HAS_TB) },
 	{ INFO("is25lp512",  0x9d601a, 0, 64 * 1024, 1024,
-			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_TB) },
 	{ INFO("is25lp01g",  0x9d601b, 0, 64 * 1024, 2048,
-			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_TB) },
 	{ INFO("is25wp008", 0x9d7014, 0, 64 * 1024,  16, SPI_NOR_QUAD_READ) },
 	{ INFO("is25wp016", 0x9d7015, 0, 64 * 1024,  32, SPI_NOR_QUAD_READ) },
 	{ INFO("is25wp032",  0x9d7016, 0, 64 * 1024,  64,
@@ -312,11 +312,15 @@ const struct flash_info spi_nor_ids[] = {
 	{ INFO("mt25qu02g",   0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
 	{ INFO("mt25ql02g",   0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) },
 #ifdef CONFIG_SPI_FLASH_MT35XU
-	{ INFO("mt35xl512aba", 0x2c5a1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
-	{ INFO("mt35xu512aba", 0x2c5b1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
+	{ INFO("mt35xl512aba", 0x2c5a1a, 0,  128 * 1024,  512,
+		USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP) },
+	{ INFO("mt35xu512aba", 0x2c5b1a, 0,  128 * 1024,  512,
+		USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP) },
+	{ INFO6("mt35xu01g",  0x2c5b1b, 0x104100, 128 * 1024,  1024,
+		USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP) },
+	{ INFO("mt35xu02g",  0x2c5b1c, 0, 128 * 1024,  2048,
+		USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP) },
 #endif /* CONFIG_SPI_FLASH_MT35XU */
-	{ INFO6("mt35xu01g",  0x2c5b1b, 0x104100, 128 * 1024,  1024, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
-	{ INFO("mt35xu02g",  0x2c5b1c, 0, 128 * 1024,  2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
 #endif
 #ifdef CONFIG_SPI_FLASH_SPANSION	/* SPANSION */
 	/* Spansion/Cypress -- single (large) sector size only, at least
@@ -325,8 +329,8 @@ const struct flash_info spi_nor_ids[] = {
 	{ INFO("s25sl032p",  0x010215, 0x4d00,  64 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("s25sl064p",  0x010216, 0x4d00,  64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
-	{ INFO("s25fl256s1", 0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
-	{ INFO6("s25fl512s",  0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+	{ INFO("s25fl256s1", 0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR | SPI_NOR_HAS_TB) },
+	{ INFO6("s25fl512s",  0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR | SPI_NOR_HAS_TB) },
 	{ INFO6("s25fs512s",  0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
 	{ INFO("s25fl512s_256k",  0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
 	{ INFO("s25fl512s_64k",  0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
@@ -334,7 +338,7 @@ const struct flash_info spi_nor_ids[] = {
 	{ INFO("s70fs01gs_256k", 0x010221, 0x4d00, 256 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("s25sl12800", 0x012018, 0x0300, 256 * 1024,  64, 0) },
 	{ INFO("s25sl12801", 0x012018, 0x0301,  64 * 1024, 256, 0) },
-	{ INFO6("s25fl128s",  0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+	{ INFO6("s25fl128s",  0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR | SPI_NOR_HAS_TB) },
 	{ INFO("s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
 	{ INFO("s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
 	{ INFO("s25sl008a",  0x010213,      0,  64 * 1024,  16, 0) },
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 09/30] mtd: spi-nor-ids: Add support for W25Q02NW
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (7 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 08/30] mtd: spi-nor: Update block protection flags for flash parts Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 10/30] mtd: spi-nor: program quad enable bit for winbond flashes Tejas Bhumkar
                   ` (21 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Algapally Santosh Sagar, Ashok Reddy Soma

From: Algapally Santosh Sagar <santoshsagar.algapally@amd.com>

Add support for Winbond 256MB flash W25Q02NW which supports 4byte
opcodes and also dual and quad read.

Signed-off-by: Algapally Santosh Sagar <santoshsagar.algapally@amd.com>
Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-ids.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 0dbd385265..51e8cce407 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -517,6 +517,12 @@ const struct flash_info spi_nor_ids[] = {
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
 			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
 	},
+	{
+		INFO("w25q02nw", 0xef8022, 0, 64 * 1024, 4096,
+		     SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ |
+			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES |
+			SPI_NOR_MULTI_DIE)
+	},
 	{ INFO("w25q80", 0xef5014, 0, 64 * 1024,  16, SECT_4K) },
 	{ INFO("w25q80bl", 0xef4014, 0, 64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("w25q16cl", 0xef4015, 0, 64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 10/30] mtd: spi-nor: program quad enable bit for winbond flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (8 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 09/30] mtd: spi-nor-ids: Add support for W25Q02NW Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 11/30] mtd: spi-nor: Send write disable cmd after every write enable Tejas Bhumkar
                   ` (20 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

Added support to program quad enable bit for Winbond flash memory.
Previously, the quad enable function from Spansion was used for
this purpose. However, for Winbond flash memory, the quad
enable bit is configured by programming the Write Status Register-2
(SR-2) rather than the Configuration Register (CR).

Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Co-developed-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 48 ++++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |  2 ++
 2 files changed, 50 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index ace5da9591..454ae6cd4e 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2141,6 +2141,49 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
 	return 0;
 }
 
+/**
+ * winbond_quad_enable() - Set QE bit in status register-2
+ * @nor:	pointer to a 'struct spi_nor'
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int winbond_quad_enable(struct spi_nor *nor)
+{
+	int ret;
+	u8 cr = 0;
+
+	/* Check current Quad Enable bit value. */
+	cr = read_cr(nor);
+	if (cr < 0) {
+		dev_dbg(nor->dev,
+			"error while reading configuration register\n");
+		return -EINVAL;
+	}
+
+	if (cr & SR2_QUAD_EN_BIT1)
+		return 0;
+
+	cr |= SR2_QUAD_EN_BIT1;
+
+	write_enable(nor);
+
+	ret = nor->write_reg(nor, SPINOR_OP_WIN_WRSR2, &cr, 1);
+	if (ret < 0) {
+		dev_dbg(nor->dev,
+			"error while writing configuration register\n");
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret) {
+		dev_dbg(nor->dev,
+			"timeout while writing configuration register\n");
+		return ret;
+	}
+
+	return write_disable(nor);
+}
+
 /**
  * spansion_read_cr_quad_enable() - set QE bit in Configuration Register.
  * @nor:	pointer to a 'struct spi_nor'
@@ -3052,6 +3095,11 @@ static int spi_nor_init_params(struct spi_nor *nor,
 		case SNOR_MFR_MICRON:
 			break;
 
+#if defined(CONFIG_SPI_FLASH_WINBOND)
+		case SNOR_MFR_WINBOND:
+			params->quad_enable = winbond_quad_enable;
+			break;
+#endif
 		default:
 #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
 			/* Kept only for backward compatibility purpose. */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 72206f51ad..34e0aedc24 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -48,6 +48,7 @@
 #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
 #define SPINOR_OP_RDSR2		0x3f	/* Read status register 2 */
 #define SPINOR_OP_WRSR2		0x3e	/* Write status register 2 */
+#define SPINOR_OP_WIN_WRSR2	0x31	/* Winbond Write status register 2 */
 #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
 #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
 #define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
@@ -187,6 +188,7 @@
 
 /* Status Register 2 bits. */
 #define SR2_QUAD_EN_BIT7	BIT(7)
+#define SR2_QUAD_EN_BIT1	BIT(1)	/* Winbond Quad I/O */
 
 /*
  * Maximum number of flashes that can be connected
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 11/30] mtd: spi-nor: Send write disable cmd after every write enable
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (9 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 10/30] mtd: spi-nor: program quad enable bit for winbond flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 12/30] mtd: spi-nor: Update erase operation function Tejas Bhumkar
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma, Amit Kumar Mahapatra

From: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>

Write enable(06h) command will be sent to a flash device to
set the write enable latch bit before every program, erase,
write command. After that write disable command (04h) needs
to be sent to clear the write enable latch.

This write_disable() is missing at the majority of the places
in the driver, add it to clear write enable latch.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
Acked-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@xilinx.com>
---
 drivers/mtd/spi/spi-nor-core.c | 47 +++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 454ae6cd4e..d790116994 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -873,6 +873,7 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
 static int clean_bar(struct spi_nor *nor)
 {
 	u8 cmd, bank_sel = 0;
+	int ret;
 
 	if (nor->bank_curr == 0)
 		return 0;
@@ -880,7 +881,11 @@ static int clean_bar(struct spi_nor *nor)
 	nor->bank_curr = 0;
 	write_enable(nor);
 
-	return nor->write_reg(nor, cmd, &bank_sel, 1);
+	ret = nor->write_reg(nor, cmd, &bank_sel, 1);
+	if (ret)
+		return ret;
+
+	return write_disable(nor);
 }
 
 static int write_bar(struct spi_nor *nor, u32 offset)
@@ -1070,11 +1075,15 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 				nor->spi->flags &= ~SPI_XFER_U_PAGE;
 			}
 		}
+		if (nor->addr_width == 3) {
 #ifdef CONFIG_SPI_FLASH_BAR
-		ret = write_bar(nor, addr);
-		if (ret < 0)
-			goto erase_err;
+		/* Update Extended Address Register */
+			ret = write_bar(nor, addr);
+			if (ret < 0)
+				goto erase_err;
 #endif
+		}
+
 		ret = write_enable(nor);
 		if (ret < 0)
 			goto erase_err;
@@ -1195,6 +1204,10 @@ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
 	if (ret)
 		return ret;
 
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
 	ret = read_sr(nor);
 	if (ret < 0)
 		return ret;
@@ -1758,13 +1771,18 @@ static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lo
 	if (ctl == SST26_CTL_CHECK)
 		return 0;
 
+	/* Write latch enable before write operation */
+	ret = write_enable(nor);
+	if (ret)
+		return ret;
+
 	ret = nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size);
 	if (ret < 0) {
 		dev_err(nor->dev, "fail to write block-protection register\n");
 		return ret;
 	}
 
-	return 0;
+	return write_disable(nor);
 }
 
 static int sst26_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
@@ -2138,7 +2156,7 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
 		return ret;
 	}
 
-	return 0;
+	return write_disable(nor);
 }
 
 /**
@@ -4248,6 +4266,7 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor)
 
 static int spi_nor_init(struct spi_nor *nor)
 {
+	u8 sr2;
 	int err;
 
 	if (nor->flags & SNOR_F_HAS_PARALLEL)
@@ -4271,6 +4290,22 @@ static int spi_nor_init(struct spi_nor *nor)
 		write_enable(nor);
 		write_sr(nor, 0);
 		spi_nor_wait_till_ready(nor);
+
+		if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
+			write_enable(nor);
+			sr2 = 0;
+			err = nor->write_reg(nor,
+					     SPINOR_OP_WIN_WRSR2, &sr2, 1);
+			if (err < 0) {
+				dev_dbg(nor->dev,
+					"error while writing SR-2 register\n");
+				return -EINVAL;
+			}
+			spi_nor_wait_till_ready(nor);
+		}
+		err = write_disable(nor);
+		if (err)
+			return err;
 	}
 
 	if (nor->quad_enable) {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 12/30] mtd: spi-nor: Update erase operation function
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (10 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 11/30] mtd: spi-nor: Send write disable cmd after every write enable Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 13/30] mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled Tejas Bhumkar
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git

If the system is in a dual parallel configuration, it's necessary to
halve the erase size since the erase command operates on two flashes
simultaneously. When dealing with a dual-stacked configuration,
determine whether the erase offset refers to the top or bottom flash,
and subsequently, adjust the flag for the relevant flash.
Consequently, the argument for the spi_nor_erase_sector function has
been modified from addr to offset.

Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index d790116994..43435e79cc 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -1078,7 +1078,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 		if (nor->addr_width == 3) {
 #ifdef CONFIG_SPI_FLASH_BAR
 		/* Update Extended Address Register */
-			ret = write_bar(nor, addr);
+			ret = write_bar(nor, offset);
 			if (ret < 0)
 				goto erase_err;
 #endif
@@ -1092,7 +1092,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 		    !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
 			ret = spi_nor_erase_chip(nor);
 		} else {
-			ret = spi_nor_erase_sector(nor, addr);
+			ret = spi_nor_erase_sector(nor, offset);
 		}
 		if (ret < 0)
 			goto erase_err;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 13/30] mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (11 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 12/30] mtd: spi-nor: Update erase operation function Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 14/30] spi: cadence_qspi: Setup ddr mode in cadence qspi driver Tejas Bhumkar
                   ` (17 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

With 'commit bebdc237507c ("mtd: spi-nor: Parse SFDP SCCR Map")', support
for spi_nor_parse_sccr is added under SFDP. But the flag
SNOR_F_IO_MODE_EN_VOLATILE in spi_nor_octal_dtr_enable is always
checked. Check this flag only if SPI_FLASH_SFDP_SUPPORT enabled.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 43435e79cc..ccda722df5 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -4252,8 +4252,9 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor)
 	      nor->write_proto == SNOR_PROTO_8_8_8_DTR))
 		return 0;
 
-	if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE))
-		return 0;
+	if (CONFIG_IS_ENABLED(SPI_FLASH_SFDP_SUPPORT))
+		if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE))
+			return 0;
 
 	ret = nor->octal_dtr_enable(nor);
 	if (ret)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 14/30] spi: cadence_qspi: Setup ddr mode in cadence qspi driver
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (12 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 13/30] mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 15/30] spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config Tejas Bhumkar
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy, Ashok Reddy Soma

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

To switch the OSPI controller from SDR to DDR mode, the
RX delay should be configured through flash tuning. Begin
by establishing a constant value for the TX delay and then
increase the RX delay by inspecting the flash IDs. To fine-tune
the RX delay, compare the flash IDs with a set of predetermined
"golden" flash IDs. This tuning process is borrowed from the
Linux Cadence driver. Moreover, make the necessary adjustments
in the DDR PHY setup to facilitate the transition from SDR
to DDR mode within the OSPI controller.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_ospi_versal.c |  25 +++
 drivers/spi/cadence_qspi.c        | 321 +++++++++++++++++++++++++++++-
 drivers/spi/cadence_qspi.h        |  52 +++++
 drivers/spi/cadence_qspi_apb.c    |  33 ++-
 4 files changed, 416 insertions(+), 15 deletions(-)

diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index e02a3b3de3..74994a690d 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -15,6 +15,7 @@
 #include <cpu_func.h>
 #include <zynqmp_firmware.h>
 #include <asm/arch/hardware.h>
+#include <soc.h>
 #include "cadence_qspi.h"
 #include <dt-bindings/power/xlnx-versal-power.h>
 
@@ -129,6 +130,30 @@ int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv)
 	return 0;
 }
 
+static const struct soc_attr matches[] = {
+	{ .family = "Versal", .revision = "v2" },
+	{ }
+};
+
+/*
+ * cadence_qspi_versal_set_dll_mode checks for silicon version
+ * and set the DLL mode.
+ * Returns 0 in case of success, -ENOTSUPP in case of failure.
+ */
+int cadence_qspi_versal_set_dll_mode(struct udevice *dev)
+{
+	struct cadence_spi_priv *priv = dev_get_priv(dev);
+	const struct soc_attr *attr;
+
+	attr = soc_device_match(matches);
+	if (attr) {
+		priv->dll_mode = CQSPI_DLL_MODE_MASTER;
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
 #if defined(CONFIG_DM_GPIO)
 int cadence_qspi_versal_flash_reset(struct udevice *dev)
 {
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index cc3a54f295..32f91825fd 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -89,11 +89,21 @@ static int spi_calibration(struct udevice *bus, uint hz)
 	/* Enable QSPI */
 	cadence_qspi_apb_controller_enable(base);
 
-	/* read the ID which will be our golden value */
-	err = cadence_spi_read_id(priv, 3, (u8 *)&idcode);
-	if (err) {
-		puts("SF: Calibration failed (read)\n");
-		return err;
+	if (!priv->ddr_init) {
+		/* read the ID which will be our golden value */
+		err = cadence_spi_read_id(priv, CQSPI_READ_ID_LEN, (u8 *)&idcode);
+		if (err) {
+			puts("SF: Calibration failed (read)\n");
+			return err;
+		}
+
+		/* Save flash id's for further use */
+		priv->device_id[0] = (u8)idcode;
+		priv->device_id[1] = (u8)(idcode >> 8);
+		priv->device_id[2] = (u8)(idcode >> 16);
+	} else {
+		idcode = priv->device_id[0] | priv->device_id[1] << 8 |
+			 priv->device_id[2] << 16;
 	}
 
 	/* use back the intended clock and find low range */
@@ -109,7 +119,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
 		cadence_qspi_apb_controller_enable(base);
 
 		/* issue a RDID to get the ID value */
-		err = cadence_spi_read_id(priv, 3, (u8 *)&temp);
+		err = cadence_spi_read_id(priv, CQSPI_READ_ID_LEN, (u8 *)&temp);
 		if (err) {
 			puts("SF: Calibration failed (read)\n");
 			return err;
@@ -190,6 +200,20 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz)
 	return 0;
 }
 
+static int cadence_spi_child_pre_probe(struct udevice *bus)
+{
+	struct spi_slave *slave = dev_get_parent_priv(bus);
+
+	slave->bytemode = SPI_4BYTE_MODE;
+
+	return 0;
+}
+
+__weak int cadence_qspi_versal_set_dll_mode(struct udevice *dev)
+{
+	return -ENOTSUPP;
+}
+
 static int cadence_spi_probe(struct udevice *bus)
 {
 	struct cadence_spi_plat *plat = dev_get_plat(bus);
@@ -247,6 +271,14 @@ static int cadence_spi_probe(struct udevice *bus)
 		priv->qspi_is_init = 1;
 	}
 
+	priv->edge_mode = CQSPI_EDGE_MODE_SDR;
+	priv->dll_mode = CQSPI_DLL_MODE_BYPASS;
+
+	/* Select dll mode */
+	ret = cadence_qspi_versal_set_dll_mode(bus);
+	if (ret == -ENOTSUPP)
+		debug("DLL mode set to bypass mode : %x\n", ret);
+
 	priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz);
 
 	/* Versal and Versal-NET use spi calibration to set read delay */
@@ -290,6 +322,279 @@ static int cadence_spi_set_mode(struct udevice *bus, uint mode)
 	return 0;
 }
 
+static int cadence_qspi_rx_dll_tuning(struct cadence_spi_priv *priv,
+				      u32 txtap, u8 extra_dummy)
+{
+	void *regbase = priv->regbase;
+	int ret, i, j;
+	u8 id[CQSPI_READ_ID_LEN + 1], min_rxtap = 0, max_rxtap = 0, avg_rxtap,
+	max_tap, windowsize, dummy_flag = 0, max_index = 0, min_index = 0;
+	s8 max_windowsize = -1;
+	bool id_matched, rxtapfound = false;
+	struct spi_mem_op op =
+		SPI_MEM_OP(SPI_MEM_OP_CMD(CQSPI_READ_ID | (CQSPI_READ_ID << 8), 8),
+			   SPI_MEM_OP_NO_ADDR,
+			   SPI_MEM_OP_DUMMY(8, 8),
+			   SPI_MEM_OP_DATA_IN(CQSPI_READ_ID_LEN, id, 8));
+
+	op.cmd.nbytes = 2;
+	op.dummy.nbytes *= 2;
+	op.cmd.dtr = true;
+	op.addr.dtr = true;
+	op.data.dtr = true;
+
+	max_tap = CQSPI_MAX_DLL_TAPS;
+	/*
+	 * Rx dll tuning is done by setting tx delay and increment rx
+	 * delay and check for correct flash id's by reading from flash.
+	 */
+	for (i = 0; i <= max_tap; i++) {
+		/* Set DLL reset bit */
+		writel((txtap | i | CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK),
+		       regbase + CQSPI_REG_PHY_CONFIG);
+		/*
+		 * Re-synchronisation delay lines to update them
+		 * with values from TX DLL Delay and RX DLL Delay fields
+		 */
+		writel((CQSPI_REG_PHY_CONFIG_RESYNC_FLD_MASK | txtap | i |
+		       CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK),
+		       regbase + CQSPI_REG_PHY_CONFIG);
+		/* Check lock of loopback */
+		if (priv->dll_mode == CQSPI_DLL_MODE_MASTER) {
+			ret = wait_for_bit_le32
+				(regbase + CQSPI_REG_DLL_LOWER,
+				 CQSPI_REG_DLL_LOWER_LPBK_LOCK_MASK, 1,
+				 CQSPI_TIMEOUT_MS, 0);
+			if (ret) {
+				printf("LOWER_DLL_LOCK bit err: %i\n", ret);
+				return ret;
+			}
+		}
+
+		ret = cadence_qspi_apb_command_read_setup(priv, &op);
+		if (!ret) {
+			ret = cadence_qspi_apb_command_read(priv, &op);
+			if (ret < 0) {
+				printf("error %d reading JEDEC ID\n", ret);
+				return ret;
+			}
+		}
+
+		id_matched = true;
+		for (j = 0; j < CQSPI_READ_ID_LEN; j++) {
+			if (priv->device_id[j] != id[j]) {
+				id_matched = false;
+				break;
+			}
+		}
+
+		if (id_matched && !rxtapfound) {
+			if (priv->dll_mode == CQSPI_DLL_MODE_MASTER) {
+				min_rxtap =
+				readl(regbase + CQSPI_REG_DLL_OBSVBLE_UPPER) &
+				      CQSPI_REG_DLL_UPPER_RX_FLD_MASK;
+				max_rxtap = min_rxtap;
+				max_index = i;
+				min_index = i;
+			} else {
+				min_rxtap = i;
+				max_rxtap = i;
+			}
+			rxtapfound = true;
+		}
+
+		if (id_matched && rxtapfound) {
+			if (priv->dll_mode == CQSPI_DLL_MODE_MASTER) {
+				max_rxtap =
+					readl(regbase +
+					      CQSPI_REG_DLL_OBSVBLE_UPPER) &
+					      CQSPI_REG_DLL_UPPER_RX_FLD_MASK;
+				max_index = i;
+			} else {
+				max_rxtap = i;
+			}
+		}
+
+		if ((!id_matched || i == max_tap) && rxtapfound) {
+			windowsize = max_rxtap - min_rxtap + 1;
+			if (windowsize > max_windowsize) {
+				dummy_flag = extra_dummy;
+				max_windowsize = windowsize;
+				if (priv->dll_mode == CQSPI_DLL_MODE_MASTER)
+					avg_rxtap = (max_index + min_index);
+				else
+					avg_rxtap = (max_rxtap + min_rxtap);
+				avg_rxtap /= 2;
+			}
+
+			if (windowsize >= 3)
+				i = max_tap;
+
+			rxtapfound = false;
+		}
+	}
+
+	if (!extra_dummy) {
+		rxtapfound = false;
+		min_rxtap = 0;
+		max_rxtap = 0;
+	}
+
+	if (!dummy_flag)
+		priv->extra_dummy = false;
+
+	if (max_windowsize < 3)
+		return -EINVAL;
+
+	return avg_rxtap;
+}
+
+static int cadence_spi_setdlldelay(struct udevice *bus)
+{
+	struct cadence_spi_priv *priv = dev_get_priv(bus);
+	void *regbase = priv->regbase;
+	u32 txtap;
+	int ret, rxtap;
+	u8 extra_dummy;
+
+	ret = wait_for_bit_le32(regbase + CQSPI_REG_CONFIG,
+				1 << CQSPI_REG_CONFIG_IDLE_LSB,
+				1, CQSPI_TIMEOUT_MS, 0);
+	if (ret) {
+		printf("spi_wait_idle error : 0x%x\n", ret);
+		return ret;
+	}
+
+	if (priv->dll_mode == CQSPI_DLL_MODE_MASTER) {
+		/* Drive DLL reset bit to low */
+		writel(0, regbase + CQSPI_REG_PHY_CONFIG);
+
+		/* Set initial delay value */
+		writel(CQSPI_REG_PHY_INITIAL_DLY,
+		       regbase + CQSPI_REG_PHY_MASTER_CTRL);
+		/* Set DLL reset bit */
+		writel(CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK,
+		       regbase + CQSPI_REG_PHY_CONFIG);
+
+		/* Check for loopback lock */
+		ret = wait_for_bit_le32(regbase + CQSPI_REG_DLL_LOWER,
+					CQSPI_REG_DLL_LOWER_LPBK_LOCK_MASK,
+					1, CQSPI_TIMEOUT_MS, 0);
+		if (ret) {
+			printf("Loopback lock bit error (%i)\n", ret);
+			return ret;
+		}
+
+		/* Re-synchronize slave DLLs */
+		writel(CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK,
+		       regbase + CQSPI_REG_PHY_CONFIG);
+		writel(CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK |
+		       CQSPI_REG_PHY_CONFIG_RESYNC_FLD_MASK,
+		       regbase + CQSPI_REG_PHY_CONFIG);
+
+		txtap = CQSPI_TX_TAP_MASTER <<
+			CQSPI_REG_PHY_CONFIG_TX_DLL_DLY_LSB;
+	}
+
+	priv->extra_dummy = false;
+	for (extra_dummy = 0; extra_dummy <= 1; extra_dummy++) {
+		if (extra_dummy)
+			priv->extra_dummy = true;
+
+		rxtap = cadence_qspi_rx_dll_tuning(priv, txtap, extra_dummy);
+		if (extra_dummy && rxtap < 0) {
+			printf("Failed RX dll tuning\n");
+			return rxtap;
+		}
+	}
+	debug("RXTAP: %d\n", rxtap);
+
+	writel((txtap | rxtap | CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK),
+	       regbase + CQSPI_REG_PHY_CONFIG);
+	writel((CQSPI_REG_PHY_CONFIG_RESYNC_FLD_MASK | txtap | rxtap |
+	       CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK),
+	       regbase + CQSPI_REG_PHY_CONFIG);
+
+	if (priv->dll_mode == CQSPI_DLL_MODE_MASTER) {
+		ret = wait_for_bit_le32(regbase + CQSPI_REG_DLL_LOWER,
+					CQSPI_REG_DLL_LOWER_LPBK_LOCK_MASK,
+					1, CQSPI_TIMEOUT_MS, 0);
+		if (ret) {
+			printf("LOWER_DLL_LOCK bit err: %i\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int priv_setup_ddrmode(struct udevice *bus)
+{
+	struct cadence_spi_priv *priv = dev_get_priv(bus);
+	void *regbase = priv->regbase;
+	int ret;
+
+	ret = wait_for_bit_le32(regbase + CQSPI_REG_CONFIG,
+				1 << CQSPI_REG_CONFIG_IDLE_LSB,
+				1, CQSPI_TIMEOUT_MS, 0);
+	if (ret) {
+		printf("spi_wait_idle error : 0x%x\n", ret);
+		return ret;
+	}
+
+	/* Disable QSPI */
+	cadence_qspi_apb_controller_disable(regbase);
+
+	/* Disable DAC mode */
+	if (priv->use_dac_mode) {
+		clrbits_le32(regbase + CQSPI_REG_CONFIG,
+			     CQSPI_REG_CONFIG_DIRECT);
+		priv->use_dac_mode = false;
+	}
+
+	setbits_le32(regbase + CQSPI_REG_CONFIG,
+		     CQSPI_REG_CONFIG_PHY_ENABLE_MASK);
+
+	/* Program POLL_CNT */
+	clrsetbits_le32(regbase + CQSPI_REG_WRCOMPLETION,
+			CQSPI_REG_WRCOMPLETION_POLLCNT_MASK,
+			CQSPI_REG_WRCOMPLETION_POLLCNT <<
+			CQSPI_REG_WRCOMPLETION_POLLCNY_LSB);
+
+	setbits_le32(regbase + CQSPI_REG_CONFIG,
+		     CQSPI_REG_CONFIG_DTR_PROT_EN_MASK);
+
+	clrsetbits_le32(regbase + CQSPI_REG_RD_DATA_CAPTURE,
+			(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK <<
+			 CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB),
+			CQSPI_REG_READCAPTURE_DQS_ENABLE);
+
+	/* Enable QSPI */
+	cadence_qspi_apb_controller_enable(regbase);
+
+	return 0;
+}
+
+static int cadence_spi_setup_ddrmode(struct udevice *bus)
+{
+	struct cadence_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+
+	ret = priv_setup_ddrmode(bus);
+	if (ret)
+		return ret;
+
+	priv->edge_mode = CQSPI_EDGE_MODE_DDR;
+	ret = cadence_spi_setdlldelay(bus);
+	if (ret) {
+		printf("DDR tuning failed with error %d\n", ret);
+		return ret;
+	}
+	priv->ddr_init = 1;
+
+	return 0;
+}
+
 static int cadence_spi_mem_exec_op(struct spi_slave *spi,
 				   const struct spi_mem_op *op)
 {
@@ -350,6 +655,9 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
 		break;
 	}
 
+	if (!priv->ddr_init && (spi->flags & SPI_XFER_SET_DDR))
+		err = cadence_spi_setup_ddrmode(bus);
+
 	return err;
 }
 
@@ -466,6 +774,7 @@ U_BOOT_DRIVER(cadence_spi) = {
 	.plat_auto	= sizeof(struct cadence_spi_plat),
 	.priv_auto	= sizeof(struct cadence_spi_priv),
 	.probe = cadence_spi_probe,
+	.child_pre_probe = cadence_spi_child_pre_probe,
 	.remove = cadence_spi_remove,
 	.flags = DM_FLAG_OS_PREPARE,
 };
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 1c59d1a9d9..eec090a7ed 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -10,6 +10,7 @@
 #include <reset.h>
 #include <linux/mtd/spi-nor.h>
 #include <spi-mem.h>
+#include <wait_bit.h>
 
 #define CQSPI_IS_ADDR(cmd_len)		(cmd_len > 1 ? 1 : 0)
 
@@ -21,6 +22,8 @@
 #define CQSPI_REG_RETRY                         10000
 #define CQSPI_POLL_IDLE_RETRY                   3
 
+#define CQSPI_TIMEOUT_MS			1000
+
 /* Transfer mode */
 #define CQSPI_INST_TYPE_SINGLE                  0
 #define CQSPI_INST_TYPE_DUAL                    1
@@ -33,6 +36,23 @@
 #define CQSPI_DUMMY_BYTES_MAX                   4
 #define CQSPI_DUMMY_CLKS_MAX                    31
 
+#define CQSPI_TX_TAP_MASTER			0x1E
+#define CQSPI_MAX_DLL_TAPS			127
+
+#define CQSPI_DLL_MODE_MASTER			0
+#define CQSPI_DLL_MODE_BYPASS			1
+
+#define CQSPI_EDGE_MODE_SDR			0
+#define CQSPI_EDGE_MODE_DDR			1
+
+#define SILICON_VER_MASK			0xFF
+#define SILICON_VER_1				0x10
+
+#define CQSPI_READ_ID				0x9F
+#define CQSPI_READ_ID_LEN			3
+#define CQSPI_READID_LOOP_MAX			10
+#define TERA_MACRO				1000000000000l
+
 /****************************************************************************
  * Controller's configuration and status register (offset from QSPI_BASE)
  ****************************************************************************/
@@ -65,6 +85,7 @@
 #define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK       0x3
 #define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK       0x3
 #define CQSPI_REG_RD_INSTR_DUMMY_MASK           0x1F
+#define CQSPI_REG_RD_INSTR_DDR_ENABLE		BIT(10)
 
 #define CQSPI_REG_WR_INSTR                      0x08
 #define CQSPI_REG_WR_INSTR_OPCODE_LSB           0
@@ -109,9 +130,14 @@
 
 #define CQSPI_REG_WR_COMPLETION_CTRL		0x38
 #define CQSPI_REG_WR_DISABLE_AUTO_POLL		BIT(14)
+#define CQSPI_REG_WRCOMPLETION			0x38
+#define CQSPI_REG_WRCOMPLETION_POLLCNT_MASK	0xFF0000
+#define CQSPI_REG_WRCOMPLETION_POLLCNY_LSB	16
+#define CQSPI_REG_WRCOMPLETION_POLLCNT		3
 
 #define CQSPI_REG_IRQSTATUS                     0x40
 #define CQSPI_REG_IRQMASK                       0x44
+#define CQSPI_REG_ECO				0x48
 
 #define CQSPI_REG_INDIRECTRD                    0x60
 #define CQSPI_REG_INDIRECTRD_START              BIT(0)
@@ -162,7 +188,31 @@
 #define CQSPI_REG_OP_EXT_STIG_LSB               0
 
 #define CQSPI_REG_PHY_CONFIG                    0xB4
+#define CQSPI_REG_PHY_CONFIG_RESYNC_FLD_MASK	0x80000000
 #define CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK     0x40000000
+#define CQSPI_REG_PHY_CONFIG_TX_DLL_DLY_LSB	16
+
+#define CQSPI_REG_PHY_MASTER_CTRL              0xB8
+#define CQSPI_REG_PHY_INITIAL_DLY              0x4
+#define CQSPI_REG_DLL_LOWER                    0xBC
+#define CQSPI_REG_DLL_LOWER_LPBK_LOCK_MASK     0x8000
+#define CQSPI_REG_DLL_LOWER_DLL_LOCK_MASK      0x1
+
+#define CQSPI_REG_DLL_OBSVBLE_UPPER		0xC0
+#define CQSPI_REG_DLL_UPPER_RX_FLD_MASK		0x7F
+
+#define CQSPI_REG_EXT_OP_LOWER			0xE0
+#define CQSPI_REG_EXT_STIG_OP_MASK		0xFF
+#define CQSPI_REG_EXT_READ_OP_MASK		0xFF000000
+#define CQSPI_REG_EXT_READ_OP_SHIFT		24
+#define CQSPI_REG_EXT_WRITE_OP_MASK		0xFF0000
+#define CQSPI_REG_EXT_WRITE_OP_SHIFT		16
+#define CQSPI_REG_DMA_SRC_ADDR			0x1000
+#define CQSPI_REG_DMA_DST_ADDR			0x1800
+#define CQSPI_REG_DMA_DST_SIZE			0x1804
+#define CQSPI_REG_DMA_DST_STS			0x1808
+#define CQSPI_REG_DMA_DST_CTRL			0x180C
+#define CQSPI_REG_DMA_DST_CTRL_VAL		0xF43FFA00
 
 #define CQSPI_DMA_DST_ADDR_REG                  0x1800
 #define CQSPI_DMA_DST_SIZE_REG                  0x1804
@@ -247,6 +297,7 @@ struct cadence_spi_priv {
 	u32		tsd2d_ns;
 	u32		tchsh_ns;
 	u32		tslch_ns;
+	u8              device_id[CQSPI_READ_ID_LEN];
 	u8              edge_mode;
 	u8              dll_mode;
 	bool		extra_dummy;
@@ -304,6 +355,7 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv);
 int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg);
 int cadence_qspi_versal_flash_reset(struct udevice *dev);
+int cadence_qspi_versal_set_dll_mode(struct udevice *dev);
 void cadence_qspi_apb_enable_linear_mode(bool enable);
 
 #endif /* __CADENCE_QSPI_H__ */
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 9ce2c0f254..d1a5a4c679 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -465,6 +465,9 @@ int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
 	unsigned int dummy_clk;
 	u8 opcode;
 
+	if (priv->dtr)
+		rxlen = ((rxlen % 2) != 0) ? (rxlen + 1) : rxlen;
+
 	if (priv->dtr)
 		opcode = op->cmd.opcode >> 8;
 	else
@@ -477,6 +480,9 @@ int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
 	if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
 		return -ENOTSUPP;
 
+	if (priv->extra_dummy)
+		dummy_clk++;
+
 	if (dummy_clk)
 		reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
 		     << CQSPI_REG_CMDCTRL_DUMMY_LSB;
@@ -553,6 +559,9 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
 	void *reg_base = priv->regbase;
 	u8 opcode;
 
+	reg = cadence_qspi_calc_rdreg(priv);
+	writel(reg, reg_base + CQSPI_REG_RD_INSTR);
+
 	if (priv->dtr)
 		opcode = op->cmd.opcode >> 8;
 	else
@@ -609,7 +618,6 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
 int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
 				const struct spi_mem_op *op)
 {
-	unsigned int reg;
 	unsigned int rd_reg;
 	unsigned int dummy_clk;
 	unsigned int dummy_bytes = op->dummy.nbytes;
@@ -647,6 +655,9 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
 		if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
 			return -ENOTSUPP;
 
+		if (priv->extra_dummy)
+			dummy_clk++;
+
 		if (dummy_clk)
 			rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
 				<< CQSPI_REG_RD_INSTR_DUMMY_LSB;
@@ -655,10 +666,10 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
 	writel(rd_reg, priv->regbase + CQSPI_REG_RD_INSTR);
 
 	/* set device size */
-	reg = readl(priv->regbase + CQSPI_REG_SIZE);
-	reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
-	reg |= (op->addr.nbytes - 1);
-	writel(reg, priv->regbase + CQSPI_REG_SIZE);
+	clrsetbits_le32(priv->regbase + CQSPI_REG_SIZE,
+			CQSPI_REG_SIZE_ADDRESS_MASK,
+			op->addr.nbytes - 1);
+
 	return 0;
 }
 
@@ -828,10 +839,10 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
 		writel(reg, priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
 	}
 
-	reg = readl(priv->regbase + CQSPI_REG_SIZE);
-	reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
-	reg |= (op->addr.nbytes - 1);
-	writel(reg, priv->regbase + CQSPI_REG_SIZE);
+	clrsetbits_le32(priv->regbase + CQSPI_REG_SIZE,
+			CQSPI_REG_SIZE_ADDRESS_MASK,
+			op->addr.nbytes - 1);
+
 	return 0;
 }
 
@@ -846,6 +857,10 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 	unsigned int write_bytes;
 	int ret;
 
+	if (priv->edge_mode == CQSPI_EDGE_MODE_DDR && (n_tx % 2) != 0)
+		n_tx++;
+	remaining = n_tx;
+
 	/*
 	 * Use bounce buffer for non 32 bit aligned txbuf to avoid data
 	 * aborts
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 15/30] spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (13 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 14/30] spi: cadence_qspi: Setup ddr mode in cadence qspi driver Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 16/30] spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal driver Tejas Bhumkar
                   ` (15 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

From: T Karthik Reddy <t.karthik.reddy@amd.com>

Cadence driver need to switch from SDR to DTR and vice versa based on
the commands from spi-nor framework and based on SPI_FLASH_DTR_ENABLE
config. If SPI_FLASH_DTR_ENABLE is not defined, do not switch to DTR
mode as it represents that controller does not support DTR. Also do
not reinitilize the SDR/DTR tuning if it is already done.
Also added the functionality to reset the controller when switching
from DTR to STR mode.
Use reset_assert, reset_deassert api's to reset the ospi controller.
In mini U-Boot case as ZYNQMP_FIRMWARE config is disabled, reset the
controller directly using register writes.

The configuration of the chip select in the Cadence QSPI driver is
now determined based on the flags received from SPI-NOR framework.

Signed-off-by: T Karthik Reddy <t.karthik.reddy@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_ospi_versal.c | 29 +++++++++++++
 drivers/spi/cadence_qspi.c        | 69 ++++++++++++++++++++++++++++---
 drivers/spi/cadence_qspi.h        |  7 ++++
 3 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index 74994a690d..243de6efaf 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -154,6 +154,35 @@ int cadence_qspi_versal_set_dll_mode(struct udevice *dev)
 	return -ENOTSUPP;
 }
 
+int cadence_spi_versal_ctrl_reset(struct cadence_spi_priv *priv)
+{
+	int ret;
+
+	if (CONFIG_IS_ENABLED(ZYNQMP_FIRMWARE)) {
+		/* Assert ospi controller */
+		ret = reset_assert(priv->resets->resets);
+		if (ret)
+			return ret;
+
+		udelay(10);
+
+		/* Deassert ospi controller */
+		ret = reset_deassert(priv->resets->resets);
+		if (ret)
+			return ret;
+	} else {
+		/* Assert ospi controller */
+		setbits_le32((u32 *)OSPI_CTRL_RST, 1);
+
+		udelay(10);
+
+		/* Deassert ospi controller */
+		clrbits_le32((u32 *)OSPI_CTRL_RST, 1);
+	}
+
+	return 0;
+}
+
 #if defined(CONFIG_DM_GPIO)
 int cadence_qspi_versal_flash_reset(struct udevice *dev)
 {
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 32f91825fd..710c4a532d 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -154,7 +154,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
 
 	/* just to ensure we do once only when speed or chip select change */
 	priv->qspi_calibrated_hz = hz;
-	priv->qspi_calibrated_cs = spi_chip_select(bus);
+	priv->qspi_calibrated_cs = priv->cs;
 
 	return 0;
 }
@@ -179,7 +179,7 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz)
 						  priv->read_delay);
 	} else if (priv->previous_hz != hz ||
 		   priv->qspi_calibrated_hz != hz ||
-		   priv->qspi_calibrated_cs != spi_chip_select(bus)) {
+		   priv->qspi_calibrated_cs != priv->cs) {
 		/*
 		 * Calibration required for different current SCLK speed,
 		 * requested SCLK speed or chip select
@@ -580,6 +580,9 @@ static int cadence_spi_setup_ddrmode(struct udevice *bus)
 	struct cadence_spi_priv *priv = dev_get_priv(bus);
 	int ret;
 
+	if (priv->ddr_init)
+		return 0;
+
 	ret = priv_setup_ddrmode(bus);
 	if (ret)
 		return ret;
@@ -590,7 +593,47 @@ static int cadence_spi_setup_ddrmode(struct udevice *bus)
 		printf("DDR tuning failed with error %d\n", ret);
 		return ret;
 	}
-	priv->ddr_init = 1;
+	priv->ddr_init = true;
+
+	return 0;
+}
+
+static int cadence_spi_setup_strmode(struct udevice *bus)
+{
+	struct cadence_spi_priv *priv = dev_get_priv(bus);
+	void *base = priv->regbase;
+	int ret;
+
+	if (!priv->ddr_init)
+		return 0;
+
+	/* Reset ospi controller */
+	ret = cadence_spi_versal_ctrl_reset(priv);
+	if (ret) {
+		printf("Cadence ctrl reset failed err: %d\n", ret);
+		return ret;
+	}
+
+	ret = wait_for_bit_le32(base + CQSPI_REG_CONFIG,
+				BIT(CQSPI_REG_CONFIG_IDLE_LSB),
+				1, CQSPI_TIMEOUT_MS, 0);
+	if (ret) {
+		printf("spi_wait_idle error : 0x%x\n", ret);
+		return ret;
+	}
+
+	cadence_qspi_apb_controller_init(priv);
+	priv->edge_mode = CQSPI_EDGE_MODE_SDR;
+	priv->extra_dummy = 0;
+	priv->previous_hz = 0;
+	priv->qspi_calibrated_hz = 0;
+
+	/* Setup default speed and calibrate */
+	ret = cadence_spi_set_speed(bus, 0);
+	if (ret)
+		return ret;
+
+	priv->ddr_init = false;
 
 	return 0;
 }
@@ -604,9 +647,22 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
 	int err = 0;
 	u32 mode;
 
+	if (!op->cmd.dtr) {
+		err = cadence_spi_setup_strmode(bus);
+		if (err)
+			return err;
+	}
+
+	if (!CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE) && op->cmd.dtr)
+		return 0;
+
+	if (spi->flags & SPI_XFER_U_PAGE)
+		priv->cs = CQSPI_CS1;
+	else
+		priv->cs = CQSPI_CS0;
+
 	/* Set Chip select */
-	cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev),
-				    priv->is_decoded_cs);
+	cadence_qspi_apb_chipselect(base, priv->cs, priv->is_decoded_cs);
 
 	if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
 		/*
@@ -655,7 +711,8 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
 		break;
 	}
 
-	if (!priv->ddr_init && (spi->flags & SPI_XFER_SET_DDR))
+	if (CONFIG_IS_ENABLED(SPI_FLASH_DTR_ENABLE) &&
+	    (spi->flags & SPI_XFER_SET_DDR))
 		err = cadence_spi_setup_ddrmode(bus);
 
 	return err;
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index eec090a7ed..505f59bed9 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -18,6 +18,9 @@
 #define CQSPI_DECODER_MAX_CS		16
 #define CQSPI_READ_CAPTURE_MAX_DELAY	16
 
+#define CQSPI_CS0				0
+#define CQSPI_CS1				1
+
 #define CQSPI_REG_POLL_US                       1 /* 1us */
 #define CQSPI_REG_RETRY                         10000
 #define CQSPI_POLL_IDLE_RETRY                   3
@@ -53,6 +56,8 @@
 #define CQSPI_READID_LOOP_MAX			10
 #define TERA_MACRO				1000000000000l
 
+#define OSPI_CTRL_RST				0xF1260304
+
 /****************************************************************************
  * Controller's configuration and status register (offset from QSPI_BASE)
  ****************************************************************************/
@@ -284,6 +289,7 @@ struct cadence_spi_priv {
 	size_t		data_len;
 
 	int		qspi_is_init;
+	unsigned int    cs;
 	unsigned int	qspi_calibrated_hz;
 	unsigned int	qspi_calibrated_cs;
 	unsigned int	previous_hz;
@@ -357,5 +363,6 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg);
 int cadence_qspi_versal_flash_reset(struct udevice *dev);
 int cadence_qspi_versal_set_dll_mode(struct udevice *dev);
 void cadence_qspi_apb_enable_linear_mode(bool enable);
+int cadence_spi_versal_ctrl_reset(struct cadence_spi_priv *priv);
 
 #endif /* __CADENCE_QSPI_H__ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 16/30] spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal driver
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (14 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 15/30] spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 17/30] spi: cadence_qspi: Fix versal ospi indirect write timed out issue Tejas Bhumkar
                   ` (14 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

Set cmd, address & data buswidth to octal. Handle dummy clock
cycles incase of reads & writes. Convert odd bytes to even
bytes lengths in ddr mode, as we cannot rx/tx odd data in
ddr mode.

Disable the DMA once the transfer is done to avoid disabling
it at other places.

Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_ospi_versal.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index 243de6efaf..9db1911110 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -21,12 +21,13 @@
 
 #define CMD_4BYTE_READ  0x13
 #define CMD_4BYTE_FAST_READ  0x0C
+#define CMD_4BYTE_OCTAL_READ 0x7c
 
 int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 			      const struct spi_mem_op *op)
 {
 	u32 reg, ret, rx_rem, n_rx, bytes_to_dma, data;
-	u8 opcode, addr_bytes, *rxbuf, dummy_cycles;
+	u8 opcode, addr_bytes, *rxbuf, dummy_cycles, unaligned_byte;
 
 	n_rx = op->data.nbytes;
 	rxbuf = op->data.buf.in;
@@ -70,13 +71,14 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 		writel(CQSPI_REG_INDIRECTRD_DONE, priv->regbase +
 		       CQSPI_REG_INDIRECTRD);
 		rxbuf += bytes_to_dma;
-	}
 
-	if (rx_rem) {
+		/* Disable DMA on completion */
 		reg = readl(priv->regbase + CQSPI_REG_CONFIG);
 		reg &= ~CQSPI_REG_CONFIG_ENBL_DMA;
 		writel(reg, priv->regbase + CQSPI_REG_CONFIG);
+	}
 
+	if (rx_rem) {
 		reg = readl(priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
 		reg += bytes_to_dma;
 		writel(reg, priv->regbase + CQSPI_REG_CMDADDRESS);
@@ -84,10 +86,10 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 		addr_bytes = readl(priv->regbase + CQSPI_REG_SIZE) &
 				   CQSPI_REG_SIZE_ADDRESS_MASK;
 
-		opcode = CMD_4BYTE_FAST_READ;
-		dummy_cycles = 8;
-		writel((dummy_cycles << CQSPI_REG_RD_INSTR_DUMMY_LSB) | opcode,
-		       priv->regbase + CQSPI_REG_RD_INSTR);
+		opcode = (u8)readl(priv->regbase + CQSPI_REG_RD_INSTR);
+		if (opcode == CMD_4BYTE_OCTAL_READ &&
+		    priv->edge_mode != CQSPI_EDGE_MODE_DDR)
+			opcode = CMD_4BYTE_FAST_READ;
 
 		reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
 		reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
@@ -99,7 +101,12 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
 				CQSPI_REG_RD_INSTR_DUMMY_MASK;
 		reg |= (dummy_cycles & CQSPI_REG_CMDCTRL_DUMMY_MASK) <<
 			CQSPI_REG_CMDCTRL_DUMMY_LSB;
-		reg |= (((rx_rem - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) <<
+		if (priv->edge_mode == CQSPI_EDGE_MODE_DDR && (rx_rem % 2) != 0)
+			unaligned_byte = 1;
+		else
+			unaligned_byte = 0;
+		reg |= (((rx_rem - 1 + unaligned_byte) &
+			CQSPI_REG_CMDCTRL_RD_BYTES_MASK) <<
 			CQSPI_REG_CMDCTRL_RD_BYTES_LSB);
 		ret = cadence_qspi_apb_exec_flash_cmd(priv->regbase, reg);
 		if (ret)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 17/30] spi: cadence_qspi: Fix versal ospi indirect write timed out issue
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (15 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 16/30] spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal driver Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 18/30] spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns Tejas Bhumkar
                   ` (13 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git

To reduce the CPU load in waiting for the OSPI internal SRAM to
clear in indirect mode, it's better to use the
CQSPI_REG_IRQSTATUS register to check for indirect operation to complete.
Enabled interrupt for Indirect Complete and Transfer Watermark
Breach interrupt status register bits and using readl_poll_timeout
function to poll for Indirect Operation Complete bit gets set.

Here not enabling IRQ coming to GIC, only IRQ from IP itself is able
to poll bits.

It is observed that the Indirect Operation Complete bit is getting
set at an average time of 0.172 usec.

Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi.h     | 11 +++++++++++
 drivers/spi/cadence_qspi_apb.c | 24 +++++++++++++++++++-----
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 505f59bed9..73ef44fc1c 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -252,6 +252,17 @@
 	(((readl((reg_base) + CQSPI_REG_SDRAMLEVEL)) >>	\
 	CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK)
 
+/* Interrupt status bits */
+#define CQSPI_REG_IRQ_UNDERFLOW                 BIT(1)
+#define CQSPI_REG_IRQ_IND_COMP                  BIT(2)
+#define CQSPI_REG_IRQ_WATERMARK                 BIT(6)
+
+#define CQSPI_IRQ_MASK_WR			(CQSPI_REG_IRQ_IND_COMP        | \
+						CQSPI_REG_IRQ_WATERMARK        | \
+						CQSPI_REG_IRQ_UNDERFLOW)
+
+#define CQSPI_IRQ_STATUS_MASK			GENMASK(16, 0)
+
 struct cadence_spi_plat {
 	unsigned int	max_hz;
 	void		*regbase;
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index d1a5a4c679..1b649abf21 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -32,6 +32,7 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/iopoll.h>
 #include <wait_bit.h>
 #include <spi.h>
 #include <spi-mem.h>
@@ -855,7 +856,7 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 	const u8 *bb_txbuf = txbuf;
 	void *bounce_buf = NULL;
 	unsigned int write_bytes;
-	int ret;
+	int ret, cr;
 
 	if (priv->edge_mode == CQSPI_EDGE_MODE_DDR && (n_tx % 2) != 0)
 		n_tx++;
@@ -873,9 +874,15 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 		bb_txbuf = bounce_buf;
 	}
 
-	/* Configure the indirect read transfer bytes */
+	/* Configure the indirect write transfer bytes */
 	writel(n_tx, priv->regbase + CQSPI_REG_INDIRECTWRBYTES);
 
+	/* Clear all interrupts */
+	writel(CQSPI_IRQ_STATUS_MASK, priv->regbase + CQSPI_REG_IRQSTATUS);
+
+	/* Enable interrupt for corresponding interrupt status register bit's */
+	writel(CQSPI_IRQ_MASK_WR, priv->regbase + CQSPI_REG_IRQMASK);
+
 	/* Start the indirect write transfer */
 	writel(CQSPI_REG_INDIRECTWR_START,
 	       priv->regbase + CQSPI_REG_INDIRECTWR);
@@ -894,9 +901,10 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 				bb_txbuf + rounddown(write_bytes, 4),
 				write_bytes % 4);
 
-		ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_SDRAMLEVEL,
-					CQSPI_REG_SDRAMLEVEL_WR_MASK <<
-					CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
+		/* Wait up to Indirect Operation Complete bit to set */
+		ret = readl_poll_timeout(priv->regbase + CQSPI_REG_IRQSTATUS, cr,
+					 cr & CQSPI_REG_IRQ_IND_COMP, 10);
+
 		if (ret) {
 			printf("Indirect write timed out (%i)\n", ret);
 			goto failwr;
@@ -914,6 +922,9 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 		goto failwr;
 	}
 
+	/* Disable interrupt. */
+	writel(0, priv->regbase + CQSPI_REG_IRQMASK);
+
 	/* Clear indirect completion status */
 	writel(CQSPI_REG_INDIRECTWR_DONE,
 	       priv->regbase + CQSPI_REG_INDIRECTWR);
@@ -931,6 +942,9 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 	return 0;
 
 failwr:
+	/* Disable interrupt. */
+	writel(0, priv->regbase + CQSPI_REG_IRQMASK);
+
 	/* Cancel the indirect write */
 	writel(CQSPI_REG_INDIRECTWR_CANCEL,
 	       priv->regbase + CQSPI_REG_INDIRECTWR);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 18/30] spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (16 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 17/30] spi: cadence_qspi: Fix versal ospi indirect write timed out issue Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 19/30] spi: cadence_qspi: Clean up registers in init Tejas Bhumkar
                   ` (12 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

tshsl_ns is the clock delay for chip select deassert. This is the delay in
master reference clocks for the length that the master mode chip select
outputs are de-asserted between transactions.

The minimum delay is always SCLK period to ensure the chip select is never
re-asserted within one SCLK period.

That is why tshsl_ns delay should be at least one sclk_ns value. If it is
less than sclk_ns, set it equal to sclk_ns.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi_apb.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 1b649abf21..0bb46f6ac2 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -306,6 +306,10 @@ void cadence_qspi_apb_delay(void *reg_base,
 		tshsl_ns -= sclk_ns + ref_clk_ns;
 	if (tchsh_ns >= sclk_ns + 3 * ref_clk_ns)
 		tchsh_ns -= sclk_ns + 3 * ref_clk_ns;
+
+	if (tshsl_ns < sclk_ns)
+		tshsl_ns = sclk_ns;
+
 	tshsl = DIV_ROUND_UP(tshsl_ns, ref_clk_ns);
 	tchsh = DIV_ROUND_UP(tchsh_ns, ref_clk_ns);
 	tslch = DIV_ROUND_UP(tslch_ns, ref_clk_ns);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 19/30] spi: cadence_qspi: Clean up registers in init
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (17 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 18/30] spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 20/30] spi: cadence_qspi: Initialize read and write watermark registers Tejas Bhumkar
                   ` (11 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>

This patch cleans up the cadence qspi registers in the init.
The register contents may be invalid if this controller is
used in previous boot and comes to uboot after a softreset
(no power on reset). This may cause issues in uboot.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi_apb.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 0bb46f6ac2..5fc5279061 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -346,12 +346,34 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
 	/* Configure the remap address register, no remap */
 	writel(0, priv->regbase + CQSPI_REG_REMAP);
 
+	/* Clear instruction read config register */
+	writel(0, priv->regbase + CQSPI_REG_RD_INSTR);
+
+	/* Reset the Delay lines */
+	writel(CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK,
+	       priv->regbase + CQSPI_REG_PHY_CONFIG);
+
+	reg = readl(priv->regbase + CQSPI_REG_RD_DATA_CAPTURE);
+	reg &= ~CQSPI_REG_READCAPTURE_DQS_ENABLE;
+	reg &= ~(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK
+		 << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB);
+	writel(reg, priv->regbase + CQSPI_REG_RD_DATA_CAPTURE);
+
 	/* Indirect mode configurations */
 	writel(priv->fifo_depth / 2, priv->regbase + CQSPI_REG_SRAMPARTITION);
 
 	/* Disable all interrupts */
 	writel(0, priv->regbase + CQSPI_REG_IRQMASK);
 
+	reg = readl(priv->regbase + CQSPI_REG_CONFIG);
+	reg &= ~CQSPI_REG_CONFIG_DTR_PROT_EN_MASK;
+	reg &= ~CQSPI_REG_CONFIG_PHY_ENABLE_MASK;
+	reg &= ~CQSPI_REG_CONFIG_DIRECT;
+	reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+			<< CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+
+	writel(reg, priv->regbase + CQSPI_REG_CONFIG);
+
 	cadence_qspi_apb_controller_enable(priv->regbase);
 }
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 20/30] spi: cadence_qspi: Initialize read and write watermark registers
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (18 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 19/30] spi: cadence_qspi: Clean up registers in init Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 21/30] spi: cadence_qspi: Enable ECO bit for higher frequencies Tejas Bhumkar
                   ` (10 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

Read and Write watermark registers are not initialized. Set read
watermark to half of the FIFO and write watermark to 1/8 of the
FIFO size.

Read watermark indicates if SRAM fill level is above this watermark,
interrupt will be generated and read or DMA can be performed.

Write watermark indicates the maximum fill level of SRAM when write is
performed to device.

These values of 1/2 for read and 1/8 for write are chosen similar to
Linux driver.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi_apb.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 5fc5279061..052d7a1766 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -362,6 +362,14 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
 	/* Indirect mode configurations */
 	writel(priv->fifo_depth / 2, priv->regbase + CQSPI_REG_SRAMPARTITION);
 
+	/* Program read watermark -- 1/2 of the FIFO. */
+	writel(priv->fifo_depth * priv->fifo_width / 2,
+	       priv->regbase + CQSPI_REG_INDIRECTRDWATERMARK);
+
+	/* Program write watermark -- 1/8 of the FIFO. */
+	writel(priv->fifo_depth * priv->fifo_width / 8,
+	       priv->regbase + CQSPI_REG_INDIRECTWRWATERMARK);
+
 	/* Disable all interrupts */
 	writel(0, priv->regbase + CQSPI_REG_IRQMASK);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 21/30] spi: cadence_qspi: Enable ECO bit for higher frequencies
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (19 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 20/30] spi: cadence_qspi: Initialize read and write watermark registers Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 22/30] spi: cadence_qspi: Add spi mem dtr support ops Tejas Bhumkar
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

Enable ECO bit for Versal for frequencies above 120Mhz
for octal spi to work properly.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi.h     | 1 +
 drivers/spi/cadence_qspi_apb.c | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 73ef44fc1c..4906b9ef96 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -55,6 +55,7 @@
 #define CQSPI_READ_ID_LEN			3
 #define CQSPI_READID_LOOP_MAX			10
 #define TERA_MACRO				1000000000000l
+#define TAP_GRAN_SEL_MIN_FREQ			120000000
 
 #define OSPI_CTRL_RST				0xF1260304
 
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 052d7a1766..43dc4c6e70 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -382,6 +382,10 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
 
 	writel(reg, priv->regbase + CQSPI_REG_CONFIG);
 
+	if (IS_ENABLED(CONFIG_ARCH_VERSAL) &&
+	    priv->ref_clk_hz >= TAP_GRAN_SEL_MIN_FREQ)
+		writel(1, priv->regbase + CQSPI_REG_ECO);
+
 	cadence_qspi_apb_controller_enable(priv->regbase);
 }
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 22/30] spi: cadence_qspi: Add spi mem dtr support ops
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (20 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 21/30] spi: cadence_qspi: Enable ECO bit for higher frequencies Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 23/30] spi: cadence_qspi: Write aligned byte length to ahbbase Tejas Bhumkar
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

In DDR mode, current default spi_mem_dtr_supports_op() function does
not allow mixed DTR operation functionality. So implement cadence
specific cadence_spi_mem_dtr_supports_op() function to verifying only
the command buswidth and command opcode bytes which satisfies the DTR
protocol.

Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 710c4a532d..282028c845 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -718,6 +718,21 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
 	return err;
 }
 
+static bool cadence_spi_mem_dtr_supports_op(struct spi_slave *slave,
+					    const struct spi_mem_op *op)
+{
+	/*
+	 * In DTR mode, except op->cmd all other parameters like address,
+	 * dummy and data could be 0.
+	 * So lets only check if the cmd buswidth and number of opcode bytes
+	 * are true for DTR to support.
+	 */
+	if (op->cmd.buswidth == 8 && op->cmd.nbytes % 2)
+		return false;
+
+	return true;
+}
+
 static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
 					const struct spi_mem_op *op)
 {
@@ -740,7 +755,7 @@ static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
 		return false;
 
 	if (all_true)
-		return spi_mem_dtr_supports_op(slave, op);
+		return cadence_spi_mem_dtr_supports_op(slave, op);
 	else
 		return spi_mem_default_supports_op(slave, op);
 }
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 23/30] spi: cadence_qspi: Write aligned byte length to ahbbase
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (21 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 22/30] spi: cadence_qspi: Add spi mem dtr support ops Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 24/30] arm64: versal: Enable soft reset support for xspi flashes Tejas Bhumkar
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Ashok Reddy Soma

From: Ashok Reddy Soma <ashok.reddy.soma@amd.com>

Incase of non-aligned length of flash data, ahbbase address is written
directly with byte count. This is causing AHB bus error's sometimes and
resulting in kernel crash while booting linux. To avoid this write 4 byte
aligned byte count to ahbbase address.

Also use a temporary variable with 0xffffffff data and overwrite this
temp with unaligned bytes data before writing to ahbbase.

The value 0xffffffff is chosen as this is flash memory, worst case we
will write 0xff to any location which doesn't effect any bits.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/spi/cadence_qspi_apb.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 43dc4c6e70..1d0e6c2180 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -934,10 +934,12 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
 	while (remaining > 0) {
 		write_bytes = remaining > page_size ? page_size : remaining;
 		writesl(priv->ahbbase, bb_txbuf, write_bytes >> 2);
-		if (write_bytes % 4)
-			writesb(priv->ahbbase,
-				bb_txbuf + rounddown(write_bytes, 4),
-				write_bytes % 4);
+		if (write_bytes % 4) {
+			unsigned int temp = 0xffffffff;
+
+			memcpy(&temp, bb_txbuf + rounddown(write_bytes, 4), write_bytes % 4);
+			writel(temp, priv->ahbbase);
+		}
 
 		/* Wait up to Indirect Operation Complete bit to set */
 		ret = readl_poll_timeout(priv->regbase + CQSPI_REG_IRQSTATUS, cr,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 24/30] arm64: versal: Enable soft reset support for xspi flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (22 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 23/30] spi: cadence_qspi: Write aligned byte length to ahbbase Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 25/30] arm64: versal: Enable octal DTR mode Tejas Bhumkar
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy

Activate the xSPI Software Reset support, which will be
utilized to transition from octal DTR mode to legacy
mode during shutdown and boot (if enabled).

Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 configs/xilinx_versal_virt_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/configs/xilinx_versal_virt_defconfig b/configs/xilinx_versal_virt_defconfig
index ec7caacca0..ee247b905f 100644
--- a/configs/xilinx_versal_virt_defconfig
+++ b/configs/xilinx_versal_virt_defconfig
@@ -95,6 +95,8 @@ CONFIG_MMC_SDHCI_ZYNQ=y
 CONFIG_ZYNQ_SDHCI_MIN_FREQ=100000
 CONFIG_MTD=y
 CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_SOFT_RESET=y
+CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT=y
 CONFIG_SPI_FLASH_MT35XU=y
 CONFIG_SPI_FLASH_GIGADEVICE=y
 CONFIG_SPI_FLASH_ISSI=y
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 25/30] arm64: versal: Enable octal DTR mode
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (23 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 24/30] arm64: versal: Enable soft reset support for xspi flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 26/30] mtd: spi-nor: Add block protection support for micron flashes Tejas Bhumkar
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git

The Cadence driver must switch between SDR and DTR modes based
on commands from the SPI-NOR framework and the configuration
set by SPI_FLASH_DTR_ENABLE. If SPI_FLASH_DTR_ENABLE is enabled,
the driver should transition to DTR mode; if it is not defined,
the driver should avoid switching to DTR mode, signaling that
the controller does not support DTR.

Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 configs/xilinx_versal_virt_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configs/xilinx_versal_virt_defconfig b/configs/xilinx_versal_virt_defconfig
index ee247b905f..69835fce55 100644
--- a/configs/xilinx_versal_virt_defconfig
+++ b/configs/xilinx_versal_virt_defconfig
@@ -98,6 +98,7 @@ CONFIG_DM_SPI_FLASH=y
 CONFIG_SPI_FLASH_SOFT_RESET=y
 CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT=y
 CONFIG_SPI_FLASH_MT35XU=y
+CONFIG_SPI_FLASH_DTR_ENABLE=y
 CONFIG_SPI_FLASH_GIGADEVICE=y
 CONFIG_SPI_FLASH_ISSI=y
 CONFIG_SPI_FLASH_MACRONIX=y
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 26/30] mtd: spi-nor: Add block protection support for micron flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (24 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 25/30] arm64: versal: Enable octal DTR mode Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 27/30] mtd: spi-nor: Add support for locking on Macronix nor flashes Tejas Bhumkar
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	T Karthik Reddy, Siva Durga Prasad Paladugu, Ashok Reddy Soma,
	Venkatesh Yadav Abbarapu

From: T Karthik Reddy <t.karthik.reddy@xilinx.com>

Micron nor flashes provide block protection support using
BP0, BP1, BP2, BP3 & TB bits in status register. This patch
supports for micron nor flashes with manufacturer id 0x20
and 0x2c.

Where BP(Block Protection) bits defines memory to be software
protected against PROGRAM or ERASE operations. When one or more
block protect bits are set to 1, a designated memory area is
protected from PROGRAM and ERASE operations. TB(Top/Bottom) bit
determines whether the protected memory area defined by the block
protect bits starts from the top or bottom of the memory array.

Block Protection table for N25Q00AA with size 128MB, sector size 64KB
and with 2048 sectors.

Top protection:
--------------
TB	BP3	BP2	BP1	BP0	Protected Area		Un-Protected Area
0	0	0	0	0	None			All sectors
0	0	0	0	1	Sector 2047		Sectors (0 to 2046)
0	0	0	1	0	Sectors (2046 to 2047)	Sectors (0 to 2045)
0	0	0	1	1	Sectors (2044 to 2047)  Sectors (0 to 2043)
0	0	1	0	0	Sectors (2040 to 2047)  Sectors (0 to 2039)
0	0	1	0	1	Sectors (2032 to 2047)  Sectors (0 to 2031)
0	0	1	1	0	Sectors (2016 to 2047)  Sectors (0 to 2015)
0	0	1	1	1	Sectors (1984 to 2047)  Sectors (0 to 1983)
0	1	0	0	0	Sectors (1920 to 2047)  Sectors (0 to 1919)
0	1	0	0	1	Sectors (1792 to 2047)  Sectors (0 to 1791)
0	1	0	1	0	Sectors (1936 to 2047)  Sectors (0 to 1535)
0	1	0	1	1	Sectors (1024 to 2047)	None

Bottom protection:
-----------------
TB	BP3	BP2	BP1	BP0	Protected Area		Un-protected Area
1	0	0	0	0	None			All sectors
1	0	0	0	1	sector 0		Sectors (1 to 2047)
1	0	0	1	0	Sectors (0 to 1)	Sectors (2 to 2047)
1	0	0	1	1	Sectors (0 to 3)	Sectors (4 to 2047)
1	0	1	0	0	Sectors (0 to 7)	Sectors (8 to 2047)
1	0	1	0	1	Sectors (0 to 15)	Sectors (16 to 2047)
1	0	1	1	0	Sectors (0 to 31)	Sectors (32 to 2047)
1	0	1	1	1	Sectors (0 to 63)	Sectors (64 to 2047)
1	1	0	0	0	Sectors (0 to 127)	Sectors (128 to 2047)
1	1	0	0	1	Sectors (0 to 255)	Sectors (256 to 2047)
1	1	0	1	0	Sectors (0 to 511)	Sectors (512 to 2047)
1	1	0	1	1	Sectors (0 to 1023)	Sectors (1024 to 2047)

Signed-off-by: Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Signed-off-by: T Karthik Reddy <t.karthik.reddy@xilinx.com>
Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 255 +++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |   7 +
 2 files changed, 262 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index ccda722df5..c40899a281 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -4336,6 +4336,253 @@ static int spi_nor_init(struct spi_nor *nor)
 	return 0;
 }
 
+#if defined(CONFIG_SPI_FLASH_LOCK)
+#if defined(CONFIG_SPI_FLASH_STMICRO)
+static inline uint16_t min_lockable_sectors(struct spi_nor *nor,
+					    uint16_t n_sectors)
+{
+	/* lock granularity */
+	return 1;
+}
+
+static inline uint32_t get_protected_area_start(struct spi_nor *nor,
+						u8 lock_bits,
+						bool is_bottom)
+{
+	u16 n_sectors;
+	u32 sector_size, flash_size;
+	int ret;
+
+	sector_size = nor->sector_size;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL) {
+		n_sectors = (nor->size >> 0x01) / sector_size;
+		flash_size = nor->size >> 0x01;
+	} else {
+		n_sectors = nor->size / sector_size;
+		flash_size = nor->size;
+	}
+
+	if (!is_bottom)
+		ret = flash_size - ((1 << (lock_bits - 1)) * sector_size *
+				    min_lockable_sectors(nor, n_sectors));
+	else
+		ret = (1 << (lock_bits - 1)) * sector_size *
+		       min_lockable_sectors(nor, n_sectors);
+
+	return ret;
+}
+
+static uint8_t min_protected_area_including_offset(struct spi_nor *nor,
+						   u32 offset,
+						   bool is_bottom)
+{
+	u8 lock_bits, lockbits_limit;
+
+	lockbits_limit = MAX_LOCKBITS;
+
+	for (lock_bits = 1; lock_bits < lockbits_limit; lock_bits++) {
+		if (!is_bottom) {
+			/* top protection */
+			if (offset >= get_protected_area_start(nor,
+							       lock_bits,
+							       is_bottom))
+				break;
+		} else {
+			/* bottom protection */
+			if (offset <= get_protected_area_start(nor,
+							       lock_bits,
+							       is_bottom))
+				break;
+		}
+	}
+	return lock_bits;
+}
+
+static int write_sr_modify_protection(struct spi_nor *nor, u8 status,
+				      u8 lock_bits, bool is_bottom)
+{
+	u8 status_new, bp_mask;
+	int ret;
+
+	status_new = status & ~BP_MASK;
+	bp_mask = (lock_bits << BP_SHIFT) & BP_MASK;
+
+	status_new &= ~SR_BP3;
+	/* Protected area starts from top */
+	status_new &= ~SR_TB;
+
+	/* If bottom area is to be Protected set SR_TB */
+	if (is_bottom)
+		status_new |= SR_TB;
+
+	if (lock_bits > 7)
+		bp_mask |= SR_BP3;
+
+	status_new |= bp_mask;
+
+	write_enable(nor);
+
+	nor->spi->flags |= SPI_XFER_LOWER;
+
+	ret = write_sr(nor, status_new);
+	if (ret)
+		return ret;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL) {
+		nor->spi->flags |= SPI_XFER_UPPER;
+		ret = write_sr(nor, status_new);
+		if (ret)
+			return ret;
+	}
+
+	return write_disable(nor);
+}
+
+static void micron_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+				    uint64_t *len)
+{
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2;
+	int shift = ffs(mask) - 1;
+	int pow;
+	u64 norsize = nor->size;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL)
+		norsize /= 2;
+
+	if (!(sr & (mask | SR_BP3))) {
+		/* No protection */
+		*ofs = 0;
+		*len = 0;
+	} else {
+		pow = (sr & mask) >> shift;
+		pow |= sr & SR_BP3 ? BIT(3) : 0;
+
+		if (pow)
+			pow--;
+
+		*len = nor->sector_size << pow;
+		if (*len >= norsize)
+			*len = norsize;
+
+		if (!(sr & SR_TB))
+			*ofs = norsize - *len;
+		else
+			*ofs = 0;
+
+		debug("%s, ofs:0x%lx, len:0x%lx\n", __func__,
+		      (unsigned long)*ofs, (unsigned long)*len);
+	}
+}
+
+static int micron_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				 u8 sr)
+{
+	loff_t lock_offs;
+	u64 lock_len;
+	int given_range, locked_range;
+	bool locked_value = false;
+	bool offset_value = false;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL)
+		ofs /= 2;
+
+	/* Avoid dividing by 2 of data length for size 1 */
+	if (nor->flags & SNOR_F_HAS_PARALLEL && len != 1)
+		len /= 2;
+
+	debug("%s, ofs:0x%lx, len:0x%lx\n", __func__,
+	      (unsigned long)ofs,
+	      (unsigned long)len);
+
+	micron_get_locked_range(nor, sr, &lock_offs, &lock_len);
+
+	given_range = ofs + len;
+	locked_range = lock_offs + lock_len;
+
+	if (given_range <= locked_range)
+		locked_value = true;
+
+	if (ofs >= lock_offs)
+		offset_value = true;
+
+	if (sr & SR_TB)
+		return !locked_value;
+
+	return !(locked_value && offset_value);
+}
+
+static int micron_flash_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	u8 status_old, status_old_up;
+	u8 lock_bits;
+	loff_t lock_len;
+	int ret = 0;
+	bool is_bottom = false; /* Use TOP protection by default */
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL)
+		nor->spi->flags |= SPI_XFER_LOWER;
+
+	status_old = read_sr(nor);
+	if (status_old < 0)
+		return status_old;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL) {
+		nor->spi->flags |= SPI_XFER_UPPER;
+		status_old_up = read_sr(nor);
+		if (status_old_up < 0)
+			return status_old_up;
+		if ((status_old & BPTB_MASK) != (status_old_up & BPTB_MASK)) {
+			printf("BP is different in both flashes lo:0x%x, up:0x%x\n",
+			       status_old, status_old_up);
+			return -EINVAL;
+		}
+	}
+
+	if (ofs < nor->size / 2)
+		is_bottom = true;	/* Change it to bottom protection */
+
+	debug("Status in both flashes lo:0x%x, up:0x%x\n",
+	      status_old, status_old_up);
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL) {
+		ofs /= 2;
+		len /= 2;
+	}
+
+	if (!is_bottom)
+		lock_len = ofs;
+	else
+		lock_len = ofs + len;
+
+	lock_bits = min_protected_area_including_offset(nor, lock_len,
+							is_bottom);
+
+	if (lock_bits > ((status_old & (BP_MASK << BP_SHIFT)) >> 2))
+		ret = write_sr_modify_protection(nor, status_old, lock_bits,
+						 is_bottom);
+
+	return ret;
+}
+
+static int micron_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	return write_sr_modify_protection(nor, 0, 0, 0);
+}
+
+static int micron_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int status;
+
+	status = read_sr(nor);
+	if (status < 0)
+		return status;
+
+	return micron_is_unlocked_sr(nor, ofs, len, status);
+}
+#endif /* CONFIG_SPI_FLASH_STMICRO */
+#endif /* CONFIG_SPI_FLASH_LOCK */
+
 #ifdef CONFIG_SPI_FLASH_SOFT_RESET
 /**
  * spi_nor_soft_reset() - perform the JEDEC Software Reset sequence
@@ -4545,6 +4792,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	}
 #endif
 
+#if defined(CONFIG_SPI_FLASH_STMICRO)
+	if (JEDEC_MFR(info) == SNOR_MFR_ST || JEDEC_MFR(info) == SNOR_MFR_MICRON) {
+		nor->flash_lock = micron_flash_lock;
+		nor->flash_unlock = micron_flash_unlock;
+		nor->flash_is_unlocked = micron_is_unlocked;
+	}
+#endif
+
 #ifdef CONFIG_SPI_FLASH_SST
 	/*
 	 * sst26 series block protection implementation differs from other
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 34e0aedc24..03413063ae 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -166,8 +166,15 @@
 #define SR_BP0			BIT(2)	/* Block protect 0 */
 #define SR_BP1			BIT(3)	/* Block protect 1 */
 #define SR_BP2			BIT(4)	/* Block protect 2 */
+#define SR_BP3			BIT(6)  /* Block protect 3 */
 #define SR_TB			BIT(5)	/* Top/Bottom protect */
 #define SR_SRWD			BIT(7)	/* SR write protect */
+
+#define BPTB_MASK		0x7C	/* BP & TB bits mask */
+#define BP_MASK			0x1C	/* BP bits mask */
+#define BP_SHIFT		0x2	/* BP shift*/
+#define MAX_LOCKBITS		15
+
 /* Spansion/Cypress specific status bits */
 #define SR_E_ERR		BIT(5)
 #define SR_P_ERR		BIT(6)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 27/30] mtd: spi-nor: Add support for locking on Macronix nor flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (25 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 26/30] mtd: spi-nor: Add block protection support for micron flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 28/30] mtd: spi-nor: Add support for locking on ISSI " Tejas Bhumkar
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Venkatesh Yadav Abbarapu

From: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>

Macronix chips implements locking in (power-of-two multiple of) 64K
blocks, not as a fraction of the chip's size. Bit 5 in the status
register is not a top/bottom select bit, but instead a fourth value
bit, allowing locking between 2^0 and 2^14 64K blocks (so up to 1GiB),
either from top or bottom.

The top/bottom select is instead done via a bit in the configuration
register, which is OTP, so once set to use bottom protect, one cannot
use top. On top of that, reading the configuration register uses a
different opcode (0x15) than the existing SPINOR_OP_RDCR (0x35).

Here's an attempt to implement locking support for Macronix flash
memory devices. This implementation has been tested and confirmed to
work on the specific chip used on our boards. Additionally, based
on the information from data sheets of various other Macronix chips,
suggest they behave in the same way.

Used bottom protect to test the locking and unlocking functionality
on the zc1751+dc1 board.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 259 +++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |   3 +
 2 files changed, 262 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index c40899a281..08f6fb66be 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -4581,6 +4581,257 @@ static int micron_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return micron_is_unlocked_sr(nor, ofs, len, status);
 }
 #endif /* CONFIG_SPI_FLASH_STMICRO */
+
+#if defined(CONFIG_SPI_FLASH_MACRONIX)
+/**
+ * mx_write_sr_cr() - write status and configuration register
+ * @nor: pointer to a 'struct spi_nor'
+ * @sr_cr: pointer status and configuration register
+ *
+ * Write status Register and configuration register with 2 bytes
+ * The first byte will be written to the status register, while the
+ * second byte will be written to the configuration register.
+ *
+ * Return: 0 on success, -errno if error occurred.
+ */
+static int mx_write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
+{
+	int ret;
+
+	write_enable(nor);
+
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+	if (ret < 0) {
+		dev_dbg(nor->dev,
+			"error while writing configuration register\n");
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret) {
+		dev_dbg(nor->dev,
+			"timeout while writing configuration register\n");
+		return ret;
+	}
+
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int mx_read_cr(struct spi_nor *nor)
+{
+	int ret;
+	u8 val;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDCR_MX, &val, 1);
+	if (ret < 0) {
+		dev_dbg(nor->dev, "error %d reading CR\n", ret);
+		return ret;
+	}
+
+	return val;
+}
+
+/**
+ * mx_get_locked_range() - get the locked range
+ * @nor: pointer to a 'struct spi_nor'
+ * @sr:  status register
+ * @cr:  configuration register
+ * @ofs: flash offset
+ * @len: length to be locked
+ *
+ * Macronix flashes do not work by locking some 1/2^k fraction of the
+ * flash - instead, the BP{0,1,2,3} bits define a number of protected
+ * 64K blocks.
+ */
+static void mx_get_locked_range(struct spi_nor *nor, u8 sr, u8 cr,
+				loff_t *ofs, uint64_t *len)
+{
+	struct mtd_info *mtd = &nor->mtd;
+	int pow, shift;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_MX;
+
+	shift = ffs(mask) - 1;
+
+	pow = ((sr & mask) >> shift) - 1;
+	if (pow < 0) {
+		/* No protection */
+		*ofs = 0;
+		*len = 0;
+	} else {
+		*len = (uint64_t)SZ_64K << pow;
+		if (*len > mtd->size)
+			*len = mtd->size;
+		if (cr & CR_TB_MX)
+			*ofs = 0;
+		else
+			*ofs = mtd->size - *len;
+	}
+}
+
+/**
+ * mx_check_lock_status() - check the locked status
+ * @nor: pointer to a 'struct spi_nor'
+ * @ofs: flash offset
+ * @len: length to be locked
+ * @sr:  status register
+ * @cr:  configuration register
+ * @locked: locked:1 unlocked:0 value
+ *
+ * Return: 1 if the entire region is locked (if @locked is true) or unlocked (if
+ * @locked is false); 0 otherwise.
+ */
+static int mx_check_lock_status(struct spi_nor *nor, loff_t ofs, u64 len,
+				u8 sr, u8 cr, bool locked)
+{
+	loff_t lock_offs;
+	u64 lock_len;
+
+	if (!len)
+		return 1;
+
+	mx_get_locked_range(nor, sr, cr, &lock_offs, &lock_len);
+
+	if (locked)
+		/* Requested range is a sub-range of locked range */
+		return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+
+	/* Requested range does not overlap with locked range */
+	return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
+}
+
+static int mx_lock_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len, bool lock)
+{
+	struct mtd_info *mtd = &nor->mtd;
+	int sr, cr, ret, val;
+	loff_t lock_len, blocks;
+	bool can_be_top, can_be_bottom, use_top;
+	u8 sr_cr[2], shift;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_MX;
+
+	shift = ffs(mask) - 1;
+
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	cr = mx_read_cr(nor);
+	if (cr < 0)
+		return cr;
+
+	log_debug("SPI Protection: %s\n", (cr & CR_TB_MX) ? "bottom" : "top");
+
+	/* CR_TB is OTP, so we can't use 'top' protection if that is already set. */
+	can_be_top = !(cr & CR_TB_MX);
+	can_be_bottom = true;
+
+	/* If the whole range is already locked (unlocked), we don't need to do anything */
+	if (mx_check_lock_status(nor, ofs, len, sr, cr, lock))
+		return 0;
+
+	/* To use 'bottom' ('top') protection, everything below us must be locked (unlocked). */
+	if (!mx_check_lock_status(nor, 0, ofs, sr, cr, lock)) {
+		if (lock)
+			can_be_bottom = false;
+		else
+			can_be_top = false;
+	}
+
+	/* To use 'top' ('bottom') protection, everything above us must be locked (unlocked). */
+	if (!mx_check_lock_status(nor, ofs + len, mtd->size - (ofs + len), sr, cr, lock)) {
+		if (lock)
+			can_be_top = false;
+		else
+			can_be_bottom = false;
+	}
+
+	if (!can_be_bottom && !can_be_top)
+		return -EINVAL;
+
+	/* Prefer top, if both are valid */
+	use_top = can_be_top;
+
+	/* lock_len: length of region that should end up locked */
+	if (lock)
+		lock_len = use_top ? mtd->size - ofs : ofs + len;
+	else
+		lock_len = use_top ? mtd->size - (ofs + len) : ofs;
+
+	/* lock_len must be a power-of-2 (2^0 .. 2^14) multiple of 64K, or 0 */
+	if (lock_len & (SZ_64K - 1))
+		return -EINVAL;
+
+	blocks = lock_len / SZ_64K;
+	if ((blocks != 0 && !is_power_of_2(blocks)) || blocks > 1 << 14)
+		return -EINVAL;
+
+	/* Compute new values of sr/cr */
+	val = blocks ? ilog2(blocks) + 1 : 0;
+	sr_cr[0] = sr & ~mask;
+	sr_cr[0] |= val << shift;
+	/*
+	 * Disallow further writes if WP pin is asserted, but remove
+	 * that bit if we unlocked the whole chip.
+	 */
+	if (lock_len)
+		sr_cr[0] |= SR_SRWD;
+	else
+		sr_cr[0] &= ~SR_SRWD;
+
+	sr_cr[1] = cr | (use_top ? 0 : CR_TB_MX);
+
+	/* Don't bother if they're the same */
+	if (sr == sr_cr[0] && cr == sr_cr[1])
+		return 0;
+
+	ret = mx_write_sr_cr(nor, sr_cr);
+	if (ret)
+		return ret;
+
+	/* Check that the bits got written as expected */
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	cr = mx_read_cr(nor);
+	if (cr < 0)
+		return cr;
+
+	if ((sr & mask) != (sr_cr[0] & mask) ||
+	    (cr & CR_TB_MX) != (sr_cr[1] & CR_TB_MX))
+		return -EIO;
+
+	return 0;
+}
+
+static int mx_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	return mx_lock_unlock(nor, ofs, len, true);
+}
+
+static int mx_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	return mx_lock_unlock(nor, ofs, len, false);
+}
+
+static int mx_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int sr, cr;
+
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	cr = mx_read_cr(nor);
+	if (cr < 0)
+		return cr;
+
+	return mx_check_lock_status(nor, ofs, len, sr, cr, false);
+}
+#endif /* CONFIG_SPI_FLASH_MACRONIX */
 #endif /* CONFIG_SPI_FLASH_LOCK */
 
 #ifdef CONFIG_SPI_FLASH_SOFT_RESET
@@ -4800,6 +5051,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	}
 #endif
 
+#if defined(CONFIG_SPI_FLASH_MACRONIX)
+	if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX) {
+		nor->flash_lock = mx_lock;
+		nor->flash_unlock = mx_unlock;
+		nor->flash_is_unlocked = mx_is_unlocked;
+	}
+#endif
+
 #ifdef CONFIG_SPI_FLASH_SST
 	/*
 	 * sst26 series block protection implementation differs from other
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 03413063ae..8ca874068a 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -70,6 +70,7 @@
 #define SPINOR_OP_RDID		0x9f	/* Read JEDEC ID */
 #define SPINOR_OP_RDSFDP	0x5a	/* Read SFDP */
 #define SPINOR_OP_RDCR		0x35	/* Read configuration register */
+#define SPINOR_OP_RDCR_MX	0x15	/* Read configuration register (Macronix) */
 #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
 #define SPINOR_OP_CLFSR		0x50	/* Clear flag status register */
 #define SPINOR_OP_RDEAR		0xc8	/* Read Extended Address Register */
@@ -167,6 +168,7 @@
 #define SR_BP1			BIT(3)	/* Block protect 1 */
 #define SR_BP2			BIT(4)	/* Block protect 2 */
 #define SR_BP3			BIT(6)  /* Block protect 3 */
+#define SR_BP3_MX		BIT(5)	/* Block protect 3 (Macronix) */
 #define SR_TB			BIT(5)	/* Top/Bottom protect */
 #define SR_SRWD			BIT(7)	/* SR write protect */
 
@@ -192,6 +194,7 @@
 
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
+#define CR_TB_MX		BIT(3)	/* Macronix Top/Bottom protect */
 
 /* Status Register 2 bits. */
 #define SR2_QUAD_EN_BIT7	BIT(7)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 28/30] mtd: spi-nor: Add support for locking on ISSI nor flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (26 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 27/30] mtd: spi-nor: Add support for locking on Macronix nor flashes Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 29/30] mtd: spi-nor: Add support for locking on GIGADEVICE " Tejas Bhumkar
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Venkatesh Yadav Abbarapu

From: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>

ISSI chips implements locking in (power-of-two multiple of) 64K
blocks, not as a fraction of the chip's size. Bit 5 in the status
register is not a top/bottom select bit, but instead a fourth value
bit, allowing locking between 2^0 and 2^14 64K blocks (so up to 1GiB),
either from top or bottom.

The top/bottom select is instead done via a bit in the function
register, which is OTP, so once set to use bottom protect, one cannot
use top. On top of that, reading the function register uses a
opcode SPINOR_OP_RDFR (0x48) and writing to the function register uses the
opcode SPINOR_OP_WRFR(0x42).

Here's an attempt at implementing a locking feature for ISSI flash memory
devices. This implementation has been rigorously tested and proven to work
on the chip used in our boards. Additionally, after examining data sheets
for various other ISSI chips, it appears that they follow a similar
behavior.

Used "bottom protect" to test the lock and unlock functionality on the
Versal Tenzing board. Please note that the Block Protection table is
specific to the IS25LP01G, which has a size of 128MB, a sector size
of 64KB, and comprises 2048 sectors.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/sf_internal.h  |   6 +
 drivers/mtd/spi/spi-nor-core.c | 340 +++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |   7 +
 3 files changed, 353 insertions(+)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 2cbdea60b0..aaa8520838 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -71,6 +71,12 @@ struct flash_info {
 #define SPI_NOR_OCTAL_DTR_READ	BIT(17)	/* Flash supports Octal DTR Read */
 #define SPI_NOR_OCTAL_DTR_PP	BIT(18) /* Flash supports Octal DTR page program */
 #define SPI_NOR_MULTI_DIE	BIT(19) /* Flash has multi dies & need split reads*/
+#define SPI_NOR_HAS_BP3		BIT(20) /* Flash SR has block protect bits
+					 * for lock/unlock purpose, few support
+					 * BP0-BP2 while few support BP0-BP3.
+					 * This flag identifies devices that
+					 * support BP3 bit.
+					 */
 };
 
 extern const struct flash_info spi_nor_ids[];
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 08f6fb66be..3bf1e5471b 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -4832,6 +4832,338 @@ static int mx_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return mx_check_lock_status(nor, ofs, len, sr, cr, false);
 }
 #endif /* CONFIG_SPI_FLASH_MACRONIX */
+
+#if defined(CONFIG_SPI_FLASH_ISSI)
+/**
+ * spi_nor_read_fr() - read function register
+ * @nor: pointer to a 'struct spi_nor'.
+ *
+ * ISSI devices have top/bottom area protection bits selection into function
+ * reg. The bits in FR are OTP. So once it's written, it cannot be changed.
+ *
+ * Return: Value in function register or negative if error.
+ */
+static int spi_nor_read_fr(struct spi_nor *nor)
+{
+	int ret;
+	u8 val;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDFR, &val, 1);
+	if (ret < 0) {
+		dev_dbg(nor->dev, "error %d reading FR\n", ret);
+		return ret;
+	}
+
+	return val;
+}
+
+static void issi_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+				  uint64_t *len)
+{
+	struct mtd_info *mtd = &nor->mtd;
+	u8 mask = 0, fr = 0;
+	int pow, shift;
+	u32 sector_size;
+
+	mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_ISSI;
+	shift = ffs(mask) - 1;
+	sector_size = nor->sector_size;
+
+	if (nor->flags & SNOR_F_HAS_PARALLEL)
+		sector_size >>= 1;
+
+	if (!(sr & mask)) {
+		/* No protection */
+		*ofs = 0;
+		*len = 0;
+	} else {
+		pow = ((sr & mask) >> shift) - 1;
+		*len = sector_size << pow;
+		if (*len > mtd->size)
+			*len = mtd->size;
+		/* ISSI device's have top/bottom select bit in function reg */
+		fr = spi_nor_read_fr(nor);
+		if (fr & FR_TB)
+			*ofs = 0;
+		else
+			*ofs = mtd->size - *len;
+	}
+}
+
+/**
+ * issi_check_lock_status_sr() - check the status register and return
+ * the region is locked or unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ * @locked: locked:1 unlocked:0 value
+ *
+ * Return: 1 if the entire region is locked (if @locked is true) or unlocked (if
+ * @locked is false); 0 otherwise.
+ */
+static int issi_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, u64 len,
+				     u8 sr, bool locked)
+{
+	loff_t lock_offs;
+	u64 lock_len;
+
+	if (!len)
+		return 1;
+
+	issi_get_locked_range(nor, sr, &lock_offs, &lock_len);
+	if (locked)
+		/* Requested range is a sub-range of locked range */
+		return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+
+	/* Requested range does not overlap with locked range */
+	return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
+}
+
+/**
+ * spi_nor_is_locked_sr() - check if the memory region is locked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is locked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				u8 sr)
+{
+	return issi_check_lock_status_sr(nor, ofs, len, sr, true);
+}
+
+/**
+ * spi_nor_is_unlocked_sr() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is unlocked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				  u8 sr)
+{
+	return issi_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * issi_is_unlocked() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ *
+ * Check if memory region is unlocked
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int issi_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int sr;
+
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	return issi_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * spi_nor_select_zone() - Select top area or bottom area to lock/unlock
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ * @sr: status register
+ * @tb: pointer to top/bottom bool used in caller function
+ * @op: zone selection is for lock/unlock operation. 1: lock 0:unlock
+ *
+ * Select the top area / bottom area pattern to protect memory blocks.
+ *
+ * Return: negative on errors, 0 on success.
+ */
+static int spi_nor_select_zone(struct spi_nor *nor, loff_t ofs, uint64_t len,
+			       u8 sr, bool *tb, bool op)
+{
+	int retval = 1;
+	bool can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, can_be_top = true;
+
+	if (op) {
+		/* Select for lock zone operation */
+
+		/*
+		 * If nothing in our range is unlocked, we don't need
+		 * to do anything.
+		 */
+		if (spi_nor_is_locked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is unlocked, we can't use 'bottom'
+		 * protection.
+		 */
+		if (!spi_nor_is_locked_sr(nor, 0, ofs, sr))
+			can_be_bottom = false;
+
+		/*
+		 * If anything above us is unlocked, we can't use 'top'
+		 * protection.
+		 */
+		if (!spi_nor_is_locked_sr(nor, ofs + len,
+					  nor->mtd.size - (ofs + len), sr))
+			can_be_top = false;
+	} else {
+		/* Select unlock zone */
+
+		/*
+		 * If nothing in our range is locked, we don't need to
+		 * do anything.
+		 */
+		if (spi_nor_is_unlocked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is locked, we can't use 'top'
+		 * protection
+		 */
+		if (!spi_nor_is_unlocked_sr(nor, 0, ofs, sr))
+			can_be_top = false;
+
+		/*
+		 * If anything above us is locked, we can't use 'bottom'
+		 * protection
+		 */
+		if (!spi_nor_is_unlocked_sr(nor, ofs + len,
+					    nor->mtd.size - (ofs + len), sr))
+			can_be_bottom = false;
+	}
+
+	if (!can_be_bottom && !can_be_top)
+		return -EINVAL;
+
+	/* Prefer top, if both are valid */
+	*tb = can_be_top;
+	return retval;
+}
+
+/**
+ * issi_flash_lock() - set BP[0123] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Lock a region of the flash.Implementation is based on stm_lock
+ * Supports the block protection bits BP{0,1,2,3} in status register
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int issi_flash_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	u32 sector_size;
+	u16 n_sectors;
+	unsigned int bp_slots, bp_slots_needed;
+	int status_old, status_new, blk_prot, fr, shift;
+	loff_t lock_len;
+	u8 pow, ret;
+	bool use_top = false;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_ISSI;
+
+	shift = ffs(mask) - 1;
+
+	status_old = read_sr(nor);
+	/* if status reg is Write protected don't update bit protection */
+	if (status_old & SR_SRWD) {
+		dev_err(nor->dev,
+			"SR is write protected, can't update BP bits...\n");
+		return -EINVAL;
+	}
+
+	fr = spi_nor_read_fr(nor);
+	log_debug("SPI Protection: %s\n", (fr & FR_TB) ? "bottom" : "top");
+
+	ret = spi_nor_select_zone(nor, ofs, len, status_old, &use_top, 1);
+	/* Older protected blocks include the new requested block's */
+	if (ret <= 0)
+		return ret;
+
+	/* lock_len: length of region that should end up locked */
+	if (use_top)
+		lock_len = nor->mtd.size - ofs;
+	else
+		lock_len = ofs + len;
+
+	sector_size = nor->sector_size;
+	n_sectors = (nor->size) / sector_size;
+
+	bp_slots = (1 << hweight8(mask)) - 2;
+	bp_slots_needed = ilog2(n_sectors);
+
+	if (bp_slots_needed > bp_slots)
+		sector_size <<= (bp_slots_needed - bp_slots);
+
+	pow = ilog2(lock_len) - ilog2(sector_size) + 1;
+	blk_prot = pow << shift;
+	status_new = status_old | blk_prot;
+	if (status_old == status_new)
+		return 0;
+
+	return write_sr_and_check(nor, status_new, mask);
+}
+
+/**
+ * issi_flash_unlock() - clear BP[0123] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to unlock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Bits [2345] of the Status Register are BP[0123].
+ * ISSI chips use a different block protection scheme than other chips.
+ * Just disable the write-protect unilaterally.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int issi_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int ret, val;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_ISSI;
+
+	val = read_sr(nor);
+	if (val < 0)
+		return val;
+
+	if (!(val & mask))
+		return 0;
+
+	write_enable(nor);
+
+	write_sr(nor, val & ~mask);
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
+	ret = read_sr(nor);
+	if (ret > 0 && !(ret & mask)) {
+		dev_info(nor->dev, "ISSI block protect bits cleared SR: 0x%x\n",
+			 ret);
+		ret = 0;
+	} else {
+		dev_err(nor->dev, "ISSI block protect bits not cleared\n");
+		ret = -EINVAL;
+	}
+	return ret;
+}
+#endif /* CONFIG_SPI_FLASH_ISSI */
 #endif /* CONFIG_SPI_FLASH_LOCK */
 
 #ifdef CONFIG_SPI_FLASH_SOFT_RESET
@@ -5059,6 +5391,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	}
 #endif
 
+#if  defined(CONFIG_SPI_FLASH_ISSI)
+	if (JEDEC_MFR(info) == SNOR_MFR_ISSI) {
+		nor->flash_lock = issi_flash_lock;
+		nor->flash_unlock = issi_flash_unlock;
+		nor->flash_is_unlocked = issi_is_unlocked;
+	}
+#endif
+
 #ifdef CONFIG_SPI_FLASH_SST
 	/*
 	 * sst26 series block protection implementation differs from other
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 8ca874068a..9a560d94a2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -77,6 +77,8 @@
 #define SPINOR_OP_WREAR		0xc5	/* Write Extended Address Register */
 #define SPINOR_OP_SRSTEN	0x66	/* Software Reset Enable */
 #define SPINOR_OP_SRST		0x99	/* Software Reset */
+#define SPINOR_OP_RDFR		0x48	/* Read Function register */
+#define SPINOR_OP_WRFR		0x42	/* Write Function register 1 byte */
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 #define SPINOR_OP_READ_4B	0x13	/* Read data bytes (low frequency) */
@@ -169,6 +171,7 @@
 #define SR_BP2			BIT(4)	/* Block protect 2 */
 #define SR_BP3			BIT(6)  /* Block protect 3 */
 #define SR_BP3_MX		BIT(5)	/* Block protect 3 (Macronix) */
+#define SR_BP3_ISSI		BIT(5)	/* Block protect 3 (ISSI) */
 #define SR_TB			BIT(5)	/* Top/Bottom protect */
 #define SR_SRWD			BIT(7)	/* SR write protect */
 
@@ -192,6 +195,9 @@
 #define FSR_P_ERR		BIT(4)	/* Program operation status */
 #define FSR_PT_ERR		BIT(1)	/* Protection error bit */
 
+/* Function register bit */
+#define FR_TB			BIT(1)	/*ISSI: Top/Bottom protect */
+
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
 #define CR_TB_MX		BIT(3)	/* Macronix Top/Bottom protect */
@@ -322,6 +328,7 @@ enum spi_nor_option_flags {
 	SNOR_F_IO_MODE_EN_VOLATILE = BIT(8),
 	SNOR_F_HAS_STACKED	= BIT(9),
 	SNOR_F_HAS_PARALLEL	= BIT(10),
+	SNOR_F_HAS_BP3		= BIT(11),
 };
 
 struct spi_nor;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 29/30] mtd: spi-nor: Add support for locking on GIGADEVICE nor flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (27 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 28/30] mtd: spi-nor: Add support for locking on ISSI " Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2023-12-06  9:31 ` [PATCH v2 30/30] mtd: spi-nor: Add support for locking on Spansion " Tejas Bhumkar
  2024-01-29 12:23 ` [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Jagan Teki
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Venkatesh Yadav Abbarapu

From: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>

GIGADEVICE nor flashes provide block protection support using
BP0, BP1, BP2, BP3 & TB bits in status register.

BP(Block Protection) bits defines memory to be software
protected against PROGRAM or ERASE operations. When one or more
block protect bits are set to 1, a designated memory area is
protected from PROGRAM and ERASE operations. TB(Top/Bottom) bit
determines whether the protected memory area defined by the block
protect bits starts from the top or bottom of the memory array.

Used bottom/top protect to test lock/unlock on zc1751+dc1 board.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 309 +++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |   2 +
 2 files changed, 311 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 3bf1e5471b..3e2491cc3e 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -5164,6 +5164,307 @@ static int issi_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return ret;
 }
 #endif /* CONFIG_SPI_FLASH_ISSI */
+
+#if defined(CONFIG_SPI_FLASH_GIGADEVICE)
+static void giga_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+				  uint64_t *len)
+{
+	struct mtd_info *mtd = &nor->mtd;
+	int shift = 0;
+	int pow;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_GIGA;
+	u32 sector_size;
+
+	sector_size = nor->sector_size;
+	if (nor->flags & SNOR_F_HAS_PARALLEL)
+		sector_size >>= 1;
+
+	shift = ffs(mask) - 1;
+
+	if (!(sr & mask)) {
+		/* No protection */
+		*ofs = 0;
+		*len = 0;
+	} else {
+		pow = ((sr & mask) >> shift) - 1;
+		*len = sector_size << pow;
+		if (*len > mtd->size)
+			*len = mtd->size;
+		/* GIGA device's have top/bottom select bit in status reg */
+		if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB_GIGA)
+			*ofs = 0;
+		else
+			*ofs = mtd->size - *len;
+	}
+}
+
+/**
+ * giga_check_lock_status_sr() - check the status register and return
+ * the region is locked or unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ * @locked: locked:1 unlocked:0 value
+ *
+ * Return: 1 if the entire region is locked (if @locked is true) or unlocked (if
+ * @locked is false); 0 otherwise.
+ */
+static int giga_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, u64 len,
+				     u8 sr, bool locked)
+{
+	loff_t lock_offs;
+	u64 lock_len;
+
+	if (!len)
+		return 1;
+
+	giga_get_locked_range(nor, sr, &lock_offs, &lock_len);
+	if (locked)
+		/* Requested range is a sub-range of locked range */
+		return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+
+	/* Requested range does not overlap with locked range */
+	return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
+}
+
+/**
+ * giga_is_locked_sr() - check if the memory region is locked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is locked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int giga_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+			     u8 sr)
+{
+	return giga_check_lock_status_sr(nor, ofs, len, sr, true);
+}
+
+/**
+ * giga_is_unlocked_sr() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is unlocked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int giga_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+			       u8 sr)
+{
+	return giga_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * giga_is_unlocked() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ *
+ * Check if memory region is unlocked
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int giga_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int sr;
+
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	return giga_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * giga_nor_select_zone() - Select top area or bottom area to lock/unlock
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ * @sr: status register
+ * @tb: pointer to top/bottom bool used in caller function
+ * @op: zone selection is for lock/unlock operation. 1: lock 0:unlock
+ *
+ * Select the top area / bottom area pattern to protect memory blocks.
+ *
+ * Return: negative on errors, 0 on success.
+ */
+static int giga_nor_select_zone(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				u8 sr, bool *tb, bool op)
+{
+	int retval = 1;
+	bool can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, can_be_top = true;
+
+	if (op) {
+		/* Select for lock zone operation */
+
+		/*
+		 * If nothing in our range is unlocked, we don't need
+		 * to do anything.
+		 */
+		if (giga_is_locked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is unlocked, we can't use 'bottom'
+		 * protection.
+		 */
+		if (!giga_is_locked_sr(nor, 0, ofs, sr))
+			can_be_bottom = false;
+
+		/*
+		 * If anything above us is unlocked, we can't use 'top'
+		 * protection.
+		 */
+		if (!giga_is_locked_sr(nor, ofs + len,
+				       nor->mtd.size - (ofs + len), sr))
+			can_be_top = false;
+	} else {
+		/* Select unlock zone */
+
+		/*
+		 * If nothing in our range is locked, we don't need to
+		 * do anything.
+		 */
+		if (giga_is_unlocked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is locked, we can't use 'top'
+		 * protection
+		 */
+		if (!giga_is_unlocked_sr(nor, 0, ofs, sr))
+			can_be_top = false;
+
+		/*
+		 * If anything above us is locked, we can't use 'bottom'
+		 * protection
+		 */
+		if (!giga_is_unlocked_sr(nor, ofs + len,
+					 nor->mtd.size - (ofs + len), sr))
+			can_be_bottom = false;
+	}
+
+	if (!can_be_bottom && !can_be_top)
+		return -EINVAL;
+
+	/* Prefer top, if both are valid */
+	*tb = can_be_top;
+	return retval;
+}
+
+/**
+ * giga_flash_lock() - set BP[0123] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Lock a region of the flash.Implementation is based on stm_lock
+ * Supports the block protection bits BP{0,1,2,3} in status register
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int giga_flash_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int status_old, status_new, blk_prot;
+	loff_t lock_len;
+	u8 pow, ret, shift;
+	bool use_top = false;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_GIGA;
+
+	shift = ffs(mask) - 1;
+
+	status_old = read_sr(nor);
+	/* if status reg is Write protected don't update bit protection */
+	if (status_old & SR_SRWD) {
+		dev_err(nor->dev,
+			"SR is write protected, can't update BP bits...\n");
+		return -EINVAL;
+	}
+
+	log_debug("SPI Protection: %s\n", (status_old & SR_TB_GIGA) ? "bottom" : "top");
+
+	ret = giga_nor_select_zone(nor, ofs, len, status_old, &use_top, 1);
+	/* Older protected blocks include the new requested block's */
+	if (ret <= 0)
+		return ret;
+
+	/* lock_len: length of region that should end up locked */
+	if (use_top)
+		lock_len = nor->mtd.size - ofs;
+	else
+		lock_len = ofs + len;
+
+	pow = order_base_2(lock_len);
+	blk_prot = mask & (((pow + 1) & 0xf) << shift);
+	if (lock_len <= 0) {
+		dev_err(nor->dev, "invalid Length to protect");
+		return -EINVAL;
+	}
+
+	status_new = status_old | blk_prot;
+	if (!use_top)
+		status_new |= SR_TB_GIGA;
+	else
+		status_new &= ~SR_TB_GIGA;
+
+	if (status_old == status_new)
+		return 0;
+
+	return write_sr_and_check(nor, status_new, mask);
+}
+
+/**
+ * giga_flash_unlock() - clear BP[0123] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to unlock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Bits [2345] of the Status Register are BP[0123].
+ * Clear the corresponding BP status bits.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int giga_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int ret, val;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3_GIGA;
+
+	val = read_sr(nor);
+	if (val < 0)
+		return val;
+
+	if (!(val & mask))
+		return 0;
+
+	write_enable(nor);
+
+	write_sr(nor, val & ~mask);
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
+	ret = read_sr(nor);
+	if (ret > 0 && !(ret & mask)) {
+		dev_info(nor->dev, "block protect bits cleared SR: 0x%x\n",
+			 ret);
+		ret = 0;
+	}
+	return ret;
+}
+#endif /* CONFIG_SPI_FLASH_GIGADEVICE */
 #endif /* CONFIG_SPI_FLASH_LOCK */
 
 #ifdef CONFIG_SPI_FLASH_SOFT_RESET
@@ -5399,6 +5700,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	}
 #endif
 
+#if defined(CONFIG_SPI_FLASH_GIGADEVICE)
+	if (JEDEC_MFR(info) == SNOR_MFR_GIGADEVICE) {
+		nor->flash_lock = giga_flash_lock;
+		nor->flash_unlock = giga_flash_unlock;
+		nor->flash_is_unlocked = giga_is_unlocked;
+	}
+#endif
+
 #ifdef CONFIG_SPI_FLASH_SST
 	/*
 	 * sst26 series block protection implementation differs from other
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 9a560d94a2..797f0f275a 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -172,6 +172,8 @@
 #define SR_BP3			BIT(6)  /* Block protect 3 */
 #define SR_BP3_MX		BIT(5)	/* Block protect 3 (Macronix) */
 #define SR_BP3_ISSI		BIT(5)	/* Block protect 3 (ISSI) */
+#define SR_TB_GIGA		BIT(6)  /* Top/Bottom protect (GIGADEVICE)*/
+#define SR_BP3_GIGA		BIT(5)	/* Block protect 3 (GIGADEVICE) */
 #define SR_TB			BIT(5)	/* Top/Bottom protect */
 #define SR_SRWD			BIT(7)	/* SR write protect */
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2 30/30] mtd: spi-nor: Add support for locking on Spansion nor flashes
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (28 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 29/30] mtd: spi-nor: Add support for locking on GIGADEVICE " Tejas Bhumkar
@ 2023-12-06  9:31 ` Tejas Bhumkar
  2024-01-29 12:23 ` [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Jagan Teki
  30 siblings, 0 replies; 35+ messages in thread
From: Tejas Bhumkar @ 2023-12-06  9:31 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, michal.simek, jagan, vigneshr, git,
	Venkatesh Yadav Abbarapu

From: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>

Spansion nor flashes provide block protection support using
BP0, BP1, BP2 bits in status register.

The top/bottom select is instead done via a bit in the configuration
register, which is OTP, so once set to use bottom protect, one cannot
use top. On top of that, reading the configuration register uses a
different opcode (0x15) than the existing SPINOR_OP_RDCR (0x35).

Used bottom protect to test s25fl512s flash part lock/unlock
on zc1751+dc1 board.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 372 +++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h    |   1 +
 2 files changed, 373 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 3e2491cc3e..42483caaaa 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -5465,6 +5465,370 @@ static int giga_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return ret;
 }
 #endif /* CONFIG_SPI_FLASH_GIGADEVICE */
+
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+/**
+ * spansion_read_cr() - read configuration register
+ * @nor: pointer to a 'struct spi_nor'.
+ *
+ * Spansion devices have top/bottom area protection bits selection into
+ * configuration reg. The bits in CR are OTP. So once it's written,
+ * it cannot be changed.
+ *
+ * Return: Value in configuration register or negative if error.
+ */
+static int spansion_read_cr(struct spi_nor *nor)
+{
+	int ret;
+	u8 val;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
+	if (ret < 0) {
+		dev_dbg(nor->dev, "error %d reading CR\n", ret);
+		return ret;
+	}
+
+	return val;
+}
+
+static void spansion_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+				      uint64_t *len)
+{
+	struct mtd_info *mtd = &nor->mtd;
+	int pow, cr;
+	int shift = 0;
+	u8 mask =  SR_BP0 | SR_BP1 | SR_BP2;
+
+	shift = ffs(mask) - 1;
+
+	cr = spansion_read_cr(nor);
+	if (!(sr & mask)) {
+		/* No protection */
+		*ofs = 0;
+		*len = 0;
+	} else {
+		pow = ((sr & mask) ^ mask) >> shift;
+		*len = mtd->size >> pow;
+		/* SPANSION device's have top/bottom select bit in configuration reg */
+		if (nor->flags & SNOR_F_HAS_SR_TB && cr & CR_TB_SPAN)
+			*ofs = 0;
+		else
+			*ofs = mtd->size - *len;
+	}
+}
+
+/**
+ * spansion_check_lock_status_sr() - check the status register and return
+ * the region is locked or unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ * @locked: locked:1 unlocked:0 value
+ *
+ * Return: 1 if the entire region is locked (if @locked is true) or unlocked (if
+ * @locked is false); 0 otherwise.
+ */
+static int spansion_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, u64 len,
+					 u8 sr, bool locked)
+{
+	loff_t lock_offs;
+	u64 lock_len;
+
+	if (!len)
+		return 1;
+
+	spansion_get_locked_range(nor, sr, &lock_offs, &lock_len);
+	if (locked)
+		/* Requested range is a sub-range of locked range */
+		return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+
+	/* Requested range does not overlap with locked range */
+	return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
+}
+
+/**
+ * spansion_is_locked_sr() - check if the memory region is locked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is locked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int spansion_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				 u8 sr)
+{
+	return spansion_check_lock_status_sr(nor, ofs, len, sr, true);
+}
+
+/**
+ * spansion_is_unlocked_sr() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ * @sr:  status register
+ *
+ * Check if memory region is unlocked.
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int spansion_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				   u8 sr)
+{
+	return spansion_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * spansion_is_unlocked() - check if the memory region is unlocked
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset of the flash
+ * @len: length to be locked
+ *
+ * check if memory region is unlocked
+ *
+ * Return: false if region is locked 0 otherwise.
+ */
+static int spansion_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int sr;
+
+	sr = read_sr(nor);
+	if (sr < 0)
+		return sr;
+
+	return spansion_check_lock_status_sr(nor, ofs, len, sr, false);
+}
+
+/**
+ * spansion_nor_select_zone() - Select top area or bottom area to lock/unlock
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ * @sr: status register
+ * @tb: pointer to top/bottom bool used in caller function
+ * @op: zone selection is for lock/unlock operation. 1: lock 0:unlock
+ *
+ * Select the top area / bottom area pattern to protect memory blocks.
+ *
+ * Return: negative on errors, 0 on success.
+ */
+static int spansion_nor_select_zone(struct spi_nor *nor, loff_t ofs, uint64_t len,
+				    u8 sr, bool *tb, bool op)
+{
+	int retval = 1;
+	bool can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, can_be_top = true;
+
+	if (op) {
+		/* Select for lock zone operation */
+
+		/*
+		 * If nothing in our range is unlocked, we don't need
+		 * to do anything.
+		 */
+		if (spansion_is_locked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is unlocked, we can't use 'bottom'
+		 * protection.
+		 */
+		if (!spansion_is_locked_sr(nor, 0, ofs, sr))
+			can_be_bottom = false;
+
+		/*
+		 * If anything above us is unlocked, we can't use 'top'
+		 * protection.
+		 */
+		if (!spansion_is_locked_sr(nor, ofs + len,
+					   nor->mtd.size - (ofs + len), sr))
+			can_be_top = false;
+	} else {
+		/* Select unlock zone */
+
+		/*
+		 * If nothing in our range is locked, we don't need to
+		 * do anything.
+		 */
+		if (spansion_is_unlocked_sr(nor, ofs, len, sr))
+			return 0;
+
+		/*
+		 * If anything below us is locked, we can't use 'top'
+		 * protection
+		 */
+		if (!spansion_is_unlocked_sr(nor, 0, ofs, sr))
+			can_be_top = false;
+
+		/*
+		 * If anything above us is locked, we can't use 'bottom'
+		 * protection
+		 */
+		if (!spansion_is_unlocked_sr(nor, ofs + len,
+					     nor->mtd.size - (ofs + len), sr))
+			can_be_bottom = false;
+	}
+
+	if (!can_be_bottom && !can_be_top)
+		return -EINVAL;
+
+	/* Prefer top, if both are valid */
+	*tb = can_be_top;
+	return retval;
+}
+
+static int spansion_write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
+{
+	int ret;
+
+	write_enable(nor);
+
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+	if (ret < 0) {
+		dev_dbg(nor->dev,
+			"error while writing configuration register\n");
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret) {
+		dev_dbg(nor->dev,
+			"timeout while writing configuration register\n");
+		return ret;
+	}
+
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/**
+ * spansion_flash_lock() - set BP[012] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to lock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Lock a region of the flash.Implementation is based on stm_lock
+ * Supports the block protection bits BP{0,1,2} in status register
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_flash_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	u64 lock_len;
+	u32 sector_size;
+	u16 n_sectors;
+	unsigned int bp_slots, bp_slots_needed;
+	int cr, can_be_top, val, status_old;
+	u8 pow, ret, shift, sr_cr[2];
+	bool use_top = false;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2;
+
+	shift = ffs(mask) - 1;
+
+	status_old = read_sr(nor);
+	/* if status reg is Write protected don't update bit protection */
+	if (status_old & SR_SRWD) {
+		dev_err(nor->dev,
+			"SR is write protected, can't update BP bits...\n");
+		return -EINVAL;
+	}
+
+	cr = spansion_read_cr(nor);
+	if (cr < 0)
+		return cr;
+
+	log_debug("SPI Protection: %s\n", (cr & CR_TB_SPAN) ? "bottom" : "top");
+
+	/* CR_TB is OTP, so we can't use 'top' protection if that is already set. */
+	can_be_top = !(cr & CR_TB_SPAN);
+
+	ret = spansion_nor_select_zone(nor, ofs, len, status_old, &use_top, 1);
+	/* Older protected blocks include the new requested block's */
+	if (ret <= 0)
+		return ret;
+
+	use_top = can_be_top;
+
+	/* lock_len: length of region that should end up locked */
+	if (use_top)
+		lock_len = nor->mtd.size - ofs;
+	else
+		lock_len = ofs + len;
+
+	sector_size = nor->sector_size;
+	n_sectors = (nor->size) / sector_size;
+
+	bp_slots = (1 << hweight8(mask)) - 2;
+	bp_slots_needed = ilog2(n_sectors);
+
+	if (bp_slots_needed > bp_slots)
+		sector_size <<= (bp_slots_needed - bp_slots);
+
+	pow = ilog2(lock_len) - ilog2(sector_size) + 1;
+	val = pow << shift;
+	sr_cr[0] = status_old & ~mask;
+	sr_cr[0] |= val;
+
+	sr_cr[1] = cr | (use_top ? 0 : CR_TB_SPAN);
+	ret = spansion_write_sr_cr(nor, sr_cr);
+	if (ret)
+		return ret;
+
+	/* Check that the bits got written as expected */
+	ret = read_sr(nor);
+	if (ret < 0)
+		return ret;
+
+	ret = spansion_read_cr(nor);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/**
+ * spansion_flash_unlock() - clear BP[012] write-protection.
+ * @nor: pointer to a 'struct spi_nor'.
+ * @ofs: offset from which to unlock memory.
+ * @len: number of bytes to unlock.
+ *
+ * Bits [234] of the Status Register are BP[012].
+ * Clear the corresponding BP status bits.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_flash_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+	int ret, val;
+	u8 mask = SR_BP0 | SR_BP1 | SR_BP2;
+
+	val = read_sr(nor);
+	if (val < 0)
+		return val;
+
+	if (!(val & mask))
+		return 0;
+
+	write_enable(nor);
+
+	write_sr(nor, val & ~mask);
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	ret = write_disable(nor);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif /* CONFIG_SPI_FLASH_SPANSION */
 #endif /* CONFIG_SPI_FLASH_LOCK */
 
 #ifdef CONFIG_SPI_FLASH_SOFT_RESET
@@ -5708,6 +6072,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	}
 #endif
 
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+	if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
+		nor->flash_lock = spansion_flash_lock;
+		nor->flash_unlock = spansion_flash_unlock;
+		nor->flash_is_unlocked = spansion_is_unlocked;
+	}
+#endif
+
 #ifdef CONFIG_SPI_FLASH_SST
 	/*
 	 * sst26 series block protection implementation differs from other
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 797f0f275a..07d6c4ff93 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -203,6 +203,7 @@
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
 #define CR_TB_MX		BIT(3)	/* Macronix Top/Bottom protect */
+#define CR_TB_SPAN		BIT(5)	/* Spansion Top/Bottom protect */
 
 /* Status Register 2 bits. */
 #define SR2_QUAD_EN_BIT7	BIT(7)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
  2023-12-06  9:31 ` [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR Tejas Bhumkar
@ 2023-12-20  7:29   ` Jagan Teki
  2023-12-31 17:57     ` Bhumkar, Tejas Arvind
  0 siblings, 1 reply; 35+ messages in thread
From: Jagan Teki @ 2023-12-20  7:29 UTC (permalink / raw)
  To: Tejas Bhumkar
  Cc: u-boot, joe.hershberger, rfried.dev, michal.simek, vigneshr, git,
	T Karthik Reddy

On Wed, Dec 6, 2023 at 3:02 PM Tejas Bhumkar
<tejas.arvind.bhumkar@amd.com> wrote:
>
> From: T Karthik Reddy <t.karthik.reddy@amd.com>
>
> The spi-nor framework will set up the flash parameters by
> reading the flash id table flags, which include cmd opcodes,
> address width, dummy bytes, and bus width. In case, flash
> supports octal DTR mode and the controller does not support
> the DTR. There is no process to switch back to SDR mode.
> To avoid this issue, create a Kconfig option SPI_FLASH_DTR_ENABLE
> to explicitly specify to enable/disable flash DTR support.
> This config is disabled by default.

We cannot control controller fixup in flash, DTR read based on the DTR
flag I don't think adding extra CONFIG to hack the controller with
impact is.

Jagan,

^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
  2023-12-20  7:29   ` Jagan Teki
@ 2023-12-31 17:57     ` Bhumkar, Tejas Arvind
  2024-01-29 12:21       ` Jagan Teki
  0 siblings, 1 reply; 35+ messages in thread
From: Bhumkar, Tejas Arvind @ 2023-12-31 17:57 UTC (permalink / raw)
  To: Jagan Teki
  Cc: u-boot, joe.hershberger, rfried.dev, Simek, Michal, vigneshr,
	git, T Karthik Reddy

[AMD Official Use Only - General]

Hi Jagan,

> -----Original Message-----
> From: Jagan Teki <jagan@amarulasolutions.com>
> Sent: Wednesday, December 20, 2023 1:00 PM
> To: Bhumkar, Tejas Arvind <tejas.arvind.bhumkar@amd.com>
> Cc: u-boot@lists.denx.de; joe.hershberger@ni.com; rfried.dev@gmail.com;
> Simek, Michal <michal.simek@amd.com>; vigneshr@ti.com; git@xilinx.com; T
> Karthik Reddy <t.karthik.reddy@amd.com>
> Subject: Re: [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> On Wed, Dec 6, 2023 at 3:02 PM Tejas Bhumkar
> <tejas.arvind.bhumkar@amd.com> wrote:
> >
> > From: T Karthik Reddy <t.karthik.reddy@amd.com>
> >
> > The spi-nor framework will set up the flash parameters by reading the
> > flash id table flags, which include cmd opcodes, address width, dummy
> > bytes, and bus width. In case, flash supports octal DTR mode and the
> > controller does not support the DTR. There is no process to switch
> > back to SDR mode.
> > To avoid this issue, create a Kconfig option SPI_FLASH_DTR_ENABLE to
> > explicitly specify to enable/disable flash DTR support.
> > This config is disabled by default.
>
> We cannot control controller fixup in flash, DTR read based on the DTR flag I don't
> think adding extra CONFIG to hack the controller with impact is.
[Tejas] : By default, this configuration is set to Disabled. It serves as a convenient option for operating the flash
        between SDR and DDR without requiring any adjustments to the nor-id table flags.

Regards,
Tejas.
>
> Jagan,

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
  2023-12-31 17:57     ` Bhumkar, Tejas Arvind
@ 2024-01-29 12:21       ` Jagan Teki
  0 siblings, 0 replies; 35+ messages in thread
From: Jagan Teki @ 2024-01-29 12:21 UTC (permalink / raw)
  To: Bhumkar, Tejas Arvind
  Cc: u-boot, joe.hershberger, rfried.dev, Simek, Michal, vigneshr,
	git, T Karthik Reddy

On Sun, Dec 31, 2023 at 11:27 PM Bhumkar, Tejas Arvind
<tejas.arvind.bhumkar@amd.com> wrote:
>
> [AMD Official Use Only - General]
>
> Hi Jagan,
>
> > -----Original Message-----
> > From: Jagan Teki <jagan@amarulasolutions.com>
> > Sent: Wednesday, December 20, 2023 1:00 PM
> > To: Bhumkar, Tejas Arvind <tejas.arvind.bhumkar@amd.com>
> > Cc: u-boot@lists.denx.de; joe.hershberger@ni.com; rfried.dev@gmail.com;
> > Simek, Michal <michal.simek@amd.com>; vigneshr@ti.com; git@xilinx.com; T
> > Karthik Reddy <t.karthik.reddy@amd.com>
> > Subject: Re: [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR
> >
> > Caution: This message originated from an External Source. Use proper caution
> > when opening attachments, clicking links, or responding.
> >
> >
> > On Wed, Dec 6, 2023 at 3:02 PM Tejas Bhumkar
> > <tejas.arvind.bhumkar@amd.com> wrote:
> > >
> > > From: T Karthik Reddy <t.karthik.reddy@amd.com>
> > >
> > > The spi-nor framework will set up the flash parameters by reading the
> > > flash id table flags, which include cmd opcodes, address width, dummy
> > > bytes, and bus width. In case, flash supports octal DTR mode and the
> > > controller does not support the DTR. There is no process to switch
> > > back to SDR mode.
> > > To avoid this issue, create a Kconfig option SPI_FLASH_DTR_ENABLE to
> > > explicitly specify to enable/disable flash DTR support.
> > > This config is disabled by default.
> >
> > We cannot control controller fixup in flash, DTR read based on the DTR flag I don't
> > think adding extra CONFIG to hack the controller with impact is.
> [Tejas] : By default, this configuration is set to Disabled. It serves as a convenient option for operating the flash
>         between SDR and DDR without requiring any adjustments to the nor-id table flags.

This look like controller hack to me, may be a clear negotiation b/w
controller and flag might have proper solution.

Jagan.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures
  2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
                   ` (29 preceding siblings ...)
  2023-12-06  9:31 ` [PATCH v2 30/30] mtd: spi-nor: Add support for locking on Spansion " Tejas Bhumkar
@ 2024-01-29 12:23 ` Jagan Teki
  30 siblings, 0 replies; 35+ messages in thread
From: Jagan Teki @ 2024-01-29 12:23 UTC (permalink / raw)
  To: Tejas Bhumkar
  Cc: u-boot, joe.hershberger, rfried.dev, michal.simek, vigneshr, git

On Wed, Dec 6, 2023 at 3:02 PM Tejas Bhumkar
<tejas.arvind.bhumkar@amd.com> wrote:
>
> A set of patches has been developed to resolve concerns regarding data
> integrity failures in QSPI and OSPI for the Versal, Versal NET, Zynq,
> and ZynqMP platforms.
>
> The series has undergone testing with flashes on the default setup,
> and comprehensive testing is currently underway to test the series
> with all available flash parts.
>
> These patches are built upon the v5 series, which can be found at the
> following link:
> https://lore.kernel.org/all/20231201031839.239567-1-venkatesh.abbarapu@amd.com/
>
> Changes in v2:
> - Removed the SPI_NOR_HAS_TB flag for gd25lx256e and is25wx256 flashes
>   since it already exists in a tree.
>
> Algapally Santosh Sagar (1):
>   mtd: spi-nor-ids: Add support for W25Q02NW
>
> Ashok Reddy Soma (10):
>   mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes
>   mtd: spi-nor: Add support for cross die read in dual flash
>     configuration
>   mtd: spi-nor: Enable DTR octal flash program
>   mtd: spi-nor: Send write disable cmd after every write enable
>   mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled
>   spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns
>   spi: cadence_qspi: Clean up registers in init
>   spi: cadence_qspi: Initialize read and write watermark registers
>   spi: cadence_qspi: Enable ECO bit for higher frequencies
>   spi: cadence_qspi: Write aligned byte length to ahbbase
>
> T Karthik Reddy (9):
>   mtd: spi-nor: Add config to enable flash DTR
>   mtd: spi-nor-core: Set dummy buswidth equal to data buswidth
>   spi: mtd: Use split reads if multi-die flag is set
>   mtd: spi-nor: program quad enable bit for winbond flashes
>   spi: cadence_qspi: Setup ddr mode in cadence qspi driver
>   spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config
>   spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal
>     driver
>   spi: cadence_qspi: Add spi mem dtr support ops
>   mtd: spi-nor: Add block protection support for micron flashes
>
> Tejas Bhumkar (5):
>   arm64: versal: Enable defconfig for Micron octal flashes
>   mtd: spi-nor: Update erase operation function
>   spi: cadence_qspi: Fix versal ospi indirect write timed out issue
>   arm64: versal: Enable soft reset support for xspi flashes
>   arm64: versal: Enable octal DTR mode
>
> Venkatesh Yadav Abbarapu (5):
>   mtd: spi-nor: Update block protection flags for flash parts
>   mtd: spi-nor: Add support for locking on Macronix nor flashes
>   mtd: spi-nor: Add support for locking on ISSI nor flashes
>   mtd: spi-nor: Add support for locking on GIGADEVICE nor flashes
>   mtd: spi-nor: Add support for locking on Spansion nor flashes

Look like there are 3 or more topics are covered in single patch set,
please break them and send separate series.

Jagan

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2024-01-29 12:24 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-06  9:31 [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 01/30] mtd: spi-nor: Add config to enable flash DTR Tejas Bhumkar
2023-12-20  7:29   ` Jagan Teki
2023-12-31 17:57     ` Bhumkar, Tejas Arvind
2024-01-29 12:21       ` Jagan Teki
2023-12-06  9:31 ` [PATCH v2 02/30] mtd: spi-nor-core: Set dummy buswidth equal to data buswidth Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 03/30] arm64: versal: Enable defconfig for Micron octal flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 04/30] mtd: spi-nor: Enable mt35xu512aba_fixups for all mt35xx flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 05/30] mtd: spi-nor: Add support for cross die read in dual flash configuration Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 06/30] mtd: spi-nor: Enable DTR octal flash program Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 07/30] spi: mtd: Use split reads if multi-die flag is set Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 08/30] mtd: spi-nor: Update block protection flags for flash parts Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 09/30] mtd: spi-nor-ids: Add support for W25Q02NW Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 10/30] mtd: spi-nor: program quad enable bit for winbond flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 11/30] mtd: spi-nor: Send write disable cmd after every write enable Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 12/30] mtd: spi-nor: Update erase operation function Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 13/30] mtd: spi-nor: Check SNOR_F_IO_MODE_EN_VOLATILE only if SFDP is enabled Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 14/30] spi: cadence_qspi: Setup ddr mode in cadence qspi driver Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 15/30] spi: cadence-qspi: Switch SDR/DTR using SPI_FLASH_DTR_ENABLE config Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 16/30] spi: cadence_ospi_versal: ospi ddr changes in cadence ospi versal driver Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 17/30] spi: cadence_qspi: Fix versal ospi indirect write timed out issue Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 18/30] spi: cadence_qspi: Set tshsl_ns to at least one sclk_ns Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 19/30] spi: cadence_qspi: Clean up registers in init Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 20/30] spi: cadence_qspi: Initialize read and write watermark registers Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 21/30] spi: cadence_qspi: Enable ECO bit for higher frequencies Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 22/30] spi: cadence_qspi: Add spi mem dtr support ops Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 23/30] spi: cadence_qspi: Write aligned byte length to ahbbase Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 24/30] arm64: versal: Enable soft reset support for xspi flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 25/30] arm64: versal: Enable octal DTR mode Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 26/30] mtd: spi-nor: Add block protection support for micron flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 27/30] mtd: spi-nor: Add support for locking on Macronix nor flashes Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 28/30] mtd: spi-nor: Add support for locking on ISSI " Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 29/30] mtd: spi-nor: Add support for locking on GIGADEVICE " Tejas Bhumkar
2023-12-06  9:31 ` [PATCH v2 30/30] mtd: spi-nor: Add support for locking on Spansion " Tejas Bhumkar
2024-01-29 12:23 ` [PATCH v2 00/30] Fix issues with QSPI and OSPI compare failures Jagan Teki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.