blob: dcf7705b3b1627ea65028a078a943888c878fcac [file]
/*
* SPDX-FileCopyrightText: 2025 Aesc Silicon
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT aesc_uart
#include <errno.h>
#include <ip_identification.h>
#include <soc.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/irq.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(aesc_uart, CONFIG_UART_LOG_LEVEL);
struct uart_aesc_data {
DEVICE_MMIO_RAM;
uintptr_t reg_base;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t irq_cb;
void *irq_cb_data;
#endif
};
struct uart_aesc_config {
DEVICE_MMIO_ROM;
uint64_t sys_clk_freq;
uint32_t current_speed;
const struct pinctrl_dev_config *pcfg;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
void (*irq_config)(const struct device *dev);
#endif
};
#define UART_AESC_DATA_WIDTH 0x00
#define UART_AESC_SAMPLING_SIZES 0x04
#define UART_AESC_FIFO_DEPTHS 0x08
#define UART_AESC_PERMISSIONS 0x0c
#define UART_AESC_READ_WRITE 0x10
#define UART_AESC_FIFO_STATUS 0x14
#define UART_AESC_CLOCK_DIV 0x18
#define UART_AESC_FRAME_CFG 0x1c
#define UART_AESC_TX_TRIGGER 0x20
#define UART_AESC_IP 0x24
#define UART_AESC_IE 0x28
#define UART_AESC_ERROR_PENDING 0x2c
#define UART_AESC_ERROR_ENABLE 0x30
#define DEV_CFG(dev) ((struct uart_aesc_config *)(dev)->config)
#define DEV_DATA(dev) ((struct uart_aesc_data *)(dev)->data)
#define AESC_UART_IRQ_TX_EN BIT(0)
#define AESC_UART_IRQ_RX_EN BIT(1)
#define AESC_UART_IRQ_TX_COMPLETE_EN BIT(2)
#define AESC_UART_FIFO_TX_COUNT_MASK GENMASK(23, 16)
#define AESC_UART_FIFO_RX_COUNT_MASK GENMASK(31, 24)
#define AESC_UART_READ_FIFO_VALID_BIT BIT(16)
#define AESC_UART_ERR_FRAMING BIT(0)
#define AESC_UART_ERR_PARITY BIT(1)
#define AESC_UART_ERR_OVERFLOW BIT(2)
static void uart_aesc_poll_out(const struct device *dev, unsigned char c)
{
struct uart_aesc_data *data = DEV_DATA(dev);
while ((sys_read32(data->reg_base + UART_AESC_FIFO_STATUS) &
AESC_UART_FIFO_TX_COUNT_MASK) == 0) {
/* Wait until transmit fifo is empty */
}
sys_write32(c, data->reg_base + UART_AESC_READ_WRITE);
}
static int uart_aesc_poll_in(const struct device *dev, unsigned char *c)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t val;
val = sys_read32(data->reg_base + UART_AESC_READ_WRITE);
if (val & AESC_UART_READ_FIFO_VALID_BIT) {
*c = val & 0xFF;
return 0;
}
return -1;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_aesc_fifo_fill(const struct device *dev,
const uint8_t *tx_data, int size)
{
struct uart_aesc_data *data = DEV_DATA(dev);
int i = 0;
while (i < size && (sys_read32(data->reg_base + UART_AESC_FIFO_STATUS) &
AESC_UART_FIFO_TX_COUNT_MASK)) {
sys_write32(tx_data[i++], data->reg_base + UART_AESC_READ_WRITE);
}
return i;
}
static int uart_aesc_fifo_read(const struct device *dev, uint8_t *rx_data,
const int size)
{
struct uart_aesc_data *data = DEV_DATA(dev);
int i = 0;
uint32_t val;
while (i < size) {
val = sys_read32(data->reg_base + UART_AESC_READ_WRITE);
if (!(val & AESC_UART_READ_FIFO_VALID_BIT)) {
break;
}
rx_data[i++] = val & 0xFF;
}
return i;
}
static void uart_aesc_irq_tx_enable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t ie = sys_read32(data->reg_base + UART_AESC_IE);
sys_write32(ie | AESC_UART_IRQ_TX_EN, data->reg_base + UART_AESC_IE);
}
static void uart_aesc_irq_tx_disable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t ie = sys_read32(data->reg_base + UART_AESC_IE);
sys_write32(ie & ~AESC_UART_IRQ_TX_EN, data->reg_base + UART_AESC_IE);
}
static int uart_aesc_irq_tx_ready(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
return !!(sys_read32(data->reg_base + UART_AESC_FIFO_STATUS) &
AESC_UART_FIFO_TX_COUNT_MASK);
}
static int uart_aesc_irq_tx_complete(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
return !!(sys_read32(data->reg_base + UART_AESC_IP) &
AESC_UART_IRQ_TX_COMPLETE_EN);
}
static void uart_aesc_irq_rx_enable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t ie = sys_read32(data->reg_base + UART_AESC_IE);
sys_write32(ie | AESC_UART_IRQ_RX_EN, data->reg_base + UART_AESC_IE);
}
static void uart_aesc_irq_rx_disable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t ie = sys_read32(data->reg_base + UART_AESC_IE);
sys_write32(ie & ~AESC_UART_IRQ_RX_EN, data->reg_base + UART_AESC_IE);
}
static int uart_aesc_irq_rx_ready(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
return !!(sys_read32(data->reg_base + UART_AESC_FIFO_STATUS) &
AESC_UART_FIFO_RX_COUNT_MASK);
}
static void uart_aesc_irq_err_enable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t error_enable = sys_read32(data->reg_base + UART_AESC_ERROR_ENABLE);
error_enable |= AESC_UART_ERR_FRAMING | AESC_UART_ERR_PARITY |
AESC_UART_ERR_OVERFLOW;
sys_write32(error_enable, data->reg_base + UART_AESC_ERROR_ENABLE);
}
static void uart_aesc_irq_err_disable(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t error_enable = sys_read32(data->reg_base + UART_AESC_ERROR_ENABLE);
error_enable &= ~(AESC_UART_ERR_FRAMING | AESC_UART_ERR_PARITY |
AESC_UART_ERR_OVERFLOW);
sys_write32(error_enable, data->reg_base + UART_AESC_ERROR_ENABLE);
}
static int uart_aesc_irq_is_pending(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
/* ip already returns pending & masks, so any set bit means active IRQ */
return !!sys_read32(data->reg_base + UART_AESC_IP);
}
static void uart_aesc_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *user_data)
{
struct uart_aesc_data *data = DEV_DATA(dev);
data->irq_cb = cb;
data->irq_cb_data = user_data;
}
static void uart_aesc_isr(const struct device *dev)
{
struct uart_aesc_data *data = DEV_DATA(dev);
uint32_t pending;
uint32_t err_pending;
pending = sys_read32(data->reg_base + UART_AESC_IP);
sys_write32(pending, data->reg_base + UART_AESC_IP);
err_pending = sys_read32(data->reg_base + UART_AESC_ERROR_PENDING);
sys_write32(err_pending, data->reg_base + UART_AESC_ERROR_PENDING);
if (data->irq_cb) {
data->irq_cb(dev, data->irq_cb_data);
}
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static int uart_aesc_init(const struct device *dev)
{
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
const struct uart_aesc_config *cfg = DEV_CFG(dev);
volatile uintptr_t *base_addr =
(volatile uintptr_t *)DEVICE_MMIO_GET(dev);
struct uart_aesc_data *data = DEV_DATA(dev);
int ret;
LOG_DBG("IP core version: %i.%i.%i.",
ip_id_get_major_version(base_addr),
ip_id_get_minor_version(base_addr),
ip_id_get_patchlevel(base_addr)
);
data->reg_base = ip_id_relocate_driver(base_addr);
LOG_DBG("Relocate driver to address 0x%lx.", data->reg_base);
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
LOG_ERR("failed to apply pinctrl");
return ret;
}
sys_write32(cfg->sys_clk_freq / cfg->current_speed / 8,
data->reg_base + UART_AESC_CLOCK_DIV);
sys_write32(7, data->reg_base + UART_AESC_FRAME_CFG);
sys_write32(0, data->reg_base + UART_AESC_TX_TRIGGER);
sys_write32(0, data->reg_base + UART_AESC_IE);
sys_write32(0, data->reg_base + UART_AESC_ERROR_ENABLE);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
cfg->irq_config(dev);
#endif
return 0;
}
static DEVICE_API(uart, uart_aesc_driver_api) = {
.poll_in = uart_aesc_poll_in,
.poll_out = uart_aesc_poll_out,
.err_check = NULL,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_aesc_fifo_fill,
.fifo_read = uart_aesc_fifo_read,
.irq_tx_enable = uart_aesc_irq_tx_enable,
.irq_tx_disable = uart_aesc_irq_tx_disable,
.irq_tx_ready = uart_aesc_irq_tx_ready,
.irq_tx_complete = uart_aesc_irq_tx_complete,
.irq_rx_enable = uart_aesc_irq_rx_enable,
.irq_rx_disable = uart_aesc_irq_rx_disable,
.irq_rx_ready = uart_aesc_irq_rx_ready,
.irq_err_enable = uart_aesc_irq_err_enable,
.irq_err_disable = uart_aesc_irq_err_disable,
.irq_is_pending = uart_aesc_irq_is_pending,
.irq_callback_set = uart_aesc_irq_callback_set,
#endif
};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
#define AESC_UART_IRQ_INIT(no) \
static void uart_aesc_irq_config_##no(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(no), \
DT_INST_IRQ(no, priority), \
uart_aesc_isr, \
DEVICE_DT_INST_GET(no), \
0); \
irq_enable(DT_INST_IRQN(no)); \
}
#define AESC_UART_IRQ_CFG(no) .irq_config = uart_aesc_irq_config_##no,
#else
#define AESC_UART_IRQ_INIT(no)
#define AESC_UART_IRQ_CFG(no)
#endif
#define AESC_UART_INIT(no) \
PINCTRL_DT_INST_DEFINE(no); \
AESC_UART_IRQ_INIT(no) \
static struct uart_aesc_data uart_aesc_dev_data_##no; \
static const struct uart_aesc_config uart_aesc_dev_cfg_##no = { \
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(no)), \
.sys_clk_freq = \
DT_PROP(DT_INST(no, aesc_uart), clock_frequency), \
.current_speed = \
DT_PROP(DT_INST(no, aesc_uart), current_speed), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(no), \
AESC_UART_IRQ_CFG(no) \
}; \
DEVICE_DT_INST_DEFINE(no, \
uart_aesc_init, \
NULL, \
&uart_aesc_dev_data_##no, \
&uart_aesc_dev_cfg_##no, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
(void *)&uart_aesc_driver_api);
DT_INST_FOREACH_STATUS_OKAY(AESC_UART_INIT)