Commit 683dbc45 authored by Ron Smith's avatar Ron Smith Committed by Christopher Friedt
Browse files

drivers: serial: uart_sam0: Fix async tx done event triggering to early.



uart_sam0_dma_tx_done callback triggers when the last byte
is transferred from the tx sram buffer to the sercom DATA register.
However the byte has yet to be transmitted completely which can lead to
incorrect event handling if UART_TX_DONE is expected to signal
the end of transmission.

Signed-off-by: default avatarRon Smith <rockyowl171@gmail.com>
parent a0d92453
Loading
Loading
Loading
Loading
+28 −20
Original line number Diff line number Diff line
@@ -139,28 +139,11 @@ static void uart_sam0_dma_tx_done(const struct device *dma_dev, void *arg,

	struct uart_sam0_dev_data *const dev_data =
		(struct uart_sam0_dev_data *const) arg;
	const struct device *dev = dev_data->dev;

	k_work_cancel_delayable(&dev_data->tx_timeout_work);

	int key = irq_lock();

	struct uart_event evt = {
		.type = UART_TX_DONE,
		.data.tx = {
			.buf = dev_data->tx_buf,
			.len = dev_data->tx_len,
		},
	};

	dev_data->tx_buf = NULL;
	dev_data->tx_len = 0U;
	const struct uart_sam0_dev_cfg *const cfg = dev_data->cfg;

	if (evt.data.tx.len != 0U && dev_data->async_cb) {
		dev_data->async_cb(dev, &evt, dev_data->async_cb_data);
	}
	SercomUsart * const regs = cfg->regs;

	irq_unlock(key);
	regs->INTENSET.reg = SERCOM_USART_INTENSET_TXC;
}

static int uart_sam0_tx_halt(struct uart_sam0_dev_data *dev_data)
@@ -729,6 +712,31 @@ static void uart_sam0_isr(const struct device *dev)
	const struct uart_sam0_dev_cfg *const cfg = DEV_CFG(dev);
	SercomUsart * const regs = cfg->regs;

	if (dev_data->tx_len && regs->INTFLAG.bit.TXC) {
		regs->INTENCLR.reg = SERCOM_USART_INTENCLR_TXC;

		k_work_cancel_delayable(&dev_data->tx_timeout_work);

		int key = irq_lock();

		struct uart_event evt = {
			.type = UART_TX_DONE,
			.data.tx = {
				.buf = dev_data->tx_buf,
				.len = dev_data->tx_len,
			},
		};

		dev_data->tx_buf = NULL;
		dev_data->tx_len = 0U;

		if (evt.data.tx.len != 0U && dev_data->async_cb) {
			dev_data->async_cb(dev, &evt, dev_data->async_cb_data);
		}

		irq_unlock(key);
	}

	if (dev_data->rx_len && regs->INTFLAG.bit.RXC &&
	    dev_data->rx_waiting_for_irq) {
		dev_data->rx_waiting_for_irq = false;