blob: e9cae52bac8c0ef7d03fbedae9a327fcb2a66912 [file] [log] [blame]
/*
* Copyright (c) 2021 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/pm/device.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(uart_neorv32, CONFIG_UART_LOG_LEVEL);
/* NEORV32 UART registers offsets */
#define NEORV32_UART_CTRL_OFFSET 0x00
#define NEORV32_UART_DATA_OFFSET 0x04
/* UART_CTRL register bits */
#define NEORV32_UART_CTRL_BAUD_MASK BIT_MASK(12)
#define NEORV32_UART_CTRL_BAUD_POS 0U
#define NEORV32_UART_CTRL_PRSC_MASK BIT_MASK(3)
#define NEORV32_UART_CTRL_PRSC_POS 24U
#define NEORV32_UART_CTRL_RTS_EN BIT(20)
#define NEORV32_UART_CTRL_CTS_EN BIT(21)
#define NEORV32_UART_CTRL_PMODE_NONE BIT(22)
#define NEORV32_UART_CTRL_PMODE_EVEN BIT(23)
#define NEORV32_UART_CTRL_PMODE_ODD (BIT(22) | BIT(23))
#define NEORV32_UART_CTRL_EN BIT(28)
#define NEORV32_UART_CTRL_TX_BUSY BIT(31)
/* UART_DATA register status bits */
#define NEORV32_UART_DATA_PERR BIT(28)
#define NEORV32_UART_DATA_FERR BIT(29)
#define NEORV32_UART_DATA_OVERR BIT(30)
#define NEORV32_UART_DATA_AVAIL BIT(31)
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);
unsigned int tx_irq;
unsigned int rx_irq;
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
struct neorv32_uart_data {
struct uart_config uart_cfg;
uint32_t last_data;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
struct k_timer timer;
uart_irq_callback_user_data_t callback;
void *callback_data;
#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_OFFSET);
}
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_OFFSET);
}
static inline uint32_t neorv32_uart_read_data(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
uint32_t reg;
/* Cache status bits as they are cleared upon read */
reg = sys_read32(config->base + NEORV32_UART_DATA_OFFSET);
data->last_data = reg;
return reg;
}
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_OFFSET);
}
static int neorv32_uart_poll_in(const struct device *dev, unsigned char *c)
{
uint32_t data;
data = neorv32_uart_read_data(dev);
if ((data & NEORV32_UART_DATA_AVAIL) != 0) {
*c = data & BIT_MASK(8);
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_BUSY) != 0) {
}
neorv32_uart_write_data(dev, c);
}
static int neorv32_uart_err_check(const struct device *dev)
{
struct neorv32_uart_data *data = dev->data;
int err = 0;
if ((data->last_data & NEORV32_UART_DATA_OVERR) != 0) {
err |= UART_ERROR_OVERRUN;
}
if ((data->last_data & NEORV32_UART_DATA_PERR) != 0) {
err |= UART_ERROR_PARITY;
}
if ((data->last_data & NEORV32_UART_DATA_FERR) != 0) {
err |= UART_ERROR_FRAMING;
}
data->last_data &= ~(NEORV32_UART_DATA_OVERR | NEORV32_UART_DATA_PERR |
NEORV32_UART_DATA_FERR);
return err;
}
static int neorv32_uart_configure(const struct device *dev, const struct uart_config *cfg)
{
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
uint32_t ctrl = NEORV32_UART_CTRL_EN;
uint16_t baudxx = 0;
uint8_t prscx = 0;
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;
}
switch (cfg->parity) {
case UART_CFG_PARITY_NONE:
ctrl |= NEORV32_UART_CTRL_PMODE_NONE;
break;
case UART_CFG_PARITY_ODD:
ctrl |= NEORV32_UART_CTRL_PMODE_ODD;
break;
case UART_CFG_PARITY_EVEN:
ctrl |= NEORV32_UART_CTRL_PMODE_EVEN;
break;
default:
LOG_ERR("unsupported parity mode %d", cfg->parity);
return -ENOTSUP;
}
switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE:
ctrl |= 0;
break;
case UART_CFG_FLOW_CTRL_RTS_CTS:
ctrl |= NEORV32_UART_CTRL_RTS_EN | NEORV32_UART_CTRL_CTS_EN;
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 >= NEORV32_UART_CTRL_BAUD_MASK) {
if ((prscx == 2) || (prscx == 4)) {
baudxx >>= 3;
} else {
baudxx >>= 1;
}
prscx++;
}
if (prscx > NEORV32_UART_CTRL_PRSC_MASK) {
LOG_ERR("unsupported baud rate %d", cfg->baudrate);
return -ENOTSUP;
}
ctrl |= (baudxx - 1) << NEORV32_UART_CTRL_BAUD_POS;
ctrl |= prscx << NEORV32_UART_CTRL_PRSC_POS;
data->uart_cfg = *cfg;
neorv32_uart_write_ctrl(dev, ctrl);
return 0;
}
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;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int neorv32_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
{
uint32_t ctrl;
if (len <= 0) {
return 0;
}
__ASSERT_NO_MSG(tx_data != NULL);
ctrl = neorv32_uart_read_ctrl(dev);
if ((ctrl & NEORV32_UART_CTRL_TX_BUSY) == 0) {
neorv32_uart_write_data(dev, *tx_data);
return 1;
}
return 0;
}
static int neorv32_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
{
struct neorv32_uart_data *data = dev->data;
int count = 0;
if (size <= 0) {
return 0;
}
__ASSERT_NO_MSG(rx_data != NULL);
while ((data->last_data & NEORV32_UART_DATA_AVAIL) != 0) {
rx_data[count++] = data->last_data & BIT_MASK(8);
data->last_data &= ~(NEORV32_UART_DATA_AVAIL);
if (count >= size) {
break;
}
(void)neorv32_uart_read_data(dev);
}
return count;
}
static void neorv32_uart_tx_soft_isr(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(timer);
struct neorv32_uart_data *data = dev->data;
uart_irq_callback_user_data_t callback = data->callback;
if (callback) {
callback(dev, data->callback_data);
}
}
static void neorv32_uart_irq_tx_enable(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
uint32_t ctrl;
irq_enable(config->tx_irq);
ctrl = neorv32_uart_read_ctrl(dev);
if ((ctrl & NEORV32_UART_CTRL_TX_BUSY) == 0) {
/*
* TX done event already generated an edge interrupt. Generate a
* soft interrupt and have it call the callback function in
* timer isr context.
*/
k_timer_start(&data->timer, K_NO_WAIT, K_NO_WAIT);
}
}
static void neorv32_uart_irq_tx_disable(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
irq_disable(config->tx_irq);
}
static int neorv32_uart_irq_tx_ready(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
uint32_t ctrl;
if (!irq_is_enabled(config->tx_irq)) {
return 0;
}
ctrl = neorv32_uart_read_ctrl(dev);
return (ctrl & NEORV32_UART_CTRL_TX_BUSY) == 0;
}
static void neorv32_uart_irq_rx_enable(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
irq_enable(config->rx_irq);
}
static void neorv32_uart_irq_rx_disable(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
irq_disable(config->rx_irq);
}
static int neorv32_uart_irq_tx_complete(const struct device *dev)
{
uint32_t ctrl;
ctrl = neorv32_uart_read_ctrl(dev);
return (ctrl & NEORV32_UART_CTRL_TX_BUSY) == 0;
}
static int neorv32_uart_irq_rx_ready(const struct device *dev)
{
const struct neorv32_uart_config *config = dev->config;
struct neorv32_uart_data *data = dev->data;
if (!irq_is_enabled(config->rx_irq)) {
return 0;
}
return (data->last_data & NEORV32_UART_DATA_AVAIL) != 0;
}
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)
{
const struct neorv32_uart_config *config = dev->config;
if (irq_is_enabled(config->rx_irq)) {
/* Cache data for use by rx_ready() and fifo_read() */
(void)neorv32_uart_read_data(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_FEATURES, &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
k_timer_init(&data->timer, &neorv32_uart_tx_soft_isr, NULL);
k_timer_user_data_set(&data->timer, (void *)dev);
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)
{
uint32_t 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);
return 0;
}
#endif /* CONFIG_PM_DEVICE */
static const struct uart_driver_api neorv32_uart_driver_api = {
.poll_in = neorv32_uart_poll_in,
.poll_out = neorv32_uart_poll_out,
.err_check = neorv32_uart_err_check,
.configure = neorv32_uart_configure,
.config_get = neorv32_uart_config_get,
#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_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); \
}
#define NEORV32_UART_CONFIG_INIT(node_id, n) \
.irq_config_func = neorv32_uart_config_func_##n, \
.tx_irq = DT_IRQ_BY_NAME(node_id, tx, irq), \
.rx_irq = DT_IRQ_BY_NAME(node_id, rx, irq),
#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_OR(node_id, parity, \
UART_CFG_PARITY_NONE), \
.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_FEATURES_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