]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
spi: atmel-quadspi: Add cs_hold and cs_inactive setting support
authorAlexander Dahl <ada@thorsis.com>
Wed, 18 Sep 2024 08:27:44 +0000 (10:27 +0200)
committerMark Brown <broonie@kernel.org>
Sun, 29 Sep 2024 23:12:26 +0000 (01:12 +0200)
spi-cs-inactive-delay-ns in dts is cs_inactive in spi core, and it maps
to DLYCS (Minimum Inactive QCS Delay) in QSPI Mode Register (QSPI_MR).

spi-cs-hold-delay-ns in dts is cs_hold in spi core, and it maps to
DLYBCT (Delay Between Consecutive Transfers) in QSPI_MR.  That one can
be set to other values than 0 only if the chip is not in Serial Memory
Mode (SMM), it must be written to '0' however when in SMM.

Tested on SAM9X60 based board with FPGA implementing custom SPI Memory
protocol.

Signed-off-by: Alexander Dahl <ada@thorsis.com>
Link: https://patch.msgid.link/20240918082744.379610-3-ada@thorsis.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/atmel-quadspi.c

index 20ee227dead29020f0f379953830ca0d121f06ac..b328076291891721f6dd6482e61cfa3fb34d4176 100644 (file)
@@ -516,21 +516,45 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
        struct spi_controller *ctrl = spi->controller;
        struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
        unsigned long clk_rate;
+       u32 cs_inactive;
        u32 cs_setup;
+       u32 cs_hold;
        int delay;
        int ret;
 
-       delay = spi_delay_to_ns(&spi->cs_setup, NULL);
-       if (delay <= 0)
-               return delay;
-
        clk_rate = clk_get_rate(aq->pclk);
        if (!clk_rate)
                return -EINVAL;
 
+       /* hold */
+       delay = spi_delay_to_ns(&spi->cs_hold, NULL);
+       if (aq->mr & QSPI_MR_SMM) {
+               if (delay > 0)
+                       dev_warn(&aq->pdev->dev,
+                                "Ignoring cs_hold, must be 0 in Serial Memory Mode.\n");
+               cs_hold = 0;
+       } else {
+               delay = spi_delay_to_ns(&spi->cs_hold, NULL);
+               if (delay < 0)
+                       return delay;
+
+               cs_hold = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)), 32000);
+       }
+
+       /* setup */
+       delay = spi_delay_to_ns(&spi->cs_setup, NULL);
+       if (delay < 0)
+               return delay;
+
        cs_setup = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)),
                                1000);
 
+       /* inactive */
+       delay = spi_delay_to_ns(&spi->cs_inactive, NULL);
+       if (delay < 0)
+               return delay;
+       cs_inactive = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)), 1000);
+
        ret = pm_runtime_resume_and_get(ctrl->dev.parent);
        if (ret < 0)
                return ret;
@@ -539,6 +563,10 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
        aq->scr |= QSPI_SCR_DLYBS(cs_setup);
        atmel_qspi_write(aq->scr, aq, QSPI_SCR);
 
+       aq->mr &= ~(QSPI_MR_DLYBCT_MASK | QSPI_MR_DLYCS_MASK);
+       aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive);
+       atmel_qspi_write(aq->mr, aq, QSPI_MR);
+
        pm_runtime_mark_last_busy(ctrl->dev.parent);
        pm_runtime_put_autosuspend(ctrl->dev.parent);