complicated_interrupts

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
complicated_interrupts [2016/07/26 15:43] – NathanWhitehorncomplicated_interrupts [2018/03/12 17:58] (current) – wiki markup MarkLinimon
Line 13: Line 13:
 This takes the requesting device, the xref phandle of the interrupt parent (e.g. from the "interrupt-parent" property, or the equivalent entry in an interrupt-map) and the byte string describing the interrupt (e.g. the contents of the "interrupts" property, or the equivalent entry in an interrupt-map) and returns a unique IRQ number that can be added to a resource list and used with bus_alloc_resource(), bus_setup_intr(), etc. This takes the requesting device, the xref phandle of the interrupt parent (e.g. from the "interrupt-parent" property, or the equivalent entry in an interrupt-map) and the byte string describing the interrupt (e.g. the contents of the "interrupts" property, or the equivalent entry in an interrupt-map) and returns a unique IRQ number that can be added to a resource list and used with bus_alloc_resource(), bus_setup_intr(), etc.
  
-Under the hood, this calls into machine-dependent code. The most general implementation (e.g. on %%PowerPC%%) has a mapping table that records the assigned IRQ and the interrupt specifier data. As PICs attach themselves during newbus discovery, they register their interest in a particular iparent value with the machine-dependent interrupt layer and eventually -- potentially at attach time but at latest by configure_final() -- are passed the section of the table corresponding to the registered iparent value and asked to configure them as specified by the PIC-dependent byte string and send interrupts corresponding to that descriptor to the IRQ assigned.+Under the hood, this calls into machine-dependent code. The most general implementations (e.g. on %%PowerPC%%) have a mapping table that records the assigned IRQ and the interrupt specifier data. As PICs attach themselves during newbus discovery, they register their interest in a particular iparent value with the machine-dependent interrupt layer and eventually -- potentially at attach time but at latest by configure_final() -- are passed the section of the table corresponding to the registered iparent value and asked to configure them as specified by the PIC-dependent byte string and send interrupts corresponding to that descriptor to the IRQ assigned. 
 + 
 +To be used with other enumeration mechanisms (e.g. ACPI), you could either abuse the function above or add another function (e.g. acpi_bus_map_intr) that takes the appropriate information and conveys it to the machine-dependent code appropriately (e.g. it would take a struct acpi_resource_extended_irq instead of an interrupt parent code and specifier).
  
 ====== Rationale ====== ====== Rationale ======
Line 21: Line 23:
 There are three requirements here for any system that handles the complexity of interrupt routing allowed by CHRP/PAPR and the FDT spec: There are three requirements here for any system that handles the complexity of interrupt routing allowed by CHRP/PAPR and the FDT spec:
  
-  - Map any N-byte (usually 1-3 for device trees) interrupt specifier + interrupt parent combination onto some globally unique 32-bit integer. The uniqueness and 32-bit requirement comes from a number of places in the kernel: the PCI MSI code and rman in particular, though there are other places.+  - Map any N-byte (usually 4-12 for device trees) interrupt specifier + interrupt parent combination onto some globally unique 32-bit integer. The uniqueness and 32-bit requirement comes from a number of places in the kernel: the PCI MSI code and rman in particular, though there are other places.
   - Allow bus_alloc_resource() and bus_setup_intr() to succeed in early boot even if the interrupt parent is not yet attached. This is required to break what would otherwise be dependency loops involving devices with interrupts handled by their bus children.   - Allow bus_alloc_resource() and bus_setup_intr() to succeed in early boot even if the interrupt parent is not yet attached. This is required to break what would otherwise be dependency loops involving devices with interrupts handled by their bus children.
   - Make the interrupt controller driver aware of the full N-byte string on attach, as it can encode flags (e.g. trigger mode and level) needed for interrupt setup that are encoded in a way specific to the model of interrupt controller.   - Make the interrupt controller driver aware of the full N-byte string on attach, as it can encode flags (e.g. trigger mode and level) needed for interrupt setup that are encoded in a way specific to the model of interrupt controller.
Line 29: Line 31:
 ==== Case 1: the G5 Powermac ==== ==== Case 1: the G5 Powermac ====
  
-Apple PowerMac G5s have two PICs, one cascaded from the other. PIC 1 lives in the northbridge, and PIC 2 lives on a device on the PCI bus behind a couple of PCI<->PCI and HyperTransport bridges. Depending on the era of the hardware, these are cascaded in different directions.+Apple %%PowerMac%% G5s have two PICs, one cascaded from the other. PIC 1 lives in the northbridge, and PIC 2 lives on a device on the PCI bus behind a couple of PCI<->PCI and %%HyperTransport%% bridges. Depending on the era of the hardware, these are cascaded in different directions.
  
-This presents two puzzles:+This presents three puzzles:
  
-A. How do I represent interrupts on the PCI bus parent of PIC 2 that are handled by PIC 2? PIC 2 obviously can't attach before its bus parent, but the bus parent can't complete initialization without the ability to setup its interrupts.+  - How do I represent interrupts on the PCI bus parent of PIC 2 that are handled by PIC 2? PIC 2 obviously can't attach before its bus parent, but the bus parent can't complete initialization without the ability to setup its interrupts. 
 +  - Devices on the PCI bus have interrupts handled by a mixture of PIC 1 and PIC 2, sometimes on the same device and not always expressable through the bus hierarchy. For example, one of the two storage controllers has an interrupt on PIC 2 run through a wire that doesn't go through the PCI connection and so isn't in the interrupt-map of the PCI bus, which is wired (mostly) to PIC 1, and about which the parent bus can and should know nothing. 
 +  - Both PICs are %%OpenPIC%% devices. In a multipass-type framework, how do you ensure that the attach order of the two %%OpenPICs%% is correct? The two %%OpenPIC%% devices are distinguished only by one of them (which one depends on the model of the machine) having an interrupts property and the other not and are implemented by the same driver.
  
-B. Devices on the PCI bus have interrupts handled by a mixture of PIC 1 and PIC 2, sometimes on the same device and not always expressable through the bus hierarchy. For example, one of the two storage controllers has an interrupt on PIC 2 run through a wire that doesn't go through the PCI connection and so isn't in the interrupt-map of the PCI bus, which is wired (mostly) to PIC 1, and about which the parent bus can and should know nothing. +These are solved by allowing bus_setup_intr() etc. to succeed before the PIC(s) has(have) attached, which breaks what would otherwise be a circular dependency loop (1). This also fixes (3) by removing any requirement on attach order of the two PICs. Because devices are free to modify their own resource lists and can call ofw_bus_map_intr() themselves, the ATA driver is free to allocate its own interrupt to satisfy (2).
- +
-These are solved by allowing bus_setup_intr() etc. to succeed before the PIC(s) has(have) attached, which breaks what would otherwise be a circular dependency loop.+
  
 ==== Case 2: IBM OPAL firmware ==== ==== Case 2: IBM OPAL firmware ====
Line 56: Line 58:
  
 These are solved by allowing the IRQ to be a single scalar number and making knowledge of the routing independent of the newbus hierarchy after assignment time. The PCI layer gets a number like it wants and handles it appropriately after assignment by the device-tree-aware PCI host bridge driver. These are solved by allowing the IRQ to be a single scalar number and making knowledge of the routing independent of the newbus hierarchy after assignment time. The PCI layer gets a number like it wants and handles it appropriately after assignment by the device-tree-aware PCI host bridge driver.
 +
 +==== Case 5: PCI LSIs ====
 +
 +The PCI LSI API (PCIB_ROUTE_INTERRUPT(%%))%% is when it is possible to match a unit and pin to the interrupt-map property of the PCI bridge, which gives an interrupt specifier and parent. The output of this routine is a single number (the IRQ). By mapping this IRQ to the specifier using ofw_bus_map_intr(), the PCI code can route the interrupt properly with no further modifications.
 +
 +===== API Control Flow: The life and times of an interrupt descriptor =====
 +
 +==== Enumeration ====
 +
 +A bus driver walks the device tree, enumerating its children. As it does so, it reads the #interrupt-parent and interrupts properties of its children (or the interrupt-map or...) and collects the xref phandle of the interrupt parent and N-byte interrupt specifier. It then calls ofw_bus_map_intr(dev, iparent, ncells, interruptspec) (or equivalent for ACPI, etc.), which returns an IRQ number. This is typically implemented by nexus, which calls into some general MD code. The MD code does the following:
 +
 +  - Check if that (parent, specifier) pair has been mapped already.
 +  - If so, return the previously allocated IRQ. Otherwise, allocate a fresh one, potentially based on the specifier in some way for ease of reading dmesg (the first word is usually an interrupt pin, so that's a friendly first guess)
 +  - Adds that (IRQ, (parent, specifier%%))%% mapping to an internal table.
 +  - If the relevant PIC is attached already, tell the PIC driver to map the given specifier to the allocated IRQ number. Otherwise, wait until the PIC attaches and tell it to map any previously allocated IRQs when it does.
 +
 +For interrupts added by other mechanisms (MSI allocation, interrupts added by children rather than their parents, etc.), the control flow is identical and ofw_bus_map_intr() is called by whatever code initially provides an IRQ number (e.g. PCIB_ALLOC_MSI(%%))%%.
 +
 +==== Resource Allocation ====
 +
 +  * bus_alloc_resource() does nothing special
 +  * bus_setup_intr(), if the PIC has attached, will unmask and configure the requested interrupt. Otherwise, like mapping (above), the setup request will be queued until the PIC attaches and success returned immediately.
 +  * bus_config_intr() will, if the PIC is attached, set the requested polarity and trigger mode. Otherwise, the request will be queued until the PIC attaches and success returned immediately.
 +
 +==== Interrupt Handling ====
 +
 +One PIC is designated the "root PIC". On %%PowerPC%%, this is determined by the PIC driver itself and is the one PIC that attaches and itself has no interrupts on other PICs. When the MD interrupt code receives a CPU interrupt, it calls a method on the root PIC to dispatch any pending interrupts. The PIC driver examines the registers on the PIC and consults its internal mapping table established during enumeration (on some PICs, like %%OpenPIC%%, this is done in hardware) to obtain the corresponding allocated IRQ number. Then it signals an interrupt on that IRQ back to the MD interrupt code.
 +
 +If several interrupt controllers are cascaded (i.e. the output pin from one controller is attached to an input pin on another), the lower-level PIC registers an interrupt filter handler on its interrupt on the higher-level one using normal newbus mechanisms. This filter runs in the same context as the dispatch routine on the root PIC (and can be, and usually is, the exact same code) and is responsible for doing the same things: check registers, look up IRQ, signal interrupt on IRQ.
  
  • complicated_interrupts.1469547784.txt.gz
  • Last modified: 2016/07/26 15:43
  • by NathanWhitehorn