diff --git a/drivers/serial/Kconfig.sf32lb b/drivers/serial/Kconfig.sf32lb index 90721d53e0b4b..bf4349c9cda88 100644 --- a/drivers/serial/Kconfig.sf32lb +++ b/drivers/serial/Kconfig.sf32lb @@ -6,6 +6,7 @@ config UART_SF32LB default y depends on DT_HAS_SIFLI_SF32LB_USART_ENABLED select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT select PINCTRL select CLOCK_CONTROL help diff --git a/drivers/serial/uart_sf32lb.c b/drivers/serial/uart_sf32lb.c index 839c8fff596b1..bc3b36535866b 100644 --- a/drivers/serial/uart_sf32lb.c +++ b/drivers/serial/uart_sf32lb.c @@ -1,5 +1,6 @@ /* * Copyright (c) Core Devices LLC + * Copyright (c) Qingsong Gou * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +12,7 @@ #include #include #include +#include #include @@ -35,13 +37,37 @@ /* minimal BRR: INT=1, FRAC=0 (0x10) */ #define UART_BRR_MIN 0x10U +struct uart_sf32lb_data { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t irq_callback; + void *cb_data; +#endif +}; + struct uart_sf32lb_config { uintptr_t base; const struct pinctrl_dev_config *pcfg; struct sf32lb_clock_dt_spec clock; struct uart_config uart_cfg; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + void (*irq_config_func)(const struct device *dev); +#endif }; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void uart_sf32lb_isr(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + struct uart_sf32lb_data *data = dev->data; + + if (data->irq_callback) { + data->irq_callback(dev, data->cb_data); + } + + sys_write32(USART_ISR_TXE | USART_ICR_TCCF | USART_ISR_RXNE, config->base + UART_ICR); +} +#endif + static int uart_sf32lb_configure(const struct device *dev, const struct uart_config *cfg) { const struct uart_sf32lb_config *config = dev->config; @@ -177,9 +203,174 @@ static void uart_sf32lb_poll_out(const struct device *dev, uint8_t c) } } +static int uart_sf32lb_err_check(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + uint32_t isr = sys_read32(config->base + UART_ISR); + int err = 0; + + if (isr & USART_ISR_ORE) { + err |= UART_ERROR_OVERRUN; + } + + if (isr & USART_ISR_PE) { + err |= UART_ERROR_PARITY; + } + + if (isr & USART_ISR_FE) { + err |= UART_ERROR_FRAMING; + } + + if (isr & USART_ISR_NF) { + err |= UART_ERROR_NOISE; + } + + /* clear error flags */ + sys_write32(USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_FECF | USART_ICR_NCF, + config->base + UART_ICR); + + return err; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int uart_sf32lb_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len) +{ + const struct uart_sf32lb_config *config = dev->config; + int i; + + for (i = 0; i < len; i++) { + if (!sys_test_bit(config->base + UART_ISR, USART_ISR_TXE_Pos)) { + break; + } + sys_write8(tx_data[i], config->base + UART_TDR); + } + + return i; +} + +static int uart_sf32lb_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + const struct uart_sf32lb_config *config = dev->config; + int i; + + for (i = 0; i < size; i++) { + if (!sys_test_bit(config->base + UART_ISR, USART_ISR_RXNE_Pos)) { + break; + } + rx_data[i] = sys_read8(config->base + UART_RDR); + } + + return i; +} + +static void uart_sf32lb_irq_tx_enable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_set_bit(config->base + UART_CR1, USART_CR1_TXEIE_Pos); +} + +static void uart_sf32lb_irq_tx_disable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_clear_bit(config->base + UART_CR1, USART_CR1_TXEIE_Pos); +} + +static int uart_sf32lb_irq_tx_ready(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + return sys_test_bit(config->base + UART_ISR, USART_ISR_TXE_Pos); +} + +static int uart_sf32lb_irq_tx_complete(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + return sys_test_bit(config->base + UART_ISR, USART_ISR_TC_Pos); +} + +static int uart_sf32lb_irq_rx_ready(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + return sys_test_bit(config->base + UART_ISR, USART_ISR_RXNE_Pos); +} + +static void uart_sf32lb_irq_err_enable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_set_bit(config->base + UART_CR1, USART_CR1_PEIE_Pos); + sys_set_bit(config->base + UART_CR3, USART_CR3_EIE_Pos); +} + +static void uart_sf32lb_irq_err_disable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_clear_bit(config->base + UART_CR1, USART_CR1_PEIE_Pos); + sys_clear_bit(config->base + UART_CR3, USART_CR3_EIE_Pos); +} + +static int uart_sf32lb_irq_is_pending(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + return sys_read32(config->base + UART_ISR) == 0U ? 0 : 1; +} + +static int uart_sf32lb_irq_update(const struct device *dev) +{ + ARG_UNUSED(dev); + return 1; +} + +static void uart_sf32lb_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *user_data) +{ + struct uart_sf32lb_data *data = dev->data; + + data->irq_callback = cb; + data->cb_data = user_data; +} + +static void uart_sf32lb_irq_rx_enable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_set_bit(config->base + UART_CR1, USART_CR1_RXNEIE_Pos); +} + +static void uart_sf32lb_irq_rx_disable(const struct device *dev) +{ + const struct uart_sf32lb_config *config = dev->config; + + sys_clear_bit(config->base + UART_CR1, USART_CR1_RXNEIE_Pos); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + static const struct uart_driver_api uart_sf32lb_api = { .poll_in = uart_sf32lb_poll_in, .poll_out = uart_sf32lb_poll_out, + .err_check = uart_sf32lb_err_check, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_sf32lb_fifo_fill, + .fifo_read = uart_sf32lb_fifo_read, + .irq_tx_enable = uart_sf32lb_irq_tx_enable, + .irq_tx_disable = uart_sf32lb_irq_tx_disable, + .irq_tx_complete = uart_sf32lb_irq_tx_complete, + .irq_tx_ready = uart_sf32lb_irq_tx_ready, + .irq_rx_enable = uart_sf32lb_irq_rx_enable, + .irq_rx_disable = uart_sf32lb_irq_rx_disable, + .irq_rx_ready = uart_sf32lb_irq_rx_ready, + .irq_err_enable = uart_sf32lb_irq_err_enable, + .irq_err_disable = uart_sf32lb_irq_err_disable, + .irq_is_pending = uart_sf32lb_irq_is_pending, + .irq_update = uart_sf32lb_irq_update, + .irq_callback_set = uart_sf32lb_irq_callback_set, +#endif }; static int uart_sf32lb_init(const struct device *dev) @@ -208,11 +399,22 @@ static int uart_sf32lb_init(const struct device *dev) return ret; } +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + config->irq_config_func(dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + return 0; } #define SF32LB_UART_DEFINE(index) \ PINCTRL_DT_INST_DEFINE(index); \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \ + (static void uart_sf32lb_irq_config_func_##index(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), uart_sf32lb_isr, \ + DEVICE_DT_INST_GET(index), 0); \ + irq_enable(DT_INST_IRQN(index)); \ + })); \ \ static const struct uart_sf32lb_config uart_sf32lb_cfg_##index = { \ .base = DT_INST_REG_ADDR(index), \ @@ -231,9 +433,14 @@ static int uart_sf32lb_init(const struct device *dev) ? UART_CFG_FLOW_CTRL_RTS_CTS \ : UART_CFG_FLOW_CTRL_NONE, \ }, \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \ + (.irq_config_func = uart_sf32lb_irq_config_func_##index,)) \ }; \ \ - DEVICE_DT_INST_DEFINE(index, uart_sf32lb_init, NULL, NULL, &uart_sf32lb_cfg_##index, \ - PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_sf32lb_api); + static struct uart_sf32lb_data uart_sf32lb_data_##index; \ + \ + DEVICE_DT_INST_DEFINE(index, uart_sf32lb_init, NULL, \ + &uart_sf32lb_data_##index, &uart_sf32lb_cfg_##index, PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, &uart_sf32lb_api); DT_INST_FOREACH_STATUS_OKAY(SF32LB_UART_DEFINE)