1 中断控制器初始化过程
arch/riscv/kernel/irq.c
void __init init_IRQ(void)
20 {
21 irqchip_init();
22 if (!handle_arch_irq)
23 panic("No interrupt controller found.");
24 }
调用irqchip_init函数初始化中断
/drivers/irqchip/irqchip.c
29 void __init irqchip_init(void)
30 {
31 of_irq_init(__irqchip_of_table);
32 acpi_probe_device_table(irqchip);
33 }
1.1 __irqchip_of_table匹配
1 设备树源文件
如sifive的设备数源码如下所示:
arch/riscv/boot/dts/sifive/fu540-c000.dtsi
plic0: interrupt-controller@c000000 {
143 #interrupt-cells = <1>;
144 compatible = "sifive,plic-1.0.0";
145 reg = <0x0 0xc000000 0x0 0x4000000>;
146 riscv,ndev = <53>;
147 interrupt-controller;
148 interrupts-extended = <
149 &cpu0_intc 0xffffffff
150 &cpu1_intc 0xffffffff &cpu1_intc 9
151 &cpu2_intc 0xffffffff &cpu2_intc 9
152 &cpu3_intc 0xffffffff &cpu3_intc 9
153 &cpu4_intc 0xffffffff &cpu4_intc 9>;
2 中断控制匹配表
我们把__irqchip_of_table称为中断控制器匹配表
drivers/irqchip/irq-sifive-plic.c
395 IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
396 IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
把IRQCHIP_DECLARE展开后
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
===>OF_DECLARE_2(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init)
===>_OF_DECLARE(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init,of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section("__" #table "_of_table") \
__aligned(__alignof__(struct of_device_id)) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
===>_OF_DECLARE(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init,of_init_fn_2)
===>static const struct of_device_id __of_table_sifive_plic
__used __section("__irqchip_of_table")
__aligned(__alignof__(struct of_device_id))
= { .compatible = "sifive,plic-1.0.0",
.data = plic_init }
链接器的链接脚本使用__irqchip_of_table 存放节"__irqchip_of_table "的起始地址,也就是中断控制器匹配表的起始地址
linux/arch/riscv/kernel/vmlinux.lds.S
78 INIT_DATA_SECTION(16)
include/asm-generic/vmlinux.lds.h
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { \
INIT_DATA \
INIT_SETUP(initsetup_align) \
INIT_CALLS \
CON_INITCALL \
INIT_RAM_FS \
}
#define INIT_DATA \
...
IRQCHIP_OF_MATCH_TABLE() \
...
#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
#define OF_TABLE(cfg, name) __OF_TABLE(IS_ENABLED(cfg), name)
#define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name)
#define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name)
#define _OF_TABLE_0(name)
#define _OF_TABLE_1(name) \
. = ALIGN(8); \
__##name##_of_table = .; \
KEEP(*(__##name##_of_table)) \
KEEP(*(__##name##_of_table_end))
===>#define _OF_TABLE_1(irqchip) \
. = ALIGN(8); \
__irqchip_of_table = .; \
KEEP(*(__irqchip_of_table)) \
KEEP(*(__irqchip_of_table_end))
3 初始化
初始化函数of_irq_init
472 /**
473 * of_irq_init - Scan and init matching interrupt controllers in DT
474 * @matches: 0 terminated array of nodes to match and init function to call
475 *
476 * This function scans the device tree for matching interrupt controller nodes,
477 * and calls their initialization functions in order with parents first.
478 */
479 void __init of_irq_init(const struct of_device_id *matches)
480 {
481 const struct of_device_id *match;
482 struct device_node *np, *parent = NULL;
483 struct of_intc_desc *desc, *temp_desc;
484 struct list_head intc_desc_list, intc_parent_list;
485
486 INIT_LIST_HEAD(&intc_desc_list);
487 INIT_LIST_HEAD(&intc_parent_list);
488
489 for_each_matching_node_and_match(np, matches, &match) {
490 if (!of_property_read_bool(np, "interrupt-controller") ||
491 !of_device_is_available(np))
492 continue;
493
494 if (WARN(!match->data, "of_irq_init: no init function for %s\n",
495 match->compatible))
496 continue;
497
498 /*
499 * Here, we allocate and populate an of_intc_desc with the node
500 * pointer, interrupt-parent device_node etc.
501 */
502 desc = kzalloc(sizeof(*desc), GFP_KERNEL);
503 if (!desc) {
504 of_node_put(np);
505 goto err;
506 }
507
508 desc->irq_init_cb = match->data;
509 desc->dev = of_node_get(np);
510 desc->interrupt_parent = of_irq_find_parent(np);
511 if (desc->interrupt_parent == np)
512 desc->interrupt_parent = NULL;
513 list_add_tail(&desc->list, &intc_desc_list);
514 }
515
516 /*
517 * The root irq controller is the one without an interrupt-parent.
518 * That one goes first, followed by the controllers that reference it,
519 * followed by the ones that reference the 2nd level controllers, etc.
520 */
521 while (!list_empty(&intc_desc_list)) {
522 /*
523 * Process all controllers with the current 'parent'.
524 * First pass will be looking for NULL as the parent.
525 * The assumption is that NULL parent means a root controller.
526 */
527 list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
528 int ret;
529
530 if (desc->interrupt_parent != parent)
531 continue;
532
533 list_del(&desc->list);
534
535 of_node_set_flag(desc->dev, OF_POPULATED);
536
537 pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
538 desc->dev,
539 desc->dev, desc->interrupt_parent);
540 ret = desc->irq_init_cb(desc->dev,
541 desc->interrupt_parent);
542 if (ret) {
543 of_node_clear_flag(desc->dev, OF_POPULATED);
544 kfree(desc);
545 continue;
546 }
547
548 /*
549 * This one is now set up; add it to the parent list so
550 * its children can get processed in a subsequent pass.
551 */
552 list_add_tail(&desc->list, &intc_parent_list);
553 }
554
555 /* Get the next pending parent that might have children */
556 desc = list_first_entry_or_null(&intc_parent_list,
557 typeof(*desc), list);
558 if (!desc) {
559 pr_err("of_irq_init: children remain, but no parents\n");
560 break;
561 }
562 list_del(&desc->list);
563 parent = desc->dev;
564 kfree(desc);
565 }
566
567 list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
568 list_del(&desc->list);
569 kfree(desc);
570 }
571 err:
572 list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
573 list_del(&desc->list);
574 of_node_put(desc->dev);
575 kfree(desc);
576 }
577 }
设备树文件arch/riscv/boot/dts/sifive/fu540-c000.dtsi里面的中断控制器属性"compatible"是 "sifive,plic-1.0.0"和中断控制器匹配表中的(sifive_plic, "sifive,plic-1.0.0", plic_init);(riscv_plic0, "riscv,plic0", plic_init); 匹配
初始化函数plic_init
276 static int __init plic_init(struct device_node *node,
277 struct device_node *parent)
278 {
279 int error = 0, nr_contexts, nr_handlers = 0, i;
280 u32 nr_irqs;
281 struct plic_priv *priv;
282 struct plic_handler *handler;
283
284 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
285 if (!priv)
286 return -ENOMEM;
287
288 priv->regs = of_iomap(node, 0);
289 if (WARN_ON(!priv->regs)) {
290 error = -EIO;
291 goto out_free_priv;
292 }
293
294 error = -EINVAL;
295 of_property_read_u32(node, "riscv,ndev", &nr_irqs);
296 if (WARN_ON(!nr_irqs))
297 goto out_iounmap;
298
299 nr_contexts = of_irq_count(node);
300 if (WARN_ON(!nr_contexts))
301 goto out_iounmap;
302
303 error = -ENOMEM;
304 priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
305 &plic_irqdomain_ops, priv);
306 if (WARN_ON(!priv->irqdomain))
307 goto out_iounmap;
308
309 for (i = 0; i < nr_contexts; i++) {
310 struct of_phandle_args parent;
311 irq_hw_number_t hwirq;
312 int cpu, hartid;
313
314 if (of_irq_parse_one(node, i, &parent)) {
315 pr_err("failed to parse parent for context %d.\n", i);
316 continue;
317 }
318
319 /*
320 * Skip contexts other than external interrupts for our
321 * privilege level.
322 */
323 if (parent.args[0] != RV_IRQ_EXT)
324 continue;
325
326 hartid = riscv_of_parent_hartid(parent.np);
327 if (hartid < 0) {
328 pr_warn("failed to parse hart ID for context %d.\n", i);
329 continue;
330 }
331
332 cpu = riscv_hartid_to_cpuid(hartid);
333 if (cpu < 0) {
334 pr_warn("Invalid cpuid for context %d\n", i);
335 continue;
336 }
337
338 /* Find parent domain and register chained handler */
339 if (!plic_parent_irq && irq_find_host(parent.np)) {
340 plic_parent_irq = irq_of_parse_and_map(node, i);
341 if (plic_parent_irq)
342 irq_set_chained_handler(plic_parent_irq,
343 plic_handle_irq);
344 }
345
346 /*
347 * When running in M-mode we need to ignore the S-mode handler.
348 * Here we assume it always comes later, but that might be a
349 * little fragile.
350 */
351 handler = per_cpu_ptr(&plic_handlers, cpu);
352 if (handler->present) {
353 pr_warn("handler already present for context %d.\n", i);
354 plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
355 goto done;
356 }
357
358 cpumask_set_cpu(cpu, &priv->lmask);
359 handler->present = true;
360 handler->hart_base =
361 priv->regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
362 raw_spin_lock_init(&handler->enable_lock);
363 handler->enable_base =
364 priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
365 handler->priv = priv;
366 done:
367 for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
368 plic_toggle(handler, hwirq, 0);
369 nr_handlers++;
370 }
371
372 /*
373 * We can have multiple PLIC instances so setup cpuhp state only
374 * when context handler for current/boot CPU is present.
375 */
376 handler = this_cpu_ptr(&plic_handlers);
377 if (handler->present && !plic_cpuhp_setup_done) {
378 cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
379 "irqchip/sifive/plic:starting",
380 plic_starting_cpu, plic_dying_cpu);
381 plic_cpuhp_setup_done = true;
382 }
383
384 pr_info("%pOFP: mapped %d interrupts with %d handlers for"
385 " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
386 return 0;
387
388 out_iounmap:
389 iounmap(priv->regs);
390 out_free_priv:
391 kfree(priv);
392 return error;
393 }
未完待更新