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:
parent
ef77fe3402
commit
017cf89a83
17
arch/Kconfig
17
arch/Kconfig
|
@ -403,6 +403,23 @@ config DYNAMIC_INTERRUPTS
|
||||||
interrupt-related data structures to RAM instead of ROM, and
|
interrupt-related data structures to RAM instead of ROM, and
|
||||||
on some architectures increase code size.
|
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
|
config GEN_ISR_TABLES
|
||||||
bool "Use generated IRQ tables"
|
bool "Use generated IRQ tables"
|
||||||
help
|
help
|
||||||
|
|
|
@ -10,6 +10,8 @@ zephyr_library_sources_ifdef(
|
||||||
sw_isr_common.c
|
sw_isr_common.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_SHARED_INTERRUPTS shared_irq.c)
|
||||||
|
|
||||||
if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND
|
if(NOT CONFIG_ARCH_HAS_TIMING_FUNCTIONS AND
|
||||||
NOT CONFIG_SOC_HAS_TIMING_FUNCTIONS AND
|
NOT CONFIG_SOC_HAS_TIMING_FUNCTIONS AND
|
||||||
NOT CONFIG_BOARD_HAS_TIMING_FUNCTIONS)
|
NOT CONFIG_BOARD_HAS_TIMING_FUNCTIONS)
|
||||||
|
|
|
@ -81,3 +81,8 @@ struct _isr_table_entry __sw_isr_table _sw_isr_table[IRQ_TABLE_SIZE] = {
|
||||||
(void *)&z_irq_spurious},
|
(void *)&z_irq_spurious},
|
||||||
};
|
};
|
||||||
#endif
|
#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
31
arch/common/shared_irq.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,15 @@
|
||||||
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
|
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
|
||||||
*(_SW_ISR_TABLE_SECTION_SYMS)
|
*(_SW_ISR_TABLE_SECTION_SYMS)
|
||||||
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} 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
|
#endif
|
||||||
|
|
||||||
SECTION_DATA_PROLOGUE(device_states,,)
|
SECTION_DATA_PROLOGUE(device_states,,)
|
||||||
|
|
|
@ -33,6 +33,15 @@
|
||||||
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
|
. = ALIGN(CONFIG_ARCH_SW_ISR_TABLE_ALIGN);
|
||||||
*(_SW_ISR_TABLE_SECTION_SYMS)
|
*(_SW_ISR_TABLE_SECTION_SYMS)
|
||||||
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} 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
|
#endif
|
||||||
|
|
||||||
/* verify we don't have rogue .z_init_<something> initlevel sections */
|
/* verify we don't have rogue .z_init_<something> initlevel sections */
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#define __irq_vector_table Z_GENERIC_SECTION(_IRQ_VECTOR_TABLE_SECTION_NAME)
|
#define __irq_vector_table Z_GENERIC_SECTION(_IRQ_VECTOR_TABLE_SECTION_NAME)
|
||||||
#define __sw_isr_table Z_GENERIC_SECTION(_SW_ISR_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 */
|
/* Attribute macros to place code and data into IMR memory */
|
||||||
#define __imr __in_section_unique(imr)
|
#define __imr __in_section_unique(imr)
|
||||||
#define __imrdata __in_section_unique(imrdata)
|
#define __imrdata __in_section_unique(imrdata)
|
||||||
|
|
|
@ -39,6 +39,11 @@
|
||||||
#define _SW_ISR_TABLE_SECTION_NAME .gnu.linkonce.sw_isr_table
|
#define _SW_ISR_TABLE_SECTION_NAME .gnu.linkonce.sw_isr_table
|
||||||
#define _SW_ISR_TABLE_SECTION_SYMS .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 */
|
/* Architecture-specific sections */
|
||||||
#if defined(CONFIG_ARM)
|
#if defined(CONFIG_ARM)
|
||||||
#define _KINETIS_FLASH_CONFIG_SECTION_NAME kinetis_flash_config
|
#define _KINETIS_FLASH_CONFIG_SECTION_NAME kinetis_flash_config
|
||||||
|
|
|
@ -61,6 +61,22 @@ struct _isr_list {
|
||||||
const void *param;
|
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 */
|
/** This interrupt gets put directly in the vector table */
|
||||||
#define ISR_FLAG_DIRECT BIT(0)
|
#define ISR_FLAG_DIRECT BIT(0)
|
||||||
|
|
||||||
|
|
|
@ -177,11 +177,37 @@ source_header = """
|
||||||
typedef void (* ISR)(const void *);
|
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)
|
fp.write(source_header)
|
||||||
|
|
||||||
nv = intlist["num_vectors"]
|
nv = intlist["num_vectors"]
|
||||||
|
|
||||||
|
if "CONFIG_SHARED_INTERRUPTS" in syms:
|
||||||
|
write_shared_table(fp, shared, nv)
|
||||||
|
|
||||||
if vt:
|
if vt:
|
||||||
if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms:
|
if "CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS" in syms:
|
||||||
write_address_irq_vector_table(fp, vt, nv)
|
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")
|
level3_offset = syms.get("CONFIG_3RD_LVL_ISR_TBL_OFFSET")
|
||||||
|
|
||||||
for i in range(nv):
|
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):
|
if isinstance(func, int):
|
||||||
func_as_string = "{0:#x}".format(func)
|
func_as_string = "{0:#x}".format(func)
|
||||||
else:
|
else:
|
||||||
|
@ -213,7 +243,7 @@ def write_source_file(fp, vt, swt, intlist, syms):
|
||||||
fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
|
fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
|
||||||
format(level3_offset))
|
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")
|
fp.write("};\n")
|
||||||
|
|
||||||
def get_symbols(obj):
|
def get_symbols(obj):
|
||||||
|
@ -291,6 +321,7 @@ def main():
|
||||||
raise ValueError('nvec is too large, check endianness.')
|
raise ValueError('nvec is too large, check endianness.')
|
||||||
|
|
||||||
swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
|
swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
|
||||||
|
swt_shared_handler = "((uintptr_t)&z_shared_isr)"
|
||||||
vt_spurious_handler = "z_irq_spurious"
|
vt_spurious_handler = "z_irq_spurious"
|
||||||
vt_irq_handler = "_isr_wrapper"
|
vt_irq_handler = "_isr_wrapper"
|
||||||
|
|
||||||
|
@ -308,6 +339,7 @@ def main():
|
||||||
# Default to spurious interrupt handler. Configured interrupts
|
# Default to spurious interrupt handler. Configured interrupts
|
||||||
# will replace these entries.
|
# will replace these entries.
|
||||||
swt = [(0, swt_spurious_handler) for i in range(nvec)]
|
swt = [(0, swt_spurious_handler) for i in range(nvec)]
|
||||||
|
shared = [([], 0) for i in range(nvec)]
|
||||||
else:
|
else:
|
||||||
if args.vector_table:
|
if args.vector_table:
|
||||||
vt = [vt_spurious_handler for i in range(nvec)]
|
vt = [vt_spurious_handler for i in range(nvec)]
|
||||||
|
@ -369,16 +401,37 @@ def main():
|
||||||
if not 0 <= table_index < len(swt):
|
if not 0 <= table_index < len(swt):
|
||||||
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
|
error("IRQ %d (offset=%d) exceeds the maximum of %d" %
|
||||||
(table_index, offset, len(swt) - 1))
|
(table_index, offset, len(swt) - 1))
|
||||||
if swt[table_index] != (0, swt_spurious_handler):
|
if "CONFIG_SHARED_INTERRUPTS" in syms:
|
||||||
error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
|
if swt[table_index] != (0, swt_spurious_handler):
|
||||||
+ f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}"
|
# check client limit
|
||||||
+ "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
|
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?")
|
||||||
swt[table_index] = (param, func)
|
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:
|
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__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in a new issue