From 637c1be2f789c81c8effe3ec85afdb33c6f2df65 Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Mon, 2 Oct 2023 12:42:24 +0800 Subject: [PATCH] devicetree: encode multi-level interrupt number in C devicetree magic 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 --- drivers/gpio/gpio_sifive.c | 2 +- include/zephyr/devicetree.h | 68 +++++++++++++++++++++++++++++++++++-- scripts/dts/gen_defines.py | 19 +---------- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/gpio_sifive.c b/drivers/gpio/gpio_sifive.c index 440ad3f78ab79fb..2d6b4b3348fcfa2 100644 --- a/drivers/gpio/gpio_sifive.c +++ b/drivers/gpio/gpio_sifive.c @@ -376,7 +376,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_IRQN_BY_IDX(DT_DRV_INST(0), n), \ DT_INST_IRQ_BY_IDX(0, n, priority), \ gpio_sifive_irq_handler, \ DEVICE_DT_INST_GET(0), \ diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index c249ad371e2e4ae..80e3d4e5bc6c9df 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -2409,6 +2409,70 @@ */ #define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell) +/* DT helper macro to get interrupt-parent node */ +#define Z_DT_PARENT_INTC(node_id) DT_PROP(node_id, interrupt_parent) +/* DT helper macro to get the node's interrupt grandparent node */ +#define Z_DT_GPARENT_INTC(node_id) Z_DT_PARENT_INTC(Z_DT_PARENT_INTC(node_id)) +/* DT helper macro to check if a node is an interrupt controller */ +#define Z_DT_IS_INTC(node_id) DT_NODE_HAS_PROP(node_id, interrupt_controller) +/* DT helper macro to check if the node has a parent interrupt controller */ +#define Z_DT_HAS_PARENT_INTC(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(Z_DT_IS_INTC(Z_DT_PARENT_INTC(node_id)), \ + /* `interrupt-parent` node has interrupt cell(s) ? 1 : 0 */ \ + (COND_CODE_0(DT_NUM_IRQS(Z_DT_PARENT_INTC(node_id)), (0), (1)))))) +/* DT helper macro to check if the node has a grandparent interrupt controller */ +#define Z_DT_HAS_GPARENT_INTC(node_id) \ + IF_ENABLED(Z_DT_HAS_PARENT_INTC(node_id), (Z_DT_HAS_PARENT_INTC(Z_DT_PARENT_INTC(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 Z_DT_IRQN_BY_IDX(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 Z_DT_PARENT_INTC_IRQN(node_id) DT_IRQ(Z_DT_PARENT_INTC(node_id), irq) +/* DT helper macro to get the node's grandparent intc's (only) irq number */ +#define Z_DT_GPARENT_INTC_IRQN(node_id) DT_IRQ(Z_DT_GPARENT_INTC(node_id), irq) + +/* Helper macro to convert interrupt number to level 2 */ +/* TODO: should this and the following be placed in `irq.h`, and include that? */ +#define Z_IRQN_TO_L2(irq) ((irq + 1) << CONFIG_1ST_LEVEL_INTERRUPT_BITS) +/* Helper macro to convert interrupt number to level 3 */ +#define Z_IRQN_TO_L3(irq) \ + ((irq + 1) << (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS)) + +/* DT helper macro to encode a node's IRQN to level 2 according to the multi-level scheme */ +#define Z_DT_IRQN_L2(node_id, idx) \ + (Z_IRQN_TO_L2(Z_DT_IRQN_BY_IDX(node_id, idx)) | Z_DT_PARENT_INTC_IRQN(node_id)) +/* DT helper macro to encode a node's IRQN to level 3 according to the multi-level scheme */ +#define Z_DT_IRQN_L3(node_id, idx) \ + (Z_IRQN_TO_L3(Z_DT_IRQN_BY_IDX(node_id, idx)) | \ + Z_IRQN_TO_L2(Z_DT_PARENT_INTC_IRQN(node_id)) | Z_DT_GPARENT_INTC_IRQN(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 Z_DT_MULTI_LEVEL_IRQN(node_id, idx) \ + COND_CODE_1(Z_DT_HAS_GPARENT_INTC(node_id), (Z_DT_IRQN_L3(node_id, idx)), \ + (COND_CODE_1(Z_DT_HAS_PARENT_INTC(node_id), (Z_DT_IRQN_L2(node_id, idx)), \ + (Z_DT_IRQN_BY_IDX(node_id, idx))))) + +/** + * @brief Get the node's Zephyr interrupt number at index + * If `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), \ + (Z_DT_MULTI_LEVEL_IRQN(node_id, idx)), (Z_DT_IRQN_BY_IDX(node_id, idx))) + /** * @brief Get a node's (only) irq number * @@ -2419,7 +2483,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) /** * @} @@ -3825,7 +3889,7 @@ * @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 bus node identifier diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index d21fdc7564640b1..7721b5b4fe88197 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -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): @@ -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 @@ -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}"