PPC: Create interrupt map for PCI

There is a draft spec that defines how interrupts are supposed to be mapped
from one bus to another, down to the PIC and CPU interrupt line.

So far we don't implement that spec. But PPC64 Linux requires us to for PCI
devices. So let's create a map here.

Draft: http://playground.sun.com/1275/practice/imap/imap0_9d.pdf

Signed-off-by: Alexander Graf <agraf@suse.de>

git-svn-id: svn://coreboot.org/openbios/trunk/openbios-devel@679 f158a5a8-5612-0410-a976-696ce0be7e32
This commit is contained in:
Alexander Graf
2010-02-22 18:52:26 +00:00
parent 50d1f0e78e
commit 81605d775a
3 changed files with 67 additions and 1 deletions

View File

@@ -18,6 +18,7 @@
#include "macio.h"
#include "cuda.h"
#include "escc.h"
#include "openbios/pci.h"
#define OW_IO_NVRAM_SIZE 0x00020000
#define OW_IO_NVRAM_OFFSET 0x00060000
@@ -172,9 +173,13 @@ openpic_init(const char *path, uint32_t addr)
fword("finish-device");
if (is_newworld()) {
u32 *interrupt_map;
int len, i;
/* patch in interrupt parent */
dnode = find_dev(buf);
target_node = find_dev("/pci");
target_node = find_dev("/pci/mac-io");
set_int_property(target_node, "interrupt-parent", dnode);
target_node = find_dev("/pci/mac-io/escc/ch-a");
@@ -182,6 +187,15 @@ openpic_init(const char *path, uint32_t addr)
target_node = find_dev("/pci/mac-io/escc/ch-b");
set_int_property(target_node, "interrupt-parent", dnode);
target_node = find_dev("/pci");
set_int_property(target_node, "interrupt-parent", dnode);
interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len);
for (i = 0; i < 4; i++) {
interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode;
}
set_property(target_node, "interrupt-map", (char *)interrupt_map, len);
}
}

View File

@@ -278,6 +278,47 @@ static void pci_set_bus_range(const pci_config_t *config)
set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0]));
}
static void pci_host_set_interrupt_map(const pci_config_t *config)
{
/* XXX We currently have a hook in the MPIC init code to fill in its handle.
* If you want to have interrupt maps for your PCI host bus, add your
* architecture to the #if and make your bridge detect code fill in its
* handle too.
*
* It would be great if someone clever could come up with a more universal
* mechanism here.
*/
#if defined(CONFIG_PPC)
phandle_t dev = get_cur_dev();
u32 props[7 * 4];
int i;
#if defined(CONFIG_PPC)
/* Oldworld macs do interrupt maps differently */
if(!is_newworld())
return;
#endif
for (i = 0; i < (7*4); i+=7) {
props[i+PCI_INT_MAP_PCI0] = 0;
props[i+PCI_INT_MAP_PCI1] = 0;
props[i+PCI_INT_MAP_PCI2] = 0;
props[i+PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1
props[i+PCI_INT_MAP_PIC_HANDLE] = 0; // gets patched in later
props[i+PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7];
props[i+PCI_INT_MAP_PIC_POL] = 3;
}
set_property(dev, "interrupt-map", (char *)props, 7 * 4 * sizeof(props[0]));
props[PCI_INT_MAP_PCI0] = 0;
props[PCI_INT_MAP_PCI1] = 0;
props[PCI_INT_MAP_PCI2] = 0;
props[PCI_INT_MAP_PCI_INT] = 0x7;
set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0]));
#endif
}
static void pci_host_set_reg(const pci_config_t *config)
{
phandle_t dev = get_cur_dev();
@@ -344,6 +385,7 @@ int host_config_cb(const pci_config_t *config)
pci_host_set_reg(config);
pci_host_set_ranges(config);
pci_set_bus_range(config);
pci_host_set_interrupt_map(config);
return 0;
}

View File

@@ -24,6 +24,16 @@ struct pci_arch_t {
extern const pci_arch_t *arch;
/* Device tree offsets */
#define PCI_INT_MAP_PCI0 0
#define PCI_INT_MAP_PCI1 1
#define PCI_INT_MAP_PCI2 2
#define PCI_INT_MAP_PCI_INT 3
#define PCI_INT_MAP_PIC_HANDLE 4
#define PCI_INT_MAP_PIC_INT 5
#define PCI_INT_MAP_PIC_POL 6
/* Device classes and subclasses */
#define PCI_BASE_CLASS_STORAGE 0x01