drivers: serial: sf32lb: add uart interrupt support

add sf32lb52x uart interrupt support

Signed-off-by: Qingsong Gou <gouqs@hotmail.com>
diff --git a/drivers/serial/Kconfig.sf32lb b/drivers/serial/Kconfig.sf32lb
index 90721d5..bf4349c 100644
--- a/drivers/serial/Kconfig.sf32lb
+++ b/drivers/serial/Kconfig.sf32lb
@@ -6,6 +6,7 @@
 	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 839c8ff..bc3b365 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 <gouqs@hotmail.com>
  * SPDX-License-Identifier: Apache-2.0
  */
 
@@ -11,6 +12,7 @@
 #include <zephyr/drivers/clock_control/sf32lb.h>
 #include <zephyr/drivers/pinctrl.h>
 #include <zephyr/drivers/uart.h>
+#include <zephyr/irq.h>
 
 #include <register.h>
 
@@ -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 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 @@
 		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 @@
 						     ? 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)