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