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.
427 lines
10 KiB
427 lines
10 KiB
/* |
|
* Copyright (c) 2018 Antmicro <www.antmicro.com> |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT microchip_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)) |
|
|
|
#define MIV_UART_0_LINECFG 0x1 |
|
|
|
#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 DEVICE_API(uart, 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_OKAY(DT_DRV_INST(0)) |
|
|
|
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_OKAY(DT_DRV_INST(0)) */
|
|
|