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);
|
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 */
|
#endif /* CONFIG_DYNAMIC_INTERRUPTS */
|
||||||
|
|
|
@ -70,6 +70,31 @@ irq_connect_dynamic(unsigned int irq, unsigned int priority,
|
||||||
flags);
|
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.
|
* @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
|
#ifdef CONFIG_DYNAMIC_INTERRUPTS
|
||||||
void z_isr_install(unsigned int irq, void (*routine)(const void *),
|
void z_isr_install(unsigned int irq, void (*routine)(const void *),
|
||||||
const void *param);
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -323,6 +323,24 @@ int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
|
||||||
void (*routine)(const void *parameter),
|
void (*routine)(const void *parameter),
|
||||||
const void *parameter, uint32_t flags);
|
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)
|
* @def ARCH_IRQ_CONNECT(irq, pri, isr, arg, flags)
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue