Skip to content

Commit b7bf6d0

Browse files
committed
drivers: riscv: imsic: add SMP initialization support
Add automatic IMSIC initialization for secondary CPUs in SMP systems, following the same pattern as the CLINT timer initialization. Changes: - Fixed IMSIC IRQ registration to avoid duplicate handler registration: * Only hart 0 (instance 0) registers the global MEXT IRQ handler via IRQ_CONNECT() * Secondary hart instances only enable MEXT locally without registering duplicate handlers - Added z_riscv_imsic_secondary_init() function: * Called automatically from arch_secondary_cpu_init() on each secondary CPU * Configures EIDELIVERY to enable interrupt delivery in MMSI mode * Sets EITHRESHOLD to 0 to allow all interrupt priorities * Enables MEXT interrupt on the current CPU * Follows the CLINT smp_timer_init() pattern for per-CPU initialization - Integrated with arch/riscv/core/smp.c: * arch_secondary_cpu_init() now calls z_riscv_imsic_secondary_init() when CONFIG_RISCV_IMSIC && CONFIG_SMP are enabled * Ensures IMSIC is properly initialized before secondary CPUs become active This allows IMSIC to work correctly in SMP configurations without requiring manual initialization in application code. Each CPU's IMSIC file is configured automatically during SMP boot. Note: IMSIC CSRs are accessed via ISELECT/IREG and are local to each CPU. When z_riscv_imsic_secondary_init() executes on CPU N, it configures that CPU's IMSIC file. Signed-off-by: Afonso Oliveira <afonsoo@synopsys.com>
1 parent cb8f343 commit b7bf6d0

File tree

2 files changed

+95
-10
lines changed

2 files changed

+95
-10
lines changed

arch/riscv/core/smp.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ void arch_secondary_cpu_init(int hartid)
7979
/* Enable on secondary cores so that they can respond to PLIC */
8080
irq_enable(RISCV_IRQ_MEXT);
8181
#endif /* CONFIG_PLIC_IRQ_AFFINITY */
82+
#if defined(CONFIG_RISCV_IMSIC) && defined(CONFIG_SMP)
83+
/* Initialize IMSIC on secondary CPU */
84+
extern void z_riscv_imsic_secondary_init(void);
85+
z_riscv_imsic_secondary_init();
86+
#endif /* CONFIG_RISCV_IMSIC && CONFIG_SMP */
8287
#ifdef CONFIG_SOC_PER_CORE_INIT_HOOK
8388
soc_per_core_init_hook();
8489
#endif /* CONFIG_SOC_PER_CORE_INIT_HOOK */

drivers/interrupt_controller/intc_riscv_imsic.c

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,39 @@ uint32_t riscv_imsic_get_pending(const struct device *dev)
125125
return p0 | (p1 ? 0x80000000U : 0U); /* signal >32 pending via MSB */
126126
}
127127

128-
#define IMSIC_IRQ_CONFIG_FUNC_DECLARE(inst) \
129-
static void imsic_irq_config_func_##inst(void)
128+
/* Separate IRQ registration for hart 0 vs other harts to avoid duplicate registration */
129+
static void imsic_irq_config_func_0(void)
130+
{
131+
/* Only hart 0 (instance 0) registers the global MEXT IRQ handler */
132+
IRQ_CONNECT(11, 0, imsic_mext_isr, DEVICE_DT_INST_GET(0), 0);
133+
irq_enable(11);
134+
LOG_INF("Registered MEXT IRQ handler from hart 0 IMSIC instance");
135+
}
130136

131-
#define IMSIC_IRQ_CONFIG_FUNC_DEFINE(inst) \
137+
#define IMSIC_IRQ_CONFIG_FUNC_DEFINE_SECONDARY(inst) \
132138
static void imsic_irq_config_func_##inst(void) \
133139
{ \
134-
IRQ_CONNECT(11, 0, imsic_mext_isr, DEVICE_DT_INST_GET(inst), 0); \
140+
/* Secondary harts just enable MEXT locally, no IRQ_CONNECT */ \
135141
irq_enable(11); \
142+
LOG_DBG("Hart %u IMSIC: enabled MEXT locally (no IRQ_CONNECT)", \
143+
DT_INST_PROP(inst, riscv_hart_id)); \
136144
}
137145

146+
/* Generate secondary IRQ config functions for instances 1+ */
147+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1
148+
IMSIC_IRQ_CONFIG_FUNC_DEFINE_SECONDARY(1)
149+
#endif
150+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 2
151+
IMSIC_IRQ_CONFIG_FUNC_DEFINE_SECONDARY(2)
152+
#endif
153+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 3
154+
IMSIC_IRQ_CONFIG_FUNC_DEFINE_SECONDARY(3)
155+
#endif
156+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 4
157+
IMSIC_IRQ_CONFIG_FUNC_DEFINE_SECONDARY(4)
158+
#endif
159+
138160
#define IMSIC_INIT(inst) \
139-
IMSIC_IRQ_CONFIG_FUNC_DECLARE(inst); \
140161
static struct imsic_data imsic_data_##inst; \
141162
static const struct imsic_cfg imsic_cfg_##inst = { \
142163
.reg_base = DT_INST_REG_ADDR(inst), \
@@ -146,17 +167,30 @@ uint32_t riscv_imsic_get_pending(const struct device *dev)
146167
}; \
147168
DEVICE_DT_INST_DEFINE(inst, imsic_init, NULL, \
148169
&imsic_data_##inst, &imsic_cfg_##inst, \
149-
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL); \
150-
IMSIC_IRQ_CONFIG_FUNC_DEFINE(inst)
170+
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
151171

152172
DT_INST_FOREACH_STATUS_OKAY(IMSIC_INIT)
153173

154174
/* Call IRQ config functions at POST_KERNEL level to register MEXT handler */
155175
static int imsic_irq_init(void)
156176
{
157-
#define IMSIC_IRQ_INIT_CALL(inst) imsic_irq_config_func_##inst();
158-
DT_INST_FOREACH_STATUS_OKAY(IMSIC_IRQ_INIT_CALL)
159-
#undef IMSIC_IRQ_INIT_CALL
177+
/* Call instance 0 (always present) */
178+
imsic_irq_config_func_0();
179+
180+
/* Call secondary instance functions if they exist */
181+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1
182+
imsic_irq_config_func_1();
183+
#endif
184+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 2
185+
imsic_irq_config_func_2();
186+
#endif
187+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 3
188+
imsic_irq_config_func_3();
189+
#endif
190+
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 4
191+
imsic_irq_config_func_4();
192+
#endif
193+
160194
return 0;
161195
}
162196

@@ -208,3 +242,49 @@ void imsic_mext_isr(const void *arg)
208242
riscv_imsic_complete(eiid);
209243
}
210244
}
245+
246+
#ifdef CONFIG_SMP
247+
/**
248+
* @brief Initialize IMSIC on secondary CPUs
249+
*
250+
* This function is called on each secondary CPU during SMP boot to initialize
251+
* the IMSIC interrupt controller on that CPU. It configures the EIDELIVERY
252+
* and EITHRESHOLD CSRs to enable interrupt delivery.
253+
*
254+
* This follows the same pattern as smp_timer_init() for the CLINT timer.
255+
*
256+
* Note: IMSIC CSRs (accessed via ISELECT/IREG) are local to each CPU.
257+
* When this function executes on CPU N, it configures that CPU's IMSIC file.
258+
*/
259+
void z_riscv_imsic_secondary_init(void)
260+
{
261+
unsigned int cpu_id = arch_proc_id();
262+
263+
LOG_INF("IMSIC secondary init on CPU %u", cpu_id);
264+
265+
/* Enable interrupt delivery in MMSI mode */
266+
/* EIDELIVERY[0] = 1: Enable delivery */
267+
/* EIDELIVERY[30:29] = 10: MMSI mode (0x40000000) */
268+
uint32_t eidelivery_value = EIDELIVERY_ENABLE | EIDELIVERY_MODE_MMSI;
269+
write_imsic_csr(ICSR_EIDELIVERY, eidelivery_value);
270+
271+
/* Set EITHRESHOLD to 0 to allow all interrupt priorities */
272+
write_imsic_csr(ICSR_EITHRESH, 0);
273+
274+
/* Enable MEXT interrupt on this CPU */
275+
irq_enable(RISCV_IRQ_MEXT);
276+
277+
/* Read back to verify initialization */
278+
uint32_t eidelivery_readback = read_imsic_csr(ICSR_EIDELIVERY);
279+
uint32_t eithresh_readback = read_imsic_csr(ICSR_EITHRESH);
280+
281+
LOG_INF("CPU %u IMSIC initialized: EIDELIVERY=0x%08x EITHRESH=0x%08x",
282+
cpu_id, eidelivery_readback, eithresh_readback);
283+
284+
/* Sanity check: verify EIDELIVERY enable bit is set */
285+
if (!(eidelivery_readback & EIDELIVERY_ENABLE)) {
286+
LOG_ERR("CPU %u IMSIC EIDELIVERY enable bit not set! Got 0x%08x",
287+
cpu_id, eidelivery_readback);
288+
}
289+
}
290+
#endif /* CONFIG_SMP */

0 commit comments

Comments
 (0)