Difference between revisions of "Creating Valid IRQ Tables"

From coreboot
Jump to: navigation, search
m (Patching the development kernel)
m
 
(9 intermediate revisions by 3 users not shown)
Line 1: Line 1:
'''Generating a valid interrupt routing table'''
+
This article should help people who can't read back a '''valid interrupt routing table''' from their system.
  
This article should help people who can't read back a valid interrupt routing table from their system.
+
== What you will need ==
 
+
== What do you need ==
+
  
 
* current Linux kernel source (this is for 2.6.21)
 
* current Linux kernel source (this is for 2.6.21)
* LinuxBios for your card with highest debug level output
+
* coreboot for your mainboard with highest debug level output
* (cross) toolchain to build the kernel and LinuxBios
+
* (cross) toolchain to build the kernel and coreboot
* a fast method to boot and reboot your card (for example etherboot and NFS root filesystem)
+
* a fast method to boot and reboot your mainboard (for example etherboot and NFS root filesystem)
 
* some data about your chipset and how it routes exernal interrupts
 
* some data about your chipset and how it routes exernal interrupts
 
* and very important: Ensure the kernel knows your interrupt router
 
* and very important: Ensure the kernel knows your interrupt router
Line 18: Line 16:
 
== Patching the development kernel ==
 
== Patching the development kernel ==
  
We start with a faked pirq table for LinuxBios to make its build system happy. It really doesn't matter what's in it, due to we will provide Linux itself with its own table.
+
We start with a faked pirq table for coreboot to make its build system happy. It really doesn't matter what's in it, due to we will provide Linux itself with its own table.
  
* copy [[http://www.pengutronix.de/software/ptxdist/temporary-src/references/my_irq_table.c this]] file as '''my_pirq_table.c''' into kernel's '''arch/i386/pci/'''
+
* copy [http://www.pengutronix.de/software/ptxdist/temporary-src/references/my_irq_table.c this file] as '''my_pirq_table.c''' into kernel's '''arch/i386/pci/'''
 
* open '''arch/i386/pci/irq.c''' with your favorite editor and search for the functions '''pirq_find_routing_table()''' and '''pirq_check_routing_table()'''.
 
* open '''arch/i386/pci/irq.c''' with your favorite editor and search for the functions '''pirq_find_routing_table()''' and '''pirq_check_routing_table()'''.
 
* Remove them with the well known '''#if 0 / #endif''' directives.
 
* Remove them with the well known '''#if 0 / #endif''' directives.
* add a '''#include "my_pirq_table.c"''' to the code
+
* add a '''#include "my_pirq_table.c"''' to the code (''after'' the #defines for PIRQ_SIGNATURE/PIRQ_VERSION)
 
* modify '''arch/i386/pci/pci.h''' and define the '''DEBUG''' macro. Linux will output helpful info, when this macro is enabled.
 
* modify '''arch/i386/pci/pci.h''' and define the '''DEBUG''' macro. Linux will output helpful info, when this macro is enabled.
 
* modify '''my_pirq_table.c''' for your requirements. Search for ''TODO'' in the source where to change something
 
* modify '''my_pirq_table.c''' for your requirements. Search for ''TODO'' in the source where to change something
Line 31: Line 29:
 
** select only one IRQ channel per INT?# in (it makes this job easier)
 
** select only one IRQ channel per INT?# in (it makes this job easier)
  
== Here what I did ==
+
== Here's what I did ==
  
I have two PCI devices connected to my Geode GX1 based board (Companion 5530 chipset):
+
I have two PCI devices connected to my Geode GX1 based board (CS5530 chipset):
 
* The internal USB OHCI
 
* The internal USB OHCI
 
* and an external Realtek 8139 network chip.
 
* and an external Realtek 8139 network chip.
Line 39: Line 37:
 
There is no additional slot, it is a very small Win-Terminal.
 
There is no additional slot, it is a very small Win-Terminal.
  
I assumed the Realtek outputs its interrupt on its INTA# pin (the starting kernel will states this if the '''DEGUB''' macro in '''pci.h''' is defined, so read the ''dmesg'' output if you're unsure). The same I assumed for the USB device. Also I assumed RealTek's INTA# output is connected to chipset's
+
I assumed the Realtek outputs its interrupt on its INTA# pin (the starting kernel will state this if the '''DEBUG''' macro in '''pci.h''' is defined, so read the ''dmesg'' output if you're unsure). The same I assumed for the USB device. Also I assumed RealTek's INTA# output is connected to chipset's
 
INTA# input, and USB's INTA# output is connected to chipset's INTB# input.
 
INTA# input, and USB's INTA# output is connected to chipset's INTB# input.
  
To find a start, I tried to use IRQ15 and IRQ11 for these devices. I wanted the network device connected to IRQ15 (green line) and the USB device connected to IRQ11 (red line). The pci chip info the table needs I got from LinuxBios, when it scans the pci bus. So activate a higher level of debug output in LinuxBios to collect all information you need here. Ignore the black lines in the picture. They may exist or not.
+
To find a start, I tried to use IRQ15 and IRQ11 for these devices. I wanted the network device connected to IRQ15 (green line) and the USB device connected to IRQ11 (red line). The PCI chip info the table needs I got from coreboot, when it scans the PCI bus. So activate a higher level of debug output in coreboot to collect all information you need here. Ignore the black lines in the picture. They may or may not exist.
  
 
[[Image:first_attempt.png]]
 
[[Image:first_attempt.png]]
  
So my [[Media:pci_irq_fake_1.c|first]] routing table in "my_pirq_table.c" looked like this:
+
So [http://www.pengutronix.de/software/ptxdist/temporary-src/references/pci_irq_fake_1.c my first routing table] in "my_pirq_table.c" looked like this:
  
 
   00:0f slot=00 0:01/8000 1:02/0800 2:03/0000 3:04/0000
 
   00:0f slot=00 0:01/8000 1:02/0800 2:03/0000 3:04/0000
 
   00:13 slot=00 0:02/0800 1:03/0000 2:04/0000 3:01/8000
 
   00:13 slot=00 0:02/0800 1:03/0000 2:04/0000 3:01/8000
  
== Building this kernel and prepare it for boot ==
+
== Building this kernel and preparing it for boot ==
  
 
   $ mkelfImage "--command-line=console=ttyS0,115200 ip=dhcp rw root=/dev/nfs irqpoll"
 
   $ mkelfImage "--command-line=console=ttyS0,115200 ip=dhcp rw root=/dev/nfs irqpoll"
Line 64: Line 62:
 
to see what happens with the interrupts.
 
to see what happens with the interrupts.
  
* "ping" the network card and check if its interrupt count increases. Check all the other pci devices in your system and let them generate interrupts. Always check '''/proc/interrupts''' to see which interrupt get this signal.
+
* "ping" the network card and check if its interrupt count increases. Check all the other PCI devices in your system and let them generate interrupts. Always check '''/proc/interrupts''' to see which interrupt get this signal.
 
* In my case I got nothing both on the network and the USB interrupt. A "ping" does not increase any interrupt count. Then I attached an USB hub to my system and IRQ15 count increases. '''BINGO!''' This means the network device isn't connected to chipset's INTA# input, its the USB device instead! And it also means INTB# does not receive any interrupt, so it seems not connected to any device. For the next step I used INTC# for the network device instead.
 
* In my case I got nothing both on the network and the USB interrupt. A "ping" does not increase any interrupt count. Then I attached an USB hub to my system and IRQ15 count increases. '''BINGO!''' This means the network device isn't connected to chipset's INTA# input, its the USB device instead! And it also means INTB# does not receive any interrupt, so it seems not connected to any device. For the next step I used INTC# for the network device instead.
  
Note: Check the interrupt steering setting the linux routing setup stores into the IRQ router! In my case it took hours to see, Linux didn't know my IRQ router! So the steering registers where still at their reset value, what means "No routing"! After adding the IRQ router support for my chipset it routes the interrupts!
+
Note: Check the interrupt steering setting the Linux routing setup stores into the IRQ router! In my case it took hours to see, Linux didn't know my IRQ router! So the steering registers were still at their reset value, which means "No routing"! After adding the IRQ router support for my chipset it routes the interrupts!
  
 
* I changed the routing table in "my_pirq_table.c" again to the routing shown below, rebuilt the kernel and reboot
 
* I changed the routing table in "my_pirq_table.c" again to the routing shown below, rebuilt the kernel and reboot
Line 76: Line 74:
 
   00:0f slot=00 0:03/8000 1:04/0000 2:01/0800 3:02/0000
 
   00:0f slot=00 0:03/8000 1:04/0000 2:01/0800 3:02/0000
  
This is the [[Media:pci_irq_fake_2.c|source]] I used in this case.
+
This is the [http://www.pengutronix.de/software/ptxdist/temporary-src/references/pci_irq_fake_2.c source] I used in this case.
  
With this table also the network interrupt receives signals. Then I removed the "irqpoll" and reboot this kernel again. The system runs with interrupts only (and no more polling).
+
With this table also the network interrupt receives signals. Then I removed the "irqpoll" option and rebooted this kernel again. The system runs with interrupts only (and no more polling).
  
The interrupt routing table from '''my_pirq_table.c''' is now ready to be used in LinuxBIOS. Only the checksum may not usable.
+
The interrupt routing table from '''my_pirq_table.c''' is now ready to be used in coreboot. Only the checksum may not be usable.
  
My kernel patch ignores a wrong checksum but calculates the right one instead! See ''dmesg'''s output, search for the right checksum and change it in the source file. Rebuild LinuxBios with this new irq routing table and flash it into your target.
+
My kernel patch ignores a wrong checksum but calculates the right one instead! See ''dmesg'''s output, search for the right checksum and change it in the source file. Rebuild coreboot with this new IRQ routing table and flash it into your target.
  
 
Now you can run an unpatched kernel on your system.
 
Now you can run an unpatched kernel on your system.
  
== Required patches for Geode's companion cs5530 ==
+
== Required patches for Geode's companion CS5530 ==
 +
 
 +
* This [http://www.pengutronix.de/software/ptxdist/temporary-src/references/geode-5530.patch patch] is needed to let Linux know the Cyrix 5530 interrupt router.
  
This patch is needed to let Linux know the Cyrix 5530 interrupt router.
+
{{Cc-by-2.5}}
+
Subject: [PATCH 001/001] i386/pci: fix nybble permutation and add Cyrix 5530 IRQ router
+
From: Juergen Beisert <juergen@kreuzholzen.de>
+
+
This patch adds CYRIX_5530_LEGACY to the list of known PCI interrupt router,
+
to setup chipset's routing register with valid data. It seems never be a
+
problem if the BIOS sets up these registers. But in the presence of LinuxBios
+
it fails for Cyrix 5530, due to LinuxBios does not setup these registers
+
(it leave it at their reset values).
+
+
I have no Cyrix 5520 to check, but as the comment in the source states the
+
Cyrix 5520 and Cyrix 5530 do interrupt routing in the same way. But the
+
(pirq-1)^1 expression to set a route always sets the wrong nibble, so
+
INTA/INTB and INTC/INTD are permuted and do not work as expected.
+
+
Signed-off-by: Juergen Beisert <juergen@kreuzholzen.de>
+
+
Index: arch/i386/pci/irq.c
+
===================================================================
+
--- arch/i386/pci/irq.c
+
+++ arch/i386/pci/irq.c
+
@@ -306,12 +306,12 @@ static int pirq_opti_set(struct pci_dev
+
  */
+
  static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+
  {
+
- return read_config_nybble(router, 0x5C, (pirq-1)^1);
+
+ return read_config_nybble(router, 0x5C, pirq-1);
+
  }
+
+
  static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+
  {
+
- write_config_nybble(router, 0x5C, (pirq-1)^1, irq);
+
+ write_config_nybble(router, 0x5C, pirq-1, irq);
+
  return 1;
+
  }
+
+
@@ -642,6 +642,7 @@ static __init int cyrix_router_probe(str
+
  {
+
  switch(device)
+
  {
+
+ case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+
  case PCI_DEVICE_ID_CYRIX_5520:
+
  r->name = "NatSemi";
+
  r->get = pirq_cyrix_get;
+

Latest revision as of 14:05, 24 June 2009

This article should help people who can't read back a valid interrupt routing table from their system.

What you will need

  • current Linux kernel source (this is for 2.6.21)
  • coreboot for your mainboard with highest debug level output
  • (cross) toolchain to build the kernel and coreboot
  • a fast method to boot and reboot your mainboard (for example etherboot and NFS root filesystem)
  • some data about your chipset and how it routes exernal interrupts
  • and very important: Ensure the kernel knows your interrupt router

For documentation only: Here is the generic routing for up to four slots

Routing full.png

Patching the development kernel

We start with a faked pirq table for coreboot to make its build system happy. It really doesn't matter what's in it, due to we will provide Linux itself with its own table.

  • copy this file as my_pirq_table.c into kernel's arch/i386/pci/
  • open arch/i386/pci/irq.c with your favorite editor and search for the functions pirq_find_routing_table() and pirq_check_routing_table().
  • Remove them with the well known #if 0 / #endif directives.
  • add a #include "my_pirq_table.c" to the code (after the #defines for PIRQ_SIGNATURE/PIRQ_VERSION)
  • modify arch/i386/pci/pci.h and define the DEBUG macro. Linux will output helpful info, when this macro is enabled.
  • modify my_pirq_table.c for your requirements. Search for TODO in the source where to change something
    • define slot/device count
    • define the interrupt router
    • select some IRQ channels you like to use for the PCI devices
    • select only one IRQ channel per INT?# in (it makes this job easier)

Here's what I did

I have two PCI devices connected to my Geode GX1 based board (CS5530 chipset):

  • The internal USB OHCI
  • and an external Realtek 8139 network chip.

There is no additional slot, it is a very small Win-Terminal.

I assumed the Realtek outputs its interrupt on its INTA# pin (the starting kernel will state this if the DEBUG macro in pci.h is defined, so read the dmesg output if you're unsure). The same I assumed for the USB device. Also I assumed RealTek's INTA# output is connected to chipset's INTA# input, and USB's INTA# output is connected to chipset's INTB# input.

To find a start, I tried to use IRQ15 and IRQ11 for these devices. I wanted the network device connected to IRQ15 (green line) and the USB device connected to IRQ11 (red line). The PCI chip info the table needs I got from coreboot, when it scans the PCI bus. So activate a higher level of debug output in coreboot to collect all information you need here. Ignore the black lines in the picture. They may or may not exist.

First attempt.png

So my first routing table in "my_pirq_table.c" looked like this:

 00:0f slot=00 0:01/8000 1:02/0800 2:03/0000 3:04/0000
 00:13 slot=00 0:02/0800 1:03/0000 2:04/0000 3:01/8000

Building this kernel and preparing it for boot

 $ mkelfImage "--command-line=console=ttyS0,115200 ip=dhcp rw root=/dev/nfs irqpoll"
    --type=bzImage-i386 --kernel=arch/i386/boot/bzImage --output=/tftpboot/igel-kernel

The important kernel parameter is irqpoll. This lets the network card do its job without a valid interrupt routing! It slows down the system, but makes it work! (in my case the NFS root filesystem)

Boot this kernel (I did it with etherboot). When the system is up and running, login to it and run a

 $ cat /proc/interrupts

to see what happens with the interrupts.

  • "ping" the network card and check if its interrupt count increases. Check all the other PCI devices in your system and let them generate interrupts. Always check /proc/interrupts to see which interrupt get this signal.
  • In my case I got nothing both on the network and the USB interrupt. A "ping" does not increase any interrupt count. Then I attached an USB hub to my system and IRQ15 count increases. BINGO! This means the network device isn't connected to chipset's INTA# input, its the USB device instead! And it also means INTB# does not receive any interrupt, so it seems not connected to any device. For the next step I used INTC# for the network device instead.

Note: Check the interrupt steering setting the Linux routing setup stores into the IRQ router! In my case it took hours to see, Linux didn't know my IRQ router! So the steering registers were still at their reset value, which means "No routing"! After adding the IRQ router support for my chipset it routes the interrupts!

  • I changed the routing table in "my_pirq_table.c" again to the routing shown below, rebuilt the kernel and reboot

Second attempt.png

 00:13 slot=00 0:01/0800 1:02/0000 2:03/8000 3:04/0000
 00:0f slot=00 0:03/8000 1:04/0000 2:01/0800 3:02/0000

This is the source I used in this case.

With this table also the network interrupt receives signals. Then I removed the "irqpoll" option and rebooted this kernel again. The system runs with interrupts only (and no more polling).

The interrupt routing table from my_pirq_table.c is now ready to be used in coreboot. Only the checksum may not be usable.

My kernel patch ignores a wrong checksum but calculates the right one instead! See dmesg's output, search for the right checksum and change it in the source file. Rebuild coreboot with this new IRQ routing table and flash it into your target.

Now you can run an unpatched kernel on your system.

Required patches for Geode's companion CS5530

  • This patch is needed to let Linux know the Cyrix 5530 interrupt router.
Creative Commons License
Creative Commons Attribution icon
This file is licensed under Creative Commons Attribution 2.5 License.
In short: you are free to distribute and modify the file as long as you attribute its author(s) or licensor(s).