|  | /* | 
|  | * Copyright (c) Core Devices LLC | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT sifli_sf32lb_usart | 
|  |  | 
|  | #include <zephyr/arch/cpu.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/devicetree.h> | 
|  | #include <zephyr/drivers/clock_control/sf32lb.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/drivers/uart.h> | 
|  |  | 
|  | #include <register.h> | 
|  |  | 
|  | #define UART_CR1   offsetof(USART_TypeDef, CR1) | 
|  | #define UART_CR2   offsetof(USART_TypeDef, CR2) | 
|  | #define UART_CR3   offsetof(USART_TypeDef, CR3) | 
|  | #define UART_BRR   offsetof(USART_TypeDef, BRR) | 
|  | #define UART_ISR   offsetof(USART_TypeDef, ISR) | 
|  | #define UART_ICR   offsetof(USART_TypeDef, ICR) | 
|  | #define UART_RDR   offsetof(USART_TypeDef, RDR) | 
|  | #define UART_TDR   offsetof(USART_TypeDef, TDR) | 
|  | #define UART_MISCR offsetof(USART_TypeDef, MISCR) | 
|  |  | 
|  | #define UART_CR1_M_6B FIELD_PREP(USART_CR1_M_Msk, 0U) | 
|  | #define UART_CR1_M_7B FIELD_PREP(USART_CR1_M_Msk, 1U) | 
|  | #define UART_CR1_M_8B FIELD_PREP(USART_CR1_M_Msk, 2U) | 
|  | #define UART_CR1_M_9B FIELD_PREP(USART_CR1_M_Msk, 3U) | 
|  |  | 
|  | #define UART_CR2_STOP_1B FIELD_PREP(USART_CR2_STOP_Msk, 0U) | 
|  | #define UART_CR2_STOP_2B FIELD_PREP(USART_CR2_STOP_Msk, 1U) | 
|  |  | 
|  | /* minimal BRR: INT=1, FRAC=0 (0x10) */ | 
|  | #define UART_BRR_MIN 0x10U | 
|  |  | 
|  | struct uart_sf32lb_config { | 
|  | uintptr_t base; | 
|  | const struct pinctrl_dev_config *pcfg; | 
|  | struct sf32lb_clock_dt_spec clock; | 
|  | struct uart_config uart_cfg; | 
|  | }; | 
|  |  | 
|  | static int uart_sf32lb_configure(const struct device *dev, const struct uart_config *cfg) | 
|  | { | 
|  | const struct uart_sf32lb_config *config = dev->config; | 
|  | enum uart_config_data_bits data_bits = cfg->data_bits; | 
|  | uint32_t cr1, cr2, cr3, brr, miscr; | 
|  |  | 
|  | /* CR1: disable USART */ | 
|  | cr1 = sys_read32(config->base + UART_CR1); | 
|  | cr1 &= ~USART_CR1_UE; | 
|  | sys_write32(cr1, config->base + UART_CR1); | 
|  |  | 
|  | /* CR1: data bits, parity, oversampling */ | 
|  | cr1 &= ~(USART_CR1_M_Msk | USART_CR1_PCE_Msk | USART_CR1_PS_Msk | USART_CR1_OVER8_Msk); | 
|  |  | 
|  | /* data bits include parity bit */ | 
|  | if (cfg->parity != UART_CFG_PARITY_NONE) { | 
|  | data_bits++; | 
|  | if (data_bits > UART_CFG_DATA_BITS_9) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (data_bits) { | 
|  | case UART_CFG_DATA_BITS_6: | 
|  | cr1 |= UART_CR1_M_6B; | 
|  | break; | 
|  | case UART_CFG_DATA_BITS_7: | 
|  | cr1 |= UART_CR1_M_7B; | 
|  | break; | 
|  | case UART_CFG_DATA_BITS_8: | 
|  | cr1 |= UART_CR1_M_8B; | 
|  | break; | 
|  | case UART_CFG_DATA_BITS_9: | 
|  | cr1 |= UART_CR1_M_9B; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | switch (cfg->parity) { | 
|  | case UART_CFG_PARITY_NONE: | 
|  | break; | 
|  | case UART_CFG_PARITY_ODD: | 
|  | cr1 |= (USART_CR1_PCE | USART_CR1_PS); | 
|  | break; | 
|  | case UART_CFG_PARITY_EVEN: | 
|  | cr1 |= USART_CR1_PCE; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | sys_write32(cr1, config->base + UART_CR1); | 
|  |  | 
|  | /* CR2: stop bits */ | 
|  | cr2 = sys_read32(config->base + UART_CR2); | 
|  | cr2 &= ~USART_CR2_STOP_Msk; | 
|  |  | 
|  | switch (cfg->stop_bits) { | 
|  | case UART_CFG_STOP_BITS_1: | 
|  | cr2 |= UART_CR2_STOP_1B; | 
|  | break; | 
|  | case UART_CFG_STOP_BITS_2: | 
|  | cr2 |= UART_CR2_STOP_2B; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | sys_write32(cr2, config->base + UART_CR2); | 
|  |  | 
|  | /* CR3: flow control */ | 
|  | cr3 = sys_read32(config->base + UART_CR3); | 
|  | cr3 &= ~(USART_CR3_RTSE_Msk | USART_CR3_CTSE_Msk); | 
|  |  | 
|  | switch (cfg->flow_ctrl) { | 
|  | case UART_CFG_FLOW_CTRL_NONE: | 
|  | break; | 
|  | case UART_CFG_FLOW_CTRL_RTS_CTS: | 
|  | cr3 |= (USART_CR3_RTSE_Msk | USART_CR3_CTSE_Msk); | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | sys_write32(cr3, config->base + UART_CR3); | 
|  |  | 
|  | /* enable USART */ | 
|  | cr1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; | 
|  | sys_write32(cr1, config->base + UART_CR1); | 
|  |  | 
|  | /* BRR: baudrate */ | 
|  | miscr = sys_read32(config->base + UART_MISCR); | 
|  | miscr &= ~USART_MISCR_SMPLINI_Msk; | 
|  |  | 
|  | brr = 48000000UL / config->uart_cfg.baudrate; | 
|  | if (brr < UART_BRR_MIN) { | 
|  | cr1 |= USART_CR1_OVER8; | 
|  | sys_write32(cr1, config->base + UART_CR1); | 
|  | /* recalculate brr with reduced oversampling */ | 
|  | brr = (48000000UL * 2U) / config->uart_cfg.baudrate; | 
|  | miscr |= FIELD_PREP(USART_MISCR_SMPLINI_Msk, 2U); | 
|  | } else { | 
|  | miscr |= FIELD_PREP(USART_MISCR_SMPLINI_Msk, 6U); | 
|  | } | 
|  |  | 
|  | sys_write32(miscr, config->base + UART_MISCR); | 
|  | sys_write32(brr, config->base + UART_BRR); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int uart_sf32lb_poll_in(const struct device *dev, uint8_t *c) | 
|  | { | 
|  | const struct uart_sf32lb_config *config = dev->config; | 
|  |  | 
|  | if ((sys_read32(config->base + UART_ISR) & USART_ISR_RXNE) != 0U) { | 
|  | *c = sys_read32(config->base + UART_RDR) & 0xFFU; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void uart_sf32lb_poll_out(const struct device *dev, uint8_t c) | 
|  | { | 
|  | const struct uart_sf32lb_config *config = dev->config; | 
|  |  | 
|  | sys_write32(USART_ISR_TC, config->base + UART_ICR); | 
|  | sys_write8(c, config->base + UART_TDR); | 
|  |  | 
|  | while ((sys_read32(config->base + UART_ISR) & USART_ISR_TC) == 0U) { | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct uart_driver_api uart_sf32lb_api = { | 
|  | .poll_in = uart_sf32lb_poll_in, | 
|  | .poll_out = uart_sf32lb_poll_out, | 
|  | }; | 
|  |  | 
|  | static int uart_sf32lb_init(const struct device *dev) | 
|  | { | 
|  | const struct uart_sf32lb_config *config = dev->config; | 
|  | int ret; | 
|  |  | 
|  | ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (config->clock.dev != NULL) { | 
|  | if (!sf3232lb_clock_is_ready_dt(&config->clock)) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | ret = sf32lb_clock_control_on_dt(&config->clock); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | ret = uart_sf32lb_configure(dev, &config->uart_cfg); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define SF32LB_UART_DEFINE(index)                                                                  \ | 
|  | PINCTRL_DT_INST_DEFINE(index);                                                             \ | 
|  | \ | 
|  | static const struct uart_sf32lb_config uart_sf32lb_cfg_##index = {                         \ | 
|  | .base = DT_INST_REG_ADDR(index),                                                   \ | 
|  | .clock = SF32LB_CLOCK_DT_INST_SPEC_GET_OR(index, {}),                              \ | 
|  | .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index),                                     \ | 
|  | .uart_cfg =                                                                        \ | 
|  | {                                                                          \ | 
|  | .baudrate = DT_INST_PROP(index, current_speed),                    \ | 
|  | .parity =                                                          \ | 
|  | DT_INST_ENUM_IDX_OR(index, parity, UART_CFG_PARITY_NONE),  \ | 
|  | .stop_bits = DT_INST_ENUM_IDX_OR(index, stop_bits,                 \ | 
|  | UART_CFG_STOP_BITS_1),            \ | 
|  | .data_bits = DT_INST_ENUM_IDX_OR(index, data_bits,                 \ | 
|  | UART_CFG_DATA_BITS_8),            \ | 
|  | .flow_ctrl = DT_INST_PROP(index, hw_flow_control)                  \ | 
|  | ? UART_CFG_FLOW_CTRL_RTS_CTS                  \ | 
|  | : UART_CFG_FLOW_CTRL_NONE,                    \ | 
|  | },                                                                         \ | 
|  | };                                                                                         \ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(index, uart_sf32lb_init, NULL, NULL, &uart_sf32lb_cfg_##index,       \ | 
|  | PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_sf32lb_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(SF32LB_UART_DEFINE) |