arch: Add support for static shared interrupts

This commit introduces all the necessary changes for
enabling the usage of shared interrupts.

This works by using a second interrupt table: _shared_sw_isr_table
which keeps track of all of the ISR/arg pairs sharing the same
interrupt line. Whenever a second ISR/arg pair is registered
on the same interrupt line using IRQ_CONNECT(), the entry in
_sw_isr_table will be overwriten by a
(shared_isr, _shared_sw_isr_table[irq]) pair. In turn, shared_isr()
will invoke all of the ISR/arg pairs registered on the same
interrupt line.

This feature only works statically, meaning you can only make use
of shared interrupts using IRQ_CONNECT(). Attempting to dynamically
register a ISR/arg pair will overwrite the hijacked _sw_isr_table
entry.

Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
This commit is contained in:
Laurentiu Mihalcea 2023-08-11 11:58:34 +03:00 committed by Carles Cufí
parent ef77fe3402
commit 017cf89a83
10 changed files with 162 additions and 11 deletions

View file

@ -403,6 +403,23 @@ config DYNAMIC_INTERRUPTS
interrupt-related data structures to RAM instead of ROM, and
on some architectures increase code size.
config SHARED_INTERRUPTS
bool "Set this to enable support for shared interrupts"
depends on GEN_SW_ISR_TABLE
select EXPERIMENTAL
help
Set this to enable support for shared interrupts. Use this with
caution as enabling this will increase the image size by a
non-negligible amount.
config SHARED_IRQ_MAX_NUM_CLIENTS
int "Maximum number of clients allowed per shared interrupt"
default 2
depends on SHARED_INTERRUPTS
help
This option controls the maximum number of clients allowed
per shared interrupt. Set this according to your needs.
config GEN_ISR_TABLES
bool "Use generated IRQ tables"
help

View file

@ -10,6 +10,8 @@ zephyr_library_sources_ifdef(
sw_isr_common.c
)
zephyr_library_sources_ifdef(CONFIG_SHARED_INTERRUPTS shared_irq.c)
if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND
NOT CONFIG_SOC_HAS_TIMING_FUNCTIONS AND
NOT CONFIG_BOARD_HAS_TIMING_FUNCTIONS)

View file

@ -81,3 +81,8 @@ struct _isr_table_entry __sw_isr_table _sw_isr_table[IRQ_TABLE_SIZE] = {
(void *)&z_irq_spurious},
};
#endif
#ifdef CONFIG_SHARED_INTERRUPTS
struct z_shared_isr_table_entry __shared_sw_isr_table z_shared_sw_isr_table[IRQ_TABLE_SIZE] = {
};
#endif

31
arch/common/shared_irq.c Normal file
View file

@ -0,0 +1,31 @@
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sw_isr_table.h>
/* an interrupt line can be considered shared only if there's
* at least 2 clients using it. As such, enforce the fact that
* the maximum number of allowed clients should be at least 2.
*/
BUILD_ASSERT(CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS >= 2,
"maximum number of clients should be at least 2");
void z_shared_isr(const void *data)
{
size_t i;
const struct z_shared_isr_table_entry *entry;
const struct z_shared_isr_client *client;
entry = data;
for (i = 0; i < entry->client_num; i++) {
client = &entry->clients[i];
if (client->isr) {
client->isr(client->arg);
}
}
}

View file

@ -27,6 +27,15 @@
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
*(_SW_ISR_TABLE_SECTION_SYMS)
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if defined(CONFIG_SHARED_INTERRUPTS)
SECTION_DATA_PROLOGUE(shared_sw_isr_table,,)
{
/* TODO: does this section require alignment? */
KEEP(*(_SHARED_SW_ISR_TABLE_SECTION_SYMS))
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#endif
#endif
SECTION_DATA_PROLOGUE(device_states,,)

View file

@ -33,6 +33,15 @@
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
*(_SW_ISR_TABLE_SECTION_SYMS)
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if defined(CONFIG_SHARED_INTERRUPTS)
SECTION_PROLOGUE(shared_sw_isr_table,,)
{
/* TODO: does this section require alignment? */
KEEP(*(_SHARED_SW_ISR_TABLE_SECTION_SYMS))
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#endif
#endif
/* verify we don't have rogue .z_init_<something> initlevel sections */

View file

@ -18,6 +18,10 @@
#define __irq_vector_table Z_GENERIC_SECTION(_IRQ_VECTOR_TABLE_SECTION_NAME)
#define __sw_isr_table Z_GENERIC_SECTION(_SW_ISR_TABLE_SECTION_NAME)
#ifdef CONFIG_SHARED_INTERRUPTS
#define __shared_sw_isr_table Z_GENERIC_SECTION(_SHARED_SW_ISR_TABLE_SECTION_NAME)
#endif /* CONFIG_SHARED_INTERRUPTS */
/* Attribute macros to place code and data into IMR memory */
#define __imr __in_section_unique(imr)
#define __imrdata __in_section_unique(imrdata)

View file

@ -39,6 +39,11 @@
#define _SW_ISR_TABLE_SECTION_NAME .gnu.linkonce.sw_isr_table
#define _SW_ISR_TABLE_SECTION_SYMS .gnu.linkonce.sw_isr_table*
#ifdef CONFIG_SHARED_INTERRUPTS
#define _SHARED_SW_ISR_TABLE_SECTION_NAME .gnu.linkonce.shared_sw_isr_table
#define _SHARED_SW_ISR_TABLE_SECTION_SYMS .gnu.linkonce.shared_sw_isr_table*
#endif /* CONFIG_SHARED_INTERRUPTS */
/* Architecture-specific sections */
#if defined(CONFIG_ARM)
#define _KINETIS_FLASH_CONFIG_SECTION_NAME kinetis_flash_config

View file

@ -61,6 +61,22 @@ struct _isr_list {
const void *param;
};
#ifdef CONFIG_SHARED_INTERRUPTS
struct z_shared_isr_client {
void (*isr)(const void *arg);
const void *arg;
};
struct z_shared_isr_table_entry {
struct z_shared_isr_client clients[CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS];
size_t client_num;
};
void z_shared_isr(const void *data);
extern struct z_shared_isr_table_entry z_shared_sw_isr_table[];
#endif /* CONFIG_SHARED_INTERRUPTS */
/** This interrupt gets put directly in the vector table */
#define ISR_FLAG_DIRECT BIT(0)

View file

@ -177,11 +177,37 @@ source_header = """
typedef void (* ISR)(const void *);
"""
def write_source_file(fp, vt, swt, intlist, syms):
def write_shared_table(fp, shared, nv):
fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
" z_shared_sw_isr_table[%d] = {\n" % nv)
for i in range(nv):
client_num = shared[i][1]
client_list = shared[i][0]
if not client_num:
fp.write("\t{ },\n")
else:
fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
for j in range(0, client_num):
routine = client_list[j][1]
arg = client_list[j][0]
fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
f".arg = (const void *){hex(arg)} }},")
fp.write(" },\n},\n")
fp.write("};\n")
def write_source_file(fp, vt, swt, intlist, syms, shared):
fp.write(source_header)
nv = intlist["num_vectors"]
if "CONFIG_SHARED_INTERRUPTS" in syms:
write_shared_table(fp, shared, nv)
if vt:
if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms:
write_address_irq_vector_table(fp, vt, nv)
@ -200,7 +226,11 @@ def write_source_file(fp, vt, swt, intlist, syms):
level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
for i in range(nv):
param, func = swt[i]
param = "{0:#x}".format(swt[i][0])
func = swt[i][1]
if isinstance (func, str) and "z_shared_isr" in func:
param = "&z_shared_sw_isr_table[{0}]".format(i)
if isinstance(func, int):
func_as_string = "{0:#x}".format(func)
else:
@ -213,7 +243,7 @@ def write_source_file(fp, vt, swt, intlist, syms):
fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
format(level3_offset))
fp.write("\t{{(const void *){0:#x}, (ISR){1}}},\n".format(param, func_as_string))
fp.write("\t{{(const void *){0}, (ISR){1}}},\n".format(param, func_as_string))
fp.write("};\n")
def get_symbols(obj):
@ -291,6 +321,7 @@ def main():
raise ValueError('nvec is too large, check endianness.')
swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
swt_shared_handler = "((uintptr_t)&z_shared_isr)"
vt_spurious_handler = "z_irq_spurious"
vt_irq_handler = "_isr_wrapper"
@ -308,6 +339,7 @@ def main():
# Default to spurious interrupt handler. Configured interrupts
# will replace these entries.
swt = [(0, swt_spurious_handler) for i in range(nvec)]
shared = [([], 0) for i in range(nvec)]
else:
if args.vector_table:
vt = [vt_spurious_handler for i in range(nvec)]
@ -369,16 +401,37 @@ def main():
if not 0 <= table_index < len(swt):
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
(table_index, offset, len(swt) - 1))
if swt[table_index] != (0, swt_spurious_handler):
error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}"
+ "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
)
swt[table_index] = (param, func)
if "CONFIG_SHARED_INTERRUPTS" in syms:
if swt[table_index] != (0, swt_spurious_handler):
# check client limit
if syms["CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"] == shared[table_index][1]:
error(f"Reached shared interrupt client limit. Maybe increase"
+ f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
lst = shared[table_index][0]
delta_size = 1
if not shared[table_index][1]:
lst.append(swt[table_index])
# note: the argument will be fixed when writing the ISR table
# to isr_table.c
swt[table_index] = (0, swt_shared_handler)
delta_size += 1
if (param, func) in lst:
error("Attempting to register the same ISR/arg pair twice.")
lst.append((param, func))
shared[table_index] = (lst, shared[table_index][1] + delta_size)
else:
swt[table_index] = (param, func)
else:
if swt[table_index] != (0, swt_spurious_handler):
error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}"
+ "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
)
else:
swt[table_index] = (param, func)
with open(args.output_source, "w") as fp:
write_source_file(fp, vt, swt, intlist, syms)
write_source_file(fp, vt, swt, intlist, syms, shared)
if __name__ == "__main__":
main()