summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRamin Moussavi <lordrasmus@gmail.com>2025-07-22 20:27:09 +0200
committerEugen Hristev <eugen.hristev@linaro.org>2025-08-13 12:59:02 +0300
commit709b5be0a48d4e5bb95ef0b107c56045a8fe67c6 (patch)
tree8a923a7cb29d832fa9eead2e417f648746550d7d
parenta8f20bb6650df56d2600cda2c66f9349df9e49c8 (diff)
spi: atmel_qspi: fix race condition in transfer completion check
In atmel_qspi_transfer(), the status register is polled with: imr = QSPI_SR_INSTRE | QSPI_SR_CSR; return readl_poll_timeout(aq->regs + QSPI_SR, sr, (sr & imr) == imr, ATMEL_QSPI_TIMEOUT); However, this is racy: QSPI_SR_INSTRE can be set before QSPI_SR_CSR, and will then be cleared by the read. If that happens, the condition "(sr & imr) == imr" can never be true, and the function times out. This race condition is avoided in at91bootstrap by accumulating the status bits across reads until both bits have been observed: /* Poll INSTruction End and Chip Select Rise flags. */ imr = (QSPI_SR_INSTRE | QSPI_SR_CSR); sr = 0; do { udelay(1); sr |= qspi_readl(qspi, QSPI_SR) & imr; } while ((--timeout) && (sr != imr)); Update U-Boot's atmel_qspi_transfer() to use the same pattern, ensuring that both flags are observed even if they are not set simultaneously. Signed-off-by: Ramin Moussavi <lordrasmus@gmail.com> [eugen.hristev@linaro.org: remove 'sr' and fix commit msg] Signed-off-by: Eugen Hristev <eugen.hristev@linaro.org>
-rw-r--r--drivers/spi/atmel-quadspi.c15
1 files changed, 12 insertions, 3 deletions
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 8aa7a83aef4..b2b96e1c4b9 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -615,7 +615,8 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
static int atmel_qspi_transfer(struct atmel_qspi *aq,
const struct spi_mem_op *op, u32 offset)
{
- u32 sr, imr;
+ u32 imr, val = 0;
+ unsigned long timeout;
/* Skip to the final steps if there is no data */
if (op->data.nbytes) {
@@ -636,8 +637,16 @@ static int atmel_qspi_transfer(struct atmel_qspi *aq,
/* Poll INSTruction End and Chip Select Rise flags. */
imr = QSPI_SR_INSTRE | QSPI_SR_CSR;
- return readl_poll_timeout(aq->regs + QSPI_SR, sr, (sr & imr) == imr,
- ATMEL_QSPI_TIMEOUT);
+
+ timeout = timer_get_us() + ATMEL_QSPI_TIMEOUT;
+ while (1) {
+ val |= readl(aq->regs + QSPI_SR) & imr;
+ if ((val & imr) == imr)
+ return 0;
+
+ if (time_after(timer_get_us(), timeout))
+ return -ETIMEDOUT;
+ }
}
static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq,