Skip to content

Commit 8dfcafa

Browse files
committed
Merge branch 'feat/intr_alloc_force_intrno' into 'master'
feat(esp_hw_support): add the possibility to allocate two sources to the same interrupt line Closes IDF-9552 See merge request espressif/esp-idf!35473
2 parents 0672b32 + cf278e8 commit 8dfcafa

File tree

7 files changed

+281
-40
lines changed

7 files changed

+281
-40
lines changed

components/esp_hw_support/include/esp_intr_alloc.h

Lines changed: 98 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ extern "C" {
9494
* the int can be left enabled while the flash cache is disabled.
9595
*
9696
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
97-
* ESP_OK otherwise
97+
* ESP_OK on success
9898
*/
9999
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
100100

@@ -108,7 +108,7 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
108108
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
109109
*
110110
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
111-
* ESP_OK otherwise
111+
* ESP_OK on success
112112
*/
113113
esp_err_t esp_intr_reserve(int intno, int cpu);
114114

@@ -131,9 +131,10 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
131131
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
132132
* choice of interrupts that this routine can choose from. If this value
133133
* is 0, it will default to allocating a non-shared interrupt of level
134-
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
135-
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
136-
* from this function with the interrupt disabled.
134+
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
135+
* the given level will be allocated (or level 1 if not specified).
136+
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
137+
* interrupt disabled.
137138
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
138139
* is requested, because these types of interrupts aren't C-callable.
139140
* @param arg Optional argument for passed to the interrupt handler
@@ -143,13 +144,13 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
143144
*
144145
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
145146
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
146-
* ESP_OK otherwise
147+
* ESP_OK on success
147148
*/
148149
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
149150

150151

151152
/**
152-
* @brief Allocate an interrupt with the given parameters.
153+
* @brief Allocate an interrupt with the given parameters, including an interrupt status register.
153154
*
154155
*
155156
* This essentially does the same as esp_intr_alloc, but allows specifying a register and mask
@@ -165,9 +166,10 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
165166
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
166167
* choice of interrupts that this routine can choose from. If this value
167168
* is 0, it will default to allocating a non-shared interrupt of level
168-
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
169-
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
170-
* from this function with the interrupt disabled.
169+
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
170+
* the given level will be allocated (or level 1 if not specified).
171+
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
172+
* interrupt disabled.
171173
* @param intrstatusreg The address of an interrupt status register
172174
* @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
173175
* that are 1 in the mask set, the ISR will be called. If not, it will be
@@ -181,11 +183,92 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
181183
*
182184
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
183185
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
184-
* ESP_OK otherwise
186+
* ESP_OK on success
185187
*/
186188
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
187189

188190

191+
/**
192+
* @brief Allocate an interrupt with the given parameters that can be bound to an existing interrupt handler.
193+
*
194+
*
195+
* This function does the same as esp_intr_alloc, but allows specifying a previously allocated handler as
196+
* the interrupt to share with the given source. This can be very handy to treat two pre-determined interrupt
197+
* sources in the same interrupt handler. The interrupt will be allocated on the same core as the given
198+
* `shared_handle`. Moreover, make sure to specify the same interrupt level as the one being used by `shared_handle`
199+
* to prevent any failure from this function.
200+
*
201+
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
202+
* sources, as defined in soc/soc.h, or one of the internal
203+
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
204+
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
205+
* choice of interrupts that this routine can choose from. If this value
206+
* is 0, it will default to allocating a non-shared interrupt of level
207+
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
208+
* the given level will be allocated (or level 1 if not specified).
209+
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
210+
* interrupt disabled.
211+
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
212+
* is requested, because these types of interrupts aren't C-callable.
213+
* @param arg Optional argument for passed to the interrupt handler
214+
* @param shared_handle Previously allocated interrupt to share the CPU interrupt line with. If NULL,
215+
* calling this function equivalent to esp_intr_alloc, else, ESP_INTR_FLAG_SHARED must
216+
* be provided in the flags parameter.
217+
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
218+
* used to request details or free the interrupt. Can be NULL if no handle
219+
* is required.
220+
*
221+
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
222+
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flasg or the given level is different
223+
* from the one assigned to the share_handle parameter.
224+
* ESP_OK on success
225+
*/
226+
esp_err_t esp_intr_alloc_bind(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle);
227+
228+
229+
/**
230+
* @brief Allocate an interrupt with the given parameters, including an interrupt status register, that can
231+
* be bound to an existing interrupt handler
232+
*
233+
*
234+
* This function does the same as esp_intr_alloc_intrstatus, but allows specifying a previously allocated handler as
235+
* the interrupt to share with the given source. This can be very handy to treat two pre-determined interrupt
236+
* sources in the same interrupt handler. The interrupt will be allocated on the same core as the given
237+
* `shared_handle`. Moreover, make sure to specify the same interrupt level as the one being used by `shared_handle`
238+
* to prevent any failure from this function.
239+
*
240+
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
241+
* sources, as defined in soc/soc.h, or one of the internal
242+
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
243+
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
244+
* choice of interrupts that this routine can choose from. If this value
245+
* is 0, it will default to allocating a non-shared interrupt of level
246+
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
247+
* the given level will be allocated (or level 1 if not specified).
248+
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
249+
* interrupt disabled.
250+
* @param intrstatusreg The address of an interrupt status register
251+
* @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
252+
* that are 1 in the mask set, the ISR will be called. If not, it will be
253+
* skipped.
254+
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
255+
* is requested, because these types of interrupts aren't C-callable.
256+
* @param arg Optional argument for passed to the interrupt handler
257+
* @param shared_handle Previously allocated interrupt to share the CPU interrupt line with. If NULL,
258+
* calling this function equivalent to esp_intr_alloc, else, ESP_INTR_FLAG_SHARED must
259+
* be provided in the flags parameter.
260+
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
261+
* used to request details or free the interrupt. Can be NULL if no handle
262+
* is required.
263+
*
264+
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
265+
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flasg or the given level is different
266+
* from the one assigned to the share_handle parameter.
267+
* ESP_OK on success
268+
*/
269+
esp_err_t esp_intr_alloc_intrstatus_bind(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
270+
void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle);
271+
189272
/**
190273
* @brief Disable and free an interrupt.
191274
*
@@ -202,7 +285,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
202285
*
203286
* @return ESP_ERR_INVALID_ARG the handle is NULL
204287
* ESP_FAIL failed to release this handle
205-
* ESP_OK otherwise
288+
* ESP_OK on success
206289
*/
207290
esp_err_t esp_intr_free(intr_handle_t handle);
208291

@@ -239,7 +322,7 @@ int esp_intr_get_intno(intr_handle_t handle);
239322
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
240323
*
241324
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
242-
* ESP_OK otherwise
325+
* ESP_OK on success
243326
*/
244327
esp_err_t esp_intr_disable(intr_handle_t handle);
245328

@@ -252,7 +335,7 @@ esp_err_t esp_intr_disable(intr_handle_t handle);
252335
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
253336
*
254337
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
255-
* ESP_OK otherwise
338+
* ESP_OK on success
256339
*/
257340
esp_err_t esp_intr_enable(intr_handle_t handle);
258341

@@ -266,7 +349,7 @@ esp_err_t esp_intr_enable(intr_handle_t handle);
266349
* Handlers residing in IRAM can be called when cache is disabled.
267350
*
268351
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
269-
* ESP_OK otherwise
352+
* ESP_OK on success
270353
*/
271354
esp_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram);
272355

components/esp_hw_support/intr_alloc.c

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -72,6 +72,16 @@ struct shared_vector_desc_t {
7272
#define VECDESC_FL_INIRAM (1<<1)
7373
#define VECDESC_FL_SHARED (1<<2)
7474
#define VECDESC_FL_NONSHARED (1<<3)
75+
#define VECDESC_FL_TYPE_MASK (0xf)
76+
77+
#if SOC_CPU_HAS_FLEXIBLE_INTC
78+
/* On targets that have configurable interrupts levels, store the assigned level in the flags */
79+
#define VECDESC_FL_LEVEL_SHIFT (8)
80+
/* Allocate 4 bits in the flag */
81+
#define VECDESC_FL_LEVEL_MASK (0xf)
82+
/* Help to extract the level from flags */
83+
#define VECDESC_FL_LEVEL(flags) (((flags) >> VECDESC_FL_LEVEL_SHIFT) & VECDESC_FL_LEVEL_MASK)
84+
#endif
7585

7686
//Pack using bitfields for better memory use
7787
struct vector_desc_t {
@@ -212,7 +222,7 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram)
212222
portEXIT_CRITICAL(&spinlock);
213223
return ESP_ERR_NO_MEM;
214224
}
215-
vd->flags = VECDESC_FL_SHARED;
225+
vd->flags = (vd->flags & ~VECDESC_FL_TYPE_MASK) | VECDESC_FL_SHARED;
216226
if (is_int_ram) {
217227
vd->flags |= VECDESC_FL_INIRAM;
218228
}
@@ -258,7 +268,17 @@ static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force
258268
return false;
259269
}
260270

261-
#ifndef SOC_CPU_HAS_FLEXIBLE_INTC
271+
#if SOC_CPU_HAS_FLEXIBLE_INTC
272+
/* On target that have configurable interrupts levels, check if the interrupt has already
273+
* been allocated, and if yes, make sure the levels are compatible. */
274+
const int vector_lvl = VECDESC_FL_LEVEL(vd->flags);
275+
/* A non-zero value means the level has already been set prior to this allocation, make
276+
* sure the current level matches what we need. */
277+
if (vector_lvl != 0 && (flags & (1 << vector_lvl)) == 0) {
278+
ALCHLOG("....Unusable: incompatible priority");
279+
return false;
280+
}
281+
#else
262282
//Check if the interrupt priority is acceptable
263283
if (!(flags & (1 << intr_desc.priority))) {
264284
ALCHLOG("....Unusable: incompatible priority");
@@ -480,8 +500,8 @@ bool esp_intr_ptr_in_isr_region(void* ptr)
480500

481501

482502
//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running.
483-
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
484-
void *arg, intr_handle_t *ret_handle)
503+
esp_err_t esp_intr_alloc_intrstatus_bind(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
504+
void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle)
485505
{
486506
intr_handle_data_t *ret=NULL;
487507
int force = -1;
@@ -509,7 +529,10 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
509529
if ((flags & ESP_INTR_FLAG_IRAM) && handler && !esp_intr_ptr_in_isr_region(handler)) {
510530
return ESP_ERR_INVALID_ARG;
511531
}
512-
532+
//Shared handler must be passed with share interrupt flag
533+
if (shared_handle != NULL && (flags & ESP_INTR_FLAG_SHARED) == 0) {
534+
return ESP_ERR_INVALID_ARG;
535+
}
513536
//Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts.
514537
if ((flags & ESP_INTR_FLAG_LEVELMASK) == 0) {
515538
if (flags & ESP_INTR_FLAG_SHARED) {
@@ -549,7 +572,17 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
549572

550573
portENTER_CRITICAL(&spinlock);
551574
uint32_t cpu = esp_cpu_get_core_id();
552-
//See if we can find an interrupt that matches the flags.
575+
if (shared_handle != NULL) {
576+
/* Sanity check, should not occur */
577+
if (shared_handle->vector_desc == NULL) {
578+
portEXIT_CRITICAL(&spinlock);
579+
return ESP_ERR_INVALID_ARG;
580+
}
581+
/* If a shared vector was given, force the current interrupt source to same CPU interrupt line */
582+
force = shared_handle->vector_desc->intno;
583+
/* Allocate the interrupt on the same core as the given handle */
584+
cpu = shared_handle->vector_desc->cpu;
585+
}
553586
int intr = get_available_int(flags, cpu, force, source);
554587
if (intr == -1) {
555588
//None found. Bail out.
@@ -640,6 +673,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
640673
#if SOC_CPU_HAS_FLEXIBLE_INTC
641674
//Extract the level from the interrupt passed flags
642675
int level = esp_intr_flags_to_level(flags);
676+
vd->flags |= level << VECDESC_FL_LEVEL_SHIFT;
643677
esp_cpu_intr_set_priority(intr, level);
644678

645679
if (flags & ESP_INTR_FLAG_EDGE) {
@@ -671,6 +705,13 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
671705
return ESP_OK;
672706
}
673707

708+
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
709+
void *arg, intr_handle_t *ret_handle)
710+
{
711+
return esp_intr_alloc_intrstatus_bind(source, flags, intrstatusreg, intrstatusmask, handler, arg, NULL, ret_handle);
712+
}
713+
714+
674715
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle)
675716
{
676717
/*
@@ -681,6 +722,13 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
681722
return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle);
682723
}
683724

725+
726+
esp_err_t esp_intr_alloc_bind(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle)
727+
{
728+
return esp_intr_alloc_intrstatus_bind(source, flags, 0, 0, handler, arg, shared_handle, ret_handle);
729+
}
730+
731+
684732
esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram)
685733
{
686734
if (!handle) {
@@ -790,6 +838,10 @@ static esp_err_t intr_free_for_current_cpu(intr_handle_t handle)
790838
//we save.(We can also not use the same exit path for empty shared ints anymore if we delete
791839
//the desc.) For now, just mark it as free.
792840
handle->vector_desc->flags &= ~(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED|VECDESC_FL_SHARED);
841+
#if SOC_CPU_HAS_FLEXIBLE_INTC
842+
//Clear the assigned level
843+
handle->vector_desc->flags &= ~(VECDESC_FL_LEVEL_MASK << VECDESC_FL_LEVEL_SHIFT);
844+
#endif
793845
handle->vector_desc->source = ETS_INTERNAL_UNUSED_INTR_SOURCE;
794846

795847
//Also kill non_iram mask bit.
@@ -802,11 +854,17 @@ static esp_err_t intr_free_for_current_cpu(intr_handle_t handle)
802854

803855
int esp_intr_get_intno(intr_handle_t handle)
804856
{
857+
if (handle == NULL || handle->vector_desc == NULL) {
858+
return -1;
859+
}
805860
return handle->vector_desc->intno;
806861
}
807862

808863
int esp_intr_get_cpu(intr_handle_t handle)
809864
{
865+
if (handle == NULL || handle->vector_desc == NULL) {
866+
return -1;
867+
}
810868
return handle->vector_desc->cpu;
811869
}
812870

components/esp_hw_support/test_apps/esp_hw_support_unity_tests/main/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ if(CONFIG_SOC_ETM_SUPPORTED)
2222
endif()
2323

2424
if(CONFIG_SOC_GPTIMER_SUPPORTED)
25-
list(APPEND SRC "test_intr_alloc.c")
25+
list(APPEND srcs "test_intr_alloc.c")
2626
endif()
2727

2828
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,

0 commit comments

Comments
 (0)