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
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
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);
|
||||
*(_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,,)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue