Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/serial/Kconfig.sf32lb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ config UART_SF32LB
default y
depends on DT_HAS_SIFLI_SF32LB_USART_ENABLED
select SERIAL_HAS_DRIVER
select SERIAL_SUPPORT_INTERRUPT
select PINCTRL
select CLOCK_CONTROL
help
Expand Down
208 changes: 206 additions & 2 deletions drivers/serial/uart_sf32lb.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) Core Devices LLC
* Copyright (c) Qingsong Gou <gouqs@hotmail.com>
* SPDX-License-Identifier: Apache-2.0
*/

Expand All @@ -11,6 +12,7 @@
#include <zephyr/drivers/clock_control/sf32lb.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/irq.h>

#include <register.h>

Expand All @@ -35,13 +37,34 @@
/* minimal BRR: INT=1, FRAC=0 (0x10) */
#define UART_BRR_MIN 0x10U

struct uart_sf32lb_data {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t irq_callback;
void *cb_data;
#endif
};

struct uart_sf32lb_config {
uintptr_t base;
const struct pinctrl_dev_config *pcfg;
struct sf32lb_clock_dt_spec clock;
struct uart_config uart_cfg;
#if CONFIG_UART_INTERRUPT_DRIVEN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if CONFIG_UART_INTERRUPT_DRIVEN
#ifdef CONFIG_UART_INTERRUPT_DRIVEN

void (*irq_config_func)(const struct device *dev);
#endif
};

#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void uart_sf32lb_isr(const struct device *dev)
{
struct uart_sf32lb_data *data = dev->data;

if (data->irq_callback) {
data->irq_callback(dev, data->cb_data);
}
Comment on lines +62 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any flag that has to be cleared, like TCCF?

}
#endif

static int uart_sf32lb_configure(const struct device *dev, const struct uart_config *cfg)
{
const struct uart_sf32lb_config *config = dev->config;
Expand Down Expand Up @@ -177,9 +200,174 @@ static void uart_sf32lb_poll_out(const struct device *dev, uint8_t c)
}
}

static int uart_sf32lb_err_check(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;
uint32_t isr = sys_read32(config->base + UART_ISR);
int err = 0;

if (isr & USART_ISR_ORE) {
err |= UART_ERROR_OVERRUN;
}

if (isr & USART_ISR_PE) {
err |= UART_ERROR_PARITY;
}

if (isr & USART_ISR_FE) {
err |= UART_ERROR_FRAMING;
}

if (isr & USART_ISR_NF) {
err |= UART_ERROR_NOISE;
}

/* clear error flags */
sys_write32(USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_FECF | USART_ICR_NCF,
config->base + UART_ICR);

return err;
}

#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_sf32lb_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
{
const struct uart_sf32lb_config *config = dev->config;
int i;

for (i = 0; i < len; i++) {
if (!sys_test_bit(config->base + UART_ISR, USART_ISR_TXE_Pos)) {
break;
}
sys_write8(tx_data[i], config->base + UART_TDR);
}

return i;
}

static int uart_sf32lb_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
{
const struct uart_sf32lb_config *config = dev->config;
int i;

for (i = 0; i < size; i++) {
if (!sys_test_bit(config->base + UART_ISR, USART_ISR_RXNE_Pos)) {
break;
}
rx_data[i] = sys_read8(config->base + UART_RDR);
}

return i;
}

static void uart_sf32lb_irq_tx_enable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_set_bit(config->base + UART_CR1, USART_CR1_TE_Pos);
}

static void uart_sf32lb_irq_tx_disable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_clear_bit(config->base + UART_CR1, USART_CR1_TE_Pos);
}

static int uart_sf32lb_irq_tx_ready(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

return sys_test_bit(config->base + UART_ISR, USART_ISR_TXE_Pos);
}

static int uart_sf32lb_irq_tx_complete(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

return sys_test_bit(config->base + UART_ISR, USART_ISR_TC_Pos);
}

static int uart_sf32lb_irq_rx_ready(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

return sys_test_bit(config->base + UART_ISR, USART_ISR_RXNE_Pos);
}

static void uart_sf32lb_irq_err_enable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_set_bit(config->base + UART_CR1, USART_CR1_PEIE_Pos);
sys_set_bit(config->base + UART_CR3, USART_CR3_EIE_Pos);
}

static void uart_sf32lb_irq_err_disable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_clear_bit(config->base + UART_CR1, USART_CR1_PEIE_Pos);
sys_clear_bit(config->base + UART_CR3, USART_CR3_EIE_Pos);
}

static int uart_sf32lb_irq_is_pending(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

return sys_read32(config->base + UART_ISR) == 0U ? 0 : 1;
}

static int uart_sf32lb_irq_update(const struct device *dev)
{
ARG_UNUSED(dev);
return 1;
}

static void uart_sf32lb_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
void *user_data)
{
struct uart_sf32lb_data *data = dev->data;

data->irq_callback = cb;
data->cb_data = user_data;
}

static void uart_sf32lb_irq_rx_enable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_set_bit(config->base + UART_CR3, USART_ISR_RXNE);
}

static void uart_sf32lb_irq_rx_disable(const struct device *dev)
{
const struct uart_sf32lb_config *config = dev->config;

sys_clear_bit(config->base + UART_CR3, USART_ISR_RXNE);
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */

static const struct uart_driver_api uart_sf32lb_api = {
.poll_in = uart_sf32lb_poll_in,
.poll_out = uart_sf32lb_poll_out,
.err_check = uart_sf32lb_err_check,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_sf32lb_fifo_fill,
.fifo_read = uart_sf32lb_fifo_read,
.irq_tx_enable = uart_sf32lb_irq_tx_enable,
.irq_tx_disable = uart_sf32lb_irq_tx_disable,
.irq_tx_complete = uart_sf32lb_irq_tx_complete,
.irq_tx_ready = uart_sf32lb_irq_tx_ready,
.irq_rx_enable = uart_sf32lb_irq_rx_enable,
.irq_rx_disable = uart_sf32lb_irq_rx_disable,
.irq_rx_ready = uart_sf32lb_irq_rx_ready,
.irq_err_enable = uart_sf32lb_irq_err_enable,
.irq_err_disable = uart_sf32lb_irq_err_disable,
.irq_is_pending = uart_sf32lb_irq_is_pending,
.irq_update = uart_sf32lb_irq_update,
.irq_callback_set = uart_sf32lb_irq_callback_set,
#endif
};

static int uart_sf32lb_init(const struct device *dev)
Expand Down Expand Up @@ -208,11 +396,22 @@ static int uart_sf32lb_init(const struct device *dev)
return ret;
}

#if CONFIG_UART_INTERRUPT_DRIVEN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if CONFIG_UART_INTERRUPT_DRIVEN
#ifdef CONFIG_UART_INTERRUPT_DRIVEN

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Kconfigs are either defined or not defined)

config->irq_config_func(dev);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */

return 0;
}

#define SF32LB_UART_DEFINE(index) \
PINCTRL_DT_INST_DEFINE(index); \
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \
(static void uart_sf32lb_irq_config_func_##index(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), uart_sf32lb_isr, \
DEVICE_DT_INST_GET(index), 0); \
irq_enable(DT_INST_IRQN(index)); \
})); \
\
static const struct uart_sf32lb_config uart_sf32lb_cfg_##index = { \
.base = DT_INST_REG_ADDR(index), \
Expand All @@ -231,9 +430,14 @@ static int uart_sf32lb_init(const struct device *dev)
? UART_CFG_FLOW_CTRL_RTS_CTS \
: UART_CFG_FLOW_CTRL_NONE, \
}, \
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \
(.irq_config_func = uart_sf32lb_irq_config_func_##index,)) \
}; \
\
DEVICE_DT_INST_DEFINE(index, uart_sf32lb_init, NULL, NULL, &uart_sf32lb_cfg_##index, \
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_sf32lb_api);
static struct uart_sf32lb_data uart_sf32lb_data_##index; \
\
DEVICE_DT_INST_DEFINE(index, uart_sf32lb_init, NULL, \
&uart_sf32lb_data_##index, &uart_sf32lb_cfg_##index, PRE_KERNEL_1, \
CONFIG_SERIAL_INIT_PRIORITY, &uart_sf32lb_api);

DT_INST_FOREACH_STATUS_OKAY(SF32LB_UART_DEFINE)