arch: Add support for dynamically disconnecting shared interrupts
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 <laurentiu.mihalcea@nxp.com>
This commit is contained in:
parent
02f52503bb
commit
b8d487e54b
|
@ -101,4 +101,109 @@ void z_isr_install(unsigned int irq, void (*routine)(const void *),
|
|||
k_spin_unlock(&lock, key);
|
||||
}
|
||||
|
||||
static void swap_client_data(struct z_shared_isr_client *a,
|
||||
struct z_shared_isr_client *b)
|
||||
{
|
||||
struct z_shared_isr_client tmp;
|
||||
|
||||
tmp.arg = a->arg;
|
||||
tmp.isr = a->isr;
|
||||
|
||||
a->arg = b->arg;
|
||||
a->isr = b->isr;
|
||||
|
||||
b->arg = tmp.arg;
|
||||
b->isr = tmp.isr;
|
||||
}
|
||||
|
||||
static void shared_irq_remove_client(struct z_shared_isr_table_entry *shared_entry,
|
||||
int client_idx, unsigned int table_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
shared_entry->clients[client_idx].isr = NULL;
|
||||
shared_entry->clients[client_idx].arg = NULL;
|
||||
|
||||
/* push back the removed client to the end of the client list */
|
||||
for (i = client_idx; i <= (int)shared_entry->client_num - 2; i++) {
|
||||
swap_client_data(&shared_entry->clients[i],
|
||||
&shared_entry->clients[i + 1]);
|
||||
}
|
||||
|
||||
shared_entry->client_num--;
|
||||
|
||||
/* "unshare" interrupt if there will be a single client left */
|
||||
if (shared_entry->client_num == 1) {
|
||||
_sw_isr_table[table_idx].isr = shared_entry->clients[0].isr;
|
||||
_sw_isr_table[table_idx].arg = shared_entry->clients[0].arg;
|
||||
|
||||
shared_entry->clients[0].isr = NULL;
|
||||
shared_entry->clients[0].arg = NULL;
|
||||
|
||||
shared_entry->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 z_shared_isr_table_entry *shared_entry;
|
||||
struct _isr_table_entry *entry;
|
||||
struct z_shared_isr_client *client;
|
||||
unsigned int table_idx;
|
||||
size_t i;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
table_idx = z_get_sw_isr_table_idx(irq);
|
||||
|
||||
/* check for out of bounds table index */
|
||||
if (table_idx >= CONFIG_NUM_IRQS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
shared_entry = &z_shared_sw_isr_table[table_idx];
|
||||
entry = &_sw_isr_table[table_idx];
|
||||
|
||||
key = k_spin_lock(&lock);
|
||||
|
||||
/* note: it's important that we remove the ISR/arg pair even if
|
||||
* the IRQ line is not being shared because z_isr_install() will
|
||||
* not overwrite it unless the _sw_isr_table entry for the given
|
||||
* IRQ line contains the default pair which is z_irq_spurious/NULL.
|
||||
*/
|
||||
if (!shared_entry->client_num) {
|
||||
if (entry->isr == routine && entry->arg == parameter) {
|
||||
entry->isr = z_irq_spurious;
|
||||
entry->arg = NULL;
|
||||
}
|
||||
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
for (i = 0; i < shared_entry->client_num; i++) {
|
||||
client = &shared_entry->clients[i];
|
||||
|
||||
if (client->isr == routine && client->arg == parameter) {
|
||||
/* note: this is the only match we're going to get */
|
||||
shared_irq_remove_client(shared_entry, i, table_idx);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
k_spin_unlock(&lock, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_INTERRUPTS */
|
||||
|
|
|
@ -70,6 +70,31 @@ 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. If the interrupt
|
||||
* is not being shared then the associated _sw_isr_table entry will be replaced
|
||||
* by (NULL, z_irq_spurious) (default entry).
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
|
|
|
@ -107,6 +107,11 @@ unsigned int z_get_sw_isr_table_idx(unsigned int irq);
|
|||
#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
|
||||
|
|
|
@ -323,6 +323,24 @@ 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, then the associated
|
||||
* _sw_isr_table entry will be replaced by (NULL, z_irq_spurious)
|
||||
* (default entry).
|
||||
*
|
||||
* @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)
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue