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