From 008bfeb43e10acebcc70fdffe639ae167ae56bc6 Mon Sep 17 00:00:00 2001 From: Arvin Farahmand Date: Fri, 16 Apr 2021 22:03:29 -0400 Subject: [PATCH] drivers: uart_sam0: add error support Adds support for enabling and detecting errors to uart. Signed-off-by: Arvin Farahmand --- drivers/serial/uart_sam0.c | 100 +++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/drivers/serial/uart_sam0.c b/drivers/serial/uart_sam0.c index ea0accfb65e..4247d45f711 100644 --- a/drivers/serial/uart_sam0.c +++ b/drivers/serial/uart_sam0.c @@ -19,6 +19,14 @@ #define SERCOM_USART_CTRLA_MODE_USART_INT_CLK SERCOM_USART_CTRLA_MODE(0x1) #endif +/* + * Interrupt error flag is only supported in devices with + * SERCOM revision 0x500 + */ +#if defined(SERCOM_U2201) && (REV_SERCOM == 0x500) +#define SERCOM_REV500 +#endif + /* Device constant configuration parameters */ struct uart_sam0_dev_cfg { SercomUsart *regs; @@ -718,42 +726,42 @@ static int uart_sam0_fifo_fill(const struct device *dev, static void uart_sam0_irq_tx_enable(const struct device *dev) { - SercomUsart *regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; regs->INTENSET.reg = SERCOM_USART_INTENCLR_DRE; } static void uart_sam0_irq_tx_disable(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; regs->INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; } static int uart_sam0_irq_tx_ready(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; return regs->INTFLAG.bit.DRE != 0; } static void uart_sam0_irq_rx_enable(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; regs->INTENSET.reg = SERCOM_USART_INTENSET_RXC; } static void uart_sam0_irq_rx_disable(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; regs->INTENCLR.reg = SERCOM_USART_INTENCLR_RXC; } static int uart_sam0_irq_rx_ready(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; return regs->INTFLAG.bit.RXC != 0; } @@ -761,7 +769,7 @@ static int uart_sam0_irq_rx_ready(const struct device *dev) static int uart_sam0_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; if (regs->INTFLAG.bit.RXC) { uint8_t ch = regs->DATA.reg; @@ -778,12 +786,79 @@ static int uart_sam0_fifo_read(const struct device *dev, uint8_t *rx_data, static int uart_sam0_irq_is_pending(const struct device *dev) { - SercomUsart *const regs = DEV_CFG(dev)->regs; + SercomUsart * const regs = DEV_CFG(dev)->regs; return (regs->INTENSET.reg & regs->INTFLAG.reg) != 0; } -static int uart_sam0_irq_update(const struct device *dev) { return 1; } +#if defined(SERCOM_REV500) +static void uart_sam0_irq_err_enable(const struct device *dev) +{ + SercomUsart * const regs = DEV_CFG(dev)->regs; + + regs->INTENSET.reg |= SERCOM_USART_INTENCLR_ERROR; + wait_synchronization(regs); +} + +static void uart_sam0_irq_err_disable(const struct device *dev) +{ + SercomUsart * const regs = DEV_CFG(dev)->regs; + + regs->INTENCLR.reg |= SERCOM_USART_INTENSET_ERROR; + wait_synchronization(regs); +} +#endif + +static int uart_sam0_irq_update(const struct device *dev) +{ + /* Clear sticky interrupts */ + SercomUsart * const regs = DEV_CFG(dev)->regs; +#if defined(SERCOM_REV500) + regs->INTFLAG.reg |= SERCOM_USART_INTENCLR_ERROR + | SERCOM_USART_INTENCLR_RXBRK + | SERCOM_USART_INTENCLR_CTSIC + | SERCOM_USART_INTENCLR_RXS; +#else + regs->INTFLAG.reg |= SERCOM_USART_INTENCLR_RXS; +#endif + return 1; +} + +static int uart_sam0_err_check(const struct device *dev) +{ + SercomUsart * const regs = DEV_CFG(dev)->regs; + uint32_t err = 0U; + + if (regs->STATUS.reg & SERCOM_USART_STATUS_BUFOVF) { + err |= UART_ERROR_OVERRUN; + } + + if (regs->STATUS.reg & SERCOM_USART_STATUS_FERR) { + err |= UART_ERROR_PARITY; + } + + if (regs->STATUS.reg & SERCOM_USART_STATUS_PERR) { + err |= UART_ERROR_FRAMING; + } + +#if defined(SERCOM_REV500) + if (regs->STATUS.reg & SERCOM_USART_STATUS_ISF) { + err |= UART_BREAK; + } + + regs->STATUS.reg |= SERCOM_USART_STATUS_BUFOVF + | SERCOM_USART_STATUS_FERR + | SERCOM_USART_STATUS_PERR + | SERCOM_USART_STATUS_ISF; +#else + regs->STATUS.reg |= SERCOM_USART_STATUS_BUFOVF + | SERCOM_USART_STATUS_FERR + | SERCOM_USART_STATUS_PERR; +#endif + + wait_synchronization(regs); + return err; +} static void uart_sam0_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, @@ -962,7 +1037,7 @@ static int uart_sam0_rx_disable(const struct device *dev) { struct uart_sam0_dev_data *const dev_data = DEV_DATA(dev); const struct uart_sam0_dev_cfg *const cfg = DEV_CFG(dev); - SercomUsart *const regs = cfg->regs; + SercomUsart * const regs = cfg->regs; struct dma_status st; k_work_cancel_delayable(&dev_data->rx_timeout_work); @@ -1032,6 +1107,7 @@ static const struct uart_driver_api uart_sam0_driver_api = { .poll_out = uart_sam0_poll_out, .configure = uart_sam0_configure, .config_get = uart_sam0_config_get, + .err_check = uart_sam0_err_check, #if CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_sam0_fifo_fill, .fifo_read = uart_sam0_fifo_read, @@ -1042,6 +1118,10 @@ static const struct uart_driver_api uart_sam0_driver_api = { .irq_rx_disable = uart_sam0_irq_rx_disable, .irq_rx_ready = uart_sam0_irq_rx_ready, .irq_is_pending = uart_sam0_irq_is_pending, +#if defined(SERCOM_REV500) + .irq_err_enable = uart_sam0_irq_err_enable, + .irq_err_disable = uart_sam0_irq_err_disable, +#endif .irq_update = uart_sam0_irq_update, .irq_callback_set = uart_sam0_irq_callback_set, #endif