|
@@ -9,6 +9,9 @@
|
|
|
#include <linux/init.h>
|
|
|
|
|
|
#include <asm/pbm.h>
|
|
|
+#include <asm/prom.h>
|
|
|
+
|
|
|
+#include "pci_impl.h"
|
|
|
|
|
|
/* Pass "pci=irq_verbose" on the kernel command line to enable this. */
|
|
|
int pci_irq_verbose;
|
|
@@ -31,16 +34,14 @@ void __init pci_fixup_host_bridge_self(struct pci_bus *pbus)
|
|
|
prom_halt();
|
|
|
}
|
|
|
|
|
|
-/* Find the OBP PROM device tree node for a PCI device.
|
|
|
- * Return zero if not found.
|
|
|
- */
|
|
|
-static int __init find_device_prom_node(struct pci_pbm_info *pbm,
|
|
|
- struct pci_dev *pdev,
|
|
|
- int bus_prom_node,
|
|
|
- struct linux_prom_pci_registers *pregs,
|
|
|
- int *nregs)
|
|
|
+/* Find the OBP PROM device tree node for a PCI device. */
|
|
|
+static struct device_node * __init
|
|
|
+find_device_prom_node(struct pci_pbm_info *pbm, struct pci_dev *pdev,
|
|
|
+ struct device_node *bus_node,
|
|
|
+ struct linux_prom_pci_registers **pregs,
|
|
|
+ int *nregs)
|
|
|
{
|
|
|
- int node;
|
|
|
+ struct device_node *dp;
|
|
|
|
|
|
*nregs = 0;
|
|
|
|
|
@@ -57,24 +58,30 @@ static int __init find_device_prom_node(struct pci_pbm_info *pbm,
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO ||
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_SABRE ||
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD))
|
|
|
- return bus_prom_node;
|
|
|
-
|
|
|
- node = prom_getchild(bus_prom_node);
|
|
|
- while (node != 0) {
|
|
|
- int err = prom_getproperty(node, "reg",
|
|
|
- (char *)pregs,
|
|
|
- sizeof(*pregs) * PROMREG_MAX);
|
|
|
- if (err == 0 || err == -1)
|
|
|
+ return bus_node;
|
|
|
+
|
|
|
+ dp = bus_node->child;
|
|
|
+ while (dp) {
|
|
|
+ struct linux_prom_pci_registers *regs;
|
|
|
+ struct property *prop;
|
|
|
+ int len;
|
|
|
+
|
|
|
+ prop = of_find_property(dp, "reg", &len);
|
|
|
+ if (!prop)
|
|
|
goto do_next_sibling;
|
|
|
- if (((pregs[0].phys_hi >> 8) & 0xff) == pdev->devfn) {
|
|
|
- *nregs = err / sizeof(*pregs);
|
|
|
- return node;
|
|
|
+
|
|
|
+ regs = prop->value;
|
|
|
+ if (((regs[0].phys_hi >> 8) & 0xff) == pdev->devfn) {
|
|
|
+ *pregs = regs;
|
|
|
+ *nregs = len / sizeof(struct linux_prom_pci_registers);
|
|
|
+ return dp;
|
|
|
}
|
|
|
|
|
|
do_next_sibling:
|
|
|
- node = prom_getsibling(node);
|
|
|
+ dp = dp->sibling;
|
|
|
}
|
|
|
- return 0;
|
|
|
+
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
/* Older versions of OBP on PCI systems encode 64-bit MEM
|
|
@@ -131,15 +138,17 @@ static void __init fixup_obp_assignments(struct pci_dev *pdev,
|
|
|
*/
|
|
|
static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
|
|
|
struct pci_dev *pdev,
|
|
|
- int bus_prom_node)
|
|
|
+ struct device_node *bus_node)
|
|
|
{
|
|
|
- struct linux_prom_pci_registers pregs[PROMREG_MAX];
|
|
|
+ struct linux_prom_pci_registers *pregs = NULL;
|
|
|
struct pcidev_cookie *pcp;
|
|
|
- int device_prom_node, nregs, err;
|
|
|
+ struct device_node *dp;
|
|
|
+ struct property *prop;
|
|
|
+ int nregs, len;
|
|
|
|
|
|
- device_prom_node = find_device_prom_node(pbm, pdev, bus_prom_node,
|
|
|
- pregs, &nregs);
|
|
|
- if (device_prom_node == 0) {
|
|
|
+ dp = find_device_prom_node(pbm, pdev, bus_node,
|
|
|
+ &pregs, &nregs);
|
|
|
+ if (!dp) {
|
|
|
/* If it is not in the OBP device tree then
|
|
|
* there must be a damn good reason for it.
|
|
|
*
|
|
@@ -153,45 +162,43 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- pcp = kmalloc(sizeof(*pcp), GFP_ATOMIC);
|
|
|
+ pcp = kzalloc(sizeof(*pcp), GFP_ATOMIC);
|
|
|
if (pcp == NULL) {
|
|
|
prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n");
|
|
|
prom_halt();
|
|
|
}
|
|
|
pcp->pbm = pbm;
|
|
|
- pcp->prom_node = device_prom_node;
|
|
|
- memcpy(pcp->prom_regs, pregs, sizeof(pcp->prom_regs));
|
|
|
+ pcp->prom_node = dp;
|
|
|
+ memcpy(pcp->prom_regs, pregs,
|
|
|
+ nregs * sizeof(struct linux_prom_pci_registers));
|
|
|
pcp->num_prom_regs = nregs;
|
|
|
- err = prom_getproperty(device_prom_node, "name",
|
|
|
- pcp->prom_name, sizeof(pcp->prom_name));
|
|
|
- if (err > 0)
|
|
|
- pcp->prom_name[err] = 0;
|
|
|
- else
|
|
|
- pcp->prom_name[0] = 0;
|
|
|
-
|
|
|
- err = prom_getproperty(device_prom_node,
|
|
|
- "assigned-addresses",
|
|
|
- (char *)pcp->prom_assignments,
|
|
|
- sizeof(pcp->prom_assignments));
|
|
|
- if (err == 0 || err == -1)
|
|
|
+
|
|
|
+ /* We can't have the pcidev_cookie assignments be just
|
|
|
+ * direct pointers into the property value, since they
|
|
|
+ * are potentially modified by the probing process.
|
|
|
+ */
|
|
|
+ prop = of_find_property(dp, "assigned-addresses", &len);
|
|
|
+ if (!prop) {
|
|
|
pcp->num_prom_assignments = 0;
|
|
|
- else
|
|
|
+ } else {
|
|
|
+ memcpy(pcp->prom_assignments, prop->value, len);
|
|
|
pcp->num_prom_assignments =
|
|
|
- (err / sizeof(pcp->prom_assignments[0]));
|
|
|
+ (len / sizeof(pcp->prom_assignments[0]));
|
|
|
+ }
|
|
|
|
|
|
- if (strcmp(pcp->prom_name, "ebus") == 0) {
|
|
|
- struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];
|
|
|
+ if (strcmp(dp->name, "ebus") == 0) {
|
|
|
+ struct linux_prom_ebus_ranges *erng;
|
|
|
int iter;
|
|
|
|
|
|
/* EBUS is special... */
|
|
|
- err = prom_getproperty(device_prom_node, "ranges",
|
|
|
- (char *)&erng[0], sizeof(erng));
|
|
|
- if (err == 0 || err == -1) {
|
|
|
+ prop = of_find_property(dp, "ranges", &len);
|
|
|
+ if (!prop) {
|
|
|
prom_printf("EBUS: Fatal error, no range property\n");
|
|
|
prom_halt();
|
|
|
}
|
|
|
- err = (err / sizeof(erng[0]));
|
|
|
- for(iter = 0; iter < err; iter++) {
|
|
|
+ erng = prop->value;
|
|
|
+ len = (len / sizeof(erng[0]));
|
|
|
+ for (iter = 0; iter < len; iter++) {
|
|
|
struct linux_prom_ebus_ranges *ep = &erng[iter];
|
|
|
struct linux_prom_pci_registers *ap;
|
|
|
|
|
@@ -203,7 +210,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
|
|
|
ap->size_hi = 0;
|
|
|
ap->size_lo = ep->size;
|
|
|
}
|
|
|
- pcp->num_prom_assignments = err;
|
|
|
+ pcp->num_prom_assignments = len;
|
|
|
}
|
|
|
|
|
|
fixup_obp_assignments(pdev, pcp);
|
|
@@ -213,7 +220,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
|
|
|
|
|
|
void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
|
|
|
struct pci_pbm_info *pbm,
|
|
|
- int prom_node)
|
|
|
+ struct device_node *dp)
|
|
|
{
|
|
|
struct pci_dev *pdev, *pdev_next;
|
|
|
struct pci_bus *this_pbus, *pbus_next;
|
|
@@ -221,7 +228,7 @@ void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
|
|
|
/* This must be _safe because the cookie fillin
|
|
|
routine can delete devices from the tree. */
|
|
|
list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list)
|
|
|
- pdev_cookie_fillin(pbm, pdev, prom_node);
|
|
|
+ pdev_cookie_fillin(pbm, pdev, dp);
|
|
|
|
|
|
list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) {
|
|
|
struct pcidev_cookie *pcp = this_pbus->self->sysdata;
|
|
@@ -244,7 +251,6 @@ static void __init bad_assignment(struct pci_dev *pdev,
|
|
|
if (res)
|
|
|
prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n",
|
|
|
res->start, res->end, res->flags);
|
|
|
- prom_printf("Please email this information to davem@redhat.com\n");
|
|
|
if (do_prom_halt)
|
|
|
prom_halt();
|
|
|
}
|
|
@@ -276,8 +282,7 @@ __init get_root_resource(struct linux_prom_pci_registers *ap,
|
|
|
return &pbm->mem_space;
|
|
|
|
|
|
default:
|
|
|
- printk("PCI: What is resource space %x? "
|
|
|
- "Tell davem@redhat.com about it!\n", space);
|
|
|
+ printk("PCI: What is resource space %x?\n", space);
|
|
|
return NULL;
|
|
|
};
|
|
|
}
|
|
@@ -572,50 +577,51 @@ static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm,
|
|
|
struct pci_dev *pbus,
|
|
|
struct pci_dev *pdev,
|
|
|
unsigned int interrupt,
|
|
|
- unsigned int *cnode)
|
|
|
+ struct device_node **cnode)
|
|
|
{
|
|
|
- struct linux_prom_pci_intmap imap[PROM_PCIIMAP_MAX];
|
|
|
- struct linux_prom_pci_intmask imask;
|
|
|
+ struct linux_prom_pci_intmap *imap;
|
|
|
+ struct linux_prom_pci_intmask *imask;
|
|
|
struct pcidev_cookie *pbus_pcp = pbus->sysdata;
|
|
|
struct pcidev_cookie *pdev_pcp = pdev->sysdata;
|
|
|
struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs;
|
|
|
+ struct property *prop;
|
|
|
int plen, num_imap, i;
|
|
|
unsigned int hi, mid, lo, irq, orig_interrupt;
|
|
|
|
|
|
*cnode = pbus_pcp->prom_node;
|
|
|
|
|
|
- plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map",
|
|
|
- (char *) &imap[0], sizeof(imap));
|
|
|
- if (plen <= 0 ||
|
|
|
+ prop = of_find_property(pbus_pcp->prom_node, "interrupt-map", &plen);
|
|
|
+ if (!prop ||
|
|
|
(plen % sizeof(struct linux_prom_pci_intmap)) != 0) {
|
|
|
printk("%s: Device %s interrupt-map has bad len %d\n",
|
|
|
pbm->name, pci_name(pbus), plen);
|
|
|
goto no_intmap;
|
|
|
}
|
|
|
+ imap = prop->value;
|
|
|
num_imap = plen / sizeof(struct linux_prom_pci_intmap);
|
|
|
|
|
|
- plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map-mask",
|
|
|
- (char *) &imask, sizeof(imask));
|
|
|
- if (plen <= 0 ||
|
|
|
+ prop = of_find_property(pbus_pcp->prom_node, "interrupt-map-mask", &plen);
|
|
|
+ if (!prop ||
|
|
|
(plen % sizeof(struct linux_prom_pci_intmask)) != 0) {
|
|
|
printk("%s: Device %s interrupt-map-mask has bad len %d\n",
|
|
|
pbm->name, pci_name(pbus), plen);
|
|
|
goto no_intmap;
|
|
|
}
|
|
|
+ imask = prop->value;
|
|
|
|
|
|
orig_interrupt = interrupt;
|
|
|
|
|
|
- hi = pregs->phys_hi & imask.phys_hi;
|
|
|
- mid = pregs->phys_mid & imask.phys_mid;
|
|
|
- lo = pregs->phys_lo & imask.phys_lo;
|
|
|
- irq = interrupt & imask.interrupt;
|
|
|
+ hi = pregs->phys_hi & imask->phys_hi;
|
|
|
+ mid = pregs->phys_mid & imask->phys_mid;
|
|
|
+ lo = pregs->phys_lo & imask->phys_lo;
|
|
|
+ irq = interrupt & imask->interrupt;
|
|
|
|
|
|
for (i = 0; i < num_imap; i++) {
|
|
|
if (imap[i].phys_hi == hi &&
|
|
|
imap[i].phys_mid == mid &&
|
|
|
imap[i].phys_lo == lo &&
|
|
|
imap[i].interrupt == irq) {
|
|
|
- *cnode = imap[i].cnode;
|
|
|
+ *cnode = of_find_node_by_phandle(imap[i].cnode);
|
|
|
interrupt = imap[i].cinterrupt;
|
|
|
}
|
|
|
}
|
|
@@ -638,21 +644,22 @@ no_intmap:
|
|
|
* all interrupt translations are complete, else we should use that node's
|
|
|
* "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt.
|
|
|
*/
|
|
|
-static unsigned int __init pci_intmap_match_to_root(struct pci_pbm_info *pbm,
|
|
|
- struct pci_dev *pdev,
|
|
|
- unsigned int *interrupt)
|
|
|
+static struct device_node * __init
|
|
|
+pci_intmap_match_to_root(struct pci_pbm_info *pbm,
|
|
|
+ struct pci_dev *pdev,
|
|
|
+ unsigned int *interrupt)
|
|
|
{
|
|
|
struct pci_dev *toplevel_pdev = pdev;
|
|
|
struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata;
|
|
|
- unsigned int cnode = toplevel_pcp->prom_node;
|
|
|
+ struct device_node *cnode = toplevel_pcp->prom_node;
|
|
|
|
|
|
while (pdev->bus->number != pbm->pci_first_busno) {
|
|
|
struct pci_dev *pbus = pdev->bus->self;
|
|
|
struct pcidev_cookie *pcp = pbus->sysdata;
|
|
|
- int plen;
|
|
|
+ struct property *prop;
|
|
|
|
|
|
- plen = prom_getproplen(pcp->prom_node, "interrupt-map");
|
|
|
- if (plen <= 0) {
|
|
|
+ prop = of_find_property(pcp->prom_node, "interrupt-map", NULL);
|
|
|
+ if (!prop) {
|
|
|
*interrupt = pci_slot_swivel(pbm, toplevel_pdev,
|
|
|
pdev, *interrupt);
|
|
|
cnode = pcp->prom_node;
|
|
@@ -669,7 +676,7 @@ static unsigned int __init pci_intmap_match_to_root(struct pci_pbm_info *pbm,
|
|
|
}
|
|
|
pdev = pbus;
|
|
|
|
|
|
- if (cnode == pbm->prom_node->node)
|
|
|
+ if (cnode == pbm->prom_node)
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -680,21 +687,24 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
|
|
|
{
|
|
|
struct pcidev_cookie *dev_pcp = pdev->sysdata;
|
|
|
struct pci_pbm_info *pbm = dev_pcp->pbm;
|
|
|
- struct linux_prom_pci_registers reg[PROMREG_MAX];
|
|
|
+ struct linux_prom_pci_registers *reg;
|
|
|
+ struct device_node *cnode;
|
|
|
+ struct property *prop;
|
|
|
unsigned int hi, mid, lo, irq;
|
|
|
- int i, cnode, plen;
|
|
|
+ int i, plen;
|
|
|
|
|
|
cnode = pci_intmap_match_to_root(pbm, pdev, interrupt);
|
|
|
- if (cnode == pbm->prom_node->node)
|
|
|
+ if (cnode == pbm->prom_node)
|
|
|
goto success;
|
|
|
|
|
|
- plen = prom_getproperty(cnode, "reg", (char *) reg, sizeof(reg));
|
|
|
- if (plen <= 0 ||
|
|
|
+ prop = of_find_property(cnode, "reg", &plen);
|
|
|
+ if (!prop ||
|
|
|
(plen % sizeof(struct linux_prom_pci_registers)) != 0) {
|
|
|
- printk("%s: OBP node %x reg property has bad len %d\n",
|
|
|
- pbm->name, cnode, plen);
|
|
|
+ printk("%s: OBP node %s reg property has bad len %d\n",
|
|
|
+ pbm->name, cnode->full_name, plen);
|
|
|
goto fail;
|
|
|
}
|
|
|
+ reg = prop->value;
|
|
|
|
|
|
hi = reg[0].phys_hi & pbm->pbm_intmask->phys_hi;
|
|
|
mid = reg[0].phys_mid & pbm->pbm_intmask->phys_mid;
|
|
@@ -734,8 +744,8 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev)
|
|
|
struct pci_controller_info *p = pbm->parent;
|
|
|
unsigned int portid = pbm->portid;
|
|
|
unsigned int prom_irq;
|
|
|
- int prom_node = pcp->prom_node;
|
|
|
- int err;
|
|
|
+ struct device_node *dp = pcp->prom_node;
|
|
|
+ struct property *prop;
|
|
|
|
|
|
/* If this is an empty EBUS device, sometimes OBP fails to
|
|
|
* give it a valid fully specified interrupts property.
|
|
@@ -746,17 +756,17 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev)
|
|
|
*/
|
|
|
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
|
|
|
pdev->device == PCI_DEVICE_ID_SUN_EBUS &&
|
|
|
- !prom_getchild(prom_node)) {
|
|
|
+ !dp->child) {
|
|
|
pdev->irq = 0;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- err = prom_getproperty(prom_node, "interrupts",
|
|
|
- (char *)&prom_irq, sizeof(prom_irq));
|
|
|
- if (err == 0 || err == -1) {
|
|
|
+ prop = of_find_property(dp, "interrupts", NULL);
|
|
|
+ if (!prop) {
|
|
|
pdev->irq = 0;
|
|
|
return;
|
|
|
}
|
|
|
+ prom_irq = *(unsigned int *) prop->value;
|
|
|
|
|
|
if (tlb_type != hypervisor) {
|
|
|
/* Fully specified already? */
|