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