blob: 339b521e71e6d55ab406908ed9d47bb5aa793ecb [file] [log] [blame]
/*
* Copyright (c) 2018 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microsemi_coreuart
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/drivers/uart.h>
/* UART REGISTERS DEFINITIONS */
/* TX register */
#define TXDATA_REG_OFFSET 0x0
#define TXDATA_OFFSET 0x0
#define TXDATA_MASK 0xFF
#define TXDATA_SHIFT 0
/* RX register */
#define RXDATA_REG_OFFSET 0x4
#define RXDATA_OFFSET 0x4
#define RXDATA_MASK 0xFF
#define RXDATA_SHIFT 0
/* Control1 register */
#define CTRL1_REG_OFFSET 0x8
/* Baud value lower 8 bits */
#define CTRL1_BAUDVALUE_OFFSET 0x8
#define CTRL1_BAUDVALUE_MASK 0xFF
#define CTRL1_BAUDVALUE_SHIFT 0
/* Control2 register */
#define CTRL2_REG_OFFSET 0xC
/* Bit length */
#define CTRL2_BIT_LENGTH_OFFSET 0xC
#define CTRL2_BIT_LENGTH_MASK 0x01
#define CTRL2_BIT_LENGTH_SHIFT 0
/* Parity enable */
#define CTRL2_PARITY_EN_OFFSET 0xC
#define CTRL2_PARITY_EN_MASK 0x02
#define CTRL2_PARITY_EN_SHIFT 1
/* Odd/even parity configuration */
#define CTRL2_ODD_EVEN_OFFSET 0xC
#define CTRL2_ODD_EVEN_MASK 0x04
#define CTRL2_ODD_EVEN_SHIFT 2
/* Baud value higher 5 bits */
#define CTRL2_BAUDVALUE_OFFSET 0xC
#define CTRL2_BAUDVALUE_MASK 0xF8
#define CTRL2_BAUDVALUE_SHIFT 3
/* Status register */
#define StatusReg_REG_OFFSET 0x10
#define STATUS_REG_OFFSET 0x10
/* TX ready */
#define STATUS_TXRDY_OFFSET 0x10
#define STATUS_TXRDY_MASK 0x01
#define STATUS_TXRDY_SHIFT 0
/* Receive full - raised even when 1 char arrived */
#define STATUS_RXFULL_OFFSET 0x10
#define STATUS_RXFULL_MASK 0x02
#define STATUS_RXFULL_SHIFT 1
/* Parity error */
#define STATUS_PARITYERR_OFFSET 0x10
#define STATUS_PARITYERR_MASK 0x04
#define STATUS_PARITYERR_SHIFT 2
/* Overflow */
#define STATUS_OVERFLOW_OFFSET 0x10
#define STATUS_OVERFLOW_MASK 0x08
#define STATUS_OVERFLOW_SHIFT 3
/* Frame error */
#define STATUS_FRAMERR_OFFSET 0x10
#define STATUS_FRAMERR_MASK 0x10
#define STATUS_FRAMERR_SHIFT 4
/* Data bits length defines */
#define DATA_7_BITS 0x00
#define DATA_8_BITS 0x01
/* Parity defines */
#define NO_PARITY 0x00
#define EVEN_PARITY 0x02
#define ODD_PARITY 0x06
/* Error Status definitions */
#define UART_PARITY_ERROR 0x01
#define UART_OVERFLOW_ERROR 0x02
#define UART_FRAMING_ERROR 0x04
#define BAUDVALUE_LSB ((uint16_t)(0x00FF))
#define BAUDVALUE_MSB ((uint16_t)(0xFF00))
#define BAUDVALUE_SHIFT ((uint8_t)(5))
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static struct k_thread rx_thread;
static K_KERNEL_STACK_DEFINE(rx_stack, 512);
#endif
struct uart_miv_regs_t {
uint8_t tx;
uint8_t reserved0[3];
uint8_t rx;
uint8_t reserved1[3];
uint8_t ctrlreg1;
uint8_t reserved2[3];
uint8_t ctrlreg2;
uint8_t reserved3[3];
uint8_t status;
};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
typedef void (*irq_cfg_func_t)(const struct device *dev);
#endif
struct uart_miv_device_config {
uint32_t uart_addr;
uint32_t sys_clk_freq;
uint32_t line_config;
uint32_t baud_rate;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
irq_cfg_func_t cfg_func;
#endif
};
struct uart_miv_data {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
const struct device *dev;
uart_irq_callback_user_data_t callback;
void *cb_data;
#endif
};
#define DEV_UART(dev) \
((struct uart_miv_regs_t *) \
((const struct uart_miv_device_config * const)(dev)->config)->uart_addr)
static void uart_miv_poll_out(const struct device *dev,
unsigned char c)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
while (!(uart->status & STATUS_TXRDY_MASK)) {
}
uart->tx = c;
}
static int uart_miv_poll_in(const struct device *dev, unsigned char *c)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
if (uart->status & STATUS_RXFULL_MASK) {
*c = (unsigned char)(uart->rx & RXDATA_MASK);
return 0;
}
return -1;
}
static int uart_miv_err_check(const struct device *dev)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
uint32_t flags = uart->status;
int err = 0;
if (flags & STATUS_PARITYERR_MASK) {
err |= UART_PARITY_ERROR;
}
if (flags & STATUS_OVERFLOW_MASK) {
err |= UART_OVERFLOW_ERROR;
}
if (flags & STATUS_FRAMERR_MASK) {
err |= UART_FRAMING_ERROR;
}
return err;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_miv_fifo_fill(const struct device *dev,
const uint8_t *tx_data,
int size)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
int i;
for (i = 0; i < size && (uart->status & STATUS_TXRDY_MASK); i++) {
uart->tx = tx_data[i];
}
return i;
}
static int uart_miv_fifo_read(const struct device *dev,
uint8_t *rx_data,
const int size)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
int i;
for (i = 0; i < size; i++) {
if (uart->status & STATUS_RXFULL_MASK) {
rx_data[i] = (unsigned char)(uart->rx & RXDATA_MASK);
} else {
break;
}
}
return i;
}
static void uart_miv_irq_tx_enable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void uart_miv_irq_tx_disable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int uart_miv_irq_tx_ready(const struct device *dev)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
return !(uart->status & STATUS_TXRDY_MASK);
}
static int uart_miv_irq_tx_complete(const struct device *dev)
{
ARG_UNUSED(dev);
return 1;
}
static void uart_miv_irq_rx_enable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void uart_miv_irq_rx_disable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int uart_miv_irq_rx_ready(const struct device *dev)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
return !!(uart->status & STATUS_RXFULL_MASK);
}
static void uart_miv_irq_err_enable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void uart_miv_irq_err_disable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int uart_miv_irq_is_pending(const struct device *dev)
{
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
return !!(uart->status & STATUS_RXFULL_MASK);
}
static int uart_miv_irq_update(const struct device *dev)
{
return 1;
}
static void uart_miv_irq_handler(const struct device *dev)
{
struct uart_miv_data *data = dev->data;
if (data->callback) {
data->callback(dev, data->cb_data);
}
}
/*
* This thread is a workaround for IRQs that are not connected in Mi-V.
* Since we cannot rely on IRQs, the rx_thread is working instead and
* polling for data. The thread calls the registered callback when data
* arrives.
*/
void uart_miv_rx_thread(void *arg1, void *arg2, void *arg3)
{
struct uart_miv_data *data = (struct uart_miv_data *)arg1;
const struct device *dev = data->dev;
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
const struct uart_miv_device_config *const cfg = dev->config;
/* Make it go to sleep for a period no longer than
* time to receive next character.
*/
uint32_t delay = 1000000 / cfg->baud_rate;
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
while (1) {
if (uart->status & STATUS_RXFULL_MASK) {
uart_miv_irq_handler(dev);
}
k_sleep(K_USEC(delay));
}
}
static void uart_miv_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *cb_data)
{
struct uart_miv_data *data = dev->data;
data->callback = cb;
data->cb_data = cb_data;
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static int uart_miv_init(const struct device *dev)
{
const struct uart_miv_device_config *const cfg = dev->config;
volatile struct uart_miv_regs_t *uart = DEV_UART(dev);
/* Calculate divider value to set baudrate */
uint16_t baud_value = (cfg->sys_clk_freq / (cfg->baud_rate * 16U)) - 1;
/* Set baud rate */
uart->ctrlreg1 = (uint8_t)(baud_value & BAUDVALUE_LSB);
uart->ctrlreg2 = (uint8_t)(cfg->line_config) |
(uint8_t)((baud_value & BAUDVALUE_MSB) >> BAUDVALUE_SHIFT);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
/* Setup thread polling for data */
cfg->cfg_func(dev);
#endif
return 0;
}
static const struct uart_driver_api uart_miv_driver_api = {
.poll_in = uart_miv_poll_in,
.poll_out = uart_miv_poll_out,
.err_check = uart_miv_err_check,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_miv_fifo_fill,
.fifo_read = uart_miv_fifo_read,
.irq_tx_enable = uart_miv_irq_tx_enable,
.irq_tx_disable = uart_miv_irq_tx_disable,
.irq_tx_ready = uart_miv_irq_tx_ready,
.irq_tx_complete = uart_miv_irq_tx_complete,
.irq_rx_enable = uart_miv_irq_rx_enable,
.irq_rx_disable = uart_miv_irq_rx_disable,
.irq_rx_ready = uart_miv_irq_rx_ready,
.irq_err_enable = uart_miv_irq_err_enable,
.irq_err_disable = uart_miv_irq_err_disable,
.irq_is_pending = uart_miv_irq_is_pending,
.irq_update = uart_miv_irq_update,
.irq_callback_set = uart_miv_irq_callback_set,
#endif
};
/* This driver is single-instance. */
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
"unsupported uart_miv instance");
#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay)
static struct uart_miv_data uart_miv_data_0;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void uart_miv_irq_cfg_func_0(const struct device *dev);
#endif
static const struct uart_miv_device_config uart_miv_dev_cfg_0 = {
.uart_addr = DT_INST_REG_ADDR(0),
.sys_clk_freq = DT_INST_PROP(0, clock_frequency),
.line_config = MIV_UART_0_LINECFG,
.baud_rate = DT_INST_PROP(0, current_speed),
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.cfg_func = uart_miv_irq_cfg_func_0,
#endif
};
DEVICE_DT_INST_DEFINE(0, uart_miv_init, NULL,
&uart_miv_data_0, &uart_miv_dev_cfg_0,
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,
(void *)&uart_miv_driver_api);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void uart_miv_irq_cfg_func_0(const struct device *dev)
{
struct uart_miv_data *data = dev->data;
data->dev = dev;
/* Create a thread which will poll for data - replacement for IRQ */
k_thread_create(&rx_thread, rx_stack, 500,
uart_miv_rx_thread, data, NULL, NULL, K_PRIO_COOP(2),
0, K_NO_WAIT);
}
#endif
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) */