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:
Paul Sokolovsky 2018-11-12 21:22:54 +03:00 committed by Kumar Gala
commit 49bb163756

View file

@ -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 */