spi: atmel-quadspi: add padcalib, 2xgclk, and dllon capabilities

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>
This commit is contained in:
Varshini Rajendran 2025-09-08 09:44:18 +05:30 committed by Mark Brown
parent f3837edc05
commit 86d074921e
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View 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[] = {