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