[PATCH v1 09/11] spi: dw: detect enhanced spi mode

From: Changhuang Liang

Date: Thu Jul 09 2026 - 05:28:23 EST


From: Sudip Mukherjee <sudip.mukherjee@xxxxxxxxxx>

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@xxxxxxxxxx>
Co-developed-by: Changhuang Liang <changhuang.liang@xxxxxxxxxxxxxxxx>
Signed-off-by: Changhuang Liang <changhuang.liang@xxxxxxxxxxxxxxxx>
---
drivers/spi/spi-dw-core.c | 62 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 0dbf250a101b..0abdee82eaab 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -1083,6 +1083,64 @@ 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.
+ */
+ 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)
{
@@ -1162,6 +1220,9 @@ 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;
+ 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);
@@ -1206,7 +1267,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