diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index e3aa4ab3f0f..0612d92920f 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -10,6 +10,7 @@ zephyr_library_sources_if_kconfig(uart_esp32.c) zephyr_library_sources_if_kconfig(uart_sifive.c) zephyr_library_sources_if_kconfig(uart_gecko.c) zephyr_library_sources_if_kconfig(leuart_gecko.c) +zephyr_library_sources_if_kconfig(uart_lpc11u6x.c) zephyr_library_sources_if_kconfig(uart_mcux.c) zephyr_library_sources_if_kconfig(uart_mcux_flexcomm.c) zephyr_library_sources_if_kconfig(uart_mcux_lpuart.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index cae12c42e16..07d9b7710bb 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -126,4 +126,6 @@ source "drivers/serial/Kconfig.xlnx" source "drivers/serial/Kconfig.xmc4xxx" +source "drivers/serial/Kconfig.lpc11u6x" + endif # SERIAL diff --git a/drivers/serial/Kconfig.lpc11u6x b/drivers/serial/Kconfig.lpc11u6x new file mode 100644 index 00000000000..8e4c78541e6 --- /dev/null +++ b/drivers/serial/Kconfig.lpc11u6x @@ -0,0 +1,12 @@ +# LPC11U6X UART configuration + +# Copyright (c) 2020 Seagate Technology +# SPDX-License-Identifier: Apache-2.0 + +config UART_LPC11U6X + bool "LPC11U6X serial driver" + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + depends on SOC_SERIES_LPC11U6X + help + Enable UART driver for LPC11U6X series diff --git a/drivers/serial/uart_lpc11u6x.c b/drivers/serial/uart_lpc11u6x.c new file mode 100644 index 00000000000..f0eeb201de7 --- /dev/null +++ b/drivers/serial/uart_lpc11u6x.c @@ -0,0 +1,997 @@ +/* + * Copyright (c) 2020, Seagate Technology LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT nxp_lpc11u6x_uart + +#include + +#include +#include +#include + +#include "uart_lpc11u6x.h" + +#define DEV_CFG(dev) ((dev)->config_info) +#define DEV_DATA(dev) ((dev)->driver_data) + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart0), okay) +static int lpc11u6x_uart0_poll_in(struct device *dev, unsigned char *c) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + if (!(cfg->uart0->lsr & LPC11U6X_UART0_LSR_RDR)) { + return -1; + } + *c = cfg->uart0->rbr; + + return 0; +} + +static void lpc11u6x_uart0_poll_out(struct device *dev, unsigned char c) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + while (!(cfg->uart0->lsr & LPC11U6X_UART0_LSR_THRE)) { + } + cfg->uart0->thr = c; +} + +static int lpc11u6x_uart0_err_check(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + uint32_t lsr; + int ret = 0; + + lsr = cfg->uart0->lsr; + if (lsr & LPC11U6X_UART0_LSR_OE) { + ret |= UART_ERROR_OVERRUN; + } + if (lsr & LPC11U6X_UART0_LSR_PE) { + ret |= UART_ERROR_PARITY; + } + if (lsr & LPC11U6X_UART0_LSR_FE) { + ret |= UART_ERROR_FRAMING; + } + if (lsr & LPC11U6X_UART0_LSR_BI) { + ret |= UART_BREAK; + } + + return ret; +} + +static void lpc11u6x_uart0_write_divisor(struct lpc11u6x_uart0_regs *uart0, + uint32_t div) +{ + /* Enable access to dll & dlm registers */ + uart0->lcr |= LPC11U6X_UART0_LCR_DLAB; + uart0->dll = div & 0xFF; + uart0->dlm = (div >> 8) & 0xFF; + uart0->lcr &= ~LPC11U6X_UART0_LCR_DLAB; +} + +static void lpc11u6x_uart0_write_fdr(struct lpc11u6x_uart0_regs *uart0, + uint32_t div, uint32_t mul) +{ + uart0->fdr = (div & 0xF) | ((mul & 0xF) << 4); +} + +static void lpc11u6x_uart0_config_baudrate(struct device *clk_drv, + const struct lpc11u6x_uart0_config *cfg, + uint32_t baudrate) +{ + uint32_t div = 1, mul, dl; + uint32_t pclk; + + /* Compute values for fractional baud rate generator. We need to have + * a clock that is as close as possible to a multiple of + * LPC11U6X_UART0_CLK so that we can have every baudrate that is + * a multiple of 9600 + */ + clock_control_get_rate(clk_drv, (clock_control_subsys_t) cfg->clkid, + &pclk); + mul = pclk / (pclk % LPC11U6X_UART0_CLK); + + dl = pclk / (16 * baudrate + 16 * baudrate / mul); + + /* Configure clock divisor and fractional baudrate generator */ + lpc11u6x_uart0_write_divisor(cfg->uart0, dl); + lpc11u6x_uart0_write_fdr(cfg->uart0, div, mul); +} + +static int lpc11u6x_uart0_configure(struct device *dev, + const struct uart_config *cfg) +{ + const struct lpc11u6x_uart0_config *dev_cfg = DEV_CFG(dev); + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + struct device *clk_dev; + uint32_t flags = 0; + + /* Check that the baudrate is a multiple of 9600 */ + if (cfg->baudrate % 9600) { + return -ENOTSUP; + } + + switch (cfg->parity) { + case UART_CFG_PARITY_NONE: + break; + case UART_CFG_PARITY_ODD: + flags |= LPC11U6X_UART0_LCR_PARTIY_ENABLE | + LPC11U6X_UART0_LCR_PARTIY_ODD; + break; + case UART_CFG_PARITY_EVEN: + flags |= LPC11U6X_UART0_LCR_PARTIY_ENABLE | + LPC11U6X_UART0_LCR_PARTIY_EVEN; + break; + case UART_CFG_PARITY_MARK: /* fallthrough */ + case UART_CFG_PARITY_SPACE: + return -ENOTSUP; + default: + return -EINVAL; + } + + switch (cfg->stop_bits) { + case UART_CFG_STOP_BITS_0_5: + return -ENOTSUP; + case UART_CFG_STOP_BITS_1: + flags |= LPC11U6X_UART0_LCR_STOP_1BIT; + break; + case UART_CFG_STOP_BITS_1_5: + return -ENOTSUP; + case UART_CFG_STOP_BITS_2: + flags |= LPC11U6X_UART0_LCR_STOP_2BIT; + break; + default: + return -EINVAL; + } + + switch (cfg->data_bits) { + case UART_CFG_DATA_BITS_5: + flags |= LPC11U6X_UART0_LCR_WLS_5BITS; + break; + case UART_CFG_DATA_BITS_6: + flags |= LPC11U6X_UART0_LCR_WLS_6BITS; + break; + case UART_CFG_DATA_BITS_7: + flags |= LPC11U6X_UART0_LCR_WLS_7BITS; + break; + case UART_CFG_DATA_BITS_8: + flags |= LPC11U6X_UART0_LCR_WLS_8BITS; + break; + case UART_CFG_DATA_BITS_9: + return -ENOTSUP; + default: + return -EINVAL; + } + + if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + + clk_dev = device_get_binding(dev_cfg->clock_drv_name); + if (!clk_dev) { + return -EINVAL; + } + lpc11u6x_uart0_config_baudrate(clk_dev, dev_cfg, cfg->baudrate); + dev_cfg->uart0->lcr = flags; + + data->baudrate = cfg->baudrate; + data->stop_bits = cfg->stop_bits; + data->data_bits = cfg->data_bits; + data->flow_ctrl = cfg->flow_ctrl; + data->parity = cfg->parity; + + return 0; +} + +static int lpc11u6x_uart0_config_get(struct device *dev, + struct uart_config *cfg) +{ + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + cfg->baudrate = data->baudrate; + cfg->parity = data->parity; + cfg->stop_bits = data->stop_bits; + cfg->data_bits = data->data_bits; + cfg->flow_ctrl = data->flow_ctrl; + + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int lpc11u6x_uart0_fifo_fill(struct device *dev, const uint8_t *data, + const int size) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + int nr_sent = 0; + + while (nr_sent < size && (cfg->uart0->lsr & LPC11U6X_UART0_LSR_THRE)) { + cfg->uart0->thr = data[nr_sent++]; + } + + return nr_sent; +} + +static int lpc11u6x_uart0_fifo_read(struct device *dev, uint8_t *data, + const int size) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + int nr_rx = 0; + + while (nr_rx < size && (cfg->uart0->lsr & LPC11U6X_UART0_LSR_RDR)) { + data[nr_rx++] = cfg->uart0->rbr; + } + + return nr_rx; +} + +static void lpc11u6x_uart0_irq_tx_enable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) | + LPC11U6X_UART0_IER_THREINTEN; + + /* Due to hardware limitations, first TX interrupt is not triggered when + * enabling it in the IER register. We have to trigger it. + */ + NVIC_SetPendingIRQ(DT_INST_IRQN(0)); +} + +static void lpc11u6x_uart0_irq_tx_disable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) & + ~LPC11U6X_UART0_IER_THREINTEN; +} + +static int lpc11u6x_uart0_irq_tx_complete(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + return (cfg->uart0->lsr & LPC11U6X_UART0_LSR_TEMT) != 0; +} + +static int lpc11u6x_uart0_irq_tx_ready(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + return (cfg->uart0->lsr & LPC11U6X_UART0_LSR_THRE) && + (cfg->uart0->ier & LPC11U6X_UART0_IER_THREINTEN); +} + +static void lpc11u6x_uart0_irq_rx_enable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) | + LPC11U6X_UART0_IER_RBRINTEN; +} + +static void lpc11u6x_uart0_irq_rx_disable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) & + ~LPC11U6X_UART0_IER_RBRINTEN; +} + +static int lpc11u6x_uart0_irq_rx_ready(struct device *dev) +{ + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + return (LPC11U6X_UART0_IIR_INTID(data->cached_iir) == + LPC11U6X_UART0_IIR_INTID_RDA) || + (LPC11U6X_UART0_IIR_INTID(data->cached_iir) == + LPC11U6X_UART0_IIR_INTID_CTI); +} + +static void lpc11u6x_uart0_irq_err_enable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) | + LPC11U6X_UART0_IER_RLSINTEN; +} + +static void lpc11u6x_uart0_irq_err_disable(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + + cfg->uart0->ier = (cfg->uart0->ier & LPC11U6X_UART0_IER_MASK) & + ~LPC11U6X_UART0_IER_RLSINTEN; +} + +static int lpc11u6x_uart0_irq_is_pending(struct device *dev) +{ + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + return !(data->cached_iir & LPC11U6X_UART0_IIR_STATUS); +} + +static int lpc11u6x_uart0_irq_update(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + data->cached_iir = cfg->uart0->iir; + return 1; +} + +static void lpc11u6x_uart0_irq_callback_set(struct device *dev, + uart_irq_callback_user_data_t cb, + void *user_data) +{ + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + data->cb = cb; + data->cb_data = user_data; +} + +static void lpc11u6x_uart0_isr(void *arg) +{ + struct device *dev = arg; + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + + if (data->cb) { + data->cb(data->cb_data); + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static int lpc11u6x_uart0_init(struct device *dev) +{ + const struct lpc11u6x_uart0_config *cfg = DEV_CFG(dev); + struct lpc11u6x_uart0_data *data = DEV_DATA(dev); + struct device *clk_drv, *rx_pinmux_drv, *tx_pinmux_drv; + + /* Configure RX and TX pin via the pinmux driver */ + rx_pinmux_drv = device_get_binding(cfg->rx_pinmux_drv_name); + if (!rx_pinmux_drv) { + return -EINVAL; + } + pinmux_pin_set(rx_pinmux_drv, cfg->rx_pin, cfg->rx_func); + + tx_pinmux_drv = device_get_binding(cfg->tx_pinmux_drv_name); + if (!tx_pinmux_drv) { + return -EINVAL; + } + pinmux_pin_set(tx_pinmux_drv, cfg->tx_pin, cfg->tx_func); + + /* Call clock driver to initialize uart0 clock */ + clk_drv = device_get_binding(cfg->clock_drv_name); + if (!clk_drv) { + return -EINVAL; + } + + clock_control_on(clk_drv, (clock_control_subsys_t) cfg->clkid); + + /* Configure baudrate, parity and stop bits */ + lpc11u6x_uart0_config_baudrate(clk_drv, cfg, cfg->baudrate); + + cfg->uart0->lcr |= LPC11U6X_UART0_LCR_WLS_8BITS; /* 8N1 */ + + data->baudrate = cfg->baudrate; + data->parity = UART_CFG_PARITY_NONE; + data->stop_bits = UART_CFG_STOP_BITS_1; + data->data_bits = UART_CFG_DATA_BITS_8; + data->flow_ctrl = UART_CFG_FLOW_CTRL_NONE; + + /* Configure FIFO */ + cfg->uart0->fcr = LPC11U6X_UART0_FCR_FIFO_EN; + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + cfg->irq_config_func(dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void lpc11u6x_uart0_isr_config(struct device *dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct lpc11u6x_uart0_config uart0_config = { + .uart0 = (struct lpc11u6x_uart0_regs *) + DT_REG_ADDR(DT_NODELABEL(uart0)), + .clock_drv_name = DT_LABEL(DT_PHANDLE(DT_NODELABEL(uart0), clocks)), + .rx_pinmux_drv_name = + DT_LABEL(DT_PHANDLE_BY_NAME(DT_NODELABEL(uart0), pinmuxs, rxd)), + .tx_pinmux_drv_name = + DT_LABEL(DT_PHANDLE_BY_NAME(DT_NODELABEL(uart0), pinmuxs, txd)), + .clkid = DT_PHA_BY_IDX(DT_NODELABEL(uart0), clocks, 0, clkid), + .rx_pin = DT_PHA_BY_NAME(DT_NODELABEL(uart0), pinmuxs, rxd, pin), + .rx_func = DT_PHA_BY_NAME(DT_NODELABEL(uart0), pinmuxs, rxd, function), + .tx_pin = DT_PHA_BY_NAME(DT_NODELABEL(uart0), pinmuxs, txd, pin), + .tx_func = DT_PHA_BY_NAME(DT_NODELABEL(uart0), pinmuxs, txd, function), + .baudrate = DT_PROP(DT_NODELABEL(uart0), current_speed), +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .irq_config_func = lpc11u6x_uart0_isr_config, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +static const struct uart_driver_api uart0_api = { + .poll_in = lpc11u6x_uart0_poll_in, + .poll_out = lpc11u6x_uart0_poll_out, + .err_check = lpc11u6x_uart0_err_check, + .configure = lpc11u6x_uart0_configure, + .config_get = lpc11u6x_uart0_config_get, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = lpc11u6x_uart0_fifo_fill, + .fifo_read = lpc11u6x_uart0_fifo_read, + .irq_tx_enable = lpc11u6x_uart0_irq_tx_enable, + .irq_tx_disable = lpc11u6x_uart0_irq_tx_disable, + .irq_tx_ready = lpc11u6x_uart0_irq_tx_ready, + .irq_tx_complete = lpc11u6x_uart0_irq_tx_complete, + .irq_rx_enable = lpc11u6x_uart0_irq_rx_enable, + .irq_rx_disable = lpc11u6x_uart0_irq_rx_disable, + .irq_rx_ready = lpc11u6x_uart0_irq_rx_ready, + .irq_err_enable = lpc11u6x_uart0_irq_err_enable, + .irq_err_disable = lpc11u6x_uart0_irq_err_disable, + .irq_is_pending = lpc11u6x_uart0_irq_is_pending, + .irq_update = lpc11u6x_uart0_irq_update, + .irq_callback_set = lpc11u6x_uart0_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +static struct lpc11u6x_uart0_data uart0_data; + +DEVICE_AND_API_INIT(lpc11u6x_uart0, DT_LABEL(DT_NODELABEL(uart0)), + &lpc11u6x_uart0_init, + &uart0_data, &uart0_config, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS, + &uart0_api); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void lpc11u6x_uart0_isr_config(struct device *dev) +{ + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(uart0)), + DT_IRQ(DT_NODELABEL(uart0), priority), + lpc11u6x_uart0_isr, DEVICE_GET(lpc11u6x_uart0), 0); + + irq_enable(DT_IRQN(DT_NODELABEL(uart0))); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart0), okay) */ + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) + +static int lpc11u6x_uartx_poll_in(struct device *dev, unsigned char *c) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + if (!(cfg->base->stat & LPC11U6X_UARTX_STAT_RXRDY)) { + return -1; + } + *c = cfg->base->rx_dat; + return 0; +} + +static void lpc11u6x_uartx_poll_out(struct device *dev, unsigned char c) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + while (!(cfg->base->stat & LPC11U6X_UARTX_STAT_TXRDY)) { + } + cfg->base->tx_dat = c; +} + +static int lpc11u6x_uartx_err_check(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + int ret = 0; + + if (cfg->base->stat & LPC11U6X_UARTX_STAT_OVERRUNINT) { + ret |= UART_ERROR_OVERRUN; + } + if (cfg->base->stat & LPC11U6X_UARTX_STAT_FRAMERRINT) { + ret |= UART_ERROR_FRAMING; + } + if (cfg->base->stat & LPC11U6X_UARTX_STAT_PARITYERRINT) { + ret |= UART_ERROR_PARITY; + } + + return ret; +} + +static void lpc11u6x_uartx_config_baud(const struct lpc11u6x_uartx_config *cfg, + struct device *clk_drv, + uint32_t baudrate) +{ + uint32_t clk_rate; + uint32_t div; + + clock_control_get_rate(clk_drv, (clock_control_subsys_t) cfg->clkid, + &clk_rate); + + div = clk_rate / (16 * baudrate); + if (div != 0) { + div -= 1; + } + cfg->base->brg = div & LPC11U6X_UARTX_BRG_MASK; +} + +static int lpc11u6x_uartx_configure(struct device *dev, + const struct uart_config *cfg) +{ + const struct lpc11u6x_uartx_config *dev_cfg = DEV_CFG(dev); + struct lpc11u6x_uartx_data *data = DEV_DATA(dev); + struct device *clk_dev; + uint32_t flags = 0; + + /* We only support baudrates that are multiple of 9600 */ + if (cfg->baudrate % 9600) { + return -ENOTSUP; + } + + switch (cfg->parity) { + case UART_CFG_PARITY_NONE: + flags |= LPC11U6X_UARTX_CFG_PARITY_NONE; + break; + case UART_CFG_PARITY_ODD: + flags |= LPC11U6X_UARTX_CFG_PARITY_ODD; + break; + case UART_CFG_PARITY_EVEN: + flags |= LPC11U6X_UARTX_CFG_PARITY_EVEN; + break; + case UART_CFG_PARITY_MARK: /* fallthrough */ + case UART_CFG_PARITY_SPACE: + return -ENOTSUP; + default: + return -EINVAL; + } + + switch (cfg->stop_bits) { + case UART_CFG_STOP_BITS_0_5: + return -ENOTSUP; + case UART_CFG_STOP_BITS_1: + flags |= LPC11U6X_UARTX_CFG_STOP_1BIT; + break; + case UART_CFG_STOP_BITS_1_5: + return -ENOTSUP; + case UART_CFG_STOP_BITS_2: + flags |= LPC11U6X_UARTX_CFG_STOP_2BIT; + break; + default: + return -EINVAL; + } + + switch (cfg->data_bits) { + case UART_CFG_DATA_BITS_5: /* fallthrough */ + case UART_CFG_DATA_BITS_6: + return -ENOTSUP; + case UART_CFG_DATA_BITS_7: + flags |= LPC11U6X_UARTX_CFG_DATALEN_7BIT; + break; + case UART_CFG_DATA_BITS_8: + flags |= LPC11U6X_UARTX_CFG_DATALEN_8BIT; + break; + case UART_CFG_DATA_BITS_9: + flags |= LPC11U6X_UARTX_CFG_DATALEN_9BIT; + break; + default: + return -EINVAL; + } + + if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + + clk_dev = device_get_binding(dev_cfg->clock_drv_name); + if (!clk_dev) { + return -EINVAL; + } + + /* Disable UART */ + dev_cfg->base->cfg = 0; + + /* Update baudrate */ + lpc11u6x_uartx_config_baud(dev_cfg, clk_dev, cfg->baudrate); + + /* Set parity, data bits, stop bits and re-enable UART interface */ + dev_cfg->base->cfg = flags | LPC11U6X_UARTX_CFG_ENABLE; + + data->baudrate = cfg->baudrate; + data->parity = cfg->parity; + data->stop_bits = cfg->stop_bits; + data->data_bits = cfg->data_bits; + data->flow_ctrl = cfg->flow_ctrl; + + return 0; +} + +static int lpc11u6x_uartx_config_get(struct device *dev, + struct uart_config *cfg) +{ + const struct lpc11u6x_uartx_data *data = DEV_DATA(dev); + + cfg->baudrate = data->baudrate; + cfg->parity = data->parity; + cfg->stop_bits = data->stop_bits; + cfg->data_bits = data->data_bits; + cfg->flow_ctrl = data->flow_ctrl; + + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int lpc11u6x_uartx_fifo_fill(struct device *dev, const uint8_t *data, + int size) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + int tx_size = 0; + + while (tx_size < size && + (cfg->base->stat & LPC11U6X_UARTX_STAT_TXRDY)) { + cfg->base->tx_dat = data[tx_size++]; + } + return tx_size; +} + +static int lpc11u6x_uartx_fifo_read(struct device *dev, uint8_t *data, int size) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + int rx_size = 0; + + while (rx_size < size && + (cfg->base->stat & LPC11U6X_UARTX_STAT_RXRDY)) { + data[rx_size++] = cfg->base->rx_dat; + } + return rx_size; +} + +static void lpc11u6x_uartx_irq_tx_enable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_set = (cfg->base->int_en_set & + LPC11U6X_UARTX_INT_EN_SET_MASK) | + LPC11U6X_UARTX_INT_EN_SET_TXRDYEN; +} + +static void lpc11u6x_uartx_irq_tx_disable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_clr = LPC11U6X_UARTX_INT_EN_CLR_TXRDYCLR; +} + +static int lpc11u6x_uartx_irq_tx_ready(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + return (cfg->base->stat & LPC11U6X_UARTX_STAT_TXRDY) && + (cfg->base->int_en_set & LPC11U6X_UARTX_INT_EN_SET_TXRDYEN); +} + +static int lpc11u6x_uartx_irq_tx_complete(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + return (cfg->base->stat & LPC11U6X_UARTX_STAT_TXIDLE) != 0; +} + +static void lpc11u6x_uartx_irq_rx_enable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_set = (cfg->base->int_en_set & + LPC11U6X_UARTX_INT_EN_SET_MASK) | + LPC11U6X_UARTX_INT_EN_SET_RXRDYEN; +} + +static void lpc11u6x_uartx_irq_rx_disable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_clr = LPC11U6X_UARTX_INT_EN_CLR_RXRDYCLR; +} + +static int lpc11u6x_uartx_irq_rx_ready(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + return (cfg->base->stat & LPC11U6X_UARTX_STAT_RXRDY) && + (cfg->base->int_en_set & LPC11U6X_UARTX_INT_EN_SET_RXRDYEN); +} + +static void lpc11u6x_uartx_irq_err_enable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_set = (cfg->base->int_en_set & + LPC11U6X_UARTX_INT_EN_SET_MASK) | + (LPC11U6X_UARTX_INT_EN_SET_RXRDYEN | + LPC11U6X_UARTX_INT_EN_SET_FRAMERREN | + LPC11U6X_UARTX_INT_EN_SET_PARITYERREN); +} + +static void lpc11u6x_uartx_irq_err_disable(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + cfg->base->int_en_clr = LPC11U6X_UARTX_INT_EN_CLR_OVERRUNCLR | + LPC11U6X_UARTX_INT_EN_CLR_FRAMERRCLR | + LPC11U6X_UARTX_INT_EN_CLR_PARITYERRCLR; +} + +static int lpc11u6x_uartx_irq_is_pending(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + + if ((cfg->base->stat & LPC11U6X_UARTX_STAT_RXRDY) && + (cfg->base->int_stat & LPC11U6X_UARTX_INT_STAT_RXRDY)) { + return 1; + } + if ((cfg->base->stat & LPC11U6X_UARTX_STAT_TXRDY) && + cfg->base->int_stat & LPC11U6X_UARTX_INT_STAT_TXRDY) { + return 1; + } + if (cfg->base->stat & (LPC11U6X_UARTX_STAT_OVERRUNINT | + LPC11U6X_UARTX_STAT_FRAMERRINT | + LPC11U6X_UARTX_STAT_PARITYERRINT)) { + return 1; + } + return 0; +} + +static int lpc11u6x_uartx_irq_update(struct device *dev) +{ + return 1; +} + +static void lpc11u6x_uartx_irq_callback_set(struct device *dev, + uart_irq_callback_user_data_t cb, + void *user_data) +{ + struct lpc11u6x_uartx_data *data = DEV_DATA(dev); + + data->cb = cb; + data->cb_data = user_data; +} + +static void lpc11u6x_uartx_isr(struct device *dev) +{ + struct lpc11u6x_uartx_data *data = DEV_DATA(dev); + + if (data->cb) { + data->cb(data->cb_data); + } +} + +static void lpc11u6x_uartx_shared_isr(void *arg) +{ + struct lpc11u6x_uartx_shared_irq *shared_irq = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(shared_irq->devices); i++) { + if (shared_irq->devices[i]) { + lpc11u6x_uartx_isr(shared_irq->devices[i]); + } + } +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static int lpc11u6x_uartx_init(struct device *dev) +{ + const struct lpc11u6x_uartx_config *cfg = DEV_CFG(dev); + struct lpc11u6x_uartx_data *data = DEV_DATA(dev); + struct device *clk_drv, *rx_pinmux_drv, *tx_pinmux_drv; + + /* Configure RX and TX pin via the pinmux driver */ + rx_pinmux_drv = device_get_binding(cfg->rx_pinmux_drv_name); + if (!rx_pinmux_drv) { + return -EINVAL; + } + pinmux_pin_set(rx_pinmux_drv, cfg->rx_pin, cfg->rx_func); + + tx_pinmux_drv = device_get_binding(cfg->tx_pinmux_drv_name); + if (!tx_pinmux_drv) { + return -EINVAL; + } + pinmux_pin_set(tx_pinmux_drv, cfg->tx_pin, cfg->tx_func); + + /* Call clock driver to initialize uart0 clock */ + clk_drv = device_get_binding(cfg->clock_drv_name); + if (!clk_drv) { + return -EINVAL; + } + + clock_control_on(clk_drv, (clock_control_subsys_t) cfg->clkid); + + /* Configure baudrate, parity and stop bits */ + lpc11u6x_uartx_config_baud(cfg, clk_drv, cfg->baudrate); + cfg->base->cfg = LPC11U6X_UARTX_CFG_DATALEN_8BIT; /* 8N1 */ + + data->baudrate = cfg->baudrate; + data->parity = UART_CFG_PARITY_NONE; + data->stop_bits = UART_CFG_STOP_BITS_1; + data->data_bits = UART_CFG_DATA_BITS_8; + data->flow_ctrl = UART_CFG_FLOW_CTRL_NONE; + + /* Enable UART */ + cfg->base->cfg = (cfg->base->cfg & LPC11U6X_UARTX_CFG_MASK) | + LPC11U6X_UARTX_CFG_ENABLE; + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) + lpc11u6x_uartx_isr_config_1(dev); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) + */ + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) + lpc11u6x_uartx_isr_config_2(dev); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) + */ +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + return 0; +} + +static const struct uart_driver_api uartx_api = { + .poll_in = lpc11u6x_uartx_poll_in, + .poll_out = lpc11u6x_uartx_poll_out, + .err_check = lpc11u6x_uartx_err_check, + .configure = lpc11u6x_uartx_configure, + .config_get = lpc11u6x_uartx_config_get, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = lpc11u6x_uartx_fifo_fill, + .fifo_read = lpc11u6x_uartx_fifo_read, + .irq_tx_enable = lpc11u6x_uartx_irq_tx_enable, + .irq_tx_disable = lpc11u6x_uartx_irq_tx_disable, + .irq_tx_ready = lpc11u6x_uartx_irq_tx_ready, + .irq_tx_complete = lpc11u6x_uartx_irq_tx_complete, + .irq_rx_enable = lpc11u6x_uartx_irq_rx_enable, + .irq_rx_disable = lpc11u6x_uartx_irq_rx_disable, + .irq_rx_ready = lpc11u6x_uartx_irq_rx_ready, + .irq_err_enable = lpc11u6x_uartx_irq_err_enable, + .irq_err_disable = lpc11u6x_uartx_irq_err_disable, + .irq_is_pending = lpc11u6x_uartx_irq_is_pending, + .irq_update = lpc11u6x_uartx_irq_update, + .irq_callback_set = lpc11u6x_uartx_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + + +#define LPC11U6X_UARTX_INIT(idx) \ + \ +static const struct lpc11u6x_uartx_config uart_cfg_##idx = { \ + .base = (struct lpc11u6x_uartx_regs *) \ + DT_REG_ADDR(DT_NODELABEL(uart##idx)), \ + .clock_drv_name = \ + DT_LABEL(DT_PHANDLE(DT_NODELABEL(uart##idx), clocks)), \ + .rx_pinmux_drv_name = \ + DT_LABEL(DT_PHANDLE_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, rxd)), \ + .tx_pinmux_drv_name = \ + DT_LABEL(DT_PHANDLE_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, txd)), \ + .clkid = DT_PHA_BY_IDX(DT_NODELABEL(uart##idx), clocks, 0, clkid), \ + .rx_pin = DT_PHA_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, rxd, pin), \ + .rx_func = DT_PHA_BY_NAME(DT_NODELABEL(uart##idx), \ + pinmuxs, rxd, function), \ + .rx_func = DT_PHA_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, \ + rxd, function), \ + .tx_pin = DT_PHA_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, txd, pin), \ + .tx_func = DT_PHA_BY_NAME(DT_NODELABEL(uart##idx), pinmuxs, \ + txd, function), \ + .baudrate = DT_PROP(DT_NODELABEL(uart##idx), current_speed), \ +}; \ + \ +static struct lpc11u6x_uartx_data uart_data_##idx; \ + \ +DEVICE_AND_API_INIT(lpc11u6x_uartx_##idx, DT_LABEL(DT_NODELABEL(uart##idx)), \ + &lpc11u6x_uartx_init, \ + &uart_data_##idx, &uart_cfg_##idx, \ + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS, \ + &uartx_api) + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) +LPC11U6X_UARTX_INIT(1); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) +LPC11U6X_UARTX_INIT(2); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) +LPC11U6X_UARTX_INIT(3); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) */ + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) +LPC11U6X_UARTX_INIT(4); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) */ + +#if CONFIG_UART_INTERRUPT_DRIVEN && \ + (DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay)) + +struct lpc11u6x_uartx_shared_irq lpc11u6x_uartx_shared_irq_info_1 = { + .devices = { +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) + DEVICE_GET(lpc11u6x_uartx_1), +#else + NULL, +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) + DEVICE_GET(lpc11u6x_uartx_4), +#else + NULL, +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay) */ + }, +}; + +static void lpc11u6x_uartx_isr_config_1(struct device *dev) +{ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(uart1)), + DT_IRQ(DT_NODELABEL(uart1), priority), + lpc11u6x_uartx_shared_isr, + &lpc11u6x_uartx_shared_irq_info_1, + 0); + irq_enable(DT_IRQN(DT_NODELABEL(uart1))); +#else + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(uart4)), + DT_IRQ(DT_NODELABEL(uart4), priority), + lpc11u6x_uartx_shared_isr, + &lpc11u6x_uartx_shared_irq_info_1, + 0); + irq_enable(DT_IRQN(DT_NODELABEL(uart4))); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN && + * (DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay)) + */ + +#if CONFIG_UART_INTERRUPT_DRIVEN && \ + (DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay)) +struct lpc11u6x_uartx_shared_irq lpc11u6x_uartx_shared_irq_info_2 = { + .devices = { +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) + DEVICE_GET(lpc11u6x_uartx_2), +#else + NULL, +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) + DEVICE_GET(lpc11u6x_uartx_3), +#else + NULL, +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay) */ + }, +}; + +static void lpc11u6x_uartx_isr_config_2(struct device *dev) +{ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(uart2)), + DT_IRQ(DT_NODELABEL(uart2), priority), + lpc11u6x_uartx_shared_isr, + &lpc11u6x_uartx_shared_irq_info_2, + 0); + irq_enable(DT_IRQN(DT_NODELABEL(uart2))); +#else + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(uart3)), + DT_IRQ(DT_NODELABEL(uart3), priority), + lpc11u6x_uartx_shared_isr, + &lpc11u6x_uartx_shared_irq_info_2, + 0); + irq_enable(DT_IRQN(DT_NODELABEL(uart3))); +#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN && + * (DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay)) + */ +#endif /* DT_NODE_EXISTS(DT_NODELABEL(uart1) || + * DT_NODE_EXISTS(DT_NODELABEL(uart2) || + * DT_NODE_EXISTS(DT_NODELABEL(uart3) || + * DT_NODE_EXISTS(DT_NODELABEL(uart4) + */ diff --git a/drivers/serial/uart_lpc11u6x.h b/drivers/serial/uart_lpc11u6x.h new file mode 100644 index 00000000000..be6699249a5 --- /dev/null +++ b/drivers/serial/uart_lpc11u6x.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2020, Seagate Technology LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SERIAL_UART_LPC11U6X_H_ +#define ZEPHYR_DRIVERS_SERIAL_UART_LPC11U6X_H_ + +#define LPC11U6X_UART0_CLK 14745600 + +#define LPC11U6X_UART0_LCR_WLS_5BITS 0 +#define LPC11U6X_UART0_LCR_WLS_6BITS 1 +#define LPC11U6X_UART0_LCR_WLS_7BITS 2 +#define LPC11U6X_UART0_LCR_WLS_8BITS 3 +#define LPC11U6X_UART0_LCR_STOP_1BIT (0 << 2) +#define LPC11U6X_UART0_LCR_STOP_2BIT (1 << 2) +#define LPC11U6X_UART0_LCR_PARTIY_ENABLE (1 << 3) +#define LPC11U6X_UART0_LCR_PARTIY_ODD (0 << 4) +#define LPC11U6X_UART0_LCR_PARTIY_EVEN (1 << 4) + +#define LPC11U6X_UART0_LCR_DLAB (1 << 7) + +#define LPC11U6X_UART0_FCR_FIFO_EN (1 << 0) + +#define LPC11U6X_UART0_LSR_RDR (1 << 0) +#define LPC11U6X_UART0_LSR_OE (1 << 1) +#define LPC11U6X_UART0_LSR_PE (1 << 2) +#define LPC11U6X_UART0_LSR_FE (1 << 3) +#define LPC11U6X_UART0_LSR_BI (1 << 4) +#define LPC11U6X_UART0_LSR_THRE (1 << 5) +#define LPC11U6X_UART0_LSR_TEMT (1 << 6) +#define LPC11U6X_UART0_LSR_RXFE (1 << 7) + +#define LPC11U6X_UART0_IER_RBRINTEN (1 << 0) +#define LPC11U6X_UART0_IER_THREINTEN (1 << 1) +#define LPC11U6X_UART0_IER_RLSINTEN (1 << 2) +#define LPC11U6X_UART0_IER_MASK (0x30F) + +#define LPC11U6X_UART0_IIR_STATUS (0x1 << 0) +#define LPC11U6X_UART0_IIR_INTID(x) (((x) >> 1) & 0x7) +#define LPC11U6X_UART0_IIR_INTID_RLS 0x3 +#define LPC11U6X_UART0_IIR_INTID_RDA 0x2 +#define LPC11U6X_UART0_IIR_INTID_CTI 0x6 +#define LPC11U6X_UART0_IIR_INTID_THRE 0x1 + +#define LPC11U6X_UART0_FIFO_SIZE 16 + +#define LPC11U6X_UARTX_CFG_ENABLE (0x1 << 0) +#define LPC11U6X_UARTX_CFG_DATALEN_7BIT (0x0 << 2) +#define LPC11U6X_UARTX_CFG_DATALEN_8BIT (0x1 << 2) +#define LPC11U6X_UARTX_CFG_DATALEN_9BIT (0x2 << 2) +#define LPC11U6X_UARTX_CFG_PARITY_NONE (0x0 << 4) +#define LPC11U6X_UARTX_CFG_PARITY_EVEN (0x2 << 4) +#define LPC11U6X_UARTX_CFG_PARITY_ODD (0x3 << 4) +#define LPC11U6X_UARTX_CFG_STOP_1BIT (0x0 << 6) +#define LPC11U6X_UARTX_CFG_STOP_2BIT (0x1 << 6) + +#define LPC11U6X_UARTX_CFG_MASK (0x00FCDAFD) + +#define LPC11U6X_UARTX_STAT_RXRDY (1 << 0) +#define LPC11U6X_UARTX_STAT_TXRDY (1 << 2) +#define LPC11U6X_UARTX_STAT_TXIDLE (1 << 3) +#define LPC11U6X_UARTX_STAT_OVERRUNINT (1 << 8) +#define LPC11U6X_UARTX_STAT_FRAMERRINT (1 << 13) +#define LPC11U6X_UARTX_STAT_PARITYERRINT (1 << 14) + +#define LPC11U6X_UARTX_BRG_MASK (0xFFFF) + +#define LPC11U6X_UARTX_INT_EN_SET_RXRDYEN (1 << 0) +#define LPC11U6X_UARTX_INT_EN_SET_TXRDYEN (1 << 2) +#define LPC11U6X_UARTX_INT_EN_SET_OVERRUNEN (1 << 8) +#define LPC11U6X_UARTX_INT_EN_SET_FRAMERREN (1 << 13) +#define LPC11U6X_UARTX_INT_EN_SET_PARITYERREN (1 << 14) +#define LPC11U6X_UARTX_INT_EN_SET_MASK (0x0001F96D) + +#define LPC11U6X_UARTX_INT_EN_CLR_RXRDYCLR (1 << 0) +#define LPC11U6X_UARTX_INT_EN_CLR_TXRDYCLR (1 << 2) +#define LPC11U6X_UARTX_INT_EN_CLR_OVERRUNCLR (1 << 8) +#define LPC11U6X_UARTX_INT_EN_CLR_FRAMERRCLR (1 << 13) +#define LPC11U6X_UARTX_INT_EN_CLR_PARITYERRCLR (1 << 14) + +#define LPC11U6X_UARTX_INT_STAT_RXRDY (1 << 0) +#define LPC11U6X_UARTX_INT_STAT_TXRDY (1 << 2) +#define LPC11U6X_UARTX_INT_STAT_OVERRUN (1 << 8) +#define LPC11U6X_UARTX_INT_STAT_FRAMERR (1 << 13) +#define LPC11U6X_UARTX_INT_STAT_PARITYERR (1 << 14) + +#define LPC11U6X_UARTX_DEVICE_PER_IRQ 2 + +struct lpc11u6x_uart0_regs { + union { + volatile const uint32_t rbr; /* RX buffer (RO) */ + volatile uint32_t thr; /* TX buffer (WO) */ + volatile uint32_t dll; /* Divisor latch LSB */ + }; + union { + volatile uint32_t dlm; /* Divisor latch MSB */ + volatile uint32_t ier; /* Interrupt enable */ + }; + union { + volatile uint32_t iir; /* Interrupt ID */ + volatile uint32_t fcr; /* FIFO Control */ + }; + volatile uint32_t lcr; /* Line Control */ + volatile uint32_t mcr; /* Modem Control */ + volatile uint32_t lsr; /* Line Status */ + volatile uint32_t msr; /* Modem Status */ + volatile uint32_t scr; /* Scratch pad */ + volatile uint32_t acr; /* Auto-baud Control */ + volatile uint32_t icr; /* IrDA Control */ + volatile uint32_t fdr; /* Fractional Divider */ + volatile uint32_t osr; /* Oversampling register */ + volatile uint32_t ter; /* Transmit enable */ + volatile uint32_t reserved1[3]; + volatile uint32_t hden; /* Half duplex */ + volatile uint32_t reserved2; + volatile uint32_t sci_ctrl; /* Smart card interface */ + volatile uint32_t rs485_ctrl; /* RS-485 control */ + volatile uint32_t rs485_addr_match; /* RS-485 address match */ + volatile uint32_t rs485_dly; /* RS-485 delay direction control + * delay + */ + volatile uint32_t sync_ctrl; /* Synchronous mode control */ +}; + +struct lpc11u6x_uart0_config { + struct lpc11u6x_uart0_regs *uart0; + const char *clock_drv_name; + const char *rx_pinmux_drv_name; + const char *tx_pinmux_drv_name; + uint32_t baudrate; + uint32_t clkid; + uint8_t rx_pin; + uint8_t rx_func; + uint8_t tx_pin; + uint8_t tx_func; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + void (*irq_config_func)(struct device *dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +struct lpc11u6x_uart0_data { + uint32_t baudrate; + uint8_t parity; + uint8_t stop_bits; + uint8_t data_bits; + uint8_t flow_ctrl; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t cb; + void *cb_data; + uint32_t cached_iir; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +struct lpc11u6x_uartx_regs { + volatile uint32_t cfg; /* Configuration register */ + volatile uint32_t ctl; /* Control register */ + volatile uint32_t stat; /* Status register */ + volatile uint32_t int_en_set; /* Interrupt enable and set */ + volatile uint32_t int_en_clr; /* Interrupt enable clear */ + volatile const uint32_t rx_dat; /* Receiver data */ + volatile const uint32_t rx_dat_stat; /* Receiver data status */ + volatile uint32_t tx_dat; /* Transmit data */ + volatile uint32_t brg; /* Baud rate generator */ + volatile const uint32_t int_stat; /* Interrupt status */ + volatile uint32_t osr; /* Oversample selection */ + volatile uint32_t addr; /* Address regiser*/ +}; + +struct lpc11u6x_uartx_config { + struct lpc11u6x_uartx_regs *base; + const char *clock_drv_name; + const char *rx_pinmux_drv_name; + const char *tx_pinmux_drv_name; + uint32_t baudrate; + uint32_t clkid; + uint8_t rx_pin; + uint8_t rx_func; + uint8_t tx_pin; + uint8_t tx_func; +}; + +struct lpc11u6x_uartx_data { + uint32_t baudrate; + uint8_t parity; + uint8_t stop_bits; + uint8_t data_bits; + uint8_t flow_ctrl; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t cb; + void *cb_data; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +/* Since UART1 and UART4 share the same IRQ (as well as UART2 and UART3), + * we need to give the ISR a way to know all the devices that should be + * notified when said IRQ is raied + */ +struct lpc11u6x_uartx_shared_irq { + struct device *devices[LPC11U6X_UARTX_DEVICE_PER_IRQ]; +}; + +#if CONFIG_UART_INTERRUPT_DRIVEN && \ + (DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart4), okay)) +static void lpc11u6x_uartx_isr_config_1(struct device *dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN && + * (DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay)) + */ + +#if CONFIG_UART_INTERRUPT_DRIVEN && \ + (DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || \ + DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay)) +static void lpc11u6x_uartx_isr_config_2(struct device *dev); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN && + * (DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) || + * DT_NODE_HAS_STATUS(DT_NODELABEL(uart3), okay)) + */ +#endif /* ZEPHYR_DRIVERS_SERIAL_UART_LPC11U6X_H_ */ diff --git a/soc/arm/nxp_lpc/lpc11u6x/Kconfig.defconfig.series b/soc/arm/nxp_lpc/lpc11u6x/Kconfig.defconfig.series index e6efa782cd4..3d4816fe089 100644 --- a/soc/arm/nxp_lpc/lpc11u6x/Kconfig.defconfig.series +++ b/soc/arm/nxp_lpc/lpc11u6x/Kconfig.defconfig.series @@ -21,4 +21,11 @@ config CLOCK_CONTROL_LPC11U6X endif # CLOCK_CONTROL +if SERIAL + +config UART_LPC11U6X + default y + +endif # SERIAL + endif # SOC_SERIES_LPC11U6X