mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
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:
@@ -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);
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user