3e8f854506
This patch introduces support for NXP S32 LINFlexD peripheral operating in UART mode. Polling and interrupt-based serial API's are supported. Signed-off-by: Dat Nguyen Duy <dat.nguyenduy@nxp.com> Signed-off-by: Manuel Arguelles <manuel.arguelles@nxp.com>
541 lines
13 KiB
C
541 lines
13 KiB
C
/*
|
|
* Copyright 2022 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/irq.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
|
|
#include <Linflexd_Uart_Ip.h>
|
|
#include <Linflexd_Uart_Ip_Irq.h>
|
|
#include "uart_s32_linflexd.h"
|
|
|
|
static int uart_s32_err_check(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
Linflexd_Uart_Ip_StatusType status;
|
|
int err = 0;
|
|
|
|
status = Linflexd_Uart_Ip_GetReceiveStatus(config->instance, NULL);
|
|
|
|
if (status == LINFLEXD_UART_IP_STATUS_RX_OVERRUN) {
|
|
err |= UART_ERROR_OVERRUN;
|
|
}
|
|
|
|
if (status == LINFLEXD_UART_IP_STATUS_PARITY_ERROR) {
|
|
err |= UART_ERROR_PARITY;
|
|
}
|
|
|
|
if (status == LINFLEXD_UART_IP_STATUS_FRAMING_ERROR) {
|
|
err |= UART_ERROR_FRAMING;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void uart_s32_poll_out(const struct device *dev, unsigned char c)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
uint32_t linflexd_ier;
|
|
uint8_t key;
|
|
|
|
key = irq_lock();
|
|
|
|
/* Save enabled Linflexd's interrupts */
|
|
linflexd_ier = sys_read32(POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
Linflexd_Uart_Ip_SyncSend(config->instance, &c, 1,
|
|
CONFIG_UART_S32_POLL_OUT_TIMEOUT);
|
|
|
|
/* Restore Linflexd's interrupts */
|
|
sys_write32(linflexd_ier, POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
irq_unlock(key);
|
|
}
|
|
|
|
static int uart_s32_poll_in(const struct device *dev, unsigned char *c)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
Linflexd_Uart_Ip_StatusType status;
|
|
uint32_t linflexd_ier;
|
|
int ret;
|
|
|
|
status = LINFLEXD_UART_IP_STATUS_SUCCESS;
|
|
|
|
/* Save enabled Linflexd's interrupts */
|
|
linflexd_ier = sys_read32(POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
/* Retrieves data with poll method */
|
|
status = Linflexd_Uart_Ip_SyncReceive(config->instance, c, 1,
|
|
CONFIG_UART_S32_POLL_IN_TIMEOUT);
|
|
|
|
/* Restore Linflexd's interrupts */
|
|
sys_write32(linflexd_ier, POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
if (status == LINFLEXD_UART_IP_STATUS_SUCCESS) {
|
|
ret = 0;
|
|
} else if (status == LINFLEXD_UART_IP_STATUS_TIMEOUT) {
|
|
ret = -1;
|
|
} else {
|
|
ret = -EBUSY;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
|
|
static int uart_s32_fifo_fill(const struct device *dev, const uint8_t *tx_data,
|
|
int size)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
if (int_data->tx_fifo_busy) {
|
|
return 0;
|
|
}
|
|
|
|
int_data->tx_fifo_busy = true;
|
|
|
|
Linflexd_Uart_Ip_AsyncSend(config->instance, tx_data, 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int uart_s32_fifo_read(const struct device *dev, uint8_t *rx_data,
|
|
const int size)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
if (int_data->rx_fifo_busy) {
|
|
return 0;
|
|
}
|
|
|
|
*rx_data = int_data->rx_fifo_data;
|
|
int_data->rx_fifo_busy = true;
|
|
|
|
Linflexd_Uart_Ip_SetRxBuffer(config->instance, &(int_data->rx_fifo_data), 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void uart_s32_irq_tx_enable(const struct device *dev)
|
|
{
|
|
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
uint8_t key;
|
|
|
|
int_data->irq_tx_enable = true;
|
|
|
|
key = irq_lock();
|
|
|
|
/* Callback is called in order to transmit the data */
|
|
if (!int_data->tx_fifo_busy) {
|
|
|
|
if (data->callback) {
|
|
data->callback(dev, data->cb_data);
|
|
}
|
|
}
|
|
|
|
irq_unlock(key);
|
|
}
|
|
|
|
static void uart_s32_irq_tx_disable(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
int_data->irq_tx_enable = false;
|
|
int_data->tx_fifo_busy = false;
|
|
|
|
Linflexd_Uart_Ip_AbortSendingData(config->instance);
|
|
}
|
|
|
|
static int uart_s32_irq_tx_ready(const struct device *dev)
|
|
{
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
return !int_data->tx_fifo_busy && int_data->irq_tx_enable;
|
|
}
|
|
|
|
static void uart_s32_irq_rx_enable(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
int_data->irq_rx_enable = true;
|
|
|
|
Linflexd_Uart_Ip_AsyncReceive(config->instance, &(int_data->rx_fifo_data), 1);
|
|
}
|
|
|
|
static void uart_s32_irq_rx_disable(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
int_data->irq_rx_enable = false;
|
|
int_data->rx_fifo_busy = false;
|
|
|
|
Linflexd_Uart_Ip_AbortReceivingData(config->instance);
|
|
}
|
|
|
|
static int uart_s32_irq_rx_ready(const struct device *dev)
|
|
{
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
|
|
return !int_data->rx_fifo_busy && int_data->irq_rx_enable;
|
|
}
|
|
|
|
static void uart_s32_irq_err_enable(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
uint32_t linflexd_ier;
|
|
|
|
linflexd_ier = sys_read32(POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
/* Enable frame error interrupt and buffer overrun error interrupt */
|
|
linflexd_ier |= (LINFLEXD_LINIER_FEIE_MASK | LINFLEXD_LINIER_BOIE_MASK);
|
|
|
|
sys_write32(linflexd_ier, POINTER_TO_UINT(&config->base->LINIER));
|
|
}
|
|
|
|
static void uart_s32_irq_err_disable(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
uint32_t linflexd_ier;
|
|
|
|
linflexd_ier = sys_read32(POINTER_TO_UINT(&config->base->LINIER));
|
|
|
|
/* Disable frame error interrupt and buffer overrun error interrupt */
|
|
linflexd_ier &= ~(LINFLEXD_LINIER_FEIE_MASK | LINFLEXD_LINIER_BOIE_MASK);
|
|
|
|
sys_write32(linflexd_ier, POINTER_TO_UINT(&config->base->LINIER));
|
|
}
|
|
|
|
static int uart_s32_irq_is_pending(const struct device *dev)
|
|
{
|
|
return (uart_s32_irq_tx_ready(dev)) || (uart_s32_irq_rx_ready(dev));
|
|
}
|
|
|
|
static int uart_s32_irq_update(const struct device *dev)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void uart_s32_irq_callback_set(const struct device *dev,
|
|
uart_irq_callback_user_data_t cb,
|
|
void *cb_data)
|
|
{
|
|
struct uart_s32_data *data = dev->data;
|
|
|
|
data->callback = cb;
|
|
data->cb_data = cb_data;
|
|
}
|
|
|
|
/**
|
|
* @brief Interrupt service routine.
|
|
*
|
|
* This simply calls the callback function, if one exists.
|
|
*
|
|
* Note: s32 UART Tx interrupts when ready to send; Rx interrupts when char
|
|
* received.
|
|
*
|
|
* @param arg Argument to ISR.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void uart_s32_isr(const struct device *dev)
|
|
{
|
|
struct uart_s32_data *data = dev->data;
|
|
|
|
if (data->callback) {
|
|
data->callback(dev, data->cb_data);
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
/**
|
|
* @brief Initialize UART channel
|
|
*
|
|
* This routine is called to reset the chip in a quiescent state.
|
|
* It is assumed that this function is called only once per UART.
|
|
*
|
|
* @param dev UART device struct
|
|
*
|
|
* @return 0
|
|
*/
|
|
static int uart_s32_init(const struct device *dev)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
static uint8_t state_idx;
|
|
uint8_t key;
|
|
int err;
|
|
|
|
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
key = irq_lock();
|
|
|
|
/* Initialize UART with default configuration */
|
|
data->hw_cfg.BaudRate = 115200;
|
|
data->hw_cfg.BaudRateMantissa = 26U;
|
|
data->hw_cfg.BaudRateFractionalDivisor = 1U;
|
|
data->hw_cfg.ParityCheck = false;
|
|
data->hw_cfg.ParityType = LINFLEXD_UART_IP_PARITY_EVEN;
|
|
data->hw_cfg.StopBitsCount = LINFLEXD_UART_IP_ONE_STOP_BIT;
|
|
data->hw_cfg.WordLength = LINFLEXD_UART_IP_8_BITS;
|
|
data->hw_cfg.TransferType = LINFLEXD_UART_IP_USING_INTERRUPTS;
|
|
data->hw_cfg.Callback = s32_uart_callback;
|
|
data->hw_cfg.CallbackParam = NULL;
|
|
data->hw_cfg.StateStruct = &Linflexd_Uart_Ip_apStateStructure[state_idx++];
|
|
|
|
Linflexd_Uart_Ip_Init(config->instance, &data->hw_cfg);
|
|
|
|
irq_unlock(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct uart_driver_api uart_s32_driver_api = {
|
|
.poll_in = uart_s32_poll_in,
|
|
.poll_out = uart_s32_poll_out,
|
|
.err_check = uart_s32_err_check,
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
.fifo_fill = uart_s32_fifo_fill,
|
|
.fifo_read = uart_s32_fifo_read,
|
|
.irq_tx_enable = uart_s32_irq_tx_enable,
|
|
.irq_tx_disable = uart_s32_irq_tx_disable,
|
|
.irq_tx_ready = uart_s32_irq_tx_ready,
|
|
.irq_rx_enable = uart_s32_irq_rx_enable,
|
|
.irq_rx_disable = uart_s32_irq_rx_disable,
|
|
.irq_rx_ready = uart_s32_irq_rx_ready,
|
|
.irq_err_enable = uart_s32_irq_err_enable,
|
|
.irq_err_disable = uart_s32_irq_err_disable,
|
|
.irq_is_pending = uart_s32_irq_is_pending,
|
|
.irq_update = uart_s32_irq_update,
|
|
.irq_callback_set = uart_s32_irq_callback_set,
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
};
|
|
|
|
#define UART_S32_NODE(n) DT_NODELABEL(uart##n)
|
|
|
|
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
|
|
#define UART_S32_INTERRUPT_DEFINE(n, isr_handler, parameter) \
|
|
do { \
|
|
IRQ_CONNECT(DT_IRQN(UART_S32_NODE(n)), \
|
|
DT_IRQ(UART_S32_NODE(n), priority), \
|
|
isr_handler, \
|
|
parameter, \
|
|
DT_IRQ(UART_S32_NODE(n), flags)); \
|
|
\
|
|
irq_enable(DT_IRQN(UART_S32_NODE(n))); \
|
|
} while (0)
|
|
|
|
#else
|
|
#define UART_S32_INTERRUPT_DEFINE(n, isr_handler, parameter)
|
|
#endif
|
|
|
|
#define UART_S32_INIT_DEVICE(n) \
|
|
PINCTRL_DT_DEFINE(UART_S32_NODE(n)); \
|
|
static struct uart_s32_data uart_s32_data_##n; \
|
|
static const struct uart_s32_config uart_s32_config_##n = { \
|
|
.instance = n, \
|
|
.base = (LINFLEXD_Type *)DT_REG_ADDR(UART_S32_NODE(n)), \
|
|
.pincfg = PINCTRL_DT_DEV_CONFIG_GET(UART_S32_NODE(n)), \
|
|
}; \
|
|
static int uart_s32_init_##n(const struct device *dev) \
|
|
{ \
|
|
UART_S32_INTERRUPT_DEFINE(n, Linflexd_Uart_Ip_IRQHandler, n); \
|
|
\
|
|
return uart_s32_init(dev); \
|
|
} \
|
|
DEVICE_DT_DEFINE(UART_S32_NODE(n), \
|
|
&uart_s32_init_##n, \
|
|
NULL, \
|
|
&uart_s32_data_##n, \
|
|
&uart_s32_config_##n, \
|
|
PRE_KERNEL_1, \
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
|
&uart_s32_driver_api);
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(0), okay)
|
|
UART_S32_INIT_DEVICE(0)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(1), okay)
|
|
UART_S32_INIT_DEVICE(1)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(2), okay)
|
|
UART_S32_INIT_DEVICE(2)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(3), okay)
|
|
UART_S32_INIT_DEVICE(3)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(4), okay)
|
|
UART_S32_INIT_DEVICE(4)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(5), okay)
|
|
UART_S32_INIT_DEVICE(5)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(6), okay)
|
|
UART_S32_INIT_DEVICE(6)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(7), okay)
|
|
UART_S32_INIT_DEVICE(7)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(8), okay)
|
|
UART_S32_INIT_DEVICE(8)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(9), okay)
|
|
UART_S32_INIT_DEVICE(9)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(10), okay)
|
|
UART_S32_INIT_DEVICE(10)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(11), okay)
|
|
UART_S32_INIT_DEVICE(11)
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(12), okay)
|
|
UART_S32_INIT_DEVICE(12)
|
|
#endif
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static void s32_uart_device_event(const struct device *dev, Linflexd_Uart_Ip_EventType event,
|
|
void *user_data)
|
|
{
|
|
const struct uart_s32_config *config = dev->config;
|
|
struct uart_s32_data *data = dev->data;
|
|
struct uart_s32_int *int_data = &(data->int_data);
|
|
Linflexd_Uart_Ip_StatusType status;
|
|
|
|
ARG_UNUSED(user_data);
|
|
|
|
if (event == LINFLEXD_UART_IP_EVENT_END_TRANSFER) {
|
|
/*
|
|
* Check the previous UART transmit has finished
|
|
* because Rx may also trigger this event
|
|
*/
|
|
status = Linflexd_Uart_Ip_GetTransmitStatus(config->instance, NULL);
|
|
if (status != LINFLEXD_UART_IP_STATUS_BUSY) {
|
|
int_data->tx_fifo_busy = false;
|
|
uart_s32_isr(dev);
|
|
}
|
|
} else if (event == LINFLEXD_UART_IP_EVENT_RX_FULL) {
|
|
int_data->rx_fifo_busy = false;
|
|
uart_s32_isr(dev);
|
|
} else if (event == LINFLEXD_UART_IP_EVENT_ERROR) {
|
|
uart_s32_isr(dev);
|
|
} else {
|
|
/* Other events are not used */
|
|
}
|
|
}
|
|
|
|
void s32_uart_callback(const uint8 instance, Linflexd_Uart_Ip_EventType event, void *user_data)
|
|
{
|
|
const struct device *dev;
|
|
|
|
switch (instance) {
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(0), okay)
|
|
case 0:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(0));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(1), okay)
|
|
case 1:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(1));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(2), okay)
|
|
case 2:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(2));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(3), okay)
|
|
case 3:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(3));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(4), okay)
|
|
case 4:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(4));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(5), okay)
|
|
case 5:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(5));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(6), okay)
|
|
case 6:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(6));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(7), okay)
|
|
case 7:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(7));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(8), okay)
|
|
case 8:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(8));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(9), okay)
|
|
case 9:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(9));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(10), okay)
|
|
case 10:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(10));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(11), okay)
|
|
case 11:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(11));
|
|
break;
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(UART_S32_NODE(12), okay)
|
|
case 12:
|
|
dev = DEVICE_DT_GET(UART_S32_NODE(12));
|
|
break;
|
|
#endif
|
|
default:
|
|
dev = NULL;
|
|
break;
|
|
}
|
|
|
|
if (dev != NULL) {
|
|
s32_uart_device_event(dev, event, user_data);
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|