Skip to content

Commit 47e43d5

Browse files
Jacky Leekartben
authored andcommitted
drivers: serial: ns16550: Fix TX IRQ not triggered when FIFO is empty
When uart_ns16550_irq_tx_enable() is called and the TX FIFO is already empty, no new interrupt is generated, causing data transmission to stall in some cases. This patch introduces a workaround to simulate an ISR callback if the FIFO is empty when enabling the TX IRQ. Signed-off-by: Jacky Lee <jacky.lee@egistec.com>
1 parent 7fa815c commit 47e43d5

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

drivers/serial/Kconfig.ns16550

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ config UART_NS16550_WA_ISR_REENABLE_INTERRUPT
103103
the IER is being toggled to re-assert interrupts at the end of ISR
104104
to nudge the host interrupt controller to fire the ISR again.
105105

106+
config UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
107+
bool "Callback directly when the TX FIFO is already empty"
108+
default y if SHELL_BACKEND_SERIAL
109+
depends on UART_INTERRUPT_DRIVEN
110+
help
111+
When calling uart_ns16550_irq_tx_enable() to wait for the TX FIFO
112+
ready interrupt, but this interrupt will only be triggered if the
113+
current state is not empty. Therefore, if the current state is
114+
empty, you need to solve this problem by calling the callback
115+
function directly.
116+
106117
endmenu
107118

108119
endif # UART_NS16550

drivers/serial/uart_ns16550.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ struct uart_ns16550_dev_data {
369369
void *cb_data; /**< Callback function arg */
370370
#endif
371371

372+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
373+
uint8_t sw_tx_irq; /**< software tx ready flag */
374+
#endif
375+
372376
#if UART_NS16550_DLF_ENABLED
373377
uint8_t dlf; /**< DLF value */
374378
#endif
@@ -933,6 +937,10 @@ static void uart_ns16550_poll_out(const struct device *dev,
933937

934938
ns16550_outbyte(dev_cfg, THR(dev), c);
935939

940+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
941+
data->sw_tx_irq = 0; /**< clean up */
942+
#endif
943+
936944
k_spin_unlock(&data->lock, key);
937945
}
938946

@@ -980,6 +988,12 @@ static int uart_ns16550_fifo_fill(const struct device *dev,
980988
ns16550_outbyte(dev_cfg, THR(dev), tx_data[i]);
981989
}
982990

991+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
992+
if (i != 0) {
993+
data->sw_tx_irq = 0; /**< clean up */
994+
}
995+
#endif
996+
983997
k_spin_unlock(&data->lock, key);
984998

985999
return i;
@@ -1042,6 +1056,26 @@ static void uart_ns16550_irq_tx_enable(const struct device *dev)
10421056
#endif
10431057
ns16550_outbyte(dev_cfg, IER(dev), ns16550_inbyte(dev_cfg, IER(dev)) | IER_TBE);
10441058

1059+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
1060+
if (ns16550_inbyte(dev_cfg, LSR(dev)) & LSR_THRE) {
1061+
k_spin_unlock(&data->lock, key);
1062+
/*
1063+
* The TX FIFO ready interrupt will be triggered if only if
1064+
* when the pre-state is not empty. Thus, if the pre-state is
1065+
* already empty, try to call the callback routine directly
1066+
* to resolve it.
1067+
*/
1068+
int irq_lock_key = arch_irq_lock();
1069+
1070+
if (data->cb && (ns16550_inbyte(dev_cfg, LSR(dev)) & LSR_THRE)) {
1071+
data->sw_tx_irq = 1; /**< set tx ready */
1072+
data->cb(dev, data->cb_data);
1073+
}
1074+
arch_irq_unlock(irq_lock_key);
1075+
return;
1076+
}
1077+
#endif
1078+
10451079
k_spin_unlock(&data->lock, key);
10461080
}
10471081

@@ -1056,6 +1090,10 @@ static void uart_ns16550_irq_tx_disable(const struct device *dev)
10561090
const struct uart_ns16550_dev_config * const dev_cfg = dev->config;
10571091
k_spinlock_key_t key = k_spin_lock(&data->lock);
10581092

1093+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
1094+
data->sw_tx_irq = 0; /**< clean up */
1095+
#endif
1096+
10591097
ns16550_outbyte(dev_cfg, IER(dev),
10601098
ns16550_inbyte(dev_cfg, IER(dev)) & (~IER_TBE));
10611099

@@ -1096,6 +1134,17 @@ static int uart_ns16550_irq_tx_ready(const struct device *dev)
10961134

10971135
int ret = ((IIRC(dev) & IIR_ID) == IIR_THRE) ? 1 : 0;
10981136

1137+
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
1138+
if (ret == 0 && data->sw_tx_irq) {
1139+
/**< replace resoult when there is a software solution */
1140+
const struct uart_ns16550_dev_config * const dev_cfg = dev->config;
1141+
1142+
if (ns16550_inbyte(dev_cfg, IER(dev)) & IER_TBE) {
1143+
ret = 1;
1144+
}
1145+
}
1146+
#endif
1147+
10991148
k_spin_unlock(&data->lock, key);
11001149

11011150
return ret;

0 commit comments

Comments
 (0)