1/*
2 * Copyright 2019, 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#define SPI_INTERRUPTS 0
21#define PPI_INTERRUPTS 1
22#define EXTENDED_SPI_INTERRUPTS 2
23#define EXTENDED_PPI_INTERRUPTS 3
24
25#define PPI_START 16
26#define SPI_START 32
27
28#define INT_FLAG_EDGE_TRIGGERED 1
29#define INT_FLAG_LEVEL_TRIGGERED 4
30
31/* The Linux documentation states that the #interrupt-cells property should be either 3 or 4 */
32#define INTERRUPT_CELL_COUNT 3
33#define INTERRUPT_AFFINITY_CELL_COUNT 4
34
35/* These offsets are in the context of the normal 'interrupts' property,
36 * for the 'interrupts-extended' property, we add one to each */
37typedef enum {
38    INT_TYPE_OFFSET,
39    INT_OFFSET,
40    INT_FLAG_OFFSET,
41    INT_AFFINITY_OFFSET,
42} interrupt_cell_offset;
43
44typedef enum {
45    EXT_INT_CONTROLLER_OFFSET,
46    EXT_INT_TYPE_OFFSET,
47    EXT_INT_OFFSET,
48    EXT_INT_FLAG_OFFSET,
49    EXT_INT_AFFINITY_OFFSET
50} ext_interrupt_cell_offset;
51
52/* Note for extended interrupts, we expect the common case that the interrupt controller phandles
53 * for each block in the property is the same as the v3 GIC's phandle */
54static int parse_arm_gicv3_interrupts(char *dtb_blob, int node_offset, int intr_controller_phandle,
55                                      irq_walk_cb_fn_t callback, void *token)
56{
57    bool is_extended = false;
58    int prop_len = 0;
59    const void *interrupts_prop = get_interrupts_prop(dtb_blob, node_offset, &is_extended, &prop_len);
60    assert(interrupts_prop != NULL);
61    int total_cells = prop_len / sizeof(uint32_t);
62
63    /*
64     * Check for the number of interrupt cells, two cases: either 3 or 4. The extra cell describes PPI
65     * affinity if the interrupt is a PPI interrupt.
66     */
67    int intr_controller_offset = fdt_node_offset_by_phandle(dtb_blob, intr_controller_phandle);
68    if (intr_controller_offset < 0) {
69        ZF_LOGE("Can't find the interrupt controller's node offset");
70        return intr_controller_offset;
71    }
72    const void *interrupt_cells_prop = fdt_getprop(dtb_blob, intr_controller_offset, "#interrupt-cells", NULL);
73    if (!interrupt_cells_prop) {
74        ZF_LOGE("No '#interrupt-cells' property!");
75        return -EINVAL;
76    }
77    uint32_t num_interrupt_cells = READ_CELL(1, interrupt_cells_prop, 0);
78    if (num_interrupt_cells != INTERRUPT_CELL_COUNT && num_interrupt_cells != INTERRUPT_AFFINITY_CELL_COUNT) {
79        ZF_LOGE("This GICv3 interrupt controller has an invalid interrupt cell count!");
80        return -EINVAL;
81    }
82
83    int stride = num_interrupt_cells + (is_extended == true);
84    int num_interrupts = total_cells / stride;
85
86    assert(total_cells % stride == 0);
87
88    for (int i = 0; i < num_interrupts; i++) {
89        ps_irq_t curr_irq = {0};
90        const void *curr = interrupts_prop + (i * stride * sizeof(uint32_t));
91        uint32_t irq_type = 0;
92        uint32_t irq = 0;
93        uint32_t UNUSED irq_flag = 0;
94        uint32_t UNUSED irq_core_affinity_phandle = 0;
95
96        if (is_extended) {
97            if (READ_CELL(1, curr, EXT_INT_CONTROLLER_OFFSET) != intr_controller_phandle) {
98                /* Bail, we hit the uncommon case where this device has interrupts routed to
99                 * different interrupt controllers */
100                return -EINVAL;
101            }
102            irq_type = READ_CELL(1, curr, EXT_INT_TYPE_OFFSET);
103            irq = READ_CELL(1, curr, EXT_INT_OFFSET);
104            irq_flag = READ_CELL(1, curr, EXT_INT_FLAG_OFFSET);
105            if (num_interrupt_cells == INTERRUPT_AFFINITY_CELL_COUNT) {
106                irq_core_affinity_phandle = READ_CELL(1, curr, EXT_INT_AFFINITY_OFFSET);
107            }
108        } else {
109            irq_type = READ_CELL(1, curr, INT_TYPE_OFFSET);
110            irq = READ_CELL(1, curr, INT_OFFSET);
111            irq_flag = READ_CELL(1, curr, INT_FLAG_OFFSET);
112            if (num_interrupt_cells == INTERRUPT_CELL_COUNT) {
113                irq_core_affinity_phandle = READ_CELL(1, curr, INT_AFFINITY_OFFSET);
114            }
115        }
116
117        /* Assumption: The parser currently treats the extended SPI/PPI interrupts as the same
118         * as the normal interrupts */
119        if (irq_type == SPI_INTERRUPTS || irq_type == EXTENDED_SPI_INTERRUPTS) {
120            curr_irq.type = PS_INTERRUPT;
121            curr_irq.irq.number = irq + SPI_START;
122        } else if (irq_type == PPI_INTERRUPTS || irq_type == EXTENDED_PPI_INTERRUPTS) {
123            /* TODO Parse PPI interrupts */
124            ZF_LOGE("Found an PPI interrupt in the GICv3 parser, skipping");
125        } else {
126            ZF_LOGE("Invalid IRQ type");
127            return -EINVAL;
128        }
129
130        int error = callback(curr_irq, i, num_interrupts, token);
131        if (error) {
132            return error;
133        }
134    }
135
136    return 0;
137}
138
139char *arm_gicv3_compatible_list[] = {
140    "arm,gic-v3",
141    NULL
142};
143DEFINE_IRQCHIP_PARSER(arm_gicv3, arm_gicv3_compatible_list, parse_arm_gicv3_interrupts);
144