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