diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 2458611a6bf..699ec58592d 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -52,6 +52,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_XEN_HVC_CONSOLEIO uart_hvc_xen_consolei zephyr_library_sources_ifdef(CONFIG_UART_PIPE uart_pipe.c) zephyr_library_sources_ifdef(CONFIG_UART_SMARTBOND uart_smartbond.c) zephyr_library_sources_ifdef(CONFIG_UART_S32_LINFLEXD uart_s32_linflexd.c) +zephyr_library_sources_ifdef(CONFIG_UART_CDNS uart_cdns.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f3f3fd5cf4e..0e79cc786de 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -206,4 +206,6 @@ source "drivers/serial/Kconfig.smartbond" source "drivers/serial/Kconfig.s32" +source "drivers/serial/Kconfig.cdns" + endif # SERIAL diff --git a/drivers/serial/Kconfig.cdns b/drivers/serial/Kconfig.cdns new file mode 100644 index 00000000000..0913c470a3a --- /dev/null +++ b/drivers/serial/Kconfig.cdns @@ -0,0 +1,11 @@ +#Copyright 2022 Meta Platforms, Inc.and its affiliates. +#SPDX-License-Identifier: Apache-2.0 + +config UART_CDNS + bool "Serial driver for Cadence UART IP6528" + default y + depends on DT_HAS_CDNS_UART_ENABLED + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + help + This option enables the serial driver for Cadence UART IP6528. diff --git a/drivers/serial/uart_cdns.c b/drivers/serial/uart_cdns.c new file mode 100644 index 00000000000..38bd174c91e --- /dev/null +++ b/drivers/serial/uart_cdns.c @@ -0,0 +1,303 @@ +/* + * Copyright 2022 Meta Platforms, Inc. and its affiliates. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT cdns_uart + +/** + * @brief Serial Driver for cadence UART IP6528 + */ + +#include "uart_cdns.h" + +#define DEV_UART(dev) ((struct uart_cdns_regs *) \ + ((const struct uart_cdns_device_config *const)(dev)->config)->port) + +/** Check if tx FIFO is full */ +bool uart_cdns_is_tx_fifo_full(struct uart_cdns_regs *uart_regs) +{ + return ((uart_regs->channel_status & CSR_TFUL_MASK) != 0); +} + +/** Check if tx FIFO is empty */ +bool uart_cdns_is_tx_fifo_empty(struct uart_cdns_regs *uart_regs) +{ + return ((uart_regs->channel_status & CSR_TEMPTY_MASK) != 0); +} + +/** Check if rx FIFO is empty */ +bool uart_cdns_is_rx_fifo_empty(struct uart_cdns_regs *uart_regs) +{ + return ((uart_regs->channel_status & CSR_REMPTY_MASK) != 0); +} + +/** Set the baudrate */ +void uart_cdns_set_baudrate(struct uart_cdns_regs *uart_regs, + const struct uart_cdns_device_config *const dev_cfg, + uint32_t baud_rate) +{ + uart_regs->baud_rate_div = dev_cfg->bdiv; + + /* + * baud_rate is calculated by hardware as below + * + * baud_rate = sel_clk / ((bdiv + 1) * clock_divisor) + * i.e. clock_divisor = sel_clk / ((bdiv + 1) * baud_rate) + * + * However to round to a nearest integer we use this: + * clock_divisor = (sel_clk + ((bdiv + 1) * baud_rate) / 2) / ((bdiv + 1) * baud_rate) + */ + uart_regs->baud_rate_gen = (dev_cfg->sys_clk_freq + ((dev_cfg->bdiv + 1) * baud_rate) / 2) / + ((dev_cfg->bdiv + 1) * baud_rate); +} + +static void uart_cdns_poll_out(const struct device *dev, unsigned char out_char) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + /* Wait while TX FIFO is full */ + while (uart_cdns_is_tx_fifo_full(uart_regs)) { + } + uart_regs->rx_tx_fifo = (uint32_t)out_char; +} + +/** @brief Poll the device for input. */ +int uart_cdns_poll_in(const struct device *dev, unsigned char *p_char) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + if (uart_cdns_is_rx_fifo_empty(uart_regs)) { + return -1; + } + + *p_char = (unsigned char)(uart_regs->rx_tx_fifo & RXDATA_MASK); + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int uart_cdns_fill_fifo(const struct device *dev, const uint8_t *tx_data, int len) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + int i = 0; + + for (i = 0; i < len && (!uart_cdns_is_tx_fifo_full(uart_regs)); i++) { + uart_regs->rx_tx_fifo = tx_data[i]; + while (!uart_cdns_is_tx_fifo_empty(uart_regs)) { + } + } + return i; +} + +static int uart_cdns_read_fifo(const struct device *dev, uint8_t *rx_data, const int size) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + int i = 0; + + for (i = 0; i < size && (!uart_cdns_is_rx_fifo_empty(uart_regs)); i++) { + rx_data[i] = uart_regs->rx_tx_fifo; + } + if (i > 0) { + uart_regs->ctrl |= CTRL_RSTTO_MASK; + } + return i; +} + +void uart_cdns_enable_tx_irq(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->intr_enable |= CSR_TTRIG_MASK; +} + +void uart_cdns_disable_tx_irq(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->intr_disable |= CSR_TTRIG_MASK; +} + +static int uart_cdns_irq_tx_ready(const struct device *dev) +{ + return !uart_cdns_is_tx_fifo_full(DEV_UART(dev)); +} + +static int uart_cdns_irq_tx_complete(const struct device *dev) +{ + return uart_cdns_is_tx_fifo_empty(DEV_UART(dev)); +} + +void uart_cdns_enable_rx_irq(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->rx_timeout = DEFAULT_RTO_PERIODS_FACTOR; + uart_regs->intr_enable |= (CSR_RTRIG_MASK | CSR_RBRK_MASK | CSR_TOUT_MASK); +} + +/** Disable RX UART interrupt */ +void uart_cdns_disable_rx_irq(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->intr_disable |= (CSR_RTRIG_MASK | CSR_RBRK_MASK | CSR_TOUT_MASK); +} + +static int uart_cdns_irq_rx_ready(const struct device *dev) +{ + return !uart_cdns_is_rx_fifo_empty(DEV_UART(dev)); +} + +static void uart_cdns_enable_irq_err(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->intr_enable |= + (CSR_TOVR_MASK | CSR_TOUT_MASK | CSR_PARE_MASK | CSR_FRAME_MASK | CSR_ROVR_MASK); +} + +static void uart_cdns_disable_irq_err(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + uart_regs->intr_disable |= + (CSR_TOVR_MASK | CSR_TOUT_MASK | CSR_PARE_MASK | CSR_FRAME_MASK | CSR_ROVR_MASK); +} + +static int uart_cdns_is_irq_pending(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + + return (uart_regs->channel_intr_status != 0); +} + +/** Check for IRQ updates */ +static int uart_cdns_update_irq(const struct device *dev) +{ + return 1; +} + +/** Set the callback function pointer for IRQ. */ +void uart_cdns_set_irq_callback(const struct device *dev, uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct uart_cdns_data *data; + + data = dev->data; + data->callback = cb; + data->cb_data = cb_data; +} + +static void uart_cdns_irq_handler(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + uint32_t key = irq_lock(); + uint32_t isr_status; + struct uart_cdns_data *data = dev->data; + + if (data->callback) { + data->callback(dev, data->cb_data); + } + + /* clear events */ + /* need to make local copy of the status */ + isr_status = uart_regs->channel_intr_status; + + irq_unlock(key); +} + +#endif + +static const struct uart_driver_api uart_cdns_driver_api = { + .poll_in = uart_cdns_poll_in, + .poll_out = uart_cdns_poll_out, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_cdns_fill_fifo, + .fifo_read = uart_cdns_read_fifo, + .irq_tx_enable = uart_cdns_enable_tx_irq, + .irq_tx_disable = uart_cdns_disable_tx_irq, + .irq_tx_ready = uart_cdns_irq_tx_ready, + .irq_tx_complete = uart_cdns_irq_tx_complete, + .irq_rx_enable = uart_cdns_enable_rx_irq, + .irq_rx_disable = uart_cdns_disable_rx_irq, + .irq_rx_ready = uart_cdns_irq_rx_ready, + .irq_err_enable = uart_cdns_enable_irq_err, + .irq_err_disable = uart_cdns_disable_irq_err, + .irq_is_pending = uart_cdns_is_irq_pending, + .irq_update = uart_cdns_update_irq, + .irq_callback_set = uart_cdns_set_irq_callback +#endif +}; + +/** Initialize the UART */ +static int uart_cdns_init(const struct device *dev) +{ + struct uart_cdns_regs *uart_regs = DEV_UART(dev); + const struct uart_cdns_device_config *const dev_cfg = dev->config; + + /* Reset RX and TX path */ + uart_regs->ctrl = (CTRL_RXRES_MASK | CTRL_TXRES_MASK); + + /* Disable TX and RX channels */ + uart_regs->ctrl = (CTRL_STPBRK_MASK | CTRL_TXDIS_MASK | CTRL_RXDIS_MASK); + + /* Configure Baud rate */ + uart_cdns_set_baudrate(uart_regs, dev_cfg, dev_cfg->baud_rate); + + /* Configure the mode */ + uart_regs->mode = (SET_VAL32(MODE_WSIZE, 1) | SET_VAL32(MODE_UCLKEN, 1) | + SET_VAL32(MODE_PAR, dev_cfg->parity)); + + /* Disable all interrupts */ + uart_regs->intr_disable = 0xFFFFFFFF; + + /* Enable TX and RX Channels */ + uart_regs->ctrl = (CTRL_TXEN_MASK | CTRL_RXEN_MASK | CTRL_STPBRK_MASK); + + if (dev_cfg->cfg_func) { + /* Setup IRQ handler */ + dev_cfg->cfg_func(); + } + + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +#define UART_CDNS_IRQ_CFG_FUNC(n) \ + static void uart_cdns_irq_cfg_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uart_cdns_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } + +#define UART_CDNS_IRQ_CFG_FUNC_INIT(n) .cfg_func = uart_cdns_irq_cfg_func_##n, + +#else + +#define UART_CDNS_IRQ_CFG_FUNC(n) +#define UART_CDNS_IRQ_CFG_FUNC_INIT(n) + +#endif + +#define UART_CDNS_INIT(n) \ + static struct uart_cdns_data uart_cdns_data_##n; \ + \ + UART_CDNS_IRQ_CFG_FUNC(n) \ + \ + static const struct uart_cdns_device_config uart_cdns_dev_cfg_##n = { \ + .port = DT_INST_REG_ADDR(n), \ + .bdiv = DT_INST_PROP(n, bdiv), \ + .sys_clk_freq = DT_INST_PROP(n, clock_frequency), \ + .baud_rate = DT_INST_PROP(n, current_speed), \ + .parity = CDNS_PARTITY_MAP(DT_ENUM_IDX(DT_DRV_INST(n), parity)), \ + UART_CDNS_IRQ_CFG_FUNC_INIT(n)}; \ + \ + DEVICE_DT_INST_DEFINE(n, uart_cdns_init, NULL, &uart_cdns_data_##n, \ + &uart_cdns_dev_cfg_##n, PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &uart_cdns_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(UART_CDNS_INIT) diff --git a/drivers/serial/uart_cdns.h b/drivers/serial/uart_cdns.h new file mode 100644 index 00000000000..744106ca36f --- /dev/null +++ b/drivers/serial/uart_cdns.h @@ -0,0 +1,145 @@ +/* + * Copyright 2022 Meta Platforms, Inc. and its affiliates. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SERIAL_UART_CDNS_H_ +#define ZEPHYR_DRIVERS_SERIAL_UART_CDNS_H_ + +#include +#include +#include + +enum csr_parity_val { + EVEN_PARITY_VAL, + ODD_PARITY_VAL, + SPACE_PARITY_VAL, + MARK_PARITY_VAL, + NO_PARITY_VAL +}; + +/* @brief Control(CTRL) Registers offset 0x00 */ +#define CTRL_STPBRK_MASK (1 << 8) +#define CTRL_STPBRK_SHIFT (8) +#define CTRL_STTBRK_MASK (1 << 7) +#define CTRL_STTBRK_SHIFT (7) +#define CTRL_RSTTO_MASK (1 << 6) +#define CTRL_RSTTO_SHIFT (6) +#define CTRL_TXDIS_MASK (1 << 5) +#define CTRL_TXDIS_SHIFT (5) +#define CTRL_TXEN_MASK (1 << 4) +#define CTRL_TXEN_SHIFT (4) +#define CTRL_RXDIS_MASK (1 << 3) +#define CTRL_RXDIS_SHIFT (3) +#define CTRL_RXEN_MASK (1 << 2) +#define CTRL_RXEN_SHIFT (2) +#define CTRL_TXRES_MASK (1 << 1) +#define CTRL_TXRES_SHIFT (1) +#define CTRL_RXRES_MASK (1 << 0) +#define CTRL_RXRES_SHIFT (0) + +/* @brief Mode Registers offset 0x04 */ +#define MODE_WSIZE_MASK (0x3 << 12) +#define MODE_WSIZE_SHIFT (12) +#define MODE_WSIZE_SIZE (2) +#define MODE_IRMODE_MASK (1 << 11) +#define MODE_IRMODE_SHIFT (11) +#define MODE_UCLKEN_MASK (1 << 10) +#define MODE_UCLKEN_SHIFT (10) +#define MODE_CHMOD_MASK (0x3 << 8) +#define MODE_CHMOD_SHIFT (8) +#define MODE_CHMOD_SIZE (2) +#define MODE_NBSTOP_MASK (0x3 << 6) +#define MODE_NBSTOP_SHIFT (6) +#define MODE_NBSTOP_SIZE (2) +#define MODE_PAR_MASK (0x7 << 3) +#define MODE_PAR_SHIFT (3) +#define MODE_PAR_SIZE (3) +#define MODE_CHRL_MASK (0x3 << 1) +#define MODE_CHRL_SHIFT (2) +#define MODE_CHRL_SIZE (2) +#define MODE_CLKS_MASK (1 << 0) +#define MODE_CLKS_SHIFT (0) + +/* @brief IER, IDR, IMR and CSIR Registers offset 0x08, 0xC, 0x10 and 0x14 */ +#define CSR_RBRK_MASK (1 << 13) +#define CSR_RBRK_SHIFT (13) +#define CSR_TOVR_MASK (1 << 12) +#define CSR_TOVR_SHIFT (12) +#define CSR_TNFUL_MASK (1 << 11) +#define CSR_TNFUL_SHIFT (11) +#define CSR_TTRIG_MASK (1 << 10) +#define CSR_TTRIG_SHIFT (10) +#define CSR_DMSI_MASK (1 << 9) +#define CSR_DMSI_SHIFT (9) +#define CSR_TOUT_MASK (1 << 8) +#define CSR_TOUT_SHIFT (8) +#define CSR_PARE_MASK (1 << 7) +#define CSR_PARE_SHIFT (7) +#define CSR_FRAME_MASK (1 << 6) +#define CSR_FRAME_SHIFT (6) +#define CSR_ROVR_MASK (1 << 5) +#define CSR_ROVR_SHIFT (5) +#define CSR_TFUL_MASK (1 << 4) +#define CSR_TFUL_SHIFT (4) +#define CSR_TEMPTY_MASK (1 << 3) +#define CSR_TEMPTY_SHIFT (3) +#define CSR_RFUL_MASK (1 << 2) +#define CSR_RFUL_SHIFT (2) +#define CSR_REMPTY_MASK (1 << 1) +#define CSR_REMPTY_SHIFT (1) +#define CSR_RTRIG_MASK (1 << 0) +#define CSR_RTRIG_SHIFT (0) + +#define RXDATA_MASK 0xFF /* Receive Data Mask */ +#define MAX_FIFO_SIZE (64) + +#define DEFAULT_RTO_PERIODS_FACTOR 8 + +#define SET_VAL32(name, val) (((uint32_t)(val) << name##_SHIFT) & name##_MASK) + +#define CDNS_PARTITY_MAP(parity) \ + (parity == UART_CFG_PARITY_NONE) ? NO_PARITY_VAL \ + : (parity == UART_CFG_PARITY_ODD) ? ODD_PARITY_VAL \ + : (parity == UART_CFG_PARITY_MARK) ? MARK_PARITY_VAL \ + : (parity == UART_CFG_PARITY_SPACE) ? SPACE_PARITY_VAL \ + : EVEN_PARITY_VAL +struct uart_cdns_regs { + volatile uint32_t ctrl; /* Control Register */ + volatile uint32_t mode; /* Mode Register */ + volatile uint32_t intr_enable; /* Interrupt Enable Register */ + volatile uint32_t intr_disable; /* Interrupt Disable Register */ + volatile uint32_t intr_mask; /* Interrupt Mask Register */ + volatile uint32_t channel_intr_status; /* Channel Interrupt Status Register */ + volatile uint32_t baud_rate_gen; /* Baud Rate Generator Register */ + volatile uint32_t rx_timeout; /* Receiver Timeout Register */ + volatile uint32_t rx_fifo_trigger_level; /* Receiver FIFO Trigger Level Register */ + volatile uint32_t modem_control; /* Modem Control Register */ + volatile uint32_t modem_status; /* Modem Status Register */ + volatile uint32_t channel_status; /* Channel status */ + volatile uint32_t rx_tx_fifo; /* RX TX FIFO Register */ + volatile uint32_t baud_rate_div; /* Baud Rate Divider Register */ + volatile uint32_t flow_ctrl_delay; /* Flow Control Delay Register */ + volatile uint32_t rpwr; /* IR Minimum Received Pulse Register */ + volatile uint32_t tpwr; /* IR TRansmitted Pulse Width Register */ + volatile uint32_t tx_fifo_trigger_level; /* Transmiter FIFO trigger level */ + volatile uint32_t rbrs; /* RX FIFO Byte Status Register */ +}; + +struct uart_cdns_device_config { + uint32_t port; + uint32_t bdiv; + uint32_t sys_clk_freq; + uint32_t baud_rate; + uint8_t parity; + void (*cfg_func)(void); +}; + +struct uart_cdns_data { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t callback; + void *cb_data; +#endif +}; + +#endif /* ZEPHYR_DRIVERS_SERIAL_UART_CDNS_H_ */ diff --git a/dts/bindings/serial/cdns,uart.yaml b/dts/bindings/serial/cdns,uart.yaml new file mode 100644 index 00000000000..2f7b154efa2 --- /dev/null +++ b/dts/bindings/serial/cdns,uart.yaml @@ -0,0 +1,26 @@ +# ************************************************************************* +# Copyright 2022 Meta Platforms, Inc. and its affiliates. +# SPDX-License-Identifier: Apache-2.0 +# ************************************************************************* + +description: This binding gives a base representation of the CADENCE UART IP6528 + +compatible: "cdns,uart" + +include: uart-controller.yaml + +properties: + current-speed: + type: int + required: true + description: Baud Rate in bps. + clock-frequency: + type: int + required: true + description: Frequency(Hz) of Clock to the UART IP. + bdiv: + type: int + required: true + description: Baud Rate Divide register value. + reg: + required: true