drivers: serial: uart_cmsdk_apb: Fix interrupt-driven operation
1. There's an expectations that TX ready (i.e. TX buffer space available) interrupt is a level interrupt, i.e. always active while there's TX buffer space available. In particular, there's an expectation that after uart_irq_tx_enable(), the TX interrupt will immediately fire (assuming free TX buffer space is available). But CMSDK UART interrupt appears to be edge interrupt, firing only on buffer state change. So, after irq_tx_enable(), we need to "bootstrap" interrupt processing by calling user-defined ISR manually (the ISR will see that TX ready to accept a new char, will write it there, then we'll get interrupt once TX buffer is ready again). 2. Interrupts should be acknowledges only after user ISR is called, because the ISR will check the status of interrupts. 3. Update stale comments. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
ee9b08d7da
commit
49bb163756
1 changed files with 16 additions and 4 deletions
|
@ -82,6 +82,7 @@ struct uart_cmsdk_apb_dev_data {
|
|||
((volatile struct uart_cmsdk_apb *)(DEV_CFG(dev))->base)
|
||||
|
||||
static const struct uart_driver_api uart_cmsdk_apb_driver_api;
|
||||
static void uart_cmsdk_apb_isr(void *arg);
|
||||
|
||||
/**
|
||||
* @brief Set the baud rate
|
||||
|
@ -258,7 +259,18 @@ static int uart_cmsdk_apb_fifo_read(struct device *dev,
|
|||
*/
|
||||
static void uart_cmsdk_apb_irq_tx_enable(struct device *dev)
|
||||
{
|
||||
unsigned int key;
|
||||
|
||||
UART_STRUCT(dev)->ctrl |= UART_TX_IN_EN;
|
||||
/* The expectation is that TX is a level interrupt, active for as
|
||||
* long as TX buffer is empty. But in CMSDK UART, it appears to be
|
||||
* edge interrupt, firing on a state change of TX buffer. So, we
|
||||
* need to "prime" it here by calling ISR directly, to get interrupt
|
||||
* processing going.
|
||||
*/
|
||||
key = irq_lock();
|
||||
uart_cmsdk_apb_isr(dev);
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,7 +322,7 @@ static void uart_cmsdk_apb_irq_rx_disable(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Verify if Tx empty interrupt has been raised
|
||||
* @brief Verify if Tx complete interrupt has been raised
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
|
@ -414,13 +426,13 @@ void uart_cmsdk_apb_isr(void *arg)
|
|||
volatile struct uart_cmsdk_apb *uart = UART_STRUCT(dev);
|
||||
struct uart_cmsdk_apb_dev_data *data = DEV_DATA(dev);
|
||||
|
||||
/* Clear pending interrupts */
|
||||
uart->intclear = UART_RX_IN | UART_TX_IN;
|
||||
|
||||
/* Verify if the callback has been registered */
|
||||
if (data->irq_cb) {
|
||||
data->irq_cb(data->irq_cb_data);
|
||||
}
|
||||
|
||||
/* Clear pending interrupts */
|
||||
uart->intclear = UART_RX_IN | UART_TX_IN;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue