Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

538 lines
14 KiB

/*
* Copyright (c) 2021,2025 Henrik Brix Andersen <henrik@brixandersen.dk>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT neorv32_uart
#include <zephyr/device.h>
#include <zephyr/drivers/syscon.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/spinlock.h>
#include <zephyr/sys/sys_io.h>
#include <soc.h>
LOG_MODULE_REGISTER(uart_neorv32, CONFIG_UART_LOG_LEVEL);
/* NEORV32 UART registers offsets */
#define NEORV32_UART_CTRL 0x00
#define NEORV32_UART_DATA 0x04
/* NEORV32 UART CTRL register bits */
#define NEORV32_UART_CTRL_EN BIT(0)
#define NEORV32_UART_CTRL_SIM_MODE BIT(1)
#define NEORV32_UART_CTRL_HWFC_EN BIT(2)
#define NEORV32_UART_CTRL_PRSC GENMASK(5, 3)
#define NEORV32_UART_CTRL_BAUD GENMASK(15, 6)
#define NEORV32_UART_CTRL_RX_NEMPTY BIT(16)
#define NEORV32_UART_CTRL_RX_HALF BIT(17)
#define NEORV32_UART_CTRL_RX_FULL BIT(18)
#define NEORV32_UART_CTRL_TX_EMPTY BIT(19)
#define NEORV32_UART_CTRL_TX_NHALF BIT(20)
#define NEORV32_UART_CTRL_TX_FULL BIT(21)
#define NEORV32_UART_CTRL_IRQ_RX_NEMPTY BIT(22)
#define NEORV32_UART_CTRL_IRQ_RX_HALF BIT(23)
#define NEORV32_UART_CTRL_IRQ_RX_FULL BIT(24)
#define NEORV32_UART_CTRL_IRQ_TX_EMPTY BIT(25)
#define NEORV32_UART_CTRL_IRQ_TX_NHALF BIT(26)
#define NEORV32_UART_CTRL_UART_CTRL_RX_CLR BIT(28)
#define NEORV32_UART_CTRL_UART_CTRL_TX_CLR BIT(29)
#define NEORV32_UART_CTRL_RX_OVER BIT(30)
#define NEORV32_UART_CTRL_TX_BUSY BIT(31)
/* NEORV32 UART DATA register bits */
#define NEORV32_UART_DATA_RTX GENMASK(7, 0)
#define NEORV32_UART_DATA_RX_FIFO_SIZE GENMASK(11, 8)
#define NEORV32_UART_DATA_TX_FIFO_SIZE GENMASK(15, 12)
struct neorv32_uart_config {
const struct device *syscon;
uint32_t feature_mask;
mm_reg_t base;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
void (*irq_config_func)(const struct device *dev);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
struct neorv32_uart_data {
struct uart_config uart_cfg;
struct k_spinlock lock;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t callback;
void *callback_data;
uint32_t last_ctrl;
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
static inline uint32_t neorv32_uart_read_ctrl(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
return sys_read32(config->base + NEORV32_UART_CTRL);
}
static inline void neorv32_uart_write_ctrl(const struct device *dev, uint32_t ctrl)
{
const struct neorv32_uart_config *config = dev->config;
sys_write32(ctrl, config->base + NEORV32_UART_CTRL);
}
static inline uint32_t neorv32_uart_read_data(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
return sys_read32(config->base + NEORV32_UART_DATA);
}
static inline void neorv32_uart_write_data(const struct device *dev, uint32_t data)
{
const struct neorv32_uart_config *config = dev->config;
sys_write32(data, config->base + NEORV32_UART_DATA);
}
static int neorv32_uart_poll_in(const struct device *dev, unsigned char *c)
{
uint32_t ctrl;
ctrl = neorv32_uart_read_ctrl(dev);
if ((ctrl & NEORV32_UART_CTRL_RX_NEMPTY) != 0U) {
*c = neorv32_uart_read_data(dev) & NEORV32_UART_DATA_RTX;
return 0;
}
return -1;
}
static void neorv32_uart_poll_out(const struct device *dev, unsigned char c)
{
while ((neorv32_uart_read_ctrl(dev) & NEORV32_UART_CTRL_TX_FULL) != 0U) {
}
neorv32_uart_write_data(dev, c);
}
static int neorv32_uart_configure(const struct device *dev, const struct uart_config *cfg)
{
const uint16_t baudxx_max = FIELD_GET(NEORV32_UART_CTRL_BAUD, NEORV32_UART_CTRL_BAUD);
const uint8_t prscx_max = FIELD_GET(NEORV32_UART_CTRL_PRSC, NEORV32_UART_CTRL_PRSC);
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint16_t baudxx = 0;
uint8_t prscx = 0;
uint32_t ctrl;
bool hwfc_en;
uint32_t clk;
int err;
__ASSERT_NO_MSG(cfg != NULL);
if (cfg->stop_bits != UART_CFG_STOP_BITS_1) {
LOG_ERR("hardware only supports one stop bit");
return -ENOTSUP;
}
if (cfg->data_bits != UART_CFG_DATA_BITS_8) {
LOG_ERR("hardware only supports 8 data bits");
return -ENOTSUP;
}
if (cfg->parity != UART_CFG_PARITY_NONE) {
LOG_ERR("hardware only supports parity mode none");
return -ENOTSUP;
}
switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE:
hwfc_en = false;
break;
case UART_CFG_FLOW_CTRL_RTS_CTS:
hwfc_en = true;
break;
default:
LOG_ERR("unsupported flow control mode %d", cfg->flow_ctrl);
return -ENOTSUP;
}
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_CLK, &clk);
if (err < 0) {
LOG_ERR("failed to determine clock rate (err %d)", err);
return -EIO;
}
if (cfg->baudrate == 0) {
LOG_ERR("invalid baud rate 0");
return -EINVAL;
}
/*
* Calculate clock prescaler and baud prescaler. Initial prscx = 0 is
* clock / 2.
*/
baudxx = clk / (2 * cfg->baudrate);
while (baudxx >= baudxx_max) {
if ((prscx == 2) || (prscx == 4)) {
baudxx >>= 3;
} else {
baudxx >>= 1;
}
prscx++;
}
if (prscx > prscx_max) {
LOG_ERR("unsupported baud rate %d", cfg->baudrate);
return -ENOTSUP;
}
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
ctrl |= NEORV32_UART_CTRL_EN;
if (hwfc_en) {
ctrl |= NEORV32_UART_CTRL_HWFC_EN;
} else {
ctrl &= ~NEORV32_UART_CTRL_HWFC_EN;
}
ctrl &= ~(NEORV32_UART_CTRL_BAUD | NEORV32_UART_CTRL_PRSC);
ctrl |= FIELD_PREP(NEORV32_UART_CTRL_BAUD, baudxx - 1U) |
FIELD_PREP(NEORV32_UART_CTRL_PRSC, prscx);
neorv32_uart_write_ctrl(dev, ctrl);
data->uart_cfg = *cfg;
k_spin_unlock(&data->lock, key);
return 0;
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
static int neorv32_uart_config_get(const struct device *dev, struct uart_config *cfg)
{
struct neorv32_uart_data *data = dev->data;
__ASSERT_NO_MSG(cfg != NULL);
*cfg = data->uart_cfg;
return 0;
}
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int neorv32_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
{
int count = 0;
if (size <= 0) {
return 0;
}
__ASSERT_NO_MSG(tx_data != NULL);
while (count < size && (neorv32_uart_read_ctrl(dev) & NEORV32_UART_CTRL_TX_FULL) == 0U) {
neorv32_uart_write_data(dev, tx_data[count++]);
}
return count;
}
static int neorv32_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
{
int count = 0;
if (size <= 0) {
return 0;
}
__ASSERT_NO_MSG(rx_data != NULL);
while (count < size && (neorv32_uart_read_ctrl(dev) & NEORV32_UART_CTRL_RX_NEMPTY) != 0U) {
rx_data[count++] = neorv32_uart_read_data(dev) & NEORV32_UART_DATA_RTX;
}
return count;
}
static void neorv32_uart_irq_tx_enable(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint32_t ctrl;
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
ctrl |= NEORV32_UART_CTRL_IRQ_TX_EMPTY;
neorv32_uart_write_ctrl(dev, ctrl);
k_spin_unlock(&data->lock, key);
}
static void neorv32_uart_irq_tx_disable(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint32_t ctrl;
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
ctrl &= ~NEORV32_UART_CTRL_IRQ_TX_EMPTY;
neorv32_uart_write_ctrl(dev, ctrl);
k_spin_unlock(&data->lock, key);
}
static int neorv32_uart_irq_tx_ready(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
if ((data->last_ctrl & NEORV32_UART_CTRL_IRQ_TX_EMPTY) == 0U) {
return 0;
}
return (data->last_ctrl & NEORV32_UART_CTRL_TX_FULL) == 0U;
}
static void neorv32_uart_irq_rx_enable(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint32_t ctrl;
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
ctrl |= NEORV32_UART_CTRL_IRQ_RX_NEMPTY;
neorv32_uart_write_ctrl(dev, ctrl);
k_spin_unlock(&data->lock, key);
}
static void neorv32_uart_irq_rx_disable(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint32_t ctrl;
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
ctrl &= ~NEORV32_UART_CTRL_IRQ_RX_NEMPTY;
neorv32_uart_write_ctrl(dev, ctrl);
k_spin_unlock(&data->lock, key);
}
static int neorv32_uart_irq_tx_complete(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
return (data->last_ctrl & NEORV32_UART_CTRL_TX_BUSY) == 0U;
}
static int neorv32_uart_irq_rx_ready(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
if ((data->last_ctrl & NEORV32_UART_CTRL_IRQ_RX_NEMPTY) == 0U) {
return 0;
}
return (data->last_ctrl & NEORV32_UART_CTRL_RX_NEMPTY) != 0U;
}
static int neorv32_uart_irq_is_pending(const struct device *dev)
{
return (neorv32_uart_irq_tx_ready(dev) ||
neorv32_uart_irq_rx_ready(dev));
}
static int neorv32_uart_irq_update(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
/* Cache the CTRL register for use in the following functions:
* - neorv32_uart_irq_tx_complete()
* - neorv32_uart_irq_tx_ready()
* - neorv32_uart_irq_rx_ready()
*/
data->last_ctrl = neorv32_uart_read_ctrl(dev);
return 1;
}
static void neorv32_uart_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb, void *user_data)
{
struct neorv32_uart_data *data = dev->data;
data->callback = cb;
data->callback_data = user_data;
}
static void neorv32_uart_isr(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
uart_irq_callback_user_data_t callback = data->callback;
if (callback) {
callback(dev, data->callback_data);
}
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static int neorv32_uart_init(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
uint32_t features;
int err;
if (!device_is_ready(config->syscon)) {
LOG_ERR("syscon device not ready");
return -EINVAL;
}
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_SOC, &features);
if (err < 0) {
LOG_ERR("failed to determine implemented features (err %d)", err);
return -EIO;
}
if ((features & config->feature_mask) == 0) {
LOG_ERR("neorv32 uart instance not supported");
return -ENODEV;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
config->irq_config_func(dev);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
return neorv32_uart_configure(dev, &data->uart_cfg);
}
#ifdef CONFIG_PM_DEVICE
static int neorv32_uart_pm_action(const struct device *dev,
enum pm_device_action action)
{
struct neorv32_uart_data *data = dev->data;
k_spinlock_key_t key;
uint32_t ctrl;
key = k_spin_lock(&data->lock);
ctrl = neorv32_uart_read_ctrl(dev);
switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
ctrl &= ~(NEORV32_UART_CTRL_EN);
break;
case PM_DEVICE_ACTION_RESUME:
ctrl |= NEORV32_UART_CTRL_EN;
break;
default:
return -ENOTSUP;
}
neorv32_uart_write_ctrl(dev, ctrl);
k_spin_unlock(&data->lock, key);
return 0;
}
#endif /* CONFIG_PM_DEVICE */
static DEVICE_API(uart, neorv32_uart_driver_api) = {
.poll_in = neorv32_uart_poll_in,
.poll_out = neorv32_uart_poll_out,
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
.configure = neorv32_uart_configure,
.config_get = neorv32_uart_config_get,
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = neorv32_uart_fifo_fill,
.fifo_read = neorv32_uart_fifo_read,
.irq_tx_enable = neorv32_uart_irq_tx_enable,
.irq_tx_disable = neorv32_uart_irq_tx_disable,
.irq_tx_ready = neorv32_uart_irq_tx_ready,
.irq_rx_enable = neorv32_uart_irq_rx_enable,
.irq_rx_disable = neorv32_uart_irq_rx_disable,
.irq_tx_complete = neorv32_uart_irq_tx_complete,
.irq_rx_ready = neorv32_uart_irq_rx_ready,
.irq_is_pending = neorv32_uart_irq_is_pending,
.irq_update = neorv32_uart_irq_update,
.irq_callback_set = neorv32_uart_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
#define NEORV32_UART_CONFIG_FUNC(node_id, n) \
static void neorv32_uart_config_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_IRQ_BY_NAME(node_id, tx, irq), \
DT_IRQ_BY_NAME(node_id, tx, priority), \
neorv32_uart_isr, \
DEVICE_DT_GET(node_id), 0); \
irq_enable(DT_IRQ_BY_NAME(node_id, tx, irq)); \
\
IRQ_CONNECT(DT_IRQ_BY_NAME(node_id, rx, irq), \
DT_IRQ_BY_NAME(node_id, rx, priority), \
neorv32_uart_isr, \
DEVICE_DT_GET(node_id), 0); \
irq_enable(DT_IRQ_BY_NAME(node_id, rx, irq)); \
}
#define NEORV32_UART_CONFIG_INIT(node_id, n) \
.irq_config_func = neorv32_uart_config_func_##n,
#else
#define NEORV32_UART_CONFIG_FUNC(node_id, n)
#define NEORV32_UART_CONFIG_INIT(node_id, n)
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#define NEORV32_UART_INIT(node_id, n) \
NEORV32_UART_CONFIG_FUNC(node_id, n) \
\
static struct neorv32_uart_data neorv32_uart_##n##_data = { \
.uart_cfg = { \
.baudrate = DT_PROP(node_id, current_speed), \
.parity = DT_ENUM_IDX(node_id, parity), \
.stop_bits = UART_CFG_STOP_BITS_1, \
.data_bits = UART_CFG_DATA_BITS_8, \
.flow_ctrl = DT_PROP(node_id, hw_flow_control) ? \
UART_CFG_FLOW_CTRL_RTS_CTS : \
UART_CFG_FLOW_CTRL_NONE, \
}, \
}; \
\
static const struct neorv32_uart_config neorv32_uart_##n##_config = { \
.syscon = DEVICE_DT_GET(DT_PHANDLE(node_id, syscon)), \
.feature_mask = NEORV32_SYSINFO_SOC_IO_UART##n, \
.base = DT_REG_ADDR(node_id), \
NEORV32_UART_CONFIG_INIT(node_id, n) \
}; \
\
PM_DEVICE_DT_DEFINE(node_id, neorv32_uart_pm_action); \
\
DEVICE_DT_DEFINE(node_id, &neorv32_uart_init, \
PM_DEVICE_DT_GET(node_id), \
&neorv32_uart_##n##_data, \
&neorv32_uart_##n##_config, \
PRE_KERNEL_1, \
CONFIG_SERIAL_INIT_PRIORITY, \
&neorv32_uart_driver_api)
#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(uart0), DT_DRV_COMPAT, okay)
NEORV32_UART_INIT(DT_NODELABEL(uart0), 0);
#endif
#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(uart1), DT_DRV_COMPAT, okay)
NEORV32_UART_INIT(DT_NODELABEL(uart1), 1);
#endif