patch-2.4.1 linux/arch/ppc/kernel/pci.c
Next file: linux/arch/ppc/kernel/pci.h
Previous file: linux/arch/ppc/kernel/open_pic_defs.h
Back to the patch index
Back to the overall index
- Lines: 746
- Date:
Wed Jan 24 15:28:36 2001
- Orig file:
v2.4.0/linux/arch/ppc/kernel/pci.c
- Orig date:
Tue Nov 7 11:25:00 2000
diff -u --recursive --new-file v2.4.0/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
@@ -3,15 +3,16 @@
* Common pmac/prep/chrp pci routines. -- Cort
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
-#include <linux/openpic.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/errno.h>
+#include <linux/bootmem.h>
#include <asm/processor.h>
#include <asm/io.h>
@@ -25,7 +26,7 @@
#include "pci.h"
-#undef DEBUG
+#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
@@ -37,54 +38,44 @@
unsigned long isa_mem_base = 0;
unsigned long pci_dram_offset = 0;
-struct pci_fixup pcibios_fixups[] = {
- { 0 }
-};
+static u8* pci_to_OF_bus_map;
-int generic_pcibios_read_byte(struct pci_dev *dev, int where, u8 *val)
-{
- return ppc_md.pcibios_read_config_byte(dev->bus->number,dev->devfn,where,val);
-}
-int generic_pcibios_read_word(struct pci_dev *dev, int where, u16 *val)
-{
- return ppc_md.pcibios_read_config_word(dev->bus->number,dev->devfn,where,val);
-}
-int generic_pcibios_read_dword(struct pci_dev *dev, int where, u32 *val)
-{
- return ppc_md.pcibios_read_config_dword(dev->bus->number,dev->devfn,where,val);
-}
-int generic_pcibios_write_byte(struct pci_dev *dev, int where, u8 val)
-{
- return ppc_md.pcibios_write_config_byte(dev->bus->number,dev->devfn,where,val);
-}
-int generic_pcibios_write_word(struct pci_dev *dev, int where, u16 val)
-{
- return ppc_md.pcibios_write_config_word(dev->bus->number,dev->devfn,where,val);
-}
-int generic_pcibios_write_dword(struct pci_dev *dev, int where, u32 val)
-{
- return ppc_md.pcibios_write_config_dword(dev->bus->number,dev->devfn,where,val);
-}
+static void pcibios_fixup_resources(struct pci_dev* dev);
+#ifdef CONFIG_ALL_PPC
+static void pcibios_fixup_cardbus(struct pci_dev* dev);
+#endif
-struct pci_ops generic_pci_ops =
-{
- generic_pcibios_read_byte,
- generic_pcibios_read_word,
- generic_pcibios_read_dword,
- generic_pcibios_write_byte,
- generic_pcibios_write_word,
- generic_pcibios_write_dword
-};
+/* By default, we don't re-assign bus numbers. We do this only on
+ * some pmacs
+ */
+int pci_assign_all_busses;
+struct pci_controller* hose_head;
+struct pci_controller** hose_tail = &hose_head;
+static int pci_bus_count;
-void pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+struct pci_fixup pcibios_fixups[] = {
+ { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources },
+#ifdef CONFIG_ALL_PPC
+ /* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */
+ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1211, pcibios_fixup_cardbus },
+#endif /* CONFIG_ALL_PPC */
+ { 0 }
+};
+
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
struct resource *res, int resource)
{
u32 new, check;
int reg;
-
- new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
+ struct pci_controller* hose = dev->sysdata;
+
+ new = res->start;
+ if (hose && res->flags & IORESOURCE_MEM)
+ new -= hose->pci_mem_offset;
+ new |= (res->flags & PCI_REGION_FLAG_MASK);
if (resource < 6) {
reg = PCI_BASE_ADDRESS_0 + 4*resource;
} else if (resource == PCI_ROM_RESOURCE) {
@@ -104,6 +95,62 @@
}
}
+static void
+pcibios_fixup_resources(struct pci_dev* dev)
+{
+ struct pci_controller* hose =
+ (struct pci_controller *)dev->sysdata;
+ int i;
+ if (!hose) {
+ printk("No hose for PCI dev %x.%x !\n", dev->bus->number, dev->devfn >> 3);
+ return;
+ }
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ struct resource *res = dev->resource + i;
+ if (!res->start)
+ continue;
+ if (res->flags & IORESOURCE_MEM) {
+ res->start += hose->pci_mem_offset;
+ res->end += hose->pci_mem_offset;
+#ifdef DEBUG
+ printk("Fixup mem res, dev: %x.%x, res_start: %lx->%lx\n",
+ dev->bus->number, dev->devfn>>3, res->start-hose->pci_mem_offset,
+ res->start);
+#endif
+ }
+
+ if ((res->flags & IORESOURCE_IO)
+ && (unsigned long) hose->io_base_virt != isa_io_base) {
+ unsigned long offs = (unsigned long) hose->io_base_virt - isa_io_base;
+ res->start += offs;
+ res->end += offs;
+ printk("Fixup IO res, dev: %x.%x, res_start: %lx->%lx\n",
+ dev->bus->number, dev->devfn>>3,
+ res->start - offs, res->start);
+ }
+ }
+}
+
+#ifdef CONFIG_ALL_PPC
+static void
+pcibios_fixup_cardbus(struct pci_dev* dev)
+{
+ /*
+ * Fix the interrupt routing on the TI1211 chip on the 1999
+ * G3 powerbook, which doesn't get initialized properly by OF.
+ */
+ if (dev->vendor == PCI_VENDOR_ID_TI
+ && dev->device == PCI_DEVICE_ID_TI_1211) {
+ u32 val;
+ /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA
+ signal out the MFUNC0 pin */
+ if (pci_read_config_dword(dev, 0x8c, &val) == 0
+ && val == 0)
+ pci_write_config_dword(dev, 0x8c, 2);
+ }
+}
+#endif /* CONFIG_ALL_PPC */
+
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
@@ -172,7 +219,8 @@
* as well.
*/
-static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
+static void __init
+pcibios_allocate_bus_resources(struct list_head *bus_list)
{
struct list_head *ln;
struct pci_bus *bus;
@@ -197,7 +245,8 @@
}
}
-static void __init pcibios_allocate_resources(int pass)
+static void __init
+pcibios_allocate_resources(int pass)
{
struct pci_dev *dev;
int idx, disabled;
@@ -250,7 +299,8 @@
}
}
-static void __init pcibios_assign_resources(void)
+static void __init
+pcibios_assign_resources(void)
{
struct pci_dev *dev;
int idx;
@@ -278,7 +328,9 @@
* the BIOS forgot to do so or because we have decided the old
* address was unusable for some reason.
*/
- if (!r->start && r->end)
+ if (!r->start && r->end &&
+ (!ppc_md.pcibios_enable_device_hook ||
+ !ppc_md.pcibios_enable_device_hook(dev, 1)))
pci_assign_resource(dev, idx);
}
@@ -293,7 +345,8 @@
}
-int pcibios_enable_resources(struct pci_dev *dev)
+int
+pcibios_enable_resources(struct pci_dev *dev)
{
u16 cmd, old_cmd;
int idx;
@@ -321,18 +374,267 @@
return 0;
}
+struct pci_controller * __init
+pcibios_alloc_controller(void)
+{
+ struct pci_controller *hose;
+
+ hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose));
+ memset(hose, 0, sizeof(struct pci_controller));
+
+ *hose_tail = hose;
+ hose_tail = &hose->next;
+
+ return hose;
+}
+
+static void
+make_one_node_map(struct device_node* node, u8 pci_bus)
+{
+ int *bus_range;
+ int len;
+
+ if (pci_bus >= pci_bus_count)
+ return;
+ bus_range = (int *) get_property(node, "bus-range", &len);
+ if (bus_range == NULL || len < 2 * sizeof(int)) {
+ printk(KERN_WARNING "Can't get bus-range for %s\n",
+ node->full_name);
+ return;
+ }
+ pci_to_OF_bus_map[pci_bus] = bus_range[0];
+
+ for (node=node->child; node != 0;node = node->sibling) {
+ struct pci_dev* dev;
+ unsigned int *class_code, *reg;
+
+ class_code = (unsigned int *) get_property(node, "class-code", 0);
+ if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+ (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+ continue;
+ reg = (unsigned int *)get_property(node, "reg", 0);
+ if (!reg)
+ continue;
+ dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff));
+ if (!dev || !dev->subordinate)
+ continue;
+ make_one_node_map(node, dev->subordinate->number);
+ }
+}
+
+void
+pcibios_make_OF_bus_map(void)
+{
+ int i;
+ struct pci_controller* hose;
+ u8* of_prop_map;
+
+ pci_to_OF_bus_map = (u8*)kmalloc(pci_bus_count, GFP_KERNEL);
+ if (!pci_to_OF_bus_map) {
+ printk(KERN_ERR "Can't allocate OF bus map !\n");
+ return;
+ }
+
+ /* We fill the bus map with invalid values, that helps
+ * debugging.
+ */
+ for (i=0; i<pci_bus_count; i++)
+ pci_to_OF_bus_map[i] = 0xff;
+
+ /* For each hose, we begin searching bridges */
+ for(hose=hose_head; hose; hose=hose->next) {
+ struct device_node* node;
+ node = (struct device_node *)hose->arch_data;
+ if (!node)
+ continue;
+ make_one_node_map(node, hose->first_busno);
+ }
+ of_prop_map = get_property(find_path_device("/"), "pci-OF-bus-map", 0);
+ if (of_prop_map)
+ memcpy(of_prop_map, pci_to_OF_bus_map, pci_bus_count);
+#ifdef DEBUG
+ printk("PCI->OF bus map:\n");
+ for (i=0; i<pci_bus_count; i++) {
+ if (pci_to_OF_bus_map[i] == 0xff)
+ continue;
+ printk("%d -> %d\n", i, pci_to_OF_bus_map[i]);
+ }
+#endif
+}
+
+static struct device_node*
+scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
+{
+ struct device_node* sub_node;
+
+ for (; node != 0;node = node->sibling) {
+ unsigned int *class_code, *reg;
+
+ reg = (unsigned int *) get_property(node, "reg", 0);
+ if (reg && ((reg[0] >> 8) & 0xff) == dev_fn
+ && ((reg[0] >> 16) & 0xff) == bus)
+ return node;
+
+ /* For PCI<->PCI bridges or CardBus bridges, we go down */
+ class_code = (unsigned int *) get_property(node, "class-code", 0);
+ if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+ (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+ continue;
+ sub_node = scan_OF_childs_for_device(node->child, bus, dev_fn);
+ if (sub_node)
+ return sub_node;
+ }
+ return NULL;
+}
+/*
+ * Scans the OF tree for a device node matching a PCI device
+ */
+struct device_node*
+pci_device_to_OF_node(struct pci_dev *dev)
+{
+ struct pci_controller *hose;
+ struct device_node *node;
+ int bus;
+
+ if (!have_of)
+ return NULL;
+
+ /* Lookup the hose */
+ bus = dev->bus->number;
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
+ return NULL;
+
+ /* Check it has an OF node associated */
+ node = (struct device_node *) hose->arch_data;
+ if (!node)
+ return NULL;
+
+ /* Fixup bus number according to what OF think it is. */
+ if (pci_to_OF_bus_map)
+ bus = pci_to_OF_bus_map[bus];
+ if (bus == 0xff)
+ return NULL;
+
+ /* Now, lookup childs of the hose */
+ return scan_OF_childs_for_device(node->child, bus, dev->devfn);
+}
+
+/* This routine is meant to be used early during boot, when the
+ * PCI bus numbers have not yet been assigned, and you need to
+ * issue PCI config cycles to an OF device.
+ * It could also be used to "fix" RTAS config cycles if you want
+ * to set pci_assign_all_busses to 1 and still use RTAS for PCI
+ * config cycles.
+ */
+struct pci_controller*
+pci_find_hose_for_OF_device(struct device_node* node)
+{
+ if (!have_of)
+ return NULL;
+ while(node) {
+ struct pci_controller* hose;
+ for (hose=hose_head;hose;hose=hose->next)
+ if (hose->arch_data == node)
+ return hose;
+ node=node->parent;
+ }
+ return NULL;
+}
+
+/*
+ * Returns the PCI device matching a given OF node
+ */
+int
+pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
+{
+ unsigned int *reg;
+ int i;
+
+ if (!have_of)
+ return -ENODEV;
+ reg = (unsigned int *) get_property(node, "reg", 0);
+ if (!reg)
+ return -ENODEV;
+ *bus = (reg[0] >> 16) & 0xff;
+ for (i=0; pci_to_OF_bus_map && i<pci_bus_count; i++)
+ if (pci_to_OF_bus_map[i] == *bus) {
+ *bus = i;
+ break;
+ }
+ *devfn = ((reg[0] >> 8) & 0xff);
+ return 0;
+}
-void __init pcibios_init(void)
+void __init
+pcibios_init(void)
{
+ struct pci_controller *hose;
+ struct pci_bus *bus;
+ int next_busno;
+
printk("PCI: Probing PCI hardware\n");
- pci_scan_bus(0, &generic_pci_ops, NULL);
+
+ /* Scan all of the recorded PCI controllers. */
+ for (next_busno = 0, hose = hose_head; hose; hose = hose->next) {
+ if (pci_assign_all_busses)
+ hose->first_busno = next_busno;
+ hose->last_busno = 0xff;
+ bus = pci_scan_bus(hose->first_busno, hose->ops, hose);
+ hose->bus = bus;
+ hose->last_busno = bus->subordinate;
+ if (pci_assign_all_busses || next_busno <= hose->last_busno)
+ next_busno = hose->last_busno+1;
+ }
+ pci_bus_count = next_busno;
+
+ /* OpenFirmware based machines need a map of OF bus
+ * numbers vs. kernel bus numbers since we may have to
+ * remap them.
+ */
+ if (pci_assign_all_busses && have_of)
+ pcibios_make_OF_bus_map();
+
+ /* Call machine dependant fixup */
if (ppc_md.pcibios_fixup)
ppc_md.pcibios_fixup();
+
+ /* Allocate and assign resources */
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
pcibios_assign_resources();
+
+#ifdef CONFIG_BLK_DEV_IDE
+ /* OF fails to initialize IDE controllers on macs
+ * (and maybe other machines)
+ *
+ * This late fixup is done here since I want it to happen after
+ * resource assignement, and there's no "late-init" arch hook
+ *
+ * Ideally, this should be moved to the IDE layer, but we need
+ * to check specifically with Andre Hedrick how to do it cleanly
+ * since the common IDE code seem to care about the fact that the
+ * BIOS may have disabled a controller.
+ *
+ * -- BenH
+ */
+ if (_machine == _MACH_Pmac) {
+ struct pci_dev *dev;
+ pci_for_each_dev(dev)
+ {
+ if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE)
+ pci_enable_device(dev);
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDE */
+}
+
+int __init
+pcibios_assign_all_busses(void)
+{
+ return pci_assign_all_busses;
}
void __init
@@ -344,9 +646,16 @@
ranges->mem_end -= bus->resource[1]->start;
}
+unsigned long resource_fixup(struct pci_dev * dev, struct resource * res,
+ unsigned long start, unsigned long size)
+{
+ return start;
+}
void __init pcibios_fixup_bus(struct pci_bus *bus)
{
+ pci_read_bridge_bases(bus);
+
if ( ppc_md.pcibios_fixup_bus )
ppc_md.pcibios_fixup_bus(bus);
}
@@ -370,6 +679,10 @@
int idx;
struct resource *r;
+ if (ppc_md.pcibios_enable_device_hook)
+ if (ppc_md.pcibios_enable_device_hook(dev, 0))
+ return -EINVAL;
+
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for (idx=0; idx<6; idx++) {
@@ -391,37 +704,99 @@
return 0;
}
-void *
-pci_dev_io_base(unsigned char bus, unsigned char devfn, int physical)
+struct pci_controller*
+pci_bus_to_hose(int bus)
{
- if (!ppc_md.pci_dev_io_base) {
- /* Please, someone fix this for non-pmac machines, we
- * need either the virtual or physical PCI IO base
- */
- return 0;
- }
- return ppc_md.pci_dev_io_base(bus, devfn, physical);
+ struct pci_controller* hose = hose_head;
+
+ for (; hose; hose = hose->next)
+ if (bus >= hose->first_busno && bus <= hose->last_busno)
+ return hose;
+ return NULL;
}
-void *
-pci_dev_mem_base(unsigned char bus, unsigned char devfn)
+void*
+pci_bus_io_base(unsigned int bus)
{
- /* Default memory base is 0 (1:1 mapping) */
- if (!ppc_md.pci_dev_mem_base) {
- /* Please, someone fix this for non-pmac machines.*/
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
+ return NULL;
+ return hose->io_base_virt;
+}
+
+unsigned long
+pci_bus_io_base_phys(unsigned int bus)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
return 0;
- }
- return ppc_md.pci_dev_mem_base(bus, devfn);
+ return hose->io_base_phys;
}
-/* Returns the root-bridge number (Uni-N number) of a device */
-int
-pci_dev_root_bridge(unsigned char bus, unsigned char devfn)
+unsigned long
+pci_bus_mem_base_phys(unsigned int bus)
{
- /* Defaults to 0 */
- if (!ppc_md.pci_dev_root_bridge)
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
return 0;
- return ppc_md.pci_dev_root_bridge(bus, devfn);
+ return hose->pci_mem_offset;
+}
+
+#ifdef CONFIG_POWER4
+extern unsigned long pci_address_offset(int, unsigned int);
+#endif /* CONFIG_POWER4 */
+
+unsigned long
+pci_resource_to_bus(struct pci_dev *pdev, struct resource *res)
+{
+ /* Hack alert again ! See comments in chrp_pci.c
+ */
+#ifdef CONFIG_POWER4
+ unsigned long offset = pci_address_offset(pdev->bus->number, res->flags);
+ return res->start - offset;
+#else /* CONFIG_POWER4 */
+ struct pci_controller* hose =
+ (struct pci_controller *)pdev->sysdata;
+ if (hose && res->flags & IORESOURCE_MEM)
+ return res->start - hose->pci_mem_offset;
+ /* We may want to do something with IOs here... */
+ return res->start;
+#endif
+}
+
+/* Obsolete functions. Should be removed once the symbios driver
+ * is fixed
+ */
+unsigned long
+pci_phys_to_bus(unsigned long pa, int busnr)
+{
+#ifdef CONFIG_POWER4
+ return pa - pci_address_offset(busnr, IORESOURCE_MEM);
+#else /* CONFIG_POWER4 */
+ struct pci_controller* hose = pci_bus_to_hose(busnr);
+ if (!hose)
+ return pa;
+ return pa - hose->pci_mem_offset;
+#endif
+}
+
+unsigned long
+pci_bus_to_phys(unsigned int ba, int busnr)
+{
+#ifdef CONFIG_POWER4
+ return ba + pci_address_offset(dev->bus->number, IORESOURCE_MEM);
+#else /* CONFIG_POWER4 */
+ struct pci_controller* hose = pci_bus_to_hose(busnr);
+ if (!hose)
+ return ba;
+ return ba + hose->pci_mem_offset;
+#endif
}
/* Provide information on locations of various I/O regions in physical
@@ -430,23 +805,93 @@
* Note that the returned IO or memory base is a physical address
*/
-asmlinkage long
+long
sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
{
+ struct pci_controller* hose = pci_bus_to_hose(bus);
long result = -EOPNOTSUPP;
+
+ if (!hose)
+ return -ENODEV;
switch (which) {
case IOBASE_BRIDGE_NUMBER:
- return (long)pci_dev_root_bridge(bus, devfn);
+ return (long)hose->first_busno;
case IOBASE_MEMORY:
- return (long)pci_dev_mem_base(bus, devfn);
+ return (long)hose->pci_mem_offset;
case IOBASE_IO:
- result = (long)pci_dev_io_base(bus, devfn, 1);
- if (result == 0)
- result = -EOPNOTSUPP;
- break;
+ return (long)hose->io_base_phys;
+ case IOBASE_ISA_IO:
+ return (long)isa_io_base;
+ case IOBASE_ISA_MEM:
+ return (long)isa_mem_base;
}
return result;
}
+/*
+ * Null PCI config access functions, for the case when we can't
+ * find a hose.
+ */
+#define NULL_PCI_OP(rw, size, type) \
+static int \
+null_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \
+{ \
+ return PCIBIOS_DEVICE_NOT_FOUND; \
+}
+
+NULL_PCI_OP(read, byte, u8 *)
+NULL_PCI_OP(read, word, u16 *)
+NULL_PCI_OP(read, dword, u32 *)
+NULL_PCI_OP(write, byte, u8)
+NULL_PCI_OP(write, word, u16)
+NULL_PCI_OP(write, dword, u32)
+
+static struct pci_ops null_pci_ops =
+{
+ null_read_config_byte,
+ null_read_config_word,
+ null_read_config_dword,
+ null_write_config_byte,
+ null_write_config_word,
+ null_write_config_dword
+};
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_dev *
+fake_pci_dev(struct pci_controller *hose, int busnr, int devfn)
+{
+ static struct pci_dev dev;
+ static struct pci_bus bus;
+
+ if (hose == 0) {
+ hose = pci_bus_to_hose(busnr);
+ if (hose == 0)
+ printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr);
+ }
+ dev.bus = &bus;
+ dev.sysdata = hose;
+ dev.devfn = devfn;
+ bus.number = busnr;
+ bus.ops = hose? hose->ops: &null_pci_ops;
+ return &dev;
+}
+
+#define EARLY_PCI_OP(rw, size, type) \
+int early_##rw##_config_##size(struct pci_controller *hose, int bus, \
+ int devfn, int offset, type value) \
+{ \
+ return pci_##rw##_config_##size(fake_pci_dev(hose, bus, devfn), \
+ offset, value); \
+}
+
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)