From 94b6df050433875f493fb8298b58d7b0dc32eb9c Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 28 Sep 2026 19:53:11 +0800 Subject: [PATCH 1/5] spi: spi-nxp-fspi: exit stop mode before waiting for DLL lock For 8D-8D-8D (octal DTR) mode the FlexSPI samples read data using MCR0[RXCLKSRC] = 3 (flash-memory-provided read strobe on the DQS pad), which requires the DLL to be locked. Per the i.MX95 Reference Manual (FlexSPI chapter, "DLL configuration for sampling"), when the serial root clock is above 100 MHz the DLL is enabled (DLLxCR[DLLEN] = 1, OVRDEN = 0) and software must then wait for STS2[xREFLOCK] and STS2[xSLVLOCK] to be asserted before starting a read or write. This DLL lock is relative to the serial root clock. While the module is in stop mode (MCR0[MDIS] = 1) the AHB and serial clocks are gated off internally, so the serial root clock the DLL locks against is not running. nxp_fspi_dll_calibration() both programs DLLxCR and then polls STS2 for lock. When it runs entirely in stop mode the STS2 REF/SLV lock bits never assert (observed as "DLL lock failed" with STS2 = 0). The MCR0 bit fields (RXCLKSRC) and the serial root clock rate still have to be reconfigured in stop mode, so keep entering stop mode around that. Only move the exit from stop mode (clearing MCR0[MDIS]) ahead of the DLL calibration/override step, so that the serial root clock is running when the DLL is calibrated and STS2 can report lock. Fixes: b4bde5cfff8e ("spi: spi-nxp-fspi: enter stop mode before reconfiguring MCR0 and DLL") Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260928-fspi-fix-v1-1-a0f9288cd5c8@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 67d991dd249d..66cb58f95fdb 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -898,10 +898,10 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0)); /* - * Per the FlexSPI reference manual (initialization sequence), MCR0 and - * the DLL control registers should be configured while the module is in - * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the - * RX sample clock source and the DLL, then exit stop mode afterwards. + * Per the FlexSPI reference manual (initialization sequence), MCR0 + * should be configured while the module is in stop mode (MCR0[MDIS] = 1). + * Enter stop mode before reconfiguring the RX sample clock source, then + * exit stop mode afterwards. */ reg = fspi_readl(f, f->iobase + FSPI_MCR0); fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); @@ -936,6 +936,16 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, if (ret) return ret; + /* + * Exit stop mode before running the DLL calibration. The DLL + * reference clock is derived from the module clock domain, which is + * gated while MCR0[MDIS] = 1. Calibrating the DLL in stop mode leaves + * the STS2 lock bits stuck at 0 (the DLL never runs), so clear MDIS + * first and let the module and reference clock run. + */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + /* * If clock rate > 100MHz, then switch from DLL override mode to * DLL calibration mode. @@ -945,10 +955,6 @@ static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, else nxp_fspi_dll_override(f); - /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */ - reg = fspi_readl(f, f->iobase + FSPI_MCR0); - fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); - f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); From fd99864b0ac936e870c7ae28354c5cd3dad5efd0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2026 17:14:44 +0200 Subject: [PATCH 2/5] spi: cadence-qspi: Fix status polling with octal DTR chips Most SPI NOR chips today use 0x05 as ReaD Status Register (RDSR) opcode. After almost every transaction, the core will wait for the said transation to be digested by the chip by polling its status register. This mechanism has been automated by certain chips, such as the Cadence QSPI controller. Unfortunately, the feature has been disabled for various (relevant) reasons. Disabling this feature has been done by setting WRITE_COMPLETION_CTRL_REG.DISABLE_POLLING_FLD, keeping the rest of the register intact. In this same register, there is also a field named WRITE_COMPLETION_CTRL_REG.OPCODE_FLD, which by default also contains 0x05, as primary RDSR opcode. As a result, both the SPI NOR and SPI NAND cores, primary users through spi-mem of the spi_mem_ops interface, do send their own RDSR. In this driver, such command goes through one of the fifth possible data path specifically crafted for short register-like commands, named STIG. In STIG mode, the opcode of the operation is written to OSPI_FLASH_CMD_CTRL_REG. This means that the opcode field of STIG configuration and the opcode field of the automatic polling configuration contain the same value. Unfortunately, the TRM explains in two different places that this configuration is forbidden, without explaining why it is problematic: $12.4.2.4.11 OSPI Software-Triggered Instruction Generator (STIG) ``` OSPI_FLASH_CMD_CTRL_REG[31-24] CMD_OPCODE_FLD bits should be set different than OSPI_DEV_INSTR_RD_CONFIG_REG[7-0] RD_OPCODE_NON_XIP_FLD and OSPI_DEV_INSTR_WR_CONFIG_REG[7-0] WR_OPCODE_FLD. ``` and $12.4.2.5.2 Configuring the OSPI Controller for Optimal Use ``` When using the OSPI Controller, the opcodes in OSPI_DEV_INSTR_RD_CONFIG_REG[7-0] RD_OPCODE_NON_XIP_FLD, OSPI_DEV_INSTR_WR_CONFIG_REG[7-0] WR_OPCODE_FLD and OSPI_WRITE_COMPLETION_CTRL_REG[7-0] OPCODE_FLD bit fields shall not match the opcode in the OSPI_FLASH_CMD_CTRL_REG[31-24] CMD_OPCODE_FLD bit field. ``` For some reasons, this is only unveiled now when testing in ODTR mode. Plain SDR modes work flawlessly with the same chips. One possible reason for this, is that the internal data path in ODTR mode is very different due to the extra opcode extension/repetition which may be interpreted differently (this is a wild guess). For sure there is an interference, as even the TRM states that there is some magic happening: $12.4.2.4.13 OSPI Command Translation ``` The WREN and the RDSR device instructions are the only ones that are sent by the controller under the hood. ``` I cannot probe the bus when the problem arises, so I don't know exactly what is sent on the bus. Practically speaking, the chip was driving the lines low (reading all 0s), which is not the usual default state when the chip is not driving the lines (all 1s). Other people on the mailing list claimed successfully using ODTR chips with this controller. My take here is that either they were using a variant of the IP without write completion at all (there is a quirk for it), or they were using chips slightly more flexible about their opcode sequency or maybe with different addr/dummy requirements. Clearing the WRITE_COMPLETION_CTRL_REG.OPCODE_FLD while disabling write completion polling entirely seems the best compromise. Signed-off-by: Miquel Raynal Link: https://patch.msgid.link/20261001-winbond-spi-next-cadence-rdsr-fix-v1-1-ec5ff594ba5d@bootlin.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index ecb0be394696..5a5f1a852cbe 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1056,8 +1056,7 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata, * care of polling the status register. */ if (cqspi->wr_completion) { - reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); - reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; + reg = CQSPI_REG_WR_DISABLE_AUTO_POLL; writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); /* * DAC mode require auto polling as flash needs to be polled From e130c54fbb603f9aeacf5029789b4a2c8c48f416 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Thu, 1 Oct 2026 02:22:50 +0800 Subject: [PATCH 3/5] spi: sg2044-nor: Return transfer errors The transfer helpers return errors, but both dispatchers discard them. Return these errors to the SPI memory layer instead of reporting success. Fixes: de16c322eefb ("spi: sophgo: add SG2044 SPI NOR controller driver") Signed-off-by: Han Gao Link: https://patch.msgid.link/20261001-fix-sg204xspi-v2-1-014dbaf22b5e@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index f3bcb8a1b92b..ff026ca0f766 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -307,15 +307,15 @@ static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc, return 0; } -static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, - const struct spi_mem_op *op) +static int sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, + const struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_IN) - sg2044_spifmc_read(spifmc, op); + return sg2044_spifmc_read(spifmc, op); else if (op->data.dir == SPI_MEM_DATA_OUT) - sg2044_spifmc_write(spifmc, op); + return sg2044_spifmc_write(spifmc, op); else - sg2044_spifmc_tran_cmd(spifmc, op); + return sg2044_spifmc_tran_cmd(spifmc, op); } static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, @@ -389,19 +389,20 @@ static int sg2044_spifmc_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct sg2044_spifmc *spifmc; + int ret; spifmc = spi_controller_get_devdata(mem->spi->controller); mutex_lock(&spifmc->lock); if (op->addr.nbytes == 0) - sg2044_spifmc_trans_reg(spifmc, op); + ret = sg2044_spifmc_trans_reg(spifmc, op); else - sg2044_spifmc_trans(spifmc, op); + ret = sg2044_spifmc_trans(spifmc, op); mutex_unlock(&spifmc->lock); - return 0; + return ret; } static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = { From 61d44a2c07b2ad3f7db167f17ff2461ffe78441c Mon Sep 17 00:00:00 2001 From: Han Gao Date: Thu, 1 Oct 2026 02:22:51 +0800 Subject: [PATCH 4/5] spi: sg2044-nor: Honor SPI clock limits The fixed divider ignores device and operation clock limits. Calculate the divider before each transfer so SCK does not exceed either limit. Advertise the controller maximum speed so the SPI core supplies a default when the device omits its maximum speed. Use the fastest divider when no speed limit is supplied. Fixes: de16c322eefb ("spi: sophgo: add SG2044 SPI NOR controller driver") Signed-off-by: Han Gao Link: https://patch.msgid.link/20261001-fix-sg204xspi-v2-2-014dbaf22b5e@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index ff026ca0f766..bc9a8ee9bce4 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -385,6 +385,29 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, return 0; } +static int sg2044_spifmc_set_speed(struct sg2044_spifmc *spifmc, + unsigned int speed_hz) +{ + unsigned long rate = clk_get_rate(spifmc->clk); + u64 div; + u32 reg; + + if (!rate) + return -EINVAL; + + /* SCK = HCLK / (2 * (SckDiv + 1)); round down the resulting SCK. */ + div = speed_hz ? DIV_ROUND_UP_ULL(rate, 2ULL * speed_hz) : 1; + if (div > SPIFMC_CTRL_SCK_DIV_MASK + 1) + return -EINVAL; + + reg = readl(spifmc->io_base + SPIFMC_CTRL); + reg &= ~SPIFMC_CTRL_SCK_DIV_MASK; + reg |= div - 1; + writel(reg, spifmc->io_base + SPIFMC_CTRL); + + return 0; +} + static int sg2044_spifmc_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { @@ -395,11 +418,18 @@ static int sg2044_spifmc_exec_op(struct spi_mem *mem, mutex_lock(&spifmc->lock); + ret = sg2044_spifmc_set_speed(spifmc, op->max_freq ? + min(op->max_freq, mem->spi->max_speed_hz) : + mem->spi->max_speed_hz); + if (ret) + goto out_unlock; + if (op->addr.nbytes == 0) ret = sg2044_spifmc_trans_reg(spifmc, op); else ret = sg2044_spifmc_trans(spifmc, op); +out_unlock: mutex_unlock(&spifmc->lock); return ret; @@ -409,6 +439,10 @@ static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = { .exec_op = sg2044_spifmc_exec_op, }; +static const struct spi_controller_mem_caps sg2044_spifmc_mem_caps = { + .per_op_freq = true, +}; + static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc) { u32 tran_csr; @@ -448,6 +482,10 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) if (IS_ERR(spifmc->clk)) return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n"); + ctrl->max_speed_hz = clk_get_rate(spifmc->clk) / 2; + if (!ctrl->max_speed_hz) + return dev_err_probe(dev, -EINVAL, "Invalid AHB clock rate\n"); + spifmc->dev = &pdev->dev; spifmc->ctrl = ctrl; @@ -459,6 +497,7 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) ctrl->bits_per_word_mask = SPI_BPW_MASK(8); ctrl->auto_runtime_pm = false; ctrl->mem_ops = &sg2044_spifmc_mem_ops; + ctrl->mem_caps = &sg2044_spifmc_mem_caps; ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD; ret = devm_mutex_init(dev, &spifmc->lock); From 00765a3467b992d0eb56704a28887256317eb4bd Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 8 Oct 2026 09:00:58 +0800 Subject: [PATCH 5/5] spi: dt-bindings: snps,dw-apb-ssi: Add Synaptics sl2610 spi Add the compatible for the Synaptics SL2610 SoC SPI controllers, which are compatible with the Synopsys DesignWare APB SSI. Signed-off-by: Jisheng Zhang Acked-by: Conor Dooley Link: https://patch.msgid.link/20261008010058.28382-1-jszhang@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index e8d6d5d858b6..41ec69f5dceb 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -77,6 +77,7 @@ properties: - mscc,ocelot-spi - mscc,jaguar2-spi - sophgo,sg2042-spi + - syna,sl2610-spi - thead,th1520-spi - ultrarisc,dp1000-spi - const: snps,dw-apb-ssi