feat(gdma): expose interrupt priority as a configurable option

- Add intr_priority field to gdma_channel_t structure
- Add intr_bind_name field to gdma_signal_conn_t for interrupt binding
- Validate intr_priority during channel allocation (must be 0-3)
- Use ESP_INTR_FLAG_SHARED_PRIVATE instead of ESP_INTR_FLAG_SHARED
- Use esp_intr_alloc_info() with bind_by.name for shared interrupts
- Add interrupt priority configuration test case
- Update all gdma_periph.c files with pair-specific bind names (gdma_gXpY)
This commit is contained in:
morris
2026-04-01 10:42:22 +08:00
parent f8f8219e13
commit f2d62bba74
20 changed files with 172 additions and 46 deletions
+17 -22
View File
@@ -76,7 +76,6 @@ typedef struct {
struct dw_gdma_group_t {
int group_id; // Group ID, index from 0
dw_gdma_hal_context_t hal; // HAL instance is at group level
int intr_priority; // all channels in the same group should share the same interrupt priority
portMUX_TYPE spinlock; // group level spinlock, protect group level stuffs, e.g. hal object, pair handle slots and reference count of each pair
dw_gdma_channel_t *channels[DW_GDMA_LL_CHANNELS_PER_GROUP]; // handles of DMA channels
};
@@ -84,6 +83,7 @@ struct dw_gdma_group_t {
struct dw_gdma_channel_t {
int chan_id; // channel ID, index from 0
intr_handle_t intr; // per-channel interrupt handle
int intr_priority; // channel requested interrupt priority
portMUX_TYPE spinlock; // channel level spinlock
dw_gdma_group_t *group; // pointer to the group which the channel belongs to
void *user_data; // user registered DMA event data
@@ -130,7 +130,6 @@ static dw_gdma_group_t *dw_gdma_acquire_group_handle(int group_id)
if (new_group) {
portMUX_INITIALIZE(&group->spinlock);
group->group_id = group_id;
group->intr_priority = -1; // interrupt priority not assigned yet
ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
}
@@ -239,22 +238,11 @@ esp_err_t dw_gdma_new_channel(const dw_gdma_channel_alloc_config_t *config, dw_g
ESP_GOTO_ON_ERROR(channel_register_to_group(chan), err, TAG, "register to group failed");
dw_gdma_group_t *group = chan->group;
dw_gdma_hal_context_t *hal = &group->hal;
int group_id = group->group_id;
int chan_id = chan->chan_id;
// all channels in the same group should use the same interrupt priority
bool intr_priority_conflict = false;
esp_os_enter_critical(&group->spinlock);
if (group->intr_priority == -1) {
group->intr_priority = config->intr_priority;
} else if (config->intr_priority != 0) {
intr_priority_conflict = (group->intr_priority != config->intr_priority);
}
esp_os_exit_critical(&group->spinlock);
ESP_GOTO_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, err, TAG, "intr_priority conflict, already is %d but attempt to %d", group->intr_priority, config->intr_priority);
// basic initialization
portMUX_INITIALIZE(&chan->spinlock);
chan->intr_priority = config->intr_priority;
chan->src_transfer_type = config->src.block_transfer_type;
chan->dst_transfer_type = config->dst.block_transfer_type;
// set transfer flow type
@@ -284,7 +272,7 @@ esp_err_t dw_gdma_new_channel(const dw_gdma_channel_alloc_config_t *config, dw_g
// enable all channel events (notes, they can't trigger an interrupt until `dw_gdma_ll_channel_enable_intr_propagation` is called)
dw_gdma_ll_channel_enable_intr_generation(hal->dev, chan_id, UINT32_MAX, true);
ESP_LOGD(TAG, "new channel (%d,%d) at %p", group_id, chan_id, chan);
ESP_LOGD(TAG, "new channel (%d,%d) at %p", group->group_id, chan_id, chan);
*ret_chan = chan;
return ESP_OK;
err:
@@ -627,17 +615,24 @@ static esp_err_t dw_gdma_install_channel_interrupt(dw_gdma_channel_t *chan)
dw_gdma_ll_channel_clear_intr(hal->dev, chan_id, UINT32_MAX);
// pre-alloc a interrupt handle, with handler disabled
// DW_GDMA multiple channels share the same interrupt source, so we use a shared interrupt handle
// DW_GDMA channels in the same group share one private interrupt group
intr_handle_t intr = NULL;
int isr_flags = DW_GDMA_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
if (group->intr_priority) {
isr_flags |= 1 << (group->intr_priority);
int isr_flags = DW_GDMA_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED_PRIVATE;
if (chan->intr_priority) {
isr_flags |= 1 << (chan->intr_priority);
} else {
isr_flags |= DW_GDMA_ALLOW_INTR_PRIORITY_MASK;
}
ret = esp_intr_alloc_intrstatus(ETS_DW_GDMA_INTR_SOURCE, isr_flags,
(uint32_t)dw_gdma_ll_get_intr_status_reg(hal->dev), DW_GDMA_LL_CHANNEL_EVENT_MASK(chan_id),
dw_gdma_channel_default_isr, chan, &intr);
esp_intr_alloc_info_t intr_info = {
.source = ETS_DW_GDMA_INTR_SOURCE,
.flags = isr_flags,
.intrstatusreg = (uint32_t)dw_gdma_ll_get_intr_status_reg(hal->dev),
.intrstatusmask = DW_GDMA_LL_CHANNEL_EVENT_MASK(chan_id),
.handler = dw_gdma_channel_default_isr,
.arg = chan,
.bind_by.name = "dw_gdma0",
};
ret = esp_intr_alloc_info(&intr_info, &intr);
ESP_RETURN_ON_ERROR(ret, TAG, "alloc interrupt failed");
ESP_LOGD(TAG, "install interrupt service for channel (%d,%d)", group->group_id, chan_id);
+51 -14
View File
@@ -32,6 +32,7 @@
#define GDMA_INVALID_PERIPH_TRIG (0x3F)
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
#define SEARCH_REQUEST_TX_CHANNEL (1 << 1)
#define GDMA_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
typedef struct gdma_platform_t {
portMUX_TYPE spinlock; // platform level spinlock, protect the group handle slots
@@ -80,6 +81,12 @@ static esp_err_t do_allocate_gdma_channel(const gdma_channel_search_info_t *sear
ESP_RETURN_ON_FALSE(search_info->start_group_id < search_info->end_group_id, ESP_ERR_INVALID_ARG, TAG, "invalid group range");
ESP_RETURN_ON_FALSE(search_info->pairs_per_group > 0, ESP_ERR_INVALID_ARG, TAG, "invalid pairs_per_group");
// Validate interrupt priority
if (config->intr_priority) {
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & GDMA_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
TAG, "invalid interrupt priority:%d", config->intr_priority);
}
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION
// retention module is per GDMA pair, before we allocate the pair object, some common registers are already configured in "hal_init"
// if a light sleep happens and powers off the gdma module, those registers will get lost
@@ -150,6 +157,7 @@ static esp_err_t do_allocate_gdma_channel(const gdma_channel_search_info_t *sear
alloc_tx_channel->base.pair = pair;
alloc_tx_channel->base.direction = GDMA_CHANNEL_DIRECTION_TX;
alloc_tx_channel->base.periph_id = GDMA_INVALID_PERIPH_TRIG;
alloc_tx_channel->base.intr_priority = config->intr_priority;
alloc_tx_channel->base.flags.isr_cache_safe = config->flags.isr_cache_safe;
alloc_tx_channel->base.del = gdma_del_tx_channel; // set channel deletion function
alloc_tx_channel->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
@@ -166,6 +174,7 @@ static esp_err_t do_allocate_gdma_channel(const gdma_channel_search_info_t *sear
alloc_rx_channel->base.pair = pair;
alloc_rx_channel->base.direction = GDMA_CHANNEL_DIRECTION_RX;
alloc_rx_channel->base.periph_id = GDMA_INVALID_PERIPH_TRIG;
alloc_rx_channel->base.intr_priority = config->intr_priority;
alloc_rx_channel->base.flags.isr_cache_safe = config->flags.isr_cache_safe;
alloc_rx_channel->base.del = gdma_del_rx_channel; // set channel deletion function
alloc_rx_channel->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
@@ -1003,18 +1012,32 @@ static esp_err_t gdma_install_rx_interrupt(gdma_rx_channel_t *rx_chan)
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
int pair_id = pair->pair_id;
const gdma_pair_signal_conn_t *pair_signals = &gdma_periph_signals.groups[group->group_id].pairs[pair_id];
bool tx_rx_share_irq = pair_signals->rx_irq_id == pair_signals->tx_irq_id;
// pre-alloc a interrupt handle, with handler disabled
int isr_flags = ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LOWMED;
int isr_flags = ESP_INTR_FLAG_INTRDISABLED;
if (rx_chan->base.intr_priority) {
isr_flags |= 1 << (rx_chan->base.intr_priority);
} else {
isr_flags |= ESP_INTR_FLAG_LOWMED;
}
if (rx_chan->base.flags.isr_cache_safe) {
isr_flags |= ESP_INTR_FLAG_IRAM;
}
#if GDMA_LL_AHB_TX_RX_SHARE_INTERRUPT
isr_flags |= ESP_INTR_FLAG_SHARED;
#endif
if (tx_rx_share_irq) {
isr_flags |= ESP_INTR_FLAG_SHARED_PRIVATE;
}
esp_intr_alloc_info_t intr_info = {
.source = pair_signals->rx_irq_id,
.flags = isr_flags,
.intrstatusreg = gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX),
.intrstatusmask = GDMA_LL_RX_EVENT_MASK,
.handler = gdma_default_rx_isr,
.arg = rx_chan,
.bind_by.name = tx_rx_share_irq ? pair_signals->name : NULL,
};
intr_handle_t intr = NULL;
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].rx_irq_id, isr_flags,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX), GDMA_LL_RX_EVENT_MASK,
gdma_default_rx_isr, rx_chan, &intr);
ret = esp_intr_alloc_info(&intr_info, &intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
rx_chan->base.intr = intr;
@@ -1035,18 +1058,32 @@ static esp_err_t gdma_install_tx_interrupt(gdma_tx_channel_t *tx_chan)
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
int pair_id = pair->pair_id;
const gdma_pair_signal_conn_t *pair_signals = &gdma_periph_signals.groups[group->group_id].pairs[pair_id];
bool tx_rx_share_irq = pair_signals->rx_irq_id == pair_signals->tx_irq_id;
// pre-alloc a interrupt handle, with handler disabled
int isr_flags = ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LOWMED;
int isr_flags = ESP_INTR_FLAG_INTRDISABLED;
if (tx_chan->base.intr_priority) {
isr_flags |= 1 << (tx_chan->base.intr_priority);
} else {
isr_flags |= ESP_INTR_FLAG_LOWMED;
}
if (tx_chan->base.flags.isr_cache_safe) {
isr_flags |= ESP_INTR_FLAG_IRAM;
}
#if GDMA_LL_AHB_TX_RX_SHARE_INTERRUPT
isr_flags |= ESP_INTR_FLAG_SHARED;
#endif
if (tx_rx_share_irq) {
isr_flags |= ESP_INTR_FLAG_SHARED_PRIVATE;
}
esp_intr_alloc_info_t intr_info = {
.source = pair_signals->tx_irq_id,
.flags = isr_flags,
.intrstatusreg = gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX),
.intrstatusmask = GDMA_LL_TX_EVENT_MASK,
.handler = gdma_default_tx_isr,
.arg = tx_chan,
.bind_by.name = tx_rx_share_irq ? pair_signals->name : NULL,
};
intr_handle_t intr = NULL;
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].tx_irq_id, isr_flags,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX), GDMA_LL_TX_EVENT_MASK,
gdma_default_tx_isr, tx_chan, &intr);
ret = esp_intr_alloc_info(&intr_info, &intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
tx_chan->base.intr = intr;
@@ -78,6 +78,7 @@ struct gdma_channel_t {
portMUX_TYPE spinlock; // channel level spinlock
gdma_channel_direction_t direction; // channel direction
int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel
int intr_priority; // interrupt priority, if set to 0, the driver will use the default priority
size_t int_mem_alignment; // alignment for memory in internal memory
size_t ext_mem_alignment; // alignment for memory in external memory
esp_err_t (*del)(gdma_channel_t *channel); // channel deletion function, it's polymorphic, see `gdma_del_tx_channel` or `gdma_del_rx_channel`