From c1fb75b616ba68657c5391a90f5c003aaa73bc52 Mon Sep 17 00:00:00 2001 From: Nhut Nguyen Date: Mon, 4 Nov 2024 09:48:26 +0000 Subject: [PATCH] drivers: serial: Add polling mode support for RZ/G3S This is the initial commit to support UART driver for Renesas RZ/G3S. The driver only implements polling API for minimal support. Signed-off-by: Nhut Nguyen Signed-off-by: Binh Nguyen --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 1 + drivers/serial/Kconfig.renesas_rz | 23 ++ drivers/serial/uart_renesas_rz_scif.c | 301 ++++++++++++++++++ dts/arm/renesas/rz/rzg/r9a08g045.dtsi | 49 +++ dts/bindings/serial/renesas,rz-scif-uart.yaml | 13 + modules/Kconfig.renesas_fsp | 5 + 7 files changed, 393 insertions(+) create mode 100644 drivers/serial/Kconfig.renesas_rz create mode 100644 drivers/serial/uart_renesas_rz_scif.c create mode 100644 dts/bindings/serial/renesas,rz-scif-uart.yaml diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 15afe117e47..c3b1f8acdd9 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -58,6 +58,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_QUICKLOGIC_USBSERIALPORT_S3B uart_ql_us zephyr_library_sources_ifdef(CONFIG_UART_RA8_SCI_B uart_renesas_ra8_sci_b.c) zephyr_library_sources_ifdef(CONFIG_UART_RCAR uart_rcar.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RA uart_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RZ_SCIF uart_renesas_rz_scif.c) zephyr_library_sources_ifdef(CONFIG_UART_RPI_PICO_PIO uart_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c) zephyr_library_sources_ifdef(CONFIG_UART_RV32M1_LPUART uart_rv32m1_lpuart.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 9b76ac170c7..013f0343bd7 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -199,6 +199,7 @@ rsource "Kconfig.ql_usbserialport_s3b" rsource "Kconfig.rcar" rsource "Kconfig.renesas_ra" rsource "Kconfig.renesas_ra8" +rsource "Kconfig.renesas_rz" rsource "Kconfig.rpi_pico" rsource "Kconfig.rtt" rsource "Kconfig.rv32m1_lpuart" diff --git a/drivers/serial/Kconfig.renesas_rz b/drivers/serial/Kconfig.renesas_rz new file mode 100644 index 00000000000..712529a7b20 --- /dev/null +++ b/drivers/serial/Kconfig.renesas_rz @@ -0,0 +1,23 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config UART_RENESAS_RZ_SCIF + bool "Renesas RZ SCIF UART" + default y + depends on DT_HAS_RENESAS_RZ_SCIF_UART_ENABLED + select SERIAL_HAS_DRIVER + select USE_RZ_FSP_SCIF_UART + select PINCTRL + help + Enable Renesas RZ SCIF UART Driver. + +if UART_RENESAS_RZ_SCIF + +config UART_RENESAS_RZG_INIT_DELAY_MS + int "UART initialization delay in milliseconds" + default 8000 + help + This option is to make a delay to wait for the A55 to complete its setting first + before UART initialization of M33. + +endif diff --git a/drivers/serial/uart_renesas_rz_scif.c b/drivers/serial/uart_renesas_rz_scif.c new file mode 100644 index 00000000000..ad08433740f --- /dev/null +++ b/drivers/serial/uart_renesas_rz_scif.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rz_scif_uart + +#include +#include +#include +#include "r_scif_uart.h" + +LOG_MODULE_REGISTER(rz_scif_uart); + +struct uart_rz_scif_config { + const struct pinctrl_dev_config *pin_config; + const uart_api_t *fsp_api; +}; + +struct uart_rz_scif_data { + struct uart_config uart_config; + uart_cfg_t *fsp_cfg; + scif_uart_instance_ctrl_t *fsp_ctrl; +}; + +static int uart_rz_scif_poll_in(const struct device *dev, unsigned char *c) +{ + struct uart_rz_scif_data *data = dev->data; + R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg; + + if (reg->FDR_b.R == 0U) { + /* There are no characters available to read. */ + return -1; + } + *c = reg->FRDR; + + return 0; +} + +static void uart_rz_scif_poll_out(const struct device *dev, unsigned char c) +{ + struct uart_rz_scif_data *data = dev->data; + R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg; + + while (!reg->FSR_b.TDFE) { + } + + reg->FTDR = c; + + while (!reg->FSR_b.TEND) { + } +} + +static int uart_rz_scif_err_check(const struct device *dev) +{ + struct uart_rz_scif_data *data = dev->data; + R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg; + + const uint32_t fsr = reg->FSR; + const uint32_t lsr = reg->LSR; + int errors = 0; + + if ((lsr & R_SCIFA0_LSR_ORER_Msk) != 0) { + errors |= UART_ERROR_OVERRUN; + } + if ((fsr & R_SCIFA0_FSR_PER_Msk) != 0) { + errors |= UART_ERROR_PARITY; + } + if ((fsr & R_SCIFA0_FSR_FER_Pos) != 0) { + errors |= UART_ERROR_FRAMING; + } + + return errors; +} + +static int uart_rz_scif_apply_config(const struct device *dev) +{ + struct uart_rz_scif_data *data = dev->data; + + struct uart_config *uart_config = &data->uart_config; + uart_cfg_t *fsp_cfg = data->fsp_cfg; + + scif_baud_setting_t baud_setting; + scif_uart_extended_cfg_t config_extend; + const scif_uart_extended_cfg_t *fsp_config_extend = fsp_cfg->p_extend; + + fsp_err_t fsp_err; + + fsp_err = R_SCIF_UART_BaudCalculate(data->fsp_ctrl, uart_config->baudrate, false, 5000, + &baud_setting); + if (fsp_err) { + return -EIO; + } + + memcpy(fsp_config_extend->p_baud_setting, &baud_setting, sizeof(scif_baud_setting_t)); + + switch (uart_config->data_bits) { + case UART_CFG_DATA_BITS_7: + fsp_cfg->data_bits = UART_DATA_BITS_7; + break; + case UART_CFG_DATA_BITS_8: + fsp_cfg->data_bits = UART_DATA_BITS_8; + break; + default: + return -ENOTSUP; + } + + switch (uart_config->parity) { + case UART_CFG_PARITY_NONE: + fsp_cfg->parity = UART_PARITY_OFF; + break; + case UART_CFG_PARITY_ODD: + fsp_cfg->parity = UART_PARITY_ODD; + break; + case UART_CFG_PARITY_EVEN: + fsp_cfg->parity = UART_PARITY_EVEN; + break; + default: + return -ENOTSUP; + } + + switch (uart_config->stop_bits) { + case UART_CFG_STOP_BITS_1: + fsp_cfg->stop_bits = UART_STOP_BITS_1; + break; + case UART_CFG_STOP_BITS_2: + fsp_cfg->stop_bits = UART_STOP_BITS_2; + break; + default: + return -ENOTSUP; + } + + memcpy(&config_extend, fsp_config_extend->p_baud_setting, sizeof(scif_baud_setting_t)); + + switch (uart_config->flow_ctrl) { + case UART_CFG_FLOW_CTRL_NONE: + config_extend.flow_control = SCIF_UART_FLOW_CONTROL_NONE; + config_extend.uart_mode = SCIF_UART_MODE_RS232; + config_extend.rs485_setting.enable = SCI_UART_RS485_DISABLE; + break; + case UART_CFG_FLOW_CTRL_RTS_CTS: + config_extend.flow_control = SCIF_UART_FLOW_CONTROL_AUTO; + config_extend.uart_mode = SCIF_UART_MODE_RS232; + config_extend.rs485_setting.enable = SCI_UART_RS485_DISABLE; + break; + default: + return -ENOTSUP; + } + + memcpy(fsp_config_extend->p_baud_setting, &config_extend, sizeof(scif_baud_setting_t)); + + return 0; +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + +static int uart_rz_scif_configure(const struct device *dev, const struct uart_config *cfg) +{ + int err; + fsp_err_t fsp_err; + const struct uart_rz_scif_config *config = dev->config; + struct uart_rz_scif_data *data = dev->data; + + memcpy(&data->uart_config, cfg, sizeof(struct uart_config)); + + err = uart_rz_scif_apply_config(dev); + + if (err) { + return err; + } + + fsp_err = config->fsp_api->close(data->fsp_ctrl); + if (fsp_err) { + return -EIO; + } + + fsp_err = config->fsp_api->open(data->fsp_ctrl, data->fsp_cfg); + if (fsp_err) { + return -EIO; + } + + R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg; + /* Temporarily disable the DRI interrupt caused by receive data ready */ + /* TODO: support interrupt-driven api */ + reg->SCR_b.RIE = 0; + + return err; +} + +static int uart_rz_scif_config_get(const struct device *dev, struct uart_config *cfg) +{ + struct uart_rz_scif_data *data = dev->data; + + memcpy(cfg, &data->uart_config, sizeof(struct uart_config)); + return 0; +} + +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +static const struct uart_driver_api uart_rz_scif_driver_api = { + .poll_in = uart_rz_scif_poll_in, + .poll_out = uart_rz_scif_poll_out, + .err_check = uart_rz_scif_err_check, +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = uart_rz_scif_configure, + .config_get = uart_rz_scif_config_get, +#endif +}; + +static int uart_rz_scif_init(const struct device *dev) +{ + const struct uart_rz_scif_config *config = dev->config; + struct uart_rz_scif_data *data = dev->data; + int ret; + + /* Configure dt provided device signals when available */ + ret = pinctrl_apply_state(config->pin_config, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + + /* uart_rz_scif_apply_config must be called first before open api */ + ret = uart_rz_scif_apply_config(dev); + if (ret < 0) { + return ret; + } + + config->fsp_api->open(data->fsp_ctrl, data->fsp_cfg); + + R_SCIFA0_Type *reg = data->fsp_ctrl->p_reg; + /* Temporarily disable the DRI interrupt caused by receive data ready */ + /* TODO: support interrupt-driven api */ + reg->SCR_b.RIE = 0; + + return 0; +} + +#define UART_RZG_INIT(n) \ + static scif_uart_instance_ctrl_t g_uart##n##_ctrl; \ + static scif_baud_setting_t g_uart##n##_baud_setting; \ + static scif_uart_extended_cfg_t g_uart##n##_cfg_extend = { \ + .bri_ipl = DT_INST_IRQ_BY_NAME(n, bri, priority), \ + .bri_irq = DT_INST_IRQ_BY_NAME(n, bri, irq), \ + .clock = SCIF_UART_CLOCK_INT, \ + .noise_cancel = SCIF_UART_NOISE_CANCELLATION_ENABLE, \ + .p_baud_setting = &g_uart##n##_baud_setting, \ + .rx_fifo_trigger = SCIF_UART_RECEIVE_TRIGGER_MAX, \ + .rts_fifo_trigger = SCIF_UART_RTS_TRIGGER_14, \ + .uart_mode = SCIF_UART_MODE_RS232, \ + .flow_control = SCIF_UART_FLOW_CONTROL_NONE, \ + .rs485_setting = \ + { \ + .enable = (sci_uart_rs485_enable_t)NULL, \ + .polarity = SCI_UART_RS485_DE_POLARITY_HIGH, \ + .de_control_pin = \ + (bsp_io_port_pin_t)SCIF_UART_INVALID_16BIT_PARAM, \ + }, \ + }; \ + static uart_cfg_t g_uart##n##_cfg = { \ + .channel = DT_INST_PROP(n, channel), \ + .p_callback = NULL, \ + .p_context = NULL, \ + .p_extend = &g_uart##n##_cfg_extend, \ + .p_transfer_tx = NULL, \ + .p_transfer_rx = NULL, \ + .rxi_ipl = DT_INST_IRQ_BY_NAME(n, rxi, priority), \ + .txi_ipl = DT_INST_IRQ_BY_NAME(n, txi, priority), \ + .tei_ipl = DT_INST_IRQ_BY_NAME(n, tei, priority), \ + .eri_ipl = DT_INST_IRQ_BY_NAME(n, eri, priority), \ + .rxi_irq = DT_INST_IRQ_BY_NAME(n, rxi, irq), \ + .txi_irq = DT_INST_IRQ_BY_NAME(n, txi, irq), \ + .tei_irq = DT_INST_IRQ_BY_NAME(n, tei, irq), \ + .eri_irq = DT_INST_IRQ_BY_NAME(n, eri, irq), \ + }; \ + PINCTRL_DT_INST_DEFINE(n); \ + static const struct uart_rz_scif_config uart_rz_scif_config_##n = { \ + .pin_config = PINCTRL_DT_INST_DEV_CONFIG_GET(n), .fsp_api = &g_uart_on_scif}; \ + static struct uart_rz_scif_data uart_rz_scif_data_##n = { \ + .uart_config = \ + { \ + .baudrate = DT_INST_PROP_OR(n, current_speed, 115200), \ + .parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE), \ + .stop_bits = \ + DT_INST_ENUM_IDX_OR(n, stop_bits, UART_CFG_STOP_BITS_1), \ + .data_bits = \ + DT_INST_ENUM_IDX_OR(n, data_bits, UART_CFG_DATA_BITS_8), \ + .flow_ctrl = DT_INST_PROP_OR(n, hw_flow_control, \ + UART_CFG_FLOW_CTRL_NONE), \ + }, \ + .fsp_cfg = &g_uart##n##_cfg, \ + .fsp_ctrl = &g_uart##n##_ctrl, \ + }; \ + static int uart_rz_scif_init_##n(const struct device *dev) \ + { \ + return uart_rz_scif_init(dev); \ + } \ + DEVICE_DT_INST_DEFINE(n, &uart_rz_scif_init_##n, NULL, &uart_rz_scif_data_##n, \ + &uart_rz_scif_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ + &uart_rz_scif_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(UART_RZG_INIT) diff --git a/dts/arm/renesas/rz/rzg/r9a08g045.dtsi b/dts/arm/renesas/rz/rzg/r9a08g045.dtsi index ec5932da7eb..e7882aef751 100644 --- a/dts/arm/renesas/rz/rzg/r9a08g045.dtsi +++ b/dts/arm/renesas/rz/rzg/r9a08g045.dtsi @@ -37,6 +37,55 @@ reg = <0x41030000 DT_SIZE_K(64)>; reg-names = "pinctrl"; }; + + scif0: serial@4004b800 { + compatible = "renesas,rz-scif-uart"; + channel = <0>; + reg = <0x4004b800 0x18>; + interrupts = <320 1>, <321 1>, <322 1>, <323 1>, <324 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; + scif1: serial@4004bc00 { + compatible = "renesas,rz-scif-uart"; + channel = <1>; + reg = <0x4004bc00 0x18>; + interrupts = <325 1>, <326 1>, <327 1>, <328 1>, <329 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; + scif2: serial@4004c000 { + compatible = "renesas,rz-scif-uart"; + channel = <2>; + reg = <0x4004c000 0x18>; + interrupts = <330 1>, <331 1>, <332 1>, <333 1>, <334 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; + scif3: serial@4004c400 { + compatible = "renesas,rz-scif-uart"; + channel = <3>; + reg = <0x4004c400 0x18>; + interrupts = <335 1>, <336 1>, <337 1>, <338 1>, <339 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; + scif4: serial@4004c800 { + compatible = "renesas,rz-scif-uart"; + channel = <4>; + reg = <0x4004c800 0x18>; + interrupts = <340 1>, <341 1>, <342 1>, <343 1>, <344 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; + scif5: serial@4004e000 { + compatible = "renesas,rz-scif-uart"; + channel = <5>; + reg = <0x4004e000 0x18>; + interrupts = <345 1>, <346 1>, <347 1>, <348 1>, <349 1>; + interrupt-names = "eri", "bri", "rxi", "txi", "tei"; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/serial/renesas,rz-scif-uart.yaml b/dts/bindings/serial/renesas,rz-scif-uart.yaml new file mode 100644 index 00000000000..b362a09e474 --- /dev/null +++ b/dts/bindings/serial/renesas,rz-scif-uart.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RZ SCIF UART controller + +compatible: "renesas,rz-scif-uart" + +include: [uart-controller.yaml, pinctrl-device.yaml] + +properties: + channel: + type: int + required: true diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index 24f57d060cd..f8c85d90eb6 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -112,4 +112,9 @@ config USE_RZ_FSP_IOPORT help Enable RZ FSP IOPORT driver +config USE_RZ_FSP_SCIF_UART + bool + help + Enable RZ FSP SCIF UART driver + endif