/* * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2023 Nuvoton Technology Corporation. */ #define DT_DRV_COMPAT nuvoton_numaker_uart #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(numaker_uart, LOG_LEVEL_ERR); struct uart_numaker_config { UART_T *uart; const struct reset_dt_spec reset; uint32_t clk_modidx; uint32_t clk_src; uint32_t clk_div; const struct device *clk_dev; uint32_t irq_n; #ifdef CONFIG_UART_INTERRUPT_DRIVEN void (*irq_config_func)(const struct device *dev); #endif const struct pinctrl_dev_config *pincfg; }; struct uart_numaker_data { const struct device *clock; struct uart_config ucfg; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_user_data_t user_cb; void *user_data; #endif }; static int uart_numaker_poll_in(const struct device *dev, unsigned char *c) { const struct uart_numaker_config *config = dev->config; uint32_t count; count = UART_Read(config->uart, c, 1); if (!count) { return -1; } return 0; } static void uart_numaker_poll_out(const struct device *dev, unsigned char c) { const struct uart_numaker_config *config = dev->config; UART_Write(config->uart, &c, 1); } static int uart_numaker_err_check(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; uint32_t flags = uart->FIFOSTS; int err = 0; if (flags & UART_FIFOSTS_RXOVIF_Msk) { err |= UART_ERROR_OVERRUN; } if (flags & UART_FIFOSTS_PEF_Msk) { err |= UART_ERROR_PARITY; } if (flags & UART_FIFOSTS_FEF_Msk) { err |= UART_ERROR_FRAMING; } if (flags & UART_FIFOSTS_BIF_Msk) { err |= UART_BREAK; } if (flags & (UART_FIFOSTS_BIF_Msk | UART_FIFOSTS_FEF_Msk | UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk)) { uart->FIFOSTS = (UART_FIFOSTS_BIF_Msk | UART_FIFOSTS_FEF_Msk | UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk); } return err; } static inline int32_t uart_numaker_convert_stopbit(enum uart_config_stop_bits sb) { switch (sb) { case UART_CFG_STOP_BITS_1: return UART_STOP_BIT_1; case UART_CFG_STOP_BITS_1_5: return UART_STOP_BIT_1_5; case UART_CFG_STOP_BITS_2: return UART_STOP_BIT_2; default: return -ENOTSUP; } }; static inline int32_t uart_numaker_convert_datalen(enum uart_config_data_bits db) { switch (db) { case UART_CFG_DATA_BITS_5: return UART_WORD_LEN_5; case UART_CFG_DATA_BITS_6: return UART_WORD_LEN_6; case UART_CFG_DATA_BITS_7: return UART_WORD_LEN_7; case UART_CFG_DATA_BITS_8: return UART_WORD_LEN_8; default: return -ENOTSUP; } } static inline uint32_t uart_numaker_convert_parity(enum uart_config_parity parity) { switch (parity) { case UART_CFG_PARITY_ODD: return UART_PARITY_ODD; case UART_CFG_PARITY_EVEN: return UART_PARITY_EVEN; case UART_CFG_PARITY_MARK: return UART_PARITY_MARK; case UART_CFG_PARITY_SPACE: return UART_PARITY_SPACE; case UART_CFG_PARITY_NONE: default: return UART_PARITY_NONE; } } #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE static int uart_numaker_configure(const struct device *dev, const struct uart_config *cfg) { const struct uart_numaker_config *config = dev->config; struct uart_numaker_data *pData = dev->data; int32_t databits, stopbits; uint32_t parity; databits = uart_numaker_convert_datalen(cfg->data_bits); if (databits < 0) { return databits; } stopbits = uart_numaker_convert_stopbit(cfg->stop_bits); if (stopbits < 0) { return stopbits; } if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE) { UART_DisableFlowCtrl(config->uart); } else if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS) { UART_EnableFlowCtrl(config->uart); } else { return -ENOTSUP; } parity = uart_numaker_convert_parity(cfg->parity); UART_SetLineConfig(config->uart, cfg->baudrate, databits, parity, stopbits); memcpy(&pData->ucfg, cfg, sizeof(*cfg)); return 0; } static int uart_numaker_config_get(const struct device *dev, struct uart_config *cfg) { struct uart_numaker_data *pData = dev->data; memcpy(cfg, &pData->ucfg, sizeof(*cfg)); return 0; } #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ static int uart_numaker_init(const struct device *dev) { const struct uart_numaker_config *config = dev->config; struct uart_numaker_data *pData = dev->data; int err = 0; SYS_UnlockReg(); struct numaker_scc_subsys scc_subsys; memset(&scc_subsys, 0x00, sizeof(scc_subsys)); scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC; scc_subsys.pcc.clk_modidx = config->clk_modidx; scc_subsys.pcc.clk_src = config->clk_src; scc_subsys.pcc.clk_div = config->clk_div; /* Equivalent to CLK_EnableModuleClock(clk_modidx) */ err = clock_control_on(config->clk_dev, (clock_control_subsys_t)&scc_subsys); if (err != 0) { goto move_exit; } /* Equivalent to CLK_SetModuleClock(clk_modidx, clk_src, clk_div) */ err = clock_control_configure(config->clk_dev, (clock_control_subsys_t)&scc_subsys, NULL); if (err != 0) { goto move_exit; } /* * Set pinctrl for UART0 RXD and TXD * Set multi-function pins for UART0 RXD and TXD */ err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); if (err != 0) { goto move_exit; } /* Same as BSP's SYS_ResetModule(id_rst) */ if (!device_is_ready(config->reset.dev)) { LOG_ERR("reset controller not ready"); return -ENODEV; } /* Reset UART to default state */ reset_line_toggle_dt(&config->reset); UART_Open(config->uart, pData->ucfg.baudrate); #ifdef CONFIG_UART_INTERRUPT_DRIVEN config->irq_config_func(dev); #endif move_exit: SYS_LockReg(); return err; } #ifdef CONFIG_UART_INTERRUPT_DRIVEN static int uart_numaker_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; int tx_bytes = 0; /* Check TX FIFO not full, then fill */ while (((size - tx_bytes) > 0) && (!(uart->FIFOSTS & UART_FIFOSTS_TXFULL_Msk))) { /* Fill one byte into TX FIFO */ uart->DAT = tx_data[tx_bytes++]; } return tx_bytes; } static int uart_numaker_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; int rx_bytes = 0; /* Check RX FIFO not empty, then read */ while (((size - rx_bytes) > 0) && (!(uart->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk))) { /* Read one byte from UART RX FIFO */ rx_data[rx_bytes++] = (uint8_t)uart->DAT; } return rx_bytes; } static void uart_numaker_irq_tx_enable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_EnableInt(uart, UART_INTEN_THREIEN_Msk); } static void uart_numaker_irq_tx_disable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_DisableInt(uart, UART_INTEN_THREIEN_Msk); } static int uart_numaker_irq_tx_ready(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; return ((!UART_IS_TX_FULL(uart)) && (uart->INTEN & UART_INTEN_THREIEN_Msk)); } static int uart_numaker_irq_tx_complete(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; return (uart->INTSTS & UART_INTSTS_THREINT_Msk); } static void uart_numaker_irq_rx_enable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_EnableInt(uart, UART_INTEN_RDAIEN_Msk); } static void uart_numaker_irq_rx_disable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_DisableInt(uart, UART_INTEN_RDAIEN_Msk); } static int uart_numaker_irq_rx_ready(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; return ((!UART_GET_RX_EMPTY(uart)) && (uart->INTEN & UART_INTEN_RDAIEN_Msk)); } static void uart_numaker_irq_err_enable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_EnableInt(uart, UART_INTEN_BUFERRIEN_Msk | UART_INTEN_SWBEIEN_Msk); } static void uart_numaker_irq_err_disable(const struct device *dev) { const struct uart_numaker_config *config = dev->config; UART_T *uart = config->uart; UART_DisableInt(uart, UART_INTEN_BUFERRIEN_Msk | UART_INTEN_SWBEIEN_Msk); } static int uart_numaker_irq_is_pending(const struct device *dev) { return (uart_numaker_irq_tx_ready(dev) || (uart_numaker_irq_rx_ready(dev))); } static int uart_numaker_irq_update(const struct device *dev) { ARG_UNUSED(dev); /* nothing to be done */ return 1; } static void uart_numaker_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *cb_data) { struct uart_numaker_data *pData = dev->data; pData->user_cb = cb; pData->user_data = cb_data; } static void uart_numaker_isr(const struct device *dev) { struct uart_numaker_data *pData = dev->data; if (pData->user_cb) { pData->user_cb(dev, pData->user_data); } } #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static const struct uart_driver_api uart_numaker_driver_api = { .poll_in = uart_numaker_poll_in, .poll_out = uart_numaker_poll_out, .err_check = uart_numaker_err_check, #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE .configure = uart_numaker_configure, .config_get = uart_numaker_config_get, #endif #ifdef CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_numaker_fifo_fill, .fifo_read = uart_numaker_fifo_read, .irq_tx_enable = uart_numaker_irq_tx_enable, .irq_tx_disable = uart_numaker_irq_tx_disable, .irq_tx_ready = uart_numaker_irq_tx_ready, .irq_tx_complete = uart_numaker_irq_tx_complete, .irq_rx_enable = uart_numaker_irq_rx_enable, .irq_rx_disable = uart_numaker_irq_rx_disable, .irq_rx_ready = uart_numaker_irq_rx_ready, .irq_err_enable = uart_numaker_irq_err_enable, .irq_err_disable = uart_numaker_irq_err_disable, .irq_is_pending = uart_numaker_irq_is_pending, .irq_update = uart_numaker_irq_update, .irq_callback_set = uart_numaker_irq_callback_set, #endif }; #define CLOCK_CTRL_INIT(n) .clk_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(n))), #define PINCTRL_DEFINE(n) PINCTRL_DT_INST_DEFINE(n); #define PINCTRL_INIT(n) .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), #ifdef CONFIG_UART_INTERRUPT_DRIVEN #define NUMAKER_UART_IRQ_CONFIG_FUNC(n) \ static void uart_numaker_irq_config_##n(const struct device *dev) \ { \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uart_numaker_isr, \ DEVICE_DT_INST_GET(n), 0); \ irq_enable(DT_INST_IRQN(n)); \ } #define IRQ_FUNC_INIT(n) .irq_config_func = uart_numaker_irq_config_##n #else #define NUMAKER_UART_IRQ_CONFIG_FUNC(n) #define IRQ_FUNC_INIT(n) #endif #define NUMAKER_UART_INIT(inst) \ PINCTRL_DEFINE(inst) \ NUMAKER_UART_IRQ_CONFIG_FUNC(inst) \ \ static const struct uart_numaker_config uart_numaker_cfg_##inst = { \ .uart = (UART_T *)DT_INST_REG_ADDR(inst), \ .reset = RESET_DT_SPEC_INST_GET(inst), \ .clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \ .clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \ .clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \ CLOCK_CTRL_INIT(inst).irq_n = DT_INST_IRQN(inst), \ PINCTRL_INIT(inst) IRQ_FUNC_INIT(inst)}; \ \ static struct uart_numaker_data uart_numaker_data_##inst = { \ .ucfg = \ { \ .baudrate = DT_INST_PROP(inst, current_speed), \ }, \ }; \ \ DEVICE_DT_INST_DEFINE(inst, uart_numaker_init, NULL, &uart_numaker_data_##inst, \ &uart_numaker_cfg_##inst, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ &uart_numaker_driver_api); DT_INST_FOREACH_STATUS_OKAY(NUMAKER_UART_INIT)