spi: atmel-quadspi: add padcalib, 2xgclk, and dllon capabilities
authorVarshini Rajendran <varshini.rajendran@microchip.com>
Mon, 8 Sep 2025 04:14:18 +0000 (09:44 +0530)
committerMark Brown <broonie@kernel.org>
Thu, 18 Sep 2025 21:26:40 +0000 (22:26 +0100)
Introduce capability flags for SoC-specific variations of the QuadSPI
controller:

  - has_padcalib: controller supports pad calibration
  - has_2xgclk: requires GCLK at half the data rate (2x clocking)
  - has_dllon: controller supports DLL clock

Set `has_padcalib` for Octal controllers that provide pad calibration
support. Use `has_2xgclk` for controllers that require the GCLK to run
at twice the data rate. Differentiate SoC integration variants with the
`has_dllon` flag and set it as needed.

Signed-off-by: Varshini Rajendran <varshini.rajendran@microchip.com>
Signed-off-by: Dharma Balasubiramani <dharma.b@microchip.com>
Link: https://patch.msgid.link/20250908-microchip-qspi-v2-3-8f3d69fdd5c9@microchip.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/atmel-quadspi.c

index 4e9bfd2..83cea5f 100644 (file)
@@ -262,6 +262,9 @@ struct atmel_qspi_caps {
        bool has_ricr;
        bool octal;
        bool has_dma;
+       bool has_2xgclk;
+       bool has_padcalib;
+       bool has_dllon;
 };
 
 struct atmel_qspi_ops;
@@ -1027,13 +1030,25 @@ static int atmel_qspi_set_pad_calibration(struct atmel_qspi *aq)
                         aq, QSPI_PCALCFG);
 
        /* DLL On + start calibration. */
-       atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
+       if (aq->caps->has_dllon)
+               atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
+       /* If there is no DLL support only start calibration. */
+       else
+               atmel_qspi_write(QSPI_CR_STPCAL, aq, QSPI_CR);
 
-       /* Check synchronization status before updating configuration. */
-       ret =  readl_poll_timeout(aq->regs + QSPI_SR2, val,
-                                 (val & QSPI_SR2_DLOCK) &&
-                                 !(val & QSPI_SR2_CALBSY), 40,
-                                 ATMEL_QSPI_TIMEOUT);
+       /*
+        * Check DLL clock lock and synchronization status before updating
+        * configuration.
+        */
+       if (aq->caps->has_dllon)
+               ret =  readl_poll_timeout(aq->regs + QSPI_SR2, val,
+                                         (val & QSPI_SR2_DLOCK) &&
+                                         !(val & QSPI_SR2_CALBSY), 40,
+                                         ATMEL_QSPI_TIMEOUT);
+       else
+               ret =  readl_poll_timeout(aq->regs + QSPI_SR2, val,
+                                         !(val & QSPI_SR2_CALBSY), 40,
+                                         ATMEL_QSPI_TIMEOUT);
 
        /* Refresh analogic blocks every 1 ms.*/
        atmel_qspi_write(FIELD_PREP(QSPI_REFRESH_DELAY_COUNTER,
@@ -1049,23 +1064,28 @@ static int atmel_qspi_set_gclk(struct atmel_qspi *aq)
        int ret;
 
        /* Disable DLL before setting GCLK */
-       status = atmel_qspi_read(aq, QSPI_SR2);
-       if (status & QSPI_SR2_DLOCK) {
-               atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+       if (aq->caps->has_dllon) {
+               status = atmel_qspi_read(aq, QSPI_SR2);
+               if (status & QSPI_SR2_DLOCK) {
+                       atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+                       ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+                                                !(val & QSPI_SR2_DLOCK), 40,
+                                                ATMEL_QSPI_TIMEOUT);
+                       if (ret)
+                               return ret;
+               }
 
-               ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
-                                        !(val & QSPI_SR2_DLOCK), 40,
-                                        ATMEL_QSPI_TIMEOUT);
-               if (ret)
-                       return ret;
+               if (aq->target_max_speed_hz > QSPI_DLLCFG_THRESHOLD_FREQ)
+                       atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG);
+               else
+                       atmel_qspi_write(0, aq, QSPI_DLLCFG);
        }
 
-       if (aq->target_max_speed_hz > QSPI_DLLCFG_THRESHOLD_FREQ)
-               atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG);
+       if (aq->caps->has_2xgclk)
+               ret = clk_set_rate(aq->gclk, 2 * aq->target_max_speed_hz);
        else
-               atmel_qspi_write(0, aq, QSPI_DLLCFG);
+               ret = clk_set_rate(aq->gclk, aq->target_max_speed_hz);
 
-       ret = clk_set_rate(aq->gclk, aq->target_max_speed_hz);
        if (ret) {
                dev_err(&aq->pdev->dev, "Failed to set generic clock rate.\n");
                return ret;
@@ -1088,11 +1108,16 @@ static int atmel_qspi_sama7g5_init(struct atmel_qspi *aq)
        if (ret)
                return ret;
 
-       if (aq->caps->octal) {
+       /*
+        * Check if the SoC supports pad calibration in Octal SPI mode.
+        * Proceed only if both the capabilities are true.
+        */
+       if (aq->caps->octal && aq->caps->has_padcalib) {
                ret = atmel_qspi_set_pad_calibration(aq);
                if (ret)
                        return ret;
-       } else {
+       /* Start DLL on only if the SoC supports the same */
+       } else if (aq->caps->has_dllon) {
                atmel_qspi_write(QSPI_CR_DLLON, aq, QSPI_CR);
                ret =  readl_poll_timeout(aq->regs + QSPI_SR2, val,
                                          (val & QSPI_SR2_DLOCK), 40,
@@ -1458,19 +1483,19 @@ static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq)
 
        clk_disable_unprepare(aq->gclk);
 
-       atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
-       ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
-                                !(val & QSPI_SR2_DLOCK), 40,
-                                ATMEL_QSPI_TIMEOUT);
-       if (ret)
-               return ret;
-
-       ret =  readl_poll_timeout(aq->regs + QSPI_SR2, val,
-                                 !(val & QSPI_SR2_CALBSY), 40,
-                                 ATMEL_QSPI_TIMEOUT);
-       if (ret)
-               return ret;
+       if (aq->caps->has_dllon) {
+               atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+               ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+                                        !(val & QSPI_SR2_DLOCK), 40,
+                                        ATMEL_QSPI_TIMEOUT);
+               if (ret)
+                       return ret;
+       }
 
+       if (aq->caps->has_padcalib)
+               return readl_poll_timeout(aq->regs + QSPI_SR2, val,
+                                         !(val & QSPI_SR2_CALBSY), 40,
+                                         ATMEL_QSPI_TIMEOUT);
        return 0;
 }
 
@@ -1607,12 +1632,15 @@ static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
        .has_gclk = true,
        .octal = true,
        .has_dma = true,
+       .has_padcalib = true,
+       .has_dllon = true,
 };
 
 static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = {
        .max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
        .has_gclk = true,
        .has_dma = true,
+       .has_dllon = true,
 };
 
 static const struct of_device_id atmel_qspi_dt_ids[] = {