mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-02 09:46:37 +03:00
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>
This commit is contained in:
committed by
Eugen Hristev
parent
a8f20bb665
commit
709b5be0a4
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user