blob: 839c8fff596b114acbf6b06f3f2b1f94f9ff3a9c [file] [log] [blame]
/*
* 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)