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:
Laurentiu Mihalcea 2023-08-11 12:04:23 +03:00 committed by Carles Cufí
parent 30d362dbac
commit 02f52503bb
2 changed files with 75 additions and 2 deletions

View file

@ -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 */

View file

@ -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;