From b2199bb744fc04bac791089dfdee7c57f5c8b37a Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 29 Mar 2023 16:06:37 +0200 Subject: [PATCH] cpu/stm32: Fix periph_spi operation in non-DMA mode The driver previously failed to reliably clear the RXNE bit, resulting in the next transfer to incorrectly read a stale register value. This was noticed with the SD card SPI driver on an STM32F4, in which the 0xff byte of the previous byte transfer was returned instead of the actual status byte, throwing the SD card driver off the rails. --- cpu/stm32/periph/spi.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index af8e498ea61b..47e324eb9562 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -356,33 +356,36 @@ static void _transfer_no_dma(spi_t bus, const void *out, void *in, size_t len) /* transfer data, use shortpath if only sending data */ if (!inbuf) { for (size_t i = 0; i < len; i++) { - while (!(dev(bus)->SR & SPI_SR_TXE)); + while (!(dev(bus)->SR & SPI_SR_TXE)) {} *DR = outbuf[i]; } - /* wait until everything is finished and empty the receive buffer */ - while (!(dev(bus)->SR & SPI_SR_TXE)) {} - while (dev(bus)->SR & SPI_SR_BSY) {} - while (dev(bus)->SR & SPI_SR_RXNE) { - dev(bus)->DR; /* we might just read 2 bytes at once here */ - } } else if (!outbuf) { for (size_t i = 0; i < len; i++) { - while (!(dev(bus)->SR & SPI_SR_TXE)); + while (!(dev(bus)->SR & SPI_SR_TXE)) { /* busy wait */ } *DR = 0; - while (!(dev(bus)->SR & SPI_SR_RXNE)); + while (!(dev(bus)->SR & SPI_SR_RXNE)) { /* busy wait */ } inbuf[i] = *DR; } } else { for (size_t i = 0; i < len; i++) { - while (!(dev(bus)->SR & SPI_SR_TXE)); + while (!(dev(bus)->SR & SPI_SR_TXE)) { /* busy wait */ } *DR = outbuf[i]; - while (!(dev(bus)->SR & SPI_SR_RXNE)); + while (!(dev(bus)->SR & SPI_SR_RXNE)) { /* busy wait */ } inbuf[i] = *DR; } } + /* wait until everything is finished and empty the receive buffer */ + while (!(dev(bus)->SR & SPI_SR_TXE)) {} + while (dev(bus)->SR & SPI_SR_BSY) {} + while (dev(bus)->SR & SPI_SR_RXNE) { + /* make sure to "read" any data, so the RXNE is indeed clear. + * Otherwise we risk reading stale data in the next transfer */ + (void)*DR; + } + _wait_for_end(bus); } @@ -404,7 +407,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, } else { #endif - _transfer_no_dma(bus, out, in, len); + _transfer_no_dma(bus, out, in, len); #ifdef MODULE_PERIPH_DMA } #endif