drivers/interrupt_controller: Introduce multi-level interrupt support
In a scenario where a platform harbours multiple interrupts to the extent the core cannot support it, an interrupt controller is added as an additional level of interrupt. It typically combines several sources of interrupt into one line that is then routed to the parent controller. Signed-off-by: Rajavardhan Gundi <rajavardhan.gundi@intel.com>
This commit is contained in:
parent
9a839df82e
commit
1e6adba9ef
|
@ -630,6 +630,7 @@ if(CONFIG_GEN_ISR_TABLES)
|
|||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
${ZEPHYR_BASE}/arch/common/gen_isr_tables.py
|
||||
--output-source isr_tables.c
|
||||
--kernel $<TARGET_FILE:zephyr_prebuilt>
|
||||
--intlist isrList.bin
|
||||
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--debug>
|
||||
--sw-isr-table
|
||||
|
|
|
@ -8,9 +8,23 @@ import argparse
|
|||
import struct
|
||||
import sys
|
||||
import os
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
ISR_FLAG_DIRECT = (1 << 0)
|
||||
|
||||
# The below few hardware independent magic numbers represent various
|
||||
# levels of interrupts in a multi-level interrupt system.
|
||||
# 0x000000FF - represents the 1st level (i.e. the interrupts
|
||||
# that directly go to the processor).
|
||||
# 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel
|
||||
# into 1 line which then goes into the 1st level)
|
||||
# 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel
|
||||
# into 1 line which then goes into the 2nd level)
|
||||
FIRST_LVL_INTERRUPTS = 0x000000FF
|
||||
SECND_LVL_INTERRUPTS = 0x0000FF00
|
||||
THIRD_LVL_INTERRUPTS = 0x00FF0000
|
||||
|
||||
def debug(text):
|
||||
if args.debug:
|
||||
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
||||
|
@ -104,6 +118,8 @@ def parse_args():
|
|||
help="Print additional debugging information")
|
||||
parser.add_argument("-o", "--output-source", required=True,
|
||||
help="Output source file")
|
||||
parser.add_argument("-k", "--kernel", required=True,
|
||||
help="Zephyr kernel image")
|
||||
parser.add_argument("-s", "--sw-isr-table", action="store_true",
|
||||
help="Generate SW ISR table")
|
||||
parser.add_argument("-V", "--vector-table", action="store_true",
|
||||
|
@ -143,13 +159,59 @@ def write_source_file(fp, vt, swt, intlist):
|
|||
fp.write("\t{(void *)0x%x, (void *)0x%x},\n" % (param, func))
|
||||
fp.write("};\n")
|
||||
|
||||
def get_symbols(obj):
|
||||
for section in obj.iter_sections():
|
||||
if isinstance(section, SymbolTableSection):
|
||||
return {sym.name: sym.entry.st_value
|
||||
for sym in section.iter_symbols()}
|
||||
|
||||
error("Could not find symbol table")
|
||||
|
||||
def getindex(irq, irq_aggregator_pos):
|
||||
for indx, val in enumerate(irq_aggregator_pos):
|
||||
if irq == irq_aggregator_pos[indx]:
|
||||
return indx
|
||||
error("The index %d has no match. Recheck interrupt configuration" % indx)
|
||||
|
||||
def main():
|
||||
parse_args()
|
||||
|
||||
with open(args.kernel, "rb") as fp:
|
||||
kernel = ELFFile(fp)
|
||||
syms = get_symbols(kernel)
|
||||
|
||||
if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
|
||||
if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
|
||||
if "CONFIG_NUM_2ND_LEVEL_AGGREGATORS" in syms:
|
||||
num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
|
||||
list_2nd_lvl_offsets = []
|
||||
for i in range(num_aggregators):
|
||||
offset_str = 'CONFIG_2ND_LVL_INTR_' + str(i).zfill(2) + '_OFFSET'
|
||||
if offset_str in syms:
|
||||
list_2nd_lvl_offsets.append(syms[offset_str])
|
||||
|
||||
debug(str(list_2nd_lvl_offsets))
|
||||
|
||||
if syms["CONFIG_3RD_LEVEL_INTERRUPTS"]:
|
||||
if "CONFIG_NUM_3RD_LEVEL_AGGREGATORS" in syms:
|
||||
num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"]
|
||||
list_3rd_lvl_offsets = []
|
||||
for i in range(num_aggregators):
|
||||
offset_str = 'CONFIG_3RD_LVL_INTR_' + str(i).zfill(2) + '_OFFSET'
|
||||
if offset_str in syms:
|
||||
list_3rd_lvl_offsets.append(syms[offset_str])
|
||||
|
||||
debug(str(list_3rd_lvl_offsets))
|
||||
|
||||
intlist = read_intlist(args.intlist)
|
||||
nvec = intlist["num_vectors"]
|
||||
offset = intlist["offset"]
|
||||
prefix = endian_prefix()
|
||||
numisrs = intlist["num_isrs"]
|
||||
|
||||
debug('offset is ' + str(offset))
|
||||
debug('num_vectors is ' + str(nvec))
|
||||
debug('num_isrs is ' + str(numisrs))
|
||||
|
||||
# Set default entries in both tables
|
||||
if args.sw_isr_table:
|
||||
|
@ -181,7 +243,40 @@ def main():
|
|||
error("Regular Interrupt %d declared with parameter 0x%x "
|
||||
"but no SW ISR_TABLE in use"
|
||||
% (irq, param))
|
||||
swt[irq - offset] = (param, func)
|
||||
|
||||
if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
|
||||
swt[irq - offset] = (param, func)
|
||||
else:
|
||||
# Figure out third level interrupt position
|
||||
debug('IRQ = ' + hex(irq))
|
||||
irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16
|
||||
if irq3:
|
||||
irq_parent = (irq & SECND_LVL_INTERRUPTS) >> 8
|
||||
list_index = getindex(irq_parent, list_3rd_lvl_offsets)
|
||||
irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"]
|
||||
irq3_pos = irq3_baseoffset + 32*list_index + irq3 - 1
|
||||
debug('IRQ_Indx = ' + str(irq3))
|
||||
debug('IRQ_Pos = ' + str(irq3_pos))
|
||||
swt[irq3_pos - offset] = (param, func)
|
||||
|
||||
# Figure out second level interrupt position
|
||||
if not irq3:
|
||||
irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8
|
||||
if irq2:
|
||||
irq_parent = (irq & FIRST_LVL_INTERRUPTS)
|
||||
list_index = getindex(irq_parent, list_2nd_lvl_offsets)
|
||||
irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
|
||||
irq2_pos = irq2_baseoffset + 32*list_index + irq2 - 1
|
||||
debug('IRQ_Indx = ' + str(irq2))
|
||||
debug('IRQ_Pos = ' + str(irq2_pos))
|
||||
swt[irq2_pos - offset] = (param, func)
|
||||
|
||||
# Figure out first level interrupt position
|
||||
if not irq3 and not irq2:
|
||||
irq1 = (irq & FIRST_LVL_INTERRUPTS)
|
||||
debug('IRQ_Indx = ' + str(irq1))
|
||||
debug('IRQ_Pos = ' + str(irq1))
|
||||
swt[irq1 - offset] = (param, func)
|
||||
|
||||
with open(args.output_source, "w") as fp:
|
||||
write_source_file(fp, vt, swt, intlist)
|
||||
|
|
|
@ -57,6 +57,58 @@ nesting support is enabled.
|
|||
alter its behavior depending on whether it is executing as part of
|
||||
a thread or as part of an ISR.
|
||||
|
||||
Multi-level Interrupt handling
|
||||
==============================
|
||||
|
||||
A hardware platform can support more interrupt lines than natively-provided
|
||||
through the use of one or more nested interrupt controllers. Sources of
|
||||
hardware interrupts are combined into one line that is then routed to
|
||||
the parent controller.
|
||||
|
||||
If nested interrupt controllers are supported, :option:`CONFIG_MULTI_LEVEL_INTERRUPTS`
|
||||
should be set to 1, and :option:`CONFIG_2ND_LEVEL_INTERRUPTS` and
|
||||
:option:`CONFIG_3RD_LEVEL_INTERRUPTS` configured as well, based on the
|
||||
hardware architecture.
|
||||
|
||||
A unique 32-bit interrupt number is assigned with information
|
||||
embedded in it to select and invoke the correct Interrupt
|
||||
Service Routine (ISR). Each interrupt level is given a byte within this 32-bit
|
||||
number, providing support for up to four interrupt levels using this arch, as
|
||||
illustrated and explained below:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
9 2 0
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ (LEVEL 1)
|
||||
5 | A |
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ (LEVEL 2)
|
||||
| C B
|
||||
_ _ _ _ _ _ _ (LEVEL 3)
|
||||
D
|
||||
|
||||
There are three interrupt levels shown here.
|
||||
|
||||
* LEVEL 1 has 12 interrupt lines, with two lines (2 and 9) connected
|
||||
to nested controllers.
|
||||
* One of the LEVEL 2 controllers has interrupt line 5 connected to
|
||||
a LEVEL 3 nested controller.
|
||||
* The other LEVEL 2 controller has no nested controllers.
|
||||
|
||||
Here's how unique interrupt numbers are generated for each
|
||||
hardware interrupt. Let's consider four interrupts shown above
|
||||
as A, B, C, and D:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
A -> 0x00000004
|
||||
B -> 0x00000302
|
||||
C -> 0x00000409
|
||||
D -> 0x00030609
|
||||
|
||||
.. note::
|
||||
The bit positions for LEVEL 2 and onward are offset by 1, as 0 means that
|
||||
interrupt number is not present for that level.
|
||||
|
||||
Preventing Interruptions
|
||||
========================
|
||||
|
||||
|
|
|
@ -128,4 +128,6 @@ config PLIC_FE310
|
|||
|
||||
source "drivers/interrupt_controller/Kconfig.stm32"
|
||||
|
||||
source "drivers/interrupt_controller/Kconfig.multilevel"
|
||||
|
||||
endmenu
|
||||
|
|
119
drivers/interrupt_controller/Kconfig.multilevel
Normal file
119
drivers/interrupt_controller/Kconfig.multilevel
Normal file
|
@ -0,0 +1,119 @@
|
|||
# Kconfig - Multilevel interrupt configuration
|
||||
#
|
||||
# Copyright (c) 2017 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config MULTI_LEVEL_INTERRUPTS
|
||||
bool "Multi-level Interrupts"
|
||||
default n
|
||||
depends on GEN_SW_ISR_TABLE
|
||||
help
|
||||
Multiple levels of interrupts are normally used to increase the
|
||||
number of addressable interrupts in a system. For e.g., if 2 levels
|
||||
of interrupts are used, the 2nd level controller would combine all
|
||||
interrupts routed to it into one line which then gets routed to one
|
||||
of the lines in 1st level interrupt controller.
|
||||
|
||||
config MAX_IRQ_PER_AGGREGATOR
|
||||
int "Max IRQs per interrupt aggregator"
|
||||
default 0
|
||||
depends on MULTI_LEVEL_INTERRUPTS
|
||||
help
|
||||
This represents the max number of interrupts that an aggregator
|
||||
can map.
|
||||
|
||||
config 2ND_LEVEL_INTERRUPTS
|
||||
bool "Second-level Interrupts"
|
||||
default n
|
||||
depends on MULTI_LEVEL_INTERRUPTS
|
||||
help
|
||||
Second level interrupts are used to increase the number of
|
||||
addressable interrupts in a system.
|
||||
|
||||
config 2ND_LVL_ISR_TBL_OFFSET
|
||||
int "Offset in the SW ISR Table for 2nd level interrupt controller"
|
||||
default 0
|
||||
depends on MULTI_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the offset in the SW ISR table beginning where the ISRs for
|
||||
2nd level interrupts are located. This typically is allocated after
|
||||
allocating first level interrupts.
|
||||
|
||||
config NUM_2ND_LEVEL_AGGREGATORS
|
||||
hex "Total Number of Second level Interrupt Aggregators"
|
||||
default 0
|
||||
depends on MULTI_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the number of second level interrupt aggregators present
|
||||
in the system. Each of these interrupt aggregators can typically
|
||||
map MAX_IRQ_PER_AGGREGATOR second level interrupts to one first
|
||||
level interrupt.
|
||||
|
||||
config 2ND_LVL_INTR_00_OFFSET
|
||||
hex "Parent interrupt number to which controller_0 maps"
|
||||
default 0x00
|
||||
depends on 2ND_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the interrupt number in the first level to which
|
||||
controller_0 of second level maps.
|
||||
|
||||
config 2ND_LVL_INTR_01_OFFSET
|
||||
hex "Parent interrupt number to which controller_1 maps"
|
||||
default 0x00
|
||||
depends on 2ND_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the interrupt number in the first level to which
|
||||
controller_1 of second level maps.
|
||||
|
||||
config 2ND_LVL_INTR_02_OFFSET
|
||||
hex "Parent interrupt number to which controller_2 maps"
|
||||
default 0x00
|
||||
depends on 2ND_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the interrupt number in the first level to which
|
||||
controller_2 of second level maps.
|
||||
|
||||
config 2ND_LVL_INTR_03_OFFSET
|
||||
hex "Parent interrupt number to which controller_3 maps"
|
||||
default 0x00
|
||||
depends on 2ND_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the interrupt number in the first level to which
|
||||
controller_3 of second level maps.
|
||||
|
||||
config 3RD_LEVEL_INTERRUPTS
|
||||
bool "Third-level Interrupts"
|
||||
default n
|
||||
depends on 2ND_LEVEL_INTERRUPTS
|
||||
help
|
||||
Third level interrupts are used to increase the number of
|
||||
addressable interrupts in a system.
|
||||
|
||||
config NUM_3RD_LEVEL_AGGREGATORS
|
||||
hex "Total Number of Third level Interrupt Aggregators"
|
||||
default 0
|
||||
depends on 3RD_LEVEL_INTERRUPTS
|
||||
help
|
||||
These are the number of third level interrupt aggregators present
|
||||
in the system. Each of these interrupt aggregators can typically
|
||||
map MAX_IRQ_PER_AGGREGATOR third level interrupts to one second
|
||||
level interrupt.
|
||||
|
||||
config 3RD_LVL_ISR_TBL_OFFSET
|
||||
int "Offset in the SW ISR Table for 3rd level interrupt controller"
|
||||
default 0
|
||||
depends on 3RD_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the offset in the SW ISR table beginning where the ISRs for
|
||||
3rd level interrupts are located. This typically is allocated after
|
||||
allocating first and second level interrupts.
|
||||
|
||||
config 3RD_LVL_INTR_00_OFFSET
|
||||
hex "Parent interrupt number to which controller_0 maps"
|
||||
default 0x00
|
||||
depends on 3RD_LEVEL_INTERRUPTS
|
||||
help
|
||||
This is the interrupt number in the second level to which
|
||||
controller_0 of third level maps.
|
Loading…
Reference in a new issue