| /* |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Copyright (c) 2020 Linumiz |
| * Author: Saravanan Sekar <saravanan@linumiz.com> |
| */ |
| |
| #include <drivers/uart.h> |
| #include <NuMicro.h> |
| #include <string.h> |
| |
| #define DT_DRV_COMPAT nuvoton_numicro_uart |
| |
| /* Device data structure */ |
| #define DEV_CFG(dev) \ |
| ((const struct uart_numicro_config * const)(dev)->config) |
| |
| #define DRV_DATA(dev) \ |
| ((struct uart_numicro_data * const)(dev)->data) |
| |
| #define UART_STRUCT(dev) \ |
| ((UART_T *)(DEV_CFG(dev))->devcfg.base) |
| |
| struct uart_numicro_config { |
| struct uart_device_config devcfg; |
| uint32_t id_rst; |
| uint32_t id_clk; |
| }; |
| |
| struct uart_numicro_data { |
| const struct device *clock; |
| struct uart_config ucfg; |
| }; |
| |
| static int uart_numicro_poll_in(const struct device *dev, unsigned char *c) |
| { |
| uint32_t count; |
| |
| count = UART_Read(UART_STRUCT(dev), c, 1); |
| if (!count) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void uart_numicro_poll_out(const struct device *dev, unsigned char c) |
| { |
| UART_Write(UART_STRUCT(dev), &c, 1); |
| } |
| |
| static int uart_numicro_err_check(const struct device *dev) |
| { |
| return 0; |
| } |
| |
| static inline int32_t uart_numicro_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_numicro_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_numicro_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_numicro_configure(const struct device *dev, |
| const struct uart_config *cfg) |
| { |
| struct uart_numicro_data *ddata = DRV_DATA(dev); |
| int32_t databits, stopbits; |
| uint32_t parity; |
| |
| databits = uart_numicro_convert_datalen(cfg->data_bits); |
| if (databits < 0) { |
| return databits; |
| } |
| |
| stopbits = uart_numicro_convert_stopbit(cfg->stop_bits); |
| if (stopbits < 0) { |
| return stopbits; |
| } |
| |
| if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE) { |
| UART_DisableFlowCtrl(UART_STRUCT(dev)); |
| } else if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS) { |
| UART_EnableFlowCtrl(UART_STRUCT(dev)); |
| } else { |
| return -ENOTSUP; |
| } |
| |
| parity = uart_numicro_convert_parity(cfg->parity); |
| |
| UART_SetLineConfig(UART_STRUCT(dev), cfg->baudrate, databits, |
| parity, stopbits); |
| |
| memcpy(&ddata->ucfg, cfg, sizeof(*cfg)); |
| |
| return 0; |
| } |
| |
| static int uart_numicro_config_get(const struct device *dev, |
| struct uart_config *cfg) |
| { |
| struct uart_numicro_data *ddata = DRV_DATA(dev); |
| |
| memcpy(cfg, &ddata->ucfg, sizeof(*cfg)); |
| |
| return 0; |
| } |
| #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
| |
| static int uart_numicro_init(const struct device *dev) |
| { |
| const struct uart_numicro_config *config = DEV_CFG(dev); |
| struct uart_numicro_data *ddata = DRV_DATA(dev); |
| |
| SYS_ResetModule(config->id_rst); |
| |
| SYS_UnlockReg(); |
| |
| /* Enable UART module clock */ |
| CLK_EnableModuleClock(config->id_clk); |
| |
| /* Select UART0 clock source is PLL */ |
| CLK_SetModuleClock(config->id_clk, CLK_CLKSEL1_UART0SEL_PLL, |
| CLK_CLKDIV0_UART0(0)); |
| |
| /* Set pinctrl for UART0 RXD and TXD */ |
| SYS->GPB_MFPH &= ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk); |
| SYS->GPB_MFPH |= (SYS_GPB_MFPH_PB12MFP_UART0_RXD | |
| SYS_GPB_MFPH_PB13MFP_UART0_TXD); |
| |
| SYS_LockReg(); |
| |
| UART_Open(UART_STRUCT(dev), ddata->ucfg.baudrate); |
| |
| return 0; |
| } |
| |
| static const struct uart_driver_api uart_numicro_driver_api = { |
| .poll_in = uart_numicro_poll_in, |
| .poll_out = uart_numicro_poll_out, |
| .err_check = uart_numicro_err_check, |
| #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
| .configure = uart_numicro_configure, |
| .config_get = uart_numicro_config_get, |
| #endif |
| }; |
| |
| #define NUMICRO_INIT(index) \ |
| \ |
| static const struct uart_numicro_config uart_numicro_cfg_##index = { \ |
| .devcfg = { \ |
| .base = (uint8_t *)DT_INST_REG_ADDR(index), \ |
| }, \ |
| .id_rst = UART##index##_RST, \ |
| .id_clk = UART##index##_MODULE, \ |
| }; \ |
| \ |
| static struct uart_numicro_data uart_numicro_data_##index = { \ |
| .ucfg = { \ |
| .baudrate = DT_INST_PROP(index, current_speed), \ |
| }, \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(index, \ |
| &uart_numicro_init, \ |
| NULL, \ |
| &uart_numicro_data_##index, \ |
| &uart_numicro_cfg_##index, \ |
| PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ |
| &uart_numicro_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(NUMICRO_INIT) |