patch-2.3.15 linux/drivers/pci/pci.c
Next file: linux/drivers/pci/pcisyms.c
Previous file: linux/drivers/pci/names.c
Back to the patch index
Back to the overall index
- Lines: 577
- Date:
Mon Aug 23 13:47:35 1999
- Orig file:
v2.3.14/linux/drivers/pci/pci.c
- Orig date:
Thu Aug 12 11:50:14 1999
diff -u --recursive --new-file v2.3.14/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
@@ -16,6 +16,7 @@
#include <linux/string.h>
#include <linux/init.h>
#include <linux/malloc.h>
+#include <linux/ioport.h>
#include <asm/page.h>
@@ -27,13 +28,10 @@
#define DBG(x...)
#endif
-extern void pci_namedevice(struct pci_dev *);
-
-struct pci_bus pci_root;
+struct pci_bus *pci_root;
struct pci_dev *pci_devices = NULL;
-int pci_reverse __initdata = 0;
-
static struct pci_dev **pci_last_dev_p = &pci_devices;
+static int pci_reverse __initdata = 0;
struct pci_dev *
pci_find_slot(unsigned int bus, unsigned int devfn)
@@ -48,29 +46,37 @@
struct pci_dev *
-pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
+pci_find_subsys(unsigned int vendor, unsigned int device,
+ unsigned int ss_vendor, unsigned int ss_device,
+ struct pci_dev *from)
{
- struct pci_dev *next;
+ struct pci_dev *dev;
- next = pci_devices;
if (from)
- next = from->next;
-
- while (next) {
- struct pci_dev *dev = next;
- next = next->next;
- if (vendor != PCI_ANY_ID && dev->vendor != vendor)
- continue;
- if (device != PCI_ANY_ID && dev->device != device)
- continue;
+ dev = from->next;
+ else
+ dev = pci_devices;
- return dev;
+ while (dev) {
+ if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
+ (device == PCI_ANY_ID || dev->device == device) &&
+ (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
+ (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
+ return dev;
+ dev = dev->next;
}
return NULL;
}
struct pci_dev *
+pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
+{
+ return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
+}
+
+
+struct pci_dev *
pci_find_class(unsigned int class, struct pci_dev *from)
{
if (!from)
@@ -83,41 +89,41 @@
}
-int
-pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val)
-{
- return pcibios_read_config_byte(dev->bus->number, dev->devfn, where, val);
-}
-
-int
-pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val)
-{
- return pcibios_read_config_word(dev->bus->number, dev->devfn, where, val);
-}
-
-int
-pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val)
-{
- return pcibios_read_config_dword(dev->bus->number, dev->devfn, where, val);
-}
-
-int
-pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
-{
- return pcibios_write_config_byte(dev->bus->number, dev->devfn, where, val);
-}
-
-int
-pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
-{
- return pcibios_write_config_word(dev->bus->number, dev->devfn, where, val);
-}
-
-int
-pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
-{
- return pcibios_write_config_dword(dev->bus->number, dev->devfn, where, val);
-}
+/*
+ * This interrupt-safe spinlock protects all accesses to PCI
+ * configuration space.
+ */
+
+static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Wrappers for all PCI configuration access functions. They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by pci_dev->ops.
+ */
+
+#define PCI_byte_BAD 0
+#define PCI_word_BAD (pos & 1)
+#define PCI_dword_BAD (pos & 3)
+
+#define PCI_OP(rw,size,type) \
+int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \
+{ \
+ int res; \
+ unsigned long flags; \
+ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
+ spin_lock_irqsave(&pci_lock, flags); \
+ res = dev->bus->ops->rw##_##size(dev, pos, value); \
+ spin_unlock_irqrestore(&pci_lock, flags); \
+ return res; \
+}
+
+PCI_OP(read, byte, u8 *)
+PCI_OP(read, word, u16 *)
+PCI_OP(read, dword, u32 *)
+PCI_OP(write, byte, u8)
+PCI_OP(write, word, u16)
+PCI_OP(write, dword, u32)
void
@@ -128,86 +134,116 @@
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
- printk("PCI: Enabling bus mastering for device %02x:%02x\n",
- dev->bus->number, dev->devfn);
+ printk("PCI: Enabling bus mastering for device %s\n", dev->name);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16) {
- printk("PCI: Increasing latency timer of device %02x:%02x to 64\n",
- dev->bus->number, dev->devfn);
+ printk("PCI: Increasing latency timer of device %s to 64\n", dev->name);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
}
}
-void __init pci_read_bases(struct pci_dev *dev, unsigned int howmany)
+/*
+ * Translate the low bits of the PCI base
+ * to the resource type
+ */
+static inline unsigned int pci_resource_flags(unsigned int flags)
{
- unsigned int reg;
- u32 l;
+ if (flags & PCI_BASE_ADDRESS_SPACE_IO)
+ return IORESOURCE_IO | flags;
- for(reg=0; reg < howmany; reg++) {
- struct resource *res = dev->resource + reg;
- unsigned int mask, newval, size;
+ if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
+ return IORESOURCE_MEM | IORESOURCE_PREFETCH;
- res->name = dev->name;
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &l);
- if (l == 0xffffffff)
- continue;
+ return IORESOURCE_MEM;
+}
- 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;
+void __init pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
+{
+ unsigned int pos, reg, next;
+ u32 l, sz, tmp;
+ u16 cmd;
+ struct resource *res;
- newval &= mask;
- if (!newval)
+ /* Disable IO and memory while we fiddle */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(dev, PCI_COMMAND, tmp);
+
+ for(pos=0; pos<howmany; pos = next) {
+ next = pos+1;
+ res = &dev->resource[pos];
+ res->name = dev->name;
+ reg = PCI_BASE_ADDRESS_0 + (pos << 2);
+ pci_read_config_dword(dev, reg, &l);
+ pci_write_config_dword(dev, reg, ~0);
+ pci_read_config_dword(dev, reg, &sz);
+ pci_write_config_dword(dev, reg, l);
+ if (!sz || sz == 0xffffffff)
continue;
-
- res->start = l & mask;
- res->flags = l & ~mask;
-
- size = 1;
- do {
- size <<= 1;
- } while (!(size & newval));
-
- /* 64-bit memory? */
+ if (l == 0xffffffff)
+ l = 0;
+ if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
+ sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
+ } else {
+ res->start = l & PCI_BASE_ADDRESS_IO_MASK;
+ sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
+ }
+ res->end = res->start + sz;
+ res->flags |= (l & 0xf) | pci_resource_flags(l);
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) {
+ pci_read_config_dword(dev, reg+4, &l);
+ next++;
#if BITS_PER_LONG == 64
- res->start |= ((unsigned long) high) << 32;
+ res->start |= ((unsigned long) l) << 32;
+ res->end = res->start + sz;
+ pci_write_config_dword(dev, reg+4, ~0);
+ pci_read_config_dword(dev, reg+4, &tmp);
+ pci_write_config_dword(dev, reg+4, l);
+ if (l)
+ res->end = res->start + (((unsigned long) ~l) << 32);
#else
- printk("PCI: Unable to handle 64-bit address for device %02x:%02x\n",
- dev->bus->number, dev->devfn);
- res->flags = 0;
+ if (l) {
+ printk("PCI: Unable to handle 64-bit address for device %s\n", dev->name);
res->start = 0;
- res->end = 0;
+ res->flags = 0;
continue;
-#endif
}
+#endif
}
- res->end = res->start + size - 1;
- request_resource((l & PCI_BASE_ADDRESS_SPACE_IO) ? &ioport_resource : &iomem_resource, res);
}
+ if (rom) {
+ res = &dev->resource[PCI_ROM_RESOURCE];
+ pci_read_config_dword(dev, rom, &l);
+ pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
+ pci_read_config_dword(dev, rom, &sz);
+ pci_write_config_dword(dev, rom, l);
+ if (l == 0xffffffff)
+ l = 0;
+ if (sz && sz != 0xffffffff) {
+ res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
+ IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
+ res->start = l & PCI_ROM_ADDRESS_MASK;
+ res->end = res->start + (~(sz & PCI_ROM_ADDRESS_MASK));
+ }
+ res->name = dev->name;
+ }
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
}
-
-unsigned int __init pci_scan_bus(struct pci_bus *bus)
+static unsigned int __init pci_do_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;
+ struct pci_dev *dev_cache = NULL;
- DBG("pci_scan_bus for bus %d\n", bus->number);
+ DBG("pci_do_scan_bus for bus %d\n", bus->number);
bus_last = &bus->devices;
max = bus->secondary;
for (devfn = 0; devfn < 0xff; ++devfn) {
@@ -215,37 +251,39 @@
/* not a multi-function device */
continue;
}
- if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type))
+ if (!dev_cache) {
+ dev_cache = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev_cache)
+ continue;
+ }
+ dev = dev_cache;
+ memset(dev, 0, sizeof(*dev));
+ dev->bus = bus;
+ dev->devfn = devfn;
+
+ if (pci_read_config_byte(dev, 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) ||
+ if (pci_read_config_dword(dev, 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_cache = NULL;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
- pci_namedevice(dev);
+ pci_name_device(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);
+ pci_read_config_byte(dev, PCI_COMMAND, &cmd);
+ pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
+ pci_read_config_byte(dev, PCI_COMMAND, &tmp);
dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
- pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd);
+ pci_write_config_byte(dev, PCI_COMMAND, cmd);
- pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class);
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
class >>= 8; /* upper 3 bytes */
dev->class = class;
class >>= 8;
@@ -259,29 +297,29 @@
* 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);
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
if (irq)
- pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq);
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
dev->irq = irq;
/*
* read base address registers, again pcibios_fixup() can
* tweak these
*/
- pci_read_bases(dev, 6);
- pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l);
- dev->rom_address = (l == 0xffffffff) ? 0 : l;
+ pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
+ pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
+ pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
- pci_read_bases(dev, 2);
- pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l);
- dev->rom_address = (l == 0xffffffff) ? 0 : l;
+ pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
- pci_read_bases(dev, 1);
+ pci_read_bases(dev, 1, 0);
+ pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
+ pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
break;
default: /* unknown header */
bad:
@@ -311,16 +349,21 @@
*bus_last = dev;
bus_last = &dev->sibling;
+ /* Fix up broken headers */
+ pci_fixup_device(PCI_FIXUP_HEADER, dev);
+
#if 0
/*
* Setting of latency timer in case it was less than 32 was
* a great idea, but it confused several broken devices. Grrr.
*/
- pcibios_read_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, &tmp);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &tmp);
if (tmp < 32)
- pcibios_write_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, 32);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32);
#endif
}
+ if (dev_cache)
+ kfree(dev_cache);
/*
* After performing arch-dependent fixup of the bus, look behind
@@ -333,23 +376,18 @@
*/
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;
- }
+ child = kmalloc(sizeof(*child), GFP_KERNEL);
memset(child, 0, sizeof(*child));
child->next = bus->children;
bus->children = child;
child->self = dev;
child->parent = bus;
+ child->ops = bus->ops;
/*
* Set up the primary, secondary and subordinate
@@ -362,16 +400,16 @@
* 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);
+ pci_read_config_word(dev, PCI_COMMAND, &cr);
+ pci_write_config_word(dev, PCI_COMMAND, 0x0000);
+ pci_write_config_word(dev, 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);
+ pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
if ((buses & 0xFFFFFF) != 0)
{
unsigned int cmax;
@@ -380,7 +418,7 @@
child->secondary = (buses >> 8) & 0xFF;
child->subordinate = (buses >> 16) & 0xFF;
child->number = child->secondary;
- cmax = pci_scan_bus(child);
+ cmax = pci_do_scan_bus(child);
if (cmax > max) max = cmax;
}
else
@@ -393,11 +431,11 @@
(((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);
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
/*
* Now we can scan all subordinate buses:
*/
- max = pci_scan_bus(child);
+ max = pci_do_scan_bus(child);
/*
* Set the subordinate bus number to its real
* value:
@@ -405,9 +443,9 @@
child->subordinate = max;
buses = (buses & 0xff00ffff)
| ((unsigned int)(child->subordinate) << 16);
- pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
}
- pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
+ pci_write_config_word(dev, PCI_COMMAND, cr);
}
/*
@@ -417,43 +455,36 @@
*
* Return how far we've got finding sub-buses.
*/
- DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
+ DBG("PCI: pci_do_scan_bus returning with max=%02x\n", max);
return max;
}
-struct pci_bus * __init pci_scan_peer_bridge(int bus)
+struct pci_bus * __init pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata)
{
struct pci_bus *b;
b = kmalloc(sizeof(*b), GFP_KERNEL);
memset(b, 0, sizeof(*b));
- b->next = pci_root.next;
- pci_root.next = b;
+ if (pci_root) {
+ b->next = pci_root->next;
+ pci_root->next = b;
+ } else
+ pci_root = b;
b->number = b->secondary = bus;
- b->subordinate = pci_scan_bus(b);
+ b->sysdata = sysdata;
+ b->ops = ops;
+ b->subordinate = pci_do_scan_bus(b);
return b;
}
void __init pci_init(void)
{
- pcibios_init();
-
- if (!pci_present()) {
- printk("PCI: No PCI bus detected\n");
- return;
- }
-
- printk("PCI: Probing PCI hardware\n");
-
- memset(&pci_root, 0, sizeof(pci_root));
- pci_root.subordinate = pci_scan_bus(&pci_root);
+ struct pci_dev *dev;
- /* give BIOS a chance to apply platform specific fixes: */
- pcibios_fixup();
+ pcibios_init();
-#ifdef CONFIG_PCI_QUIRKS
- pci_quirks_init();
-#endif
+ for(dev=pci_devices; dev; dev=dev->next)
+ pci_fixup_device(PCI_FIXUP_FINAL, dev);
}
static int __init pci_setup(char *str)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)