From 2e26a4497a44906ab22121d41d0863f33da98093 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Wed, 23 Apr 2025 16:53:01 +0700 Subject: [PATCH] drivers: serial: Add support for RZ/A2M Add serial support for RZ/A2M Signed-off-by: Hoang Nguyen Signed-off-by: Binh Nguyen --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig.renesas_rz | 12 +- drivers/serial/uart_renesas_rza2m_scif.c | 786 ++++++++++++++++++ dts/arm/renesas/rz/rza/r7s9210.dtsi | 70 ++ .../serial/renesas,rza2m-scif-uart.yaml | 20 + 5 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 drivers/serial/uart_renesas_rza2m_scif.c create mode 100644 dts/bindings/serial/renesas,rza2m-scif-uart.yaml diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 7ff54af2931..1a070d66c59 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -64,6 +64,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_PSOC6 uart_psoc6.c) zephyr_library_sources_ifdef(CONFIG_UART_QUICKLOGIC_USBSERIALPORT_S3B uart_ql_usbserialport_s3b.c) 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_RZA2M_SCIF uart_renesas_rza2m_scif.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RZ_SCI uart_renesas_rz_sci.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) diff --git a/drivers/serial/Kconfig.renesas_rz b/drivers/serial/Kconfig.renesas_rz index 90fa4ff2d3d..dfbd9f13437 100644 --- a/drivers/serial/Kconfig.renesas_rz +++ b/drivers/serial/Kconfig.renesas_rz @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Renesas Electronics Corporation +# Copyright (c) 2024-2025 Renesas Electronics Corporation # SPDX-License-Identifier: Apache-2.0 config UART_RENESAS_RZ_SCIF @@ -33,3 +33,13 @@ config UART_RENESAS_RZG_INIT_DELAY_MS before UART initialization of M33. endif + +config UART_RENESAS_RZA2M_SCIF + bool "Renesas A2M SCIF UART Driver" + default y + depends on DT_HAS_RENESAS_RZA2M_SCIF_UART_ENABLED + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + select PINCTRL + help + Enable Renesas SCIF UART Driver. diff --git a/drivers/serial/uart_renesas_rza2m_scif.c b/drivers/serial/uart_renesas_rza2m_scif.c new file mode 100644 index 00000000000..c6b8068781e --- /dev/null +++ b/drivers/serial/uart_renesas_rza2m_scif.c @@ -0,0 +1,786 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rza2m_scif_uart + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(uart_renesas_rza2m_scif, CONFIG_UART_LOG_LEVEL); + +struct scif_reg { + uint8_t offset, size; +}; + +enum { + RZA2M_SMR, /* Serial Mode Register */ + RZA2M_BRR, /* Bit Rate Register */ + RZA2M_SCR, /* Serial Control Register */ + RZA2M_FSR, /* Serial Status Register */ + RZA2M_FCR, /* FIFO Control Register */ + RZA2M_FDR, /* FIFO Data Count Register */ + RZA2M_FTDR, /* Transmit (FIFO) Data Register */ + RZA2M_FRDR, /* Receive (FIFO) Data Register */ + RZA2M_LSR, /* Line Status Register */ + RZA2M_TFDR, /* Transmit FIFO Data Count Register */ + RZA2M_RFDR, /* Receive FIFO Data Count Register */ + RZA2M_SPTR, /* Serial Port Register */ + RZA2M_SEMR, /* Serial extended mode register */ + RZA2M_FTCR, /* FIFO Trigger Control Register */ + + RZA2M_SCIF_NR_REGS, +}; + +struct scif_params { + const struct scif_reg regs[RZA2M_SCIF_NR_REGS]; + uint16_t init_lsr_mask; + uint16_t init_interrupt_mask; +}; + +struct uart_rza2m_scif_cfg { + DEVICE_MMIO_ROM; /* Must be first */ + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + const struct pinctrl_dev_config *pcfg; + const struct scif_params *params; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + void (*irq_config_func)(const struct device *dev); +#endif +}; + +struct uart_rza2m_scif_int { + uint16_t rxi_status; + uint16_t line_status; +}; + +struct uart_rza2m_scif_data { + DEVICE_MMIO_RAM; /* Must be first */ + struct uart_config current_config; + uint8_t channel; + uint32_t clk_rate; + struct k_spinlock lock; + struct uart_rza2m_scif_int int_data; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t irq_cb; + void *irq_cb_data; +#endif +}; + +#define RZA2M_SCIF_DEFAULT_SPEED 115200 +#define RZA2M_SCIF_DEFAULT_PARITY UART_CFG_PARITY_NONE +#define RZA2M_SCIF_DEFAULT_STOP_BITS UART_CFG_STOP_BITS_1 +#define RZA2M_SCIF_DEFAULT_DATA_BITS UART_CFG_DATA_BITS_8 + +/* SMR (Serial Mode Register) */ +#define RZA2M_SMR_C_A BIT(7) /* Communication Mode */ +#define RZA2M_SMR_CHR BIT(6) /* 7-bit Character Length */ +#define RZA2M_SMR_PE BIT(5) /* Parity Enable */ +#define RZA2M_SMR_O_E BIT(4) /* Odd Parity */ +#define RZA2M_SMR_STOP BIT(3) /* Stop Bit Length */ +#define RZA2M_SMR_CKS_MASK (BIT(0) | BIT(1)) /* Clock Select */ +#define RZA2M_SMR_CKS_SHIFT 0 /* Clock Select shift */ + +/* SCR (Serial Control Register) */ +#define RZA2M_SCR_TIE BIT(7) /* Transmit Interrupt Enable */ +#define RZA2M_SCR_RIE BIT(6) /* Receive Interrupt Enable */ +#define RZA2M_SCR_TE BIT(5) /* Transmit Enable */ +#define RZA2M_SCR_RE BIT(4) /* Receive Enable */ +#define RZA2M_SCR_REIE BIT(3) /* Receive Error Interrupt Enable */ +#define RZA2M_SCR_TEIE BIT(2) /* Transmit End Interrupt Enable */ +#define RZA2M_SCR_CKE1 BIT(1) /* Clock Enable 1 */ +#define RZA2M_SCR_CKE0 BIT(0) /* Clock Enable 0 */ + +/* FCR (FIFO Control Register) */ +#define RZA2M_FCR_RTRG1 BIT(7) /* Receive FIFO Data Count Trigger 1 */ +#define RZA2M_FCR_RTRG0 BIT(6) /* Receive FIFO Data Count Trigger 0 */ +#define RZA2M_FCR_TTRG1 BIT(5) /* Transmit FIFO Data Count Trigger 1 */ +#define RZA2M_FCR_TTRG0 BIT(4) /* Transmit FIFO Data Count Trigger 0 */ +#define RZA2M_FCR_MCE BIT(3) /* Modem Control Enable */ +#define RZA2M_FCR_TFRST BIT(2) /* Transmit FIFO Data Register Reset */ +#define RZA2M_FCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */ +#define RZA2M_FCR_LOOP BIT(0) /* Loopback Test */ + +/* FSR (Serial Status Register) */ +#define RZA2M_FSR_PER3 BIT(15) /* Parity Error Count 3 */ +#define RZA2M_FSR_PER2 BIT(14) /* Parity Error Count 2 */ +#define RZA2M_FSR_PER1 BIT(13) /* Parity Error Count 1 */ +#define RZA2M_FSR_PER0 BIT(12) /* Parity Error Count 0 */ +#define RZA2M_FSR_FER3 BIT(11) /* Framing Error Count 3 */ +#define RZA2M_FSR_FER2 BIT(10) /* Framing Error Count 2 */ +#define RZA2M_FSR_FER1 BIT(9) /* Framing Error Count 1 */ +#define RZA2M_FSR_FER0 BIT(8) /* Framing Error Count 0 */ +#define RZA2M_FSR_ER BIT(7) /* Receive Error */ +#define RZA2M_FSR_TEND BIT(6) /* Transmission ended */ +#define RZA2M_FSR_TDFE BIT(5) /* Transmit FIFO Data Empty */ +#define RZA2M_FSR_BRK BIT(4) /* Break Detect */ +#define RZA2M_FSR_FER BIT(3) /* Framing Error */ +#define RZA2M_FSR_PER BIT(2) /* Parity Error */ +#define RZA2M_FSR_RDF BIT(1) /* Receive FIFO Data Full */ +#define RZA2M_FSR_DR BIT(0) /* Receive Data Ready */ + +/* SPTR (Serial Port Register) on SCIFA */ +#define RZA2M_SPTR_SPB2IO BIT(1) /* Serial Port Break Input/Output */ +#define RZA2M_SPTR_SPB2DT BIT(0) /* Serial Port Break Data Select */ + +/* LSR (Line Status Register) on SCIFA */ +#define RZA2M_LSR_TO_SCIFA BIT(2) /* Timeout on SCIFA */ +#define RZA2M_LSR_ORER BIT(0) /* Overrun Error */ + +/* Serial Extended Mode Register */ +#define RZA2M_SEMR_ABCS0 BIT(0) /* Asynchronous Base Clock Select */ +#define RZA2M_SEMR_NFEN BIT(2) /* Noise Cancellation Enable */ +#define RZA2M_SEMR_DIR BIT(3) /* Data Transfer Direction Select */ +#define RZA2M_SEMR_MDDRS BIT(4) /* Modulation Duty Register Select */ +#define RZA2M_SEMR_BRME BIT(5) /* Bit Rate Modulation Enable */ +/* Baud Rate Generator Double-Speed Mode Select */ +#define RZA2M_SEMR_BGDM BIT(7) + +#define RZA2M_SCIF_DRV_ERR (-1) + +/* clang-format off */ +/* Registers */ +static const struct scif_params port_params = { + .regs = { + [RZA2M_SMR] = {0x00, 16}, + [RZA2M_BRR] = {0x02, 8}, + [RZA2M_SCR] = {0x04, 16}, + [RZA2M_FTDR] = {0x06, 8}, + [RZA2M_FSR] = {0x08, 16}, + [RZA2M_FRDR] = {0x0A, 8}, + [RZA2M_FCR] = {0x0C, 16}, + [RZA2M_FDR] = {0x0E, 16}, + [RZA2M_SPTR] = {0x10, 16}, + [RZA2M_LSR] = {0x12, 16}, + [RZA2M_SEMR] = {0x14, 8}, + [RZA2M_FTCR] = {0x16, 16}, + }, + .init_lsr_mask = RZA2M_LSR_ORER, + .init_interrupt_mask = + RZA2M_SCR_TIE | RZA2M_SCR_RIE | RZA2M_SCR_REIE | RZA2M_SCR_TEIE, +}; +/* clang-format on */ + +#define RZA2M_NUM_DIVISORS_ASYNC (9) + +/* Baud divisor info */ +/** + * When ABCS = 0 & BGDM = 0, divisor = 64 x 2^(2n - 1) + * When ABCS = 1 & BGDM = 0 OR ABCS = 0 & BGDM = 1, divisor = 32 x 2^(2n - 1) + * When ABCS = 1 & BGDM = 1, divisor = 16 x 2^(2n - 1) + */ + +typedef struct { + int16_t divisor; /* Clock divisor */ + uint8_t abcs; /* ABCS value to get divisor */ + uint8_t bgdm; /* BGDM value to get divisor */ + uint8_t cks; /* CKS value to get divisor (CKS = n) */ +} st_baud_divisorb_t; + +/* Divisor result, ABCS, BGDM, n */ +static const st_baud_divisorb_t gs_scifa_async_baud[RZA2M_NUM_DIVISORS_ASYNC] = { + {8, 1, 1, 0}, {16, 0, 1, 0}, {32, 0, 0, 0}, {64, 0, 1, 1}, {128, 0, 0, 1}, + {256, 0, 1, 2}, {512, 0, 0, 2}, {1024, 0, 1, 3}, {2048, 0, 0, 3}}; + +static uint8_t uart_rza2m_scif_read_8(const struct device *dev, uint32_t offs) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + uint32_t offset = config->params->regs[offs].offset; + + return sys_read8(DEVICE_MMIO_GET(dev) + offset); +} + +static void uart_rza2m_scif_write_8(const struct device *dev, uint32_t offs, uint8_t value) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + uint32_t offset = config->params->regs[offs].offset; + + sys_write8(value, DEVICE_MMIO_GET(dev) + offset); +} + +static uint16_t uart_rza2m_scif_read_16(const struct device *dev, uint32_t offs) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + uint32_t offset = config->params->regs[offs].offset; + + return sys_read16(DEVICE_MMIO_GET(dev) + offset); +} + +static void uart_rza2m_scif_write_16(const struct device *dev, uint32_t offs, uint16_t value) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + uint32_t offset = config->params->regs[offs].offset; + + sys_write16(value, DEVICE_MMIO_GET(dev) + offset); +} + +static uint8_t find_divisor_index(uint8_t channel, uint32_t desired_baud_rate, uint32_t clock_freq) +{ + /* Find the divisor; table has associated ABCS, BGDM and CKS values */ + /* BRR must be 255 or less */ + /* BRR = (PCLK / (divisor * desired_baud)) - 1 */ + /* BRR = (ratio / divisor) - 1 */ + + uint32_t ratio = clock_freq / desired_baud_rate; + uint8_t divisor_index = 0; + + if (channel == 0) { + /* The hardware manual states that for channel 0, P1f/16 input is not provided */ + /* So the setting CKS [1:0] 1 0 cannot be used */ + /* This restriction may be lifted in future releases */ + + while ((divisor_index < RZA2M_NUM_DIVISORS_ASYNC) && + ((ratio >= (uint32_t)(gs_scifa_async_baud[divisor_index].divisor * 256)) || + (2 == gs_scifa_async_baud[divisor_index].cks))) { + divisor_index++; + } + } else { + /* Cast to uint32_t for comparison with ratio */ + while ((divisor_index < RZA2M_NUM_DIVISORS_ASYNC) && + (ratio >= (uint32_t)(gs_scifa_async_baud[divisor_index].divisor * 256))) { + divisor_index++; + } + } + + return divisor_index; +} + +static int uart_rza2m_scif_set_baudrate(const struct device *dev, uint8_t channel, + uint32_t baud_rate) +{ + struct uart_rza2m_scif_data *data = dev->data; + uint16_t reg_16; + uint8_t reg_8; + uint32_t clk_freq = data->clk_rate; + uint8_t divisor_index; + uint32_t divisor; + uint32_t brr; + + divisor_index = find_divisor_index(channel, baud_rate, clk_freq); + divisor = (uint32_t)gs_scifa_async_baud[divisor_index].divisor; + + brr = clk_freq / (divisor * baud_rate); + + if (brr == 0) { + return RZA2M_SCIF_DRV_ERR; /* Illegal value; return error */ + } + + /* Divide by half the divisor */ + brr = clk_freq / ((divisor * baud_rate) / 2); + + /* Formula: BRR = (PCLK / (divisor * desired_baud)) - 1 */ + /* If (BRR * 2) is odd, "round up" by ignoring -1; divide by 2 again for rest of divisor */ + brr = ((brr & 0x01) ? (brr / 2) : ((brr / 2) - 1)); + + /* Write BRR */ + uart_rza2m_scif_write_8(dev, RZA2M_BRR, brr); + + /* Write CKS[1:0] */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SMR); + reg_16 &= ~(RZA2M_SMR_CKS_MASK << RZA2M_SMR_CKS_SHIFT); + reg_16 |= (gs_scifa_async_baud[divisor_index].cks & RZA2M_SMR_CKS_MASK) + << RZA2M_SMR_CKS_SHIFT; + uart_rza2m_scif_write_16(dev, RZA2M_SMR, reg_16); + + /* Write ABCS0 and BGDM */ + reg_8 = uart_rza2m_scif_read_8(dev, RZA2M_SEMR); + reg_8 = reg_8 | (gs_scifa_async_baud[divisor_index].abcs ? RZA2M_SEMR_ABCS0 : 0); + reg_8 = reg_8 | (gs_scifa_async_baud[divisor_index].bgdm ? RZA2M_SEMR_BGDM : 0); + uart_rza2m_scif_write_8(dev, RZA2M_SEMR, reg_8); + + return 0; +} + +static int uart_rza2m_scif_poll_in(const struct device *dev, unsigned char *p_char) +{ + struct uart_rza2m_scif_data *data = dev->data; + uint16_t reg_16; + int ret = 0; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + /* Receive FIFO empty */ + if (!(uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_RDF)) { + ret = -1; + goto unlock; + } + + *p_char = uart_rza2m_scif_read_8(dev, RZA2M_FRDR); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FSR); + reg_16 &= ~RZA2M_FSR_RDF; + uart_rza2m_scif_write_16(dev, RZA2M_FSR, reg_16); + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static void uart_rza2m_scif_poll_out(const struct device *dev, unsigned char out_char) +{ + struct uart_rza2m_scif_data *data = dev->data; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + /* Wait for empty space in transmit FIFO */ + while (!(uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_TDFE)) { + } + + uart_rza2m_scif_write_8(dev, RZA2M_FTDR, out_char); + + while (!(uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_TEND)) { + } + + k_spin_unlock(&data->lock, key); +} + +static int uart_rza2m_scif_err_check(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + struct uart_rza2m_scif_int int_data = data->int_data; + int err = 0; + + if (int_data.line_status & RZA2M_LSR_ORER) { + err |= UART_ERROR_OVERRUN; + } + if (int_data.rxi_status & RZA2M_FSR_FER) { + err |= UART_ERROR_FRAMING; + } + if (int_data.rxi_status & RZA2M_FSR_PER) { + err |= UART_ERROR_PARITY; + } + if (int_data.rxi_status & RZA2M_FSR_BRK) { + err |= UART_BREAK; + } + + return err; +} + +static int uart_rza2m_scif_configure(const struct device *dev, const struct uart_config *cfg) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + struct uart_rza2m_scif_data *data = dev->data; + uint16_t reg_16; + k_spinlock_key_t key; + int err; + + err = 0; + + if (cfg->data_bits < UART_CFG_DATA_BITS_7 || cfg->data_bits > UART_CFG_DATA_BITS_8 || + cfg->stop_bits == UART_CFG_STOP_BITS_0_5 || cfg->stop_bits == UART_CFG_STOP_BITS_1_5 || + cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + + key = k_spin_lock(&data->lock); + + /* Set the TXD output high */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SPTR); + reg_16 |= (RZA2M_SPTR_SPB2DT | RZA2M_SPTR_SPB2IO); + uart_rza2m_scif_write_16(dev, RZA2M_SPTR, reg_16); + + /* Disable Transmit and Receive */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 &= ~(RZA2M_SCR_TE | RZA2M_SCR_RE | RZA2M_SCR_TIE | RZA2M_SCR_RIE); + reg_16 &= ~(RZA2M_SCR_TEIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + /* Emptying Transmit and Receive FIFO */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FCR); + reg_16 |= (RZA2M_FCR_TFRST | RZA2M_FCR_RFRST); + uart_rza2m_scif_write_16(dev, RZA2M_FCR, reg_16); + + /* Resetting Errors Registers */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FSR); + reg_16 &= ~(RZA2M_FSR_ER | RZA2M_FSR_DR | RZA2M_FSR_BRK | RZA2M_FSR_RDF); + uart_rza2m_scif_write_16(dev, RZA2M_FSR, reg_16); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_LSR); + reg_16 &= ~(config->params->init_lsr_mask); + uart_rza2m_scif_write_16(dev, RZA2M_LSR, reg_16); + + /* Select internal clock */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 &= ~(RZA2M_SCR_CKE1 | RZA2M_SCR_CKE0); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + /* Serial Configuration (8N1) & Clock divider selection */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SMR); + reg_16 &= ~(RZA2M_SMR_C_A | RZA2M_SMR_CHR | RZA2M_SMR_PE | RZA2M_SMR_O_E | RZA2M_SMR_STOP); + switch (cfg->parity) { + case UART_CFG_PARITY_NONE: + break; + case UART_CFG_PARITY_ODD: + reg_16 |= RZA2M_SMR_PE | RZA2M_SMR_O_E; + break; + case UART_CFG_PARITY_EVEN: + reg_16 |= RZA2M_SMR_PE; + break; + default: + return -ENOTSUP; + } + if (cfg->stop_bits == UART_CFG_STOP_BITS_2) { + reg_16 |= RZA2M_SMR_STOP; + } + if (cfg->data_bits == UART_CFG_DATA_BITS_7) { + reg_16 |= RZA2M_SMR_CHR; + } + uart_rza2m_scif_write_16(dev, RZA2M_SMR, reg_16); + + /* Set baudrate */ + err = uart_rza2m_scif_set_baudrate(dev, data->channel, cfg->baudrate); + if (err) { + return -EIO; + } + + /* FIFOs data count trigger configuration */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FCR); + reg_16 &= ~(RZA2M_FCR_RTRG1 | RZA2M_FCR_RTRG0 | RZA2M_FCR_TTRG1 | RZA2M_FCR_TTRG0 | + RZA2M_FCR_MCE | RZA2M_FCR_TFRST | RZA2M_FCR_RFRST); + uart_rza2m_scif_write_16(dev, RZA2M_FCR, reg_16); + + /* Enable Transmit & Receive + disable Interrupts */ + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 |= (RZA2M_SCR_TE | RZA2M_SCR_RE); + reg_16 &= ~(config->params->init_interrupt_mask); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + data->current_config = *cfg; + + k_spin_unlock(&data->lock, key); + + return err; +} + +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE +static int uart_rza2m_scif_config_get(const struct device *dev, struct uart_config *cfg) +{ + struct uart_rza2m_scif_data *data = dev->data; + + *cfg = data->current_config; + + return 0; +} +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +static int uart_rza2m_scif_init(const struct device *dev) +{ + const struct uart_rza2m_scif_cfg *config = dev->config; + struct uart_rza2m_scif_data *data = dev->data; + int ret; + + /* Configure dt provided device signals when available */ + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)config->clock_subsys); + if (ret < 0) { + return ret; + } + + ret = clock_control_get_rate(config->clock_dev, + (clock_control_subsys_t)config->clock_subsys, &data->clk_rate); + if (ret < 0) { + return ret; + } + + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + + ret = uart_rza2m_scif_configure(dev, &data->current_config); + if (ret != 0) { + return ret; + } + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + config->irq_config_func(dev); +#endif + + return 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +static bool uart_rza2m_scif_irq_is_enabled(const struct device *dev, uint32_t irq) +{ + return !!(uart_rza2m_scif_read_16(dev, RZA2M_SCR) & irq); +} + +static int uart_rza2m_scif_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len) +{ + struct uart_rza2m_scif_data *data = dev->data; + int num_tx = 0; + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + while (((len - num_tx) > 0) && (uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_TDFE)) { + /* Send current byte */ + uart_rza2m_scif_write_8(dev, RZA2M_FTDR, tx_data[num_tx]); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FSR); + reg_16 &= ~(RZA2M_FSR_TDFE | RZA2M_FSR_TEND); + uart_rza2m_scif_write_16(dev, RZA2M_FSR, reg_16); + + num_tx++; + } + + k_spin_unlock(&data->lock, key); + + return num_tx; +} + +static int uart_rza2m_scif_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + struct uart_rza2m_scif_data *data = dev->data; + int num_rx = 0; + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + while (((size - num_rx) > 0) && (uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_RDF)) { + /* Receive current byte */ + rx_data[num_rx++] = uart_rza2m_scif_read_8(dev, RZA2M_FRDR); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_FSR); + reg_16 &= ~(RZA2M_FSR_RDF); + uart_rza2m_scif_write_16(dev, RZA2M_FSR, reg_16); + } + + k_spin_unlock(&data->lock, key); + + return num_rx; +} + +static void uart_rza2m_scif_irq_tx_enable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 |= (RZA2M_SCR_TIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static void uart_rza2m_scif_irq_tx_disable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 &= ~(RZA2M_SCR_TIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static int uart_rza2m_scif_irq_tx_ready(const struct device *dev) +{ + return !!(uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_TDFE); +} + +static void uart_rza2m_scif_irq_rx_enable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 |= (RZA2M_SCR_RIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static void uart_rza2m_scif_irq_rx_disable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 &= ~(RZA2M_SCR_RIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static int uart_rza2m_scif_irq_rx_ready(const struct device *dev) +{ + return !!(uart_rza2m_scif_read_16(dev, RZA2M_FSR) & RZA2M_FSR_RDF); +} + +static void uart_rza2m_scif_irq_err_enable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 |= (RZA2M_SCR_REIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static void uart_rza2m_scif_irq_err_disable(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + uint16_t reg_16; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + reg_16 = uart_rza2m_scif_read_16(dev, RZA2M_SCR); + reg_16 &= ~(RZA2M_SCR_REIE); + uart_rza2m_scif_write_16(dev, RZA2M_SCR, reg_16); + + k_spin_unlock(&data->lock, key); +} + +static int uart_rza2m_scif_irq_is_pending(const struct device *dev) +{ + return (uart_rza2m_scif_irq_rx_ready(dev) && + uart_rza2m_scif_irq_is_enabled(dev, RZA2M_SCR_RIE)) || + (uart_rza2m_scif_irq_tx_ready(dev) && + uart_rza2m_scif_irq_is_enabled(dev, RZA2M_SCR_TIE)); +} + +static int uart_rza2m_scif_irq_update(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 1; +} + +static void uart_rza2m_scif_irq_callback_set(const struct device *dev, + uart_irq_callback_user_data_t cb, void *cb_data) +{ + struct uart_rza2m_scif_data *data = dev->data; + + data->irq_cb = cb; + data->irq_cb_data = cb_data; +} + +void uart_rza2m_scif_isr(const struct device *dev) +{ + struct uart_rza2m_scif_data *data = dev->data; + + if (data->irq_cb) { + data->irq_cb(dev, data->irq_cb_data); + } + + /* Get interrupt status */ + data->int_data.rxi_status = uart_rza2m_scif_read_16(dev, RZA2M_FSR); + data->int_data.rxi_status &= (RZA2M_FSR_FER | RZA2M_FSR_PER | RZA2M_FSR_BRK); + data->int_data.line_status = uart_rza2m_scif_read_16(dev, RZA2M_LSR); + data->int_data.line_status &= RZA2M_LSR_ORER; +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static DEVICE_API(uart, uart_rza2m_scif_driver_api) = { + .poll_in = uart_rza2m_scif_poll_in, + .poll_out = uart_rza2m_scif_poll_out, + .err_check = uart_rza2m_scif_err_check, +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + .configure = uart_rza2m_scif_configure, + .config_get = uart_rza2m_scif_config_get, +#endif +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_rza2m_scif_fifo_fill, + .fifo_read = uart_rza2m_scif_fifo_read, + .irq_tx_enable = uart_rza2m_scif_irq_tx_enable, + .irq_tx_disable = uart_rza2m_scif_irq_tx_disable, + .irq_tx_ready = uart_rza2m_scif_irq_tx_ready, + .irq_rx_enable = uart_rza2m_scif_irq_rx_enable, + .irq_rx_disable = uart_rza2m_scif_irq_rx_disable, + .irq_rx_ready = uart_rza2m_scif_irq_rx_ready, + .irq_err_enable = uart_rza2m_scif_irq_err_enable, + .irq_err_disable = uart_rza2m_scif_irq_err_disable, + .irq_is_pending = uart_rza2m_scif_irq_is_pending, + .irq_update = uart_rza2m_scif_irq_update, + .irq_callback_set = uart_rza2m_scif_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +/* Device Instantiation */ +#define UART_RZA2M_DECLARE_CFG(n, IRQ_FUNC_INIT) \ + PINCTRL_DT_INST_DEFINE(n); \ + uint32_t clock_subsys##n = DT_INST_CLOCKS_CELL(n, clk_id); \ + static const struct uart_rza2m_scif_cfg uart_rza2m_scif_cfg_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = (clock_control_subsys_t)(&clock_subsys##n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .params = &port_params, \ + IRQ_FUNC_INIT} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +#define UART_RZA2M_SET_IRQ(n, name) \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, name, irq) - GIC_SPI_INT_BASE, \ + DT_INST_IRQ_BY_NAME(n, name, priority), uart_rza2m_scif_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQ_BY_NAME(n, name, irq) - GIC_SPI_INT_BASE); + +#define UART_RZA2M_IRQ_CONFIG_FUNC(n) \ + static void irq_config_func_##n(const struct device *dev) \ + { \ + UART_RZA2M_SET_IRQ(n, eri); \ + UART_RZA2M_SET_IRQ(n, rxi); \ + UART_RZA2M_SET_IRQ(n, txi); \ + UART_RZA2M_SET_IRQ(n, tei); \ + } +#define UART_RZA2M_IRQ_CFG_FUNC_INIT(n) .irq_config_func = irq_config_func_##n +#define UART_RZA2M_INIT_CFG(n) UART_RZA2M_DECLARE_CFG(n, UART_RZA2M_IRQ_CFG_FUNC_INIT(n)) +#else +#define UART_RZA2M_IRQ_CONFIG_FUNC(n) +#define UART_RZA2M_IRQ_CFG_FUNC_INIT +#define UART_RZA2M_INIT_CFG(n) UART_RZA2M_DECLARE_CFG(n, UART_RZA2M_IRQ_CFG_FUNC_INIT) +#endif + +#define UART_RZA2M_INIT(n) \ + static struct uart_rza2m_scif_data uart_rza2m_scif_data_##n = { \ + .current_config = \ + { \ + .baudrate = DT_INST_PROP_OR(n, current_speed, \ + RZA2M_SCIF_DEFAULT_SPEED), \ + .parity = \ + DT_INST_ENUM_IDX_OR(n, parity, RZA2M_SCIF_DEFAULT_PARITY), \ + .stop_bits = DT_INST_ENUM_IDX_OR(n, stop_bits, \ + RZA2M_SCIF_DEFAULT_STOP_BITS), \ + .data_bits = DT_INST_ENUM_IDX_OR(n, data_bits, \ + RZA2M_SCIF_DEFAULT_DATA_BITS), \ + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \ + }, \ + .channel = DT_INST_PROP(n, channel), \ + }; \ + static const struct uart_rza2m_scif_cfg uart_rza2m_scif_cfg_##n; \ + DEVICE_DT_INST_DEFINE(n, uart_rza2m_scif_init, NULL, &uart_rza2m_scif_data_##n, \ + &uart_rza2m_scif_cfg_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ + &uart_rza2m_scif_driver_api); \ + \ + UART_RZA2M_IRQ_CONFIG_FUNC(n) \ + UART_RZA2M_INIT_CFG(n); + +DT_INST_FOREACH_STATUS_OKAY(UART_RZA2M_INIT) diff --git a/dts/arm/renesas/rz/rza/r7s9210.dtsi b/dts/arm/renesas/rz/rza/r7s9210.dtsi index 6872a5301da..6da1b1e7cae 100644 --- a/dts/arm/renesas/rz/rza/r7s9210.dtsi +++ b/dts/arm/renesas/rz/rza/r7s9210.dtsi @@ -102,5 +102,75 @@ compatible = "renesas,rza2m-pinctrl"; reg = <0xfcffe000 0x1000>; }; + + scif0: serial@e8007000 { + compatible = "renesas,rza2m-scif-uart"; + channel = <0>; + reg = <0xe8007000 0x18>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg RZA2M_CLOCK(RZA2M_MODULE_SCIFA0, RZA2M_CLK_P1C)>; + status = "disabled"; + }; + + scif1: serial@e8007800 { + compatible = "renesas,rza2m-scif-uart"; + channel = <1>; + reg = <0xe8007800 0x18>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg RZA2M_CLOCK(RZA2M_MODULE_SCIFA1, RZA2M_CLK_P1C)>; + status = "disabled"; + }; + + scif2: serial@e8008000 { + compatible = "renesas,rza2m-scif-uart"; + channel = <2>; + reg = <0xe8008000 0x18>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg RZA2M_CLOCK(RZA2M_MODULE_SCIFA2, RZA2M_CLK_P1C)>; + status = "disabled"; + }; + + scif3: serial@e8008800 { + compatible = "renesas,rza2m-scif-uart"; + channel = <3>; + reg = <0xe8008800 0x18>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg RZA2M_CLOCK(RZA2M_MODULE_SCIFA3, RZA2M_CLK_P1C)>; + status = "disabled"; + }; + + scif4: serial@e8009000 { + compatible = "renesas,rza2m-scif-uart"; + channel = <4>; + reg = <0xe8009000 0x18>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg RZA2M_CLOCK(RZA2M_MODULE_SCIFA4, RZA2M_CLK_P1C)>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/serial/renesas,rza2m-scif-uart.yaml b/dts/bindings/serial/renesas,rza2m-scif-uart.yaml new file mode 100644 index 00000000000..c749eb990e5 --- /dev/null +++ b/dts/bindings/serial/renesas,rza2m-scif-uart.yaml @@ -0,0 +1,20 @@ +description: Renesas RZ/A2M UART + +compatible: "renesas,rza2m-scif-uart" + +include: [uart-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + description: | + Clock source for calculating BRR value + + channel: + type: int + required: true