blob: 20358dd0ebdd019b7cdaed51f439ffad5611bef1 [file]
/*
* Copyright (c) 2026, Realtek Semiconductor Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT realtek_bee_uart
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/__assert.h>
#include <soc.h>
#include <zephyr/init.h>
#include <zephyr/linker/sections.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/clock_control/bee_clock_control.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/irq.h>
#ifdef CONFIG_UART_ASYNC_API
#include <zephyr/drivers/dma/dma_bee.h>
#include <zephyr/drivers/dma.h>
#if defined(CONFIG_SOC_SERIES_RTL87X2G)
#include <rtl_gdma.h>
#elif defined(CONFIG_SOC_SERIES_RTL8752H)
#include <rtl876x_gdma.h>
#endif
#endif
#if defined(CONFIG_SOC_SERIES_RTL87X2G)
#include <rtl_uart.h>
#elif defined(CONFIG_SOC_SERIES_RTL8752H)
#include <rtl876x_uart.h>
#else
#error "Unsupported Realtek Bee SoC series"
#endif
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(uart_bee, CONFIG_UART_LOG_LEVEL);
struct uart_bee_config {
UART_TypeDef *uart;
uint16_t clkid;
uint8_t rx_threshold;
bool hw_flow_ctrl;
const struct pinctrl_dev_config *pcfg;
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
uart_irq_config_func_t irq_config_func;
#endif
};
#ifdef CONFIG_UART_ASYNC_API
struct uart_dma_stream {
const struct device *dma_dev;
uint32_t dma_channel;
struct dma_config dma_cfg;
uint8_t priority;
uint8_t src_addr_increment;
uint8_t dst_addr_increment;
int fifo_threshold;
struct dma_block_config blk_cfg;
uint8_t *buffer;
size_t buffer_length;
size_t offset;
volatile size_t counter;
int32_t timeout;
struct k_work_delayable timeout_work;
bool enabled;
};
#endif
struct uart_bee_data {
const struct device *dev;
struct uart_config uart_config;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t user_cb;
void *user_data;
bool tx_int_en;
bool rx_int_en;
#endif
#ifdef CONFIG_UART_ASYNC_API
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
};
/* clang-format off */
static const struct {
uint16_t div;
uint16_t ovsr;
uint16_t ovsr_adj;
uint32_t baudrate;
} uart_bee_baudrate_table[] = {
{271, 10, 0x24A, 9600},
{150, 8, 0x3EF, 19200},
{20, 12, 0x252, 115200},
{11, 10, 0x3BB, 230400},
{11, 9, 0x084, 256000},
{7, 9, 0x3EF, 384000},
{6, 9, 0x0AA, 460800},
{3, 9, 0x0AA, 921600},
{4, 5, 0, 1000000},
{2, 5, 0, 2000000},
{1, 8, 0x292, 3000000},
};
/* clang-format on */
static int uart_bee_cfg2idx_baudrate(uint32_t baudrate)
{
for (uint32_t i = 0; i < ARRAY_SIZE(uart_bee_baudrate_table); i++) {
if (uart_bee_baudrate_table[i].baudrate == baudrate) {
return i;
}
}
return -ENOTSUP;
}
static int uart_bee_cfg2mac_data_bits(enum uart_config_data_bits data_bits)
{
switch (data_bits) {
case UART_CFG_DATA_BITS_7:
return UART_WORD_LENGTH_7BIT;
case UART_CFG_DATA_BITS_8:
return UART_WORD_LENGTH_8BIT;
default:
return -ENOTSUP;
}
}
static int uart_bee_cfg2mac_stopbits(enum uart_config_stop_bits stop_bits)
{
switch (stop_bits) {
case UART_CFG_STOP_BITS_1:
return UART_STOP_BITS_1;
case UART_CFG_STOP_BITS_2:
return UART_STOP_BITS_2;
default:
return -ENOTSUP;
}
}
static int uart_bee_cfg2mac_parity(enum uart_config_parity parity)
{
switch (parity) {
case UART_CFG_PARITY_NONE:
return UART_PARITY_NO_PARTY;
case UART_CFG_PARITY_ODD:
return UART_PARITY_ODD;
case UART_CFG_PARITY_EVEN:
return UART_PARITY_EVEN;
default:
return -ENOTSUP;
}
}
static int uart_bee_configure(const struct device *dev, const struct uart_config *cfg)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
UART_InitTypeDef uart_init_struct;
int baudrate_idx;
int wordlen;
int stopbits;
int parity;
baudrate_idx = uart_bee_cfg2idx_baudrate(cfg->baudrate);
if (baudrate_idx < 0) {
LOG_DBG("Unsupported baudrate: %d", cfg->baudrate);
return -ENOTSUP;
}
wordlen = uart_bee_cfg2mac_data_bits(cfg->data_bits);
if (wordlen < 0) {
LOG_DBG("Unsupported data_bits: %d", cfg->data_bits);
return -ENOTSUP;
}
stopbits = uart_bee_cfg2mac_stopbits(cfg->stop_bits);
if (stopbits < 0) {
LOG_DBG("Unsupported stop_bits: %d", cfg->stop_bits);
return -ENOTSUP;
}
parity = uart_bee_cfg2mac_parity(cfg->parity);
if (parity < 0) {
LOG_DBG("Unsupported parity: %d", cfg->parity);
return -ENOTSUP;
}
LOG_DBG("baudrate_idx=%d, wordlen=%d, stopbits=%d, parity=%d, "
"hw_flow_ctrl=%d",
baudrate_idx, wordlen, stopbits, parity, config->hw_flow_ctrl);
UART_StructInit(&uart_init_struct);
uart_init_struct.UART_Div = uart_bee_baudrate_table[baudrate_idx].div;
uart_init_struct.UART_Ovsr = uart_bee_baudrate_table[baudrate_idx].ovsr;
uart_init_struct.UART_OvsrAdj = uart_bee_baudrate_table[baudrate_idx].ovsr_adj;
uart_init_struct.UART_IdleTime = UART_RX_IDLE_1BYTE;
uart_init_struct.UART_WordLen = wordlen;
uart_init_struct.UART_StopBits = stopbits;
uart_init_struct.UART_Parity = parity;
uart_init_struct.UART_HardwareFlowControl = cfg->flow_ctrl;
uart_init_struct.UART_RxThdLevel = config->rx_threshold;
uart_init_struct.UART_TxThdLevel = UART_TX_FIFO_SIZE / 2;
#ifdef CONFIG_UART_ASYNC_API
uart_init_struct.UART_DmaEn = UART_DMA_ENABLE;
if (data->dma_tx.dma_dev != NULL) {
uart_init_struct.UART_TxWaterLevel = 16 - data->dma_tx.dma_cfg.dest_burst_length;
}
if (data->dma_rx.dma_dev != NULL) {
uart_init_struct.UART_RxWaterLevel = data->dma_rx.dma_cfg.source_burst_length;
}
#endif
(void)clock_control_off(BEE_CLOCK_CONTROLLER, (clock_control_subsys_t)&config->clkid);
(void)clock_control_on(BEE_CLOCK_CONTROLLER, (clock_control_subsys_t)&config->clkid);
UART_Init(uart, &uart_init_struct);
data->uart_config = *cfg;
return 0;
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
static int uart_bee_config_get(const struct device *dev, struct uart_config *cfg)
{
struct uart_bee_data *data = dev->data;
*cfg = data->uart_config;
return 0;
}
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
static int uart_bee_poll_in(const struct device *dev, unsigned char *c)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
if (!UART_GetFlagStatus(uart, UART_FLAG_RX_DATA_AVA)) {
return -1;
}
LOG_DBG("c=%c", *c);
*c = (unsigned char)UART_ReceiveByte(uart);
return 0;
}
static void uart_bee_poll_out(const struct device *dev, unsigned char c)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
while (!(UART_GetTxFIFODataLen(uart) < UART_TX_FIFO_SIZE)) {
}
UART_SendByte(uart, (uint8_t)c);
while (UART_GetTxFIFODataLen(uart)) {
}
}
static int uart_bee_err_check(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
uint32_t err = 0U;
if (UART_GetFlagStatus(uart, UART_FLAG_RX_OVERRUN)) {
err |= UART_ERROR_OVERRUN;
}
if (UART_GetFlagStatus(uart, UART_FLAG_RX_PARITY_ERR)) {
err |= UART_ERROR_PARITY;
}
if (UART_GetFlagStatus(uart, UART_FLAG_RX_FRAME_ERR)) {
err |= UART_ERROR_FRAMING;
}
if (UART_GetFlagStatus(uart, UART_FLAG_RX_BREAK_ERR)) {
err |= UART_BREAK;
}
return err;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_bee_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
uint8_t num_tx = 0U;
unsigned int key;
if (!(UART_GetTxFIFODataLen(uart) < UART_TX_FIFO_SIZE)) {
return num_tx;
}
key = irq_lock();
while ((size - num_tx > 0) && (UART_GetTxFIFODataLen(uart) < UART_TX_FIFO_SIZE)) {
UART_SendByte(uart, (uint8_t)tx_data[num_tx++]);
}
irq_unlock(key);
LOG_DBG("num_tx=%d", num_tx);
return num_tx;
}
static int uart_bee_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
uint8_t num_rx = 0U;
while ((size - num_rx > 0) && UART_GetFlagStatus(uart, UART_FLAG_RX_DATA_AVA)) {
rx_data[num_rx++] = UART_ReceiveByte(uart);
}
LOG_DBG("num_rx=%d", num_rx);
return num_rx;
}
static void uart_bee_irq_tx_enable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
struct uart_bee_data *data = dev->data;
data->tx_int_en = true;
UART_INTConfig(uart, UART_INT_TX_FIFO_EMPTY, ENABLE);
}
static void uart_bee_irq_tx_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
struct uart_bee_data *data = dev->data;
data->tx_int_en = false;
UART_INTConfig(uart, UART_INT_TX_FIFO_EMPTY, DISABLE);
}
static int uart_bee_irq_tx_ready(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
return UART_GetFlagStatus(uart, UART_FLAG_TX_EMPTY) && data->tx_int_en;
}
static int uart_bee_irq_tx_complete(const struct device *dev)
{
return uart_bee_irq_tx_ready(dev);
}
static void uart_bee_irq_rx_enable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
struct uart_bee_data *data = dev->data;
data->rx_int_en = true;
UART_INTConfig(uart, UART_INT_RD_AVA, ENABLE);
UART_INTConfig(uart, UART_INT_RX_IDLE, ENABLE);
}
static void uart_bee_irq_rx_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
struct uart_bee_data *data = dev->data;
data->rx_int_en = false;
UART_INTConfig(uart, UART_INT_RD_AVA, DISABLE);
UART_INTConfig(uart, UART_INT_RX_IDLE, DISABLE);
}
static int uart_bee_irq_rx_ready(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
return UART_GetFlagStatus(uart, UART_FLAG_RX_DATA_AVA);
}
static void uart_bee_irq_err_enable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
UART_INTConfig(uart, UART_INT_RX_LINE_STS, ENABLE);
}
static void uart_bee_irq_err_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
UART_INTConfig(uart, UART_INT_RX_LINE_STS, DISABLE);
}
static int uart_bee_irq_is_pending(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
return ((UART_GetFlagStatus(uart, UART_FLAG_TX_EMPTY) && data->tx_int_en) ||
(UART_GetFlagStatus(uart, UART_INT_RD_AVA) && data->rx_int_en));
}
static void uart_bee_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
void *cb_data)
{
struct uart_bee_data *data = dev->data;
data->user_cb = cb;
data->user_data = cb_data;
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#ifdef CONFIG_UART_LINE_CTRL
int uart_bee_line_ctrl_set(const struct device *dev, uint32_t ctrl, uint32_t val)
{
LOG_ERR("Unsupported line_ctrl_set function");
return -ENOTSUP;
}
int uart_bee_line_ctrl_get(const struct device *dev, uint32_t ctrl, uint32_t *val)
{
LOG_ERR("Unsupported line_ctrl_get function");
return -ENOTSUP;
}
#endif
#ifdef CONFIG_UART_DRV_CMD
int uart_bee_drv_cmd(const struct device *dev, uint32_t cmd, uint32_t p)
{
LOG_ERR("Unsupported drv_cmd function");
return -ENOTSUP;
}
#endif
#ifdef CONFIG_UART_ASYNC_API
static void uart_bee_dma_replace_buffer(const struct device *dev);
static int uart_bee_async_rx_disable(const struct device *dev);
static void uart_bee_dma_tx_control(UART_TypeDef *uart, bool en)
{
#if defined(CONFIG_SOC_SERIES_RTL87X2G)
UART_TxDmaCmd(uart, en);
#elif defined(CONFIG_SOC_SERIES_RTL8752H)
if (en) {
uart->MISCR |= BIT(1);
} else {
uart->MISCR &= ~BIT(1);
}
#endif
}
static void uart_bee_dma_rx_control(UART_TypeDef *uart, bool en)
{
#if defined(CONFIG_SOC_SERIES_RTL87X2G)
UART_RxDmaCmd(uart, en);
#elif defined(CONFIG_SOC_SERIES_RTL8752H)
if (en) {
uart->MISCR |= BIT(2);
} else {
uart->MISCR &= ~BIT(2);
}
#endif
}
static int uart_bee_async_callback_set(const struct device *dev, uart_callback_t callback,
void *user_data)
{
struct uart_bee_data *data = dev->data;
data->async_cb = callback;
data->async_user_data = user_data;
return 0;
}
static inline void async_user_callback(struct uart_bee_data *data, struct uart_event *event)
{
if (data->async_cb) {
data->async_cb(data->dev, event, data->async_user_data);
}
}
static inline void async_evt_tx_done(struct uart_bee_data *data)
{
struct uart_event event = {.type = UART_TX_DONE,
.data.tx.buf = data->dma_tx.buffer,
.data.tx.len = data->dma_tx.counter};
/* Reset the TX buffer */
data->dma_tx.buffer_length = 0;
data->dma_tx.counter = 0;
async_user_callback(data, &event);
}
static inline void async_evt_tx_abort(struct uart_bee_data *data)
{
struct uart_event event = {.type = UART_TX_ABORTED,
.data.tx.buf = data->dma_tx.buffer,
.data.tx.len = data->dma_tx.counter};
/* Reset the TX buffer */
data->dma_tx.buffer_length = 0;
data->dma_tx.counter = 0;
async_user_callback(data, &event);
}
static inline void async_evt_rx_rdy(struct uart_bee_data *data)
{
struct uart_event event = {.type = UART_RX_RDY,
.data.rx.buf = data->dma_rx.buffer,
.data.rx.len = data->dma_rx.counter,
.data.rx.offset = 0};
/* Send event only for new data */
if (event.data.rx.len > 0) {
async_user_callback(data, &event);
}
}
static inline void async_evt_rx_buf_request(struct uart_bee_data *data)
{
struct uart_event event = {
.type = UART_RX_BUF_REQUEST,
};
async_user_callback(data, &event);
}
static inline void async_evt_rx_buf_release(struct uart_bee_data *data)
{
struct uart_event event = {
.type = UART_RX_BUF_RELEASED,
.data.rx_buf.buf = data->dma_rx.buffer,
};
async_user_callback(data, &event);
}
static inline void async_evt_rx_disable(struct uart_bee_data *data)
{
struct uart_event event = {
.type = UART_RX_DISABLED,
};
async_user_callback(data, &event);
}
static inline void async_evt_rx_err(struct uart_bee_data *data, int err_code)
{
struct uart_event event = {.type = UART_RX_STOPPED,
.data.rx_stop.reason = err_code,
.data.rx_stop.data.len = data->dma_rx.counter,
.data.rx_stop.data.offset = 0,
.data.rx_stop.data.buf = data->dma_rx.buffer};
async_user_callback(data, &event);
}
static inline void async_timer_start(struct k_work_delayable *work, int32_t timeout)
{
if ((timeout != SYS_FOREVER_US) && (timeout != 0)) {
LOG_DBG("Async timer started for %d us", timeout);
/* Start the timeout timer */
k_work_reschedule(work, K_USEC(timeout));
}
}
static inline void uart_bee_dma_tx_enable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
uart_bee_dma_tx_control(uart, true);
}
static inline void uart_bee_dma_tx_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
uart_bee_dma_tx_control(uart, false);
}
static inline void uart_bee_dma_rx_enable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
uart_bee_dma_rx_control(uart, true);
data->dma_rx.enabled = true;
}
static inline void uart_bee_dma_rx_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
uart_bee_dma_rx_control(uart, false);
data->dma_rx.enabled = false;
}
void uart_bee_dma_tx_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status)
{
const struct device *uart_dev = user_data;
struct uart_bee_data *data = uart_dev->data;
struct dma_status stat;
/* Disable the UART TX DMA requests */
uart_bee_dma_tx_disable(uart_dev);
/* Stop the TX timeout timer */
(void)k_work_cancel_delayable(&data->dma_tx.timeout_work);
/* Get the DMA TX length */
if (!dma_get_status(data->dma_tx.dma_dev, data->dma_tx.dma_channel, &stat)) {
data->dma_tx.counter = data->dma_tx.buffer_length - stat.pending_length;
}
LOG_DBG("channel=%d, status=%d, dma_tx.counter=%d", channel, status, data->dma_tx.counter);
data->dma_tx.buffer_length = 0;
/* Stop the TX DMA */
dma_stop(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
/* Generate TX_DONE event when TX is done */
async_evt_tx_done(data);
}
static void uart_bee_dma_replace_buffer(const struct device *dev)
{
struct uart_bee_data *data = dev->data;
LOG_DBG("Replacing RX buffer: %d", data->rx_next_buffer_len);
/* Replace the RX DMA buffer and reload the RX DMA */
data->dma_rx.blk_cfg.block_size = data->rx_next_buffer_len;
data->dma_rx.blk_cfg.dest_address = (uint32_t)(data->rx_next_buffer);
dma_reload(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
data->dma_rx.blk_cfg.source_address, data->dma_rx.blk_cfg.dest_address,
data->dma_rx.blk_cfg.block_size);
dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
}
static void uart_bee_dma_rx_calc_counter(struct uart_bee_data *data)
{
struct dma_status stat;
/* Get the DMA RX length */
if (!dma_get_status(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &stat)) {
data->dma_rx.counter = data->dma_rx.buffer_length - stat.pending_length;
}
}
static void uart_bee_rx_proceed_next_or_disable(const struct device *dev)
{
struct uart_bee_data *data = dev->data;
if (data->rx_next_buffer) {
/* Directly reload the RX DMA buffer and start DMA to minimize overhead, preventing
* data loss caused by UART RX FIFO overflow.
*/
uart_bee_dma_replace_buffer(dev);
/* Generate RX_RDY event with the received data */
async_evt_rx_rdy(data);
/* Generate RX_BUF_RELEASED event to release every passed buffer */
async_evt_rx_buf_release(data);
data->dma_rx.buffer = data->rx_next_buffer;
data->dma_rx.buffer_length = data->rx_next_buffer_len;
data->rx_next_buffer = 0;
data->rx_next_buffer_len = 0;
/* Generate RX_BUF_REQUEST event to get the next buffer */
async_evt_rx_buf_request(data);
} else {
/* No next buffer available. Proceed directly to the disable flow and report the
* received data.
*/
uart_bee_async_rx_disable(dev);
}
}
void uart_bee_dma_rx_cb(const struct device *dma_dev, void *user_data, uint32_t channel, int status)
{
const struct device *uart_dev = user_data;
struct uart_bee_data *data = uart_dev->data;
/* Get the DMA RX length */
uart_bee_dma_rx_calc_counter(data);
if (status < 0) {
async_evt_rx_err(data, status);
return;
}
/* Reload buffer or disable RX */
uart_bee_rx_proceed_next_or_disable(uart_dev);
/* Stop the RX timeout timer */
(void)k_work_cancel_delayable(&data->dma_rx.timeout_work);
}
static int uart_bee_async_tx(const struct device *dev, const uint8_t *tx_data, size_t buf_size,
int32_t timeout)
{
struct uart_bee_data *data = dev->data;
int ret;
LOG_DBG("bufsize=%d, timeout=%d", buf_size, timeout);
if (data->dma_tx.dma_dev == NULL) {
return -ENODEV;
}
if (data->dma_tx.buffer_length != 0) {
return -EBUSY;
}
data->dma_tx.buffer = (uint8_t *)tx_data;
data->dma_tx.buffer_length = 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_length;
ret = dma_config(data->dma_tx.dma_dev, data->dma_tx.dma_channel, &data->dma_tx.dma_cfg);
if (ret != 0) {
LOG_ERR("DMA TX config error!");
return -EINVAL;
}
/* Start the TX timeout timer */
async_timer_start(&data->dma_tx.timeout_work, data->dma_tx.timeout);
/* Enable the UART TX DMA requests */
uart_bee_dma_tx_enable(dev);
/* Start the TX dma */
dma_start(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
return 0;
}
static int uart_bee_async_tx_abort(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
size_t tx_buffer_length = data->dma_tx.buffer_length;
struct dma_status stat;
if (tx_buffer_length == 0) {
return -EFAULT;
}
/* Stop the TX timeout timer */
(void)k_work_cancel_delayable(&data->dma_tx.timeout_work);
/* Suspend the TX DMA to get the transmitted data length */
dma_suspend(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
/* Get the transmitted data length */
if (!dma_get_status(data->dma_tx.dma_dev, data->dma_tx.dma_channel, &stat)) {
data->dma_tx.counter = tx_buffer_length - stat.pending_length;
}
/* Stop the TX dma */
dma_stop(data->dma_tx.dma_dev, data->dma_tx.dma_channel);
while (UART_GetTxFIFODataLen(uart)) {
}
/* Generate TX_ABORTED event with the TX is aborted */
async_evt_tx_abort(data);
return 0;
}
static int uart_bee_async_rx_enable(const struct device *dev, uint8_t *rx_buf, size_t buf_size,
int32_t timeout)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
int ret;
LOG_DBG("buf_size=%d, timeout=%d", buf_size, timeout);
/* Flush the UART RX FIFO to prevent processing stale data received before enabling. */
uint32_t cnt = UART_GetRxFIFODataLen(uart);
for (uint32_t i = 0; i < cnt; i++) {
UART_ReceiveByte(uart);
}
if (data->dma_rx.dma_dev == NULL) {
return -ENODEV;
}
if (data->dma_rx.enabled) {
LOG_WRN("RX was already enabled");
return -EBUSY;
}
data->dma_rx.buffer = rx_buf;
data->dma_rx.buffer_length = buf_size;
data->dma_rx.timeout = timeout;
data->rx_next_buffer = 0;
data->rx_next_buffer_len = 0;
/* Disable UART RX AVA interrupts to let DMA to handle it */
UART_INTConfig(uart, UART_INT_RD_AVA, DISABLE);
data->dma_rx.blk_cfg.block_size = buf_size;
data->dma_rx.blk_cfg.dest_address = (uint32_t)rx_buf;
/* Configure the RX DMA with the passed buffer */
ret = dma_config(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &data->dma_rx.dma_cfg);
if (ret != 0) {
LOG_ERR("UART ERR: RX DMA config failed!");
return -EINVAL;
}
/* Enable the UART RX DMA requests */
uart_bee_dma_rx_enable(dev);
/* Enable UART_INT_RX_IDLE to define the end of a RX DMA transaction */
UART_INTConfig(uart, UART_INT_RX_IDLE, DISABLE);
UART_INTConfig(uart, UART_INT_RX_IDLE, ENABLE);
dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
/* Generate RX_BUF_REQUEST event to get the next buffer */
async_evt_rx_buf_request(data);
return ret;
}
static int uart_bee_async_rx_buf_rsp(const struct device *dev, uint8_t *buf, size_t len)
{
struct uart_bee_data *data = dev->data;
LOG_DBG("buf=0x%p len=%d", buf, len);
data->rx_next_buffer = buf;
data->rx_next_buffer_len = len;
return 0;
}
static int uart_bee_async_rx_disable(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
struct dma_status stat;
if (!data->dma_rx.enabled) {
async_evt_rx_disable(data);
return -EFAULT;
}
UART_INTConfig(uart, UART_INT_RX_IDLE, DISABLE);
/* Disable the UART RX DMA requests */
uart_bee_dma_rx_disable(dev);
/* Get the received DMA length before stopping the RX DMA */
if (!dma_get_status(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &stat)) {
data->dma_rx.counter = data->dma_rx.buffer_length - stat.pending_length;
}
/* Stop the RX DMA */
dma_stop(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
/* Stop the RX timeout timer */
(void)k_work_cancel_delayable(&data->dma_rx.timeout_work);
/* Generate RX_RDY event with the received data */
async_evt_rx_rdy(data);
/* Generate RX_BUF_RELEASED event to release every passed buffer */
while (data->dma_rx.buffer) {
async_evt_rx_buf_release(data);
data->dma_rx.buffer = data->rx_next_buffer;
data->dma_rx.buffer_length = 0;
data->rx_next_buffer = NULL;
data->rx_next_buffer_len = 0;
}
/* Generate RX_DISABLED event when RX is disabled */
async_evt_rx_disable(data);
return 0;
}
static void uart_bee_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_bee_data *data = CONTAINER_OF(tx_stream, struct uart_bee_data, dma_tx);
const struct device *dev = data->dev;
uart_bee_async_tx_abort(dev);
}
static void uart_bee_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_bee_data *data = CONTAINER_OF(rx_stream, struct uart_bee_data, dma_rx);
const struct device *dev = data->dev;
/* Get the DMA RX length */
uart_bee_dma_rx_calc_counter(data);
if (data->dma_rx.counter) {
/* Reload buffer or disable RX */
uart_bee_rx_proceed_next_or_disable(dev);
}
}
static int uart_bee_async_init(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
UART_TypeDef *uart = config->uart;
data->dev = dev;
if (data->dma_rx.dma_dev != NULL) {
if (!device_is_ready(data->dma_rx.dma_dev)) {
return -ENODEV;
}
}
if (data->dma_tx.dma_dev != NULL) {
if (!device_is_ready(data->dma_tx.dma_dev)) {
return -ENODEV;
}
}
if (data->dma_tx.dma_dev == NULL && data->dma_rx.dma_dev == NULL) {
return 0;
}
/*
* Claim the devicetree-assigned channels through the DMA controller's
* allocation bitmap so that dma_request_channel() (e.g. a memory-to-memory
* user on the same controller) can never hand them out again. Using a
* BIT(channel) filter forces exactly the DT channel and fails loudly if it
* is already taken.
*/
if (data->dma_rx.dma_dev != NULL) {
uint32_t ch_filter = BIT(data->dma_rx.dma_channel);
int ch = dma_request_channel(data->dma_rx.dma_dev, &ch_filter);
if (ch < 0) {
LOG_ERR("UART RX DMA channel %u already in use",
data->dma_rx.dma_channel);
return ch;
}
}
if (data->dma_tx.dma_dev != NULL) {
uint32_t ch_filter = BIT(data->dma_tx.dma_channel);
int ch = dma_request_channel(data->dma_tx.dma_dev, &ch_filter);
if (ch < 0) {
LOG_ERR("UART TX DMA channel %u already in use",
data->dma_tx.dma_channel);
return ch;
}
}
/* Disable both UART TX and UART RX DMA requests */
uart_bee_dma_rx_disable(dev);
uart_bee_dma_tx_disable(dev);
k_work_init_delayable(&data->dma_rx.timeout_work, uart_bee_async_rx_timeout);
k_work_init_delayable(&data->dma_tx.timeout_work, uart_bee_async_tx_timeout);
/* Configure DMA RX config */
memset(&data->dma_rx.blk_cfg, 0, sizeof(data->dma_rx.blk_cfg));
data->dma_rx.blk_cfg.source_address = UART_RX_FIFO_ADDR(uart);
data->dma_rx.blk_cfg.dest_address = 0; /* dest not ready */
data->dma_rx.blk_cfg.source_addr_adj = data->dma_rx.src_addr_increment;
data->dma_rx.blk_cfg.dest_addr_adj = data->dma_rx.dst_addr_increment;
/* Disable the RX circular buffer */
data->dma_rx.blk_cfg.source_reload_en = 0;
data->dma_rx.blk_cfg.dest_reload_en = 0;
data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg;
data->dma_rx.dma_cfg.user_data = (void *)dev;
data->rx_next_buffer = NULL;
data->rx_next_buffer_len = 0;
/* Configure DMA TX config */
memset(&data->dma_tx.blk_cfg, 0, sizeof(data->dma_tx.blk_cfg));
data->dma_tx.blk_cfg.dest_address = UART_TX_FIFO_ADDR(uart);
data->dma_tx.blk_cfg.source_address = 0; /* not ready */
data->dma_tx.blk_cfg.source_addr_adj = data->dma_tx.src_addr_increment;
data->dma_tx.blk_cfg.dest_addr_adj = data->dma_tx.dst_addr_increment;
data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg;
data->dma_tx.dma_cfg.user_data = (void *)dev;
return 0;
}
#endif /* CONFIG_UART_ASYNC_API */
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
#ifdef CONFIG_UART_ASYNC_API
static void uart_bee_handle_async_rx_idle(const struct device *dev)
{
struct uart_bee_data *data = dev->data;
if (!data->dma_rx.dma_dev) {
return;
}
/* Get the DMA RX length */
uart_bee_dma_rx_calc_counter(data);
if (data->dma_rx.counter) {
if (data->dma_rx.timeout == 0) {
/* Reload buffer or disable RX */
uart_bee_rx_proceed_next_or_disable(dev);
} else {
/* Start the RX timeout timer */
async_timer_start(&data->dma_rx.timeout_work, data->dma_rx.timeout);
}
}
}
#endif
static void uart_bee_isr(const struct device *dev)
{
struct uart_bee_data *data = dev->data;
const struct uart_bee_config *config = dev->config;
UART_TypeDef *uart = config->uart;
UART_GetIID(uart);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
if (data->user_cb) {
data->user_cb(dev, data->user_data);
}
#endif
if (UART_GetFlagStatus(uart, UART_FLAG_RX_IDLE)) {
UART_INTConfig(uart, UART_INT_RX_IDLE, DISABLE);
UART_INTConfig(uart, UART_INT_RX_IDLE, ENABLE);
#ifdef CONFIG_UART_ASYNC_API
uart_bee_handle_async_rx_idle(dev);
#endif
}
#ifdef CONFIG_UART_ASYNC_API
/* Clear errors */
uart_bee_err_check(dev);
#endif /* CONFIG_UART_ASYNC_API */
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API */
static int uart_bee_init(const struct device *dev)
{
const struct uart_bee_config *config = dev->config;
struct uart_bee_data *data = dev->data;
int err;
data->dev = dev;
/* Configure pinmux */
err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (err < 0) {
return err;
}
(void)clock_control_on(BEE_CLOCK_CONTROLLER, (clock_control_subsys_t)&config->clkid);
/* Configure peripheral */
err = uart_bee_configure(dev, &data->uart_config);
if (err) {
return err;
}
/* Enable nvic */
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
config->irq_config_func(dev);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API */
#ifdef CONFIG_UART_ASYNC_API
return uart_bee_async_init(dev);
#else
return 0;
#endif
}
static DEVICE_API(uart, uart_bee_driver_api) = {
.poll_in = uart_bee_poll_in,
.poll_out = uart_bee_poll_out,
.err_check = uart_bee_err_check,
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
.configure = uart_bee_configure,
.config_get = uart_bee_config_get,
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_bee_fifo_fill,
.fifo_read = uart_bee_fifo_read,
.irq_tx_enable = uart_bee_irq_tx_enable,
.irq_tx_disable = uart_bee_irq_tx_disable,
.irq_tx_ready = uart_bee_irq_tx_ready,
.irq_tx_complete = uart_bee_irq_tx_complete,
.irq_rx_enable = uart_bee_irq_rx_enable,
.irq_rx_disable = uart_bee_irq_rx_disable,
.irq_rx_ready = uart_bee_irq_rx_ready,
.irq_err_enable = uart_bee_irq_err_enable,
.irq_err_disable = uart_bee_irq_err_disable,
.irq_is_pending = uart_bee_irq_is_pending,
.irq_callback_set = uart_bee_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#ifdef CONFIG_UART_ASYNC_API
.callback_set = uart_bee_async_callback_set,
.tx = uart_bee_async_tx,
.tx_abort = uart_bee_async_tx_abort,
.rx_enable = uart_bee_async_rx_enable,
.rx_buf_rsp = uart_bee_async_rx_buf_rsp,
.rx_disable = uart_bee_async_rx_disable,
#endif /* CONFIG_UART_ASYNC_API */
#ifdef CONFIG_UART_LINE_CTRL
.line_ctrl_set = uart_bee_line_ctrl_set,
.line_ctrl_get = uart_bee_line_ctrl_get,
#endif
#ifdef CONFIG_UART_DRV_CMD
.drv_cmd = uart_bee_drv_cmd,
#endif
};
#ifdef CONFIG_UART_ASYNC_API
#define UART_DMA_CHANNEL_INIT(index, dir) \
.dma_dev = DEVICE_DT_GET(BEE_DMA_CTLR(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, slot), \
.channel_direction = \
BEE_DMA_CONFIG_DIRECTION(BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.channel_priority = \
BEE_DMA_CONFIG_PRIORITY(BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.source_data_size = BEE_DMA_CONFIG_SOURCE_DATA_SIZE( \
BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.dest_data_size = BEE_DMA_CONFIG_DESTINATION_DATA_SIZE( \
BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.source_burst_length = \
BEE_DMA_CONFIG_SOURCE_MSIZE(BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.dest_burst_length = BEE_DMA_CONFIG_DESTINATION_MSIZE( \
BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.block_count = 1, \
.cyclic = false, \
.complete_callback_en = 1, \
.dma_callback = uart_bee_dma_##dir##_cb, \
}, \
.src_addr_increment = BEE_DMA_CONFIG_SOURCE_ADDR_INC(BEE_DMA_CHANNEL_CONFIG(index, dir)), \
.dst_addr_increment = \
BEE_DMA_CONFIG_DESTINATION_ADDR_INC(BEE_DMA_CHANNEL_CONFIG(index, dir)),
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
#define BEE_UART_IRQ_HANDLER_DECL(index) \
static void uart_bee_irq_config_func_##index(const struct device *dev);
#define BEE_UART_IRQ_HANDLER(index) \
static void uart_bee_irq_config_func_##index(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), uart_bee_isr, \
DEVICE_DT_INST_GET(index), 0); \
irq_enable(DT_INST_IRQN(index)); \
}
#else
#define BEE_UART_IRQ_HANDLER_DECL(index) /* Not used */
#define BEE_UART_IRQ_HANDLER(index) /* Not used */
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
#define BEE_UART_IRQ_HANDLER_FUNC(index) .irq_config_func = uart_bee_irq_config_func_##index,
#else
#define BEE_UART_IRQ_HANDLER_FUNC(index) /* Not used */
#endif
#ifdef CONFIG_UART_ASYNC_API
#define UART_DMA_CHANNEL(index, dir) \
.dma_##dir = {COND_CODE_1(DT_INST_DMAS_HAS_NAME(index, dir), \
(UART_DMA_CHANNEL_INIT(index, dir)), (NULL))},
#else
#define UART_DMA_CHANNEL(index, dir)
#endif
#define BEE_UART_INIT(index) \
BEE_UART_IRQ_HANDLER_DECL(index) \
\
PINCTRL_DT_INST_DEFINE(index); \
\
static const struct uart_bee_config uart_bee_cfg_##index = { \
.uart = (UART_TypeDef *)DT_INST_REG_ADDR(index), \
.clkid = DT_INST_CLOCKS_CELL(index, id), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
.rx_threshold = DT_INST_PROP_OR(index, rx_threshold, 10), \
.hw_flow_ctrl = DT_INST_PROP_OR(index, flow_ctrl, false), \
BEE_UART_IRQ_HANDLER_FUNC(index)}; \
\
static struct uart_bee_data uart_bee_data_##index = { \
.uart_config = \
{ \
.baudrate = DT_INST_PROP(index, current_speed), \
.parity = \
DT_INST_ENUM_IDX_OR(index, parity, UART_CFG_PARITY_NONE), \
.stop_bits = DT_INST_ENUM_IDX_OR(index, stop_bits, \
UART_CFG_STOP_BITS_1), \
.data_bits = DT_INST_ENUM_IDX_OR(index, data_bits, \
UART_CFG_DATA_BITS_8), \
.flow_ctrl = DT_INST_PROP(index, hw_flow_control), \
}, \
UART_DMA_CHANNEL(index, rx) UART_DMA_CHANNEL(index, tx)}; \
\
DEVICE_DT_INST_DEFINE(index, &uart_bee_init, NULL, &uart_bee_data_##index, \
&uart_bee_cfg_##index, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
&uart_bee_driver_api); \
\
BEE_UART_IRQ_HANDLER(index)
DT_INST_FOREACH_STATUS_OKAY(BEE_UART_INIT)