diff --git a/CODEOWNERS b/CODEOWNERS index c046100925c..51d77e0cb52 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -291,6 +291,7 @@ /drivers/sensor/lsm*/ @avisconti /drivers/sensor/mpr/ @sven-hm /drivers/sensor/st*/ @avisconti +/drivers/serial/*b91* @yurvyn /drivers/serial/uart_altera_jtag_hal.c @nashif /drivers/serial/*ns16550* @dcpleung @nashif @jenmwms @aasthagr /drivers/serial/*nrfx* @Mierunski @anangl diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 03cd54d84ff..c56b5c2fa58 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_UART_ALTERA_JTAG uart_altera_jtag_hal.c) +zephyr_library_sources_ifdef(CONFIG_UART_TELINK_B91 uart_b91.c) zephyr_library_sources_ifdef(CONFIG_UART_IMX uart_imx.c) zephyr_library_sources_ifdef(CONFIG_UART_CC13XX_CC26XX uart_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_UART_CC32XX uart_cc32xx.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 637c55f02b3..999f282dbb5 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -79,6 +79,8 @@ config UART_DRV_CMD comment "Serial Drivers" +source "drivers/serial/Kconfig.b91" + source "drivers/serial/Kconfig.ns16550" source "drivers/serial/Kconfig.mcux" diff --git a/drivers/serial/Kconfig.b91 b/drivers/serial/Kconfig.b91 new file mode 100644 index 00000000000..298aa2be6fc --- /dev/null +++ b/drivers/serial/Kconfig.b91 @@ -0,0 +1,12 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Telink B91 UART configuration options + +config UART_TELINK_B91 + bool "Telink B91 UART driver" + depends on SOC_RISCV_TELINK_B91 + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + help + This option enables the B91 serial driver. diff --git a/drivers/serial/uart_b91.c b/drivers/serial/uart_b91.c new file mode 100644 index 00000000000..82b45f53b3c --- /dev/null +++ b/drivers/serial/uart_b91.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "analog.h" +#include "clock.h" + +#include +#include +#include +#include + + +/* Driver dts compatibility: telink,b91_uart */ +#define DT_DRV_COMPAT telink_b91_uart + +/* Get UART instance */ +#define GET_UART(dev) ((volatile struct uart_b91_t *) \ + ((const struct uart_b91_config *)dev->config)->uart_addr) + +/* Get UART configuration */ +#define GET_CFG(dev) ((const struct uart_b91_config *)dev->config) + +/* Get instance data */ +#define DEV_DATA(dev) ((struct uart_b91_data *const)dev->data) + +/* UART TX buffer count max value */ +#define UART_TX_BUF_CNT ((uint8_t)8u) + +/* Parity type */ +#define UART_PARITY_NONE ((uint8_t)0u) +#define UART_PARITY_EVEN ((uint8_t)1u) +#define UART_PARITY_ODD ((uint8_t)2u) + +/* Stop bits length */ +#define UART_STOP_BIT_1 ((uint8_t)0u) +#define UART_STOP_BIT_1P5 BIT(4) +#define UART_STOP_BIT_2 BIT(5) + + +/* B91 UART registers structure */ +struct uart_b91_t { + uint8_t data_buf[4]; + uint16_t clk_div; + uint8_t ctrl0; + uint8_t ctrl1; + uint8_t ctrl2; + uint8_t ctrl3; + uint16_t rxtimeout; + uint8_t bufcnt; + uint8_t status; + uint8_t txrx_status; + uint8_t state; +}; + +/* B91 UART data structure */ +struct uart_b91_data { + uint8_t tx_byte_index; + uint8_t rx_byte_index; + struct uart_config cfg; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t callback; + void *cb_data; +#endif +}; + +/* B91 UART config structure */ +struct uart_b91_config { + const uint32_t *pinctrl_list; + size_t pinctrl_list_size; + uint32_t uart_addr; + uint32_t baud_rate; + void (*pirq_connect)(void); +}; + +/* rxtimeout register enums */ +enum { + UART_ERR_IRQ_MASK = BIT(15), +}; + +/* ctrl0 register enums */ +enum { + UART_RX_IRQ_MASK = BIT(6), + UART_TX_IRQ_MASK = BIT(7), +}; + +/* ctrl3 register enums */ +enum { + FLD_UART_RX_IRQ_TRIQ_LEV_OFFSET = 0, + FLD_UART_TX_IRQ_TRIQ_LEV_OFFSET = 4, +}; + +/* bufcnt register enums */ +enum { + FLD_UART_RX_BUF_CNT_OFFSET = 0, + FLD_UART_TX_BUF_CNT_OFFSET = 4, +}; + +/* status register enums */ +enum { + UART_IRQ_STATUS = BIT(3), + UART_RX_ERR_STATUS = BIT(7), +}; + + +/* Get tx fifo count */ +static inline uint8_t uart_b91_get_tx_bufcnt(volatile struct uart_b91_t *uart) +{ + return (uart->bufcnt & FLD_UART_TX_BUF_CNT) >> FLD_UART_TX_BUF_CNT_OFFSET; +} + +/* Get rx fifo count */ +static inline uint8_t uart_b91_get_rx_bufcnt(volatile struct uart_b91_t *uart) +{ + return (uart->bufcnt & FLD_UART_RX_BUF_CNT) >> FLD_UART_RX_BUF_CNT_OFFSET; +} + +/* Check for prime */ +static uint8_t uart_b91_is_prime(uint32_t n) +{ + uint32_t i = 5; + + if (n <= 3) { + return 1; + } else if ((n % 2 == 0) || (n % 3 == 0)) { + return 0; + } + + for (i = 5; i * i < n; i += 6) { + if ((n % i == 0) || (n % (i + 2)) == 0) { + return 0; + } + } + + return 1; +} + +/* Calculate the best bit width */ +static void uart_b91_cal_div_and_bwpc(uint32_t baudrate, uint32_t pclk, + uint16_t *divider, uint8_t *bwpc) +{ + uint8_t i = 0, j = 0; + uint32_t primeInt = 0; + uint8_t primeDec = 0; + uint32_t D_intdec[13], D_int[13]; + uint8_t D_dec[13]; + + primeInt = pclk / baudrate; + primeDec = 10 * pclk / baudrate - 10 * primeInt; + + if (uart_b91_is_prime(primeInt)) { + primeInt += 1; + } else if (primeDec > 5) { + primeInt += 1; + if (uart_b91_is_prime(primeInt)) { + primeInt -= 1; + } + } + + for (i = 3; i <= 15; i++) { + D_intdec[i - 3] = (10 * primeInt) / (i + 1); + D_dec[i - 3] = D_intdec[i - 3] - 10 * (D_intdec[i - 3] / 10); + D_int[i - 3] = D_intdec[i - 3] / 10; + } + + /* find the max and min one decimation point */ + uint8_t position_min = 0, position_max = 0; + uint32_t min = 0xffffffff, max = 0x00; + + for (j = 0; j < 13; j++) { + if ((D_dec[j] <= min) && (D_int[j] != 0x01)) { + min = D_dec[j]; + position_min = j; + } + if (D_dec[j] >= max) { + max = D_dec[j]; + position_max = j; + } + } + + if ((D_dec[position_min] < 5) && (D_dec[position_max] >= 5)) { + if (D_dec[position_min] < (10 - D_dec[position_max])) { + *bwpc = position_min + 3; + *divider = D_int[position_min] - 1; + } else { + *bwpc = position_max + 3; + *divider = D_int[position_max]; + } + } else if ((D_dec[position_min] < 5) && (D_dec[position_max] < 5)) { + *bwpc = position_min + 3; + *divider = D_int[position_min] - 1; + } else { + *bwpc = position_max + 3; + *divider = D_int[position_max]; + } +} + +/* Initializes the UART instance */ +static void uart_b91_init(volatile struct uart_b91_t *uart, uint16_t divider, + uint8_t bwpc, uint8_t parity, uint8_t stop_bit) +{ + /* config clock */ + divider = divider | FLD_UART_CLK_DIV_EN; + uart->ctrl0 = bwpc; + uart->clk_div = divider; + + /* config parity */ + if (parity) { + /* enable parity function */ + uart->ctrl1 |= FLD_UART_PARITY_ENABLE; + + if (parity == UART_PARITY_EVEN) { + /* enable even parity */ + uart->ctrl1 &= (~FLD_UART_PARITY_POLARITY); + } else if (parity == UART_PARITY_ODD) { + /* enable odd parity */ + uart->ctrl1 |= FLD_UART_PARITY_POLARITY; + } + } else { + uart->ctrl1 &= (~FLD_UART_PARITY_ENABLE); /* disable parity function */ + } + + /* stop bit config */ + uart->ctrl1 &= (~FLD_UART_STOP_SEL); + uart->ctrl1 |= stop_bit; +} + +/* API implementation: irq handler */ +static void uart_b91_irq_handler(const struct device *dev) +{ +#ifndef CONFIG_UART_INTERRUPT_DRIVEN + ARG_UNUSED(dev); +#else + struct uart_b91_data *data = DEV_DATA(dev); + + if (data->callback != NULL) { + data->callback(dev, data->cb_data); + } +#endif +} + +/* API implementation: configure */ +static int uart_b91_configure(const struct device *dev, + const struct uart_config *cfg) +{ + uint16_t divider; + uint8_t bwpc; + uint8_t parity; + uint8_t stop_bits; + + volatile struct uart_b91_t *uart = GET_UART(dev); + + /* check parity */ + if (cfg->parity == UART_CFG_PARITY_NONE) { + parity = UART_PARITY_NONE; + } else if (cfg->parity == UART_CFG_PARITY_ODD) { + parity = UART_PARITY_ODD; + } else if (cfg->parity == UART_CFG_PARITY_EVEN) { + parity = UART_PARITY_EVEN; + } else { + return -ENOTSUP; + } + + /* check stop bits */ + if (cfg->stop_bits == UART_CFG_STOP_BITS_1) { + stop_bits = UART_STOP_BIT_1; + } else if (cfg->stop_bits == UART_CFG_STOP_BITS_1_5) { + stop_bits = UART_STOP_BIT_1P5; + } else if (cfg->stop_bits == UART_CFG_STOP_BITS_2) { + stop_bits = UART_STOP_BIT_2; + } else { + return -ENOTSUP; + } + + /* check flow control */ + if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + + /* UART configure */ + uart_b91_cal_div_and_bwpc(cfg->baudrate, sys_clk.pclk * 1000 * 1000, ÷r, &bwpc); + uart_b91_init(uart, divider, bwpc, parity, stop_bits); + + /* save configuration */ + DEV_DATA(dev)->cfg = *cfg; + + return 0; +} + +/* API implementation: config_get */ +static int uart_b91_config_get(const struct device *dev, + struct uart_config *cfg) +{ + *cfg = DEV_DATA(dev)->cfg; + + return 0; +} + +/* API implementation: driver initialization */ +static int uart_b91_driver_init(const struct device *dev) +{ + uint16_t divider = 0u; + uint8_t bwpc = 0u; + const struct device *pinmux; + volatile struct uart_b91_t *uart = GET_UART(dev); + const struct uart_b91_config *cfg = GET_CFG(dev); + + pinmux = DEVICE_DT_GET(DT_NODELABEL(pinmux)); + if (!device_is_ready(pinmux)) { + return -ENODEV; + } + + for (int i = 0; i < cfg->pinctrl_list_size; i++) { + pinmux_pin_set(pinmux, B91_PINMUX_GET_PIN(cfg->pinctrl_list[i]), + B91_PINMUX_GET_FUNC(cfg->pinctrl_list[i])); + } + + uart_b91_cal_div_and_bwpc(cfg->baud_rate, sys_clk.pclk * 1000 * 1000, ÷r, &bwpc); + uart_b91_init(uart, divider, bwpc, UART_PARITY_NONE, UART_STOP_BIT_1); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + cfg->pirq_connect(); +#endif + + return 0; +} + +/* API implementation: poll_out */ +static void uart_b91_poll_out(const struct device *dev, uint8_t c) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + struct uart_b91_data *data = DEV_DATA(dev); + + while (uart_b91_get_tx_bufcnt(uart) >= UART_TX_BUF_CNT) { + }; + + uart->data_buf[data->tx_byte_index] = c; + data->tx_byte_index = (data->tx_byte_index + 1) % ARRAY_SIZE(uart->data_buf); +} + +/* API implementation: poll_in */ +static int uart_b91_poll_in(const struct device *dev, unsigned char *c) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + struct uart_b91_data *data = DEV_DATA(dev); + + if (uart_b91_get_rx_bufcnt(uart) == 0) { + return -1; + } + + *c = uart->data_buf[data->rx_byte_index]; + data->rx_byte_index = (data->rx_byte_index + 1) % ARRAY_SIZE(uart->data_buf); + + return 0; +} + +/* API implementation: err_check */ +static int uart_b91_err_check(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return ((uart->status & UART_RX_ERR_STATUS) != 0) ? 1 : 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +/* API implementation: fifo_fill */ +static int uart_b91_fifo_fill(const struct device *dev, + const uint8_t *tx_data, + int size) +{ + int i = 0; + + for (i = 0; i < size; i++) { + uart_b91_poll_out(dev, tx_data[i]); + } + + return i; +} + +/* API implementation: fifo_read */ +static int uart_b91_fifo_read(const struct device *dev, + uint8_t *rx_data, + const int size) +{ + int rx_count; + volatile struct uart_b91_t *uart = GET_UART(dev); + + for (rx_count = 0; rx_count < size; rx_count++) { + if (uart_b91_get_rx_bufcnt(uart) == 0) { + break; + } + + uart_b91_poll_in(dev, &rx_data[rx_count]); + } + + return rx_count; +} + +/* API implementation: irq_tx_enable */ +static void uart_b91_irq_tx_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl3 = (uart->ctrl3 & (~FLD_UART_TX_IRQ_TRIQ_LEV)) | + BIT(FLD_UART_TX_IRQ_TRIQ_LEV_OFFSET); + uart->ctrl0 |= UART_TX_IRQ_MASK; +} + +/* API implementation: irq_tx_disable */ +static void uart_b91_irq_tx_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl0 &= ~UART_TX_IRQ_MASK; +} + +/* API implementation: irq_tx_ready */ +static int uart_b91_irq_tx_ready(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_tx_bufcnt(uart) < UART_TX_BUF_CNT) ? 1 : 0; +} + +/* API implementation: irq_tx_complete */ +static int uart_b91_irq_tx_complete(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_tx_bufcnt(uart) == 0) ? 1 : 0; +} + +/* API implementation: irq_rx_enable */ +static void uart_b91_irq_rx_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl3 = (uart->ctrl3 & (~FLD_UART_RX_IRQ_TRIQ_LEV)) | + BIT(FLD_UART_RX_IRQ_TRIQ_LEV_OFFSET); + uart->ctrl0 |= UART_RX_IRQ_MASK; +} + +/* API implementation: irq_rx_disable */ +static void uart_b91_irq_rx_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl0 &= ~UART_RX_IRQ_MASK; +} + +/* API implementation: irq_rx_ready */ +static int uart_b91_irq_rx_ready(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_rx_bufcnt(uart) > 0) ? 1 : 0; +} + +/* API implementation: irq_err_enable */ +static void uart_b91_irq_err_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->rxtimeout |= UART_ERR_IRQ_MASK; +} + +/* API implementation: irq_err_disable*/ +static void uart_b91_irq_err_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->rxtimeout &= ~UART_ERR_IRQ_MASK; +} + +/* API implementation: irq_is_pending */ +static int uart_b91_irq_is_pending(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return ((uart->status & UART_IRQ_STATUS) != 0) ? 1 : 0; +} + +/* API implementation: irq_update */ +static int uart_b91_irq_update(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* nothing to be done */ + return 1; +} + +/* API implementation: irq_callback_set */ +static void uart_b91_irq_callback_set(const struct device *dev, + uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct uart_b91_data *data = DEV_DATA(dev); + + data->callback = cb; + data->cb_data = cb_data; +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api uart_b91_driver_api = { + .poll_in = uart_b91_poll_in, + .poll_out = uart_b91_poll_out, + .err_check = uart_b91_err_check, + .configure = uart_b91_configure, + .config_get = uart_b91_config_get, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_b91_fifo_fill, + .fifo_read = uart_b91_fifo_read, + .irq_tx_enable = uart_b91_irq_tx_enable, + .irq_tx_disable = uart_b91_irq_tx_disable, + .irq_tx_ready = uart_b91_irq_tx_ready, + .irq_tx_complete = uart_b91_irq_tx_complete, + .irq_rx_enable = uart_b91_irq_rx_enable, + .irq_rx_disable = uart_b91_irq_rx_disable, + .irq_rx_ready = uart_b91_irq_rx_ready, + .irq_err_enable = uart_b91_irq_err_enable, + .irq_err_disable = uart_b91_irq_err_disable, + .irq_is_pending = uart_b91_irq_is_pending, + .irq_update = uart_b91_irq_update, + .irq_callback_set = uart_b91_irq_callback_set, +#endif +}; + + +#define UART_B91_INIT(n) \ + \ + static void uart_b91_irq_connect_##n(void); \ + \ + static const uint32_t uart_pins_##n[] = \ + B91_PINMUX_DT_INST_GET_ARRAY(n, 0); \ + \ + static const struct uart_b91_config uart_b91_cfg_##n = \ + { \ + .uart_addr = DT_INST_REG_ADDR(n), \ + .baud_rate = DT_INST_PROP(n, current_speed), \ + .pinctrl_list_size = ARRAY_SIZE(uart_pins_##n), \ + .pinctrl_list = uart_pins_##n, \ + .pirq_connect = uart_b91_irq_connect_##n \ + }; \ + \ + static struct uart_b91_data uart_b91_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, uart_b91_driver_init, \ + NULL, \ + &uart_b91_data_##n, \ + &uart_b91_cfg_##n, \ + PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + (void *)&uart_b91_driver_api); \ + \ + static void uart_b91_irq_connect_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + uart_b91_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + riscv_plic_irq_enable(DT_INST_IRQN(n)); \ + riscv_plic_set_priority(DT_INST_IRQN(n), DT_INST_IRQ(n, priority)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(UART_B91_INIT)