Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 00/11] Add support for StarFive JHB100 SFC
@ 2026-08-03 12:40 Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 01/11] spi: dw: Introduce spi_frf and STD_SPI Changhuang Liang
                   ` (11 more replies)
  0 siblings, 12 replies; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

This serial add support for the StarFive JHB100 SoC SPI Flash
Controller (SFC), which is based on the Synopsys DesignWare SSI
version 2.00a but with some customizations and it also add
enhanced SPI for DesignWare SPI controllers.

I picked up some patches from series [1].
This series depends on the series [2]:

[1] https://lore.kernel.org/all/20221212180732.79167-1-sudip.mukherjee@sifive.com/
[2] https://lore.kernel.org/all/20260521012932.24163-1-changhuang.liang@starfivetech.com/

Change since v1:
- Reordered the series so that every symbol is introduced before its
  first user and each patch is independently reviewable.
- adjust_op_size(): clamp DATA_IN to DW_SPI_NDF_MASK + 1 and DATA_OUT to
  DW_SPI_NDF_MASK, matching the two NDF programming conventions (receive
  writes ndf - 1, transmit writes ndf).
- exec_enh_mem_op(): use op->max_freq rather than mem->spi->max_speed_hz
- detect_enh_mode(): clear the accumulated mode bits when clock
  stretching turns out to be unavailable.
- dw_spi_jhb100_set_addr_nbyte() returns an error code, rejects anything
  other than 3 or 4 address bytes with a message.
- platform_suspend() and platform_resume() callbacks are introduced to
  handle platform-private suspend/resume procedures. JHB100 SFC used it
  to re-mask the SFC filter interrupts after a system resume.
- Limit address bytes supported by JHB100 SFC.
- Remove underflow/overflow interrupts in enhanced SPI mode, as clock
  stretching ensures these will not occur.

v1: https://lore.kernel.org/all/20260709055204.138168-1-changhuang.liang@starfivetech.com/

Changhuang Liang (2):
  spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc
  spi: dw: Add support for StarFive JHB100 SoC SFC

Sudip Mukherjee (9):
  spi: dw: Introduce spi_frf and STD_SPI
  spi: dw: update SPI_CTRLR0 register
  spi: dw: add check for support of enhanced spi
  spi: dw: adjust size of mem_op
  spi: dw: update NDF while using enhanced spi mode
  spi: dw: Introduce enhanced single/dual/quad/octal spi
  spi: dw: send cmd and addr to start the spi transfer
  spi: dw: use irq handler for enhanced spi
  spi: dw: detect enhanced spi mode

 .../bindings/spi/snps,dw-apb-ssi.yaml         |  22 +
 drivers/spi/spi-dw-core.c                     | 386 +++++++++++++++++-
 drivers/spi/spi-dw-mmio.c                     |  72 ++++
 drivers/spi/spi-dw.h                          |  52 ++-
 4 files changed, 521 insertions(+), 11 deletions(-)

--
2.25.1

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

* [PATCH v2 01/11] spi: dw: Introduce spi_frf and STD_SPI
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 02/11] spi: dw: update SPI_CTRLR0 register Changhuang Liang
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

The DW APB SSI controllers of v4.x and newer and DW AHB SSI controllers
supports enhanced SPI modes which can be defined from SPI_FRF of
DW_SPI_CTRLR0 register. Without enhanced mode, these controllers will
work in the standard spi mode.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 13 ++++++++++++-
 drivers/spi/spi-dw.h      |  7 +++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index eff08461c2f5..86e3e7487bc7 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -330,6 +330,16 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 		/* CTRLR0[11:10] Transfer Mode */
 		cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode);
 
+	if (dw_spi_ver_is_ge(dws, HSSI, 103A)) {
+		cr0 &= ~DW_HSSI_CTRLR0_SPI_FRF_MASK;
+		cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_SPI_FRF_MASK,
+				  cfg->spi_frf);
+	} else if (dw_spi_ver_is_ge(dws, PSSI, 400A)) {
+		cr0 &= ~DW_PSSI_CTRLR0_SPI_FRF_MASK;
+		cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_SPI_FRF_MASK,
+				  cfg->spi_frf);
+	}
+
 	dw_writel(dws, DW_SPI_CTRLR0, cr0);
 
 	if (spi_controller_is_target(dws->ctlr))
@@ -422,6 +432,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
 		.tmode = DW_SPI_CTRLR0_TMOD_TR,
 		.dfs = transfer->bits_per_word,
 		.freq = transfer->speed_hz,
+		.spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI,
 	};
 	int ret;
 
@@ -677,7 +688,7 @@ static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi)
 static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
 {
 	struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
-	struct dw_spi_cfg cfg;
+	struct dw_spi_cfg cfg = {0};
 	unsigned long flags;
 	int ret;
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 2f2debc64e73..19cf1b1a5d4f 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -17,6 +17,9 @@
 
 /* Synopsys DW SSI component versions (FourCC sequence) */
 #define DW_HSSI_102A			0x3130322a
+#define DW_HSSI_103A			0x3130332a
+#define DW_HSSI_200A			0x3230302a
+#define DW_PSSI_400A			0x3430302a
 
 /* DW SSI IP-core ID and version check helpers */
 #define dw_spi_ip_is(_dws, _ip) \
@@ -94,6 +97,9 @@
 #define DW_HSSI_CTRLR0_TMOD_MASK		GENMASK(11, 10)
 #define DW_HSSI_CTRLR0_SRL			BIT(13)
 #define DW_HSSI_CTRLR0_MST			BIT(31)
+#define DW_HSSI_CTRLR0_SPI_FRF_MASK		GENMASK(23, 22)
+#define DW_PSSI_CTRLR0_SPI_FRF_MASK		GENMASK(22, 21)
+#define DW_SPI_CTRLR0_SPI_FRF_STD_SPI		0x0
 
 /* Bit fields in CTRLR1 */
 #define DW_SPI_NDF_MASK				GENMASK(15, 0)
@@ -135,6 +141,7 @@ struct dw_spi_cfg {
 	u8 dfs;
 	u32 ndf;
 	u32 freq;
+	u8 spi_frf;
 };
 
 struct dw_spi;
-- 
2.25.1


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

* [PATCH v2 02/11] spi: dw: update SPI_CTRLR0 register
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 01/11] spi: dw: Introduce spi_frf and STD_SPI Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 03/11] spi: dw: add check for support of enhanced spi Changhuang Liang
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

If the SPI transfer is being done in enhanced mode then SPI_CTRLR0
register needs to be updated to mention the instruction length, address
length, address and instruction transfer format, wait cycles. And, we
also need to enable clock stretching.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 15 ++++++++++++---
 drivers/spi/spi-dw.h      | 17 ++++++++++++++++-
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 86e3e7487bc7..7601a53d6f34 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -313,7 +313,7 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
 }
 
 void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
-			  struct dw_spi_cfg *cfg)
+			  struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg)
 {
 	struct dw_spi_chip_data *chip = spi_get_ctldata(spi);
 	u32 cr0 = chip->cr0;
@@ -363,6 +363,15 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 		dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly);
 		dws->cur_rx_sample_dly = chip->rx_sample_dly;
 	}
+
+	if (enh_cfg) {
+		cr0 = DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN;
+		cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, enh_cfg->wait_c);
+		cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, enh_cfg->inst_l);
+		cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, enh_cfg->addr_l);
+		cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, enh_cfg->trans_t);
+		dw_writel(dws, DW_SPI_SPI_CTRLR0, cr0);
+	}
 }
 EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE");
 
@@ -448,7 +457,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
 
 	dw_spi_enable_chip(dws, 0);
 
-	dw_spi_update_config(dws, spi, &cfg);
+	dw_spi_update_config(dws, spi, &cfg, NULL);
 
 	transfer->effective_speed_hz = dws->current_freq;
 
@@ -715,7 +724,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
 
 	dw_spi_enable_chip(dws, 0);
 
-	dw_spi_update_config(dws, mem->spi, &cfg);
+	dw_spi_update_config(dws, mem->spi, &cfg, NULL);
 
 	dw_spi_mask_intr(dws, 0xff);
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 19cf1b1a5d4f..16a8c7ab7364 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -63,6 +63,7 @@
 #define DW_SPI_VERSION			0x5c
 #define DW_SPI_DR			0x60
 #define DW_SPI_RX_SAMPLE_DLY		0xf0
+#define DW_SPI_SPI_CTRLR0		0xf4
 #define DW_SPI_CS_OVERRIDE		0xf4
 
 /* Bit fields in CTRLR0 (DWC APB SSI) */
@@ -127,6 +128,13 @@
 #define DW_SPI_DMACR_RDMAE			BIT(0)
 #define DW_SPI_DMACR_TDMAE			BIT(1)
 
+/* Bit fields in SPI_CTRLR0 */
+#define DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN	BIT(30)
+#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK	GENMASK(15, 11)
+#define DW_SPI_ENH_CTRLR0_INST_L_MASK		GENMASK(9, 8)
+#define DW_SPI_ENH_CTRLR0_ADDR_L_MASK		GENMASK(5, 2)
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK	GENMASK(1, 0)
+
 /* Mem/DMA operations helpers */
 #define DW_SPI_WAIT_RETRIES			5
 #define DW_SPI_BUF_SIZE \
@@ -144,6 +152,13 @@ struct dw_spi_cfg {
 	u8 spi_frf;
 };
 
+struct dw_spi_enh_cfg {
+	u8 wait_c;
+	u8 inst_l;
+	u8 addr_l;
+	u8 trans_t;
+};
+
 struct dw_spi;
 struct dw_spi_dma_ops {
 	int (*dma_init)(struct device *dev, struct dw_spi *dws);
@@ -294,7 +309,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
 
 extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
 extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
-				 struct dw_spi_cfg *cfg);
+				 struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg);
 extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
 extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
 extern void dw_spi_remove_controller(struct dw_spi *dws);
-- 
2.25.1


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

* [PATCH v2 03/11] spi: dw: add check for support of enhanced spi
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 01/11] spi: dw: Introduce spi_frf and STD_SPI Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 02/11] spi: dw: update SPI_CTRLR0 register Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:53   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 04/11] spi: dw: adjust size of mem_op Changhuang Liang
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

Before doing the mem op, spi controller will be queried about the
buswidths it supports. Add the single/dual/quad/octal if the controller
has the DW_SPI_CAP_EMODE capability.
The DW_SPI_CAP_EMODE capability will be enabled in a later patch.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 34 +++++++++++++++++++++++++++++++++-
 drivers/spi/spi-dw.h      |  1 +
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 7601a53d6f34..7275b7a9945f 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -518,6 +518,35 @@ static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
 	return 0;
 }
 
+static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
+				       const struct spi_mem_op *op)
+{
+	if (op->addr.nbytes != 0 && op->addr.buswidth != 1 &&
+	    op->addr.buswidth != op->data.buswidth)
+		return false;
+
+	if (op->addr.nbytes >= 8)
+		return false;
+
+	if (op->cmd.buswidth != 1 && op->cmd.buswidth != op->addr.buswidth &&
+	    op->cmd.buswidth != op->data.buswidth)
+		return false;
+
+	if (op->dummy.nbytes && !op->dummy.buswidth)
+		return false;
+
+	if (op->dummy.nbytes != 0 && op->data.dir == SPI_MEM_DATA_OUT)
+		return false;
+
+	/* WAIT_CYCLES is a 5-bit field in SPI_CTRLR0 */
+	if (op->dummy.nbytes != 0 &&
+	    op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth >
+	    FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
+		return false;
+
+	return spi_mem_default_supports_op(mem, op);
+}
+
 static bool dw_spi_supports_mem_op(struct spi_mem *mem,
 				   const struct spi_mem_op *op)
 {
@@ -800,7 +829,10 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws)
 	if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
 	    !dws->set_cs) {
 		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
-		dws->mem_ops.supports_op = dw_spi_supports_mem_op;
+		if (dws->caps & DW_SPI_CAP_EMODE)
+			dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
+		else
+			dws->mem_ops.supports_op = dw_spi_supports_mem_op;
 		dws->mem_ops.exec_op = dw_spi_exec_mem_op;
 		if (!dws->max_mem_freq)
 			dws->max_mem_freq = dws->max_freq;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 16a8c7ab7364..81a433ab759b 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -35,6 +35,7 @@
 /* DW SPI controller capabilities */
 #define DW_SPI_CAP_CS_OVERRIDE		BIT(0)
 #define DW_SPI_CAP_DFS32		BIT(1)
+#define DW_SPI_CAP_EMODE		BIT(2)
 
 /* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
 #define DW_SPI_CTRLR0			0x00
-- 
2.25.1


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

* [PATCH v2 04/11] spi: dw: adjust size of mem_op
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (2 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 03/11] spi: dw: add check for support of enhanced spi Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode Changhuang Liang
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

In enhanced mode adjust the size of the data that can be sent or received
as this will then be used to set the NDF.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 7275b7a9945f..c7cd24337188 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -510,6 +510,16 @@ static int dw_spi_target_abort(struct spi_controller *ctlr)
 	return 0;
 }
 
+static int dw_spi_adjust_enh_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	if (op->data.dir == SPI_MEM_DATA_IN)
+		op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1);
+	else
+		op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK);
+
+	return 0;
+}
+
 static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
 {
 	if (op->data.dir == SPI_MEM_DATA_IN)
@@ -829,10 +839,14 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws)
 	if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
 	    !dws->set_cs) {
 		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
-		if (dws->caps & DW_SPI_CAP_EMODE)
+		if (dws->caps & DW_SPI_CAP_EMODE) {
 			dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
-		else
+			dws->mem_ops.adjust_op_size = dw_spi_adjust_enh_mem_op_size;
+		} else {
 			dws->mem_ops.supports_op = dw_spi_supports_mem_op;
+			dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
+		}
+
 		dws->mem_ops.exec_op = dw_spi_exec_mem_op;
 		if (!dws->max_mem_freq)
 			dws->max_mem_freq = dws->max_freq;
-- 
2.25.1


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

* [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (3 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 04/11] spi: dw: adjust size of mem_op Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 13:02   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi Changhuang Liang
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

If the transfer of Transmit only mode is using enhanced SPI then
NDF needs to be updated with the number of data frames.
If the Transmit FIFO goes empty in-between, DWC_ssi masks the serial
clock and wait for rest of the data until the programmed amount of
frames are transferred successfully.
In receive mode, the number of data frames received is equal to this
register value plus 1.
In transmit mode, the number of data frames is equal this register
value.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index c7cd24337188..5f215e76a7ad 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -348,6 +348,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 	if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
 	    cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
 		dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
+	else if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO &&
+		 dws->caps & DW_SPI_CAP_EMODE)
+		dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf);
 
 	/* Note DW APB SSI clock divider doesn't support odd numbers */
 	clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe;
-- 
2.25.1


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

* [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (4 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:58   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer Changhuang Liang
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

If the spi transfer is using enhanced single/dual/quad/octal spi mode,
then we need to update the SPI_CTRLR0 register. The SPI_CTRLR0 register
will be updated in dw_spi_update_config() via the values in
dw_spi_enh_cfg.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 87 ++++++++++++++++++++++++++++++++++++++-
 drivers/spi/spi-dw.h      |  9 ++++
 2 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 5f215e76a7ad..7441e8db0ea7 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -828,6 +828,90 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
 	return ret;
 }
 
+static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)
+{
+	dws->n_bytes = 1;
+	if (op->data.dir == SPI_MEM_DATA_IN) {
+		dws->rx = op->data.buf.in;
+		dws->rx_len = op->data.nbytes;
+		dws->tx = NULL;
+		dws->tx_len = 0;
+	} else if (op->data.dir == SPI_MEM_DATA_OUT) {
+		dws->tx_len = op->data.nbytes;
+		dws->tx = (void *)op->data.buf.out;
+		dws->rx = NULL;
+		dws->rx_len = 0;
+	} else {
+		dws->rx = NULL;
+		dws->rx_len = 0;
+		dws->tx = NULL;
+		dws->tx_len = 0;
+	}
+}
+
+static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
+	struct dw_spi_enh_cfg enh_cfg = {0};
+	struct dw_spi_cfg cfg = {0};
+
+	switch (op->data.buswidth) {
+	case 0:
+	case 1:
+		cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI;
+		break;
+	case 2:
+		cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI;
+		break;
+	case 4:
+		cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI;
+		break;
+	case 8:
+		cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dw_spi_init_enh_mem_buf(dws, op);
+
+	cfg.dfs = 8;
+	cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
+	cfg.ndf = op->data.nbytes;
+	if (op->data.dir == SPI_MEM_DATA_IN)
+		cfg.tmode = DW_SPI_CTRLR0_TMOD_RO;
+	else
+		cfg.tmode = DW_SPI_CTRLR0_TMOD_TO;
+
+	if (op->data.buswidth == op->addr.buswidth &&
+	    op->data.buswidth == op->cmd.buswidth)
+		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2;
+	else if (op->data.buswidth == op->addr.buswidth)
+		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1;
+	else
+		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0;
+
+	enh_cfg.addr_l = op->addr.nbytes << 1;
+	if (op->cmd.nbytes == 2)
+		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L16;
+	else if (op->cmd.nbytes == 1)
+		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L8;
+	else
+		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L0;
+
+	if (op->dummy.buswidth)
+		enh_cfg.wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
+
+	dw_spi_enable_chip(dws, 0);
+
+	dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
+
+	dw_spi_enable_chip(dws, 1);
+
+	return 0;
+}
+
 /*
  * Initialize the default memory operations if a glue layer hasn't specified
  * custom ones. Direct mapping operations will be preserved anyway since DW SPI
@@ -843,14 +927,15 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws)
 	    !dws->set_cs) {
 		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
 		if (dws->caps & DW_SPI_CAP_EMODE) {
+			dws->mem_ops.exec_op = dw_spi_exec_enh_mem_op;
 			dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
 			dws->mem_ops.adjust_op_size = dw_spi_adjust_enh_mem_op_size;
 		} else {
+			dws->mem_ops.exec_op = dw_spi_exec_mem_op;
 			dws->mem_ops.supports_op = dw_spi_supports_mem_op;
 			dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
 		}
 
-		dws->mem_ops.exec_op = dw_spi_exec_mem_op;
 		if (!dws->max_mem_freq)
 			dws->max_mem_freq = dws->max_freq;
 	}
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 81a433ab759b..2dae81c15423 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -102,6 +102,9 @@
 #define DW_HSSI_CTRLR0_SPI_FRF_MASK		GENMASK(23, 22)
 #define DW_PSSI_CTRLR0_SPI_FRF_MASK		GENMASK(22, 21)
 #define DW_SPI_CTRLR0_SPI_FRF_STD_SPI		0x0
+#define DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI		0x1
+#define DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI		0x2
+#define DW_SPI_CTRLR0_SPI_FRF_OCT_SPI		0x3
 
 /* Bit fields in CTRLR1 */
 #define DW_SPI_NDF_MASK				GENMASK(15, 0)
@@ -133,7 +136,13 @@
 #define DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN	BIT(30)
 #define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK	GENMASK(15, 11)
 #define DW_SPI_ENH_CTRLR0_INST_L_MASK		GENMASK(9, 8)
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L0	0x0
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L8	0x2
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L16	0x3
 #define DW_SPI_ENH_CTRLR0_ADDR_L_MASK		GENMASK(5, 2)
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0	0x0
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1	0x1
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2	0x2
 #define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK	GENMASK(1, 0)
 
 /* Mem/DMA operations helpers */
-- 
2.25.1


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

* [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (5 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:59   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi Changhuang Liang
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

In enhanced spi mode, read or write will start by sending the cmd
and address (if present).

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 7441e8db0ea7..2b3149ac64b3 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -849,6 +849,19 @@ static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op
 	}
 }
 
+static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op)
+{
+	/* Send cmd as 32 bit value */
+	dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
+	if (op->addr.nbytes) {
+		dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));
+		if (op->addr.nbytes > 4) {
+			/* address more than 32bit */
+			dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
+		}
+	}
+}
+
 static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
 {
 	struct spi_controller *ctlr = mem->spi->controller;
@@ -909,6 +922,8 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
 
 	dw_spi_enable_chip(dws, 1);
 
+	dw_spi_enh_write_cmd_addr(dws, op);
+
 	return 0;
 }
 
-- 
2.25.1


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

* [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (6 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:59   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 09/11] spi: dw: detect enhanced spi mode Changhuang Liang
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

Introduce the interrupt handler for enhanced spi to read or write based
on the generated irq. Also, use the xfer_completion from spi_controller
to wait for a timeout or completion from irq handler.

In enhanced mode we need to calculate RXFTLR based on the length of data
we are expecting to receive or the fifo length.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 106 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 104 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 2b3149ac64b3..a1c6fa1a780b 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -248,6 +248,29 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t dw_spi_enh_handler(struct dw_spi *dws)
+{
+	u16 irq_status = dw_readl(dws, DW_SPI_ISR);
+
+	if (irq_status & DW_SPI_INT_RXFI) {
+		dw_reader(dws);
+		if (dws->rx_len && dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR))
+			dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
+	}
+
+	if (irq_status & DW_SPI_INT_TXEI)
+		dw_writer(dws);
+
+	if (!dws->tx_len && dws->rx_len) {
+		dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
+	} else if (!dws->rx_len && !dws->tx_len) {
+		dw_spi_mask_intr(dws, 0xff);
+		spi_finalize_current_transfer(dws->ctlr);
+	}
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t dw_spi_irq(int irq, void *dev_id)
 {
 	struct spi_controller *ctlr = dev_id;
@@ -257,10 +280,17 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
 	if (!irq_status)
 		return IRQ_NONE;
 
-	if (!ctlr->cur_msg) {
+	if (!dws->transfer_handler ||
+	    (!ctlr->cur_msg && dws->transfer_handler == dw_spi_transfer_handler)) {
 		dw_spi_mask_intr(dws, 0xff);
 		return IRQ_HANDLED;
 	}
+	if (dws->transfer_handler == dw_spi_enh_handler &&
+	    !dws->rx_len && !dws->tx_len) {
+		dw_spi_mask_intr(dws, 0xff);
+		spi_finalize_current_transfer(ctlr);
+		return IRQ_HANDLED;
+	}
 
 	return dws->transfer_handler(dws);
 }
@@ -399,6 +429,33 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
 	dw_spi_umask_intr(dws, imask);
 }
 
+static void dw_spi_enh_irq_setup(struct dw_spi *dws)
+{
+	u16 level;
+	u8 imask;
+
+	/*
+	 * Originally Tx and Rx data lengths match. Rx FIFO Threshold level
+	 * will be adjusted at the final stage of the IRQ-based SPI transfer
+	 * execution so not to lose the leftover of the incoming data.
+	 */
+	level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
+	dw_writel(dws, DW_SPI_TXFTLR, level);
+
+	/*
+	 * In enhanced mode if we are reading then tx_len is 0 as we
+	 * have nothing to transmit. Calculate DW_SPI_RXFTLR with
+	 * rx_len.
+	 */
+	level = min_t(unsigned int, dws->fifo_len / 2, dws->rx_len);
+	dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0);
+
+	dws->transfer_handler = dw_spi_enh_handler;
+
+	imask = DW_SPI_INT_TXEI | DW_SPI_INT_RXFI;
+	dw_spi_umask_intr(dws, imask);
+}
+
 /*
  * The iterative procedure of the poll-based transfer is simple: write as much
  * as possible to the Tx FIFO, wait until the pending to receive data is ready
@@ -868,6 +925,8 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
 	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
 	struct dw_spi_enh_cfg enh_cfg = {0};
 	struct dw_spi_cfg cfg = {0};
+	unsigned long long ms;
+	int ret;
 
 	switch (op->data.buswidth) {
 	case 0:
@@ -920,11 +979,54 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
 
 	dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
 
+	dw_spi_mask_intr(dws, 0xff);
+	reinit_completion(&ctlr->xfer_completion);
 	dw_spi_enable_chip(dws, 1);
 
 	dw_spi_enh_write_cmd_addr(dws, op);
+	dw_spi_set_cs(mem->spi, false);
 
-	return 0;
+	/*
+	 * FIXME: The exact reason for this delay is not fully understood,
+	 * but empirical testing shows it significantly improves the stability
+	 * of read/write operations. Without this delay, occasional transfer
+	 * errors or timeouts may occur under certain conditions.
+	 * Keeping it as a safeguard based on practical validation.
+	 */
+	udelay(5);
+
+	dw_spi_enh_irq_setup(dws);
+
+	/* Use timeout calculation from spi_transfer_wait() */
+	ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
+	do_div(ms, dws->current_freq);
+
+	/*
+	 * Increase it twice and add 200 ms tolerance, use
+	 * predefined maximum in case of overflow.
+	 */
+	ms += ms + 200;
+	if (ms > UINT_MAX)
+		ms = UINT_MAX;
+
+	ms = wait_for_completion_timeout(&ctlr->xfer_completion,
+					 msecs_to_jiffies(ms));
+	if (ms == 0) {
+		dw_spi_mask_intr(dws, 0xff);
+		synchronize_irq(dws->irq);
+		dws->rx = NULL;
+		dws->tx = NULL;
+		dws->rx_len = 0;
+		dws->tx_len = 0;
+		dw_spi_stop_mem_op(dws, mem->spi);
+		return -EIO;
+	}
+
+	ret = dw_spi_wait_mem_op_done(dws);
+
+	dw_spi_stop_mem_op(dws, mem->spi);
+
+	return ret;
 }
 
 /*
-- 
2.25.1


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

* [PATCH v2 09/11] spi: dw: detect enhanced spi mode
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (7 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 13:07   ` sashiko-bot
  2026-08-03 12:40 ` [PATCH v2 10/11] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc Changhuang Liang
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

From: Sudip Mukherjee <sudip.mukherjee@sifive.com>

All the SSI controllers supporting enhanced spi modes might not support
all the three dual or quad or octal modes. Detect the modes that are
supported and finally enable the DW_SPI_CAP_EMODE capability which will
start using all the enhanced spi functions that has been added.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee@sifive.com>
Co-developed-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 75 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index a1c6fa1a780b..0d900afa015d 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -1029,6 +1029,12 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
 	return ret;
 }
 
+static bool dw_spi_can_use_mem_ops(struct dw_spi *dws)
+{
+	return !dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
+	       !dws->set_cs;
+}
+
 /*
  * Initialize the default memory operations if a glue layer hasn't specified
  * custom ones. Direct mapping operations will be preserved anyway since DW SPI
@@ -1040,8 +1046,7 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
  */
 static void dw_spi_init_mem_ops(struct dw_spi *dws)
 {
-	if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
-	    !dws->set_cs) {
+	if (dw_spi_can_use_mem_ops(dws)) {
 		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
 		if (dws->caps & DW_SPI_CAP_EMODE) {
 			dws->mem_ops.exec_op = dw_spi_exec_enh_mem_op;
@@ -1103,6 +1108,66 @@ static void dw_spi_cleanup(struct spi_device *spi)
 	spi_set_ctldata(spi, NULL);
 }
 
+static u16 detect_enh_mode(struct dw_spi *dws)
+{
+	u32 tmp_spi_ctrlr0, tmp_ctrlr0;
+	u32 tmp_val, frf_shift;
+	u16 mode = 0;
+
+	if (dw_spi_ver_is_ge(dws, HSSI, 103A))
+		frf_shift = __bf_shf(DW_HSSI_CTRLR0_SPI_FRF_MASK);
+	else if (dw_spi_ver_is_ge(dws, PSSI, 400A))
+		frf_shift = __bf_shf(DW_PSSI_CTRLR0_SPI_FRF_MASK);
+	else
+		return 0;
+
+	tmp_ctrlr0 = dw_readl(dws, DW_SPI_CTRLR0);
+	tmp_spi_ctrlr0 = dw_readl(dws, DW_SPI_SPI_CTRLR0);
+	dw_spi_enable_chip(dws, 0);
+
+	/* test dual mode */
+	tmp_val = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI << frf_shift;
+	dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+	if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+		mode |= SPI_TX_DUAL | SPI_RX_DUAL;
+
+	/* test quad mode */
+	tmp_val = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI << frf_shift;
+	dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+	if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+		mode |= SPI_TX_QUAD | SPI_RX_QUAD;
+
+	/* test octal mode */
+	tmp_val = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI << frf_shift;
+	dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+	if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+		mode |= SPI_TX_OCTAL | SPI_RX_OCTAL;
+
+	if (!mode)
+		goto disable_enh;
+
+	/* test clock stretching */
+	dw_writel(dws, DW_SPI_SPI_CTRLR0, DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN);
+	if ((DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN & dw_readl(dws, DW_SPI_SPI_CTRLR0)) !=
+	    DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN) {
+		/*
+		 * If clock stretching is not enabled then do not use
+		 * enhanced mode.
+		 */
+		mode = 0;
+		goto disable_enh;
+	}
+
+	dws->caps |= DW_SPI_CAP_EMODE;
+
+disable_enh:
+	dw_writel(dws, DW_SPI_CTRLR0, tmp_ctrlr0);
+	dw_writel(dws, DW_SPI_SPI_CTRLR0, tmp_spi_ctrlr0);
+	dw_spi_enable_chip(dws, 1);
+
+	return mode;
+}
+
 /* Restart the controller, disable all interrupts, clean rx fifo */
 static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
 {
@@ -1182,6 +1247,11 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
 		dws->caps |= DW_SPI_CAP_DFS32;
 	}
 
+	dws->ctlr->mode_bits |= SPI_CPOL | SPI_CPHA;
+
+	if (!spi_controller_is_target(dws->ctlr) && dw_spi_can_use_mem_ops(dws))
+		dws->ctlr->mode_bits |= detect_enh_mode(dws);
+
 	/* enable HW fixup for explicit CS deselect for Amazon's alpine chip */
 	if (dws->caps & DW_SPI_CAP_CS_OVERRIDE)
 		dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
@@ -1226,7 +1296,6 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
 
 	dw_spi_init_mem_ops(dws);
 
-	ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
 	if (dws->caps & DW_SPI_CAP_DFS32)
 		ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
 	else
-- 
2.25.1


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

* [PATCH v2 10/11] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (8 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 09/11] spi: dw: detect enhanced spi mode Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 12:40 ` [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC Changhuang Liang
  2026-08-04 12:53 ` [PATCH v2 00/11] Add support for StarFive JHB100 SFC Mark Brown
  11 siblings, 0 replies; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

Add a new compatible string "starfive,jhb100-sfc" for the StarFive
JHB100 SPI Flash Controller, it based on the Synopsys DesignWare
SSI version 2.00a but with minor modifications.

Due to these minor modifications, it only supports access for flash
memory and requires a system controller register to configure the
address mode filter for SPI NOR flash devices.

The starfive,sfc-filter-syscon property is required to provide a phandle
to the system controller that manages switching between 3-byte and
4-byte addressing modes, essential for supporting SPI NOR flash devices
with different address width requirements.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
 .../bindings/spi/snps,dw-apb-ssi.yaml         | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index 4458316326fc..f13cb963db88 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -33,6 +33,17 @@ allOf:
     else:
       properties:
         amd,pensando-elba-syscon: false
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: starfive,jhb100-sfc
+    then:
+      required:
+        - starfive,sfc-filter-syscon
+    else:
+      properties:
+        starfive,sfc-filter-syscon: false
 
 properties:
   compatible:
@@ -68,6 +79,8 @@ properties:
         const: amd,pensando-elba-spi
       - description: Canaan Kendryte K210 SoS SPI Controller
         const: canaan,k210-spi
+      - description: StarFive JHB100 SoC SPI Flash Controller
+        const: starfive,jhb100-sfc
       - description: Renesas RZ/N1 SPI Controller
         items:
           - const: renesas,r9a06g032-spi # RZ/N1D
@@ -139,6 +152,15 @@ properties:
       Block address to control SPI chip-selects. The Elba SoC system controller
       provides an interface to override the native DWC SSI CS control.
 
+  starfive,sfc-filter-syscon:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      Phandle to the system controller register that controls the SPI NOR flash
+      address mode filter. This system controller interface provides additional
+      configuration to switch between 3-byte and 4-byte addressing modes, which
+      is required when accessing SPI NOR flash devices with different address
+      width requirements.
+
 patternProperties:
   "@[0-9a-f]$":
     type: object
-- 
2.25.1


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

* [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (9 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 10/11] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc Changhuang Liang
@ 2026-08-03 12:40 ` Changhuang Liang
  2026-08-03 13:07   ` sashiko-bot
  2026-08-04 12:53 ` [PATCH v2 00/11] Add support for StarFive JHB100 SFC Mark Brown
  11 siblings, 1 reply; 20+ messages in thread
From: Changhuang Liang @ 2026-08-03 12:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree,
	Changhuang Liang

Add support for the StarFive JHB100 SoC SPI Flash Controller (SFC),
which is based on the Synopsys DesignWare SSI version 2.00a but with
some customizations.

The JHB100 SFC controller has the following special features:
1. Separate registers for instruction and address (DW_SPI_JHB100_INST
   and DW_SPI_JHB100_ADDR) instead of using the common data register.
2. A filter interrupt mask register (DW_SPI_JHB100_FILTER_IMR),
   which is default masked to disable filter interrupts as they
   are not used.
3. Requires a system controller phandle "starfive,sfc-filter-syscon"
   to configure 3-byte/4-byte address mode switching per chip select.
4. Different Set CS and Enable Controller Timing.

A new quirk flag DW_SPI_QUIRK_JHB100 is introduced to handle these
differences in the enhanced SPI memory operation path. The controller
uses the HSSI initialization path (DW_HSSI_ID) and shares the same
interrupt masking logic. Limit the JHB100 SFC address to 3-byte or
4-byte length.

Additionally, the platform_suspend() and platform_resume() callbacks
are introduced to handle platform-private suspend/resume procedures.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 48 ++++++++++++++++++++------
 drivers/spi/spi-dw-mmio.c | 72 +++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-dw.h      | 18 ++++++++++
 3 files changed, 127 insertions(+), 11 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 0d900afa015d..ce6144a34785 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -591,6 +591,8 @@ static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
 static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
 				       const struct spi_mem_op *op)
 {
+	struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
+
 	if (op->addr.nbytes != 0 && op->addr.buswidth != 1 &&
 	    op->addr.buswidth != op->data.buswidth)
 		return false;
@@ -614,6 +616,10 @@ static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
 	    FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
 		return false;
 
+	if ((dws->quirk_flags & DW_SPI_QUIRK_JHB100) && op->addr.nbytes &&
+	    op->addr.nbytes != 3 && op->addr.nbytes != 4)
+		return false;
+
 	return spi_mem_default_supports_op(mem, op);
 }
 
@@ -906,16 +912,30 @@ static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op
 	}
 }
 
-static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op)
+static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op,
+				      struct spi_mem *mem)
 {
-	/* Send cmd as 32 bit value */
-	dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
-	if (op->addr.nbytes) {
-		dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));
-		if (op->addr.nbytes > 4) {
-			/* address more than 32bit */
-			dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
+	if (dws->quirk_flags & DW_SPI_QUIRK_JHB100) {
+		dw_write_io_reg(dws, DW_SPI_JHB100_INST, op->cmd.opcode);
+		if (op->addr.nbytes)
+			dw_write_io_reg(dws, DW_SPI_JHB100_ADDR, op->addr.val);
+
+		dw_spi_set_cs(mem->spi, false);
+		dw_spi_enable_chip(dws, 1);
+	} else {
+		dw_spi_enable_chip(dws, 1);
+
+		/* Send cmd as 32 bit value */
+		dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
+		if (op->addr.nbytes) {
+			dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));
+			if (op->addr.nbytes > 4) {
+				/* address more than 32bit */
+				dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
+			}
 		}
+
+		dw_spi_set_cs(mem->spi, false);
 	}
 }
 
@@ -981,10 +1001,16 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
 
 	dw_spi_mask_intr(dws, 0xff);
 	reinit_completion(&ctlr->xfer_completion);
-	dw_spi_enable_chip(dws, 1);
 
-	dw_spi_enh_write_cmd_addr(dws, op);
-	dw_spi_set_cs(mem->spi, false);
+	if (op->addr.nbytes && dws->set_addr_nbyte) {
+		ret = dws->set_addr_nbyte(mem->spi, op->addr.nbytes);
+		if (ret) {
+			dw_spi_enable_chip(dws, 1);
+			return ret;
+		}
+	}
+
+	dw_spi_enh_write_cmd_addr(dws, op, mem);
 
 	/*
 	 * FIXME: The exact reason for this delay is not fully understood,
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 603e81a92c57..8cdb0351605b 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -31,6 +31,8 @@ struct dw_spi_mmio {
 	struct clk     *pclk;
 	void           *priv;
 	struct reset_control *rstc;
+	void (*platform_suspend)(struct dw_spi_mmio *dwsmmio);
+	int (*platform_resume)(struct dw_spi_mmio *dwsmmio);
 };
 
 #define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL	0x24
@@ -48,6 +50,8 @@ struct dw_spi_mmio {
 #define SPARX5_FORCE_ENA			0xa4
 #define SPARX5_FORCE_VAL			0xa8
 
+#define JHB100_ADDRMODE_CS			0x00
+
 struct dw_spi_mscc {
 	struct regmap       *syscon;
 	void __iomem        *spi_mst; /* Not sparx5 */
@@ -310,6 +314,58 @@ static int dw_spi_elba_init(struct platform_device *pdev,
 	return 0;
 }
 
+static int dw_spi_jhb100_set_addr_nbyte(struct spi_device *spi, u8 nbyte)
+{
+	struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
+	struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
+	struct regmap *syscon = dwsmmio->priv;
+
+	if (nbyte == 3) {
+		regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
+				   BIT(spi_get_chipselect(spi, 0)),
+				   0);
+	} else if (nbyte == 4) {
+		regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
+				   BIT(spi_get_chipselect(spi, 0)),
+				   BIT(spi_get_chipselect(spi, 0)));
+	} else {
+		dev_err(&spi->dev, "Unsupported address nbyte %d\n", nbyte);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dw_spi_jhb100_resume(struct dw_spi_mmio *dwsmmio)
+{
+	dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
+
+	return 0;
+}
+
+static int dw_spi_jhb100_init(struct platform_device *pdev,
+			      struct dw_spi_mmio *dwsmmio)
+{
+	struct regmap *syscon;
+
+	syscon = syscon_regmap_lookup_by_phandle(dev_of_node(&pdev->dev),
+						 "starfive,sfc-filter-syscon");
+	if (IS_ERR(syscon))
+		return dev_err_probe(&pdev->dev, PTR_ERR(syscon),
+				     "syscon regmap lookup failed\n");
+
+	dwsmmio->priv = syscon;
+	dwsmmio->platform_resume = dw_spi_jhb100_resume;
+
+	dwsmmio->dws.set_addr_nbyte = dw_spi_jhb100_set_addr_nbyte;
+	dwsmmio->dws.ip = DW_HSSI_ID;
+	dwsmmio->dws.quirk_flags = DW_SPI_QUIRK_JHB100;
+
+	dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
+
+	return 0;
+}
+
 static int dw_spi_mmio_probe(struct platform_device *pdev)
 {
 	int (*init_func)(struct platform_device *pdev,
@@ -399,6 +455,9 @@ static int dw_spi_mmio_suspend(struct device *dev)
 	if (ret)
 		return ret;
 
+	if (dwsmmio->platform_suspend)
+		dwsmmio->platform_suspend(dwsmmio);
+
 	reset_control_assert(dwsmmio->rstc);
 
 	clk_disable_unprepare(dwsmmio->pclk);
@@ -410,12 +469,24 @@ static int dw_spi_mmio_suspend(struct device *dev)
 static int dw_spi_mmio_resume(struct device *dev)
 {
 	struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
+	int ret;
 
 	clk_prepare_enable(dwsmmio->clk);
 	clk_prepare_enable(dwsmmio->pclk);
 
 	reset_control_deassert(dwsmmio->rstc);
 
+	if (dwsmmio->platform_resume) {
+		ret = dwsmmio->platform_resume(dwsmmio);
+		if (ret) {
+			reset_control_assert(dwsmmio->rstc);
+
+			clk_disable_unprepare(dwsmmio->pclk);
+			clk_disable_unprepare(dwsmmio->clk);
+			return ret;
+		}
+	}
+
 	return dw_spi_resume_controller(&dwsmmio->dws);
 }
 
@@ -447,6 +518,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
 	{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
 	{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
 	{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
+	{ .compatible = "starfive,jhb100-sfc", .data = dw_spi_jhb100_init},
 	{ /* end of table */}
 };
 MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 2dae81c15423..2fce5bde75f0 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -67,6 +67,11 @@
 #define DW_SPI_SPI_CTRLR0		0xf4
 #define DW_SPI_CS_OVERRIDE		0xf4
 
+/* Register offsets (StarFive JHB100 DWC SSI IP-cores) */
+#define DW_SPI_JHB100_INST		0x1000
+#define DW_SPI_JHB100_ADDR		0x1004
+#define DW_SPI_JHB100_FILTER_IMR	0x1008
+
 /* Bit fields in CTRLR0 (DWC APB SSI) */
 #define DW_PSSI_CTRLR0_DFS_MASK			GENMASK(3, 0)
 #define DW_PSSI_CTRLR0_DFS32_MASK		GENMASK(20, 16)
@@ -199,6 +204,7 @@ struct dw_spi {
 	u32			num_cs;		/* chip select lines */
 	u16			bus_num;
 	void (*set_cs)(struct spi_device *spi, bool enable);
+	int (*set_addr_nbyte)(struct spi_device *spi, u8 nbyte);
 
 	/* Current message transfer state info */
 	void			*tx;
@@ -228,6 +234,9 @@ struct dw_spi {
 	const struct dw_spi_dma_ops *dma_ops;
 	struct completion	dma_completion;
 
+#define DW_SPI_QUIRK_JHB100		BIT(0)
+	u32			quirk_flags;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs;
 	struct debugfs_regset32 regset;
@@ -296,6 +305,15 @@ static inline void dw_spi_umask_intr(struct dw_spi *dws, u32 mask)
 	dw_writel(dws, DW_SPI_IMR, new_mask);
 }
 
+/* Disable JHB100 SPI filter IRQ bits */
+static inline void dw_spi_jhb100_mask_intr(struct dw_spi *dws, u32 mask)
+{
+	u32 new_mask;
+
+	new_mask = dw_readl(dws, DW_SPI_JHB100_FILTER_IMR) & ~mask;
+	dw_writel(dws, DW_SPI_JHB100_FILTER_IMR, new_mask);
+}
+
 /*
  * This disables the SPI controller, interrupts, clears the interrupts status
  * and CS, then re-enables the controller back. Transmit and receive FIFO
-- 
2.25.1


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

* Re: [PATCH v2 03/11] spi: dw: add check for support of enhanced spi
  2026-08-03 12:40 ` [PATCH v2 03/11] spi: dw: add check for support of enhanced spi Changhuang Liang
@ 2026-08-03 12:53   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 12:53 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Flawed buswidth validation erroneously rejects valid multi-bit operations (when phases are absent) and incorrectly accepts physically unsupported operations.
--

--- Patch [3]: [PATCH v2 03/11] spi: dw: add check for support of enhanced spi ---
commit 0eaff4ea0f38f3c8901810547641176fba69f40c
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: add check for support of enhanced spi

This commit adds support for validating single/dual/quad/octal SPI memory
operations if the controller has the DW_SPI_CAP_EMODE capability.

> diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c
[ ... ]
> +static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
> +				       const struct spi_mem_op *op)
> +{
> +	if (op->addr.nbytes != 0 && op->addr.buswidth != 1 &&
> +	    op->addr.buswidth != op->data.buswidth)
> +		return false;

[Severity: High]
Will this erroneously reject valid multi-bit operations that do not have a
data phase (such as 1-4-0 or 4-4-0)?

If a multi-bit operation like an Erase command has no data phase,
op->data.nbytes is 0 and op->data.buswidth evaluates to 0. Since the
address buswidth (e.g., 4) does not equal the data buswidth (0), this check
evaluates to true and incorrectly returns false.

Should there be a check for op->data.nbytes != 0 before comparing the bus
widths?

> +
> +	if (op->addr.nbytes >= 8)
> +		return false;
> +
> +	if (op->cmd.buswidth != 1 && op->cmd.buswidth != op->addr.buswidth &&
> +	    op->cmd.buswidth != op->data.buswidth)
> +		return false;

[Severity: High]
Does this logic allow hardware-unsupported mixed-buswidth permutations?

For an invalid operation like 4-1-4, op->cmd.buswidth (4) does not equal
op->addr.buswidth (1), but it does equal op->data.buswidth (4). This makes
the final condition (op->cmd.buswidth != op->data.buswidth) false, which
short-circuits the if statement and allows the operation.

Since the DW SPI controller's TRANS_TYPE cannot natively support such
permutations, should this logic be adjusted to prevent accepting operations
that will result in corrupted transfers on the SPI bus?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=3

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

* Re: [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi
  2026-08-03 12:40 ` [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi Changhuang Liang
@ 2026-08-03 12:58   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 12:58 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] The newly introduced dw_spi_exec_enh_mem_op() lacks actual SPI data transfer execution logic, returning success without performing I/O and leaving hardware enabled.
- [High] Incorrect transfer type (trans_t) mapping for multi-bit commands without an address phase (e.g., 4-0-4) silently misconfigures the wire protocol.
--

--- Patch [6]: [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi ---
commit 02b326945674bcd1a149d5a5a3e3cd78b2ccabda
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: Introduce enhanced single/dual/quad/octal spi

If the spi transfer is using enhanced single/dual/quad/octal spi mode,
then we need to update the SPI_CTRLR0 register. The SPI_CTRLR0 register
will be updated in dw_spi_update_config() via the values in
dw_spi_enh_cfg.

> diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
> index d654cb748646a..dbe8779ced865 100644
> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c

[ ... ]

> @@ -828,6 +828,90 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
>  	return ret;
>  }
>  
> +static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)

[ ... ]

> +static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
> +	struct dw_spi_enh_cfg enh_cfg = {0};
> +	struct dw_spi_cfg cfg = {0};

[ ... ]

> +	if (op->data.buswidth == op->addr.buswidth &&
> +	    op->data.buswidth == op->cmd.buswidth)
> +		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2;
> +	else if (op->data.buswidth == op->addr.buswidth)
> +		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1;
> +	else
> +		enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0;

[Severity: High]
Does this code handle multi-bit commands without an address phase correctly?

For example, on a 4-0-4 operation where op->addr.nbytes == 0 and 
op->addr.buswidth == 0, op->data.buswidth (4) won't match op->addr.buswidth (0).
This logic would fall through to the else branch and set trans_t to
DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0, which misconfigures the wire protocol to
send the multi-bit command as a 1-bit Standard SPI command.

> +
> +	enh_cfg.addr_l = op->addr.nbytes << 1;
> +	if (op->cmd.nbytes == 2)
> +		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L16;
> +	else if (op->cmd.nbytes == 1)
> +		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L8;
> +	else
> +		enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L0;
> +
> +	if (op->dummy.buswidth)
> +		enh_cfg.wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
> +
> +	dw_spi_enable_chip(dws, 0);
> +
> +	dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
> +
> +	dw_spi_enable_chip(dws, 1);
> +
> +	return 0;
> +}

[Severity: Critical]
Is it intentional that this function omits the actual data transfer and chip
teardown?

It appears to return success (0) immediately after enabling the chip, without
calling any I/O routines like dw_spi_write_then_read() or dw_spi_stop_mem_op().
For read operations, this would leave the caller's receive buffer uninitialized
and the SPI chip enabled indefinitely.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=6

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

* Re: [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi
  2026-08-03 12:40 ` [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi Changhuang Liang
@ 2026-08-03 12:59   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 12:59 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The SPI controller's FIFO threshold registers are modified while the controller is active, and a hardcoded delay introduces a race condition leading to silent RX FIFO overflows.
- [High] dw_spi_exec_enh_mem_op() unconditionally relies on interrupts, breaking SPI operations on systems without an IRQ line.
--

--- Patch [8]: [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi ---
commit e91a45b5a4fd49e726c98b12e74d1f4306f85fe7
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: use irq handler for enhanced spi

This commit introduces an interrupt handler for enhanced SPI read/write
operations. It utilizes the controller's xfer_completion to wait for
transfer completion or timeout from the IRQ handler. It also adjusts
RXFTLR dynamically based on the expected data length.

> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c
[ ... ]
> @@ -920,11 +979,54 @@ static int dw_spi_exec_enh_mem_op(...)
>  
>  	dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
>  
> +	dw_spi_mask_intr(dws, 0xff);
> +	reinit_completion(&ctlr->xfer_completion);
>  	dw_spi_enable_chip(dws, 1);
>  
>  	dw_spi_enh_write_cmd_addr(dws, op);
> +	dw_spi_set_cs(mem->spi, false);
>  
> -	return 0;
> +	/*
> +	 * FIXME: The exact reason for this delay is not fully understood,
> +	 * but empirical testing shows it significantly improves the stability
> +	 * of read/write operations. Without this delay, occasional transfer
> +	 * errors or timeouts may occur under certain conditions.
> +	 * Keeping it as a safeguard based on practical validation.
> +	 */
> +	udelay(5);
> +
> +	dw_spi_enh_irq_setup(dws);

[Severity: High]
Does modifying the SPI controller's FIFO threshold registers inside
dw_spi_enh_irq_setup() while the controller is active (after calling
dw_spi_enable_chip()) violate the DW APB SSI hardware specifications?

Additionally, with this 5us delay between starting the transfer and unmasking
interrupts, could the RX FIFO overflow before dw_spi_enh_irq_setup() prepares
the IRQ handler to drain it?

At typical SPI frequencies (e.g., 50MHz), 5us is enough time to clock in
more data than a small RX FIFO can hold, which might lead to silent data loss
and immediate -EIO timeouts.

> +
> +	/* Use timeout calculation from spi_transfer_wait() */
> +	ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
> +	do_div(ms, dws->current_freq);
> +
> +	/*
> +	 * Increase it twice and add 200 ms tolerance, use
> +	 * predefined maximum in case of overflow.
> +	 */
> +	ms += ms + 200;
> +	if (ms > UINT_MAX)
> +		ms = UINT_MAX;
> +
> +	ms = wait_for_completion_timeout(&ctlr->xfer_completion,
> +					 msecs_to_jiffies(ms));
> +	if (ms == 0) {

[Severity: High]
Will this wait unconditionally time out on platforms without a connected IRQ?

Unlike the standard dw_spi_exec_mem_op() which falls back to polling, this
appears to rely solely on the interrupt handler to signal completion. If a
system uses polling mode (no dedicated SPI IRQ), it seems enhanced memory
operations will always result in an -EIO timeout here.

> +		dw_spi_mask_intr(dws, 0xff);
> +		synchronize_irq(dws->irq);
> +		dws->rx = NULL;
> +		dws->tx = NULL;
> +		dws->rx_len = 0;
> +		dws->tx_len = 0;
> +		dw_spi_stop_mem_op(dws, mem->spi);
> +		return -EIO;
> +	}
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=8

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

* Re: [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer
  2026-08-03 12:40 ` [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer Changhuang Liang
@ 2026-08-03 12:59   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 12:59 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Missing Data Phase and Completion Wait in Enhanced SPI Mode
- [Medium] Address Truncation on 16-bit I/O Width
- [Medium] Preemption / FIFO Overflow Vulnerability
--

--- Patch [7]: [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer ---
commit 0e79a8e7a3709454ed5a5603c033614bdc1b0b5a
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: send cmd and addr to start the spi transfer
    
In enhanced spi mode, read or write will start by sending the cmd
and address (if present).

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index dbe8779ced865..3388f62dc695c 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
[ ... ]
> +static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op)
> +{
> +       /* Send cmd as 32 bit value */
> +       dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
> +       if (op->addr.nbytes) {
> +               dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));

[Severity: Medium]
Does passing a 32-bit chunk to dw_write_io_reg() truncate the address if the
controller is configured with a 16-bit I/O width?

Looking at dw_write_io_reg(), it uses writew_relaxed() when dws->reg_io_width
is 2. This would drop the upper 16 bits of the lower 32-bit value when
executing an enhanced SPI operation with an address phase.

> +               if (op->addr.nbytes > 4) {
> +                       /* address more than 32bit */
> +                       dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
> +               }
> +       }
> +}
> +
[ ... ]
>  static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
>  {
>         struct spi_controller *ctlr = mem->spi->controller;
> @@ -909,6 +922,8 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
>  
>         dw_spi_enable_chip(dws, 1);
>  
> +       dw_spi_enh_write_cmd_addr(dws, op);
> +
>         return 0;
>  }

[Severity: High]
Is the payload data phase, chip select assertion, and completion wait
intentionally omitted here?

By returning 0 immediately after dw_spi_enh_write_cmd_addr(), the data
phase (SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT) seems to be skipped entirely,
which would leave read buffers uninitialized and fail to transfer write
payloads.

[Severity: Medium]
Does dw_spi_exec_enh_mem_op() need to disable local interrupts and preemption
during the IO transfer?

The standard dw_spi_exec_mem_op() wraps the transfer in local_irq_save() and
preempt_disable() to prevent the DesignWare APB SSI RX FIFO from overflowing
if the CPU is preempted or handles a long interrupt. If the CPU is preempted
here in enhanced mode under heavy system load, the incoming data might
similarly overflow the RX FIFO.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=7

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

* Re: [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode
  2026-08-03 12:40 ` [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode Changhuang Liang
@ 2026-08-03 13:02   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 13:02 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The `cfg.ndf` (Number of Data Frames) field is left uninitialized (0) for Transmit Only operations in `dw_spi_exec_mem_op()`, causing the hardware to be erroneously programmed to transmit 0 frames.
- [Medium] The condition to update the NDF register for Transmit Only mode incorrectly checks the static controller capability instead of the actual transfer format.
--

--- Patch [5]: [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode ---
commit 5891fe2f8ceff4e681ce6ee14b10868e4b3ce4bc
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: update NDF while using enhanced spi mode

This commit updates the driver to configure the Number of Data Frames (NDF)
register during Transmit Only mode on enhanced SPI controllers. It modifies
dw_spi_update_config() to write cfg->ndf to DW_SPI_CTRLR1 when the transfer
mode is DW_SPI_CTRLR0_TMOD_TO and the controller supports enhanced SPI.

> diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
> index f1b7328fed772..d654cb748646a 100644
> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c
> @@ -348,6 +348,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
>  	if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
>  	    cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
>  		dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
> +	else if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO &&
> +		 dws->caps & DW_SPI_CAP_EMODE)
> +		dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf);

[Severity: Medium]
Will this write a 0 to DW_SPI_CTRLR1 for outbound memory operations?

Looking at dw_spi_exec_mem_op(), cfg is initialized to zero, and cfg.ndf is
only assigned for SPI_MEM_DATA_IN:

drivers/spi/spi-dw-core.c:dw_spi_exec_mem_op() {
	struct dw_spi_cfg cfg = {0};
	...
	if (op->data.dir == SPI_MEM_DATA_IN) {
		cfg.tmode = DW_SPI_CTRLR0_TMOD_EPROMREAD;
		cfg.ndf = op->data.nbytes;
	} else {
		cfg.tmode = DW_SPI_CTRLR0_TMOD_TO;
	}
	...
}

For transmit operations (SPI_MEM_DATA_OUT), cfg.ndf remains 0 when it's passed
into dw_spi_update_config(), causing the hardware to be programmed to transmit
0 frames.

[Severity: Medium]
Does this condition correctly identify an Enhanced SPI transfer?

Checking dws->caps & DW_SPI_CAP_EMODE verifies that the controller supports
Enhanced SPI, but it does not check if the current transfer is actually
using it.

Could this cause standard 1-bit SPI memory writes on EMODE-capable controllers
to inappropriately update DW_SPI_CTRLR1 with cfg->ndf?

>  	/* Note DW APB SSI clock divider doesn't support odd numbers */
>  	clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=5

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

* Re: [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC
  2026-08-03 12:40 ` [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC Changhuang Liang
@ 2026-08-03 13:07   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 13:07 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The return value of `regmap_update_bits` is silently ignored in `dw_spi_jhb100_set_addr_nbyte`.
--

--- Patch [11]: [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC ---
commit 6fd643ebea16814890aa0dbec055ee773f4f746d
Author: Changhuang Liang <changhuang.liang@starfivetech.com>

spi: dw: Add support for StarFive JHB100 SoC SFC

This commit adds support for the StarFive JHB100 SoC SPI Flash Controller
(SFC). It introduces customizations to handle separate registers for
instruction and address, and utilizes a syscon to configure 3-byte/4-byte
address mode switching per chip select.

> diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
> index 603e81a92c57a..8cdb0351605b2 100644
> --- a/drivers/spi/spi-dw-mmio.c
> +++ b/drivers/spi/spi-dw-mmio.c
[ ... ]
> @@ -310,6 +314,58 @@ static int dw_spi_elba_init(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int dw_spi_jhb100_set_addr_nbyte(struct spi_device *spi, u8 nbyte)
> +{
> +	struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
> +	struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
> +	struct regmap *syscon = dwsmmio->priv;
> +
> +	if (nbyte == 3) {
> +		regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
> +				   BIT(spi_get_chipselect(spi, 0)),
> +				   0);
> +	} else if (nbyte == 4) {
> +		regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
> +				   BIT(spi_get_chipselect(spi, 0)),
> +				   BIT(spi_get_chipselect(spi, 0)));

[Severity: Medium]
Is it intentional to silently ignore the return value of
regmap_update_bits() here?

If the regmap_update_bits() call fails, the hardware might be left in an
incorrect address mode (e.g., 3-byte instead of 4-byte). This could
silently corrupt subsequent SPI flash operations.

> +	} else {
> +		dev_err(&spi->dev, "Unsupported address nbyte %d\n", nbyte);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=11

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

* Re: [PATCH v2 09/11] spi: dw: detect enhanced spi mode
  2026-08-03 12:40 ` [PATCH v2 09/11] spi: dw: detect enhanced spi mode Changhuang Liang
@ 2026-08-03 13:07   ` sashiko-bot
  0 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-03 13:07 UTC (permalink / raw)
  To: Changhuang Liang; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Naive register read-back probing for OCTAL mode can incorrectly detect OCTAL support on QUAD-only hardware.
--

--- Patch [9]: [PATCH v2 09/11] spi: dw: detect enhanced spi mode ---
commit 55207343ce55e6e564d0b04f2044bd3036ccb30f
Author: Sudip Mukherjee <sudip.mukherjee@sifive.com>

spi: dw: detect enhanced spi mode

This commit adds detection for enhanced SPI modes (dual, quad, octal) by
testing if the controller supports them via a register read-back. If
supported, it enables the DW_SPI_CAP_EMODE capability.

> diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
> index cca8f4ba1004b..bfa017df7673f 100644
> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c
[ ... ]
> @@ -1103,6 +1108,66 @@ static void dw_spi_cleanup(struct spi_device *spi)
>  	spi_set_ctldata(spi, NULL);
>  }
>  
> +static u16 detect_enh_mode(struct dw_spi *dws)
> +{
[ ... ]
> +	/* test octal mode */
> +	tmp_val = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI << frf_shift;
> +	dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
> +	if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
> +		mode |= SPI_TX_OCTAL | SPI_RX_OCTAL;

[Severity: High]
Could this read-back test incorrectly detect OCTAL support on hardware that
only supports up to QUAD mode?

Since DW_SPI_CTRLR0_SPI_FRF_OCT_SPI is 3, writing this to the 2-bit wide
SPI_FRF field will succeed if the hardware supports QUAD mode (value 2),
as the field is already 2 bits wide. The value will read back as 3 in the
register flip-flops even if the IP's internal state machine physically lacks
OCTAL support.

Can this falsely advertise SPI_TX_OCTAL and SPI_RX_OCTAL capabilities,
potentially leading to communication failure or hardware lockups when the
SPI flash driver attempts to use OCTAL mode?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260803124044.156998-1-changhuang.liang@starfivetech.com?part=9

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

* Re: [PATCH v2 00/11] Add support for StarFive JHB100 SFC
  2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
                   ` (10 preceding siblings ...)
  2026-08-03 12:40 ` [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC Changhuang Liang
@ 2026-08-04 12:53 ` Mark Brown
  11 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2026-08-04 12:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Changhuang Liang
  Cc: Sudip Mukherjee, Serge Semin, linux-spi, linux-kernel, devicetree

On Mon, 03 Aug 2026 05:40:33 -0700, Changhuang Liang wrote:
> Add support for StarFive JHB100 SFC
> 
> This serial add support for the StarFive JHB100 SoC SPI Flash
> Controller (SFC), which is based on the Synopsys DesignWare SSI
> version 2.00a but with some customizations and it also add
> enhanced SPI for DesignWare SPI controllers.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-7.3

Thanks!

[01/11] spi: dw: Introduce spi_frf and STD_SPI
        https://git.kernel.org/broonie/spi/c/c87b4ba09702
[02/11] spi: dw: update SPI_CTRLR0 register
        https://git.kernel.org/broonie/spi/c/607687a869e6
[03/11] spi: dw: add check for support of enhanced spi
        https://git.kernel.org/broonie/spi/c/f6b36efcbe01
[04/11] spi: dw: adjust size of mem_op
        https://git.kernel.org/broonie/spi/c/3d7fd27c970e
[05/11] spi: dw: update NDF while using enhanced spi mode
        https://git.kernel.org/broonie/spi/c/8678095362cc
[06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi
        https://git.kernel.org/broonie/spi/c/4cd882e1da79
[07/11] spi: dw: send cmd and addr to start the spi transfer
        https://git.kernel.org/broonie/spi/c/51119176c252
[08/11] spi: dw: use irq handler for enhanced spi
        https://git.kernel.org/broonie/spi/c/e017fae89e6d
[09/11] spi: dw: detect enhanced spi mode
        https://git.kernel.org/broonie/spi/c/defdd0f3d09a
[10/11] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc
        https://git.kernel.org/broonie/spi/c/8edce30fc953
[11/11] spi: dw: Add support for StarFive JHB100 SoC SFC
        https://git.kernel.org/broonie/spi/c/012c1b04f528

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark


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

end of thread, other threads:[~2026-08-04 19:41 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 12:40 [PATCH v2 00/11] Add support for StarFive JHB100 SFC Changhuang Liang
2026-08-03 12:40 ` [PATCH v2 01/11] spi: dw: Introduce spi_frf and STD_SPI Changhuang Liang
2026-08-03 12:40 ` [PATCH v2 02/11] spi: dw: update SPI_CTRLR0 register Changhuang Liang
2026-08-03 12:40 ` [PATCH v2 03/11] spi: dw: add check for support of enhanced spi Changhuang Liang
2026-08-03 12:53   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 04/11] spi: dw: adjust size of mem_op Changhuang Liang
2026-08-03 12:40 ` [PATCH v2 05/11] spi: dw: update NDF while using enhanced spi mode Changhuang Liang
2026-08-03 13:02   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 06/11] spi: dw: Introduce enhanced single/dual/quad/octal spi Changhuang Liang
2026-08-03 12:58   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 07/11] spi: dw: send cmd and addr to start the spi transfer Changhuang Liang
2026-08-03 12:59   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 08/11] spi: dw: use irq handler for enhanced spi Changhuang Liang
2026-08-03 12:59   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 09/11] spi: dw: detect enhanced spi mode Changhuang Liang
2026-08-03 13:07   ` sashiko-bot
2026-08-03 12:40 ` [PATCH v2 10/11] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-sfc Changhuang Liang
2026-08-03 12:40 ` [PATCH v2 11/11] spi: dw: Add support for StarFive JHB100 SoC SFC Changhuang Liang
2026-08-03 13:07   ` sashiko-bot
2026-08-04 12:53 ` [PATCH v2 00/11] Add support for StarFive JHB100 SFC Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox