arch: Add support for dynamic shared interrupts
This works by overwriting z_isr_install()'s definition (possible since the symbol is now weak) with our own definiton. Whenever trying to register a new ISR/arg pair, z_isr_install() will check to see if the interrupt line is already in use. If it's not then it will not share the interrupt and will work exactly as the old z_isr_install(), meaning it will just write the new ISR/arg pair to _sw_isr_table. If the interrupt line is already being used by an ISR/arg pair then that line will become shared, meaning we'll overwrite _sw_isr_table with our own (z_shared_isr, z_shared_sw_isr_table[irq]) pair. Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
This commit is contained in:
parent
30d362dbac
commit
02f52503bb
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <zephyr/sw_isr_table.h>
|
||||
#include <zephyr/spinlock.h>
|
||||
|
||||
/* an interrupt line can be considered shared only if there's
|
||||
* at least 2 clients using it. As such, enforce the fact that
|
||||
|
@ -29,3 +30,75 @@ void z_shared_isr(const void *data)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_INTERRUPTS
|
||||
|
||||
static struct k_spinlock lock;
|
||||
|
||||
void z_isr_install(unsigned int irq, void (*routine)(const void *),
|
||||
const void *param)
|
||||
{
|
||||
struct z_shared_isr_table_entry *shared_entry;
|
||||
struct _isr_table_entry *entry;
|
||||
struct z_shared_isr_client *client;
|
||||
unsigned int table_idx;
|
||||
int 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;
|
||||
}
|
||||
|
||||
shared_entry = &z_shared_sw_isr_table[table_idx];
|
||||
entry = &_sw_isr_table[table_idx];
|
||||
|
||||
key = k_spin_lock(&lock);
|
||||
|
||||
/* have we reached the client limit? */
|
||||
__ASSERT(shared_entry->client_num < CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS,
|
||||
"reached maximum number of clients");
|
||||
|
||||
if (entry->isr == z_irq_spurious) {
|
||||
/* this is the first time a ISR/arg pair is registered
|
||||
* for INTID => no need to share it.
|
||||
*/
|
||||
entry->isr = routine;
|
||||
entry->arg = param;
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
|
||||
return;
|
||||
} else if (entry->isr != z_shared_isr) {
|
||||
/* INTID is being used by another ISR/arg pair.
|
||||
* Push back the ISR/arg pair registered in _sw_isr_table
|
||||
* to the list of clients and hijack the pair stored in
|
||||
* _sw_isr_table with our own z_shared_isr/shared_entry pair.
|
||||
*/
|
||||
shared_entry->clients[shared_entry->client_num].isr = entry->isr;
|
||||
shared_entry->clients[shared_entry->client_num].arg = entry->arg;
|
||||
|
||||
shared_entry->client_num++;
|
||||
|
||||
entry->isr = z_shared_isr;
|
||||
entry->arg = shared_entry;
|
||||
}
|
||||
|
||||
/* don't register the same ISR/arg pair multiple times */
|
||||
for (i = 0; i < shared_entry->client_num; i++) {
|
||||
client = &shared_entry->clients[i];
|
||||
|
||||
__ASSERT(client->isr != routine && client->arg != param,
|
||||
"trying to register duplicate ISR/arg pair");
|
||||
}
|
||||
|
||||
shared_entry->clients[shared_entry->client_num].isr = routine;
|
||||
shared_entry->clients[shared_entry->client_num].arg = param;
|
||||
shared_entry->client_num++;
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_INTERRUPTS */
|
||||
|
|
|
@ -108,8 +108,8 @@ unsigned int z_get_sw_isr_table_idx(unsigned int irq)
|
|||
return table_idx;
|
||||
}
|
||||
|
||||
void z_isr_install(unsigned int irq, void (*routine)(const void *),
|
||||
const void *param)
|
||||
void __weak z_isr_install(unsigned int irq, void (*routine)(const void *),
|
||||
const void *param)
|
||||
{
|
||||
unsigned int table_idx;
|
||||
|
||||
|
|
Loading…
Reference in a new issue