zephyr/drivers/serial/uart_xmc4xxx.c
Daniel Leung 9f02eeadf8 serial: allow callback setting to be exclusive
Both the IRQ API and Asynchronous API support callback.
However, since they are both interrupt driven, having
callbacks on both API would interfere with each other
in almost all cases. So this adds a kconfig to signal
that the callbacks should be exclusive to each other.
In other words, if one is set, the other should not
be active. Drivers implementing both APIs have been
updated to remove the callbacks from the other API.
Though, this still leaves the option to disable
the kconfig and allows both APIs to have callbacks
if one desires.

Fixes #48606

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2023-07-18 11:13:02 +00:00

1015 lines
30 KiB
C

/*
* Copyright (c) 2020 Linumiz
* Author: Parthiban Nallathambi <parthiban@linumiz.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT infineon_xmc4xxx_uart
#include <xmc_uart.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/util.h>
#include <zephyr/irq.h>
#define MAX_FIFO_SIZE 64
#define USIC_IRQ_MIN 84
#define USIC_IRQ_MAX 101
#define IRQS_PER_USIC 6
#define CURRENT_BUFFER 0
#define NEXT_BUFFER 1
struct uart_xmc4xxx_config {
XMC_USIC_CH_t *uart;
const struct pinctrl_dev_config *pcfg;
uint8_t input_src;
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
uart_irq_config_func_t irq_config_func;
uint8_t irq_num_tx;
uint8_t irq_num_rx;
#endif
uint8_t fifo_start_offset;
uint8_t fifo_tx_size;
uint8_t fifo_rx_size;
};
#ifdef CONFIG_UART_ASYNC_API
struct uart_dma_stream {
const struct device *dma_dev;
uint32_t dma_channel;
struct dma_config dma_cfg;
struct dma_block_config blk_cfg;
uint8_t *buffer;
size_t buffer_len;
size_t offset;
size_t counter;
int32_t timeout;
struct k_work_delayable timeout_work;
};
#endif
struct uart_xmc4xxx_data {
XMC_UART_CH_CONFIG_t config;
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
uart_irq_callback_user_data_t user_cb;
void *user_data;
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
uint8_t service_request_tx;
uint8_t service_request_rx;
#endif
#if defined(CONFIG_UART_ASYNC_API)
const struct device *dev;
uart_callback_t async_cb;
void *async_user_data;
struct uart_dma_stream dma_rx;
struct uart_dma_stream dma_tx;
uint8_t *rx_next_buffer;
size_t rx_next_buffer_len;
#endif
};
static int uart_xmc4xxx_poll_in(const struct device *dev, unsigned char *c)
{
const struct uart_xmc4xxx_config *config = dev->config;
bool fifo_empty;
if (config->fifo_rx_size > 0) {
fifo_empty = XMC_USIC_CH_RXFIFO_IsEmpty(config->uart);
} else {
fifo_empty = !XMC_USIC_CH_GetReceiveBufferStatus(config->uart);
}
if (fifo_empty) {
return -1;
}
*c = (unsigned char)XMC_UART_CH_GetReceivedData(config->uart);
return 0;
}
static void uart_xmc4xxx_poll_out(const struct device *dev, unsigned char c)
{
const struct uart_xmc4xxx_config *config = dev->config;
/* XMC_UART_CH_Transmit() only blocks for UART to finish transmitting */
/* when fifo is not used */
while (config->fifo_tx_size > 0 && XMC_USIC_CH_TXFIFO_IsFull(config->uart)) {
}
XMC_UART_CH_Transmit(config->uart, c);
}
#if defined(CONFIG_UART_ASYNC_API)
static inline void async_timer_start(struct k_work_delayable *work, int32_t timeout)
{
if ((timeout != SYS_FOREVER_US) && (timeout != 0)) {
k_work_reschedule(work, K_USEC(timeout));
}
}
static void disable_tx_events(const struct uart_xmc4xxx_config *config)
{
if (config->fifo_tx_size > 0) {
XMC_USIC_CH_TXFIFO_DisableEvent(config->uart,
XMC_USIC_CH_TXFIFO_EVENT_CONF_STANDARD);
} else {
XMC_USIC_CH_DisableEvent(config->uart, XMC_USIC_CH_EVENT_TRANSMIT_BUFFER);
}
}
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
static void enable_tx_events(const struct uart_xmc4xxx_config *config)
{
if (config->fifo_tx_size > 0) {
/* wait till the fifo has at least 1 byte free */
while (XMC_USIC_CH_TXFIFO_IsFull(config->uart)) {
}
XMC_USIC_CH_TXFIFO_EnableEvent(config->uart,
XMC_USIC_CH_TXFIFO_EVENT_CONF_STANDARD);
} else {
XMC_USIC_CH_EnableEvent(config->uart, XMC_USIC_CH_EVENT_TRANSMIT_BUFFER);
}
}
#define NVIC_ICPR_BASE 0xe000e280u
static void clear_pending_interrupt(int irq_num)
{
uint32_t *clearpend = (uint32_t *)(NVIC_ICPR_BASE) + irq_num / 32;
irq_num = irq_num & 0x1f;
/* writing zero has not effect, i.e. we only clear irq_num */
*clearpend = BIT(irq_num);
}
static void uart_xmc4xxx_isr(void *arg)
{
const struct device *dev = arg;
struct uart_xmc4xxx_data *data = dev->data;
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
if (data->user_cb) {
data->user_cb(dev, data->user_data);
}
#endif
#if defined(CONFIG_UART_ASYNC_API)
const struct uart_xmc4xxx_config *config = dev->config;
unsigned int key = irq_lock();
if (data->dma_rx.buffer_len) {
/* We only need to trigger this irq once to start timer */
/* event. Everything else is handled by the timer callback and dma_rx_callback. */
/* Note that we can't simply disable the event that triggers this irq, since the */
/* same service_request gets routed to the dma. Thus we disable the nvic irq */
/* below. Any pending irq must be cleared before irq_enable() is called. */
irq_disable(config->irq_num_rx);
async_timer_start(&data->dma_rx.timeout_work, data->dma_rx.timeout);
}
irq_unlock(key);
#endif
}
static void uart_xmc4xxx_configure_service_requests(const struct device *dev)
{
struct uart_xmc4xxx_data *data = dev->data;
const struct uart_xmc4xxx_config *config = dev->config;
__ASSERT(config->irq_num_tx >= USIC_IRQ_MIN && config->irq_num_tx <= USIC_IRQ_MAX,
"Invalid irq number\n");
data->service_request_tx = (config->irq_num_tx - USIC_IRQ_MIN) % IRQS_PER_USIC;
if (config->fifo_tx_size > 0) {
XMC_USIC_CH_TXFIFO_SetInterruptNodePointer(
config->uart, XMC_USIC_CH_TXFIFO_INTERRUPT_NODE_POINTER_STANDARD,
data->service_request_tx);
} else {
XMC_USIC_CH_SetInterruptNodePointer(
config->uart, XMC_USIC_CH_INTERRUPT_NODE_POINTER_TRANSMIT_BUFFER,
data->service_request_tx);
}
__ASSERT(config->irq_num_rx >= USIC_IRQ_MIN && config->irq_num_rx <= USIC_IRQ_MAX,
"Invalid irq number\n");
data->service_request_rx = (config->irq_num_rx - USIC_IRQ_MIN) % IRQS_PER_USIC;
if (config->fifo_rx_size > 0) {
XMC_USIC_CH_RXFIFO_SetInterruptNodePointer(
config->uart, XMC_USIC_CH_RXFIFO_INTERRUPT_NODE_POINTER_STANDARD,
data->service_request_rx);
XMC_USIC_CH_RXFIFO_SetInterruptNodePointer(
config->uart, XMC_USIC_CH_RXFIFO_INTERRUPT_NODE_POINTER_ALTERNATE,
data->service_request_rx);
} else {
XMC_USIC_CH_SetInterruptNodePointer(config->uart,
XMC_USIC_CH_INTERRUPT_NODE_POINTER_RECEIVE,
data->service_request_rx);
XMC_USIC_CH_SetInterruptNodePointer(
config->uart, XMC_USIC_CH_INTERRUPT_NODE_POINTER_ALTERNATE_RECEIVE,
data->service_request_rx);
}
}
static int uart_xmc4xxx_irq_tx_ready(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
if (config->fifo_tx_size > 0) {
return !XMC_USIC_CH_TXFIFO_IsFull(config->uart);
} else {
return XMC_USIC_CH_GetTransmitBufferStatus(config->uart) ==
XMC_USIC_CH_TBUF_STATUS_IDLE;
}
}
static void uart_xmc4xxx_irq_rx_disable(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
if (config->fifo_rx_size > 0) {
XMC_USIC_CH_RXFIFO_DisableEvent(config->uart,
XMC_USIC_CH_RXFIFO_EVENT_CONF_STANDARD |
XMC_USIC_CH_RXFIFO_EVENT_CONF_ALTERNATE);
} else {
XMC_USIC_CH_DisableEvent(config->uart, XMC_USIC_CH_EVENT_STANDARD_RECEIVE |
XMC_USIC_CH_EVENT_ALTERNATIVE_RECEIVE);
}
}
static void uart_xmc4xxx_irq_rx_enable(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
uint32_t recv_status;
/* re-enable the IRQ as it may have been disabled during async_rx */
clear_pending_interrupt(config->irq_num_rx);
irq_enable(config->irq_num_rx);
if (config->fifo_rx_size > 0) {
XMC_USIC_CH_RXFIFO_Flush(config->uart);
XMC_USIC_CH_RXFIFO_SetSizeTriggerLimit(config->uart, config->fifo_rx_size, 0);
#if CONFIG_UART_XMC4XXX_RX_FIFO_INT_TRIGGER
config->uart->RBCTR |= BIT(USIC_CH_RBCTR_SRBTEN_Pos);
#endif
XMC_USIC_CH_RXFIFO_EnableEvent(config->uart,
XMC_USIC_CH_RXFIFO_EVENT_CONF_STANDARD |
XMC_USIC_CH_RXFIFO_EVENT_CONF_ALTERNATE);
} else {
/* flush out any received bytes while the uart rx irq was disabled */
recv_status = XMC_USIC_CH_GetReceiveBufferStatus(config->uart);
if (recv_status & USIC_CH_RBUFSR_RDV0_Msk) {
XMC_UART_CH_GetReceivedData(config->uart);
}
if (recv_status & USIC_CH_RBUFSR_RDV1_Msk) {
XMC_UART_CH_GetReceivedData(config->uart);
}
XMC_USIC_CH_EnableEvent(config->uart, XMC_USIC_CH_EVENT_STANDARD_RECEIVE |
XMC_USIC_CH_EVENT_ALTERNATIVE_RECEIVE);
}
}
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
static int uart_xmc4xxx_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
{
const struct uart_xmc4xxx_config *config = dev->config;
int i = 0;
for (i = 0; i < len; i++) {
bool fifo_full;
XMC_UART_CH_Transmit(config->uart, tx_data[i]);
if (config->fifo_tx_size == 0) {
return 1;
}
fifo_full = XMC_USIC_CH_TXFIFO_IsFull(config->uart);
if (fifo_full) {
return i + 1;
}
}
return i;
}
static int uart_xmc4xxx_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
{
const struct uart_xmc4xxx_config *config = dev->config;
int i;
for (i = 0; i < size; i++) {
bool fifo_empty;
if (config->fifo_rx_size > 0) {
fifo_empty = XMC_USIC_CH_RXFIFO_IsEmpty(config->uart);
} else {
fifo_empty = !XMC_USIC_CH_GetReceiveBufferStatus(config->uart);
}
if (fifo_empty) {
break;
}
rx_data[i] = XMC_UART_CH_GetReceivedData(config->uart);
}
return i;
}
static void uart_xmc4xxx_irq_tx_enable(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
const struct uart_xmc4xxx_data *data = dev->data;
clear_pending_interrupt(config->irq_num_tx);
irq_enable(config->irq_num_tx);
enable_tx_events(config);
XMC_USIC_CH_TriggerServiceRequest(config->uart, data->service_request_tx);
}
static void uart_xmc4xxx_irq_tx_disable(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
if (config->fifo_tx_size > 0) {
XMC_USIC_CH_TXFIFO_DisableEvent(config->uart,
XMC_USIC_CH_TXFIFO_EVENT_CONF_STANDARD);
} else {
XMC_USIC_CH_DisableEvent(config->uart, XMC_USIC_CH_EVENT_TRANSMIT_BUFFER);
}
}
static int uart_xmc4xxx_irq_rx_ready(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
if (config->fifo_rx_size > 0) {
return !XMC_USIC_CH_RXFIFO_IsEmpty(config->uart);
} else {
return XMC_USIC_CH_GetReceiveBufferStatus(config->uart);
}
}
static void uart_xmc4xxx_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb, void *user_data)
{
struct uart_xmc4xxx_data *data = dev->data;
data->user_cb = cb;
data->user_data = user_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->async_cb = NULL;
data->async_user_data = NULL;
#endif
}
#define NVIC_ISPR_BASE 0xe000e200u
static int uart_xmc4xxx_irq_is_pending(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
uint32_t irq_num_tx = config->irq_num_tx;
uint32_t irq_num_rx = config->irq_num_rx;
bool tx_pending;
bool rx_pending;
uint32_t setpend;
/* the NVIC_ISPR_BASE address stores info which interrupts are pending */
/* bit 0 -> irq 0, bit 1 -> irq 1,... */
setpend = *((uint32_t *)(NVIC_ISPR_BASE) + irq_num_tx / 32);
irq_num_tx = irq_num_tx & 0x1f; /* take modulo 32 */
tx_pending = setpend & BIT(irq_num_tx);
setpend = *((uint32_t *)(NVIC_ISPR_BASE) + irq_num_rx / 32);
irq_num_rx = irq_num_rx & 0x1f; /* take modulo 32 */
rx_pending = setpend & BIT(irq_num_rx);
return tx_pending || rx_pending;
}
#endif
#if defined(CONFIG_UART_ASYNC_API)
static inline void async_evt_rx_buf_request(struct uart_xmc4xxx_data *data)
{
struct uart_event evt = {.type = UART_RX_BUF_REQUEST};
if (data->async_cb) {
data->async_cb(data->dev, &evt, data->async_user_data);
}
}
static inline void async_evt_rx_release_buffer(struct uart_xmc4xxx_data *data, int buffer_type)
{
struct uart_event event = {.type = UART_RX_BUF_RELEASED};
if (buffer_type == NEXT_BUFFER && !data->rx_next_buffer) {
return;
}
if (buffer_type == CURRENT_BUFFER && !data->dma_rx.buffer) {
return;
}
if (buffer_type == NEXT_BUFFER) {
event.data.rx_buf.buf = data->rx_next_buffer;
data->rx_next_buffer = NULL;
data->rx_next_buffer_len = 0;
} else {
event.data.rx_buf.buf = data->dma_rx.buffer;
data->dma_rx.buffer = NULL;
data->dma_rx.buffer_len = 0;
}
if (data->async_cb) {
data->async_cb(data->dev, &event, data->async_user_data);
}
}
static inline void async_evt_rx_disabled(struct uart_xmc4xxx_data *data)
{
struct uart_event event = {.type = UART_RX_DISABLED};
data->dma_rx.buffer = NULL;
data->dma_rx.buffer_len = 0;
data->dma_rx.offset = 0;
data->dma_rx.counter = 0;
if (data->async_cb) {
data->async_cb(data->dev, &event, data->async_user_data);
}
}
static inline void async_evt_rx_rdy(struct uart_xmc4xxx_data *data)
{
struct uart_event event = {.type = UART_RX_RDY,
.data.rx.buf = (uint8_t *)data->dma_rx.buffer,
.data.rx.len = data->dma_rx.counter - data->dma_rx.offset,
.data.rx.offset = data->dma_rx.offset};
data->dma_rx.offset = data->dma_rx.counter;
if (event.data.rx.len > 0 && data->async_cb) {
data->async_cb(data->dev, &event, data->async_user_data);
}
}
static inline void async_evt_tx_done(struct uart_xmc4xxx_data *data)
{
struct uart_event event = {.type = UART_TX_DONE,
.data.tx.buf = data->dma_tx.buffer,
.data.tx.len = data->dma_tx.counter};
data->dma_tx.buffer = NULL;
data->dma_tx.buffer_len = 0;
data->dma_tx.counter = 0;
if (data->async_cb) {
data->async_cb(data->dev, &event, data->async_user_data);
}
}
static inline void async_evt_tx_abort(struct uart_xmc4xxx_data *data)
{
struct uart_event event = {.type = UART_TX_ABORTED,
.data.tx.buf = data->dma_tx.buffer,
.data.tx.len = data->dma_tx.counter};
data->dma_tx.buffer = NULL;
data->dma_tx.buffer_len = 0;
data->dma_tx.counter = 0;
if (data->async_cb) {
data->async_cb(data->dev, &event, data->async_user_data);
}
}
static void uart_xmc4xxx_async_rx_timeout(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct uart_dma_stream *rx_stream =
CONTAINER_OF(dwork, struct uart_dma_stream, timeout_work);
struct uart_xmc4xxx_data *data = CONTAINER_OF(rx_stream, struct uart_xmc4xxx_data, dma_rx);
struct dma_status stat;
unsigned int key = irq_lock();
if (data->dma_rx.buffer_len == 0) {
irq_unlock(key);
return;
}
if (dma_get_status(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &stat) == 0) {
size_t rx_rcv_len = data->dma_rx.buffer_len - stat.pending_length;
if (rx_rcv_len > data->dma_rx.offset) {
data->dma_rx.counter = rx_rcv_len;
async_evt_rx_rdy(data);
}
}
irq_unlock(key);
async_timer_start(&data->dma_rx.timeout_work, data->dma_rx.timeout);
}
static int uart_xmc4xxx_async_tx_abort(const struct device *dev)
{
struct uart_xmc4xxx_data *data = dev->data;
struct dma_status stat;
size_t tx_buffer_len;
unsigned int key = irq_lock();
k_work_cancel_delayable(&data->dma_tx.timeout_work);
tx_buffer_len = data->dma_tx.buffer_len;
if (tx_buffer_len == 0) {
irq_unlock(key);
return -EINVAL;
}
if (!dma_get_status(data->dma_tx.dma_dev, data->dma_tx.dma_channel, &stat)) {
data->dma_tx.counter = tx_buffer_len - stat.pending_length;
}
dma_stop(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
disable_tx_events(dev->config);
async_evt_tx_abort(data);
irq_unlock(key);
return 0;
}
static void uart_xmc4xxx_async_tx_timeout(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct uart_dma_stream *tx_stream =
CONTAINER_OF(dwork, struct uart_dma_stream, timeout_work);
struct uart_xmc4xxx_data *data = CONTAINER_OF(tx_stream, struct uart_xmc4xxx_data, dma_tx);
uart_xmc4xxx_async_tx_abort(data->dev);
}
static int uart_xmc4xxx_async_init(const struct device *dev)
{
const struct uart_xmc4xxx_config *config = dev->config;
struct uart_xmc4xxx_data *data = dev->data;
data->dev = dev;
if (data->dma_rx.dma_dev != NULL) {
if (!device_is_ready(data->dma_rx.dma_dev)) {
return -ENODEV;
}
k_work_init_delayable(&data->dma_rx.timeout_work, uart_xmc4xxx_async_rx_timeout);
if (config->fifo_rx_size > 0) {
data->dma_rx.blk_cfg.source_address = (uint32_t)&config->uart->OUTR;
} else {
data->dma_rx.blk_cfg.source_address = (uint32_t)&config->uart->RBUF;
}
data->dma_rx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT;
data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg;
data->dma_rx.dma_cfg.user_data = (void *)dev;
}
if (data->dma_tx.dma_dev != NULL) {
if (!device_is_ready(data->dma_tx.dma_dev)) {
return -ENODEV;
}
k_work_init_delayable(&data->dma_tx.timeout_work, uart_xmc4xxx_async_tx_timeout);
if (config->fifo_tx_size > 0) {
data->dma_tx.blk_cfg.dest_address = (uint32_t)&config->uart->IN[0];
} else {
data->dma_tx.blk_cfg.dest_address = (uint32_t)&config->uart->TBUF[0];
}
data->dma_tx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT;
data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg;
data->dma_tx.dma_cfg.user_data = (void *)dev;
}
return 0;
}
static int uart_xmc4xxx_async_callback_set(const struct device *dev, uart_callback_t callback,
void *user_data)
{
struct uart_xmc4xxx_data *data = dev->data;
data->async_cb = callback;
data->async_user_data = user_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->user_cb = NULL;
data->user_data = NULL;
#endif
return 0;
}
static int uart_xmc4xxx_async_tx(const struct device *dev, const uint8_t *tx_data, size_t buf_size,
int32_t timeout)
{
struct uart_xmc4xxx_data *data = dev->data;
const struct uart_xmc4xxx_config *config = dev->config;
int ret;
/* Assume threads are pre-emptive so this call cannot be interrupted */
/* by uart_xmc4xxx_async_tx_abort */
if (data->dma_tx.dma_dev == NULL) {
return -ENODEV;
}
if (tx_data == NULL || buf_size == 0) {
return -EINVAL;
}
/* No need to lock irq. Isr uart_xmc4xxx_dma_tx_cb() will only trigger if */
/* dma_tx.buffer_len != 0 */
if (data->dma_tx.buffer_len != 0) {
return -EBUSY;
}
data->dma_tx.buffer = (uint8_t *)tx_data;
data->dma_tx.buffer_len = buf_size;
data->dma_tx.timeout = timeout;
/* set source address */
data->dma_tx.blk_cfg.source_address = (uint32_t)data->dma_tx.buffer;
data->dma_tx.blk_cfg.block_size = data->dma_tx.buffer_len;
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel, &data->dma_tx.dma_cfg);
if (ret < 0) {
return ret;
}
/* make sure the tx is not transmitting */
while (!uart_xmc4xxx_irq_tx_ready(dev)) {
};
/* Tx irq is not used in async mode so disable it */
irq_disable(config->irq_num_tx);
enable_tx_events(config);
XMC_USIC_CH_TriggerServiceRequest(config->uart, data->service_request_tx);
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
return dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
}
static int uart_xmc4xxx_async_rx_enable(const struct device *dev, uint8_t *buf, size_t len,
int32_t timeout)
{
struct uart_xmc4xxx_data *data = dev->data;
int ret;
if (data->dma_rx.dma_dev == NULL) {
return -ENODEV;
}
if (data->dma_rx.buffer_len != 0) {
return -EBUSY;
}
uart_xmc4xxx_irq_rx_disable(dev);
data->dma_rx.buffer = buf;
data->dma_rx.buffer_len = len;
data->dma_rx.timeout = timeout;
data->dma_rx.blk_cfg.dest_address = (uint32_t)data->dma_rx.buffer;
data->dma_rx.blk_cfg.block_size = data->dma_rx.buffer_len;
ret = dma_config(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &data->dma_rx.dma_cfg);
if (ret < 0) {
return ret;
}
/* Request buffers before enabling rx. It's unlikely, but we may not */
/* request a new buffer in time (for example if receive buffer size is one byte). */
async_evt_rx_buf_request(data);
uart_xmc4xxx_irq_rx_enable(dev);
return dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
}
static void uart_xmc4xxx_dma_rx_cb(const struct device *dma_dev, void *user_data, uint32_t channel,
int status)
{
const struct device *dev_uart = user_data;
struct uart_xmc4xxx_data *data = dev_uart->data;
unsigned int key;
int ret;
if (status != 0) {
return;
}
__ASSERT_NO_MSG(channel == data->dma_rx.dma_channel);
key = irq_lock();
k_work_cancel_delayable(&data->dma_rx.timeout_work);
if (data->dma_rx.buffer_len == 0) {
goto done;
}
data->dma_rx.counter = data->dma_rx.buffer_len;
async_evt_rx_rdy(data);
async_evt_rx_release_buffer(data, CURRENT_BUFFER);
if (!data->rx_next_buffer) {
dma_stop(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
uart_xmc4xxx_irq_rx_disable(dev_uart);
async_evt_rx_disabled(data);
goto done;
}
data->dma_rx.buffer = data->rx_next_buffer;
data->dma_rx.buffer_len = data->rx_next_buffer_len;
data->dma_rx.offset = 0;
data->dma_rx.counter = 0;
data->rx_next_buffer = NULL;
data->rx_next_buffer_len = 0;
ret = dma_reload(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
data->dma_rx.blk_cfg.source_address, (uint32_t)data->dma_rx.buffer,
data->dma_rx.buffer_len);
if (ret < 0) {
dma_stop(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
uart_xmc4xxx_irq_rx_disable(dev_uart);
async_evt_rx_release_buffer(data, CURRENT_BUFFER);
async_evt_rx_disabled(data);
goto done;
}
dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
async_evt_rx_buf_request(data);
async_timer_start(&data->dma_rx.timeout_work, data->dma_rx.timeout);
done:
irq_unlock(key);
}
static int uart_xmc4xxx_async_rx_disable(const struct device *dev)
{
struct uart_xmc4xxx_data *data = dev->data;
struct dma_status stat;
unsigned int key;
k_work_cancel_delayable(&data->dma_rx.timeout_work);
key = irq_lock();
if (data->dma_rx.buffer_len == 0) {
__ASSERT_NO_MSG(data->dma_rx.buffer == NULL);
irq_unlock(key);
return -EINVAL;
}
dma_stop(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
uart_xmc4xxx_irq_rx_disable(dev);
if (dma_get_status(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &stat) == 0) {
size_t rx_rcv_len = data->dma_rx.buffer_len - stat.pending_length;
if (rx_rcv_len > data->dma_rx.offset) {
data->dma_rx.counter = rx_rcv_len;
async_evt_rx_rdy(data);
}
}
async_evt_rx_release_buffer(data, CURRENT_BUFFER);
async_evt_rx_release_buffer(data, NEXT_BUFFER);
async_evt_rx_disabled(data);
irq_unlock(key);
return 0;
}
static void uart_xmc4xxx_dma_tx_cb(const struct device *dma_dev, void *user_data, uint32_t channel,
int status)
{
const struct device *dev_uart = user_data;
struct uart_xmc4xxx_data *data = dev_uart->data;
size_t tx_buffer_len = data->dma_tx.buffer_len;
struct dma_status stat;
if (status != 0) {
return;
}
__ASSERT_NO_MSG(channel == data->dma_tx.dma_channel);
k_work_cancel_delayable(&data->dma_tx.timeout_work);
if (tx_buffer_len == 0) {
return;
}
if (!dma_get_status(data->dma_tx.dma_dev, channel, &stat)) {
data->dma_tx.counter = tx_buffer_len - stat.pending_length;
}
async_evt_tx_done(data);
/* if the callback doesn't doesn't do a chained uart_tx write, then stop the dma */
if (data->dma_tx.buffer == NULL) {
dma_stop(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
disable_tx_events(dev_uart->config);
}
}
static int uart_xmc4xxx_rx_buf_rsp(const struct device *dev, uint8_t *buf, size_t len)
{
struct uart_xmc4xxx_data *data = dev->data;
unsigned int key;
int ret = 0;
key = irq_lock();
if (data->dma_rx.buffer_len == 0U) {
ret = -EACCES;
goto done;
}
if (data->rx_next_buffer_len != 0U) {
ret = -EBUSY;
goto done;
}
data->rx_next_buffer = buf;
data->rx_next_buffer_len = len;
done:
irq_unlock(key);
return ret;
}
#endif
static int uart_xmc4xxx_init(const struct device *dev)
{
int ret;
const struct uart_xmc4xxx_config *config = dev->config;
struct uart_xmc4xxx_data *data = dev->data;
uint8_t fifo_offset = config->fifo_start_offset;
data->config.data_bits = 8U;
data->config.stop_bits = 1U;
XMC_UART_CH_Init(config->uart, &(data->config));
if (config->fifo_tx_size > 0) {
/* fifos need to be aligned on fifo size */
fifo_offset = ROUND_UP(fifo_offset, BIT(config->fifo_tx_size));
XMC_USIC_CH_TXFIFO_Configure(config->uart, fifo_offset, config->fifo_tx_size, 1);
fifo_offset += BIT(config->fifo_tx_size);
}
if (config->fifo_rx_size > 0) {
/* fifos need to be aligned on fifo size */
fifo_offset = ROUND_UP(fifo_offset, BIT(config->fifo_rx_size));
XMC_USIC_CH_RXFIFO_Configure(config->uart, fifo_offset, config->fifo_rx_size, 0);
fifo_offset += BIT(config->fifo_rx_size);
}
if (fifo_offset > MAX_FIFO_SIZE) {
return -EINVAL;
}
/* Connect UART RX to logical 1. It is connected to proper pin after pinctrl is applied */
XMC_UART_CH_SetInputSource(config->uart, XMC_UART_CH_INPUT_RXD, 0x7);
/* Start the UART before pinctrl, because the USIC is driving the TX line */
/* low in off state */
XMC_UART_CH_Start(config->uart);
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
/* Connect UART RX to the target pin */
XMC_UART_CH_SetInputSource(config->uart, XMC_UART_CH_INPUT_RXD,
config->input_src);
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
config->irq_config_func(dev);
uart_xmc4xxx_configure_service_requests(dev);
#endif
#if defined(CONFIG_UART_ASYNC_API)
ret = uart_xmc4xxx_async_init(dev);
#endif
return ret;
}
static const struct uart_driver_api uart_xmc4xxx_driver_api = {
.poll_in = uart_xmc4xxx_poll_in,
.poll_out = uart_xmc4xxx_poll_out,
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
.fifo_fill = uart_xmc4xxx_fifo_fill,
.fifo_read = uart_xmc4xxx_fifo_read,
.irq_tx_enable = uart_xmc4xxx_irq_tx_enable,
.irq_tx_disable = uart_xmc4xxx_irq_tx_disable,
.irq_tx_ready = uart_xmc4xxx_irq_tx_ready,
.irq_rx_enable = uart_xmc4xxx_irq_rx_enable,
.irq_rx_disable = uart_xmc4xxx_irq_rx_disable,
.irq_rx_ready = uart_xmc4xxx_irq_rx_ready,
.irq_callback_set = uart_xmc4xxx_irq_callback_set,
.irq_is_pending = uart_xmc4xxx_irq_is_pending,
#endif
#if defined(CONFIG_UART_ASYNC_API)
.callback_set = uart_xmc4xxx_async_callback_set,
.tx = uart_xmc4xxx_async_tx,
.tx_abort = uart_xmc4xxx_async_tx_abort,
.rx_enable = uart_xmc4xxx_async_rx_enable,
.rx_buf_rsp = uart_xmc4xxx_rx_buf_rsp,
.rx_disable = uart_xmc4xxx_async_rx_disable,
#endif
};
#ifdef CONFIG_UART_ASYNC_API
#define UART_DMA_CHANNEL_INIT(index, dir, ch_dir, src_burst, dst_burst) \
.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(index, dir)), \
.dma_channel = DT_INST_DMAS_CELL_BY_NAME(index, dir, channel), \
.dma_cfg = { \
.dma_slot = DT_INST_DMAS_CELL_BY_NAME(index, dir, config), \
.channel_direction = ch_dir, \
.channel_priority = DT_INST_DMAS_CELL_BY_NAME(index, dir, priority), \
.source_data_size = 1, \
.dest_data_size = 1, \
.source_burst_length = src_burst, \
.dest_burst_length = dst_burst, \
.block_count = 1, \
.dma_callback = uart_xmc4xxx_dma_##dir##_cb, \
},
#define UART_DMA_CHANNEL(index, dir, ch_dir, src_burst, dst_burst) \
.dma_##dir = {COND_CODE_1( \
DT_INST_DMAS_HAS_NAME(index, dir), \
(UART_DMA_CHANNEL_INIT(index, dir, ch_dir, src_burst, dst_burst)), (NULL))},
#else
#define UART_DMA_CHANNEL(index, dir, ch_dir, src_burst, dst_burst)
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
#define XMC4XXX_IRQ_HANDLER(index) \
static void uart_xmc4xxx_irq_setup_##index(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, tx, irq), \
DT_INST_IRQ_BY_NAME(index, tx, priority), uart_xmc4xxx_isr, \
DEVICE_DT_INST_GET(index), 0); \
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, rx, irq), \
DT_INST_IRQ_BY_NAME(index, rx, priority), uart_xmc4xxx_isr, \
DEVICE_DT_INST_GET(index), 0); \
irq_enable(DT_INST_IRQ_BY_NAME(index, tx, irq)); \
irq_enable(DT_INST_IRQ_BY_NAME(index, rx, irq)); \
}
#define XMC4XXX_IRQ_STRUCT_INIT(index) \
.irq_config_func = uart_xmc4xxx_irq_setup_##index, \
.irq_num_tx = DT_INST_IRQ_BY_NAME(index, tx, irq), \
.irq_num_rx = DT_INST_IRQ_BY_NAME(index, rx, irq),
#else
#define XMC4XXX_IRQ_HANDLER(index)
#define XMC4XXX_IRQ_STRUCT_INIT(index)
#endif
#define XMC4XXX_INIT(index) \
PINCTRL_DT_INST_DEFINE(index); \
XMC4XXX_IRQ_HANDLER(index) \
static struct uart_xmc4xxx_data xmc4xxx_data_##index = { \
.config.baudrate = DT_INST_PROP(index, current_speed), \
UART_DMA_CHANNEL(index, tx, MEMORY_TO_PERIPHERAL, 8, 1) \
UART_DMA_CHANNEL(index, rx, PERIPHERAL_TO_MEMORY, 1, 8) \
}; \
\
static const struct uart_xmc4xxx_config xmc4xxx_config_##index = { \
.uart = (XMC_USIC_CH_t *)DT_INST_REG_ADDR(index), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
.input_src = DT_INST_ENUM_IDX(index, input_src), \
XMC4XXX_IRQ_STRUCT_INIT(index) \
.fifo_start_offset = DT_INST_PROP(index, fifo_start_offset), \
.fifo_tx_size = DT_INST_ENUM_IDX(index, fifo_tx_size), \
.fifo_rx_size = DT_INST_ENUM_IDX(index, fifo_rx_size), \
}; \
\
DEVICE_DT_INST_DEFINE(index, &uart_xmc4xxx_init, \
NULL, \
&xmc4xxx_data_##index, \
&xmc4xxx_config_##index, PRE_KERNEL_1, \
CONFIG_SERIAL_INIT_PRIORITY, \
&uart_xmc4xxx_driver_api);
DT_INST_FOREACH_STATUS_OKAY(XMC4XXX_INIT)