You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
708 lines
24 KiB
708 lines
24 KiB
/* |
|
* Copyright (c) 2022-2025 Espressif Systems (Shanghai) Co., Ltd. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT espressif_esp32_gdma |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(dma_esp32_gdma, CONFIG_DMA_LOG_LEVEL); |
|
|
|
#include <hal/gdma_hal.h> |
|
#include <hal/gdma_ll.h> |
|
#include <soc/gdma_channel.h> |
|
#include <hal/dma_types.h> |
|
|
|
#include <soc.h> |
|
#include <esp_memory_utils.h> |
|
#include <errno.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/dma.h> |
|
#include <zephyr/drivers/dma/dma_esp32.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <zephyr/drivers/interrupt_controller/intc_esp32.h> |
|
|
|
#define DMA_MAX_CHANNEL SOC_GDMA_PAIRS_PER_GROUP |
|
|
|
#define ESP_DMA_M2M_ON 0 |
|
#define ESP_DMA_M2M_OFF 1 |
|
|
|
struct dma_esp32_data { |
|
gdma_hal_context_t hal; |
|
}; |
|
|
|
enum dma_channel_dir { |
|
DMA_RX, |
|
DMA_TX, |
|
DMA_UNCONFIGURED |
|
}; |
|
|
|
struct irq_config { |
|
uint8_t irq_source; |
|
uint8_t irq_priority; |
|
int irq_flags; |
|
}; |
|
|
|
struct dma_esp32_channel { |
|
uint8_t dir; |
|
uint8_t channel_id; |
|
int host_id; |
|
int periph_id; |
|
dma_callback_t cb; |
|
void *user_data; |
|
dma_descriptor_t desc_list[CONFIG_DMA_ESP32_MAX_DESCRIPTOR_NUM]; |
|
}; |
|
|
|
struct dma_esp32_config { |
|
struct irq_config *irq_config; |
|
uint8_t irq_size; |
|
void **irq_handlers; |
|
uint8_t dma_channel_max; |
|
uint8_t sram_alignment; |
|
struct dma_esp32_channel dma_channel[DMA_MAX_CHANNEL * 2]; |
|
void (*config_irq)(const struct device *dev); |
|
struct device *src_dev; |
|
const struct device *clock_dev; |
|
clock_control_subsys_t clock_subsys; |
|
}; |
|
|
|
static void IRAM_ATTR dma_esp32_isr_handle_rx(const struct device *dev, |
|
struct dma_esp32_channel *rx, uint32_t intr_status) |
|
{ |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
|
|
gdma_ll_rx_clear_interrupt_status(data->hal.dev, rx->channel_id, intr_status); |
|
if (rx->cb) { |
|
int status; |
|
|
|
if (intr_status == (GDMA_LL_EVENT_RX_SUC_EOF | GDMA_LL_EVENT_RX_DONE)) { |
|
status = DMA_STATUS_COMPLETE; |
|
} else if (intr_status == GDMA_LL_EVENT_RX_DONE) { |
|
status = DMA_STATUS_BLOCK; |
|
#if defined(CONFIG_SOC_SERIES_ESP32S3) |
|
} else if (intr_status == GDMA_LL_EVENT_RX_WATER_MARK) { |
|
status = DMA_STATUS_BLOCK; |
|
#endif |
|
} else { |
|
status = -intr_status; |
|
} |
|
|
|
rx->cb(dev, rx->user_data, rx->channel_id * 2, status); |
|
} |
|
} |
|
|
|
static void IRAM_ATTR dma_esp32_isr_handle_tx(const struct device *dev, |
|
struct dma_esp32_channel *tx, uint32_t intr_status) |
|
{ |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
|
|
gdma_ll_tx_clear_interrupt_status(data->hal.dev, tx->channel_id, intr_status); |
|
if (tx->cb) { |
|
intr_status &= ~(GDMA_LL_EVENT_TX_TOTAL_EOF | GDMA_LL_EVENT_TX_DONE | |
|
GDMA_LL_EVENT_TX_EOF); |
|
|
|
tx->cb(dev, tx->user_data, tx->channel_id * 2 + 1, -intr_status); |
|
} |
|
} |
|
|
|
#if defined(SOC_GDMA_TX_RX_SHARE_INTERRUPT) |
|
static void IRAM_ATTR dma_esp32_isr_handle(const struct device *dev, uint8_t rx_id, uint8_t tx_id) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
struct dma_esp32_channel *dma_channel_rx = &config->dma_channel[rx_id]; |
|
struct dma_esp32_channel *dma_channel_tx = &config->dma_channel[tx_id]; |
|
uint32_t intr_status = 0; |
|
|
|
intr_status = gdma_ll_rx_get_interrupt_status(data->hal.dev, dma_channel_rx->channel_id); |
|
if (intr_status) { |
|
dma_esp32_isr_handle_rx(dev, dma_channel_rx, intr_status); |
|
} |
|
|
|
intr_status = gdma_ll_tx_get_interrupt_status(data->hal.dev, dma_channel_tx->channel_id); |
|
if (intr_status) { |
|
dma_esp32_isr_handle_tx(dev, dma_channel_tx, intr_status); |
|
} |
|
} |
|
#endif |
|
|
|
static int dma_esp32_config_descriptor(struct dma_esp32_channel *dma_channel, |
|
struct dma_block_config *block) |
|
{ |
|
if (!block) { |
|
LOG_ERR("At least one dma block is required"); |
|
return -EINVAL; |
|
} |
|
|
|
uint32_t target_address = 0, block_size = 0; |
|
dma_descriptor_t *desc_iter = dma_channel->desc_list; |
|
|
|
for (int i = 0; i < CONFIG_DMA_ESP32_MAX_DESCRIPTOR_NUM; ++i) { |
|
if (block_size == 0) { |
|
if (dma_channel->dir == DMA_TX) { |
|
target_address = block->source_address; |
|
} else { |
|
target_address = block->dest_address; |
|
} |
|
|
|
if (!esp_ptr_dma_capable((uint32_t *)target_address) |
|
#if defined(CONFIG_ESP_SPIRAM) |
|
&& !esp_ptr_dma_ext_capable((uint32_t *)target_address) |
|
#endif |
|
) { |
|
if (dma_channel->dir == DMA_TX) { |
|
LOG_ERR("Tx buffer not in DMA capable memory: %p", |
|
(uint32_t *)target_address); |
|
} else { |
|
LOG_ERR("Rx buffer not in DMA capable memory: %p", |
|
(uint32_t *)target_address); |
|
} |
|
|
|
return -EINVAL; |
|
} |
|
|
|
block_size = block->block_size; |
|
} |
|
|
|
uint32_t buffer_size; |
|
|
|
if (block_size > DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { |
|
buffer_size = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; |
|
} else { |
|
buffer_size = block_size; |
|
} |
|
|
|
memset(desc_iter, 0, sizeof(dma_descriptor_t)); |
|
desc_iter->buffer = (void *)target_address; |
|
desc_iter->dw0.size = buffer_size; |
|
if (dma_channel->dir == DMA_TX) { |
|
desc_iter->dw0.length = buffer_size; |
|
} |
|
desc_iter->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; |
|
|
|
target_address += buffer_size; |
|
block_size -= buffer_size; |
|
|
|
if (!block_size) { |
|
if (block->next_block) { |
|
block = block->next_block; |
|
} else { |
|
desc_iter->next = NULL; |
|
if (dma_channel->dir == DMA_TX) { |
|
desc_iter->dw0.suc_eof = 1; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
desc_iter->next = desc_iter + 1; |
|
desc_iter += 1; |
|
} |
|
|
|
if (desc_iter->next) { |
|
memset(dma_channel->desc_list, 0, sizeof(dma_channel->desc_list)); |
|
LOG_ERR("Run out of DMA descriptors. Increase CONFIG_DMA_ESP32_MAX_DESCRIPTOR_NUM"); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_config_rx(const struct device *dev, struct dma_esp32_channel *dma_channel, |
|
struct dma_config *config_dma) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
|
|
dma_channel->dir = DMA_RX; |
|
|
|
gdma_ll_rx_reset_channel(data->hal.dev, dma_channel->channel_id); |
|
|
|
gdma_ll_rx_connect_to_periph( |
|
data->hal.dev, dma_channel->channel_id, |
|
dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0 ? ESP_DMA_M2M_ON |
|
: ESP_DMA_M2M_OFF, |
|
dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0 ? ESP_DMA_M2M_ON |
|
: dma_channel->periph_id); |
|
|
|
if (config_dma->dest_burst_length) { |
|
/* |
|
* RX channel burst mode depends on specific data alignment |
|
*/ |
|
gdma_ll_rx_enable_data_burst(data->hal.dev, dma_channel->channel_id, |
|
config->sram_alignment >= 4); |
|
gdma_ll_rx_enable_descriptor_burst(data->hal.dev, dma_channel->channel_id, |
|
config->sram_alignment >= 4); |
|
} |
|
|
|
dma_channel->cb = config_dma->dma_callback; |
|
dma_channel->user_data = config_dma->user_data; |
|
|
|
gdma_ll_rx_enable_interrupt(data->hal.dev, dma_channel->channel_id, GDMA_LL_RX_EVENT_MASK, |
|
false); |
|
|
|
gdma_ll_rx_clear_interrupt_status(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_RX_EVENT_MASK); |
|
|
|
return dma_esp32_config_descriptor(dma_channel, config_dma->head_block); |
|
} |
|
|
|
static int dma_esp32_config_tx(const struct device *dev, struct dma_esp32_channel *dma_channel, |
|
struct dma_config *config_dma) |
|
{ |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
|
|
dma_channel->dir = DMA_TX; |
|
|
|
gdma_ll_tx_reset_channel(data->hal.dev, dma_channel->channel_id); |
|
|
|
gdma_ll_tx_connect_to_periph( |
|
data->hal.dev, dma_channel->channel_id, |
|
dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0 ? ESP_DMA_M2M_ON |
|
: ESP_DMA_M2M_OFF, |
|
dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0 ? ESP_DMA_M2M_ON |
|
: dma_channel->periph_id); |
|
|
|
/* |
|
* TX channel can always enable burst mode, no matter data alignment |
|
*/ |
|
if (config_dma->source_burst_length) { |
|
gdma_ll_tx_enable_data_burst(data->hal.dev, dma_channel->channel_id, true); |
|
gdma_ll_tx_enable_descriptor_burst(data->hal.dev, dma_channel->channel_id, true); |
|
} |
|
|
|
dma_channel->cb = config_dma->dma_callback; |
|
dma_channel->user_data = config_dma->user_data; |
|
|
|
gdma_ll_tx_enable_interrupt(data->hal.dev, dma_channel->channel_id, GDMA_LL_TX_EVENT_MASK, |
|
false); |
|
|
|
gdma_ll_tx_clear_interrupt_status(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_TX_EVENT_MASK); |
|
|
|
return dma_esp32_config_descriptor(dma_channel, config_dma->head_block); |
|
} |
|
|
|
static int dma_esp32_config(const struct device *dev, uint32_t channel, |
|
struct dma_config *config_dma) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_channel *dma_channel = &config->dma_channel[channel]; |
|
int ret = 0; |
|
|
|
if (channel >= config->dma_channel_max) { |
|
LOG_ERR("Unsupported channel"); |
|
return -EINVAL; |
|
} |
|
|
|
if (!config_dma) { |
|
return -EINVAL; |
|
} |
|
|
|
if (config_dma->source_burst_length != config_dma->dest_burst_length) { |
|
LOG_ERR("Source and destination burst lengths must be equal"); |
|
return -EINVAL; |
|
} |
|
|
|
dma_channel->periph_id = config_dma->channel_direction == MEMORY_TO_MEMORY |
|
? SOC_GDMA_TRIG_PERIPH_M2M0 |
|
: config_dma->dma_slot; |
|
|
|
dma_channel->channel_id = channel / 2; |
|
|
|
switch (config_dma->channel_direction) { |
|
case MEMORY_TO_MEMORY: |
|
/* |
|
* Create both Tx and Rx stream on the same channel_id |
|
*/ |
|
struct dma_esp32_channel *dma_channel_rx = |
|
&config->dma_channel[dma_channel->channel_id * 2]; |
|
struct dma_esp32_channel *dma_channel_tx = |
|
&config->dma_channel[(dma_channel->channel_id * 2) + 1]; |
|
|
|
dma_channel_rx->channel_id = dma_channel->channel_id; |
|
dma_channel_tx->channel_id = dma_channel->channel_id; |
|
|
|
dma_channel_rx->periph_id = dma_channel->periph_id; |
|
dma_channel_tx->periph_id = dma_channel->periph_id; |
|
|
|
ret = dma_esp32_config_rx(dev, dma_channel_rx, config_dma); |
|
ret = dma_esp32_config_tx(dev, dma_channel_tx, config_dma); |
|
break; |
|
case PERIPHERAL_TO_MEMORY: |
|
ret = dma_esp32_config_rx(dev, dma_channel, config_dma); |
|
break; |
|
case MEMORY_TO_PERIPHERAL: |
|
ret = dma_esp32_config_tx(dev, dma_channel, config_dma); |
|
break; |
|
default: |
|
LOG_ERR("Invalid Channel direction"); |
|
return -EINVAL; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static int dma_esp32_start(const struct device *dev, uint32_t channel) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
struct dma_esp32_channel *dma_channel = &config->dma_channel[channel]; |
|
|
|
if (channel >= config->dma_channel_max) { |
|
LOG_ERR("Unsupported channel"); |
|
return -EINVAL; |
|
} |
|
|
|
if (dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0) { |
|
struct dma_esp32_channel *dma_channel_rx = |
|
&config->dma_channel[dma_channel->channel_id * 2]; |
|
struct dma_esp32_channel *dma_channel_tx = |
|
&config->dma_channel[(dma_channel->channel_id * 2) + 1]; |
|
|
|
gdma_ll_rx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_EVENT_RX_SUC_EOF | GDMA_LL_EVENT_RX_DONE, true); |
|
gdma_ll_tx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_EVENT_TX_EOF, true); |
|
|
|
gdma_ll_rx_set_desc_addr(data->hal.dev, dma_channel->channel_id, |
|
(int32_t)dma_channel_rx->desc_list); |
|
gdma_ll_rx_start(data->hal.dev, dma_channel->channel_id); |
|
|
|
gdma_ll_tx_set_desc_addr(data->hal.dev, dma_channel->channel_id, |
|
(int32_t)dma_channel_tx->desc_list); |
|
gdma_ll_tx_start(data->hal.dev, dma_channel->channel_id); |
|
} else { |
|
if (dma_channel->dir == DMA_RX) { |
|
gdma_ll_rx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_EVENT_RX_SUC_EOF | |
|
GDMA_LL_EVENT_RX_DONE, true); |
|
gdma_ll_rx_set_desc_addr(data->hal.dev, dma_channel->channel_id, |
|
(int32_t)dma_channel->desc_list); |
|
gdma_ll_rx_start(data->hal.dev, dma_channel->channel_id); |
|
} else if (dma_channel->dir == DMA_TX) { |
|
gdma_ll_tx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_EVENT_TX_EOF, true); |
|
gdma_ll_tx_set_desc_addr(data->hal.dev, dma_channel->channel_id, |
|
(int32_t)dma_channel->desc_list); |
|
gdma_ll_tx_start(data->hal.dev, dma_channel->channel_id); |
|
} else { |
|
LOG_ERR("Channel %d is not configured", channel); |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_stop(const struct device *dev, uint32_t channel) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
struct dma_esp32_channel *dma_channel = &config->dma_channel[channel]; |
|
|
|
if (channel >= config->dma_channel_max) { |
|
LOG_ERR("Unsupported channel"); |
|
return -EINVAL; |
|
} |
|
|
|
if (dma_channel->periph_id == SOC_GDMA_TRIG_PERIPH_M2M0) { |
|
gdma_ll_rx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_RX_EVENT_MASK, false); |
|
gdma_ll_tx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_TX_EVENT_MASK, false); |
|
gdma_ll_rx_stop(data->hal.dev, dma_channel->channel_id); |
|
gdma_ll_tx_stop(data->hal.dev, dma_channel->channel_id); |
|
} |
|
|
|
if (dma_channel->dir == DMA_RX) { |
|
gdma_ll_rx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_RX_EVENT_MASK, false); |
|
gdma_ll_rx_stop(data->hal.dev, dma_channel->channel_id); |
|
} else if (dma_channel->dir == DMA_TX) { |
|
gdma_ll_tx_enable_interrupt(data->hal.dev, dma_channel->channel_id, |
|
GDMA_LL_TX_EVENT_MASK, false); |
|
gdma_ll_tx_stop(data->hal.dev, dma_channel->channel_id); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_get_status(const struct device *dev, uint32_t channel, |
|
struct dma_status *status) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
struct dma_esp32_channel *dma_channel = &config->dma_channel[channel]; |
|
dma_descriptor_t *desc; |
|
|
|
if (channel >= config->dma_channel_max) { |
|
LOG_ERR("Unsupported channel"); |
|
return -EINVAL; |
|
} |
|
|
|
if (!status) { |
|
return -EINVAL; |
|
} |
|
|
|
memset(status, 0, sizeof(struct dma_status)); |
|
|
|
if (dma_channel->dir == DMA_RX) { |
|
status->busy = !gdma_ll_rx_is_fsm_idle(data->hal.dev, dma_channel->channel_id); |
|
status->dir = PERIPHERAL_TO_MEMORY; |
|
desc = (dma_descriptor_t *)gdma_ll_rx_get_current_desc_addr( |
|
data->hal.dev, dma_channel->channel_id); |
|
if (desc >= dma_channel->desc_list) { |
|
status->read_position = desc - dma_channel->desc_list; |
|
status->total_copied = desc->dw0.length |
|
+ dma_channel->desc_list[0].dw0.size |
|
* status->read_position; |
|
} |
|
} else if (dma_channel->dir == DMA_TX) { |
|
status->busy = !gdma_ll_tx_is_fsm_idle(data->hal.dev, dma_channel->channel_id); |
|
status->dir = MEMORY_TO_PERIPHERAL; |
|
desc = (dma_descriptor_t *)gdma_ll_tx_get_current_desc_addr( |
|
data->hal.dev, dma_channel->channel_id); |
|
if (desc >= dma_channel->desc_list) { |
|
status->write_position = desc - dma_channel->desc_list; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_reload(const struct device *dev, uint32_t channel, uint32_t src, uint32_t dst, |
|
size_t size) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; |
|
struct dma_esp32_channel *dma_channel = &config->dma_channel[channel]; |
|
dma_descriptor_t *desc_iter = dma_channel->desc_list; |
|
uint32_t buf; |
|
|
|
if (channel >= config->dma_channel_max) { |
|
LOG_ERR("Unsupported channel"); |
|
return -EINVAL; |
|
} |
|
|
|
if (dma_channel->dir == DMA_RX) { |
|
gdma_ll_rx_reset_channel(data->hal.dev, dma_channel->channel_id); |
|
buf = dst; |
|
} else if (dma_channel->dir == DMA_TX) { |
|
gdma_ll_tx_reset_channel(data->hal.dev, dma_channel->channel_id); |
|
buf = src; |
|
} else { |
|
return -EINVAL; |
|
} |
|
|
|
for (int i = 0; i < ARRAY_SIZE(dma_channel->desc_list); ++i) { |
|
memset(desc_iter, 0, sizeof(dma_descriptor_t)); |
|
desc_iter->buffer = (void *)(buf + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED * i); |
|
desc_iter->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; |
|
if (size <= DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { |
|
desc_iter->dw0.size = size; |
|
if (dma_channel->dir == DMA_TX) { |
|
desc_iter->dw0.length = size; |
|
desc_iter->dw0.suc_eof = 1; |
|
} |
|
desc_iter->next = NULL; |
|
break; |
|
} |
|
desc_iter->dw0.size = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; |
|
if (dma_channel->dir == DMA_TX) { |
|
desc_iter->dw0.length = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; |
|
} |
|
size -= DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; |
|
desc_iter->next = desc_iter + 1; |
|
desc_iter += 1; |
|
} |
|
|
|
if (desc_iter->next) { |
|
memset(desc_iter, 0, sizeof(dma_descriptor_t)); |
|
LOG_ERR("Not enough DMA descriptors. Increase CONFIG_DMA_ESP32_MAX_DESCRIPTOR_NUM"); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_configure_irq(const struct device *dev) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *)dev->data; |
|
struct irq_config *irq_cfg = (struct irq_config *)config->irq_config; |
|
volatile uint32_t *status_reg; |
|
uint32_t status_mask; |
|
|
|
for (uint8_t i = 0; i < config->irq_size; i++) { |
|
#if defined(SOC_GDMA_TX_RX_SHARE_INTERRUPT) |
|
status_reg = gdma_ll_rx_get_interrupt_status_reg(data->hal.dev, i); |
|
status_mask = (GDMA_LL_RX_EVENT_MASK | GDMA_LL_TX_EVENT_MASK); |
|
#else |
|
/* We assume device tree to feature RX/TX pairs */ |
|
if (i % 2) { |
|
status_reg = gdma_ll_tx_get_interrupt_status_reg(data->hal.dev, i / 2); |
|
status_mask = GDMA_LL_TX_EVENT_MASK; |
|
} else { |
|
status_reg = gdma_ll_rx_get_interrupt_status_reg(data->hal.dev, i / 2); |
|
status_mask = GDMA_LL_RX_EVENT_MASK; |
|
} |
|
#endif |
|
|
|
int ret = esp_intr_alloc_intrstatus(irq_cfg[i].irq_source, |
|
ESP_PRIO_TO_FLAGS(irq_cfg[i].irq_priority) | |
|
ESP_INT_FLAGS_CHECK(irq_cfg[i].irq_flags) | ESP_INTR_FLAG_IRAM, |
|
(uint32_t)status_reg, |
|
status_mask, |
|
(intr_handler_t)config->irq_handlers[i], |
|
(void *)dev, |
|
NULL); |
|
|
|
if (ret != 0) { |
|
LOG_ERR("Could not allocate interrupt handler"); |
|
return ret; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int dma_esp32_init(const struct device *dev) |
|
{ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; |
|
struct dma_esp32_data *data = (struct dma_esp32_data *)dev->data; |
|
struct dma_esp32_channel *dma_channel; |
|
int ret = 0; |
|
|
|
if (!device_is_ready(config->clock_dev)) { |
|
LOG_ERR("clock control device not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
ret = clock_control_on(config->clock_dev, config->clock_subsys); |
|
if (ret < 0) { |
|
LOG_ERR("Could not initialize clock (%d)", ret); |
|
return ret; |
|
} |
|
|
|
ret = dma_esp32_configure_irq(dev); |
|
if (ret < 0) { |
|
LOG_ERR("Could not configure IRQ (%d)", ret); |
|
return ret; |
|
} |
|
|
|
for (uint8_t i = 0; i < DMA_MAX_CHANNEL * 2; i++) { |
|
dma_channel = &config->dma_channel[i]; |
|
dma_channel->cb = NULL; |
|
dma_channel->dir = DMA_UNCONFIGURED; |
|
dma_channel->periph_id = ESP_GDMA_TRIG_PERIPH_INVALID; |
|
memset(dma_channel->desc_list, 0, sizeof(dma_channel->desc_list)); |
|
} |
|
|
|
gdma_hal_init(&data->hal, 0); |
|
gdma_ll_enable_clock(data->hal.dev, true); |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(dma, dma_esp32_api) = { |
|
.config = dma_esp32_config, |
|
.start = dma_esp32_start, |
|
.stop = dma_esp32_stop, |
|
.get_status = dma_esp32_get_status, |
|
.reload = dma_esp32_reload, |
|
}; |
|
|
|
#if defined(SOC_GDMA_TX_RX_SHARE_INTERRUPT) |
|
|
|
#define DMA_ESP32_DEFINE_IRQ_HANDLER(channel) \ |
|
__attribute__((unused)) static void IRAM_ATTR dma_esp32_isr_##channel( \ |
|
const struct device *dev) \ |
|
{ \ |
|
dma_esp32_isr_handle(dev, channel * 2, channel * 2 + 1); \ |
|
} |
|
|
|
#else |
|
|
|
#define DMA_ESP32_DEFINE_IRQ_HANDLER(channel) \ |
|
__attribute__((unused)) static void IRAM_ATTR dma_esp32_isr_##channel##_rx( \ |
|
const struct device *dev) \ |
|
{ \ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; \ |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; \ |
|
uint32_t intr_status = gdma_ll_rx_get_interrupt_status(data->hal.dev, channel); \ |
|
if (intr_status) { \ |
|
dma_esp32_isr_handle_rx(dev, &config->dma_channel[channel * 2], \ |
|
intr_status); \ |
|
} \ |
|
} \ |
|
\ |
|
__attribute__((unused)) static void IRAM_ATTR dma_esp32_isr_##channel##_tx( \ |
|
const struct device *dev) \ |
|
{ \ |
|
struct dma_esp32_config *config = (struct dma_esp32_config *)dev->config; \ |
|
struct dma_esp32_data *data = (struct dma_esp32_data *const)(dev)->data; \ |
|
uint32_t intr_status = gdma_ll_tx_get_interrupt_status(data->hal.dev, channel); \ |
|
if (intr_status) { \ |
|
dma_esp32_isr_handle_tx(dev, &config->dma_channel[channel * 2 + 1], \ |
|
intr_status); \ |
|
} \ |
|
} |
|
|
|
#endif |
|
|
|
#if defined(SOC_GDMA_TX_RX_SHARE_INTERRUPT) |
|
#define ESP32_DMA_HANDLER(channel) dma_esp32_isr_##channel |
|
#else |
|
#define ESP32_DMA_HANDLER(channel) dma_esp32_isr_##channel##_rx, dma_esp32_isr_##channel##_tx |
|
#endif |
|
|
|
DMA_ESP32_DEFINE_IRQ_HANDLER(0) |
|
DMA_ESP32_DEFINE_IRQ_HANDLER(1) |
|
DMA_ESP32_DEFINE_IRQ_HANDLER(2) |
|
#if DMA_MAX_CHANNEL >= 5 |
|
DMA_ESP32_DEFINE_IRQ_HANDLER(3) |
|
DMA_ESP32_DEFINE_IRQ_HANDLER(4) |
|
#endif |
|
|
|
static void *irq_handlers[] = { |
|
ESP32_DMA_HANDLER(0), |
|
ESP32_DMA_HANDLER(1), |
|
ESP32_DMA_HANDLER(2), |
|
#if DMA_MAX_CHANNEL >= 5 |
|
ESP32_DMA_HANDLER(3), |
|
ESP32_DMA_HANDLER(4), |
|
#endif |
|
}; |
|
|
|
#define IRQ_NUM(idx) DT_NUM_IRQS(DT_DRV_INST(idx)) |
|
#define IRQ_ENTRY(n, idx) { \ |
|
DT_INST_IRQ_BY_IDX(idx, n, irq), \ |
|
DT_INST_IRQ_BY_IDX(idx, n, priority), \ |
|
DT_INST_IRQ_BY_IDX(idx, n, flags) }, |
|
|
|
#define DMA_ESP32_INIT(idx) \ |
|
static struct irq_config irq_config_##idx[] = { \ |
|
LISTIFY(IRQ_NUM(idx), IRQ_ENTRY, (), idx) \ |
|
}; \ |
|
static struct dma_esp32_config dma_config_##idx = { \ |
|
.irq_config = irq_config_##idx, \ |
|
.irq_size = IRQ_NUM(idx), \ |
|
.irq_handlers = irq_handlers, \ |
|
.dma_channel_max = DT_INST_PROP(idx, dma_channels), \ |
|
.sram_alignment = DT_INST_PROP(idx, dma_buf_addr_alignment), \ |
|
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ |
|
.clock_subsys = (void *)DT_INST_CLOCKS_CELL(idx, offset), \ |
|
}; \ |
|
static struct dma_esp32_data dma_data_##idx = { \ |
|
.hal = \ |
|
{ \ |
|
.dev = (gdma_dev_t *)DT_INST_REG_ADDR(idx), \ |
|
}, \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(idx, dma_esp32_init, NULL, &dma_data_##idx, &dma_config_##idx, \ |
|
PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, &dma_esp32_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DMA_ESP32_INIT)
|
|
|