1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# Copyright 2020, HENSOLDT Cyber GmbH 4# 5# SPDX-License-Identifier: GPL-2.0-only 6# 7 8import logging 9 10from hardware.device import Utils, WrappedNode 11 12 13class IrqController: 14 ''' Base class for IRQ controllers ''' 15 16 def parse_irq(self, child, data): 17 ''' Given a node and a list of 32-bit integers representing 18 that node's interrupt specifier list, parse one interrupt and return 19 its number. ''' 20 logging.warning('Not sure how to parse interrupts for "{}"'.format(self.node.path)) 21 # pop the right number of irq cells 22 for _ in range(self.get_interrupt_cells()): 23 data.pop(0) 24 return -1 25 26 def __init__(self, node: WrappedNode, tree: 'FdtParser'): 27 self.node = node 28 self.tree = tree 29 30 def get_nexus_addr_cells(self) -> int: 31 ''' Get the IRQ controller's address-cells ''' 32 if self.node.has_prop('#address-cells'): 33 return self.node.get_addr_cells() 34 return 0 35 36 def get_interrupt_cells(self) -> int: 37 ''' Get the IRQ controller's interrupt-cells ''' 38 return self.node.get_prop('#interrupt-cells').words[0] 39 40 def __repr__(self): 41 return 'IrqController(node={},kind={})'.format(self.node.path, type(self).__name__) 42 43 44class InterruptNexus(IrqController): 45 ''' IrqController for interrupt nexuses, which are a mechanism for 46 "routing" interrupts from a child to multiple IRQ controllers. ''' 47 48 def parse_irq(self, child, data): 49 # interrupt-map is a list of the following: 50 # <<child unit address> <child interrupt specifier> <interrupt parent> 51 # <parent unit address> <parent interrupt specifier>> 52 53 # "child unit address" seems to be special: the docs say one thing, but 54 # Linux implements something else. We go with the Linux implementation here: 55 # child unit address size is specified by '#address-cells' in the nexus node, 56 # or the first '#address-cells' specified in a parent node. (note: not interrupt parent) 57 # see drivers/of/irq.c, 'of_irq_parse_raw' for the implementation. 58 nexus_data = list(self.node.get_prop('interrupt-map').words) 59 60 child_addr_cells = self.node.recursive_get_addr_cells() 61 child_interrupt_cells = self.get_interrupt_cells() 62 63 # only look at the first child address. 64 # note we're using our #address-cells, not the child node's, 65 # so we can't just call node.get_regions() 66 if child.has_prop('reg'): 67 addr = Utils.make_number(child_addr_cells, list(child.get_prop('reg').words)) 68 else: 69 addr = 0 70 71 specifier = Utils.make_number(child_interrupt_cells, data) 72 73 # make default address masks. 74 addr_mask = (1 << (32 * child_addr_cells)) - 1 75 spec_mask = (1 << (32 * child_interrupt_cells)) - 1 76 77 if self.node.has_prop('interrupt-map-mask'): 78 masks = list(self.node.get_prop('interrupt-map-mask').words) 79 addr_mask = Utils.make_number(child_addr_cells, masks) 80 spec_mask = Utils.make_number(child_interrupt_cells, masks) 81 82 addr &= addr_mask 83 specifier &= spec_mask 84 85 # find matching entry in the nexus. 86 ok = False 87 while len(nexus_data) > 0: 88 # <child unit address> 89 ent_addr = Utils.make_number(child_addr_cells, nexus_data) & addr_mask 90 # <child interrupt specifier> 91 ent_spec = Utils.make_number(child_interrupt_cells, nexus_data) & spec_mask 92 # <interrupt parent> 93 controller = self.tree.get_irq_controller(nexus_data.pop(0)) 94 95 # if it matches, stop here. 96 if ent_addr == addr and ent_spec == specifier: 97 ok = True 98 break 99 100 # otherwise, keep going. 101 cells = controller.get_nexus_addr_cells() 102 cells += controller.get_interrupt_cells() 103 104 # slice off the rest of this entry and move on. 105 nexus_data = nexus_data[cells:] 106 107 if not ok: 108 logging.warning("could not find matching interrupt in nexus '{}' for address/spec {:x} {:x} (from node '{}')".format( 109 self.node.path, addr, specifier, child.path)) 110 return -1 111 112 return controller.parse_irq(child, nexus_data) 113 114 115class ArmGic(IrqController): 116 ''' parses IRQs for ARM GICs ''' 117 IRQ_TYPE_SPI = 0 118 IRQ_TYPE_PPI = 1 119 IRQ_TYPE_EXTENDED_SPI = 2 120 IRQ_TYPE_EXTENDED_PPI = 3 121 122 def parse_irq(self, child, data): 123 # at least 3 cells: 124 # first cell is 1 if PPI, 0 if SPI 125 # second cell: PPI or SPI number 126 # third cell: interrupt trigger flags, ignored by us. 127 # fourth cell (gicv3 only): PPI cpu affinity, ignored for now. 128 # 129 cells = self.get_interrupt_cells() 130 interrupt_type = data.pop(0) 131 number = data.pop(0) 132 cells -= 2 133 while cells > 0: 134 data.pop(0) 135 cells -= 1 136 137 number += 16 # SGI takes 0-15 138 if interrupt_type != ArmGic.IRQ_TYPE_PPI: 139 number += 16 # PPI is 16-31 140 141 if interrupt_type != ArmGic.IRQ_TYPE_SPI and interrupt_type != ArmGic.IRQ_TYPE_PPI: 142 # we don't have any boards with extended SPI/PPI interrupts, so 143 # we don't support them here. 144 logging.warning('Node {} has interrupt with unsupported type ({}).'.format( 145 self.node.path, interrupt_type)) 146 return -1 147 148 return number 149 150 151class RawIrqController(IrqController): 152 ''' parses IRQs of format <irq-num data...> ''' 153 154 def parse_irq(self, child, data): 155 cells = self.get_interrupt_cells() 156 num = data.pop(0) 157 while cells > 1: 158 data.pop(0) 159 cells -= 1 160 return num 161 162 163class PassthroughIrqController(IrqController): 164 ''' passes off IRQ parsing to node's interrupt-parent ''' 165 166 def parse_irq(self, child, data): 167 irq_parent_ph = self.node.get_interrupt_parent() 168 irq_parent = self.tree.get_irq_controller(irq_parent_ph) 169 170 return irq_parent.parse_irq(child, data) 171 172 173CONTROLLERS = { 174 'arm,gic-400': ArmGic, 175 'arm,cortex-a7-gic': ArmGic, 176 'arm,cortex-a9-gic': ArmGic, 177 'arm,cortex-a15-gic': ArmGic, 178 'arm,gic-v3': ArmGic, 179 'brcm,bcm2836-l1-intc': RawIrqController, 180 'fsl,avic': RawIrqController, 181 'fsl,imx6q-gpc': PassthroughIrqController, 182 'fsl,imx6sx-gpc': PassthroughIrqController, 183 'fsl,imx7d-gpc': PassthroughIrqController, 184 'nvidia,tegra124-ictlr': PassthroughIrqController, 185 'qcom,msm-qgic2': ArmGic, 186 'ti,am33xx-intc': RawIrqController, 187 'ti,omap3-intc': RawIrqController, 188 'riscv,cpu-intc': RawIrqController, 189 'riscv,plic0': RawIrqController, 190} 191 192 193def create_irq_controller(node: WrappedNode, tree: 'FdtParser'): 194 if node.has_prop('interrupt-map'): 195 # interrupt nexus 196 return InterruptNexus(node, tree) 197 elif node.has_prop('compatible'): 198 # try and find a matching class that will know how to parse it 199 for compat in node.get_prop('compatible').strings: 200 if compat in CONTROLLERS: 201 return CONTROLLERS[compat](node, tree) 202 # otherwise, just return a dummy irq controller 203 return IrqController(node, tree) 204