Skip to content

Commit a52c2f7

Browse files
committed
[nrf fromtree] drivers: mspi_dw: Fix race condition in RX interrupt handling
Refactor a bit the RX FIFO handling to prevent RX transfers performed in Single IO mode from getting stuck when they are suspended due to the empty TX FIFO and cannot be resumed because there is no further interrupt in which the TX FIFO could be filled properly with dummy bytes above its transfer start level. Signed-off-by: Andrzej Głąbek <[email protected]> (cherry picked from commit f0f5f8c)
1 parent b804bab commit a52c2f7

File tree

1 file changed

+30
-18
lines changed

1 file changed

+30
-18
lines changed

drivers/mspi/mspi_dw.c

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,12 @@ static bool read_rx_fifo(const struct device *dev,
250250
uint8_t *buf_pos = dev_data->buf_pos;
251251
const uint8_t *buf_end = &packet->data_buf[packet->num_bytes];
252252
uint8_t bytes_per_frame_exp = dev_data->bytes_per_frame_exp;
253-
/* See `room` in tx_data(). */
254-
uint32_t in_fifo = 1;
255253
uint32_t remaining_frames;
254+
uint32_t in_fifo = FIELD_GET(RXFLR_RXTFL_MASK, read_rxflr(dev));
255+
256+
if (in_fifo == 0) {
257+
return false;
258+
}
256259

257260
do {
258261
uint32_t data = read_dr(dev);
@@ -327,28 +330,37 @@ static void handle_fifos(const struct device *dev)
327330
}
328331
} else {
329332
for (;;) {
333+
/* Always read everything from the RX FIFO, regardless
334+
* of the interrupt status.
335+
* tx_dummy_bytes() subtracts the number of items that
336+
* are present in the RX FIFO from the number of dummy
337+
* bytes it is allowed to send, so it can potentially
338+
* not fill the TX FIFO above its transfer start level
339+
* in some iteration of this loop. If in such case the
340+
* interrupt handler exited without emptying the RX FIFO
341+
* (seeing the RXFIS flag not set due to not enough
342+
* items in the RX FIFO), this could lead to a situation
343+
* in which a transfer stopped temporarily (after the TX
344+
* FIFO got empty) is not resumed (since the TX FIFO is
345+
* not filled above its transfer start level), so no
346+
* further dummy bytes are transmitted and the RX FIFO
347+
* has no chance to get new entries, hence no further
348+
* interrupts are generated and the transfer gets stuck.
349+
*/
350+
if (read_rx_fifo(dev, packet)) {
351+
finished = true;
352+
break;
353+
}
354+
330355
/* Use RISR, not ISR, because when this function is
331356
* executed through the system workqueue, all interrupts
332357
* are masked (IMR is 0).
333358
*/
334359
uint32_t int_status = read_risr(dev);
335360

336-
if (int_status & RISR_RXFIR_BIT) {
337-
if (read_rx_fifo(dev, packet)) {
338-
finished = true;
339-
break;
340-
}
341-
342-
if (int_status & RISR_RXOIR_BIT) {
343-
finished = true;
344-
break;
345-
}
346-
347-
/* Refresh interrupt status, as during reading
348-
* from the RX FIFO, the TX FIFO status might
349-
* have changed.
350-
*/
351-
int_status = read_risr(dev);
361+
if (int_status & RISR_RXOIR_BIT) {
362+
finished = true;
363+
break;
352364
}
353365

354366
if (dev_data->dummy_bytes == 0 ||

0 commit comments

Comments
 (0)