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

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)