Skip to content

Commit

Permalink
devicetree: encode multi-level interrupt number in C devicetree magic
Browse files Browse the repository at this point in the history
The multi-level encoding of the interrupt number currently
happens in the `gen_defines.py`, which is called in the
`dts.cmake` module after `kconfig.cmake`. However, the number
of bits used by each level is defined in Kconfig and this means
that `gen_defines.py` will not be able to get that information
during build.

To fix this, do the multi-level encoding in C devicetree macro
magic instead of the python script. This ticks one of a
long-standing TODO item from the `gen_defines.py`.

Signed-off-by: Yong Cong Sin <[email protected]>
  • Loading branch information
ycsin authored and cfriedt committed Oct 30, 2023
1 parent d7302f4 commit df2c068
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 21 deletions.
2 changes: 1 addition & 1 deletion drivers/gpio/gpio_sifive.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ DEVICE_DT_INST_DEFINE(0,
&gpio_sifive_driver);

#define IRQ_INIT(n) \
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, n, irq), \
IRQ_CONNECT(DT_INST_IRQN_BY_IDX(0, n), \
DT_INST_IRQ_BY_IDX(0, n, priority), \
gpio_sifive_irq_handler, \
DEVICE_DT_INST_GET(0), \
Expand Down
84 changes: 82 additions & 2 deletions include/zephyr/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define DEVICETREE_H

#include <devicetree_generated.h>
#include <zephyr/irq_multilevel.h>

#if !defined(_LINKER) && !defined(_ASMLANGUAGE)
#include <stdint.h>
Expand Down Expand Up @@ -2409,6 +2410,77 @@
*/
#define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell)

/**
* @cond INTERNAL_HIDDEN
*/

/* DT helper macro to get interrupt-parent node */
#define DT_PARENT_INTC_INTERNAL(node_id) DT_PROP(node_id, interrupt_parent)
/* DT helper macro to get the node's interrupt grandparent node */
#define DT_GPARENT_INTC_INTERNAL(node_id) DT_PARENT_INTC_INTERNAL(DT_PARENT_INTC_INTERNAL(node_id))
/* DT helper macro to check if a node is an interrupt controller */
#define DT_IS_INTC_INTERNAL(node_id) DT_NODE_HAS_PROP(node_id, interrupt_controller)
/* DT helper macro to check if the node has a parent interrupt controller */
#define DT_HAS_PARENT_INTC_INTERNAL(node_id) \
/* node has `interrupt-parent`? */ \
IF_ENABLED(DT_NODE_HAS_PROP(node_id, interrupt_parent), \
/* `interrupt-parent` node is an interrupt controller? */ \
(IF_ENABLED(DT_IS_INTC_INTERNAL(DT_PARENT_INTC_INTERNAL(node_id)), \
/* `interrupt-parent` node has interrupt cell(s) ? 1 : 0 */ \
(COND_CODE_0(DT_NUM_IRQS(DT_PARENT_INTC_INTERNAL(node_id)), (0), \
(1))))))
/* DT helper macro to check if the node has a grandparent interrupt controller */
#define DT_HAS_GPARENT_INTC_INTERNAL(node_id) \
IF_ENABLED(DT_HAS_PARENT_INTC_INTERNAL(node_id), \
(DT_HAS_PARENT_INTC_INTERNAL(DT_PARENT_INTC_INTERNAL(node_id))))

/**
* DT helper macro to get the as-seen interrupt number in devicetree,
* or ARM GIC IRQ encoded output from `gen_defines.py`
*/
#define DT_IRQN_BY_IDX_INTERNAL(node_id, idx) DT_IRQ_BY_IDX(node_id, idx, irq)

/* DT helper macro to get the node's parent intc's (only) irq number */
#define DT_PARENT_INTC_IRQN_INTERNAL(node_id) DT_IRQ(DT_PARENT_INTC_INTERNAL(node_id), irq)
/* DT helper macro to get the node's grandparent intc's (only) irq number */
#define DT_GPARENT_INTC_IRQN_INTERNAL(node_id) DT_IRQ(DT_GPARENT_INTC_INTERNAL(node_id), irq)

/* DT helper macro to encode a node's IRQN to level 2 according to the multi-level scheme */
#define DT_IRQN_L2_INTERNAL(node_id, idx) \
(IRQ_TO_L2(DT_IRQN_BY_IDX_INTERNAL(node_id, idx)) | \
DT_PARENT_INTC_IRQN_INTERNAL(node_id))
/* DT helper macro to encode a node's IRQN to level 3 according to the multi-level scheme */
#define DT_IRQN_L3_INTERNAL(node_id, idx) \
(IRQ_TO_L3(DT_IRQN_BY_IDX_INTERNAL(node_id, idx)) | \
IRQ_TO_L2(DT_PARENT_INTC_IRQN_INTERNAL(node_id)) | \
DT_GPARENT_INTC_IRQN_INTERNAL(node_id))
/**
* DT helper macro to encode a node's interrupt number according to the Zephyr's multi-level scheme
* See doc/kernel/services/interrupts.rst for details
*/
#define DT_MULTI_LEVEL_IRQN_INTERNAL(node_id, idx) \
COND_CODE_1(DT_HAS_GPARENT_INTC_INTERNAL(node_id), (DT_IRQN_L3_INTERNAL(node_id, idx)), \
(COND_CODE_1(DT_HAS_PARENT_INTC_INTERNAL(node_id), \
(DT_IRQN_L2_INTERNAL(node_id, idx)), \
(DT_IRQN_BY_IDX_INTERNAL(node_id, idx)))))

/**
* INTERNAL_HIDDEN @endcond
*/

/**
* @brief Get the node's Zephyr interrupt number at index
* If @kconfig{CONFIG_MULTI_LEVEL_INTERRUPTS} is enabled, the interrupt number at index will be
* multi-level encoded
* @param node_id node identifier
* @param idx logical index into the interrupt specifier array
* @return the Zephyr interrupt number
*/
#define DT_IRQN_BY_IDX(node_id, idx) \
COND_CODE_1(IS_ENABLED(CONFIG_MULTI_LEVEL_INTERRUPTS), \
(DT_MULTI_LEVEL_IRQN_INTERNAL(node_id, idx)), \
(DT_IRQN_BY_IDX_INTERNAL(node_id, idx)))

/**
* @brief Get a node's (only) irq number
*
Expand All @@ -2419,7 +2491,7 @@
* @param node_id node identifier
* @return the interrupt number for the node's only interrupt
*/
#define DT_IRQN(node_id) DT_IRQ(node_id, irq)
#define DT_IRQN(node_id) DT_IRQN_BY_IDX(node_id, 0)

/**
* @}
Expand Down Expand Up @@ -3825,7 +3897,15 @@
* @param inst instance number
* @return the interrupt number for the node's only interrupt
*/
#define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq)
#define DT_INST_IRQN(inst) DT_IRQN(DT_DRV_INST(inst))

/**
* @brief Get a `DT_DRV_COMPAT`'s irq number at index
* @param inst instance number
* @param idx logical index into the interrupt specifier array
* @return the interrupt number for the node's idx-th interrupt
*/
#define DT_INST_IRQN_BY_IDX(inst, idx) DT_IRQN_BY_IDX(DT_DRV_INST(inst), idx)

/**
* @brief Get a `DT_DRV_COMPAT`'s bus node identifier
Expand Down
1 change: 1 addition & 0 deletions include/zephyr/irq_multilevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#ifndef _ASMLANGUAGE
#include <zephyr/sys/util_macro.h>
#include <zephyr/types.h>

#ifdef __cplusplus
extern "C" {
Expand Down
19 changes: 1 addition & 18 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ def write_interrupts(node):
# interrupts property: we have some hard-coded logic for interrupt
# mapping here.
#
# TODO: can we push map_arm_gic_irq_type() and
# encode_zephyr_multi_level_irq() out of Python and into C with
# TODO: can we push map_arm_gic_irq_type() out of Python and into C with
# macro magic in devicetree.h?

def map_arm_gic_irq_type(irq, irq_num):
Expand All @@ -453,21 +452,6 @@ def map_arm_gic_irq_type(irq, irq_num):
return irq_num + 16
err(f"Invalid interrupt type specified for {irq!r}")

def encode_zephyr_multi_level_irq(irq, irq_num):
# See doc/kernel/services/interrupts.rst for details
# on how this encoding works

irq_ctrl = irq.controller
# Look for interrupt controller parent until we have none
while irq_ctrl.interrupts:
irq_num = (irq_num + 1) << 8
if "irq" not in irq_ctrl.interrupts[0].data:
err(f"Expected binding for {irq_ctrl!r} to have 'irq' in "
"interrupt-cells")
irq_num |= irq_ctrl.interrupts[0].data["irq"]
irq_ctrl = irq_ctrl.interrupts[0].controller
return irq_num

idx_vals = []
name_vals = []
path_id = node.z_path_id
Expand All @@ -482,7 +466,6 @@ def encode_zephyr_multi_level_irq(irq, irq_num):
if cell_name == "irq":
if "arm,gic" in irq.controller.compats:
cell_value = map_arm_gic_irq_type(irq, cell_value)
cell_value = encode_zephyr_multi_level_irq(irq, cell_value)

idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1))
idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}"
Expand Down

0 comments on commit df2c068

Please sign in to comment.