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>
This commit is contained in:
parent
7fa815c929
commit
47e43d552e
2 changed files with 60 additions and 0 deletions
|
@ -103,6 +103,17 @@ config UART_NS16550_WA_ISR_REENABLE_INTERRUPT
|
|||
the IER is being toggled to re-assert interrupts at the end of ISR
|
||||
to nudge the host interrupt controller to fire the ISR again.
|
||||
|
||||
config UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
bool "Callback directly when the TX FIFO is already empty"
|
||||
default y if SHELL_BACKEND_SERIAL
|
||||
depends on UART_INTERRUPT_DRIVEN
|
||||
help
|
||||
When calling uart_ns16550_irq_tx_enable() to wait for the TX FIFO
|
||||
ready interrupt, but this interrupt will only be triggered if the
|
||||
current state is not empty. Therefore, if the current state is
|
||||
empty, you need to solve this problem by calling the callback
|
||||
function directly.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # UART_NS16550
|
||||
|
|
|
@ -369,6 +369,10 @@ struct uart_ns16550_dev_data {
|
|||
void *cb_data; /**< Callback function arg */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
uint8_t sw_tx_irq; /**< software tx ready flag */
|
||||
#endif
|
||||
|
||||
#if UART_NS16550_DLF_ENABLED
|
||||
uint8_t dlf; /**< DLF value */
|
||||
#endif
|
||||
|
@ -933,6 +937,10 @@ static void uart_ns16550_poll_out(const struct device *dev,
|
|||
|
||||
ns16550_outbyte(dev_cfg, THR(dev), c);
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
data->sw_tx_irq = 0; /**< clean up */
|
||||
#endif
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
|
@ -980,6 +988,12 @@ static int uart_ns16550_fifo_fill(const struct device *dev,
|
|||
ns16550_outbyte(dev_cfg, THR(dev), tx_data[i]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
if (i != 0) {
|
||||
data->sw_tx_irq = 0; /**< clean up */
|
||||
}
|
||||
#endif
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return i;
|
||||
|
@ -1042,6 +1056,26 @@ static void uart_ns16550_irq_tx_enable(const struct device *dev)
|
|||
#endif
|
||||
ns16550_outbyte(dev_cfg, IER(dev), ns16550_inbyte(dev_cfg, IER(dev)) | IER_TBE);
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
if (ns16550_inbyte(dev_cfg, LSR(dev)) & LSR_THRE) {
|
||||
k_spin_unlock(&data->lock, key);
|
||||
/*
|
||||
* The TX FIFO ready interrupt will be triggered if only if
|
||||
* when the pre-state is not empty. Thus, if the pre-state is
|
||||
* already empty, try to call the callback routine directly
|
||||
* to resolve it.
|
||||
*/
|
||||
int irq_lock_key = arch_irq_lock();
|
||||
|
||||
if (data->cb && (ns16550_inbyte(dev_cfg, LSR(dev)) & LSR_THRE)) {
|
||||
data->sw_tx_irq = 1; /**< set tx ready */
|
||||
data->cb(dev, data->cb_data);
|
||||
}
|
||||
arch_irq_unlock(irq_lock_key);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
|
@ -1056,6 +1090,10 @@ static void uart_ns16550_irq_tx_disable(const struct device *dev)
|
|||
const struct uart_ns16550_dev_config * const dev_cfg = dev->config;
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
data->sw_tx_irq = 0; /**< clean up */
|
||||
#endif
|
||||
|
||||
ns16550_outbyte(dev_cfg, IER(dev),
|
||||
ns16550_inbyte(dev_cfg, IER(dev)) & (~IER_TBE));
|
||||
|
||||
|
@ -1096,6 +1134,17 @@ static int uart_ns16550_irq_tx_ready(const struct device *dev)
|
|||
|
||||
int ret = ((IIRC(dev) & IIR_ID) == IIR_THRE) ? 1 : 0;
|
||||
|
||||
#ifdef CONFIG_UART_NS16550_WA_TX_FIFO_EMPTY_INTERRUPT
|
||||
if (ret == 0 && data->sw_tx_irq) {
|
||||
/**< replace resoult when there is a software solution */
|
||||
const struct uart_ns16550_dev_config * const dev_cfg = dev->config;
|
||||
|
||||
if (ns16550_inbyte(dev_cfg, IER(dev)) & IER_TBE) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return ret;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue