This is part of the PA-RISC Interrupts handling system.
The following content is extracted from the file `drivers/parisc/iosapic.c`:
The I/O sapic driver manages the Interrupt Redirection Table which is the control logic to convert PCI line based interrupts into a Message Signaled Interrupt (aka Transaction Based Interrupt, TBI).
- HPA:: Hard Physical Address (aka MMIO address)
- IRQ:: Interrupt Re``Quest. Implies Line based interrupt.
- IRT:: Interrupt Routing Table (provided by PAT firmware)
- IRdT:: Interrupt Redirection Table. IRQ line to TXN ADDR/DATA table which is implemented in I/O SAPIC.
- ISR:: Interrupt Service Routine. aka Interrupt handler.
- MSI:: Message Signaled Interrupt. PCI 2.2 functionality. aka Transaction Based Interrupt (or TBI).
- PA:: Precision Architecture. HP's RISC architecture.
- RISC:: Reduced Instruction Set Computer.
What's a Message Signalled Interrupt?
MSI is a write transaction which targets a processor and is similar to a processor write to memory or MMIO. MSIs can be generated by I/O devices as well as processors and require architecture to work.
PA only supports MSI. So I/O subsystems must either natively generate MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs (e.g. PCI and EISA). IA64 supports MSIs via a "local SAPIC" which acts on behalf of a processor.
MSI allows any I/O device to interrupt any processor. This makes load balancing of the interrupt processing possible on an SMP platform. Interrupts are also ordered with respect to DMA data. It's possible on I/O coherent systems to completely eliminate PIO reads from the interrupt path. The device and driver must be designed and implemented to guarantee all DMA has been issued (issues about atomicity here) before the MSI is issued. I/O status can then safely be read from DMA'd data by the ISR.
PA-RISC platforms have two fundementally different types of firmware (see ProcessorDependentCode).
For PCI devices, "Legacy" PDC initializes the `INTERRUPT_LINE` register and BARs similar to a traditional PC BIOS. The newer "PAT" firmware supports PDC calls which return tables. PAT firmware only initializes PCI Console and Boot interface. With these tables, the OS can progam all other PCI devices.
One such PAT PDC call returns the "Interrupt Routing Table" (IRT). The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC input line. If the IRT is not available, this driver assumes `INTERRUPT_LINE` register has been programmed by firmware. The latter case also means online addition of PCI cards can NOT be supported even if HW support is present.
All platforms with PAT firmware to date (Oct 1999) use one Interrupt Routing Table for the entire platform.
Where's the iosapic?
I/O sapic is part of the "Core Electronics Complex". And on HP platforms it's integrated as part of the PCI bus adapter, LBA. So no bus walk will discover I/O Sapic. I/O Sapic driver learns about each device when LBA driver advertises the presence of the I/O sapic by calling `iosapic_register()`.
IRQ handling notes
The IO-SAPIC can indicate to the CPU which interrupt was asserted. So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per IO-SAPIC interrupt and call the device driver's handler directly. The IO-SAPIC driver hijacks the CPU interrupt handler so it can issue the End Of Interrupt command to the IO-SAPIC.
Overview of exported iosapic functions
(caveat: code isn't finished yet - this is just the plan)
* initialize globals (lock, etc) * try to read IRT. Presence of IRT determines if this is a PAT platform or not.
* create `iosapic_info` instance data structure * allocate `vector_info` array for this iosapic * initialize `vector_info` - read corresponding IRdT?
`iosapic_xlate_pin`: (only called by fixup_irq for PAT platform)
* `intr_pin = read cfg (INTERRUPT_PIN);` * if (device under PCI-PCI bridge) * translate slot/pin
* if PAT platform (IRT present) * `intr_pin = iosapic_xlate_pin(isi,pcidev)` * `intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin)` * save IRT entry into vector_info later * write cfg `INTERRUPT_LINE` (with intr_line)? * else * `intr_line = pcidev->irq` * IRT pointer = `NULL` * endif * locate `vector_info` (needs: `isi, intr_line`) * allocate processor "irq" and get `txn_addr/data` * `request_irq(processor_irq, iosapic_interrupt, vector_info,...)`
* clear any pending IRQ on that line * enable IRdT - call `enable_irq(vector[line]->processor_irq)` * write EOI in case line is already asserted.
* disable IRdT - call `disable_irq(vector[line]->processor_irq)`