1/*
2 * Copyright 2020, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <assert.h>
14#include <stdbool.h>
15
16#include <utils/util.h>
17
18#include "../../../irqchip.h"
19
20/*
21 * Interrupt parser for the RISC-V PLIC.
22 *
23 * RISC-V PLIC interrupts specified as single-cell and each device node
24 * may specify one more more interrupts that it uses.
25 *
26 * Most properties in the `interrupt-controller` is useless to us - the
27 * kernel takes care of that.
28 *
29 * We just want to confirm that the `interrupt-controller` is specified as
30 * we expect, and then return the interrupts specified in the device node.
31 */
32
33static int parse_riscv_plic_interrupts(char *dtb_blob, int node_offset, int intr_controller_phandle,
34                                       irq_walk_cb_fn_t callback, void *token)
35{
36    bool is_extended = false;
37    int prop_len = 0;
38    const void *interrupts_prop = get_interrupts_prop(dtb_blob, node_offset, &is_extended, &prop_len);
39    assert(interrupts_prop != NULL);
40    int total_cells = prop_len / sizeof(uint32_t);
41
42    if (is_extended) {
43        ZF_LOGE("This parser doesn't understand extended interrupts");
44        return ENODEV;
45    }
46
47    /* Make sure that the interrupt we are parsing is 1-cell format */
48    int intr_controller_offset = fdt_node_offset_by_phandle(dtb_blob, intr_controller_phandle);
49    if (intr_controller_offset < 0) {
50        ZF_LOGE("Can't find the interrupt controller's node offset");
51        return intr_controller_offset;
52    }
53    const void *interrupt_cells_prop = fdt_getprop(dtb_blob, intr_controller_offset, "#interrupt-cells", NULL);
54    if (!interrupt_cells_prop) {
55        ZF_LOGE("No '#interrupt-cells' property!");
56        return ENODEV;
57    }
58    uint32_t num_interrupt_cells = READ_CELL(1, interrupt_cells_prop, 0);
59    if (num_interrupt_cells != 1) {
60        ZF_LOGE("This parser doesn't understand multi-cell interrupts");
61        return ENODEV;
62    }
63
64    /* Loop through each cell and call the callback */
65    for (int i = 0; i < total_cells; i++) {
66        ps_irq_t irq = {
67            .type = PS_INTERRUPT,
68            .irq = {
69                .number = READ_CELL(1, interrupts_prop, i)
70            }
71        };
72
73        int error = callback(irq, i, total_cells, token);
74        if (error) {
75            return error;
76        }
77    }
78
79    return 0;
80}
81
82char *riscv_plic_compatible_list[] = {
83    "riscv,plic0",
84    NULL
85};
86DEFINE_IRQCHIP_PARSER(riscv_plic, riscv_plic_compatible_list, parse_riscv_plic_interrupts);
87