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:
parent
8b066bcfe7
commit
475ff826d6
5 changed files with 93 additions and 24 deletions
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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 >;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue