Skip to content

Commit

Permalink
arch: Add support for dynamically disconnecting shared interrupts
Browse files Browse the repository at this point in the history
This commit provides the users a way to disconnect dynamic
interrupts. This is needed because we don't want to keep
piling up ISR/arg pairs until the number of registrable
clients is reached.

This feature is only relevant for shared and dynamic interrupts.

Signed-off-by: Laurentiu Mihalcea <[email protected]>
  • Loading branch information
LaurentiuM1234 committed Aug 11, 2023
1 parent 9661820 commit fce4907
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 0 deletions.
92 changes: 92 additions & 0 deletions arch/common/shared_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,96 @@ void z_isr_install(unsigned int irq, void (*routine)(const void *),
k_spin_unlock(&lock, key);
}

static void swap_client_data(struct shared_irq_client *a,
struct shared_irq_client *b)
{
struct shared_irq_client tmp;

tmp.arg = a->arg;
tmp.routine = a->routine;

a->arg = b->arg;
a->routine = b->routine;

b->arg = tmp.arg;
b->routine = tmp.routine;
}

static void shared_irq_remove_client(struct shared_irq_data *irq_data,
int client_idx, unsigned int table_idx)
{
int i;

irq_data->clients[client_idx].routine = NULL;
irq_data->clients[client_idx].arg = NULL;

/* push back the removed client to the end of the client list */
for (i = client_idx; i <= (int)irq_data->client_num - 2; i++)
swap_client_data(&irq_data->clients[i],
&irq_data->clients[i + 1]);

irq_data->client_num--;

/* "unshare" interrupt if there will be a single client left */
if (irq_data->client_num == 1) {
_sw_isr_table[table_idx].isr = irq_data->clients[0].routine;
_sw_isr_table[table_idx].arg = irq_data->clients[0].arg;

irq_data->clients[0].routine = NULL;
irq_data->clients[0].arg = NULL;

irq_data->client_num--;
}
}

int __weak arch_irq_disconnect_dynamic(unsigned int irq, unsigned int priority,
void (*routine)(const void *parameter),
const void *parameter, uint32_t flags)
{
ARG_UNUSED(priority);
ARG_UNUSED(flags);

return z_isr_uninstall(irq, routine, parameter);
}

int z_isr_uninstall(unsigned int irq,
void (*routine)(const void *),
const void *parameter)
{
struct shared_irq_data *irq_data;
unsigned int table_idx;
int i;
k_spinlock_key_t key;

table_idx = irq - CONFIG_GEN_IRQ_START_VECTOR;

/* check for out of bounds table index */
if (table_idx >= CONFIG_NUM_IRQS)
return -EINVAL;

irq_data = &_shared_irq_table[table_idx];

key = k_spin_lock(&lock);

/* IRQ is not being shared ATM */
/* TODO: would it be useful to remove the ISR/arg pair from
* _sw_isr_table?
*/
if (!irq_data->client_num)
return 0;

for (i = 0; i < irq_data->client_num; i++) {
if (irq_data->clients[i].routine == routine &&
irq_data->clients[i].arg == parameter) {
/* note: this is the only match we're going to get */
shared_irq_remove_client(irq_data, i, table_idx);
goto out_unlock;
}
}

out_unlock:
k_spin_unlock(&lock, key);
return 0;
}

#endif /* CONFIG_DYNAMIC_INTERRUPTS */
24 changes: 24 additions & 0 deletions include/zephyr/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ irq_connect_dynamic(unsigned int irq, unsigned int priority,
flags);
}

/**
* Disconnect a dynamic interrupt.
*
* Use this in conjunction with shared interrupts to remove a routine/parameter
* pair from the list of clients using the same interrupt line. This will have
* no effect if the interrupt is not being shared.
*
* @param irq IRQ line number
* @param priority Interrupt priority
* @param routine Interrupt service routine
* @param parameter ISR parameter
* @param flags Arch-specific IRQ configuration flags
*
* @return 0 in case of success, negative value otherwise
*/
static inline int
irq_disconnect_dynamic(unsigned int irq, unsigned int priority,
void (*routine)(const void *parameter),
const void *parameter, uint32_t flags)
{
return arch_irq_disconnect_dynamic(irq, priority, routine,
parameter, flags);
}

/**
* @brief Initialize a 'direct' interrupt handler.
*
Expand Down
5 changes: 5 additions & 0 deletions include/zephyr/sw_isr_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ extern struct shared_irq_data _shared_irq_table[];
#ifdef CONFIG_DYNAMIC_INTERRUPTS
void z_isr_install(unsigned int irq, void (*routine)(const void *),
const void *param);

#ifdef CONFIG_SHARED_INTERRUPTS
int z_isr_uninstall(unsigned int irq, void (*routine)(const void *),
const void *param);
#endif /* CONFIG_SHARED_INTERRUPTS */
#endif

#ifdef __cplusplus
Expand Down
16 changes: 16 additions & 0 deletions include/zephyr/sys/arch_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,22 @@ int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
void (*routine)(const void *parameter),
const void *parameter, uint32_t flags);

/**
* Arch-specific hook to dynamically uninstall a shared interrupt.
* If the interrupt is not being shared, this will have no effect.
*
* @param irq IRQ line number
* @param priority Interrupt priority
* @param routine Interrupt service routine
* @param parameter ISR parameter
* @param flags Arch-specific IRQ configuration flag
*
* @return 0 in case of success, negative value otherwise
*/
int arch_irq_disconnect_dynamic(unsigned int irq, unsigned int priority,
void (*routine)(const void *parameter),
const void *parameter, uint32_t flags);

/**
* @def ARCH_IRQ_CONNECT(irq, pri, isr, arg, flags)
*
Expand Down

0 comments on commit fce4907

Please sign in to comment.