Commit dae4828d authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Paul Mackerras

[POWERPC] Fix irq routing on some 32-bit PowerMacs

The changes to use pci_read_irq_line() broke interrupt parsing
on some 32-bit powermacs (oops).  The reason is a bit obscure.
The code to parse interrupts happens earlier now, during
pcibios_fixup() as the PCI bus is being probed.  However, the
current implementation pci_device_to_OF_node() for 32-bit
powerpc relies, on machines like PowerMac which renumber PCI buses,
on a table called pci_OF_bus_map containing a map of bus numbers
between the kernel and the firmware which is setup only later.
Thus, it fails to match the device node.  In addition, some of
Apple internal PCI devices lack a proper PCI_INTERRUPT_PIN, thus
preventing the fallback mapping code to work.

This patch fixes it by making pci_device_to_OF_node() 32-bit
implementation use a different algorithm that works without
using the pci_OF_bus_map thing (which I intend to deprecate
anyway). It's a bit slower but that function isn't called in
any hot path hopefully.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 74e95d5d
...@@ -736,25 +736,51 @@ scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* ...@@ -736,25 +736,51 @@ scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void*
return NULL; return NULL;
} }
static int static struct device_node *scan_OF_for_pci_dev(struct device_node *parent,
scan_OF_pci_childs_iterator(struct device_node* node, void* data) unsigned int devfn)
{ {
const unsigned int *reg; struct device_node *np = NULL;
u8* fdata = (u8*)data; const u32 *reg;
unsigned int psize;
reg = get_property(node, "reg", NULL);
if (reg && ((reg[0] >> 8) & 0xff) == fdata[1] while ((np = of_get_next_child(parent, np)) != NULL) {
&& ((reg[0] >> 16) & 0xff) == fdata[0]) reg = get_property(np, "reg", &psize);
return 1; if (reg == NULL || psize < 4)
return 0; continue;
if (((reg[0] >> 8) & 0xff) == devfn)
return np;
}
return NULL;
} }
static struct device_node*
scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus)
{ {
u8 filter_data[2] = {bus, dev_fn}; struct device_node *parent, *np;
/* Are we a root bus ? */
if (bus->self == NULL || bus->parent == NULL) {
struct pci_controller *hose = pci_bus_to_hose(bus->number);
if (hose == NULL)
return NULL;
return of_node_get(hose->arch_data);
}
/* not a root bus, we need to get our parent */
parent = scan_OF_for_pci_bus(bus->parent);
if (parent == NULL)
return NULL;
/* now iterate for children for a match */
np = scan_OF_for_pci_dev(parent, bus->self->devfn);
of_node_put(parent);
return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data); /* sanity check */
if (strcmp(np->type, "pci") != 0)
printk(KERN_WARNING "pci: wrong type \"%s\" for bridge %s\n",
np->type, np->full_name);
return np;
} }
/* /*
...@@ -763,43 +789,25 @@ scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) ...@@ -763,43 +789,25 @@ scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
struct device_node * struct device_node *
pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) pci_busdev_to_OF_node(struct pci_bus *bus, int devfn)
{ {
struct pci_controller *hose; struct device_node *parent, *np;
struct device_node *node;
int busnr;
if (!have_of) if (!have_of)
return NULL; return NULL;
/* Lookup the hose */
busnr = bus->number;
hose = pci_bus_to_hose(busnr);
if (!hose)
return NULL;
/* Check it has an OF node associated */ DBG("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
node = (struct device_node *) hose->arch_data; parent = scan_OF_for_pci_bus(bus);
if (!node) if (parent == NULL)
return NULL; return NULL;
DBG(" parent is %s\n", parent ? parent->full_name : "<NULL>");
/* Fixup bus number according to what OF think it is. */ np = scan_OF_for_pci_dev(parent, devfn);
#ifdef CONFIG_PPC_PMAC of_node_put(parent);
/* The G5 need a special case here. Basically, we don't remap all DBG(" result is %s\n", np ? np->full_name : "<NULL>");
* busses on it so we don't create the pci-OF-map. However, we do
* remap the AGP bus and so have to deal with it. A future better /* XXX most callers don't release the returned node
* fix has to be done by making the remapping per-host and always * mostly because ppc64 doesn't increase the refcount,
* filling the pci_to_OF map. --BenH * we need to fix that.
*/ */
if (machine_is(powermac) && busnr >= 0xf0) return np;
busnr -= 0xf0;
else
#endif
if (pci_to_OF_bus_map)
busnr = pci_to_OF_bus_map[busnr];
if (busnr == 0xff)
return NULL;
/* Now, lookup childs of the hose */
return scan_OF_childs_for_device(node->child, busnr, devfn);
} }
EXPORT_SYMBOL(pci_busdev_to_OF_node); EXPORT_SYMBOL(pci_busdev_to_OF_node);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment