1284990Scy/*
2284990Scy * Copyright 2019, Data61
3284990Scy * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4284990Scy * ABN 41 687 119 230.
5284990Scy *
6284990Scy * This software may be distributed and modified according to the terms of
7284990Scy * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8284990Scy * See "LICENSE_BSD2.txt" for details.
9284990Scy *
10284990Scy * @TAG(DATA61_BSD)
11284990Scy */
12284990Scy
13284990Scy#include <assert.h>
14284990Scy#include <stdbool.h>
15284990Scy
16284990Scy#include <utils/util.h>
17284990Scy
18284990Scy#include "../../../irqchip.h"
19284990Scy
20284990Scy#define ARM_GIC_INT_CELL_COUNT 3
21284990Scy
22284990Scy#define SPI_IRQ_TYPE 0
23284990Scy#define PPI_IRQ_TYPE 1
24284990Scy
25289997Sglebius/* These offsets are in the context of the normal 'interrupts' property,
26289997Sglebius * for the 'interrupts-extended' property, we add one to each */
27293650Sglebiustypedef enum {
28284990Scy    INT_TYPE_OFFSET,
29284990Scy    INT_OFFSET
30284990Scy} interrupt_cell_offset;
31284990Scy
32284990Scytypedef enum {
33284990Scy    EXT_INT_CONTROLLER_OFFSET,
34284990Scy    EXT_INT_TYPE_OFFSET,
35284990Scy    EXT_INT_OFFSET
36289997Sglebius} ext_interrupt_cell_offset;
37289997Sglebius
38284990Scy/* Note for extended interrupts, we expect the common case that the interrupt controller phandles
39284990Scy * for each block in the property is the same as the GICs phandle */
40284990Scystatic int parse_arm_gic_interrupts(char *dtb_blob, int node_offset, int intr_controller_phandle,
41284990Scy                                    irq_walk_cb_fn_t callback, void *token)
42284990Scy{
43289997Sglebius    bool is_extended = false;
44284990Scy    int prop_len = 0;
45289997Sglebius    const void *interrupts_prop = get_interrupts_prop(dtb_blob, node_offset, &is_extended, &prop_len);
46284990Scy    assert(interrupts_prop != NULL);
47289997Sglebius    int total_cells = prop_len / sizeof(uint32_t);
48284990Scy
49289997Sglebius    int stride = ARM_GIC_INT_CELL_COUNT + (is_extended == true);
50289997Sglebius    int num_interrupts = total_cells / stride;
51293650Sglebius
52284990Scy    assert(total_cells % stride == 0);
53284990Scy
54284990Scy    for (int i = 0; i < num_interrupts; i++) {
55        ps_irq_t curr_irq = {0};
56        const void *curr = interrupts_prop + (i * stride * sizeof(uint32_t));
57        curr_irq.type = PS_INTERRUPT;
58        uint32_t irq_type = 0;
59        uint32_t irq = 0;
60        if (is_extended) {
61            if (READ_CELL(1, curr, EXT_INT_CONTROLLER_OFFSET) != intr_controller_phandle) {
62                /* Bail, we hit the uncommon case where this device has interrupts routed to
63                 * different interrupt controllers */
64                return -EINVAL;
65            }
66            irq_type = READ_CELL(1, curr, EXT_INT_TYPE_OFFSET);
67            irq = READ_CELL(1, curr, EXT_INT_OFFSET);
68        } else {
69            irq_type = READ_CELL(1, curr, INT_TYPE_OFFSET);
70            irq = READ_CELL(1, curr, INT_OFFSET);
71        }
72        curr_irq.irq.number = irq + (irq_type == SPI_IRQ_TYPE ? 32 : 0);
73        int error = callback(curr_irq, i, num_interrupts, token);
74        if (error) {
75            return error;
76        }
77    }
78
79    return 0;
80}
81
82char *arm_gic_compatible_list[] = {
83    "arm,gic-400",
84    "arm,cortex-a9-gic",
85    "arm,cortex-a15-gic",
86    NULL
87};
88DEFINE_IRQCHIP_PARSER(arm_gic, arm_gic_compatible_list, parse_arm_gic_interrupts);
89