diff --git a/drivers/serial/Kconfig.max32 b/drivers/serial/Kconfig.max32 index c50199bc856f..b2c86c2359eb 100644 --- a/drivers/serial/Kconfig.max32 +++ b/drivers/serial/Kconfig.max32 @@ -16,3 +16,15 @@ config UART_MAX32 This option enables the UART driver for MAX32 family of processors. Say y if you wish to use serial port on MAX32 MCU. + +if UART_MAX32 + +config UART_TX_CACHE_LEN + int "TX cache buffer size" + range 8 64 + default 8 + help + Size of UART transmit buffer that is used when source buffer + is not located in a DMA-able region. + +endif # UART_MAX32 diff --git a/drivers/serial/uart_max32.c b/drivers/serial/uart_max32.c index 1b5a4ad250f2..9cd16a59066c 100644 --- a/drivers/serial/uart_max32.c +++ b/drivers/serial/uart_max32.c @@ -6,6 +6,7 @@ #ifdef CONFIG_UART_ASYNC_API #include +#include #endif #include #include @@ -43,9 +44,15 @@ struct max32_uart_config { }; #ifdef CONFIG_UART_ASYNC_API +#define MAX32_UART_TX_CACHE_NUM 2 struct max32_uart_async_tx { const uint8_t *buf; + const uint8_t *src; size_t len; + uint8_t cache[MAX32_UART_TX_CACHE_NUM][CONFIG_UART_TX_CACHE_LEN]; + uint8_t cache_id; + struct dma_block_config dma_blk; + int32_t timeout; struct k_work_delayable timeout_work; }; @@ -86,6 +93,10 @@ struct max32_uart_data { static void uart_max32_isr(const struct device *dev); #endif +#ifdef CONFIG_UART_ASYNC_API +static int uart_max32_tx_dma_load(const struct device *dev, uint8_t *buf, size_t len); +#endif + static void api_poll_out(const struct device *dev, unsigned char c) { const struct max32_uart_config *cfg = dev->config; @@ -492,13 +503,22 @@ static void async_user_callback(const struct device *dev, struct uart_event *evt } } +static uint32_t load_tx_cache(const uint8_t *src, size_t len, uint8_t *dest) +{ + memcpy(dest, src, MIN(len, CONFIG_UART_TX_CACHE_LEN)); + + return MIN(len, CONFIG_UART_TX_CACHE_LEN); +} + static void uart_max32_async_tx_callback(const struct device *dma_dev, void *user_data, uint32_t channel, int status) { const struct device *dev = user_data; const struct max32_uart_config *config = dev->config; struct max32_uart_data *data = dev->data; + struct max32_uart_async_tx *tx = &data->async.tx; struct dma_status dma_stat; + int ret; unsigned int key = irq_lock(); @@ -509,17 +529,73 @@ static void uart_max32_async_tx_callback(const struct device *dma_dev, void *use return; } - k_work_cancel_delayable(&data->async.tx.timeout_work); + k_work_cancel_delayable(&tx->timeout_work); Wrap_MXC_UART_DisableTxDMA(config->regs); irq_unlock(key); - struct uart_event tx_done = { - .type = status == 0 ? UART_TX_DONE : UART_TX_ABORTED, - .data.tx.buf = data->async.tx.buf, - .data.tx.len = data->async.tx.len, - }; - async_user_callback(dev, &tx_done); + tx->len -= tx->dma_blk.block_size; + if (tx->len > 0) { + tx->cache_id = !(tx->cache_id); + ret = uart_max32_tx_dma_load(dev, tx->cache[tx->cache_id], + MIN(tx->len, CONFIG_UART_TX_CACHE_LEN)); + if (ret < 0) { + LOG_ERR("Error configuring Tx DMA (%d)", ret); + return; + } + + ret = dma_start(config->tx_dma.dev, config->tx_dma.channel); + if (ret < 0) { + LOG_ERR("Error starting Tx DMA (%d)", ret); + return; + } + + async_timer_start(&tx->timeout_work, tx->timeout); + + Wrap_MXC_UART_SetTxDMALevel(config->regs, 2); + Wrap_MXC_UART_EnableTxDMA(config->regs); + + /* Load next chunk as well */ + if (tx->len > CONFIG_UART_TX_CACHE_LEN) { + tx->src += load_tx_cache(tx->src, tx->len - CONFIG_UART_TX_CACHE_LEN, + tx->cache[!(tx->cache_id)]); + } + } else { + struct uart_event tx_done = { + .type = status == 0 ? UART_TX_DONE : UART_TX_ABORTED, + .data.tx.buf = tx->buf, + .data.tx.len = tx->len, + }; + async_user_callback(dev, &tx_done); + } +} + +static int uart_max32_tx_dma_load(const struct device *dev, uint8_t *buf, size_t len) +{ + int ret; + const struct max32_uart_config *config = dev->config; + struct max32_uart_data *data = dev->data; + struct dma_config dma_cfg = {0}; + struct dma_block_config *dma_blk = &data->async.tx.dma_blk; + + dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL; + dma_cfg.dma_callback = uart_max32_async_tx_callback; + dma_cfg.user_data = (void *)dev; + dma_cfg.dma_slot = config->tx_dma.slot; + dma_cfg.block_count = 1; + dma_cfg.source_data_size = 1U; + dma_cfg.source_burst_length = 1U; + dma_cfg.dest_data_size = 1U; + dma_cfg.head_block = dma_blk; + dma_blk->block_size = len; + dma_blk->source_address = (uint32_t)buf; + + ret = dma_config(config->tx_dma.dev, config->tx_dma.channel, &dma_cfg); + if (ret < 0) { + return ret; + } + + return 0; } static int api_callback_set(const struct device *dev, uart_callback_t callback, void *user_data) @@ -537,15 +613,14 @@ static int api_tx(const struct device *dev, const uint8_t *buf, size_t len, int3 struct max32_uart_data *data = dev->data; const struct max32_uart_config *config = dev->config; struct dma_status dma_stat; - struct dma_config dma_cfg = {0}; - struct dma_block_config dma_blk = {0}; int ret; + bool use_cache = false; unsigned int key = irq_lock(); if (config->tx_dma.channel == 0xFF) { LOG_ERR("Tx DMA channel is not configured"); - irq_unlock(key); - return -ENOTSUP; + ret = -ENOTSUP; + goto unlock; } ret = dma_get_status(config->tx_dma.dev, config->tx_dma.channel, &dma_stat); @@ -557,38 +632,37 @@ static int api_tx(const struct device *dev, const uint8_t *buf, size_t len, int3 data->async.tx.buf = buf; data->async.tx.len = len; + data->async.tx.src = data->async.tx.buf; + + if (((uint32_t)buf < MXC_SRAM_MEM_BASE) || + (((uint32_t)buf + len) > (MXC_SRAM_MEM_BASE + MXC_SRAM_MEM_SIZE))) { + use_cache = true; + len = load_tx_cache(data->async.tx.src, MIN(len, CONFIG_UART_TX_CACHE_LEN), + data->async.tx.cache[0]); + data->async.tx.src += len; + data->async.tx.cache_id = 0; + } - dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL; - dma_cfg.dma_callback = uart_max32_async_tx_callback; - dma_cfg.user_data = (void *)dev; - dma_cfg.dma_slot = config->tx_dma.slot; - dma_cfg.block_count = 1; - dma_cfg.source_data_size = 1U; - dma_cfg.source_burst_length = 1U; - dma_cfg.dest_data_size = 1U; - dma_cfg.head_block = &dma_blk; - dma_blk.block_size = len; - dma_blk.source_address = (uint32_t)buf; - - ret = dma_config(config->tx_dma.dev, config->tx_dma.channel, &dma_cfg); + ret = uart_max32_tx_dma_load(dev, use_cache ? data->async.tx.cache[0] : ((uint8_t *)buf), + len); if (ret < 0) { LOG_ERR("Error configuring Tx DMA (%d)", ret); - irq_unlock(key); - return ret; + goto unlock; } ret = dma_start(config->tx_dma.dev, config->tx_dma.channel); if (ret < 0) { LOG_ERR("Error starting Tx DMA (%d)", ret); - irq_unlock(key); - return ret; + goto unlock; } + data->async.tx.timeout = timeout; async_timer_start(&data->async.tx.timeout_work, timeout); Wrap_MXC_UART_SetTxDMALevel(config->regs, 2); Wrap_MXC_UART_EnableTxDMA(config->regs); +unlock: irq_unlock(key); return ret; @@ -709,6 +783,12 @@ static void uart_max32_async_rx_callback(const struct device *dma_dev, void *use unsigned int key = irq_lock(); dma_get_status(config->rx_dma.dev, config->rx_dma.channel, &dma_stat); + + if (dma_stat.pending_length > 0) { + irq_unlock(key); + return; + } + total_rx = async->rx.len - dma_stat.pending_length; api_irq_rx_disable(dev); @@ -717,7 +797,6 @@ static void uart_max32_async_rx_callback(const struct device *dma_dev, void *use if (total_rx > async->rx.offset) { async->rx.counter = total_rx - async->rx.offset; - struct uart_event rdy_event = { .type = UART_RX_RDY, .data.rx.buf = async->rx.buf,