blob: 311b586a51634896f3b2b214f08ab39ae9854d10 [file] [log] [blame]
/*
* 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_nxp_s32_linflexd.h"
static int uart_nxp_s32_err_check(const struct device *dev)
{
const struct uart_nxp_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_nxp_s32_poll_out(const struct device *dev, unsigned char c)
{
const struct uart_nxp_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_NXP_S32_POLL_OUT_TIMEOUT);
/* Restore Linflexd's interrupts */
sys_write32(linflexd_ier, POINTER_TO_UINT(&config->base->LINIER));
irq_unlock(key);
}
static int uart_nxp_s32_poll_in(const struct device *dev, unsigned char *c)
{
const struct uart_nxp_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_NXP_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_nxp_s32_fifo_fill(const struct device *dev, const uint8_t *tx_data,
int size)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_fifo_read(const struct device *dev, uint8_t *rx_data,
const int size)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_irq_tx_enable(const struct device *dev)
{
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_irq_tx_disable(const struct device *dev)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_irq_tx_ready(const struct device *dev)
{
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_s32_int *int_data = &(data->int_data);
return !int_data->tx_fifo_busy && int_data->irq_tx_enable;
}
static void uart_nxp_s32_irq_rx_enable(const struct device *dev)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_irq_rx_disable(const struct device *dev)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_irq_rx_ready(const struct device *dev)
{
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_s32_int *int_data = &(data->int_data);
return !int_data->rx_fifo_busy && int_data->irq_rx_enable;
}
static void uart_nxp_s32_irq_err_enable(const struct device *dev)
{
const struct uart_nxp_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_nxp_s32_irq_err_disable(const struct device *dev)
{
const struct uart_nxp_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_nxp_s32_irq_is_pending(const struct device *dev)
{
return (uart_nxp_s32_irq_tx_ready(dev)) || (uart_nxp_s32_irq_rx_ready(dev));
}
static int uart_nxp_s32_irq_update(const struct device *dev)
{
return 1;
}
static void uart_nxp_s32_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *cb_data)
{
struct uart_nxp_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_nxp_s32_isr(const struct device *dev)
{
struct uart_nxp_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_nxp_s32_init(const struct device *dev)
{
const struct uart_nxp_s32_config *config = dev->config;
struct uart_nxp_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_nxp_s32_driver_api = {
.poll_in = uart_nxp_s32_poll_in,
.poll_out = uart_nxp_s32_poll_out,
.err_check = uart_nxp_s32_err_check,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_nxp_s32_fifo_fill,
.fifo_read = uart_nxp_s32_fifo_read,
.irq_tx_enable = uart_nxp_s32_irq_tx_enable,
.irq_tx_disable = uart_nxp_s32_irq_tx_disable,
.irq_tx_ready = uart_nxp_s32_irq_tx_ready,
.irq_rx_enable = uart_nxp_s32_irq_rx_enable,
.irq_rx_disable = uart_nxp_s32_irq_rx_disable,
.irq_rx_ready = uart_nxp_s32_irq_rx_ready,
.irq_err_enable = uart_nxp_s32_irq_err_enable,
.irq_err_disable = uart_nxp_s32_irq_err_disable,
.irq_is_pending = uart_nxp_s32_irq_is_pending,
.irq_update = uart_nxp_s32_irq_update,
.irq_callback_set = uart_nxp_s32_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
#define UART_NXP_S32_NODE(n) DT_NODELABEL(uart##n)
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
#define UART_NXP_S32_INTERRUPT_DEFINE(n, isr_handler, parameter) \
do { \
IRQ_CONNECT(DT_IRQN(UART_NXP_S32_NODE(n)), \
DT_IRQ(UART_NXP_S32_NODE(n), priority), \
isr_handler, \
parameter, \
DT_IRQ(UART_NXP_S32_NODE(n), flags)); \
\
irq_enable(DT_IRQN(UART_NXP_S32_NODE(n))); \
} while (0)
#else
#define UART_NXP_S32_INTERRUPT_DEFINE(n, isr_handler, parameter)
#endif
#define UART_NXP_S32_INIT_DEVICE(n) \
PINCTRL_DT_DEFINE(UART_NXP_S32_NODE(n)); \
static struct uart_nxp_s32_data uart_nxp_s32_data_##n; \
static const struct uart_nxp_s32_config uart_nxp_s32_config_##n = { \
.instance = n, \
.base = (LINFLEXD_Type *)DT_REG_ADDR(UART_NXP_S32_NODE(n)), \
.pincfg = PINCTRL_DT_DEV_CONFIG_GET(UART_NXP_S32_NODE(n)), \
}; \
static int uart_nxp_s32_init_##n(const struct device *dev) \
{ \
UART_NXP_S32_INTERRUPT_DEFINE(n, Linflexd_Uart_Ip_IRQHandler, n);\
\
return uart_nxp_s32_init(dev); \
} \
DEVICE_DT_DEFINE(UART_NXP_S32_NODE(n), \
&uart_nxp_s32_init_##n, \
NULL, \
&uart_nxp_s32_data_##n, \
&uart_nxp_s32_config_##n, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_nxp_s32_driver_api);
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(0), okay)
UART_NXP_S32_INIT_DEVICE(0)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(1), okay)
UART_NXP_S32_INIT_DEVICE(1)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(2), okay)
UART_NXP_S32_INIT_DEVICE(2)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(3), okay)
UART_NXP_S32_INIT_DEVICE(3)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(4), okay)
UART_NXP_S32_INIT_DEVICE(4)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(5), okay)
UART_NXP_S32_INIT_DEVICE(5)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(6), okay)
UART_NXP_S32_INIT_DEVICE(6)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(7), okay)
UART_NXP_S32_INIT_DEVICE(7)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(8), okay)
UART_NXP_S32_INIT_DEVICE(8)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(9), okay)
UART_NXP_S32_INIT_DEVICE(9)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(10), okay)
UART_NXP_S32_INIT_DEVICE(10)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(11), okay)
UART_NXP_S32_INIT_DEVICE(11)
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(12), okay)
UART_NXP_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_nxp_s32_config *config = dev->config;
struct uart_nxp_s32_data *data = dev->data;
struct uart_nxp_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_nxp_s32_isr(dev);
}
} else if (event == LINFLEXD_UART_IP_EVENT_RX_FULL) {
int_data->rx_fifo_busy = false;
uart_nxp_s32_isr(dev);
} else if (event == LINFLEXD_UART_IP_EVENT_ERROR) {
uart_nxp_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_NXP_S32_NODE(0), okay)
case 0:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(0));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(1), okay)
case 1:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(1));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(2), okay)
case 2:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(2));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(3), okay)
case 3:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(3));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(4), okay)
case 4:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(4));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(5), okay)
case 5:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(5));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(6), okay)
case 6:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(6));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(7), okay)
case 7:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(7));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(8), okay)
case 8:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(8));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(9), okay)
case 9:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(9));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(10), okay)
case 10:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(10));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(11), okay)
case 11:
dev = DEVICE_DT_GET(UART_NXP_S32_NODE(11));
break;
#endif
#if DT_NODE_HAS_STATUS(UART_NXP_S32_NODE(12), okay)
case 12:
dev = DEVICE_DT_GET(UART_NXP_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 */