spi: microchip-core: prevent RX overflows when transmit size > FIFO size
commit91cf42c63fupstream. When the size of a transfer exceeds the size of the FIFO (32 bytes), RX overflows will be generated and receive data will be corrupted and warnings will be produced. For example, here's an error generated by a transfer of 36 bytes: spi_master spi0: mchp_corespi_interrupt: RX OVERFLOW: rxlen: 4, txlen: 0 The driver is currently split between handling receiving in the interrupt handler, and sending outside of it. Move all handling out of the interrupt handling, and explicitly link the number of bytes read of of the RX FIFO to the number written into the TX one. This both resolves the overflow problems as well as simplifying the flow of the driver. CC: stable@vger.kernel.org Fixes:9ac8d17694("spi: add support for microchip fpga spi controllers") Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Link: https://patch.msgid.link/20250303-veal-snooper-712c1dfad336@wendy Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
488ffc0cac
commit
220e26960e
@@ -70,8 +70,7 @@
|
||||
#define INT_RX_CHANNEL_OVERFLOW BIT(2)
|
||||
#define INT_TX_CHANNEL_UNDERRUN BIT(3)
|
||||
|
||||
#define INT_ENABLE_MASK (CONTROL_RX_DATA_INT | CONTROL_TX_DATA_INT | \
|
||||
CONTROL_RX_OVER_INT | CONTROL_TX_UNDER_INT)
|
||||
#define INT_ENABLE_MASK (CONTROL_RX_OVER_INT | CONTROL_TX_UNDER_INT)
|
||||
|
||||
#define REG_CONTROL (0x00)
|
||||
#define REG_FRAME_SIZE (0x04)
|
||||
@@ -133,10 +132,15 @@ static inline void mchp_corespi_disable(struct mchp_corespi *spi)
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi)
|
||||
static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max)
|
||||
{
|
||||
while (spi->rx_len >= spi->n_bytes && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) {
|
||||
u32 data = mchp_corespi_read(spi, REG_RX_DATA);
|
||||
for (int i = 0; i < fifo_max; i++) {
|
||||
u32 data;
|
||||
|
||||
while (mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
|
||||
;
|
||||
|
||||
data = mchp_corespi_read(spi, REG_RX_DATA);
|
||||
|
||||
spi->rx_len -= spi->n_bytes;
|
||||
|
||||
@@ -211,11 +215,10 @@ static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
|
||||
mchp_corespi_write(spi, REG_FRAMESUP, len);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi)
|
||||
static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max)
|
||||
{
|
||||
int fifo_max, i = 0;
|
||||
int i = 0;
|
||||
|
||||
fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
|
||||
mchp_corespi_set_xfer_size(spi, fifo_max);
|
||||
|
||||
while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
|
||||
@@ -413,19 +416,6 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
|
||||
if (intfield == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (intfield & INT_TXDONE)
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_TXDONE);
|
||||
|
||||
if (intfield & INT_RXRDY) {
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_RXRDY);
|
||||
|
||||
if (spi->rx_len)
|
||||
mchp_corespi_read_fifo(spi);
|
||||
}
|
||||
|
||||
if (!spi->rx_len && !spi->tx_len)
|
||||
finalise = true;
|
||||
|
||||
if (intfield & INT_RX_CHANNEL_OVERFLOW) {
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
|
||||
finalise = true;
|
||||
@@ -512,9 +502,14 @@ static int mchp_corespi_transfer_one(struct spi_controller *host,
|
||||
|
||||
mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
|
||||
|
||||
while (spi->tx_len)
|
||||
mchp_corespi_write_fifo(spi);
|
||||
while (spi->tx_len) {
|
||||
int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
|
||||
|
||||
mchp_corespi_write_fifo(spi, fifo_max);
|
||||
mchp_corespi_read_fifo(spi, fifo_max);
|
||||
}
|
||||
|
||||
spi_finalize_current_transfer(host);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user