From 9f22c8951bfc751f159c4fb335fcdaeba9dfa680 Mon Sep 17 00:00:00 2001 From: Steven Chang Date: Fri, 15 Mar 2024 10:46:28 +0800 Subject: [PATCH] drivers: uart: initial device driver for ENE KB1200 Add uart driver for ENE KB1200 Signed-off-by: Steven Chang --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.ene | 12 + drivers/serial/uart_ene_kb1200.c | 380 +++++++++++++++++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 drivers/serial/Kconfig.ene create mode 100644 drivers/serial/uart_ene_kb1200.c diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 1648f3a7081..4480e676234 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -73,6 +73,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_SEDI uart_sedi.c) zephyr_library_sources_ifdef(CONFIG_UART_BCM2711_MU uart_bcm2711.c) zephyr_library_sources_ifdef(CONFIG_UART_INTEL_LW uart_intel_lw.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RA uart_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_UART_ENE_KB1200 uart_ene_kb1200.c) zephyr_library_sources_ifdef(CONFIG_UART_RZT2M uart_rzt2m.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index a3041b2cae6..86b9114b33b 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -270,6 +270,8 @@ source "drivers/serial/Kconfig.intel_lw" source "drivers/serial/Kconfig.renesas_ra" +source "drivers/serial/Kconfig.ene" + source "drivers/serial/Kconfig.rzt2m" endif # SERIAL diff --git a/drivers/serial/Kconfig.ene b/drivers/serial/Kconfig.ene new file mode 100644 index 00000000000..dd2b246f3d8 --- /dev/null +++ b/drivers/serial/Kconfig.ene @@ -0,0 +1,12 @@ +# Copyright (c) 2023 ENE Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +config UART_ENE_KB1200 + bool "ENE KB1200 serial driver" + default y + depends on DT_HAS_ENE_KB1200_UART_ENABLED + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + select PINCTRL + help + This option enables the KB1200 serial driver. diff --git a/drivers/serial/uart_ene_kb1200.c b/drivers/serial/uart_ene_kb1200.c new file mode 100644 index 00000000000..00105bd1783 --- /dev/null +++ b/drivers/serial/uart_ene_kb1200.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2023 ENE Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ene_kb1200_uart + +#include +#include +#include +#include + +struct kb1200_uart_config { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + void (*irq_cfg_func)(void); +#endif + struct serial_regs *ser; + const struct pinctrl_dev_config *pcfg; +}; + +struct kb1200_uart_data { + uart_irq_callback_user_data_t callback; + struct uart_config current_config; + void *callback_data; + uint8_t pending_flag_data; +}; + +static int kb1200_uart_err_check(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + int err = 0; + + if (config->ser->SERSTS & SERSTS_RX_OVERRUN) { + err |= UART_ERROR_OVERRUN; + } + if (config->ser->SERSTS & SERSTS_PARITY_ERROR) { + err |= UART_ERROR_PARITY; + } + if (config->ser->SERSTS & SERSTS_FRAME_ERROR) { + err |= UART_ERROR_FRAMING; + } + return err; +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE +static int kb1200_uart_configure(const struct device *dev, const struct uart_config *cfg) +{ + uint16_t reg_baudrate = 0; + uint8_t reg_parity = 0; + int ret = 0; + const struct kb1200_uart_config *config = dev->config; + struct kb1200_uart_data *data = dev->data; + + reg_baudrate = (DIVIDER_BASE_CLK / cfg->baudrate) - 1; + + switch (cfg->parity) { + case UART_CFG_PARITY_NONE: + reg_parity = SERCFG_PARITY_NONE; + break; + case UART_CFG_PARITY_ODD: + reg_parity = SERCFG_PARITY_ODD; + break; + case UART_CFG_PARITY_EVEN: + reg_parity = SERCFG_PARITY_EVEN; + break; + case UART_CFG_PARITY_MARK: + case UART_CFG_PARITY_SPACE: + default: + ret = -ENOTSUP; + break; + } + + switch (cfg->stop_bits) { + case UART_CFG_STOP_BITS_1: + break; + case UART_CFG_STOP_BITS_0_5: + case UART_CFG_STOP_BITS_1_5: + case UART_CFG_STOP_BITS_2: + default: + ret = -ENOTSUP; + break; + } + + switch (cfg->data_bits) { + case UART_CFG_DATA_BITS_8: + break; + case UART_CFG_DATA_BITS_5: + case UART_CFG_DATA_BITS_6: + case UART_CFG_DATA_BITS_7: + case UART_CFG_DATA_BITS_9: + default: + ret = -ENOTSUP; + break; + } + + switch (cfg->flow_ctrl) { + case UART_CFG_FLOW_CTRL_NONE: + break; + case UART_CFG_FLOW_CTRL_RTS_CTS: + case UART_CFG_FLOW_CTRL_DTR_DSR: + case UART_CFG_FLOW_CTRL_RS485: + default: + ret = -ENOTSUP; + break; + } + config->ser->SERCFG = + (reg_baudrate << 16) | (reg_parity << 2) | (SERIE_RX_ENABLE | SERIE_TX_ENABLE); + config->ser->SERCTRL = SERCTRL_MODE1; + data->current_config = *cfg; + return ret; +} + +static int kb1200_uart_config_get(const struct device *dev, struct uart_config *cfg) +{ + struct kb1200_uart_data *data = dev->data; + + *cfg = data->current_config; + return 0; +} +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int kb1200_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) +{ + const struct kb1200_uart_config *config = dev->config; + uint16_t tx_bytes = 0U; + + while ((size - tx_bytes) > 0) { + /* Check Tx FIFO not Full*/ + while (config->ser->SERSTS & SERSTS_TX_FULL) + ; + /* Put a character into Tx FIFO */ + config->ser->SERTBUF = tx_data[tx_bytes]; + tx_bytes++; + } + return tx_bytes; +} + +static int kb1200_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + const struct kb1200_uart_config *config = dev->config; + uint16_t rx_bytes = 0U; + + /* Check Rx FIFO not Empty*/ + while ((size - rx_bytes > 0) && (!(config->ser->SERSTS & SERSTS_RX_EMPTY))) { + /* Put a character into Tx FIFO */ + rx_data[rx_bytes] = config->ser->SERRBUF; + rx_bytes++; + } + return rx_bytes; +} + +static void kb1200_uart_irq_tx_enable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERPF = SERPF_TX_EMPTY; + config->ser->SERIE |= SERIE_TX_ENABLE; +} + +static void kb1200_uart_irq_tx_disable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERIE &= ~SERIE_TX_ENABLE; + config->ser->SERPF = SERPF_TX_EMPTY; +} + +static int kb1200_uart_irq_tx_ready(const struct device *dev) +{ + struct kb1200_uart_data *data = dev->data; + + return (data->pending_flag_data & SERPF_TX_EMPTY) ? 1 : 0; +} + +static void kb1200_uart_irq_rx_enable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERPF = SERPF_RX_CNT_FULL; + config->ser->SERIE |= SERIE_RX_ENABLE; +} + +static void kb1200_uart_irq_rx_disable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERIE &= (~SERIE_RX_ENABLE); + config->ser->SERPF = SERPF_RX_CNT_FULL; +} + +static int kb1200_uart_irq_rx_ready(const struct device *dev) +{ + struct kb1200_uart_data *data = dev->data; + + return (data->pending_flag_data & SERPF_RX_CNT_FULL) ? 1 : 0; +} + +static void kb1200_uart_irq_err_enable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERPF = SERPF_RX_ERROR; + config->ser->SERIE |= SERIE_RX_ERROR; +} + +static void kb1200_uart_irq_err_disable(const struct device *dev) +{ + const struct kb1200_uart_config *config = dev->config; + + config->ser->SERIE &= (~SERIE_RX_ERROR); + config->ser->SERPF = SERPF_RX_ERROR; +} + +static int kb1200_uart_irq_is_pending(const struct device *dev) +{ + struct kb1200_uart_data *data = dev->data; + + return (data->pending_flag_data) ? 1 : 0; +} + +static int kb1200_uart_irq_update(const struct device *dev) +{ + struct kb1200_uart_data *data = dev->data; + const struct kb1200_uart_config *config = dev->config; + + data->pending_flag_data = (config->ser->SERPF) & (config->ser->SERIE); + /*clear pending flag*/ + config->ser->SERPF = data->pending_flag_data; + return 1; +} + +static void kb1200_uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct kb1200_uart_data *data = dev->data; + + data->callback = cb; + data->callback_data = cb_data; +} + +static void kb1200_uart_irq_handler(const struct device *dev) +{ + struct kb1200_uart_data *data = dev->data; + + if (data->callback) { + data->callback(dev, data->callback_data); + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static int kb1200_uart_poll_in(const struct device *dev, unsigned char *c) +{ +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + return kb1200_uart_fifo_read(dev, c, 1) ? 0 : -1; +#else + const struct kb1200_uart_config *config = dev->config; + + /* Check Rx FIFO not Empty*/ + if (config->ser->SERSTS & SERSTS_RX_EMPTY) { + return -1; + } + /* Put a character into Tx FIFO */ + *c = config->ser->SERRBUF; + return 0; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +} + +static void kb1200_uart_poll_out(const struct device *dev, unsigned char c) +{ +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + kb1200_uart_fifo_fill(dev, &c, 1); +#else + const struct kb1200_uart_config *config = dev->config; + + /* Wait Tx FIFO not Full*/ + while (config->ser->SERSTS & SER_TxFull) { + ; + } + /* Put a character into Tx FIFO */ + config->ser->SERTBUF = c; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +} + +static const struct uart_driver_api kb1200_uart_api = { + .poll_in = kb1200_uart_poll_in, + .poll_out = kb1200_uart_poll_out, + .err_check = kb1200_uart_err_check, +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = kb1200_uart_configure, + .config_get = kb1200_uart_config_get, +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = kb1200_uart_fifo_fill, + .fifo_read = kb1200_uart_fifo_read, + .irq_tx_enable = kb1200_uart_irq_tx_enable, + .irq_tx_disable = kb1200_uart_irq_tx_disable, + .irq_tx_ready = kb1200_uart_irq_tx_ready, + .irq_rx_enable = kb1200_uart_irq_rx_enable, + .irq_rx_disable = kb1200_uart_irq_rx_disable, + .irq_rx_ready = kb1200_uart_irq_rx_ready, + .irq_err_enable = kb1200_uart_irq_err_enable, + .irq_err_disable = kb1200_uart_irq_err_disable, + .irq_is_pending = kb1200_uart_irq_is_pending, + .irq_update = kb1200_uart_irq_update, + .irq_callback_set = kb1200_uart_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +/* GPIO module instances */ +#define KB1200_UART_DEV(inst) DEVICE_DT_INST_GET(inst), +static const struct device *const uart_devices[] = {DT_INST_FOREACH_STATUS_OKAY(KB1200_UART_DEV)}; +static void kb1200_uart_isr_wrap(const struct device *dev) +{ + for (size_t i = 0; i < ARRAY_SIZE(uart_devices); i++) { + const struct device *dev_ = uart_devices[i]; + const struct kb1200_uart_config *config = dev_->config; + + if (config->ser->SERIE & config->ser->SERPF) { + kb1200_uart_irq_handler(dev_); + } + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static int kb1200_uart_init(const struct device *dev) +{ + int ret; + const struct kb1200_uart_config *config = dev->config; + struct kb1200_uart_data *data = dev->data; + + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret != 0) { + return ret; + } + + kb1200_uart_configure(dev, &data->current_config); +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + config->irq_cfg_func(); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static bool init_irq = true; +static void kb1200_uart_irq_init(void) +{ + if (init_irq) { + init_irq = false; + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), kb1200_uart_isr_wrap, NULL, + 0); + irq_enable(DT_INST_IRQN(0)); + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#define KB1200_UART_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static struct kb1200_uart_data kb1200_uart_data_##n = { \ + .current_config = { \ + .baudrate = DT_INST_PROP(n, current_speed), \ + .parity = UART_CFG_PARITY_NONE, \ + .stop_bits = UART_CFG_STOP_BITS_1, \ + .data_bits = UART_CFG_DATA_BITS_8, \ + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \ + }, \ + }; \ + static const struct kb1200_uart_config kb1200_uart_config_##n = { \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, (.irq_cfg_func = kb1200_uart_irq_init,)) \ + .ser = (struct serial_regs *)DT_INST_REG_ADDR(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n)}; \ + DEVICE_DT_INST_DEFINE(n, &kb1200_uart_init, NULL, &kb1200_uart_data_##n, \ + &kb1200_uart_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ + &kb1200_uart_api); + +DT_INST_FOREACH_STATUS_OKAY(KB1200_UART_INIT)