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
|
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.
|
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
|
endmenu
|
||||||
|
|
||||||
endif # UART_NS16550
|
endif # UART_NS16550
|
||||||
|
|
|
@ -369,6 +369,10 @@ struct uart_ns16550_dev_data {
|
||||||
void *cb_data; /**< Callback function arg */
|
void *cb_data; /**< Callback function arg */
|
||||||
#endif
|
#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
|
#if UART_NS16550_DLF_ENABLED
|
||||||
uint8_t dlf; /**< DLF value */
|
uint8_t dlf; /**< DLF value */
|
||||||
#endif
|
#endif
|
||||||
|
@ -933,6 +937,10 @@ static void uart_ns16550_poll_out(const struct device *dev,
|
||||||
|
|
||||||
ns16550_outbyte(dev_cfg, THR(dev), c);
|
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);
|
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]);
|
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);
|
k_spin_unlock(&data->lock, key);
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
|
@ -1042,6 +1056,26 @@ static void uart_ns16550_irq_tx_enable(const struct device *dev)
|
||||||
#endif
|
#endif
|
||||||
ns16550_outbyte(dev_cfg, IER(dev), ns16550_inbyte(dev_cfg, IER(dev)) | IER_TBE);
|
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);
|
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;
|
const struct uart_ns16550_dev_config * const dev_cfg = dev->config;
|
||||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
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_outbyte(dev_cfg, IER(dev),
|
||||||
ns16550_inbyte(dev_cfg, IER(dev)) & (~IER_TBE));
|
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;
|
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);
|
k_spin_unlock(&data->lock, key);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue