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 <ycsin@meta.com>
Signed-off-by: Yong Cong Sin <yongcong.sin@gmail.com>
This commit is contained in:
Yong Cong Sin 2024-09-09 13:40:24 +08:00 committed by Alberto Escolar
parent 8b066bcfe7
commit 475ff826d6
5 changed files with 93 additions and 24 deletions

View file

@ -64,9 +64,11 @@
#ifdef CONFIG_TEST_INTC_PLIC #ifdef CONFIG_TEST_INTC_PLIC
#define INTC_PLIC_STATIC #define INTC_PLIC_STATIC
#define INTC_PLIC_STATIC_INLINE
#else #else
#define INTC_PLIC_STATIC static inline #define INTC_PLIC_STATIC static
#endif #define INTC_PLIC_STATIC_INLINE static inline
#endif /* CONFIG_TEST_INTC_PLIC */
typedef void (*riscv_plic_irq_config_func_t)(void); typedef void (*riscv_plic_irq_config_func_t)(void);
struct plic_config { struct plic_config {
@ -78,6 +80,7 @@ struct plic_config {
uint32_t num_irqs; uint32_t num_irqs;
riscv_plic_irq_config_func_t irq_config_func; riscv_plic_irq_config_func_t irq_config_func;
struct _isr_table_entry *isr_table; struct _isr_table_entry *isr_table;
const uint32_t *const hart_context;
}; };
struct plic_stats { struct plic_stats {
@ -92,12 +95,12 @@ struct plic_data {
static uint32_t save_irq; static uint32_t save_irq;
static const struct device *save_dev; 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); 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); 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; 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) 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; uint32_t hartid;
/* /*
* We want to return the irq_en address for the context of given hart. * 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 #if CONFIG_SMP
hartid = _kernel.cpus[cpu_num].arch.hartid; hartid = _kernel.cpus[cpu_num].arch.hartid;
#else #else
hartid = arch_proc_id(); hartid = arch_proc_id();
#endif #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) 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 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; CONTEXT_CLAIM;
} }
@ -162,7 +158,7 @@ static inline mem_addr_t get_threshold_priority_addr(const struct device *dev, u
hartid = arch_proc_id(); hartid = arch_proc_id();
#endif #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)); \ 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) \ #define PLIC_INTC_CONFIG_INIT(n) \
PLIC_INTC_IRQ_FUNC_DECLARE(n); \ PLIC_INTC_IRQ_FUNC_DECLARE(n); \
PLIC_HART_CONTEXT_DECLARE(n); \
static const struct plic_config plic_config_##n = { \ static const struct plic_config plic_config_##n = { \
.prio = PLIC_BASE_ADDR(n), \ .prio = PLIC_BASE_ADDR(n), \
.irq_en = PLIC_BASE_ADDR(n) + CONTEXT_ENABLE_BASE, \ .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), \ .num_irqs = DT_INST_PROP(n, riscv_ndev), \
.irq_config_func = plic_irq_config_func_##n, \ .irq_config_func = plic_irq_config_func_##n, \
.isr_table = &_sw_isr_table[INTC_INST_ISR_TBL_OFFSET(n)], \ .isr_table = &_sw_isr_table[INTC_INST_ISR_TBL_OFFSET(n)], \
.hart_context = plic_hart_contexts_##n, \
}; \ }; \
PLIC_INTC_IRQ_FUNC_DEFINE(n) PLIC_INTC_IRQ_FUNC_DEFINE(n)

View file

@ -7,4 +7,7 @@ config TEST_INTC_PLIC
help help
Declare some intc_plic.c functions in the global scope for verification. 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" source "Kconfig"

View file

@ -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 >;
};
};
};

View file

@ -29,3 +29,30 @@ ZTEST(intc_plic, test_local_irq_to_reg_offset)
zassert_equal(4, local_irq_to_reg_offset(0x3f)); zassert_equal(4, local_irq_to_reg_offset(0x3f));
zassert_equal(8, local_irq_to_reg_offset(0x40)); 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);
}
}

View file

@ -1,7 +1,14 @@
common:
platform_allow: qemu_riscv64
tags:
- drivers
- interrupt
- plic
tests: tests:
drivers.interrupt_controller.intc_plic: drivers.interrupt_controller.intc_plic: {}
tags: drivers.interrupt_controller.intc_plic.alt_mapping:
- drivers extra_args:
- interrupt DTC_OVERLAY_FILE="./alt_mapping.overlay"
- plic extra_configs:
platform_allow: qemu_riscv64 - CONFIG_TEST_INTC_PLIC_ALT_MAPPING=y