patch-2.4.20 linux-2.4.20/drivers/hotplug/acpiphp_pci.c

Next file: linux-2.4.20/drivers/hotplug/acpiphp_res.c
Previous file: linux-2.4.20/drivers/hotplug/acpiphp_glue.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/hotplug/acpiphp_pci.c linux-2.4.20/drivers/hotplug/acpiphp_pci.c
@@ -0,0 +1,763 @@
+/*
+ * ACPI PCI HotPlug PCI configuration space management
+ *
+ * Copyright (c) 1995,2001 Compaq Computer Corporation
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com)
+ * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (c) 2002 NEC Corporation
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <t-kouchi@cq.jp.nec.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+#include "acpiphp.h"
+
+static int debug = 1;			/* XXX set 0 after debug */
+#define MY_NAME "acpiphp_pci"
+
+static void acpiphp_configure_irq (struct pci_dev *dev);
+
+
+/* allocate mem/pmem/io resource to a new function */
+static int alloc_resource (struct acpiphp_func *func)
+{
+	u64 base;
+	u32 bar, len;
+	u32 address[] = {
+		PCI_BASE_ADDRESS_0,
+		PCI_BASE_ADDRESS_1,
+		PCI_BASE_ADDRESS_2,
+		PCI_BASE_ADDRESS_3,
+		PCI_BASE_ADDRESS_4,
+		PCI_BASE_ADDRESS_5,
+		0
+	};
+	int count;
+	struct acpiphp_bridge *bridge;
+	struct pci_resource *res;
+	struct pci_ops *ops;
+	int bus, device, function;
+
+	bridge = func->slot->bridge;
+	bus = bridge->bus;
+	device = func->slot->device;
+	function = func->function;
+	ops = bridge->pci_ops;
+
+	for (count = 0; address[count]; count++) {	/* for 6 BARs */
+		pci_write_config_dword_nodev (ops, bus, device, function, address[count], 0xFFFFFFFF);
+		pci_read_config_dword_nodev(ops, bus, device, function, address[count], &bar);
+
+		if (!bar)	/* This BAR is not implemented */
+			continue;
+
+		dbg("Device %02x.%02x BAR %d wants %x", device, function, count, bar);
+
+		if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+
+			len = bar & 0xFFFFFFFC;
+			len = ~len + 1;
+
+			dbg ("len in IO %x, BAR %d", len, count);
+
+			spin_lock(&bridge->res_lock);
+			res = acpiphp_get_io_resource(&bridge->io_head, len);
+			spin_unlock(&bridge->res_lock);
+
+			if (!res) {
+				err("cannot allocate requested io for %02x:%02x.%d len %x\n",
+				    bus, device, function, len);
+				return -1;
+			}
+			pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)res->base);
+			res->next = func->io_head;
+			func->io_head = res;
+
+		} else {
+			/* This is Memory */
+			if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+
+				len = bar & 0xFFFFFFF0;
+				len = ~len + 1;
+
+				dbg("len in PFMEM %x, BAR %d", len, count);
+
+				spin_lock(&bridge->res_lock);
+				res = acpiphp_get_resource(&bridge->p_mem_head, len);
+				spin_unlock(&bridge->res_lock);
+
+				if (!res) {
+					err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
+					    bus, device, function, len);
+					return -1;
+				}
+
+				pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)res->base);
+
+				if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {	/* takes up another dword */
+					dbg ("inside the pfmem 64 case, count %d", count);
+					count += 1;
+					pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)(res->base >> 32));
+				}
+
+				res->next = func->p_mem_head;
+				func->p_mem_head = res;
+
+			} else {
+				/* regular memory */
+
+				len = bar & 0xFFFFFFF0;
+				len = ~len + 1;
+
+				dbg("len in MEM %x, BAR %d", len, count);
+
+				spin_lock(&bridge->res_lock);
+				res = acpiphp_get_resource(&bridge->mem_head, len);
+				spin_unlock(&bridge->res_lock);
+
+				if (!res) {
+					err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
+					    bus, device, function, len);
+					return -1;
+				}
+
+				pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)res->base);
+
+				if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					dbg ("inside mem 64 case, reg. mem, count %d", count);
+					count += 1;
+					pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)(res->base >> 32));
+				}
+
+				res->next = func->mem_head;
+				func->mem_head = res;
+
+			}
+		}
+	}
+
+	/* disable expansion rom */
+	pci_write_config_dword_nodev(ops, bus, device, function, PCI_ROM_ADDRESS, 0x00000000);
+
+	return 0;
+}
+
+
+/* enable pci_dev */
+static int configure_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+	u16 tmp;
+	struct acpiphp_func *func;
+	struct acpiphp_bridge *bridge;
+	struct pci_dev *dev;
+
+	func = (struct acpiphp_func *)wrapped_dev->data;
+	bridge = (struct acpiphp_bridge *)wrapped_bus->data;
+	dev = wrapped_dev->dev;
+
+	/* TBD: support PCI-to-PCI bridge case */
+	if (!func || !bridge)
+		return 0;
+
+	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, bridge->hpp.cache_line_size);
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, bridge->hpp.latency_timer);
+
+	pci_read_config_word(dev, PCI_COMMAND, &tmp);
+	if (bridge->hpp.enable_SERR)
+		tmp |= PCI_COMMAND_SERR;
+	if (bridge->hpp.enable_PERR)
+		tmp |= PCI_COMMAND_PARITY;
+	//tmp |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	pci_write_config_word(dev, PCI_COMMAND, tmp);
+
+	acpiphp_configure_irq(dev);
+#ifdef CONFIG_PROC_FS
+	pci_proc_attach_device(dev);
+#endif
+	pci_announce_device_to_drivers(dev);
+
+	return 0;
+}
+
+
+static int is_pci_dev_in_use (struct pci_dev* dev) 
+{
+	/* 
+	 * dev->driver will be set if the device is in use by a new-style 
+	 * driver -- otherwise, check the device's regions to see if any
+	 * driver has claimed them
+	 */
+
+	int i, inuse=0;
+
+	if (dev->driver) return 1; //assume driver feels responsible
+
+	for (i = 0; !dev->driver && !inuse && (i < 6); i++) {
+		if (!pci_resource_start(dev, i))
+			continue;
+
+		if (pci_resource_flags(dev, i) & IORESOURCE_IO)
+			inuse = check_region(pci_resource_start(dev, i),
+					     pci_resource_len(dev, i));
+		else if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
+			inuse = check_mem_region(pci_resource_start(dev, i),
+						 pci_resource_len(dev, i));
+	}
+
+	return inuse;
+}
+
+
+static int pci_hp_remove_device (struct pci_dev *dev)
+{
+	if (is_pci_dev_in_use(dev)) {
+		err("***Cannot safely power down device -- "
+		       "it appears to be in use***\n");
+		return -EBUSY;
+	}
+	pci_remove_device(dev);
+	return 0;
+}
+
+
+/* remove device driver */
+static int unconfigure_pci_dev_driver (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+	struct pci_dev *dev = wrapped_dev->dev;
+
+	dbg("attempting removal of driver for device %s", dev->slot_name);
+
+	/* Now, remove the Linux Driver Representation */
+	if (dev->driver) {
+		if (dev->driver->remove) {
+			dev->driver->remove(dev);
+			dbg("driver was properly removed");
+		}
+		dev->driver = NULL;
+	}
+
+	return is_pci_dev_in_use(dev);
+}
+
+
+/* remove pci_dev itself from system */
+static int unconfigure_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+	struct pci_dev *dev = wrapped_dev->dev;
+
+	/* Now, remove the Linux Representation */
+	if (dev) {
+		if (pci_hp_remove_device(dev) == 0) {
+			kfree(dev); /* Now, remove */
+		} else {
+			return -1; /* problems while freeing, abort visitation */
+		}
+	}
+
+	return 0;
+}
+
+
+/* remove pci_bus itself from system */
+static int unconfigure_pci_bus (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev)
+{
+	struct pci_bus *bus = wrapped_bus->bus;
+
+#ifdef CONFIG_PROC_FS
+	/* Now, remove the Linux Representation */
+	if (bus->procdir) {
+		pci_proc_detach_bus(bus);
+	}
+#endif
+	/* the cleanup code should live in the kernel ... */
+	bus->self->subordinate = NULL;
+	/* unlink from parent bus */
+	list_del(&bus->node);
+
+	/* Now, remove */
+	if (bus)
+		kfree(bus);
+
+	return 0;
+}
+
+
+/* detect_used_resource - subtract resource under dev from bridge */
+static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev)
+{
+	u32 bar, len, pin;
+	u64 base;
+	u32 address[] = {
+		PCI_BASE_ADDRESS_0,
+		PCI_BASE_ADDRESS_1,
+		PCI_BASE_ADDRESS_2,
+		PCI_BASE_ADDRESS_3,
+		PCI_BASE_ADDRESS_4,
+		PCI_BASE_ADDRESS_5,
+		0
+	};
+	int count;
+	struct pci_resource *res;
+
+	dbg("Device %s", dev->slot_name);
+
+	for (count = 0; address[count]; count++) {	/* for 6 BARs */
+		pci_read_config_dword(dev, address[count], &bar);
+
+		if (!bar)	/* This BAR is not implemented */
+			continue;
+
+		pci_write_config_dword(dev, address[count], 0xFFFFFFFF);
+		pci_read_config_dword(dev, address[count], &len);
+
+		if (len & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			base = bar & 0xFFFFFFFC;
+			len &= 0xFFFFFFFC;
+			len = ~len + 1;
+
+			dbg("BAR[%d] %08x - %08x (IO)", count, (u32)base, (u32)base + len - 1);
+
+			spin_lock(&bridge->res_lock);
+			res = acpiphp_get_resource_with_base(&bridge->io_head, base, len);
+			spin_unlock(&bridge->res_lock);
+			if (res)
+				kfree(res);
+		} else {
+			/* This is Memory */
+			base = bar & 0xFFFFFFF0;
+			if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+
+				len &= 0xFFFFFFF0;
+				len = ~len + 1;
+
+				if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {	/* takes up another dword */
+					dbg ("prefetch mem 64");
+					count += 1;
+				}
+				dbg("BAR[%d] %08x - %08x (PMEM)", count, (u32)base, (u32)base + len - 1);
+				spin_lock(&bridge->res_lock);
+				res = acpiphp_get_resource_with_base(&bridge->p_mem_head, base, len);
+				spin_unlock(&bridge->res_lock);
+				if (res)
+					kfree(res);
+			} else {
+				/* regular memory */
+
+				len &= 0xFFFFFFF0;
+				len = ~len + 1;
+
+				if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					dbg ("mem 64");
+					count += 1;
+				}
+				dbg("BAR[%d] %08x - %08x (MEM)", count, (u32)base, (u32)base + len - 1);
+				spin_lock(&bridge->res_lock);
+				res = acpiphp_get_resource_with_base(&bridge->mem_head, base, len);
+				spin_unlock(&bridge->res_lock);
+				if (res)
+					kfree(res);
+			}
+		}
+
+		pci_write_config_dword(dev, address[count], bar);
+	}
+
+	return 0;
+}
+
+
+/* detect_pci_resource_bus - subtract resource under pci_bus */
+static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_bus *bus)
+{
+	struct list_head *l;
+	struct pci_dev *dev;
+
+	list_for_each(l, &bus->devices) {
+		dev = pci_dev_b(l);
+		detect_used_resource(bridge, dev);
+		/* XXX recursive call */
+		if (dev->subordinate)
+			detect_used_resource_bus(bridge, dev->subordinate);
+	}
+}
+
+
+/**
+ * acpiphp_detect_pci_resource - detect resources under bridge
+ * @bridge: detect all resources already used under this bridge
+ *
+ * collect all resources already allocated for all devices under a bridge.
+ */
+int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge)
+{
+	struct list_head *l;
+	struct pci_dev *dev;
+
+	detect_used_resource_bus(bridge, bridge->pci_bus);
+
+	return 0;
+}
+
+
+/**
+ * acpiphp_init_slot_resource - gather resource usage information of a slot
+ * @slot: ACPI slot object to be checked, should have valid pci_dev member
+ *
+ * TBD: PCI-to-PCI bridge case
+ *      use pci_dev->resource[]
+ */
+int acpiphp_init_func_resource (struct acpiphp_func *func)
+{
+	u64 base;
+	u32 bar, len;
+	u32 address[] = {
+		PCI_BASE_ADDRESS_0,
+		PCI_BASE_ADDRESS_1,
+		PCI_BASE_ADDRESS_2,
+		PCI_BASE_ADDRESS_3,
+		PCI_BASE_ADDRESS_4,
+		PCI_BASE_ADDRESS_5,
+		0
+	};
+	int count;
+	struct pci_resource *res;
+	struct pci_ops *ops;
+	struct pci_dev *dev;
+
+	dev = func->pci_dev;
+	dbg("Hot-pluggable device %s", dev->slot_name);
+
+	for (count = 0; address[count]; count++) {	/* for 6 BARs */
+		pci_read_config_dword (dev, address[count], &bar);
+
+		if (!bar)	/* This BAR is not implemented */
+			continue;
+
+		pci_write_config_dword (dev, address[count], 0xFFFFFFFF);
+		pci_read_config_dword (dev, address[count], &len);
+
+		if (len & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			base = bar & 0xFFFFFFFC;
+			len &= 0xFFFFFFFC;
+			len = ~len + 1;
+
+			dbg("BAR[%d] %08x - %08x (IO)", count, (u32)base, (u32)base + len - 1);
+
+			res = acpiphp_make_resource(base, len);
+			if (!res)
+				goto no_memory;
+
+			res->next = func->io_head;
+			func->io_head = res;
+
+		} else {
+			/* This is Memory */
+			base = bar & 0xFFFFFFF0;
+			if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+
+				len &= 0xFFFFFFF0;
+				len = ~len + 1;
+
+				if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {	/* takes up another dword */
+					dbg ("prefetch mem 64");
+					count += 1;
+				}
+				dbg("BAR[%d] %08x - %08x (PMEM)", count, (u32)base, (u32)base + len - 1);
+				res = acpiphp_make_resource(base, len);
+				if (!res)
+					goto no_memory;
+
+				res->next = func->p_mem_head;
+				func->p_mem_head = res;
+
+			} else {
+				/* regular memory */
+
+				len &= 0xFFFFFFF0;
+				len = ~len + 1;
+
+				if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					dbg ("mem 64");
+					count += 1;
+				}
+				dbg("BAR[%d] %08x - %08x (MEM)", count, (u32)base, (u32)base + len - 1);
+				res = acpiphp_make_resource(base, len);
+				if (!res)
+					goto no_memory;
+
+				res->next = func->mem_head;
+				func->mem_head = res;
+
+			}
+		}
+
+		pci_write_config_dword (dev, address[count], bar);
+	}
+#if 1
+	acpiphp_dump_func_resource(func);
+#endif
+
+	return 0;
+
+ no_memory:
+	err("out of memory");
+	acpiphp_free_resource(&func->io_head);
+	acpiphp_free_resource(&func->mem_head);
+	acpiphp_free_resource(&func->p_mem_head);
+
+	return -1;
+}
+
+
+/**
+ * acpiphp_configure_slot - allocate PCI resources
+ * @slot: slot to be configured
+ *
+ * initializes a PCI functions on a device inserted
+ * into the slot
+ *
+ */
+int acpiphp_configure_slot (struct acpiphp_slot *slot)
+{
+	struct acpiphp_func *func;
+	struct list_head *l;
+	u8 hdr;
+	u32 dvid;
+	int retval = 0;
+	int is_multi = 0;
+
+	pci_read_config_byte_nodev(slot->bridge->pci_ops,
+				   slot->bridge->bus, slot->device, 0,
+				   PCI_HEADER_TYPE, &hdr);
+
+	if (hdr & 0x80)
+		is_multi = 1;
+
+	list_for_each(l, &slot->funcs) {
+		func = list_entry(l, struct acpiphp_func, sibling);
+		if (is_multi || func->function == 0) {
+			pci_read_config_dword_nodev(slot->bridge->pci_ops,
+						    slot->bridge->bus,
+						    slot->device,
+						    func->function,
+						    PCI_VENDOR_ID, &dvid);
+			if (dvid != 0xffffffff) {
+				retval = alloc_resource(func);
+				if (retval)
+					break;
+			}
+		}
+	}
+
+	return retval;
+}
+
+
+/* for pci_visit_dev() */
+static struct pci_visit configure_functions = {
+	post_visit_pci_dev:	configure_pci_dev
+};
+
+static struct pci_visit unconfigure_functions_phase1 = {
+	post_visit_pci_dev:	unconfigure_pci_dev_driver
+};
+
+static struct pci_visit unconfigure_functions_phase2 = {
+	post_visit_pci_bus:	unconfigure_pci_bus,
+	post_visit_pci_dev:	unconfigure_pci_dev
+};
+
+
+/**
+ * acpiphp_configure_function - configure PCI function
+ * @func: function to be configured
+ *
+ * initializes a PCI functions on a device inserted
+ * into the slot
+ *
+ */
+int acpiphp_configure_function (struct acpiphp_func *func)
+{
+	int retval = 0;
+	struct pci_dev_wrapped wrapped_dev;
+	struct pci_bus_wrapped wrapped_bus;
+	struct acpiphp_bridge *bridge;
+
+	/* if pci_dev is NULL, ignore it */
+	if (!func->pci_dev)
+		goto err_exit;
+
+	bridge = func->slot->bridge;
+
+	memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+	memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+	wrapped_dev.dev = func->pci_dev;
+	wrapped_dev.data = func;
+	wrapped_bus.bus = bridge->pci_bus;
+	wrapped_bus.data = bridge;
+
+	retval = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus);
+	if (retval)
+		goto err_exit;
+
+ err_exit:
+	return retval;
+}
+
+
+/**
+ * acpiphp_unconfigure_function - unconfigure PCI function
+ * @func: function to be unconfigured
+ *
+ */
+int acpiphp_unconfigure_function (struct acpiphp_func *func)
+{
+	struct acpiphp_bridge *bridge;
+	struct pci_resource *tmp;
+	struct pci_dev_wrapped wrapped_dev;
+	struct pci_bus_wrapped wrapped_bus;
+	int retval = 0;
+
+	/* if pci_dev is NULL, ignore it */
+	if (!func->pci_dev)
+		goto err_exit;
+
+	memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+	memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+	wrapped_dev.dev = func->pci_dev;
+	//wrapped_dev.data = func;
+	wrapped_bus.bus = func->slot->bridge->pci_bus;
+	//wrapped_bus.data = func->slot->bridge;
+
+	retval = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus);
+	if (retval)
+		goto err_exit;
+
+	retval = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus);
+	if (retval)
+		goto err_exit;
+
+	/* free all resources */
+	bridge = func->slot->bridge;
+
+	spin_lock(&bridge->res_lock);
+	acpiphp_move_resource(&func->io_head, &bridge->io_head);
+	acpiphp_move_resource(&func->mem_head, &bridge->mem_head);
+	acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head);
+	acpiphp_move_resource(&func->bus_head, &bridge->bus_head);
+	spin_unlock(&bridge->res_lock);
+
+ err_exit:
+	return retval;
+}
+
+
+/* XXX IA64 specific */
+#ifdef CONFIG_IA64
+static int ia64_get_irq(struct pci_dev *dev, int pin)
+{
+	extern int pci_pin_to_vector(int bus, int slot, int pci_pin);
+	int irq;
+
+	irq = pci_pin_to_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+
+	if (irq < 0 && dev->bus->parent) {
+		/* go back to the bridge */
+		struct pci_dev *bridge = dev->bus->self;
+
+		if (bridge) {
+			/* allow for multiple bridges on an adapter */
+			do {
+				/* do the bridge swizzle... */
+				pin = (pin + PCI_SLOT(dev->devfn)) % 4;
+				irq = pci_pin_to_vector(bridge->bus->number,
+							PCI_SLOT(bridge->devfn),
+							   pin);
+			} while (irq < 0 && (bridge = bridge->bus->self));
+		}
+		if (irq >= 0)
+			printk(KERN_WARNING
+			       "PCI: using PPB(B%d,I%d,P%d) to get vector %02x\n",
+			       dev->bus->number, PCI_SLOT(dev->devfn),
+			       pin, irq);
+		else
+			printk(KERN_WARNING
+			       "PCI: Couldn't map irq for (B%d,I%d,P%d)\n",
+			       dev->bus->number, PCI_SLOT(dev->devfn), pin);
+	}
+
+	return irq;
+}
+#endif
+
+
+/*
+ * acpiphp_configure_irq - configure PCI_INTERRUPT_PIN
+ *
+ * for x86 platforms, pcibios_enable_device calls pcibios_enable_irq,
+ * which allocates irq for pci_dev
+ *
+ * for IA64 platforms, we have to program dev->irq from pci IRQ routing
+ * information derived from ACPI table
+ *
+ * TBD:
+ * separate architecture dependent part
+ * (preferably, pci_enable_device() cares for allocating irq...)
+ */
+static void acpiphp_configure_irq (struct pci_dev *dev)
+{
+#if CONFIG_IA64		    /* XXX IA64 specific, need i386 version */
+	int bus, device, function, irq;
+	u8 tmp;
+
+	bus = dev->bus->number;
+	device = PCI_SLOT(dev->devfn);
+	function = PCI_FUNC(dev->devfn);
+
+	pci_read_config_byte (dev, PCI_INTERRUPT_PIN, &tmp);
+
+	if ((tmp > 0x00) && (tmp < 0x05)) {
+		irq = ia64_get_irq(dev, tmp - 1);
+		if (irq > 0) {
+			dev->irq = irq;
+			pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq);
+		} else {
+			err("Couldn't get IRQ for INT%c", 'A' + tmp - 1);
+		}
+	}
+#endif
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)