patch-2.3.13 linux/arch/sparc64/kernel/psycho.c
Next file: linux/arch/sparc64/kernel/ptrace.c
Previous file: linux/arch/sparc64/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 497
- Date:
Fri Aug 6 11:58:00 1999
- Orig file:
v2.3.12/linux/arch/sparc64/kernel/psycho.c
- Orig date:
Thu Jul 8 15:42:19 1999
diff -u --recursive --new-file v2.3.12/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.86 1999/07/01 10:39:43 davem Exp $
+/* $Id: psycho.c,v 1.89 1999/08/06 10:37:35 davem Exp $
* psycho.c: Ultra/AX U2P PCI controller support.
*
* Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -808,6 +808,264 @@
}
}
+extern struct pci_bus pci_root;
+extern struct pci_dev *pci_devices;
+static struct pci_dev **pci_last_dev_p = &pci_devices;
+extern int pci_reverse;
+
+extern void pci_namedevice(struct pci_dev *);
+
+static void __init sparc64_pci_read_bases(struct pci_dev *dev, unsigned int howmany)
+{
+ unsigned int reg;
+ u32 l;
+
+ for(reg=0; reg < howmany; reg++) {
+ struct resource *res = dev->resource + reg;
+ unsigned long mask;
+ unsigned int newval, size;
+
+ res->name = dev->name;
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
+ if (l == 0xffffffff)
+ continue;
+
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), 0xffffffff);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &newval);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), l);
+
+ mask = PCI_BASE_ADDRESS_MEM_MASK;
+ if (l & PCI_BASE_ADDRESS_SPACE_IO)
+ mask = PCI_BASE_ADDRESS_IO_MASK;
+
+ newval &= mask;
+ if (!newval)
+ continue;
+
+ res->start = l & mask;
+ res->flags = l & ~mask;
+
+ size = 1;
+ do {
+ size <<= 1;
+ } while (!(size & newval));
+
+ /* 64-bit memory? */
+ if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
+ == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+ unsigned int high;
+ reg++;
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &high);
+ if (high)
+ res->start |= ((unsigned long) high) << 32;
+ }
+ res->end = res->start + size - 1;
+ }
+}
+
+static unsigned int __init sparc64_pci_scan_bus(struct pci_bus *bus)
+{
+ unsigned int devfn, l, max, class;
+ unsigned char cmd, irq, tmp, hdr_type, is_multi = 0;
+ struct pci_dev *dev, **bus_last;
+ struct pci_bus *child;
+
+ bus_last = &bus->devices;
+ max = bus->secondary;
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) && !is_multi) {
+ /* not a multi-function device */
+ continue;
+ }
+ if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type))
+ continue;
+ if (!PCI_FUNC(devfn))
+ is_multi = hdr_type & 0x80;
+
+ if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) ||
+ /* some broken boards return 0 if a slot is empty: */
+ l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
+ continue;
+
+ dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
+ if(dev==NULL)
+ {
+ printk(KERN_ERR "pci: out of memory.\n");
+ continue;
+ }
+ memset(dev, 0, sizeof(*dev));
+ dev->bus = bus;
+ dev->devfn = devfn;
+ dev->vendor = l & 0xffff;
+ dev->device = (l >> 16) & 0xffff;
+ pci_namedevice(dev);
+
+ /* non-destructively determine if device can be a master: */
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &cmd);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &tmp);
+ dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd);
+
+ pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class);
+ class >>= 8; /* upper 3 bytes */
+ dev->class = class;
+ class >>= 8;
+ dev->hdr_type = hdr_type;
+
+ switch (hdr_type & 0x7f) { /* header type */
+ case PCI_HEADER_TYPE_NORMAL: /* standard header */
+ if (class == PCI_CLASS_BRIDGE_PCI)
+ goto bad;
+ /*
+ * If the card generates interrupts, read IRQ number
+ * (some architectures change it during pcibios_fixup())
+ */
+ pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_PIN, &irq);
+ if (irq)
+ pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq);
+ dev->irq = irq;
+ sparc64_pci_read_bases(dev, 6);
+ pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l);
+ dev->rom_address = (l == 0xffffffff) ? 0 : l;
+ break;
+ case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
+ if (class != PCI_CLASS_BRIDGE_PCI)
+ goto bad;
+ sparc64_pci_read_bases(dev, 2);
+ pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l);
+ dev->rom_address = (l == 0xffffffff) ? 0 : l;
+ break;
+ case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
+ if (class != PCI_CLASS_BRIDGE_CARDBUS)
+ goto bad;
+ sparc64_pci_read_bases(dev, 1);
+ break;
+ default: /* unknown header */
+ bad:
+ printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
+ bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type);
+ continue;
+ }
+
+ /*
+ * Put it into the global PCI device chain. It's used to
+ * find devices once everything is set up.
+ */
+ if (!pci_reverse) {
+ *pci_last_dev_p = dev;
+ pci_last_dev_p = &dev->next;
+ } else {
+ dev->next = pci_devices;
+ pci_devices = dev;
+ }
+
+ /*
+ * Now insert it into the list of devices held
+ * by the parent bus.
+ */
+ *bus_last = dev;
+ bus_last = &dev->sibling;
+ }
+
+ /*
+ * After performing arch-dependent fixup of the bus, look behind
+ * all PCI-to-PCI bridges on this bus.
+ */
+ pcibios_fixup_bus(bus);
+ for(dev=bus->devices; dev; dev=dev->sibling)
+ /*
+ * If it's a bridge, scan the bus behind it.
+ */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int buses;
+ unsigned int devfn = dev->devfn;
+ unsigned short cr;
+
+ /*
+ * Insert it into the tree of buses.
+ */
+ child = kmalloc(sizeof(*child), GFP_ATOMIC);
+ if(child==NULL)
+ {
+ printk(KERN_ERR "pci: out of memory for bridge.\n");
+ continue;
+ }
+ memset(child, 0, sizeof(*child));
+ child->next = bus->children;
+ bus->children = child;
+ child->self = dev;
+ child->parent = bus;
+
+ /*
+ * Set up the primary, secondary and subordinate
+ * bus numbers.
+ */
+ child->number = child->secondary = ++max;
+ child->primary = bus->secondary;
+ child->subordinate = 0xff;
+ /*
+ * Clear all status bits and turn off memory,
+ * I/O and master enables.
+ */
+ pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr);
+ pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000);
+ pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff);
+ /*
+ * Read the existing primary/secondary/subordinate bus
+ * number configuration to determine if the PCI bridge
+ * has already been configured by the system. If so,
+ * do not modify the configuration, merely note it.
+ */
+ pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
+ if ((buses & 0xFFFFFF) != 0)
+ {
+ unsigned int cmax;
+
+ child->primary = buses & 0xFF;
+ child->secondary = (buses >> 8) & 0xFF;
+ child->subordinate = (buses >> 16) & 0xFF;
+ child->number = child->secondary;
+ cmax = sparc64_pci_scan_bus(child);
+ if (cmax > max) max = cmax;
+ }
+ else
+ {
+ /*
+ * Configure the bus numbers for this bridge:
+ */
+ buses &= 0xff000000;
+ buses |=
+ (((unsigned int)(child->primary) << 0) |
+ ((unsigned int)(child->secondary) << 8) |
+ ((unsigned int)(child->subordinate) << 16));
+ pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
+ /*
+ * Now we can scan all subordinate buses:
+ */
+ max = sparc64_pci_scan_bus(child);
+ /*
+ * Set the subordinate bus number to its real
+ * value:
+ */
+ child->subordinate = max;
+ buses = (buses & 0xff00ffff)
+ | ((unsigned int)(child->subordinate) << 16);
+ pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
+ }
+ pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
+ }
+
+ /*
+ * We've scanned the bus and so we know all about what's on
+ * the other side of any bridges that may be on this bus plus
+ * any devices.
+ *
+ * Return how far we've got finding sub-buses.
+ */
+ return max;
+}
+
static void __init sabre_probe(struct linux_psycho *sabre)
{
struct pci_bus *pbus = sabre->pci_bus;
@@ -816,7 +1074,7 @@
pbus->number = pbus->secondary = busno;
pbus->sysdata = sabre;
- pbus->subordinate = pci_scan_bus(pbus);
+ pbus->subordinate = sparc64_pci_scan_bus(pbus);
busno = pbus->subordinate + 1;
for(pbus = pbus->children; pbus; pbus = pbus->next) {
@@ -847,7 +1105,7 @@
pbm_fixup_busno(pbm, busno);
- pbus->subordinate = pci_scan_bus(pbus);
+ pbus->subordinate = sparc64_pci_scan_bus(pbus);
/*
* Set the maximum subordinate bus of this pbm.
@@ -1159,7 +1417,7 @@
int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
int bsreg, brindex;
unsigned int rtmp;
- u64 pci_addr;
+ u64 pci_addr, pci_size;
if(bustype == 0) {
/* Config space cookie, nothing to do. */
@@ -1216,6 +1474,8 @@
/* Now construct UPA physical address. */
pci_addr = (((u64)pregs[preg].phys_mid) << 32UL);
pci_addr |= (((u64)pregs[preg].phys_lo));
+ pci_size = (((u64)pregs[preg].size_hi) << 32UL);
+ pci_size |= (((u64)pregs[preg].size_lo));
if(ap) {
pci_addr += ((u64)ap->phys_lo);
@@ -1224,12 +1484,12 @@
/* Final step, apply PBM range. */
for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
- struct linux_prom_pci_ranges *rp = &pbm->pbm_ranges[rng];
- int space = (rp->child_phys_hi >> 24) & 3;
+ struct linux_prom_pci_ranges *rngp = &pbm->pbm_ranges[rng];
+ int space = (rngp->child_phys_hi >> 24) & 3;
if(space == bustype) {
- pci_addr += ((u64)rp->parent_phys_lo);
- pci_addr += (((u64)rp->parent_phys_hi) << 32UL);
+ pci_addr += ((u64)rngp->parent_phys_lo);
+ pci_addr += (((u64)rngp->parent_phys_hi) << 32UL);
break;
}
}
@@ -1246,15 +1506,31 @@
*/
pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp);
pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp & ~1);
- } else
- pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
-
- /* Preserve I/O space bit. */
- if(bustype == 0x1) {
- pdev->base_address[brindex] |= 1;
- IO_seen = 1;
} else {
- MEM_seen = 1;
+ struct resource *root, *rp;
+
+ rp = &pdev->resource[brindex];
+
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0 + (brindex * 4), &rtmp);
+ if (rtmp & 0x1)
+ rtmp &= 0x1;
+ else
+ rtmp &= 0xf;
+
+ rp->name = pdev->name;
+ rp->start = (unsigned long)__va(pci_addr);
+ rp->end = rp->start + pci_size - 1;
+
+ /* Keep track of what we've seen so far. */
+ if(rtmp & 0x1) {
+ IO_seen = 1;
+ root = &ioport_resource;
+ } else {
+ MEM_seen = 1;
+ root = &iomem_resource;
+ }
+ rp->flags = rtmp;
+ request_resource(root, rp);
}
}
@@ -1267,28 +1543,32 @@
int breg;
for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
+ struct resource *rp;
int io;
ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
- base = (unsigned int)pdev->base_address[ridx];
+ rp = &pdev->resource[ridx];
+ base = (unsigned int)rp->start;
- if(pdev->base_address[ridx] > PAGE_OFFSET)
+ /* Already handled? */
+ if(rp->start > PAGE_OFFSET)
continue;
- io = (base & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO;
- base &= ~((io ?
- PCI_BASE_ADDRESS_IO_MASK :
- PCI_BASE_ADDRESS_MEM_MASK));
+ pci_read_config_dword(pdev, breg, &rtmp);
+ io = (rtmp & 0x1);
offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
vp = pci_find_vma(pbm, base, offset, io);
if(!vp || vp->start > base) {
unsigned int size, new_base;
- pci_read_config_dword(pdev, breg, &rtmp);
pci_write_config_dword(pdev, breg, 0xffffffff);
pci_read_config_dword(pdev, breg, &size);
+
if(io)
- size &= ~1;
+ size &= ~0x1;
+ else
+ size &= ~0xf;
+
size = (~(size) + 1);
if(!size)
continue;
@@ -1332,17 +1612,17 @@
/* Apply PBM ranges and update pci_dev. */
pci_addr = new_base;
for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
- struct linux_prom_pci_ranges *rp;
+ struct linux_prom_pci_ranges *rngp;
int rspace;
- rp = &pbm->pbm_ranges[rng];
- rspace = (rp->child_phys_hi >> 24) & 3;
+ rngp = &pbm->pbm_ranges[rng];
+ rspace = (rngp->child_phys_hi >> 24) & 3;
if(io && rspace != 1)
continue;
else if(!io && rspace != 2)
continue;
- pci_addr += ((u64)rp->parent_phys_lo);
- pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+ pci_addr += ((u64)rngp->parent_phys_lo);
+ pci_addr += (((u64)rngp->parent_phys_hi)<<32UL);
break;
}
if(rng == pbm->num_pbm_ranges) {
@@ -1350,14 +1630,19 @@
prom_printf("fixup_doit: YIEEE, cannot find "
"PBM ranges\n");
}
- pdev->base_address[ridx] = (unsigned long)__va(pci_addr);
+ rp->name = pdev->name;
+ rp->start = (unsigned long) __va(pci_addr);
+ rp->end = rp->start + size - 1;
- /* Preserve I/O space bit. */
+ /* Keep track of what we've seen so far. */
if(io) {
- pdev->base_address[ridx] |= 1;
IO_seen = 1;
+ rp->flags = rtmp & 0x1;
+ request_resource(&ioport_resource, rp);
} else {
MEM_seen = 1;
+ rp->flags = rtmp & 0xf;
+ request_resource(&iomem_resource, rp);
}
}
}
@@ -1420,15 +1705,15 @@
/* Apply PBM ranges and update pci_dev. */
pci_addr = new_base;
for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
- struct linux_prom_pci_ranges *rp;
+ struct linux_prom_pci_ranges *rngp;
int rspace;
- rp = &pbm->pbm_ranges[rng];
- rspace = (rp->child_phys_hi >> 24) & 3;
+ rngp = &pbm->pbm_ranges[rng];
+ rspace = (rngp->child_phys_hi >> 24) & 3;
if(rspace != 2)
continue;
- pci_addr += ((u64)rp->parent_phys_lo);
- pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+ pci_addr += ((u64)rngp->parent_phys_lo);
+ pci_addr += (((u64)rngp->parent_phys_hi)<<32UL);
break;
}
if(rng == pbm->num_pbm_ranges) {
@@ -1471,8 +1756,8 @@
#ifdef FIXUP_REGS_DEBUG
dprintf("REG_FIXUP[%04x,%04x]: ", pdev->vendor, pdev->device);
for(preg = 0; preg < 6; preg++) {
- if(pdev->base_address[preg] != 0)
- dprintf("%d[%016lx] ", preg, pdev->base_address[preg]);
+ if(pdev->resource[preg].start != 0)
+ dprintf("%d[%016lx] ", preg, pdev->resource[preg].start);
}
dprintf("\n");
#endif
@@ -2119,9 +2404,10 @@
static inline int
sabre_out_of_range(unsigned char devfn)
{
- return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
- ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
- (PCI_SLOT(devfn) > 1);
+ return (((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
+ ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
+ (PCI_SLOT(devfn) > 1) ||
+ (pci_probe_enable == 0));
}
static int
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)