summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/pci_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r--arch/sparc64/kernel/pci_common.c85
1 files changed, 76 insertions, 9 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index 98b41d079..5cd905244 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -1,4 +1,4 @@
-/* $Id: pci_common.c,v 1.7 2000/03/25 05:18:11 davem Exp $
+/* $Id: pci_common.c,v 1.11 2000/04/26 10:48:02 davem Exp $
* pci_common.c: PCI controller common support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -66,6 +66,27 @@ static void pci_device_delete(struct pci_dev *pdev)
kfree(pdev);
}
+/* Older versions of OBP on PCI systems encode 64-bit MEM
+ * space assignments incorrectly, this fixes them up.
+ */
+static void __init fixup_obp_assignments(struct pcidev_cookie *pcp)
+{
+ int i;
+
+ for (i = 0; i < pcp->num_prom_assignments; i++) {
+ struct linux_prom_pci_registers *ap;
+ int space;
+
+ ap = &pcp->prom_assignments[i];
+ space = ap->phys_hi >> 24;
+ if ((space & 0x3) == 2 &&
+ (space & 0x4) != 0) {
+ ap->phys_hi &= ~(0x7 << 24);
+ ap->phys_hi |= 0x3 << 24;
+ }
+ }
+}
+
/* Fill in the PCI device cookie sysdata for the given
* PCI device. This cookie is the means by which one
* can get to OBP and PCI controller specific information
@@ -147,6 +168,8 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
(err / sizeof(pcp->prom_assignments[0]));
}
+ fixup_obp_assignments(pcp);
+
pdev->sysdata = pcp;
}
@@ -219,10 +242,15 @@ __init get_root_resource(struct linux_prom_pci_registers *ap,
return &pbm->mem_space;
case 3:
+ /* 64-bit MEM space, these are allocated out of
+ * the 32-bit mem_space range for the PBM, ie.
+ * we just zero out the upper 32-bits.
+ */
+ return &pbm->mem_space;
+
default:
- /* 64-bit MEM space, unsupported. */
- printk("PCI: 64-bit MEM assignment??? "
- "Tell davem@redhat.com about it!\n");
+ printk("PCI: What is resource space %x? "
+ "Tell davem@redhat.com about it!\n", space);
return NULL;
};
}
@@ -231,6 +259,7 @@ static struct resource *
__init get_device_resource(struct linux_prom_pci_registers *ap,
struct pci_dev *pdev)
{
+ struct resource *res;
int breg = (ap->phys_hi & 0xff);
int space = (ap->phys_hi >> 24) & 3;
@@ -240,7 +269,8 @@ __init get_device_resource(struct linux_prom_pci_registers *ap,
if (space != 2)
bad_assignment(ap, NULL, 0);
- return &pdev->resource[PCI_ROM_RESOURCE];
+ res = &pdev->resource[PCI_ROM_RESOURCE];
+ break;
case PCI_BASE_ADDRESS_0:
case PCI_BASE_ADDRESS_1:
@@ -248,12 +278,16 @@ __init get_device_resource(struct linux_prom_pci_registers *ap,
case PCI_BASE_ADDRESS_3:
case PCI_BASE_ADDRESS_4:
case PCI_BASE_ADDRESS_5:
- return &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4];
+ res = &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4];
+ break;
default:
bad_assignment(ap, NULL, 0);
- return NULL;
+ res = NULL;
+ break;
};
+
+ return res;
}
static void __init pdev_record_assignments(struct pci_pbm_info *pbm,
@@ -281,6 +315,31 @@ static void __init pdev_record_assignments(struct pci_pbm_info *pbm,
if ((res->start & 0xffffffffUL) != ap->phys_lo)
bad_assignment(ap, res, 1);
+ /* If it is a 64-bit MEM space assignment, verify that
+ * the resource is too and that the upper 32-bits match.
+ */
+ if (((ap->phys_hi >> 24) & 3) == 3) {
+ if (((res->flags & IORESOURCE_MEM) == 0) ||
+ ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+ != PCI_BASE_ADDRESS_MEM_TYPE_64))
+ bad_assignment(ap, res, 1);
+ if ((res->start >> 32) != ap->phys_mid)
+ bad_assignment(ap, res, 1);
+
+ /* PBM cannot generate cpu initiated PIOs
+ * to the full 64-bit space. Therefore the
+ * upper 32-bits better be zero. If it is
+ * not, just skip it and we will assign it
+ * properly ourselves.
+ */
+ if ((res->start >> 32) != 0UL) {
+ printk(KERN_ERR "PCI: OBP assigns out of range MEM address "
+ "%016lx for region %ld on device %s\n",
+ res->start, (res - &pdev->resource[0]), pdev->name);
+ continue;
+ }
+ }
+
/* Adjust the resource into the physical address space
* of this PBM.
*/
@@ -425,13 +484,21 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
return 0;
/* If we are underneath a PCI bridge, use PROM register
- * property of parent bridge.
+ * property of the parent bridge which is closest to
+ * the PBM.
*/
if (pdev->bus->number != pbm->pci_first_busno) {
struct pcidev_cookie *bus_pcp;
+ struct pci_dev *pwalk;
int offset;
- bus_pcp = pdev->bus->self->sysdata;
+ pwalk = pdev->bus->self;
+ while (pwalk->bus &&
+ pwalk->bus->number != pbm->pci_first_busno)
+ pwalk = pwalk->bus->self;
+
+ bus_pcp = pwalk->bus->self->sysdata;
+
pregs = bus_pcp->prom_regs;
offset = prom_getint(bus_pcp->prom_node,
"fcode-rom-offset");