[PATCH 3/5] spi: atmel-quadspi: add padcalib, 2xgclk, and dllon capabilities

Dharma Balasubiramani
2025-09-02T06:06:50Z

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>
---
drivers/spi/atmel-quadspi.c | 92 +++++++++++++++++++++++++++++----------------
1 file changed, 60 insertions(+), 32 deletions(-)

diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 4e9bfd26aa80..83cea5faff78 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -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[] = {

--
2.43.0