Here's how PCI interrupt mapping works in OFW + x86 Linux:
The basic (Geode-independent) mechanism:
There is a platform-specific function named "assign-int-line" that lives in the file cpu/x86/<platform>/pcinode.fth . Its arguments are the base PCI config address of the device/function and the interrupt pin number (the value from the device's "interrupt pin" register at config offset 3d). The arguments thus tell you which PCI slot and which of the four possible PCI INTx lines from that slot. The return value is the IRQ number.
So the purpose of "assign-int-line" is to describe the platform-specific routing from PCI slots to CPU interrupt channels (IRQs in the legacy PC world).
The generic PCI probing code calls assign-int-line to determine the IRQ, then puts the return value in the device's "interrupt line" register at config offset 3c. Linux later reads that hardware register to find the IRQ that the device interrupts on. (But see below for Geode-specific additions...)
The implementation of assign-int-line depends on how the platform hardware routes interrupt lines to IRQs. In the early days of PCI, motherboards often had fixed routing, wiring specific PCI INT signals to specific IRQs. Nowadays, most chipsets (including Geode) have "interrupt mapper" hardware that lets software direct each PCI INT to a dynamically-choosable IRQ. But even if an interrupt mapper is present, you don't have to implement dynamic assigment. If the system hardware configuration is fixed or has only a small number of PCI slots, it often makes sense to simplify the model by choosing the interrupt routing in advance, initializing the mapper to that routing, and implementing "assign-int-line" to report the fixed mapping.
The actual coding technique for assign-int-line could be a case statement, a table lookup, or a dynamic assignment algorithm, depending on what makes sense for your system. For a very simple system, a case statement is probably easiest. For a moderately large PCI slot farm with fixed int wiring, a table lookup is appropriate. For a large system with lots of slots and dynamic routing assignment, you would need the algorithmic approach (and assign-int-line would dynamically reprogram the interrupt mapper to reflect the dynamic assignment).
Geode-specific PCI config register spoofing:
Some of the Geode "PCI devices" aren't really PCI devices. The graphics display is inside the CPU chip, accessed at the hardware level via an internal bus that has nothing to do with PCI. The USB and AC97 devices are inside the CS5536 companion chip, which is connected to the CPU chip via a "real" PCI bus, but those devices don't really have PCI configuration registers at the hardware level.
But since software is harder to change than hardware, PC operating systems expect everything to look just like a conventional PC at the hardware level. Geode traditionally copes with this using a magic wad of bits called "VSA". VSA virtualizes the hardware by trapping certain I/O ports, including the PCI config access ports, then running System Management Mode code that emulates what PCI hardware is supposed to do. (System Management Mode is a special kind of trap handler that uses resources outside the normal domain of the x86 instruction set.)
For OLPC, we decided not to use the VSA mechanism, because
a) Some of the VSA source code was encumbered so we didn't have access to it
b) The parts of the VSA code that we did have couldn't be compiled because it depended on a toolchain that is no longer available
Instead of using VSA to emulate the nonexistent PCI config registers, OLPC took advantage of the fact that the implementations of PCI configuration access primitives are modular both in Open Firmware and in Linux. So instead of just going directly to the normal CF8/CFC I/O ports for config cycles, we substituted a procedure that
a) For slots with "real" PCI hardware (just the CaFe chip on OLPC), it does the normal CF8/CFC thing.
b) For other "slots" - i.e. the devices that really aren't on the PCI bus - it looks up the appropriate value in a table.
The code for this alternate approach is a factor of 20 smaller than the code for VSA-style virtualization (I know because I recently had to write the virtualization code in order to make Windows XP run on OLPC).
There is a separate copy of this "PCI spoofing" code in OFW (vsapci.fth) and Linux
http://dev.laptop.org/git?p=olpc-2.6;a=blob_plain;f=arch/x86/pci/olpc.c
The tables therein contain platform-specific base address register values, but the overall structure of the tables - i.e. the list of virtualized devices and their static characteristics - depends mainly on fixed characteristics of the Geode chipset (but you might have to add in a device or two that OLPC doesn't use, such as the IDE function).
Note that it is still possible to dynamically-assign base addresses to real plug-in PCI devices, because the spoofing only applies to the Geode-internal devices. Real PCI devices are accessed using the normal hardware mechanisms.
OLPC PCI config.
OLPC has no pluggable PCI slots. Everything on the PCI bus is soldered down, so dynamic configuration is pointless. OLPC early init code stuffs a preassigned value into the interrupt line register of the only real PCI device (the CaFe chip). The OLPC implementation of assign-int-line is just a stub that returns the value that has already been stuffed into that interrupt line register.
In addition to the one real PCI device, OLPC also has the "spoofed" Geode PCI devices as described above. For those devices, the spoofing code discards writes to the (virtual) interrupt line register (reads return the preassigned value that is already in the table), so it doesn't actually matter what IRQ number that assign-int-line returns for those devices. (The generic PCI code calls assign-int-line to get the IRQ number, then writes it to the interrupt line register, but the write doesn't actually do anything.)
So the OLPC implementation of assign-int-line is not a good model for an arbitrary PC, but it might be a useful template for Geode-based systems that have no pluggable slots.
Andrea wrote:
Dear OFW developers,
I'm going to a "new" PCI device on our custom (Geode LX based) board.
I already add standard PCI support to our build (${BP}/dev/pcibus.fth), plus enabling some debugging stuff:
true value pcimsg? true value probemsg?
Is it possible to see its vendor/device ID from OFW? (we are going to create a custom OFW driver soon, but it will be enough to see it IDs for debugging purpose)
AFAIK I also have to add "something" on PCI IRQ table to let the OS (in our case Linux) to attach its driver to the right IRQ. I found something like that inside vsapci.fth source (cpu/x86/pc/*/vsapci.fth) but it seems that this is used only for Geode companion chip devices.
Any clue in how enumerate and assign IRQ to PCI devices with OFW?
Thanks in advance and best regards,