From 0480b2625b4492a2d58b5610c442aaa9cfe922c0 Mon Sep 17 00:00:00 2001 From: Sadik Ozer Date: Fri, 13 Oct 2023 16:19:45 +0300 Subject: [PATCH] drivers: Add MAX32690 uart driver UART driver for MAX32690 Co-authored-by: Okan Sahin Co-authored-by: Tahsin Mutlugun Signed-off-by: Sadik Ozer --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.max32 | 16 + drivers/serial/uart_max32.c | 453 ++++++++++++++++++++++++ dts/bindings/serial/adi,max32-uart.yaml | 36 ++ 5 files changed, 508 insertions(+) create mode 100644 drivers/serial/Kconfig.max32 create mode 100644 drivers/serial/uart_max32.c create mode 100644 dts/bindings/serial/adi,max32-uart.yaml diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index ef0133a5ca1..dc3e4208ff4 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -69,6 +69,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_EMUL uart_emul.c) zephyr_library_sources_ifdef(CONFIG_UART_NUMAKER uart_numaker.c) zephyr_library_sources_ifdef(CONFIG_UART_EFINIX_SAPPIHIRE uart_efinix_sapphire.c) zephyr_library_sources_ifdef(CONFIG_UART_SEDI uart_sedi.c) +zephyr_library_sources_ifdef(CONFIG_UART_MAX32 uart_max32.c) zephyr_library_sources_ifdef(CONFIG_UART_BCM2711_MU uart_bcm2711.c) zephyr_library_sources_ifdef(CONFIG_UART_INTEL_LW uart_intel_lw.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RA uart_renesas_ra.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 91b79ce6dfa..856b3f2eb89 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -266,6 +266,8 @@ source "drivers/serial/Kconfig.efinix_sapphire" source "drivers/serial/Kconfig.sedi" +source "drivers/serial/Kconfig.max32" + source "drivers/serial/Kconfig.bcm2711" source "drivers/serial/Kconfig.intel_lw" diff --git a/drivers/serial/Kconfig.max32 b/drivers/serial/Kconfig.max32 new file mode 100644 index 00000000000..99b5c7b45ef --- /dev/null +++ b/drivers/serial/Kconfig.max32 @@ -0,0 +1,16 @@ +# MAX32 UART configuration + +# Copyright (c) 2023-2024 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +config UART_MAX32 + bool "MAX32 MCU serial driver" + default y + depends on DT_HAS_ADI_MAX32_UART_ENABLED + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + select PINCTRL + help + This option enables the UART driver for MAX32 family of + processors. + Say y if you wish to use serial port on MAX32 MCU. diff --git a/drivers/serial/uart_max32.c b/drivers/serial/uart_max32.c new file mode 100644 index 00000000000..bd34f9e131f --- /dev/null +++ b/drivers/serial/uart_max32.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2023-2024 Analog Devices, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include + +#define DT_DRV_COMPAT adi_max32_uart + +LOG_MODULE_REGISTER(uart_max32, CONFIG_UART_LOG_LEVEL); + +struct max32_uart_config { + mxc_uart_regs_t *regs; + const struct pinctrl_dev_config *pctrl; + const struct device *clock; + struct max32_perclk perclk; + struct uart_config uart_conf; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_config_func_t irq_config_func; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +struct max32_uart_data { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t cb; /* Interrupt callback */ + void *cb_data; /* Interrupt callback arg */ + uint32_t flags; /* Cached interrupt flags */ + uint32_t status; /* Cached status flags */ +#endif + struct uart_config conf; /* baudrate, stopbits, ... */ +}; + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void uart_max32_isr(const struct device *dev); +#endif + +static void api_poll_out(const struct device *dev, unsigned char c) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_WriteCharacter(cfg->regs, c); +} + +static int api_poll_in(const struct device *dev, unsigned char *c) +{ + int val; + const struct max32_uart_config *cfg = dev->config; + + val = MXC_UART_ReadCharacterRaw(cfg->regs); + if (val >= 0) { + *c = (unsigned char)val; + } else { + return -1; + } + + return 0; +} + +static int api_err_check(const struct device *dev) +{ + int err = 0; + uint32_t flags; + const struct max32_uart_config *cfg = dev->config; + + flags = MXC_UART_GetFlags(cfg->regs); + + if (flags & ADI_MAX32_UART_ERROR_FRAMING) { + err |= UART_ERROR_FRAMING; + } + + if (flags & ADI_MAX32_UART_ERROR_PARITY) { + err |= UART_ERROR_PARITY; + } + + if (flags & ADI_MAX32_UART_ERROR_OVERRUN) { + err |= UART_ERROR_OVERRUN; + } + + return err; +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + +static int api_configure(const struct device *dev, const struct uart_config *uart_cfg) +{ + int err; + const struct max32_uart_config *const cfg = dev->config; + mxc_uart_regs_t *regs = cfg->regs; + struct max32_uart_data *data = dev->data; + + /* + * Set parity + */ + if (data->conf.parity != uart_cfg->parity) { + mxc_uart_parity_t mxc_parity; + + switch (uart_cfg->parity) { + case UART_CFG_PARITY_NONE: + mxc_parity = ADI_MAX32_UART_CFG_PARITY_NONE; + break; + case UART_CFG_PARITY_ODD: + mxc_parity = ADI_MAX32_UART_CFG_PARITY_ODD; + break; + case UART_CFG_PARITY_EVEN: + mxc_parity = ADI_MAX32_UART_CFG_PARITY_EVEN; + break; + case UART_CFG_PARITY_MARK: +#if defined(ADI_MAX32_UART_CFG_PARITY_MARK) + mxc_parity = ADI_MAX32_UART_CFG_PARITY_MARK; + break; +#else + return -ENOTSUP; +#endif + case UART_CFG_PARITY_SPACE: +#if defined(ADI_MAX32_UART_CFG_PARITY_SPACE) + mxc_parity = ADI_MAX32_UART_CFG_PARITY_SPACE; + break; +#else + return -ENOTSUP; +#endif + default: + return -EINVAL; + } + + err = MXC_UART_SetParity(regs, mxc_parity); + if (err < 0) { + return -ENOTSUP; + } + /* incase of success keep configuration */ + data->conf.parity = uart_cfg->parity; + } + + /* + * Set stop bit + */ + if (data->conf.stop_bits != uart_cfg->stop_bits) { + if (uart_cfg->stop_bits == UART_CFG_STOP_BITS_1) { + err = MXC_UART_SetStopBits(regs, MXC_UART_STOP_1); + } else if (uart_cfg->stop_bits == UART_CFG_STOP_BITS_2) { + err = MXC_UART_SetStopBits(regs, MXC_UART_STOP_2); + } else { + return -ENOTSUP; + } + if (err < 0) { + return -ENOTSUP; + } + /* incase of success keep configuration */ + data->conf.stop_bits = uart_cfg->stop_bits; + } + + /* + * Set data bit + * Valid data for MAX32 is 5-6-7-8 + * Valid data for Zepyhr is 0-1-2-3 + * Added +5 to index match. + */ + if (data->conf.data_bits != uart_cfg->data_bits) { + err = MXC_UART_SetDataSize(regs, (5 + uart_cfg->data_bits)); + if (err < 0) { + return -ENOTSUP; + } + /* incase of success keep configuration */ + data->conf.data_bits = uart_cfg->data_bits; + } + + /* + * Set flow control + * Flow control not implemented yet so that only support no flow mode + */ + if (data->conf.flow_ctrl != uart_cfg->flow_ctrl) { + if (uart_cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + data->conf.flow_ctrl = uart_cfg->flow_ctrl; + } + + /* + * Set baudrate + */ + if (data->conf.baudrate != uart_cfg->baudrate) { + err = Wrap_MXC_UART_SetFrequency(regs, uart_cfg->baudrate, cfg->perclk.clk_src); + if (err < 0) { + return -ENOTSUP; + } + /* In case of success keep configuration */ + data->conf.baudrate = uart_cfg->baudrate; + } + return 0; +} + +static int api_config_get(const struct device *dev, struct uart_config *uart_cfg) +{ + struct max32_uart_data *data = dev->data; + + /* copy configs from global setting */ + *uart_cfg = data->conf; + + return 0; +} + +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +static int uart_max32_init(const struct device *dev) +{ + int ret; + const struct max32_uart_config *const cfg = dev->config; + mxc_uart_regs_t *regs = cfg->regs; + + if (!device_is_ready(cfg->clock)) { + LOG_ERR("Clock control device not ready"); + return -ENODEV; + } + + ret = MXC_UART_Shutdown(regs); + if (ret) { + return ret; + } + + ret = clock_control_on(cfg->clock, (clock_control_subsys_t)&cfg->perclk); + if (ret != 0) { + LOG_ERR("Cannot enable UART clock"); + return ret; + } + + ret = pinctrl_apply_state(cfg->pctrl, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + ret = api_configure(dev, &cfg->uart_conf); + if (ret) { + return ret; + } + + ret = Wrap_MXC_UART_Init(regs); + if (ret) { + return ret; + } + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + /* Clear any pending UART RX/TX interrupts */ + MXC_UART_ClearFlags(regs, (ADI_MAX32_UART_INT_RX | ADI_MAX32_UART_INT_TX)); + cfg->irq_config_func(dev); +#endif + + return ret; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +static int api_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) +{ + unsigned int num_tx = 0; + const struct max32_uart_config *cfg = dev->config; + + num_tx = MXC_UART_WriteTXFIFO(cfg->regs, (unsigned char *)tx_data, size); + + return (int)num_tx; +} + +static int api_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + unsigned int num_rx = 0; + const struct max32_uart_config *cfg = dev->config; + + num_rx = MXC_UART_ReadRXFIFO(cfg->regs, (unsigned char *)rx_data, size); + if (num_rx == 0) { + MXC_UART_ClearFlags(cfg->regs, ADI_MAX32_UART_INT_RX); + } + + return num_rx; +} + +static void api_irq_tx_enable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + unsigned int key; + + MXC_UART_EnableInt(cfg->regs, ADI_MAX32_UART_INT_TX | ADI_MAX32_UART_INT_TX_OEM); + + key = irq_lock(); + uart_max32_isr(dev); + irq_unlock(key); +} + +static void api_irq_tx_disable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_DisableInt(cfg->regs, ADI_MAX32_UART_INT_TX | ADI_MAX32_UART_INT_TX_OEM); +} + +static int api_irq_tx_ready(const struct device *dev) +{ + struct max32_uart_data *const data = dev->data; + + return ((data->flags & (ADI_MAX32_UART_INT_TX | ADI_MAX32_UART_INT_TX_OEM)) || + !(data->status & MXC_F_UART_STATUS_TX_FULL)); +} + +static void api_irq_rx_enable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_EnableInt(cfg->regs, ADI_MAX32_UART_INT_RX); +} + +static void api_irq_rx_disable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_DisableInt(cfg->regs, ADI_MAX32_UART_INT_RX); +} + +static int api_irq_tx_complete(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + if (MXC_UART_GetActive(cfg->regs) == E_BUSY) { + return 0; + } else { + return 1; /* transmission completed */ + } +} + +static int api_irq_rx_ready(const struct device *dev) +{ + struct max32_uart_data *const data = dev->data; + + return (data->flags & ADI_MAX32_UART_INT_RX); +} + +static void api_irq_err_enable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_EnableInt(cfg->regs, ADI_MAX32_UART_ERROR_INTERRUPTS); +} + +static void api_irq_err_disable(const struct device *dev) +{ + const struct max32_uart_config *cfg = dev->config; + + MXC_UART_DisableInt(cfg->regs, ADI_MAX32_UART_ERROR_INTERRUPTS); +} + +static int api_irq_is_pending(const struct device *dev) +{ + struct max32_uart_data *const data = dev->data; + + return (data->flags & (ADI_MAX32_UART_INT_RX | ADI_MAX32_UART_INT_TX)); +} + +static int api_irq_update(const struct device *dev) +{ + struct max32_uart_data *const data = dev->data; + const struct max32_uart_config *const cfg = dev->config; + + data->flags = MXC_UART_GetFlags(cfg->regs); + data->status = MXC_UART_GetStatus(cfg->regs); + + MXC_UART_ClearFlags(cfg->regs, data->flags); + + return 1; +} + +static void api_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct max32_uart_data *const data = dev->data; + + data->cb = cb; + data->cb_data = cb_data; +} + +static void uart_max32_isr(const struct device *dev) +{ + struct max32_uart_data *data = dev->data; + + if (data->cb) { + data->cb(dev, data->cb_data); + } +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api uart_max32_driver_api = { + .poll_in = api_poll_in, + .poll_out = api_poll_out, + .err_check = api_err_check, +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = api_configure, + .config_get = api_config_get, +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = api_fifo_fill, + .fifo_read = api_fifo_read, + .irq_tx_enable = api_irq_tx_enable, + .irq_tx_disable = api_irq_tx_disable, + .irq_tx_ready = api_irq_tx_ready, + .irq_rx_enable = api_irq_rx_enable, + .irq_rx_disable = api_irq_rx_disable, + .irq_tx_complete = api_irq_tx_complete, + .irq_rx_ready = api_irq_rx_ready, + .irq_err_enable = api_irq_err_enable, + .irq_err_disable = api_irq_err_disable, + .irq_is_pending = api_irq_is_pending, + .irq_update = api_irq_update, + .irq_callback_set = api_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +#define MAX32_UART_INIT(_num) \ + PINCTRL_DT_INST_DEFINE(_num); \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \ + (static void uart_max32_irq_init_##_num(const struct device *dev) \ + { \ + IF_ENABLED( \ + CONFIG_UART_INTERRUPT_DRIVEN, \ + (IRQ_CONNECT(DT_INST_IRQN(_num), DT_INST_IRQ(_num, priority), \ + uart_max32_isr, DEVICE_DT_INST_GET(_num), 0); \ + irq_enable(DT_INST_IRQN(_num)))); \ + })); \ + static const struct max32_uart_config max32_uart_config_##_num = { \ + .regs = (mxc_uart_regs_t *)DT_INST_REG_ADDR(_num), \ + .pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(_num), \ + .clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(_num)), \ + .perclk.bus = DT_INST_CLOCKS_CELL(_num, offset), \ + .perclk.bit = DT_INST_CLOCKS_CELL(_num, bit), \ + .perclk.clk_src = \ + DT_INST_PROP_OR(_num, clock_source, ADI_MAX32_PRPH_CLK_SRC_PCLK), \ + .uart_conf.baudrate = DT_INST_PROP(_num, current_speed), \ + .uart_conf.parity = DT_INST_ENUM_IDX_OR(_num, parity, UART_CFG_PARITY_NONE), \ + .uart_conf.data_bits = DT_INST_ENUM_IDX(_num, data_bits), \ + .uart_conf.stop_bits = DT_INST_ENUM_IDX_OR(_num, stop_bits, UART_CFG_STOP_BITS_1), \ + .uart_conf.flow_ctrl = \ + DT_INST_PROP_OR(_num, hw_flow_control, UART_CFG_FLOW_CTRL_NONE), \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \ + (.irq_config_func = uart_max32_irq_init_##_num,))}; \ + static struct max32_uart_data max32_uart_data##_num = { \ + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, (.cb = NULL,))}; \ + DEVICE_DT_INST_DEFINE(_num, uart_max32_init, NULL, &max32_uart_data##_num, \ + &max32_uart_config_##_num, PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, (void *)&uart_max32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MAX32_UART_INIT) diff --git a/dts/bindings/serial/adi,max32-uart.yaml b/dts/bindings/serial/adi,max32-uart.yaml new file mode 100644 index 00000000000..c79543cf6b8 --- /dev/null +++ b/dts/bindings/serial/adi,max32-uart.yaml @@ -0,0 +1,36 @@ +# Copyright (c) 2023-2024 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: MAX32 UART + +include: [uart-controller.yaml, pinctrl-device.yaml] + +compatible: "adi,max32-uart" + +properties: + reg: + required: true + + clocks: + required: true + + interrupts: + required: true + + pinctrl: + type: phandles + + clock-source: + type: int + enum: [0, 1, 2, 3, 4, 5] + description: | + Clock source to be used by the UART peripheral. The following options + are available: + - 0: "ADI_MAX32_PRPH_CLK_SRC_PCLK" Peripheral clock + - 1: "ADI_MAX32_PRPH_CLK_SRC_EXTCLK" External Clock + - 2: "ADI_MAX32_PRPH_CLK_SRC_IBRO" Internal Baud Rate Oscillator + - 3: "ADI_MAX32_PRPH_CLK_SRC_ERFO" External Radio Frequency Oscillator + - 4: "ADI_MAX32_PRPH_CLK_SRC_ERTCO" External Real-Time Clock Oscillator + - 5: "ADI_MAX32_PRPH_CLK_SRC_INRO" Internal Ring Oscillator + The target device might not support all option please take a look on + target device user guide