zephyr/drivers/serial/uart_esp32.c
Sylvio Alves d0d7a7909e driver: uart: esp32: get port number from reg address
ESP32 uart driver requires uart port number to its low level calls.

In case uart0 is disabled and uart1 is enabled, driver will set
default port num to 0 when it should be 1. This fixes this scenario
by retrieving uart pot number based on periphral address.

Fixes #69973

Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>
2024-03-12 09:48:36 +00:00

1022 lines
28 KiB
C

/*
* Copyright (c) 2019 Mohamed ElShahawi (extremegtx@hotmail.com)
* Copyright (c) 2023 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_uart
/* Include esp-idf headers first to avoid redefining BIT() macro */
/* TODO: include w/o prefix */
#ifdef CONFIG_SOC_SERIES_ESP32
#include <esp32/rom/ets_sys.h>
#include <esp32/rom/gpio.h>
#include <soc/dport_reg.h>
#elif defined(CONFIG_SOC_SERIES_ESP32S2)
#include <esp32s2/rom/ets_sys.h>
#include <esp32s2/rom/gpio.h>
#include <soc/dport_reg.h>
#elif defined(CONFIG_SOC_SERIES_ESP32S3)
#include <esp32s3/rom/ets_sys.h>
#include <esp32s3/rom/gpio.h>
#include <zephyr/dt-bindings/clock/esp32s3_clock.h>
#elif defined(CONFIG_SOC_SERIES_ESP32C3)
#include <esp32c3/rom/ets_sys.h>
#include <esp32c3/rom/gpio.h>
#include <zephyr/dt-bindings/clock/esp32c3_clock.h>
#endif
#ifdef CONFIG_UART_ASYNC_API
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/dma/dma_esp32.h>
#include <hal/uhci_ll.h>
#endif
#include <soc/uart_struct.h>
#include <hal/uart_ll.h>
#include <hal/uart_hal.h>
#include <hal/uart_types.h>
#include <zephyr/drivers/pinctrl.h>
#include <soc/uart_reg.h>
#include <zephyr/device.h>
#include <soc.h>
#include <zephyr/drivers/uart.h>
#ifndef CONFIG_SOC_SERIES_ESP32C3
#include <zephyr/drivers/interrupt_controller/intc_esp32.h>
#else
#include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
#endif
#include <zephyr/drivers/clock_control.h>
#include <errno.h>
#include <zephyr/sys/util.h>
#include <esp_attr.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(uart_esp32, CONFIG_UART_LOG_LEVEL);
#ifdef CONFIG_SOC_SERIES_ESP32C3
#define ISR_HANDLER isr_handler_t
#else
#define ISR_HANDLER intr_handler_t
#endif
struct uart_esp32_config {
const struct device *clock_dev;
const struct pinctrl_dev_config *pcfg;
const clock_control_subsys_t clock_subsys;
int irq_source;
int irq_priority;
#if CONFIG_UART_ASYNC_API
const struct device *dma_dev;
uint8_t tx_dma_channel;
uint8_t rx_dma_channel;
#endif
};
#if CONFIG_UART_ASYNC_API
struct uart_esp32_async_data {
struct k_work_delayable tx_timeout_work;
const uint8_t *tx_buf;
size_t tx_len;
struct k_work_delayable rx_timeout_work;
uint8_t *rx_buf;
uint8_t *rx_next_buf;
size_t rx_len;
size_t rx_next_len;
size_t rx_timeout;
volatile size_t rx_counter;
size_t rx_offset;
uart_callback_t cb;
void *user_data;
};
#endif
/* driver data */
struct uart_esp32_data {
struct uart_config uart_config;
uart_hal_context_t hal;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t irq_cb;
void *irq_cb_data;
#endif
#if CONFIG_UART_ASYNC_API
struct uart_esp32_async_data async;
uhci_dev_t *uhci_dev;
const struct device *uart_dev;
#endif
};
#define UART_FIFO_LIMIT (UART_LL_FIFO_DEF_LEN)
#define UART_TX_FIFO_THRESH 0x1
#define UART_RX_FIFO_THRESH 0x16
#if CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API
static void uart_esp32_isr(void *arg);
#endif
static int uart_esp32_poll_in(const struct device *dev, unsigned char *p_char)
{
struct uart_esp32_data *data = dev->data;
int inout_rd_len = 1;
if (uart_hal_get_rxfifo_len(&data->hal) == 0) {
return -1;
}
uart_hal_read_rxfifo(&data->hal, p_char, &inout_rd_len);
return 0;
}
static void uart_esp32_poll_out(const struct device *dev, unsigned char c)
{
struct uart_esp32_data *data = dev->data;
uint32_t written;
/* Wait for space in FIFO */
while (uart_hal_get_txfifo_len(&data->hal) == 0) {
; /* Wait */
}
/* Send a character */
uart_hal_write_txfifo(&data->hal, &c, 1, &written);
}
static int uart_esp32_err_check(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uint32_t mask = uart_hal_get_intsts_mask(&data->hal);
uint32_t err = mask & (UART_INTR_PARITY_ERR | UART_INTR_FRAM_ERR);
return err;
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
static int uart_esp32_config_get(const struct device *dev, struct uart_config *cfg)
{
struct uart_esp32_data *data = dev->data;
uart_parity_t parity;
uart_stop_bits_t stop_bit;
uart_word_length_t data_bit;
uart_hw_flowcontrol_t hw_flow;
uart_hal_get_baudrate(&data->hal, &cfg->baudrate);
uart_hal_get_parity(&data->hal, &parity);
switch (parity) {
case UART_PARITY_DISABLE:
cfg->parity = UART_CFG_PARITY_NONE;
break;
case UART_PARITY_EVEN:
cfg->parity = UART_CFG_PARITY_EVEN;
break;
case UART_PARITY_ODD:
cfg->parity = UART_CFG_PARITY_ODD;
break;
default:
return -ENOTSUP;
}
uart_hal_get_stop_bits(&data->hal, &stop_bit);
switch (stop_bit) {
case UART_STOP_BITS_1:
cfg->stop_bits = UART_CFG_STOP_BITS_1;
break;
case UART_STOP_BITS_1_5:
cfg->stop_bits = UART_CFG_STOP_BITS_1_5;
break;
case UART_STOP_BITS_2:
cfg->stop_bits = UART_CFG_STOP_BITS_2;
break;
default:
return -ENOTSUP;
}
uart_hal_get_data_bit_num(&data->hal, &data_bit);
switch (data_bit) {
case UART_DATA_5_BITS:
cfg->data_bits = UART_CFG_DATA_BITS_5;
break;
case UART_DATA_6_BITS:
cfg->data_bits = UART_CFG_DATA_BITS_6;
break;
case UART_DATA_7_BITS:
cfg->data_bits = UART_CFG_DATA_BITS_7;
break;
case UART_DATA_8_BITS:
cfg->data_bits = UART_CFG_DATA_BITS_8;
break;
default:
return -ENOTSUP;
}
uart_hal_get_hw_flow_ctrl(&data->hal, &hw_flow);
switch (hw_flow) {
case UART_HW_FLOWCTRL_DISABLE:
cfg->flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
break;
case UART_HW_FLOWCTRL_CTS_RTS:
cfg->flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS;
break;
default:
return -ENOTSUP;
}
if (uart_hal_is_mode_rs485_half_duplex(&data->hal)) {
cfg->flow_ctrl = UART_CFG_FLOW_CTRL_RS485;
}
return 0;
}
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
static int uart_esp32_configure(const struct device *dev, const struct uart_config *cfg)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
if (!device_is_ready(config->clock_dev)) {
return -ENODEV;
}
clock_control_on(config->clock_dev, config->clock_subsys);
uart_hal_set_sclk(&data->hal, UART_SCLK_APB);
uart_hal_set_rxfifo_full_thr(&data->hal, UART_RX_FIFO_THRESH);
uart_hal_set_txfifo_empty_thr(&data->hal, UART_TX_FIFO_THRESH);
uart_hal_rxfifo_rst(&data->hal);
switch (cfg->parity) {
case UART_CFG_PARITY_NONE:
uart_hal_set_parity(&data->hal, UART_PARITY_DISABLE);
break;
case UART_CFG_PARITY_EVEN:
uart_hal_set_parity(&data->hal, UART_PARITY_EVEN);
break;
case UART_CFG_PARITY_ODD:
uart_hal_set_parity(&data->hal, UART_PARITY_ODD);
break;
default:
return -ENOTSUP;
}
switch (cfg->stop_bits) {
case UART_CFG_STOP_BITS_1:
uart_hal_set_stop_bits(&data->hal, UART_STOP_BITS_1);
break;
case UART_CFG_STOP_BITS_1_5:
uart_hal_set_stop_bits(&data->hal, UART_STOP_BITS_1_5);
break;
case UART_CFG_STOP_BITS_2:
uart_hal_set_stop_bits(&data->hal, UART_STOP_BITS_2);
break;
default:
return -ENOTSUP;
}
switch (cfg->data_bits) {
case UART_CFG_DATA_BITS_5:
uart_hal_set_data_bit_num(&data->hal, UART_DATA_5_BITS);
break;
case UART_CFG_DATA_BITS_6:
uart_hal_set_data_bit_num(&data->hal, UART_DATA_6_BITS);
break;
case UART_CFG_DATA_BITS_7:
uart_hal_set_data_bit_num(&data->hal, UART_DATA_7_BITS);
break;
case UART_CFG_DATA_BITS_8:
uart_hal_set_data_bit_num(&data->hal, UART_DATA_8_BITS);
break;
default:
return -ENOTSUP;
}
uart_hal_set_mode(&data->hal, UART_MODE_UART);
switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE:
uart_hal_set_hw_flow_ctrl(&data->hal, UART_HW_FLOWCTRL_DISABLE, 0);
break;
case UART_CFG_FLOW_CTRL_RTS_CTS:
uart_hal_set_hw_flow_ctrl(&data->hal, UART_HW_FLOWCTRL_CTS_RTS, 10);
break;
case UART_CFG_FLOW_CTRL_RS485:
uart_hal_set_mode(&data->hal, UART_MODE_RS485_HALF_DUPLEX);
break;
default:
return -ENOTSUP;
}
uart_hal_set_baudrate(&data->hal, cfg->baudrate);
uart_hal_set_rx_timeout(&data->hal, 0x16);
return 0;
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_esp32_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
{
struct uart_esp32_data *data = dev->data;
uint32_t written = 0;
if (len < 0) {
return 0;
}
uart_hal_write_txfifo(&data->hal, tx_data, len, &written);
return written;
}
static int uart_esp32_fifo_read(const struct device *dev, uint8_t *rx_data, const int len)
{
struct uart_esp32_data *data = dev->data;
const int num_rx = uart_hal_get_rxfifo_len(&data->hal);
int read = MIN(len, num_rx);
if (!read) {
return 0;
}
uart_hal_read_rxfifo(&data->hal, rx_data, &read);
return read;
}
static void uart_esp32_irq_tx_enable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_TXFIFO_EMPTY);
uart_hal_ena_intr_mask(&data->hal, UART_INTR_TXFIFO_EMPTY);
}
static void uart_esp32_irq_tx_disable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_disable_intr_mask(&data->hal, UART_INTR_TXFIFO_EMPTY);
}
static int uart_esp32_irq_tx_ready(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
return (uart_hal_get_txfifo_len(&data->hal) > 0 &&
uart_hal_get_intr_ena_status(&data->hal) & UART_INTR_TXFIFO_EMPTY);
}
static void uart_esp32_irq_rx_disable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_disable_intr_mask(&data->hal, UART_INTR_RXFIFO_FULL);
uart_hal_disable_intr_mask(&data->hal, UART_INTR_RXFIFO_TOUT);
}
static int uart_esp32_irq_tx_complete(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
return uart_hal_is_tx_idle(&data->hal);
}
static int uart_esp32_irq_rx_ready(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
return (uart_hal_get_rxfifo_len(&data->hal) > 0);
}
static void uart_esp32_irq_err_enable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
/* enable framing, parity */
uart_hal_ena_intr_mask(&data->hal, UART_INTR_FRAM_ERR);
uart_hal_ena_intr_mask(&data->hal, UART_INTR_PARITY_ERR);
}
static void uart_esp32_irq_err_disable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_disable_intr_mask(&data->hal, UART_INTR_FRAM_ERR);
uart_hal_disable_intr_mask(&data->hal, UART_INTR_PARITY_ERR);
}
static int uart_esp32_irq_is_pending(const struct device *dev)
{
return uart_esp32_irq_rx_ready(dev) || uart_esp32_irq_tx_ready(dev);
}
static int uart_esp32_irq_update(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_RXFIFO_FULL);
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_RXFIFO_TOUT);
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_TXFIFO_EMPTY);
return 1;
}
static void uart_esp32_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
void *cb_data)
{
struct uart_esp32_data *data = dev->data;
data->irq_cb = cb;
data->irq_cb_data = cb_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->async.cb = NULL;
data->async.user_data = NULL;
#endif
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#ifdef CONFIG_UART_ASYNC_API
static inline void uart_esp32_async_timer_start(struct k_work_delayable *work, size_t timeout)
{
if ((timeout != SYS_FOREVER_US) && (timeout != 0)) {
LOG_DBG("Async timer started for %d us", timeout);
k_work_reschedule(work, K_USEC(timeout));
}
}
#endif
#if CONFIG_UART_ASYNC_API || CONFIG_UART_INTERRUPT_DRIVEN
static void uart_esp32_irq_rx_enable(const struct device *dev)
{
struct uart_esp32_data *data = dev->data;
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_RXFIFO_FULL);
uart_hal_clr_intsts_mask(&data->hal, UART_INTR_RXFIFO_TOUT);
uart_hal_ena_intr_mask(&data->hal, UART_INTR_RXFIFO_FULL);
uart_hal_ena_intr_mask(&data->hal, UART_INTR_RXFIFO_TOUT);
}
static void uart_esp32_isr(void *arg)
{
const struct device *dev = (const struct device *)arg;
struct uart_esp32_data *data = dev->data;
uint32_t uart_intr_status = uart_hal_get_intsts_mask(&data->hal);
const struct uart_esp32_config *config = dev->config;
if (uart_intr_status == 0) {
return;
}
uart_hal_clr_intsts_mask(&data->hal, uart_intr_status);
#if CONFIG_UART_INTERRUPT_DRIVEN
/* Verify if the callback has been registered */
if (data->irq_cb) {
data->irq_cb(dev, data->irq_cb_data);
}
#endif
#if CONFIG_UART_ASYNC_API
if (uart_intr_status & UART_INTR_RXFIFO_FULL) {
data->async.rx_counter++;
uart_esp32_async_timer_start(&data->async.rx_timeout_work, data->async.rx_timeout);
}
#endif
}
#endif
#if CONFIG_UART_ASYNC_API
static void IRAM_ATTR uart_esp32_dma_rx_done(const struct device *dma_dev, void *user_data,
uint32_t channel, int status)
{
const struct device *uart_dev = user_data;
const struct uart_esp32_config *config = uart_dev->config;
struct uart_esp32_data *data = uart_dev->data;
struct uart_event evt = {0};
struct dma_status dma_status = {0};
unsigned int key = irq_lock();
/* If the receive buffer is not complete we reload the DMA at current buffer position and
* let the timeout callback handle the notifications
*/
if (data->async.rx_counter != data->async.rx_len) {
dma_reload(config->dma_dev, config->rx_dma_channel, 0,
(uint32_t)data->async.rx_buf + data->async.rx_counter,
data->async.rx_len - data->async.rx_counter);
dma_start(config->dma_dev, config->rx_dma_channel);
data->uhci_dev->pkt_thres.thrs = data->async.rx_len - data->async.rx_counter;
irq_unlock(key);
return;
}
/*Notify RX_RDY*/
evt.type = UART_RX_RDY;
evt.data.rx.buf = data->async.rx_buf;
evt.data.rx.len = data->async.rx_counter - data->async.rx_offset;
evt.data.rx.offset = data->async.rx_offset;
if (data->async.cb && evt.data.rx.len) {
data->async.cb(data->uart_dev, &evt, data->async.user_data);
}
data->async.rx_offset = 0;
data->async.rx_counter = 0;
/*Release current buffer*/
evt.type = UART_RX_BUF_RELEASED;
evt.data.rx_buf.buf = data->async.rx_buf;
if (data->async.cb) {
data->async.cb(uart_dev, &evt, data->async.user_data);
}
/*Load next buffer and request another*/
data->async.rx_buf = data->async.rx_next_buf;
data->async.rx_len = data->async.rx_next_len;
data->async.rx_next_buf = NULL;
data->async.rx_next_len = 0U;
evt.type = UART_RX_BUF_REQUEST;
if (data->async.cb) {
data->async.cb(uart_dev, &evt, data->async.user_data);
}
/*Notify RX_DISABLED when there is no buffer*/
if (!data->async.rx_buf) {
evt.type = UART_RX_DISABLED;
if (data->async.cb) {
data->async.cb(uart_dev, &evt, data->async.user_data);
}
} else {
/*Reload DMA with new buffer*/
dma_reload(config->dma_dev, config->rx_dma_channel, 0, (uint32_t)data->async.rx_buf,
data->async.rx_len);
dma_start(config->dma_dev, config->rx_dma_channel);
data->uhci_dev->pkt_thres.thrs = data->async.rx_len;
}
irq_unlock(key);
}
static void IRAM_ATTR uart_esp32_dma_tx_done(const struct device *dma_dev, void *user_data,
uint32_t channel, int status)
{
const struct device *uart_dev = user_data;
const struct uart_esp32_config *config = uart_dev->config;
struct uart_esp32_data *data = uart_dev->data;
struct uart_event evt = {0};
unsigned int key = irq_lock();
k_work_cancel_delayable(&data->async.tx_timeout_work);
evt.type = UART_TX_DONE;
evt.data.tx.buf = data->async.tx_buf;
evt.data.tx.len = data->async.tx_len;
if (data->async.cb) {
data->async.cb(uart_dev, &evt, data->async.user_data);
}
/* Reset TX Buffer */
data->async.tx_buf = NULL;
data->async.tx_len = 0U;
irq_unlock(key);
}
static int uart_esp32_async_tx_abort(const struct device *dev)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
struct uart_event evt = {0};
int err = 0;
unsigned int key = irq_lock();
k_work_cancel_delayable(&data->async.tx_timeout_work);
err = dma_stop(config->dma_dev, config->tx_dma_channel);
if (err) {
LOG_ERR("Error stopping Tx DMA (%d)", err);
goto unlock;
}
evt.type = UART_TX_ABORTED;
evt.data.tx.buf = data->async.tx_buf;
evt.data.tx.len = data->async.tx_len;
if (data->async.cb) {
data->async.cb(dev, &evt, data->async.user_data);
}
unlock:
irq_unlock(key);
return err;
}
static void uart_esp32_async_tx_timeout(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct uart_esp32_async_data *async =
CONTAINER_OF(dwork, struct uart_esp32_async_data, tx_timeout_work);
struct uart_esp32_data *data = CONTAINER_OF(async, struct uart_esp32_data, async);
uart_esp32_async_tx_abort(data->uart_dev);
}
static void uart_esp32_async_rx_timeout(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct uart_esp32_async_data *async =
CONTAINER_OF(dwork, struct uart_esp32_async_data, rx_timeout_work);
struct uart_esp32_data *data = CONTAINER_OF(async, struct uart_esp32_data, async);
const struct uart_esp32_config *config = data->uart_dev->config;
struct uart_event evt = {0};
int err = 0;
unsigned int key = irq_lock();
evt.type = UART_RX_RDY;
evt.data.rx.buf = data->async.rx_buf;
evt.data.rx.len = data->async.rx_counter - data->async.rx_offset;
evt.data.rx.offset = data->async.rx_offset;
if (data->async.cb && evt.data.rx.len) {
data->async.cb(data->uart_dev, &evt, data->async.user_data);
}
data->async.rx_offset = data->async.rx_counter;
k_work_cancel_delayable(&data->async.rx_timeout_work);
irq_unlock(key);
}
static int uart_esp32_async_callback_set(const struct device *dev, uart_callback_t callback,
void *user_data)
{
struct uart_esp32_data *data = dev->data;
if (!callback) {
return -EINVAL;
}
data->async.cb = callback;
data->async.user_data = user_data;
#if defined(CONFIG_UART_EXCLUSIVE_API_CALLBACKS)
data->irq_cb = NULL;
data->irq_cb_data = NULL;
#endif
return 0;
}
static int uart_esp32_async_tx(const struct device *dev, const uint8_t *buf, size_t len,
int32_t timeout)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
struct dma_config dma_cfg = {0};
struct dma_block_config dma_blk = {0};
struct dma_status dma_status = {0};
int err = 0;
unsigned int key = irq_lock();
if (config->tx_dma_channel == 0xFF) {
LOG_ERR("Tx DMA channel is not configured");
err = -ENOTSUP;
goto unlock;
}
err = dma_get_status(config->dma_dev, config->tx_dma_channel, &dma_status);
if (err) {
LOG_ERR("Unable to get Tx status (%d)", err);
goto unlock;
}
if (dma_status.busy) {
LOG_ERR("Tx DMA Channel is busy");
err = -EBUSY;
goto unlock;
}
data->async.tx_buf = buf;
data->async.tx_len = len;
dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
dma_cfg.dma_callback = uart_esp32_dma_tx_done;
dma_cfg.user_data = (void *)dev;
dma_cfg.dma_slot = GDMA_TRIG_PERIPH_UHCI0;
dma_cfg.block_count = 1;
dma_cfg.head_block = &dma_blk;
dma_blk.block_size = len;
dma_blk.source_address = (uint32_t)buf;
err = dma_config(config->dma_dev, config->tx_dma_channel, &dma_cfg);
if (err) {
LOG_ERR("Error configuring Tx DMA (%d)", err);
goto unlock;
}
uart_esp32_async_timer_start(&data->async.tx_timeout_work, timeout);
err = dma_start(config->dma_dev, config->tx_dma_channel);
if (err) {
LOG_ERR("Error starting Tx DMA (%d)", err);
goto unlock;
}
unlock:
irq_unlock(key);
return err;
}
static int uart_esp32_async_rx_enable(const struct device *dev, uint8_t *buf, size_t len,
int32_t timeout)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
struct dma_config dma_cfg = {0};
struct dma_block_config dma_blk = {0};
struct dma_status dma_status = {0};
int err = 0;
struct uart_event evt = {0};
if (config->rx_dma_channel == 0xFF) {
LOG_ERR("Rx DMA channel is not configured");
return -ENOTSUP;
}
err = dma_get_status(config->dma_dev, config->rx_dma_channel, &dma_status);
if (err) {
LOG_ERR("Unable to get Rx status (%d)", err);
return err;
}
if (dma_status.busy) {
LOG_ERR("Rx DMA Channel is busy");
return -EBUSY;
}
unsigned int key = irq_lock();
data->async.rx_buf = buf;
data->async.rx_len = len;
data->async.rx_timeout = timeout;
dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
dma_cfg.dma_callback = uart_esp32_dma_rx_done;
dma_cfg.user_data = (void *)dev;
dma_cfg.dma_slot = GDMA_TRIG_PERIPH_UHCI0;
dma_cfg.block_count = 1;
dma_cfg.head_block = &dma_blk;
dma_blk.block_size = len;
dma_blk.dest_address = (uint32_t)data->async.rx_buf;
err = dma_config(config->dma_dev, config->rx_dma_channel, &dma_cfg);
if (err) {
LOG_ERR("Error configuring Rx DMA (%d)", err);
goto unlock;
}
/*
* Enable interrupt on first receive byte so we can start async timer
*/
uart_hal_set_rxfifo_full_thr(&data->hal, 1);
uart_esp32_irq_rx_enable(dev);
err = dma_start(config->dma_dev, config->rx_dma_channel);
if (err) {
LOG_ERR("Error starting Rx DMA (%d)", err);
goto unlock;
}
data->uhci_dev->pkt_thres.thrs = len;
/**
* Request next buffer
*/
evt.type = UART_RX_BUF_REQUEST;
if (data->async.cb) {
data->async.cb(dev, &evt, data->async.user_data);
}
unlock:
irq_unlock(key);
return err;
}
static int uart_esp32_async_rx_buf_rsp(const struct device *dev, uint8_t *buf, size_t len)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
data->async.rx_next_buf = buf;
data->async.rx_next_len = len;
return 0;
}
static int uart_esp32_async_rx_disable(const struct device *dev)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
unsigned int key = irq_lock();
int err = 0;
struct uart_event evt = {0};
k_work_cancel_delayable(&data->async.rx_timeout_work);
if (!data->async.rx_len) {
err = -EINVAL;
goto unlock;
}
err = dma_stop(config->dma_dev, config->rx_dma_channel);
if (err) {
LOG_ERR("Error stopping Rx DMA (%d)", err);
goto unlock;
}
/*If any bytes have been received notify RX_RDY*/
evt.type = UART_RX_RDY;
evt.data.rx.buf = data->async.rx_buf;
evt.data.rx.len = data->async.rx_counter - data->async.rx_offset;
evt.data.rx.offset = data->async.rx_offset;
if (data->async.cb && evt.data.rx.len) {
data->async.cb(data->uart_dev, &evt, data->async.user_data);
}
data->async.rx_offset = 0;
data->async.rx_counter = 0;
/* Release current buffer*/
evt.type = UART_RX_BUF_RELEASED;
evt.data.rx_buf.buf = data->async.rx_buf;
if (data->async.cb) {
data->async.cb(dev, &evt, data->async.user_data);
}
data->async.rx_len = 0;
data->async.rx_buf = NULL;
/*Release next buffer*/
if (data->async.rx_next_len) {
evt.type = UART_RX_BUF_RELEASED;
evt.data.rx_buf.buf = data->async.rx_buf;
if (data->async.cb) {
data->async.cb(dev, &evt, data->async.user_data);
}
data->async.rx_next_len = 0;
data->async.rx_next_buf = NULL;
}
/*Notify UART_RX_DISABLED*/
evt.type = UART_RX_DISABLED;
if (data->async.cb) {
data->async.cb(dev, &evt, data->async.user_data);
}
unlock:
irq_unlock(key);
return err;
}
#endif /* CONFIG_UART_ASYNC_API */
static int uart_esp32_init(const struct device *dev)
{
const struct uart_esp32_config *config = dev->config;
struct uart_esp32_data *data = dev->data;
int ret = uart_esp32_configure(dev, &data->uart_config);
if (ret < 0) {
LOG_ERR("Error configuring UART (%d)", ret);
return ret;
}
#if CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API
ret = esp_intr_alloc(config->irq_source,
config->irq_priority,
(ISR_HANDLER)uart_esp32_isr,
(void *)dev,
NULL);
if (ret < 0) {
LOG_ERR("Error allocating UART interrupt (%d)", ret);
return ret;
}
#endif
#if CONFIG_UART_ASYNC_API
if (config->dma_dev) {
if (!device_is_ready(config->dma_dev)) {
LOG_ERR("DMA device is not ready");
return -ENODEV;
}
clock_control_on(config->clock_dev, (clock_control_subsys_t)ESP32_UHCI0_MODULE);
uhci_ll_init(data->uhci_dev);
uhci_ll_set_eof_mode(data->uhci_dev, UHCI_RX_IDLE_EOF | UHCI_RX_LEN_EOF);
uhci_ll_attach_uart_port(data->uhci_dev, uart_hal_get_port_num(&data->hal));
data->uart_dev = dev;
k_work_init_delayable(&data->async.tx_timeout_work, uart_esp32_async_tx_timeout);
k_work_init_delayable(&data->async.rx_timeout_work, uart_esp32_async_rx_timeout);
}
#endif
return 0;
}
static const DRAM_ATTR struct uart_driver_api uart_esp32_api = {
.poll_in = uart_esp32_poll_in,
.poll_out = uart_esp32_poll_out,
.err_check = uart_esp32_err_check,
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
.configure = uart_esp32_configure,
.config_get = uart_esp32_config_get,
#endif
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_esp32_fifo_fill,
.fifo_read = uart_esp32_fifo_read,
.irq_tx_enable = uart_esp32_irq_tx_enable,
.irq_tx_disable = uart_esp32_irq_tx_disable,
.irq_tx_ready = uart_esp32_irq_tx_ready,
.irq_rx_enable = uart_esp32_irq_rx_enable,
.irq_rx_disable = uart_esp32_irq_rx_disable,
.irq_tx_complete = uart_esp32_irq_tx_complete,
.irq_rx_ready = uart_esp32_irq_rx_ready,
.irq_err_enable = uart_esp32_irq_err_enable,
.irq_err_disable = uart_esp32_irq_err_disable,
.irq_is_pending = uart_esp32_irq_is_pending,
.irq_update = uart_esp32_irq_update,
.irq_callback_set = uart_esp32_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#if CONFIG_UART_ASYNC_API
.callback_set = uart_esp32_async_callback_set,
.tx = uart_esp32_async_tx,
.tx_abort = uart_esp32_async_tx_abort,
.rx_enable = uart_esp32_async_rx_enable,
.rx_buf_rsp = uart_esp32_async_rx_buf_rsp,
.rx_disable = uart_esp32_async_rx_disable,
#endif /*CONFIG_UART_ASYNC_API*/
};
#if CONFIG_UART_ASYNC_API
#define ESP_UART_DMA_INIT(n) \
.dma_dev = ESP32_DT_INST_DMA_CTLR(n, tx), \
.tx_dma_channel = ESP32_DT_INST_DMA_CELL(n, tx, channel), \
.rx_dma_channel = ESP32_DT_INST_DMA_CELL(n, rx, channel)
#define ESP_UART_UHCI_INIT(n) \
.uhci_dev = COND_CODE_1(DT_INST_NODE_HAS_PROP(n, dmas), (&UHCI0), (NULL))
#define UART_IRQ_PRIORITY ESP_INTR_FLAG_LEVEL2
#else
#define ESP_UART_DMA_INIT(n)
#define ESP_UART_UHCI_INIT(n)
#define UART_IRQ_PRIORITY (0)
#endif
#define ESP32_UART_INIT(idx) \
\
PINCTRL_DT_INST_DEFINE(idx); \
\
static const DRAM_ATTR struct uart_esp32_config uart_esp32_cfg_port_##idx = { \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, offset), \
.irq_source = DT_INST_IRQN(idx), \
.irq_priority = UART_IRQ_PRIORITY, \
ESP_UART_DMA_INIT(idx)}; \
\
static struct uart_esp32_data uart_esp32_data_##idx = { \
.uart_config = {.baudrate = DT_INST_PROP(idx, current_speed), \
.parity = DT_INST_ENUM_IDX_OR(idx, parity, UART_CFG_PARITY_NONE), \
.stop_bits = DT_INST_ENUM_IDX_OR(idx, stop_bits, \
UART_CFG_STOP_BITS_1), \
.data_bits = DT_INST_ENUM_IDX_OR(idx, data_bits, \
UART_CFG_DATA_BITS_8), \
.flow_ctrl = MAX(COND_CODE_1(DT_INST_PROP(idx, hw_rs485_hd_mode), \
(UART_CFG_FLOW_CTRL_RS485), \
(UART_CFG_FLOW_CTRL_NONE)), \
COND_CODE_1(DT_INST_PROP(idx, hw_flow_control), \
(UART_CFG_FLOW_CTRL_RTS_CTS), \
(UART_CFG_FLOW_CTRL_NONE)))}, \
.hal = \
{ \
.dev = (uart_dev_t *)DT_INST_REG_ADDR(idx), \
}, \
ESP_UART_UHCI_INIT(idx)}; \
\
DEVICE_DT_INST_DEFINE(idx, &uart_esp32_init, NULL, &uart_esp32_data_##idx, \
&uart_esp32_cfg_port_##idx, PRE_KERNEL_1, \
CONFIG_SERIAL_INIT_PRIORITY, &uart_esp32_api);
DT_INST_FOREACH_STATUS_OKAY(ESP32_UART_INIT);