drivers: uart_nrfx_uart: Fix disabling of TX IRQ

The STOPTX task cannot be triggered directly in the function that
disables TX interrupt because this task stops the UART transmitter
immediately, even if it is in the middle of shifting out a byte.
Instead, this task needs to be triggered in the interrupt handler,
when the end of transmission is signaled.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2020-06-29 09:55:55 +02:00 committed by Carles Cufí
commit 5f6985ce64

View file

@ -99,6 +99,7 @@ static void *irq_cb_data; /**< Callback function arg */
* to accept a new byte.
*/
static volatile uint8_t uart_sw_event_txdrdy;
static volatile bool disable_tx_irq;
#endif /* CONFIG_UART_0_INTERRUPT_DRIVEN */
@ -805,6 +806,8 @@ static void uart_nrfx_irq_tx_enable(struct device *dev)
{
uint32_t key;
disable_tx_irq = false;
/* Indicate that this device started a transaction that should not be
* interrupted by putting the SoC into the deep sleep mode.
*/
@ -832,17 +835,8 @@ static void uart_nrfx_irq_tx_enable(struct device *dev)
/** Interrupt driven transfer disabling function */
static void uart_nrfx_irq_tx_disable(struct device *dev)
{
nrf_uart_int_disable(uart0_addr, NRF_UART_INT_MASK_TXDRDY);
/* Deactivate the transmitter so that it does not needlessly consume
* power.
*/
nrf_uart_task_trigger(uart0_addr, NRF_UART_TASK_STOPTX);
/* The transaction is over. It is okay to enter the deep sleep mode
* if needed.
*/
device_busy_clear(dev);
/* Disable TX interrupt in uart_nrfx_isr() when transmission is done. */
disable_tx_irq = true;
}
/** Interrupt driven receiver enabling function */
@ -860,7 +854,15 @@ static void uart_nrfx_irq_rx_disable(struct device *dev)
/** Interrupt driven transfer empty function */
static int uart_nrfx_irq_tx_ready_complete(struct device *dev)
{
return event_txdrdy_check();
/* Signal TX readiness only when the TX interrupt is enabled and there
* is no pending request to disable it. Note that this function may get
* called after the TX interrupt is requested to be disabled but before
* the disabling is actually performed (in the IRQ handler).
*/
return nrf_uart_int_enable_check(uart0_addr,
NRF_UART_INT_MASK_TXDRDY) &&
!disable_tx_irq &&
event_txdrdy_check();
}
/** Interrupt driven receiver ready function */
@ -886,7 +888,7 @@ static int uart_nrfx_irq_is_pending(struct device *dev)
{
return ((nrf_uart_int_enable_check(uart0_addr,
NRF_UART_INT_MASK_TXDRDY) &&
event_txdrdy_check())
uart_nrfx_irq_tx_ready_complete(dev))
||
(nrf_uart_int_enable_check(uart0_addr,
NRF_UART_INT_MASK_RXDRDY) &&
@ -920,7 +922,26 @@ static void uart_nrfx_irq_callback_set(struct device *dev,
*/
static void uart_nrfx_isr(void *arg)
{
ARG_UNUSED(arg);
struct device *dev = arg;
if (disable_tx_irq &&
nrf_uart_event_check(uart0_addr, NRF_UART_EVENT_TXDRDY)) {
nrf_uart_int_disable(uart0_addr, NRF_UART_INT_MASK_TXDRDY);
/* Deactivate the transmitter so that it does not needlessly
* consume power.
*/
nrf_uart_task_trigger(uart0_addr, NRF_UART_TASK_STOPTX);
/* The transaction is over. It is okay to enter the deep sleep
* mode if needed.
*/
device_busy_clear(dev);
disable_tx_irq = false;
return;
}
if (nrf_uart_event_check(uart0_addr, NRF_UART_EVENT_ERROR)) {
nrf_uart_event_clear(uart0_addr, NRF_UART_EVENT_ERROR);