From 017cf89a836216802f6fe69d8c7a77e970c31918 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 11 Aug 2023 11:58:34 +0300 Subject: [PATCH] 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 --- arch/Kconfig | 17 +++++ arch/common/CMakeLists.txt | 2 + arch/common/isr_tables.c | 5 ++ arch/common/shared_irq.c | 31 ++++++++ include/zephyr/linker/common-ram.ld | 9 +++ .../common-rom/common-rom-kernel-devices.ld | 9 +++ include/zephyr/linker/section_tags.h | 4 + include/zephyr/linker/sections.h | 5 ++ include/zephyr/sw_isr_table.h | 16 ++++ scripts/build/gen_isr_tables.py | 75 ++++++++++++++++--- 10 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 arch/common/shared_irq.c diff --git a/arch/Kconfig b/arch/Kconfig index 332bb49455..4560fdfcc5 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -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 diff --git a/arch/common/CMakeLists.txt b/arch/common/CMakeLists.txt index f9c6f8a72b..8056ad5d95 100644 --- a/arch/common/CMakeLists.txt +++ b/arch/common/CMakeLists.txt @@ -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) diff --git a/arch/common/isr_tables.c b/arch/common/isr_tables.c index a22776f8ed..0311f81f25 100644 --- a/arch/common/isr_tables.c +++ b/arch/common/isr_tables.c @@ -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 diff --git a/arch/common/shared_irq.c b/arch/common/shared_irq.c new file mode 100644 index 0000000000..9548ceb7e7 --- /dev/null +++ b/arch/common/shared_irq.c @@ -0,0 +1,31 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* 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); + } + } +} diff --git a/include/zephyr/linker/common-ram.ld b/include/zephyr/linker/common-ram.ld index bfaa26ed40..b77e16a618 100644 --- a/include/zephyr/linker/common-ram.ld +++ b/include/zephyr/linker/common-ram.ld @@ -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,,) diff --git a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld index 6b26691fa5..3d074ac1b3 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -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_ initlevel sections */ diff --git a/include/zephyr/linker/section_tags.h b/include/zephyr/linker/section_tags.h index 65df354a7b..d0ef2ecf12 100644 --- a/include/zephyr/linker/section_tags.h +++ b/include/zephyr/linker/section_tags.h @@ -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) diff --git a/include/zephyr/linker/sections.h b/include/zephyr/linker/sections.h index ff6dae15dc..2f1049d09f 100644 --- a/include/zephyr/linker/sections.h +++ b/include/zephyr/linker/sections.h @@ -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 diff --git a/include/zephyr/sw_isr_table.h b/include/zephyr/sw_isr_table.h index 3fd687d2c5..dd700fdfd4 100644 --- a/include/zephyr/sw_isr_table.h +++ b/include/zephyr/sw_isr_table.h @@ -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) diff --git a/scripts/build/gen_isr_tables.py b/scripts/build/gen_isr_tables.py index 1b564e17a3..5c525d6787 100755 --- a/scripts/build/gen_isr_tables.py +++ b/scripts/build/gen_isr_tables.py @@ -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()