Skip to content

Commit e885c8c

Browse files
xudongzhengkartben
authored andcommitted
drivers: serial: pl011: prevent concurrent interrupt callback execution
Since the callback can be executed from either software or a hardware interrupt, it's possible for multiple instances of the callback function to run concurrently. That should not happen and can be particularly problematic on SMP systems. This commit adds a spinlock to prevent concurrent execution. This addresses a Twister CI failure discovered in #85539. Signed-off-by: Xudong Zheng <[email protected]>
1 parent 784f5b5 commit e885c8c

File tree

1 file changed

+34
-21
lines changed

1 file changed

+34
-21
lines changed

drivers/serial/uart_pl011.c

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ struct pl011_data {
7474
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
7575
volatile bool sw_call_txdrdy;
7676
uart_irq_callback_user_data_t irq_cb;
77+
struct k_spinlock irq_cb_lock;
7778
void *irq_cb_data;
7879
#endif
7980
};
@@ -342,26 +343,36 @@ static void pl011_irq_tx_enable(const struct device *dev)
342343
struct pl011_data *data = dev->data;
343344

344345
get_uart(dev)->imsc |= PL011_IMSC_TXIM;
345-
if (data->sw_call_txdrdy) {
346-
data->sw_call_txdrdy = false;
347-
348-
/* Verify if the callback has been registered */
349-
if (data->irq_cb) {
350-
/*
351-
* Due to HW limitation, the first TX interrupt should
352-
* be triggered by the software.
353-
*
354-
* PL011 TX interrupt is based on a transition through
355-
* a level, rather than on the level itself[1]. So that,
356-
* enable TX interrupt can not trigger TX interrupt if
357-
* no data was filled to TX FIFO at the beginning.
358-
*
359-
* [1]: PrimeCell UART (PL011) Technical Reference Manual
360-
* functional-overview/interrupts
361-
*/
362-
while (get_uart(dev)->imsc & PL011_IMSC_TXIM) {
363-
data->irq_cb(dev, data->irq_cb_data);
364-
}
346+
if (!data->sw_call_txdrdy) {
347+
return;
348+
}
349+
data->sw_call_txdrdy = false;
350+
351+
/*
352+
* Verify if the callback has been registered. Due to HW limitation, the
353+
* first TX interrupt should be triggered by the software.
354+
*
355+
* PL011 TX interrupt is based on a transition through a level, rather
356+
* than on the level itself[1]. So that, enable TX interrupt can not
357+
* trigger TX interrupt if no data was filled to TX FIFO at the
358+
* beginning.
359+
*
360+
* [1]: PrimeCell UART (PL011) Technical Reference Manual
361+
* functional-overview/interrupts
362+
*/
363+
if (!data->irq_cb) {
364+
return;
365+
}
366+
367+
/*
368+
* Execute callback while TX interrupt remains enabled. If
369+
* uart_fifo_fill() is called with small amounts of data, the 1/8 TX
370+
* FIFO threshold may never be reached, and the hardware TX interrupt
371+
* will never trigger.
372+
*/
373+
while (get_uart(dev)->imsc & PL011_IMSC_TXIM) {
374+
K_SPINLOCK(&data->irq_cb_lock) {
375+
data->irq_cb(dev, data->irq_cb_data);
365376
}
366377
}
367378
}
@@ -626,7 +637,9 @@ void pl011_isr(const struct device *dev)
626637

627638
/* Verify if the callback has been registered */
628639
if (data->irq_cb) {
629-
data->irq_cb(dev, data->irq_cb_data);
640+
K_SPINLOCK(&data->irq_cb_lock) {
641+
data->irq_cb(dev, data->irq_cb_data);
642+
}
630643
}
631644
}
632645
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */

0 commit comments

Comments
 (0)