]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
spi: spi-nxp-fspi: propagate clock reconfig failures in nxp_fspi_select_mem()
authorHaibo Chen <haibo.chen@nxp.com>
Tue, 28 Jul 2026 10:18:10 +0000 (18:18 +0800)
committerMark Brown <broonie@kernel.org>
Thu, 30 Jul 2026 12:13:50 +0000 (13:13 +0100)
nxp_fspi_select_mem() disables the FlexSPI clocks before calling
clk_set_rate() and re-enabling them. If clk_set_rate() or the clock
re-enable fails, the function returned early (as void) leaving both the
serial root clock and the register interface clock disabled.

As the function returned void, nxp_fspi_exec_op() had no way to know
about the failure and continued to access FlexSPI registers (LUT setup,
data transfer, AHB buffer invalidation). Accessing the controller
registers while its clock is gated off results in a synchronous external
abort.

Make nxp_fspi_select_mem() return an error code and have
nxp_fspi_exec_op() bail out on failure before any further register
access, including nxp_fspi_invalid().

Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Link: https://patch.msgid.link/20260728-fspi-clock-v2-3-dbe786a4a6eb@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-nxp-fspi.c

index 54355295f9198bd6cf2ab9a43fe12d25ced80769..4425132432ec87ff075ea043ef0358db65ffcae7 100644 (file)
@@ -859,8 +859,8 @@ static void nxp_fspi_dll_override(struct nxp_fspi *f)
  * Value for rest of the CS FLSHxxCR0 register would be zero.
  *
  */
-static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
-                               const struct spi_mem_op *op)
+static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
+                              const struct spi_mem_op *op)
 {
        /* flexspi only support one DTR mode: 8D-8D-8D */
        bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr;
@@ -881,7 +881,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
        if ((f->selected == spi_get_chipselect(spi, 0)) &&
            (!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) &&
            (f->pre_op_rate == op->max_freq))
-               return;
+               return 0;
 
        /* Reset FLSHxxCR0 registers */
        fspi_writel(f, 0, f->iobase + FSPI_FLSHA1CR0);
@@ -922,12 +922,19 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
        nxp_fspi_clk_disable_unprep(f);
 
        ret = clk_set_rate(f->clk, rate);
-       if (ret)
-               return;
+       if (ret) {
+               /*
+                * clk_set_rate() failed with the clocks already disabled.
+                * Re-enable them so the enable count matches what the caller's
+                * pm_runtime_put() (runtime_suspend) will drop.
+                */
+               nxp_fspi_clk_prep_enable(f);
+               return ret;
+       }
 
        ret = nxp_fspi_clk_prep_enable(f);
        if (ret)
-               return;
+               return ret;
 
        /*
         * If clock rate > 100MHz, then switch from DLL override mode to
@@ -945,6 +952,8 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
        f->pre_op_rate = op->max_freq;
 
        f->selected = spi_get_chipselect(spi, 0);
+
+       return 0;
 }
 
 static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
@@ -1132,7 +1141,16 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
                                   FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
        WARN_ON(err);
 
-       nxp_fspi_select_mem(f, mem->spi, op);
+       err = nxp_fspi_select_mem(f, mem->spi, op);
+       if (err) {
+               /*
+                * On failure the FlexSPI clock may be left disabled, so avoid
+                * any further register access (which would trigger a synchronous
+                * external abort) and bail out.
+                */
+               pm_runtime_put_autosuspend(f->dev);
+               return err;
+       }
 
        nxp_fspi_prepare_lut(f, op);
        /*