b1a0d0ae86
Some files were using the Kernel APIs/types without including zephyr/kernel.h. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
528 lines
14 KiB
C
528 lines
14 KiB
C
/*
|
|
* Copyright (c) 2022 Renesas Electronics Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_smartbond_uart
|
|
|
|
#include <errno.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/spinlock.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <DA1469xAB.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
#define IIR_NO_INTR 1
|
|
#define IIR_THR_EMPTY 2
|
|
#define IIR_RX_DATA 4
|
|
#define IIR_LINE_STATUS 5
|
|
#define IIR_BUSY 7
|
|
#define IIR_TIMEOUT 12
|
|
|
|
#define STOP_BITS_1 0
|
|
#define STOP_BITS_2 1
|
|
|
|
#define DATA_BITS_5 0
|
|
#define DATA_BITS_6 1
|
|
#define DATA_BITS_7 2
|
|
#define DATA_BITS_8 3
|
|
|
|
#define RX_FIFO_TRIG_1_CHAR 0
|
|
#define RX_FIFO_TRIG_1_4_FULL 1
|
|
#define RX_FIFO_TRIG_1_2_FULL 2
|
|
#define RX_FIFO_TRIG_MINUS_2_CHARS 3
|
|
|
|
#define TX_FIFO_TRIG_EMPTY 0
|
|
#define TX_FIFO_TRIG_2_CHARS 1
|
|
#define TX_FIFO_TRIG_1_4_FULL 2
|
|
#define TX_FIFO_TRIG_1_2_FULL 3
|
|
|
|
#define BAUDRATE_CFG_DLH(cfg) (((cfg) >> 16) & 0xff)
|
|
#define BAUDRATE_CFG_DLL(cfg) (((cfg) >> 8) & 0xff)
|
|
#define BAUDRATE_CFG_DLF(cfg) ((cfg) & 0xff)
|
|
|
|
struct uart_smartbond_baudrate_cfg {
|
|
uint32_t baudrate;
|
|
/* DLH=cfg[23:16] DLL=cfg[15:8] DLF=cfg[7:0] */
|
|
uint32_t cfg;
|
|
};
|
|
|
|
static const struct uart_smartbond_baudrate_cfg uart_smartbond_baudrate_table[] = {
|
|
{ 2000000, 0x00000100 },
|
|
{ 1000000, 0x00000200 },
|
|
{ 921600, 0x00000203 },
|
|
{ 500000, 0x00000400 },
|
|
{ 230400, 0x0000080b },
|
|
{ 115200, 0x00001106 },
|
|
{ 57600, 0x0000220c },
|
|
{ 38400, 0x00003401 },
|
|
{ 28800, 0x00004507 },
|
|
{ 19200, 0x00006803 },
|
|
{ 14400, 0x00008a0e },
|
|
{ 9600, 0x0000d005 },
|
|
{ 4800, 0x0001a00b },
|
|
};
|
|
|
|
struct uart_smartbond_cfg {
|
|
UART2_Type *regs;
|
|
int periph_clock_config;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
bool hw_flow_control_supported;
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
void (*irq_config_func)(const struct device *dev);
|
|
#endif
|
|
};
|
|
|
|
struct uart_smartbond_data {
|
|
struct uart_config current_config;
|
|
struct k_spinlock lock;
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
uart_irq_callback_user_data_t callback;
|
|
void *cb_data;
|
|
uint32_t flags;
|
|
uint8_t rx_enabled;
|
|
uint8_t tx_enabled;
|
|
#endif
|
|
};
|
|
|
|
static int uart_smartbond_poll_in(const struct device *dev, unsigned char *p_char)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
if ((config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk) == 0) {
|
|
k_spin_unlock(&data->lock, key);
|
|
return -1;
|
|
}
|
|
|
|
*p_char = config->regs->UART2_RBR_THR_DLL_REG;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void uart_smartbond_poll_out(const struct device *dev, unsigned char out_char)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
while (!(config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk)) {
|
|
/* Wait until FIFO has free space */
|
|
}
|
|
|
|
config->regs->UART2_RBR_THR_DLL_REG = out_char;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static int uart_smartbond_configure(const struct device *dev,
|
|
const struct uart_config *cfg)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
struct uart_smartbond_data *data = dev->data;
|
|
uint32_t baudrate_cfg = 0;
|
|
k_spinlock_key_t key;
|
|
uint32_t reg_val;
|
|
int err;
|
|
int i;
|
|
|
|
if ((cfg->parity != UART_CFG_PARITY_NONE && cfg->parity != UART_CFG_PARITY_ODD &&
|
|
cfg->parity != UART_CFG_PARITY_EVEN) ||
|
|
(cfg->stop_bits != UART_CFG_STOP_BITS_1 && cfg->stop_bits != UART_CFG_STOP_BITS_2) ||
|
|
(cfg->data_bits != UART_CFG_DATA_BITS_5 && cfg->data_bits != UART_CFG_DATA_BITS_6 &&
|
|
cfg->data_bits != UART_CFG_DATA_BITS_7 && cfg->data_bits != UART_CFG_DATA_BITS_8) ||
|
|
(cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE &&
|
|
cfg->flow_ctrl != UART_CFG_FLOW_CTRL_RTS_CTS)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Flow control is not supported on UART */
|
|
if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS &&
|
|
!config->hw_flow_control_supported) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Lookup configuration for baudrate */
|
|
for (i = 0; i < ARRAY_SIZE(uart_smartbond_baudrate_table); i++) {
|
|
if (uart_smartbond_baudrate_table[i].baudrate == cfg->baudrate) {
|
|
baudrate_cfg = uart_smartbond_baudrate_table[i].cfg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (baudrate_cfg == 0) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
key = k_spin_lock(&data->lock);
|
|
|
|
CRG_COM->SET_CLK_COM_REG = config->periph_clock_config;
|
|
|
|
config->regs->UART2_SRR_REG = UART2_UART2_SRR_REG_UART_UR_Msk |
|
|
UART2_UART2_SRR_REG_UART_RFR_Msk |
|
|
UART2_UART2_SRR_REG_UART_XFR_Msk;
|
|
|
|
config->regs->UART2_LCR_REG |= UART2_UART2_LCR_REG_UART_DLAB_Msk;
|
|
config->regs->UART2_IER_DLH_REG = BAUDRATE_CFG_DLH(baudrate_cfg);
|
|
config->regs->UART2_RBR_THR_DLL_REG = BAUDRATE_CFG_DLL(baudrate_cfg);
|
|
config->regs->UART2_DLF_REG = BAUDRATE_CFG_DLF(baudrate_cfg);
|
|
config->regs->UART2_LCR_REG &= ~UART2_UART2_LCR_REG_UART_DLAB_Msk;
|
|
|
|
/* Configure frame */
|
|
|
|
reg_val = 0;
|
|
|
|
switch (cfg->parity) {
|
|
case UART_CFG_PARITY_NONE:
|
|
break;
|
|
case UART_CFG_PARITY_EVEN:
|
|
reg_val |= UART2_UART2_LCR_REG_UART_EPS_Msk;
|
|
/* no break */
|
|
case UART_CFG_PARITY_ODD:
|
|
reg_val |= UART2_UART2_LCR_REG_UART_PEN_Msk;
|
|
break;
|
|
}
|
|
|
|
if (cfg->stop_bits == UART_CFG_STOP_BITS_2) {
|
|
reg_val |= STOP_BITS_2 << UART2_UART2_LCR_REG_UART_STOP_Pos;
|
|
}
|
|
|
|
switch (cfg->data_bits) {
|
|
case UART_CFG_DATA_BITS_6:
|
|
reg_val |= DATA_BITS_6 << UART2_UART2_LCR_REG_UART_DLS_Pos;
|
|
break;
|
|
case UART_CFG_DATA_BITS_7:
|
|
reg_val |= DATA_BITS_7 << UART2_UART2_LCR_REG_UART_DLS_Pos;
|
|
break;
|
|
case UART_CFG_DATA_BITS_8:
|
|
reg_val |= DATA_BITS_8 << UART2_UART2_LCR_REG_UART_DLS_Pos;
|
|
break;
|
|
}
|
|
|
|
config->regs->UART2_LCR_REG = reg_val;
|
|
|
|
/* Enable hardware FIFO */
|
|
config->regs->UART2_SFE_REG = UART2_UART2_SFE_REG_UART_SHADOW_FIFO_ENABLE_Msk;
|
|
|
|
config->regs->UART2_SRT_REG = RX_FIFO_TRIG_1_CHAR;
|
|
config->regs->UART2_STET_REG = TX_FIFO_TRIG_1_2_FULL;
|
|
|
|
data->current_config = *cfg;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
|
static int uart_smartbond_config_get(const struct device *dev,
|
|
struct uart_config *cfg)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
|
|
*cfg = data->current_config;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
|
|
|
|
static int uart_smartbond_init(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
|
|
return uart_smartbond_configure(dev, &data->current_config);
|
|
}
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static inline void irq_tx_enable(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
config->regs->UART2_IER_DLH_REG |= UART2_UART2_IER_DLH_REG_PTIME_DLH7_Msk |
|
|
UART2_UART2_IER_DLH_REG_ETBEI_DLH1_Msk;
|
|
}
|
|
|
|
static inline void irq_tx_disable(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
config->regs->UART2_IER_DLH_REG &= ~(UART2_UART2_IER_DLH_REG_PTIME_DLH7_Msk |
|
|
UART2_UART2_IER_DLH_REG_ETBEI_DLH1_Msk);
|
|
}
|
|
|
|
static inline void irq_rx_enable(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
config->regs->UART2_IER_DLH_REG |= UART2_UART2_IER_DLH_REG_ERBFI_DLH0_Msk;
|
|
}
|
|
|
|
static inline void irq_rx_disable(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
config->regs->UART2_IER_DLH_REG &= ~UART2_UART2_IER_DLH_REG_ERBFI_DLH0_Msk;
|
|
}
|
|
|
|
static int uart_smartbond_fifo_fill(const struct device *dev,
|
|
const uint8_t *tx_data,
|
|
int len)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
struct uart_smartbond_data *data = dev->data;
|
|
int num_tx = 0;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
while ((len - num_tx > 0) &&
|
|
(config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk)) {
|
|
config->regs->UART2_RBR_THR_DLL_REG = tx_data[num_tx++];
|
|
}
|
|
|
|
if (data->tx_enabled) {
|
|
irq_tx_enable(dev);
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return num_tx;
|
|
}
|
|
|
|
static int uart_smartbond_fifo_read(const struct device *dev, uint8_t *rx_data,
|
|
const int size)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
struct uart_smartbond_data *data = dev->data;
|
|
int num_rx = 0;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
while ((size - num_rx > 0) &&
|
|
(config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk)) {
|
|
rx_data[num_rx++] = config->regs->UART2_RBR_THR_DLL_REG;
|
|
}
|
|
|
|
if (data->rx_enabled) {
|
|
irq_rx_enable(dev);
|
|
}
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
|
|
return num_rx;
|
|
}
|
|
|
|
static void uart_smartbond_irq_tx_enable(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
data->tx_enabled = 1;
|
|
irq_tx_enable(dev);
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static void uart_smartbond_irq_tx_disable(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
irq_tx_disable(dev);
|
|
data->tx_enabled = 0;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static int uart_smartbond_irq_tx_ready(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFNF_Msk) != 0;
|
|
}
|
|
|
|
static void uart_smartbond_irq_rx_enable(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
data->rx_enabled = 1;
|
|
irq_rx_enable(dev);
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static void uart_smartbond_irq_rx_disable(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
|
|
|
irq_rx_disable(dev);
|
|
data->rx_enabled = 0;
|
|
|
|
k_spin_unlock(&data->lock, key);
|
|
}
|
|
|
|
static int uart_smartbond_irq_tx_complete(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_TFE_Msk) != 0;
|
|
}
|
|
|
|
static int uart_smartbond_irq_rx_ready(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
|
|
return (config->regs->UART2_USR_REG & UART2_UART2_USR_REG_UART_RFNE_Msk) != 0;
|
|
}
|
|
|
|
static void uart_smartbond_irq_err_enable(const struct device *dev)
|
|
{
|
|
k_panic();
|
|
}
|
|
|
|
static void uart_smartbond_irq_err_disable(const struct device *dev)
|
|
{
|
|
k_panic();
|
|
}
|
|
|
|
static int uart_smartbond_irq_is_pending(const struct device *dev)
|
|
{
|
|
k_panic();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int uart_smartbond_irq_update(const struct device *dev)
|
|
{
|
|
const struct uart_smartbond_cfg *config = dev->config;
|
|
bool no_intr = false;
|
|
|
|
while (!no_intr) {
|
|
switch (config->regs->UART2_IIR_FCR_REG & 0x0F) {
|
|
case IIR_NO_INTR:
|
|
no_intr = true;
|
|
break;
|
|
case IIR_THR_EMPTY:
|
|
irq_tx_disable(dev);
|
|
break;
|
|
case IIR_RX_DATA:
|
|
irq_rx_disable(dev);
|
|
break;
|
|
case IIR_LINE_STATUS:
|
|
case IIR_TIMEOUT:
|
|
/* ignore */
|
|
break;
|
|
case IIR_BUSY:
|
|
/* busy detect */
|
|
/* fall-through */
|
|
default:
|
|
k_panic();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void uart_smartbond_irq_callback_set(const struct device *dev,
|
|
uart_irq_callback_user_data_t cb,
|
|
void *cb_data)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
|
|
data->callback = cb;
|
|
data->cb_data = cb_data;
|
|
}
|
|
|
|
static void uart_smartbond_isr(const struct device *dev)
|
|
{
|
|
struct uart_smartbond_data *data = dev->data;
|
|
|
|
if (data->callback) {
|
|
data->callback(dev, data->cb_data);
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
static const struct uart_driver_api uart_smartbond_driver_api = {
|
|
.poll_in = uart_smartbond_poll_in,
|
|
.poll_out = uart_smartbond_poll_out,
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
|
|
.configure = uart_smartbond_configure,
|
|
.config_get = uart_smartbond_config_get,
|
|
#endif
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
.fifo_fill = uart_smartbond_fifo_fill,
|
|
.fifo_read = uart_smartbond_fifo_read,
|
|
.irq_tx_enable = uart_smartbond_irq_tx_enable,
|
|
.irq_tx_disable = uart_smartbond_irq_tx_disable,
|
|
.irq_tx_ready = uart_smartbond_irq_tx_ready,
|
|
.irq_rx_enable = uart_smartbond_irq_rx_enable,
|
|
.irq_rx_disable = uart_smartbond_irq_rx_disable,
|
|
.irq_tx_complete = uart_smartbond_irq_tx_complete,
|
|
.irq_rx_ready = uart_smartbond_irq_rx_ready,
|
|
.irq_err_enable = uart_smartbond_irq_err_enable,
|
|
.irq_err_disable = uart_smartbond_irq_err_disable,
|
|
.irq_is_pending = uart_smartbond_irq_is_pending,
|
|
.irq_update = uart_smartbond_irq_update,
|
|
.irq_callback_set = uart_smartbond_irq_callback_set,
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
};
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
#define UART_SMARTBOND_CONFIGURE(id) \
|
|
do { \
|
|
IRQ_CONNECT(DT_INST_IRQN(id), \
|
|
DT_INST_IRQ(id, priority), \
|
|
uart_smartbond_isr, \
|
|
DEVICE_DT_INST_GET(id), 0); \
|
|
\
|
|
irq_enable(DT_INST_IRQN(id)); \
|
|
} while (0)
|
|
#else
|
|
#define UART_SMARTBOND_CONFIGURE(id)
|
|
#endif
|
|
|
|
#define UART_SMARTBOND_DEVICE(id) \
|
|
PINCTRL_DT_INST_DEFINE(id); \
|
|
static const struct uart_smartbond_cfg uart_smartbond_##id##_cfg = { \
|
|
.regs = (UART2_Type *)DT_INST_REG_ADDR(id), \
|
|
.periph_clock_config = DT_INST_PROP(id, periph_clock_config), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
|
|
.hw_flow_control_supported = DT_INST_PROP(id, hw_flow_control_supported), \
|
|
}; \
|
|
static struct uart_smartbond_data uart_smartbond_##id##_data = { \
|
|
.current_config = { \
|
|
.baudrate = DT_INST_PROP(id, current_speed), \
|
|
.parity = UART_CFG_PARITY_NONE, \
|
|
.stop_bits = UART_CFG_STOP_BITS_1, \
|
|
.data_bits = UART_CFG_DATA_BITS_8, \
|
|
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \
|
|
}, \
|
|
}; \
|
|
static int uart_smartbond_##id##_init(const struct device *dev) \
|
|
{ \
|
|
UART_SMARTBOND_CONFIGURE(id); \
|
|
return uart_smartbond_init(dev); \
|
|
} \
|
|
DEVICE_DT_INST_DEFINE(id, \
|
|
uart_smartbond_##id##_init, \
|
|
NULL, \
|
|
&uart_smartbond_##id##_data, \
|
|
&uart_smartbond_##id##_cfg, \
|
|
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
|
|
&uart_smartbond_driver_api); \
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(UART_SMARTBOND_DEVICE)
|