From 02f52503bb32fdf4d363305a9e9bfe1a7ee23709 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 11 Aug 2023 12:04:23 +0300 Subject: [PATCH] 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 --- arch/common/shared_irq.c | 73 +++++++++++++++++++++++++++++++++++++ arch/common/sw_isr_common.c | 4 +- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/arch/common/shared_irq.c b/arch/common/shared_irq.c index 9548ceb7e7..075a167985 100644 --- a/arch/common/shared_irq.c +++ b/arch/common/shared_irq.c @@ -5,6 +5,7 @@ */ #include +#include /* 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 */ diff --git a/arch/common/sw_isr_common.c b/arch/common/sw_isr_common.c index 2aa6202aae..a5262e7fa8 100644 --- a/arch/common/sw_isr_common.c +++ b/arch/common/sw_isr_common.c @@ -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;