patch-2.4.20 linux-2.4.20/arch/ia64/kernel/pci.c
Next file: linux-2.4.20/arch/ia64/kernel/perfmon.c
Previous file: linux-2.4.20/arch/ia64/kernel/palinfo.c
Back to the patch index
Back to the overall index
- Lines: 370
- Date:
Thu Nov 28 15:53:09 2002
- Orig file:
linux-2.4.19/arch/ia64/kernel/pci.c
- Orig date:
Fri Dec 21 09:41:53 2001
diff -urN linux-2.4.19/arch/ia64/kernel/pci.c linux-2.4.20/arch/ia64/kernel/pci.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
+#include <linux/acpi.h>
#include <asm/machvec.h>
#include <asm/page.h>
@@ -42,104 +43,256 @@
extern void ia64_mca_check_errors( void );
#endif
+struct pci_fixup pcibios_fixups[1];
+
+struct pci_ops *pci_root_ops;
+
+int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value);
+int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value);
+
+
/*
- * This interrupt-safe spinlock protects all accesses to PCI
- * configuration space.
+ * Low-level SAL-based PCI configuration access functions. Note that SAL
+ * calls are already serialized (via sal_lock), so we don't need another
+ * synchronization mechanism here.
*/
-static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
-struct pci_fixup pcibios_fixups[] = {
- { 0 }
-};
+#define PCI_SAL_ADDRESS(seg, bus, dev, fn, reg) \
+ ((u64)(seg << 24) | (u64)(bus << 16) | \
+ (u64)(dev << 11) | (u64)(fn << 8) | (u64)(reg))
+
+static int
+pci_sal_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
+{
+ int result = 0;
+ u64 data = 0;
+
+ if (!value || (seg > 255) || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
-/* Macro to build a PCI configuration address to be passed as a parameter to SAL. */
+ result = ia64_sal_pci_config_read(PCI_SAL_ADDRESS(seg, bus, dev, fn, reg), len, &data);
-#define PCI_CONFIG_ADDRESS(dev, where) \
- (((u64) dev->bus->number << 16) | ((u64) (dev->devfn & 0xff) << 8) | (where & 0xff))
+ *value = (u32) data;
+
+ return result;
+}
static int
-pci_conf_read_config_byte(struct pci_dev *dev, int where, u8 *value)
+pci_sal_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
{
- s64 status;
- u64 lval;
+ if ((seg > 255) || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
+
+ return ia64_sal_pci_config_write(PCI_SAL_ADDRESS(seg, bus, dev, fn, reg), len, value);
+}
+
+
+static int
+pci_sal_read_config_byte (struct pci_dev *dev, int where, u8 *value)
+{
+ int result = 0;
+ u32 data = 0;
+
+ if (!value)
+ return -EINVAL;
- status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 1, &lval);
- *value = lval;
- return status;
+ result = pci_sal_read(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 1, &data);
+
+ *value = (u8) data;
+
+ return result;
}
static int
-pci_conf_read_config_word(struct pci_dev *dev, int where, u16 *value)
+pci_sal_read_config_word (struct pci_dev *dev, int where, u16 *value)
{
- s64 status;
- u64 lval;
+ int result = 0;
+ u32 data = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ result = pci_sal_read(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 2, &data);
+
+ *value = (u16) data;
- status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 2, &lval);
- *value = lval;
- return status;
+ return result;
}
static int
-pci_conf_read_config_dword(struct pci_dev *dev, int where, u32 *value)
+pci_sal_read_config_dword (struct pci_dev *dev, int where, u32 *value)
{
- s64 status;
- u64 lval;
+ if (!value)
+ return -EINVAL;
- status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 4, &lval);
- *value = lval;
- return status;
+ return pci_sal_read(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 4, value);
}
static int
-pci_conf_write_config_byte (struct pci_dev *dev, int where, u8 value)
+pci_sal_write_config_byte (struct pci_dev *dev, int where, u8 value)
{
- return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 1, value);
+ return pci_sal_write(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 1, value);
}
static int
-pci_conf_write_config_word (struct pci_dev *dev, int where, u16 value)
+pci_sal_write_config_word (struct pci_dev *dev, int where, u16 value)
{
- return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 2, value);
+ return pci_sal_write(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 2, value);
}
static int
-pci_conf_write_config_dword (struct pci_dev *dev, int where, u32 value)
+pci_sal_write_config_dword (struct pci_dev *dev, int where, u32 value)
{
- return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 4, value);
+ return pci_sal_write(PCI_SEGMENT(dev), dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), where, 4, value);
}
-struct pci_ops pci_conf = {
- pci_conf_read_config_byte,
- pci_conf_read_config_word,
- pci_conf_read_config_dword,
- pci_conf_write_config_byte,
- pci_conf_write_config_word,
- pci_conf_write_config_dword
+struct pci_ops pci_sal_ops = {
+ pci_sal_read_config_byte,
+ pci_sal_read_config_word,
+ pci_sal_read_config_dword,
+ pci_sal_write_config_byte,
+ pci_sal_write_config_word,
+ pci_sal_write_config_dword
};
+
/*
* Initialization. Uses the SAL interface
*/
+
+static struct pci_controller *
+alloc_pci_controller(int seg)
+{
+ struct pci_controller *controller;
+
+ controller = kmalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return NULL;
+
+ memset(controller, 0, sizeof(*controller));
+ controller->segment = seg;
+ return controller;
+}
+
+static struct pci_bus *
+scan_root_bus(int bus, struct pci_ops *ops, void *sysdata)
+{
+ struct pci_bus *b;
+
+ /*
+ * We know this is a new root bus we haven't seen before, so
+ * scan it, even if we've seen the same bus number in a different
+ * segment.
+ */
+ b = kmalloc(sizeof(*b), GFP_KERNEL);
+ if (!b)
+ return NULL;
+
+ memset(b, 0, sizeof(*b));
+ INIT_LIST_HEAD(&b->children);
+ INIT_LIST_HEAD(&b->devices);
+
+ list_add_tail(&b->node, &pci_root_buses);
+
+ b->number = b->secondary = bus;
+ b->resource[0] = &ioport_resource;
+ b->resource[1] = &iomem_resource;
+
+ b->sysdata = sysdata;
+ b->ops = ops;
+ b->subordinate = pci_do_scan_bus(b);
+
+ return b;
+}
+
+struct pci_bus *
+pcibios_scan_root(void *handle, int seg, int bus)
+{
+ struct pci_controller *controller;
+ u64 base, size, offset;
+
+ printk("PCI: Probing PCI hardware on bus (%02x:%02x)\n", seg, bus);
+
+ controller = alloc_pci_controller(seg);
+ if (!controller)
+ return NULL;
+
+ controller->acpi_handle = handle;
+
+ acpi_get_addr_space(handle, ACPI_MEMORY_RANGE, &base, &size, &offset);
+ controller->mem_offset = offset;
+
+ return scan_root_bus(bus, pci_root_ops, controller);
+}
+
+void __init
+pcibios_config_init (void)
+{
+ if (pci_root_ops)
+ return;
+
+ printk("PCI: Using SAL to access configuration space\n");
+
+ pci_root_ops = &pci_sal_ops;
+ pci_config_read = pci_sal_read;
+ pci_config_write = pci_sal_write;
+
+ return;
+}
+
void __init
pcibios_init (void)
{
# define PCI_BUSES_TO_SCAN 255
- int i;
+ int i = 0;
+ struct pci_controller *controller;
#ifdef CONFIG_IA64_MCA
ia64_mca_check_errors(); /* For post-failure MCA error logging */
#endif
- platform_pci_fixup(0); /* phase 0 initialization (before PCI bus has been scanned) */
+ pcibios_config_init();
+
+ platform_pci_fixup(0); /* phase 0 fixups (before buses scanned) */
printk("PCI: Probing PCI hardware\n");
- for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
- pci_scan_bus(i, &pci_conf, NULL);
+ controller = alloc_pci_controller(0);
+ if (controller)
+ for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
+ pci_scan_bus(i, pci_root_ops, controller);
+
+ platform_pci_fixup(1); /* phase 1 fixups (after buses scanned) */
- platform_pci_fixup(1); /* phase 1 initialization (after PCI bus has been scanned) */
return;
}
+static void __init
+pcibios_fixup_resource(struct resource *res, u64 offset)
+{
+ res->start += offset;
+ res->end += offset;
+}
+
+void __init
+pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ if (!dev->resource[i].start)
+ continue;
+ if (dev->resource[i].flags & IORESOURCE_MEM)
+ pcibios_fixup_resource(&dev->resource[i],
+ PCI_CONTROLLER(dev)->mem_offset);
+ }
+}
+
/*
* Called after each bus is probed, but before its children
* are examined.
@@ -147,7 +300,10 @@
void __init
pcibios_fixup_bus (struct pci_bus *b)
{
- return;
+ struct list_head *ln;
+
+ for (ln = b->devices.next; ln != &b->devices; ln = ln->next)
+ pcibios_fixup_device_resources(pci_dev_b(ln), b);
}
void __init
@@ -186,12 +342,45 @@
int
pcibios_enable_device (struct pci_dev *dev)
{
- /* Not needed, since we enable all devices at startup. */
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ if (!dev)
+ return -EINVAL;
+
+ platform_pci_enable_device(dev);
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx=0; idx<6; idx++) {
+ r = &dev->resource[idx];
+ if (!r->start && r->end) {
+ printk(KERN_ERR
+ "PCI: Device %s not available because of resource collisions\n",
+ dev->slot_name);
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ cmd |= PCI_COMMAND_MEMORY;
+ if (cmd != old_cmd) {
+ printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+
+ printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", dev->irq, dev->slot_name);
+
return 0;
}
void
-pcibios_align_resource (void *data, struct resource *res, unsigned long size)
+pcibios_align_resource (void *data, struct resource *res,
+ unsigned long size, unsigned long align)
{
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)