From 475ff826d6c206840eadc6b5e3bbba4f77c4a769 Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Mon, 9 Sep 2024 13:40:24 +0800 Subject: [PATCH] drivers: intc: plic: fix IRQ on every hart regardless of mapping Allow IRQs to work on every hart regardless of the mapping of the contexts. Add a test to validate the hart-context mapping. Signed-off-by: Yong Cong Sin Signed-off-by: Yong Cong Sin --- drivers/interrupt_controller/intc_plic.c | 39 ++++++++++--------- .../interrupt_controller/intc_plic/Kconfig | 3 ++ .../intc_plic/alt_mapping.overlay | 29 ++++++++++++++ .../interrupt_controller/intc_plic/src/main.c | 27 +++++++++++++ .../intc_plic/testcase.yaml | 19 ++++++--- 5 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 tests/drivers/interrupt_controller/intc_plic/alt_mapping.overlay diff --git a/drivers/interrupt_controller/intc_plic.c b/drivers/interrupt_controller/intc_plic.c index c67041f45e7..7a235083cb2 100644 --- a/drivers/interrupt_controller/intc_plic.c +++ b/drivers/interrupt_controller/intc_plic.c @@ -64,9 +64,11 @@ #ifdef CONFIG_TEST_INTC_PLIC #define INTC_PLIC_STATIC +#define INTC_PLIC_STATIC_INLINE #else -#define INTC_PLIC_STATIC static inline -#endif +#define INTC_PLIC_STATIC static +#define INTC_PLIC_STATIC_INLINE static inline +#endif /* CONFIG_TEST_INTC_PLIC */ typedef void (*riscv_plic_irq_config_func_t)(void); struct plic_config { @@ -78,6 +80,7 @@ struct plic_config { uint32_t num_irqs; riscv_plic_irq_config_func_t irq_config_func; struct _isr_table_entry *isr_table; + const uint32_t *const hart_context; }; struct plic_stats { @@ -92,12 +95,12 @@ struct plic_data { static uint32_t save_irq; static const struct device *save_dev; -INTC_PLIC_STATIC uint32_t local_irq_to_reg_index(uint32_t local_irq) +INTC_PLIC_STATIC_INLINE uint32_t local_irq_to_reg_index(uint32_t local_irq) { return local_irq >> LOG2(PLIC_REG_SIZE); } -INTC_PLIC_STATIC uint32_t local_irq_to_reg_offset(uint32_t local_irq) +INTC_PLIC_STATIC_INLINE uint32_t local_irq_to_reg_offset(uint32_t local_irq) { return local_irq_to_reg_index(local_irq) * sizeof(uint32_t); } @@ -109,9 +112,11 @@ static inline uint32_t get_plic_enabled_size(const struct device *dev) return local_irq_to_reg_index(config->num_irqs) + 1; } -static inline uint32_t get_first_context(uint32_t hartid) +static ALWAYS_INLINE uint32_t get_hart_context(const struct device *dev, uint32_t hartid) { - return hartid == 0 ? 0 : (hartid * 2) - 1; + const struct plic_config *config = dev->config; + + return config->hart_context[hartid]; } static inline mem_addr_t get_context_en_addr(const struct device *dev, uint32_t cpu_num) @@ -120,17 +125,13 @@ static inline mem_addr_t get_context_en_addr(const struct device *dev, uint32_t uint32_t hartid; /* * We want to return the irq_en address for the context of given hart. - * If hartid is 0, we return the devices irq_en property, job done. If it is - * greater than zero, we assume that there are two context's associated with - * each hart: M mode enable, followed by S mode enable. We return the M mode - * enable address. */ #if CONFIG_SMP hartid = _kernel.cpus[cpu_num].arch.hartid; #else hartid = arch_proc_id(); #endif - return config->irq_en + get_first_context(hartid) * CONTEXT_ENABLE_SIZE; + return config->irq_en + get_hart_context(dev, hartid) * CONTEXT_ENABLE_SIZE; } static inline mem_addr_t get_claim_complete_addr(const struct device *dev) @@ -139,14 +140,9 @@ static inline mem_addr_t get_claim_complete_addr(const struct device *dev) /* * We want to return the claim complete addr for the hart's context. - * We are making a few assumptions here: - * 1. for hart 0, return the first context claim complete. - * 2. for any other hart, we assume they have two privileged mode contexts - * which are contiguous, where the m mode context is first. - * We return the m mode context. */ - return config->reg + get_first_context(arch_proc_id()) * CONTEXT_SIZE + + return config->reg + get_hart_context(dev, arch_proc_id()) * CONTEXT_SIZE + CONTEXT_CLAIM; } @@ -162,7 +158,7 @@ static inline mem_addr_t get_threshold_priority_addr(const struct device *dev, u hartid = arch_proc_id(); #endif - return config->reg + (get_first_context(hartid) * CONTEXT_SIZE); + return config->reg + (get_hart_context(dev, hartid) * CONTEXT_SIZE); } /** @@ -571,8 +567,14 @@ SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands", irq_enable(DT_INST_IRQN(n)); \ } +#define HART_CONTEXTS(i, n) IF_ENABLED(IS_EQ(DT_INST_IRQN_BY_IDX(n, i), DT_INST_IRQN(n)), (i,)) +#define PLIC_HART_CONTEXT_DECLARE(n) \ + INTC_PLIC_STATIC const uint32_t plic_hart_contexts_##n[DT_CHILD_NUM(DT_PATH(cpus))] = { \ + LISTIFY(DT_INST_NUM_IRQS(n), HART_CONTEXTS, (), n)} + #define PLIC_INTC_CONFIG_INIT(n) \ PLIC_INTC_IRQ_FUNC_DECLARE(n); \ + PLIC_HART_CONTEXT_DECLARE(n); \ static const struct plic_config plic_config_##n = { \ .prio = PLIC_BASE_ADDR(n), \ .irq_en = PLIC_BASE_ADDR(n) + CONTEXT_ENABLE_BASE, \ @@ -583,6 +585,7 @@ SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands", .num_irqs = DT_INST_PROP(n, riscv_ndev), \ .irq_config_func = plic_irq_config_func_##n, \ .isr_table = &_sw_isr_table[INTC_INST_ISR_TBL_OFFSET(n)], \ + .hart_context = plic_hart_contexts_##n, \ }; \ PLIC_INTC_IRQ_FUNC_DEFINE(n) diff --git a/tests/drivers/interrupt_controller/intc_plic/Kconfig b/tests/drivers/interrupt_controller/intc_plic/Kconfig index 53ccc573482..476593aa360 100644 --- a/tests/drivers/interrupt_controller/intc_plic/Kconfig +++ b/tests/drivers/interrupt_controller/intc_plic/Kconfig @@ -7,4 +7,7 @@ config TEST_INTC_PLIC help Declare some intc_plic.c functions in the global scope for verification. +config TEST_INTC_PLIC_ALT_MAPPING + bool "Test alternate hartid - context mapping" + source "Kconfig" diff --git a/tests/drivers/interrupt_controller/intc_plic/alt_mapping.overlay b/tests/drivers/interrupt_controller/intc_plic/alt_mapping.overlay new file mode 100644 index 00000000000..a25223d03d1 --- /dev/null +++ b/tests/drivers/interrupt_controller/intc_plic/alt_mapping.overlay @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Meta Platforms + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/{ + soc { + plic: interrupt-controller@c000000 { + riscv,max-priority = <7>; + riscv,ndev = < 1024 >; + reg = <0x0c000000 0x04000000>; + interrupts-extended = < + &hlic0 0x0b + &hlic1 0x0b &hlic1 0x09 + &hlic2 0x0b &hlic2 0x09 + &hlic3 0x0b &hlic3 0x09 + &hlic4 0x0b &hlic4 0x09 + &hlic5 0x0b &hlic5 0x09 + &hlic6 0x0b &hlic6 0x09 + &hlic7 0x0b &hlic7 0x09 + >; + interrupt-controller; + compatible = "sifive,plic-1.0.0"; + #address-cells = < 0x00 >; + #interrupt-cells = < 0x02 >; + }; + }; +}; diff --git a/tests/drivers/interrupt_controller/intc_plic/src/main.c b/tests/drivers/interrupt_controller/intc_plic/src/main.c index 631e0e8915a..de22642e787 100644 --- a/tests/drivers/interrupt_controller/intc_plic/src/main.c +++ b/tests/drivers/interrupt_controller/intc_plic/src/main.c @@ -29,3 +29,30 @@ ZTEST(intc_plic, test_local_irq_to_reg_offset) zassert_equal(4, local_irq_to_reg_offset(0x3f)); zassert_equal(8, local_irq_to_reg_offset(0x40)); } + +ZTEST(intc_plic, test_hart_context_mapping) +{ + extern const uint32_t plic_hart_contexts_0[]; + + if (!IS_ENABLED(CONFIG_TEST_INTC_PLIC_ALT_MAPPING)) { + /* Based on the default qemu_riscv64 devicetree */ + zassert_equal(plic_hart_contexts_0[0], 0); + zassert_equal(plic_hart_contexts_0[1], 2); + zassert_equal(plic_hart_contexts_0[2], 4); + zassert_equal(plic_hart_contexts_0[3], 6); + zassert_equal(plic_hart_contexts_0[4], 8); + zassert_equal(plic_hart_contexts_0[5], 10); + zassert_equal(plic_hart_contexts_0[6], 12); + zassert_equal(plic_hart_contexts_0[7], 14); + } else { + /* Based on the definition in the `alt_mapping.overlay` */ + zassert_equal(plic_hart_contexts_0[0], 0); + zassert_equal(plic_hart_contexts_0[1], 1); + zassert_equal(plic_hart_contexts_0[2], 3); + zassert_equal(plic_hart_contexts_0[3], 5); + zassert_equal(plic_hart_contexts_0[4], 7); + zassert_equal(plic_hart_contexts_0[5], 9); + zassert_equal(plic_hart_contexts_0[6], 11); + zassert_equal(plic_hart_contexts_0[7], 13); + } +} diff --git a/tests/drivers/interrupt_controller/intc_plic/testcase.yaml b/tests/drivers/interrupt_controller/intc_plic/testcase.yaml index 11c1f74fa39..a798cd76f8f 100644 --- a/tests/drivers/interrupt_controller/intc_plic/testcase.yaml +++ b/tests/drivers/interrupt_controller/intc_plic/testcase.yaml @@ -1,7 +1,14 @@ +common: + platform_allow: qemu_riscv64 + tags: + - drivers + - interrupt + - plic + tests: - drivers.interrupt_controller.intc_plic: - tags: - - drivers - - interrupt - - plic - platform_allow: qemu_riscv64 + drivers.interrupt_controller.intc_plic: {} + drivers.interrupt_controller.intc_plic.alt_mapping: + extra_args: + DTC_OVERLAY_FILE="./alt_mapping.overlay" + extra_configs: + - CONFIG_TEST_INTC_PLIC_ALT_MAPPING=y