File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
new file mode 100644
index 0000000..df8caec
--- /dev/null
+++ b/drivers/pci/hotplug/Kconfig
@@ -0,0 +1,159 @@
+#
+# PCI Hotplug support
+#
+
+menuconfig HOTPLUG_PCI
+	bool "Support for PCI Hotplug"
+	depends on PCI && SYSFS
+	---help---
+	  Say Y here if you have a motherboard with a PCI Hotplug controller.
+	  This allows you to add and remove PCI cards while the machine is
+	  powered up and running.
+
+	  When in doubt, say N.
+
+if HOTPLUG_PCI
+
+config HOTPLUG_PCI_COMPAQ
+	tristate "Compaq PCI Hotplug driver"
+	depends on X86 && PCI_BIOS
+	help
+	  Say Y here if you have a motherboard with a Compaq PCI Hotplug
+	  controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cpqphp.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_COMPAQ_NVRAM
+	bool "Save configuration into NVRAM on Compaq servers"
+	depends on HOTPLUG_PCI_COMPAQ
+	help
+	  Say Y here if you have a Compaq server that has a PCI Hotplug
+	  controller.  This will allow the PCI Hotplug driver to store the PCI
+	  system configuration options in NVRAM.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_IBM
+	tristate "IBM PCI Hotplug driver"
+	depends on X86_IO_APIC && X86 && PCI_BIOS
+	help
+	  Say Y here if you have a motherboard with a IBM PCI Hotplug
+	  controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ibmphp.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_ACPI
+	bool "ACPI PCI Hotplug driver"
+	depends on HOTPLUG_PCI=y && ((!ACPI_DOCK && ACPI) || (ACPI_DOCK))
+	help
+	  Say Y here if you have a system that supports PCI Hotplug using
+	  ACPI.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_ACPI_IBM
+	tristate "ACPI PCI Hotplug driver IBM extensions"
+	depends on HOTPLUG_PCI_ACPI
+	help
+	  Say Y here if you have an IBM system that supports PCI Hotplug using
+	  ACPI.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called acpiphp_ibm.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI
+	bool "CompactPCI Hotplug driver"
+	help
+	  Say Y here if you have a CompactPCI system card with CompactPCI
+	  hotswap support per the PICMG 2.1 specification.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI_ZT5550
+	tristate "Ziatech ZT5550 CompactPCI Hotplug driver"
+	depends on HOTPLUG_PCI_CPCI && X86
+	help
+	  Say Y here if you have an Performance Technologies (formerly Intel,
+          formerly just Ziatech) Ziatech ZT5550 CompactPCI system card.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cpcihp_zt5550.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI_GENERIC
+	tristate "Generic port I/O CompactPCI Hotplug driver"
+	depends on HOTPLUG_PCI_CPCI && X86
+	help
+	  Say Y here if you have a CompactPCI system card that exposes the #ENUM
+	  hotswap signal as a bit in a system register that can be read through
+	  standard port I/O.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cpcihp_generic.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_SHPC
+	tristate "SHPC PCI Hotplug driver"
+	help
+	  Say Y here if you have a motherboard with a SHPC PCI Hotplug
+	  controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called shpchp.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_RPA
+	tristate "RPA PCI Hotplug driver"
+	depends on PPC_PSERIES && EEH
+	help
+	  Say Y here if you have a RPA system that supports PCI Hotplug.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpaphp.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_RPA_DLPAR
+	tristate "RPA Dynamic Logical Partitioning for I/O slots"
+	depends on HOTPLUG_PCI_RPA
+	help
+	  Say Y here if your system supports Dynamic Logical Partitioning
+	  for I/O slots.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpadlpar_io.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_SGI
+	tristate "SGI PCI Hotplug Support"
+	depends on IA64_SGI_SN2 || IA64_GENERIC
+	help
+	  Say Y here if you want to use the SGI Altix Hotplug
+	  Driver for PCI devices.
+
+	  When in doubt, say N.
+
+config HOTPLUG_PCI_S390
+	bool "System z PCI Hotplug Support"
+	depends on S390 && 64BIT
+	help
+	  Say Y here if you want to use the System z PCI Hotplug
+	  driver for PCI devices. Without this driver it is not
+	  possible to access stand-by PCI functions nor to deconfigure
+	  PCI functions.
+
+	  When in doubt, say Y.
+
+endif # HOTPLUG_PCI
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
new file mode 100644
index 0000000..b616e75
--- /dev/null
+++ b/drivers/pci/hotplug/Makefile
@@ -0,0 +1,69 @@
+#
+# Makefile for the Linux kernel pci hotplug controller drivers.
+#
+
+obj-$(CONFIG_HOTPLUG_PCI)		+= pci_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_COMPAQ)	+= cpqphp.o
+obj-$(CONFIG_HOTPLUG_PCI_IBM)		+= ibmphp.o
+
+# native drivers should be linked before acpiphp in order to allow the
+# native driver to attempt to bind first. We can then fall back to
+# generic support.
+
+obj-$(CONFIG_HOTPLUG_PCI_PCIE)		+= pciehp.o
+obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
+obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
+obj-$(CONFIG_HOTPLUG_PCI_SHPC)		+= shpchp.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
+obj-$(CONFIG_HOTPLUG_PCI_SGI)		+= sgi_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_S390)		+= s390_pci_hpc.o
+
+# acpiphp_ibm extends acpiphp, so should be linked afterwards.
+
+obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM)	+= acpiphp_ibm.o
+
+pci_hotplug-objs	:=	pci_hotplug_core.o
+
+ifdef CONFIG_HOTPLUG_PCI_CPCI
+pci_hotplug-objs	+=	cpci_hotplug_core.o	\
+				cpci_hotplug_pci.o
+endif
+ifdef CONFIG_ACPI
+pci_hotplug-objs	+=	acpi_pcihp.o
+endif
+
+cpqphp-objs		:=	cpqphp_core.o	\
+				cpqphp_ctrl.o	\
+				cpqphp_sysfs.o	\
+				cpqphp_pci.o
+cpqphp-$(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM) += cpqphp_nvram.o
+cpqphp-objs += $(cpqphp-y)
+
+ibmphp-objs		:=	ibmphp_core.o	\
+				ibmphp_ebda.o	\
+				ibmphp_pci.o	\
+				ibmphp_res.o	\
+				ibmphp_hpc.o
+
+acpiphp-objs		:=	acpiphp_core.o	\
+				acpiphp_glue.o
+
+rpaphp-objs		:=	rpaphp_core.o	\
+				rpaphp_pci.o	\
+				rpaphp_slot.o
+
+rpadlpar_io-objs	:=	rpadlpar_core.o \
+				rpadlpar_sysfs.o
+
+pciehp-objs		:=	pciehp_core.o	\
+				pciehp_ctrl.o	\
+				pciehp_pci.o	\
+				pciehp_hpc.o
+
+shpchp-objs		:=	shpchp_core.o	\
+				shpchp_ctrl.o	\
+				shpchp_pci.o	\
+				shpchp_sysfs.o	\
+				shpchp_hpc.o
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
new file mode 100644
index 0000000..876ccc6
--- /dev/null
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -0,0 +1,227 @@
+/*
+ * Common ACPI functions for hot plug platforms
+ *
+ * Copyright (C) 2006 Intel 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 <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/slab.h>
+
+#define MY_NAME	"acpi_pcihp"
+
+#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
+
+#define	METHOD_NAME__SUN	"_SUN"
+#define	METHOD_NAME_OSHP	"OSHP"
+
+static bool debug_acpi;
+
+/* acpi_run_oshp - get control of hotplug from the firmware
+ *
+ * @handle - the handle of the hotplug controller.
+ */
+static acpi_status acpi_run_oshp(acpi_handle handle)
+{
+	acpi_status		status;
+	struct acpi_buffer	string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+	acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+
+	/* run OSHP */
+	status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
+	if (ACPI_FAILURE(status))
+		if (status != AE_NOT_FOUND)
+			printk(KERN_ERR "%s:%s OSHP fails=0x%x\n",
+			       __func__, (char *)string.pointer, status);
+		else
+			dbg("%s:%s OSHP not found\n",
+			    __func__, (char *)string.pointer);
+	else
+		pr_debug("%s:%s OSHP passes\n", __func__,
+			(char *)string.pointer);
+
+	kfree(string.pointer);
+	return status;
+}
+
+/**
+ * acpi_get_hp_hw_control_from_firmware
+ * @dev: the pci_dev of the bridge that has a hotplug controller
+ * @flags: requested control bits for _OSC
+ *
+ * Attempt to take hotplug control from firmware.
+ */
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
+{
+	acpi_status status;
+	acpi_handle chandle, handle;
+	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+	flags &= OSC_PCI_SHPC_NATIVE_HP_CONTROL;
+	if (!flags) {
+		err("Invalid flags %u specified!\n", flags);
+		return -EINVAL;
+	}
+
+	/*
+	 * Per PCI firmware specification, we should run the ACPI _OSC
+	 * method to get control of hotplug hardware before using it. If
+	 * an _OSC is missing, we look for an OSHP to do the same thing.
+	 * To handle different BIOS behavior, we look for _OSC on a root
+	 * bridge preferentially (according to PCI fw spec). Later for
+	 * OSHP within the scope of the hotplug controller and its parents,
+	 * up to the host bridge under which this controller exists.
+	 */
+	handle = acpi_find_root_bridge_handle(pdev);
+	if (handle) {
+		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+		dbg("Trying to get hotplug control for %s\n",
+				(char *)string.pointer);
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
+		if (ACPI_SUCCESS(status))
+			goto got_one;
+		if (status == AE_SUPPORT)
+			goto no_control;
+		kfree(string.pointer);
+		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
+	}
+
+	handle = ACPI_HANDLE(&pdev->dev);
+	if (!handle) {
+		/*
+		 * This hotplug controller was not listed in the ACPI name
+		 * space at all. Try to get acpi handle of parent pci bus.
+		 */
+		struct pci_bus *pbus;
+		for (pbus = pdev->bus; pbus; pbus = pbus->parent) {
+			handle = acpi_pci_get_bridge_handle(pbus);
+			if (handle)
+				break;
+		}
+	}
+
+	while (handle) {
+		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+		dbg("Trying to get hotplug control for %s \n",
+		    (char *)string.pointer);
+		status = acpi_run_oshp(handle);
+		if (ACPI_SUCCESS(status))
+			goto got_one;
+		if (acpi_is_root_bridge(handle))
+			break;
+		chandle = handle;
+		status = acpi_get_parent(chandle, &handle);
+		if (ACPI_FAILURE(status))
+			break;
+	}
+no_control:
+	dbg("Cannot get control of hotplug hardware for pci %s\n",
+	    pci_name(pdev));
+	kfree(string.pointer);
+	return -ENODEV;
+got_one:
+	dbg("Gained control for hotplug HW for pci %s (%s)\n",
+	    pci_name(pdev), (char *)string.pointer);
+	kfree(string.pointer);
+	return 0;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
+
+static int pcihp_is_ejectable(acpi_handle handle)
+{
+	acpi_status status;
+	unsigned long long removable;
+	if (!acpi_has_method(handle, "_ADR"))
+		return 0;
+	if (acpi_has_method(handle, "_EJ0"))
+		return 1;
+	status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable);
+	if (ACPI_SUCCESS(status) && removable)
+		return 1;
+	return 0;
+}
+
+/**
+ * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot
+ * @pbus: the PCI bus of the PCI slot corresponding to 'handle'
+ * @handle: ACPI handle to check
+ *
+ * Return 1 if handle is ejectable PCI slot, 0 otherwise.
+ */
+int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle)
+{
+	acpi_handle bridge_handle, parent_handle;
+
+	bridge_handle = acpi_pci_get_bridge_handle(pbus);
+	if (!bridge_handle)
+		return 0;
+	if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))))
+		return 0;
+	if (bridge_handle != parent_handle)
+		return 0;
+	return pcihp_is_ejectable(handle);
+}
+EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable);
+
+static acpi_status
+check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *found = (int *)context;
+	if (pcihp_is_ejectable(handle)) {
+		*found = 1;
+		return AE_CTRL_TERMINATE;
+	}
+	return AE_OK;
+}
+
+/**
+ * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots
+ * @handle - handle of the PCI bus to scan
+ *
+ * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise.
+ */
+int acpi_pci_detect_ejectable(acpi_handle handle)
+{
+	int found = 0;
+
+	if (!handle)
+		return found;
+
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+			    check_hotplug, NULL, (void *)&found, NULL);
+	return found;
+}
+EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable);
+
+module_param(debug_acpi, bool, 0644);
+MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not");
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
new file mode 100644
index 0000000..b0e61bf
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -0,0 +1,202 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
+ * Copyright (C) 2003-2005 Hewlett Packard
+ *
+ * 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 <gregkh@us.ibm.com>,
+ *		    <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#ifndef _ACPIPHP_H
+#define _ACPIPHP_H
+
+#include <linux/acpi.h>
+#include <linux/mutex.h>
+#include <linux/pci_hotplug.h>
+
+struct acpiphp_context;
+struct acpiphp_bridge;
+struct acpiphp_slot;
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+	struct hotplug_slot	*hotplug_slot;
+	struct acpiphp_slot	*acpi_slot;
+	struct hotplug_slot_info info;
+	unsigned int sun;	/* ACPI _SUN (Slot User Number) value */
+};
+
+static inline const char *slot_name(struct slot *slot)
+{
+	return hotplug_slot_name(slot->hotplug_slot);
+}
+
+/*
+ * struct acpiphp_bridge - PCI bridge information
+ *
+ * for each bridge device in ACPI namespace
+ */
+struct acpiphp_bridge {
+	struct list_head list;
+	struct list_head slots;
+	struct kref ref;
+
+	struct acpiphp_context *context;
+
+	int nr_slots;
+
+	/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
+	struct pci_bus *pci_bus;
+
+	/* PCI-to-PCI bridge device */
+	struct pci_dev *pci_dev;
+
+	bool is_going_away;
+};
+
+
+/*
+ * struct acpiphp_slot - PCI slot information
+ *
+ * PCI slot information for each *physical* PCI slot
+ */
+struct acpiphp_slot {
+	struct list_head node;
+	struct pci_bus *bus;
+	struct list_head funcs;		/* one slot may have different
+					   objects (i.e. for each function) */
+	struct slot *slot;
+
+	u8		device;		/* pci device# */
+	u32		flags;		/* see below */
+};
+
+
+/*
+ * struct acpiphp_func - PCI function information
+ *
+ * PCI function information for each object in ACPI namespace
+ * typically 8 objects per slot (i.e. for each PCI function)
+ */
+struct acpiphp_func {
+	struct acpiphp_bridge *parent;
+	struct acpiphp_slot *slot;
+
+	struct list_head sibling;
+
+	u8		function;	/* pci function# */
+	u32		flags;		/* see below */
+};
+
+struct acpiphp_context {
+	struct acpi_hotplug_context hp;
+	struct acpiphp_func func;
+	struct acpiphp_bridge *bridge;
+	unsigned int refcount;
+};
+
+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+	return container_of(hp, struct acpiphp_context, hp);
+}
+
+static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
+{
+	return container_of(func, struct acpiphp_context, func);
+}
+
+static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
+{
+	return func_to_context(func)->hp.self;
+}
+
+static inline acpi_handle func_to_handle(struct acpiphp_func *func)
+{
+	return func_to_acpi_device(func)->handle;
+}
+
+struct acpiphp_root_context {
+	struct acpi_hotplug_context hp;
+	struct acpiphp_bridge *root_bridge;
+};
+
+static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_hotplug_context *hp)
+{
+	return container_of(hp, struct acpiphp_root_context, hp);
+}
+
+/*
+ * struct acpiphp_attention_info - device specific attention registration
+ *
+ * ACPI has no generic method of setting/getting attention status
+ * this allows for device specific driver registration
+ */
+struct acpiphp_attention_info
+{
+	int (*set_attn)(struct hotplug_slot *slot, u8 status);
+	int (*get_attn)(struct hotplug_slot *slot, u8 *status);
+	struct module *owner;
+};
+
+/* ACPI _STA method value (ignore bit 4; battery present) */
+#define ACPI_STA_ALL			(0x0000000f)
+
+/* slot flags */
+
+#define SLOT_ENABLED		(0x00000001)
+#define SLOT_IS_GOING_AWAY	(0x00000002)
+
+/* function flags */
+
+#define FUNC_HAS_STA		(0x00000001)
+#define FUNC_HAS_EJ0		(0x00000002)
+
+/* function prototypes */
+
+/* acpiphp_core.c */
+int acpiphp_register_attention(struct acpiphp_attention_info*info);
+int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
+int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
+void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
+
+/* acpiphp_glue.c */
+typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
+
+int acpiphp_enable_slot(struct acpiphp_slot *slot);
+int acpiphp_disable_slot(struct acpiphp_slot *slot);
+u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot);
+
+/* variables */
+extern bool acpiphp_disabled;
+
+#endif /* _ACPIPHP_H */
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
new file mode 100644
index 0000000..e291efc
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -0,0 +1,356 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
+ * Copyright (C) 2003-2005 Hewlett Packard
+ *
+ * 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 <kristen.c.accardi@intel.com>
+ *
+ */
+
+#define pr_fmt(fmt) "acpiphp: " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci_hotplug.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include "acpiphp.h"
+
+/* name size which is used for entries in pcihpfs */
+#define SLOT_NAME_SIZE  21              /* {_SUN} */
+
+bool acpiphp_disabled;
+
+/* local variables */
+static struct acpiphp_attention_info *attention_info;
+
+#define DRIVER_VERSION	"0.5"
+#define DRIVER_AUTHOR	"Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>"
+#define DRIVER_DESC	"ACPI Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(disable, "disable acpiphp driver");
+module_param_named(disable, acpiphp_disabled, bool, 0444);
+
+static int enable_slot		(struct hotplug_slot *slot);
+static int disable_slot		(struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int get_power_status	(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
+	.enable_slot		= enable_slot,
+	.disable_slot		= disable_slot,
+	.set_attention_status	= set_attention_status,
+	.get_power_status	= get_power_status,
+	.get_attention_status	= get_attention_status,
+	.get_latch_status	= get_latch_status,
+	.get_adapter_status	= get_adapter_status,
+};
+
+/**
+ * acpiphp_register_attention - set attention LED callback
+ * @info: must be completely filled with LED callbacks
+ *
+ * Description: This is used to register a hardware specific ACPI
+ * driver that manipulates the attention LED.  All the fields in
+ * info must be set.
+ */
+int acpiphp_register_attention(struct acpiphp_attention_info *info)
+{
+	int retval = -EINVAL;
+
+	if (info && info->owner && info->set_attn &&
+			info->get_attn && !attention_info) {
+		retval = 0;
+		attention_info = info;
+	}
+	return retval;
+}
+EXPORT_SYMBOL_GPL(acpiphp_register_attention);
+
+
+/**
+ * acpiphp_unregister_attention - unset attention LED callback
+ * @info: must match the pointer used to register
+ *
+ * Description: This is used to un-register a hardware specific acpi
+ * driver that manipulates the attention LED.  The pointer to the
+ * info struct must be the same as the one used to set it.
+ */
+int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
+{
+	int retval = -EINVAL;
+
+	if (info && attention_info == info) {
+		attention_info = NULL;
+		retval = 0;
+	}
+	return retval;
+}
+EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
+
+
+/**
+ * enable_slot - power on and enable a slot
+ * @hotplug_slot: slot to enable
+ *
+ * Actual tasks are done in acpiphp_enable_slot()
+ */
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	/* enable the specified slot */
+	return acpiphp_enable_slot(slot->acpi_slot);
+}
+
+
+/**
+ * disable_slot - disable and power off a slot
+ * @hotplug_slot: slot to disable
+ *
+ * Actual tasks are done in acpiphp_disable_slot()
+ */
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	/* disable the specified slot */
+	return acpiphp_disable_slot(slot->acpi_slot);
+}
+
+
+/**
+ * set_attention_status - set attention LED
+ * @hotplug_slot: slot to set attention LED on
+ * @status: value to set attention LED to (0 or 1)
+ *
+ * attention status LED, so we use a callback that
+ * was registered with us.  This allows hardware specific
+ * ACPI implementations to blink the light for us.
+ */
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+	int retval = -ENODEV;
+
+	pr_debug("%s - physical_slot = %s\n", __func__,
+		hotplug_slot_name(hotplug_slot));
+
+	if (attention_info && try_module_get(attention_info->owner)) {
+		retval = attention_info->set_attn(hotplug_slot, status);
+		module_put(attention_info->owner);
+	} else
+		attention_info = NULL;
+	return retval;
+}
+
+
+/**
+ * get_power_status - get power status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * Some platforms may not implement _STA method properly.
+ * In that case, the value returned may not be reliable.
+ */
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = acpiphp_get_power_status(slot->acpi_slot);
+
+	return 0;
+}
+
+
+/**
+ * get_attention_status - get attention LED status
+ * @hotplug_slot: slot to get status from
+ * @value: returns with value of attention LED
+ *
+ * ACPI doesn't have known method to determine the state
+ * of the attention status LED, so we use a callback that
+ * was registered with us.  This allows hardware specific
+ * ACPI implementations to determine its state.
+ */
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int retval = -EINVAL;
+
+	pr_debug("%s - physical_slot = %s\n", __func__,
+		hotplug_slot_name(hotplug_slot));
+
+	if (attention_info && try_module_get(attention_info->owner)) {
+		retval = attention_info->get_attn(hotplug_slot, value);
+		module_put(attention_info->owner);
+	} else
+		attention_info = NULL;
+	return retval;
+}
+
+
+/**
+ * get_latch_status - get latch status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * ACPI doesn't provide any formal means to access latch status.
+ * Instead, we fake latch status from _STA.
+ */
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = acpiphp_get_latch_status(slot->acpi_slot);
+
+	return 0;
+}
+
+
+/**
+ * get_adapter_status - get adapter status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * ACPI doesn't provide any formal means to access adapter status.
+ * Instead, we fake adapter status from _STA.
+ */
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = acpiphp_get_adapter_status(slot->acpi_slot);
+
+	return 0;
+}
+
+/**
+ * release_slot - free up the memory used by a slot
+ * @hotplug_slot: slot to free
+ */
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+/* callback routine to initialize 'struct slot' for each slot */
+int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
+				  unsigned int sun)
+{
+	struct slot *slot;
+	int retval = -ENOMEM;
+	char name[SLOT_NAME_SIZE];
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (!slot)
+		goto error;
+
+	slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
+	if (!slot->hotplug_slot)
+		goto error_slot;
+
+	slot->hotplug_slot->info = &slot->info;
+
+	slot->hotplug_slot->private = slot;
+	slot->hotplug_slot->release = &release_slot;
+	slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
+
+	slot->acpi_slot = acpiphp_slot;
+	slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
+	slot->hotplug_slot->info->attention_status = 0;
+	slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
+	slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
+
+	acpiphp_slot->slot = slot;
+	slot->sun = sun;
+	snprintf(name, SLOT_NAME_SIZE, "%u", sun);
+
+	retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
+				 acpiphp_slot->device, name);
+	if (retval == -EBUSY)
+		goto error_hpslot;
+	if (retval) {
+		pr_err("pci_hp_register failed with error %d\n", retval);
+		goto error_hpslot;
+	}
+
+	pr_info("Slot [%s] registered\n", slot_name(slot));
+
+	return 0;
+error_hpslot:
+	kfree(slot->hotplug_slot);
+error_slot:
+	kfree(slot);
+error:
+	return retval;
+}
+
+
+void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
+{
+	struct slot *slot = acpiphp_slot->slot;
+	int retval = 0;
+
+	pr_info("Slot [%s] unregistered\n", slot_name(slot));
+
+	retval = pci_hp_deregister(slot->hotplug_slot);
+	if (retval)
+		pr_err("pci_hp_deregister failed with error %d\n", retval);
+}
+
+
+void __init acpiphp_init(void)
+{
+	pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
+		acpiphp_disabled ? ", disabled by user; please report a bug"
+				 : "");
+}
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
new file mode 100644
index 0000000..0b3e0bf
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -0,0 +1,1037 @@
+/*
+ * ACPI PCI HotPlug glue functions to ACPI CA subsystem
+ *
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
+ * Copyright (C) 2003-2005 Hewlett Packard
+ * Copyright (C) 2005 Rajesh Shah (rajesh.shah@intel.com)
+ * Copyright (C) 2005 Intel 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 <kristen.c.accardi@intel.com>
+ *
+ */
+
+/*
+ * Lifetime rules for pci_dev:
+ *  - The one in acpiphp_bridge has its refcount elevated by pci_get_slot()
+ *    when the bridge is scanned and it loses a refcount when the bridge
+ *    is removed.
+ *  - When a P2P bridge is present, we elevate the refcount on the subordinate
+ *    bus. It loses the refcount when the the driver unloads.
+ */
+
+#define pr_fmt(fmt) "acpiphp_glue: " fmt
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#include "../pci.h"
+#include "acpiphp.h"
+
+static LIST_HEAD(bridge_list);
+static DEFINE_MUTEX(bridge_mutex);
+
+static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type);
+static void acpiphp_post_dock_fixup(struct acpi_device *adev);
+static void acpiphp_sanitize_bus(struct pci_bus *bus);
+static void hotplug_event(u32 type, struct acpiphp_context *context);
+static void free_bridge(struct kref *kref);
+
+/**
+ * acpiphp_init_context - Create hotplug context and grab a reference to it.
+ * @adev: ACPI device object to create the context for.
+ *
+ * Call under acpi_hp_context_lock.
+ */
+static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
+{
+	struct acpiphp_context *context;
+
+	context = kzalloc(sizeof(*context), GFP_KERNEL);
+	if (!context)
+		return NULL;
+
+	context->refcount = 1;
+	context->hp.notify = acpiphp_hotplug_notify;
+	context->hp.fixup = acpiphp_post_dock_fixup;
+	acpi_set_hp_context(adev, &context->hp);
+	return context;
+}
+
+/**
+ * acpiphp_get_context - Get hotplug context and grab a reference to it.
+ * @adev: ACPI device object to get the context for.
+ *
+ * Call under acpi_hp_context_lock.
+ */
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
+{
+	struct acpiphp_context *context;
+
+	if (!adev->hp)
+		return NULL;
+
+	context = to_acpiphp_context(adev->hp);
+	context->refcount++;
+	return context;
+}
+
+/**
+ * acpiphp_put_context - Drop a reference to ACPI hotplug context.
+ * @context: ACPI hotplug context to drop a reference to.
+ *
+ * The context object is removed if there are no more references to it.
+ *
+ * Call under acpi_hp_context_lock.
+ */
+static void acpiphp_put_context(struct acpiphp_context *context)
+{
+	if (--context->refcount)
+		return;
+
+	WARN_ON(context->bridge);
+	context->hp.self->hp = NULL;
+	kfree(context);
+}
+
+static inline void get_bridge(struct acpiphp_bridge *bridge)
+{
+	kref_get(&bridge->ref);
+}
+
+static inline void put_bridge(struct acpiphp_bridge *bridge)
+{
+	kref_put(&bridge->ref, free_bridge);
+}
+
+static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev)
+{
+	struct acpiphp_context *context;
+
+	acpi_lock_hp_context();
+	context = acpiphp_get_context(adev);
+	if (!context || context->func.parent->is_going_away) {
+		acpi_unlock_hp_context();
+		return NULL;
+	}
+	get_bridge(context->func.parent);
+	acpiphp_put_context(context);
+	acpi_unlock_hp_context();
+	return context;
+}
+
+static void acpiphp_let_context_go(struct acpiphp_context *context)
+{
+	put_bridge(context->func.parent);
+}
+
+static void free_bridge(struct kref *kref)
+{
+	struct acpiphp_context *context;
+	struct acpiphp_bridge *bridge;
+	struct acpiphp_slot *slot, *next;
+	struct acpiphp_func *func, *tmp;
+
+	acpi_lock_hp_context();
+
+	bridge = container_of(kref, struct acpiphp_bridge, ref);
+
+	list_for_each_entry_safe(slot, next, &bridge->slots, node) {
+		list_for_each_entry_safe(func, tmp, &slot->funcs, sibling)
+			acpiphp_put_context(func_to_context(func));
+
+		kfree(slot);
+	}
+
+	context = bridge->context;
+	/* Root bridges will not have hotplug context. */
+	if (context) {
+		/* Release the reference taken by acpiphp_enumerate_slots(). */
+		put_bridge(context->func.parent);
+		context->bridge = NULL;
+		acpiphp_put_context(context);
+	}
+
+	put_device(&bridge->pci_bus->dev);
+	pci_dev_put(bridge->pci_dev);
+	kfree(bridge);
+
+	acpi_unlock_hp_context();
+}
+
+/**
+ * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices.
+ * @adev: ACPI device object corresponding to a PCI device.
+ *
+ * TBD - figure out a way to only call fixups for systems that require them.
+ */
+static void acpiphp_post_dock_fixup(struct acpi_device *adev)
+{
+	struct acpiphp_context *context = acpiphp_grab_context(adev);
+	struct pci_bus *bus;
+	u32 buses;
+
+	if (!context)
+		return;
+
+	bus = context->func.slot->bus;
+	if (!bus->self)
+		goto out;
+
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses);
+
+	if (((buses >> 8) & 0xff) != bus->busn_res.start) {
+		buses = (buses & 0xff000000)
+			| ((unsigned int)(bus->primary)     <<  0)
+			| ((unsigned int)(bus->busn_res.start)   <<  8)
+			| ((unsigned int)(bus->busn_res.end) << 16);
+		pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
+	}
+
+ out:
+	acpiphp_let_context_go(context);
+}
+
+/* Check whether the PCI device is managed by native PCIe hotplug driver */
+static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
+{
+	u32 reg32;
+	acpi_handle tmp;
+	struct acpi_pci_root *root;
+
+	/* Check whether the PCIe port supports native PCIe hotplug */
+	if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
+		return false;
+	if (!(reg32 & PCI_EXP_SLTCAP_HPC))
+		return false;
+
+	/*
+	 * Check whether native PCIe hotplug has been enabled for
+	 * this PCIe hierarchy.
+	 */
+	tmp = acpi_find_root_bridge_handle(pdev);
+	if (!tmp)
+		return false;
+	root = acpi_pci_find_root(tmp);
+	if (!root)
+		return false;
+	if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+		return false;
+
+	return true;
+}
+
+/**
+ * acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
+ * @handle: ACPI handle of the object to add a context to.
+ * @lvl: Not used.
+ * @data: The object's parent ACPIPHP bridge.
+ * @rv: Not used.
+ */
+static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
+				       void **rv)
+{
+	struct acpiphp_bridge *bridge = data;
+	struct acpiphp_context *context;
+	struct acpi_device *adev;
+	struct acpiphp_slot *slot;
+	struct acpiphp_func *newfunc;
+	acpi_status status = AE_OK;
+	unsigned long long adr;
+	int device, function;
+	struct pci_bus *pbus = bridge->pci_bus;
+	struct pci_dev *pdev = bridge->pci_dev;
+	u32 val;
+
+	status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+	if (ACPI_FAILURE(status)) {
+		if (status != AE_NOT_FOUND)
+			acpi_handle_warn(handle,
+				"can't evaluate _ADR (%#x)\n", status);
+		return AE_OK;
+	}
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+
+	device = (adr >> 16) & 0xffff;
+	function = adr & 0xffff;
+
+	acpi_lock_hp_context();
+	context = acpiphp_init_context(adev);
+	if (!context) {
+		acpi_unlock_hp_context();
+		acpi_handle_err(handle, "No hotplug context\n");
+		return AE_NOT_EXIST;
+	}
+	newfunc = &context->func;
+	newfunc->function = function;
+	newfunc->parent = bridge;
+	acpi_unlock_hp_context();
+
+	/*
+	 * If this is a dock device, its _EJ0 should be executed by the dock
+	 * notify handler after calling _DCK.
+	 */
+	if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0"))
+		newfunc->flags = FUNC_HAS_EJ0;
+
+	if (acpi_has_method(handle, "_STA"))
+		newfunc->flags |= FUNC_HAS_STA;
+
+	/* search for objects that share the same slot */
+	list_for_each_entry(slot, &bridge->slots, node)
+		if (slot->device == device)
+			goto slot_found;
+
+	slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
+	if (!slot) {
+		acpi_lock_hp_context();
+		acpiphp_put_context(context);
+		acpi_unlock_hp_context();
+		return AE_NO_MEMORY;
+	}
+
+	slot->bus = bridge->pci_bus;
+	slot->device = device;
+	INIT_LIST_HEAD(&slot->funcs);
+
+	list_add_tail(&slot->node, &bridge->slots);
+
+	/*
+	 * Expose slots to user space for functions that have _EJ0 or _RMV or
+	 * are located in dock stations.  Do not expose them for devices handled
+	 * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to
+	 * expose slots to user space in those cases.
+	 */
+	if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev))
+	    && !(pdev && device_is_managed_by_native_pciehp(pdev))) {
+		unsigned long long sun;
+		int retval;
+
+		bridge->nr_slots++;
+		status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+		if (ACPI_FAILURE(status))
+			sun = bridge->nr_slots;
+
+		pr_debug("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
+		    sun, pci_domain_nr(pbus), pbus->number, device);
+
+		retval = acpiphp_register_hotplug_slot(slot, sun);
+		if (retval) {
+			slot->slot = NULL;
+			bridge->nr_slots--;
+			if (retval == -EBUSY)
+				pr_warn("Slot %llu already registered by another hotplug driver\n", sun);
+			else
+				pr_warn("acpiphp_register_hotplug_slot failed (err code = 0x%x)\n", retval);
+		}
+		/* Even if the slot registration fails, we can still use it. */
+	}
+
+ slot_found:
+	newfunc->slot = slot;
+	list_add_tail(&newfunc->sibling, &slot->funcs);
+
+	if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function),
+				       &val, 60*1000))
+		slot->flags |= SLOT_ENABLED;
+
+	return AE_OK;
+}
+
+static void cleanup_bridge(struct acpiphp_bridge *bridge)
+{
+	struct acpiphp_slot *slot;
+	struct acpiphp_func *func;
+
+	list_for_each_entry(slot, &bridge->slots, node) {
+		list_for_each_entry(func, &slot->funcs, sibling) {
+			struct acpi_device *adev = func_to_acpi_device(func);
+
+			acpi_lock_hp_context();
+			adev->hp->notify = NULL;
+			adev->hp->fixup = NULL;
+			acpi_unlock_hp_context();
+		}
+		slot->flags |= SLOT_IS_GOING_AWAY;
+		if (slot->slot)
+			acpiphp_unregister_hotplug_slot(slot);
+	}
+
+	mutex_lock(&bridge_mutex);
+	list_del(&bridge->list);
+	mutex_unlock(&bridge_mutex);
+
+	acpi_lock_hp_context();
+	bridge->is_going_away = true;
+	acpi_unlock_hp_context();
+}
+
+/**
+ * acpiphp_max_busnr - return the highest reserved bus number under the given bus.
+ * @bus: bus to start search with
+ */
+static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
+{
+	struct pci_bus *tmp;
+	unsigned char max, n;
+
+	/*
+	 * pci_bus_max_busnr will return the highest
+	 * reserved busnr for all these children.
+	 * that is equivalent to the bus->subordinate
+	 * value.  We don't want to use the parent's
+	 * bus->subordinate value because it could have
+	 * padding in it.
+	 */
+	max = bus->busn_res.start;
+
+	list_for_each_entry(tmp, &bus->children, node) {
+		n = pci_bus_max_busnr(tmp);
+		if (n > max)
+			max = n;
+	}
+	return max;
+}
+
+static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
+{
+	struct acpiphp_func *func;
+	union acpi_object params[2];
+	struct acpi_object_list arg_list;
+
+	list_for_each_entry(func, &slot->funcs, sibling) {
+		arg_list.count = 2;
+		arg_list.pointer = params;
+		params[0].type = ACPI_TYPE_INTEGER;
+		params[0].integer.value = ACPI_ADR_SPACE_PCI_CONFIG;
+		params[1].type = ACPI_TYPE_INTEGER;
+		params[1].integer.value = 1;
+		/* _REG is optional, we don't care about if there is failure */
+		acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list,
+				     NULL);
+	}
+}
+
+static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
+{
+	struct acpiphp_func *func;
+
+	/* quirk, or pcie could set it already */
+	if (dev->is_hotplug_bridge)
+		return;
+
+	list_for_each_entry(func, &slot->funcs, sibling) {
+		if (PCI_FUNC(dev->devfn) == func->function) {
+			dev->is_hotplug_bridge = 1;
+			break;
+		}
+	}
+}
+
+static int acpiphp_rescan_slot(struct acpiphp_slot *slot)
+{
+	struct acpiphp_func *func;
+
+	list_for_each_entry(func, &slot->funcs, sibling) {
+		struct acpi_device *adev = func_to_acpi_device(func);
+
+		acpi_bus_scan(adev->handle);
+		if (acpi_device_enumerated(adev))
+			acpi_device_set_power(adev, ACPI_STATE_D0);
+	}
+	return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
+}
+
+/**
+ * enable_slot - enable, configure a slot
+ * @slot: slot to be enabled
+ *
+ * This function should be called per *physical slot*,
+ * not per each slot object in ACPI namespace.
+ */
+static void enable_slot(struct acpiphp_slot *slot)
+{
+	struct pci_dev *dev;
+	struct pci_bus *bus = slot->bus;
+	struct acpiphp_func *func;
+	int max, pass;
+	LIST_HEAD(add_list);
+
+	acpiphp_rescan_slot(slot);
+	max = acpiphp_max_busnr(bus);
+	for (pass = 0; pass < 2; pass++) {
+		list_for_each_entry(dev, &bus->devices, bus_list) {
+			if (PCI_SLOT(dev->devfn) != slot->device)
+				continue;
+
+			if (pci_is_bridge(dev)) {
+				max = pci_scan_bridge(bus, dev, max, pass);
+				if (pass && dev->subordinate) {
+					check_hotplug_bridge(slot, dev);
+					pcibios_resource_survey_bus(dev->subordinate);
+					__pci_bus_size_bridges(dev->subordinate,
+							       &add_list);
+				}
+			}
+		}
+	}
+	__pci_bus_assign_resources(bus, &add_list, NULL);
+
+	acpiphp_sanitize_bus(bus);
+	pcie_bus_configure_settings(bus);
+	acpiphp_set_acpi_region(slot);
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		/* Assume that newly added devices are powered on already. */
+		if (!dev->is_added)
+			dev->current_state = PCI_D0;
+	}
+
+	pci_bus_add_devices(bus);
+
+	slot->flags |= SLOT_ENABLED;
+	list_for_each_entry(func, &slot->funcs, sibling) {
+		dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
+						  func->function));
+		if (!dev) {
+			/* Do not set SLOT_ENABLED flag if some funcs
+			   are not added. */
+			slot->flags &= (~SLOT_ENABLED);
+			continue;
+		}
+	}
+}
+
+/**
+ * disable_slot - disable a slot
+ * @slot: ACPI PHP slot
+ */
+static void disable_slot(struct acpiphp_slot *slot)
+{
+	struct pci_bus *bus = slot->bus;
+	struct pci_dev *dev, *prev;
+	struct acpiphp_func *func;
+
+	/*
+	 * enable_slot() enumerates all functions in this device via
+	 * pci_scan_slot(), whether they have associated ACPI hotplug
+	 * methods (_EJ0, etc.) or not.  Therefore, we remove all functions
+	 * here.
+	 */
+	list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list)
+		if (PCI_SLOT(dev->devfn) == slot->device)
+			pci_stop_and_remove_bus_device(dev);
+
+	list_for_each_entry(func, &slot->funcs, sibling)
+		acpi_bus_trim(func_to_acpi_device(func));
+
+	slot->flags &= (~SLOT_ENABLED);
+}
+
+static bool slot_no_hotplug(struct acpiphp_slot *slot)
+{
+	struct pci_bus *bus = slot->bus;
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) == slot->device && dev->ignore_hotplug)
+			return true;
+	}
+	return false;
+}
+
+/**
+ * get_slot_status - get ACPI slot status
+ * @slot: ACPI PHP slot
+ *
+ * If a slot has _STA for each function and if any one of them
+ * returned non-zero status, return it.
+ *
+ * If a slot doesn't have _STA and if any one of its functions'
+ * configuration space is configured, return 0x0f as a _STA.
+ *
+ * Otherwise return 0.
+ */
+static unsigned int get_slot_status(struct acpiphp_slot *slot)
+{
+	unsigned long long sta = 0;
+	struct acpiphp_func *func;
+
+	list_for_each_entry(func, &slot->funcs, sibling) {
+		if (func->flags & FUNC_HAS_STA) {
+			acpi_status status;
+
+			status = acpi_evaluate_integer(func_to_handle(func),
+						       "_STA", NULL, &sta);
+			if (ACPI_SUCCESS(status) && sta)
+				break;
+		} else {
+			u32 dvid;
+
+			pci_bus_read_config_dword(slot->bus,
+						  PCI_DEVFN(slot->device,
+							    func->function),
+						  PCI_VENDOR_ID, &dvid);
+			if (dvid != 0xffffffff) {
+				sta = ACPI_STA_ALL;
+				break;
+			}
+		}
+	}
+
+	return (unsigned int)sta;
+}
+
+static inline bool device_status_valid(unsigned int sta)
+{
+	/*
+	 * ACPI spec says that _STA may return bit 0 clear with bit 3 set
+	 * if the device is valid but does not require a device driver to be
+	 * loaded (Section 6.3.7 of ACPI 5.0A).
+	 */
+	unsigned int mask = ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_FUNCTIONING;
+	return (sta & mask) == mask;
+}
+
+/**
+ * trim_stale_devices - remove PCI devices that are not responding.
+ * @dev: PCI device to start walking the hierarchy from.
+ */
+static void trim_stale_devices(struct pci_dev *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
+	struct pci_bus *bus = dev->subordinate;
+	bool alive = dev->ignore_hotplug;
+
+	if (adev) {
+		acpi_status status;
+		unsigned long long sta;
+
+		status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
+		alive = alive || (ACPI_SUCCESS(status) && device_status_valid(sta));
+	}
+	if (!alive)
+		alive = pci_device_is_present(dev);
+
+	if (!alive) {
+		pci_stop_and_remove_bus_device(dev);
+		if (adev)
+			acpi_bus_trim(adev);
+	} else if (bus) {
+		struct pci_dev *child, *tmp;
+
+		/* The device is a bridge. so check the bus below it. */
+		pm_runtime_get_sync(&dev->dev);
+		list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list)
+			trim_stale_devices(child);
+
+		pm_runtime_put(&dev->dev);
+	}
+}
+
+/**
+ * acpiphp_check_bridge - re-enumerate devices
+ * @bridge: where to begin re-enumeration
+ *
+ * Iterate over all slots under this bridge and make sure that if a
+ * card is present they are enabled, and if not they are disabled.
+ */
+static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
+{
+	struct acpiphp_slot *slot;
+
+	/* Bail out if the bridge is going away. */
+	if (bridge->is_going_away)
+		return;
+
+	list_for_each_entry(slot, &bridge->slots, node) {
+		struct pci_bus *bus = slot->bus;
+		struct pci_dev *dev, *tmp;
+
+		if (slot_no_hotplug(slot)) {
+			; /* do nothing */
+		} else if (device_status_valid(get_slot_status(slot))) {
+			/* remove stale devices if any */
+			list_for_each_entry_safe_reverse(dev, tmp,
+							 &bus->devices, bus_list)
+				if (PCI_SLOT(dev->devfn) == slot->device)
+					trim_stale_devices(dev);
+
+			/* configure all functions */
+			enable_slot(slot);
+		} else {
+			disable_slot(slot);
+		}
+	}
+}
+
+/*
+ * Remove devices for which we could not assign resources, call
+ * arch specific code to fix-up the bus
+ */
+static void acpiphp_sanitize_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev, *tmp;
+	int i;
+	unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM;
+
+	list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
+		for (i=0; i<PCI_BRIDGE_RESOURCES; i++) {
+			struct resource *res = &dev->resource[i];
+			if ((res->flags & type_mask) && !res->start &&
+					res->end) {
+				/* Could not assign a required resources
+				 * for this device, remove it */
+				pci_stop_and_remove_bus_device(dev);
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * ACPI event handlers
+ */
+
+void acpiphp_check_host_bridge(struct acpi_device *adev)
+{
+	struct acpiphp_bridge *bridge = NULL;
+
+	acpi_lock_hp_context();
+	if (adev->hp) {
+		bridge = to_acpiphp_root_context(adev->hp)->root_bridge;
+		if (bridge)
+			get_bridge(bridge);
+	}
+	acpi_unlock_hp_context();
+	if (bridge) {
+		pci_lock_rescan_remove();
+
+		acpiphp_check_bridge(bridge);
+
+		pci_unlock_rescan_remove();
+		put_bridge(bridge);
+	}
+}
+
+static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
+
+static void hotplug_event(u32 type, struct acpiphp_context *context)
+{
+	acpi_handle handle = context->hp.self->handle;
+	struct acpiphp_func *func = &context->func;
+	struct acpiphp_slot *slot = func->slot;
+	struct acpiphp_bridge *bridge;
+
+	acpi_lock_hp_context();
+	bridge = context->bridge;
+	if (bridge)
+		get_bridge(bridge);
+
+	acpi_unlock_hp_context();
+
+	pci_lock_rescan_remove();
+
+	switch (type) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		/* bus re-enumerate */
+		acpi_handle_debug(handle, "Bus check in %s()\n", __func__);
+		if (bridge)
+			acpiphp_check_bridge(bridge);
+		else if (!(slot->flags & SLOT_IS_GOING_AWAY))
+			enable_slot(slot);
+
+		break;
+
+	case ACPI_NOTIFY_DEVICE_CHECK:
+		/* device check */
+		acpi_handle_debug(handle, "Device check in %s()\n", __func__);
+		if (bridge) {
+			acpiphp_check_bridge(bridge);
+		} else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
+			/*
+			 * Check if anything has changed in the slot and rescan
+			 * from the parent if that's the case.
+			 */
+			if (acpiphp_rescan_slot(slot))
+				acpiphp_check_bridge(func->parent);
+		}
+		break;
+
+	case ACPI_NOTIFY_EJECT_REQUEST:
+		/* request device eject */
+		acpi_handle_debug(handle, "Eject request in %s()\n", __func__);
+		acpiphp_disable_and_eject_slot(slot);
+		break;
+	}
+
+	pci_unlock_rescan_remove();
+	if (bridge)
+		put_bridge(bridge);
+}
+
+static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type)
+{
+	struct acpiphp_context *context;
+
+	context = acpiphp_grab_context(adev);
+	if (!context)
+		return -ENODATA;
+
+	hotplug_event(type, context);
+	acpiphp_let_context_go(context);
+	return 0;
+}
+
+/**
+ * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus.
+ * @bus: PCI bus to enumerate the slots for.
+ *
+ * A "slot" is an object associated with a PCI device number.  All functions
+ * (PCI devices) with the same bus and device number belong to the same slot.
+ */
+void acpiphp_enumerate_slots(struct pci_bus *bus)
+{
+	struct acpiphp_bridge *bridge;
+	struct acpi_device *adev;
+	acpi_handle handle;
+	acpi_status status;
+
+	if (acpiphp_disabled)
+		return;
+
+	adev = ACPI_COMPANION(bus->bridge);
+	if (!adev)
+		return;
+
+	handle = adev->handle;
+	bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
+	if (!bridge) {
+		acpi_handle_err(handle, "No memory for bridge object\n");
+		return;
+	}
+
+	INIT_LIST_HEAD(&bridge->slots);
+	kref_init(&bridge->ref);
+	bridge->pci_dev = pci_dev_get(bus->self);
+	bridge->pci_bus = bus;
+
+	/*
+	 * Grab a ref to the subordinate PCI bus in case the bus is
+	 * removed via PCI core logical hotplug. The ref pins the bus
+	 * (which we access during module unload).
+	 */
+	get_device(&bus->dev);
+
+	acpi_lock_hp_context();
+	if (pci_is_root_bus(bridge->pci_bus)) {
+		struct acpiphp_root_context *root_context;
+
+		root_context = kzalloc(sizeof(*root_context), GFP_KERNEL);
+		if (!root_context)
+			goto err;
+
+		root_context->root_bridge = bridge;
+		acpi_set_hp_context(adev, &root_context->hp);
+	} else {
+		struct acpiphp_context *context;
+
+		/*
+		 * This bridge should have been registered as a hotplug function
+		 * under its parent, so the context should be there, unless the
+		 * parent is going to be handled by pciehp, in which case this
+		 * bridge is not interesting to us either.
+		 */
+		context = acpiphp_get_context(adev);
+		if (!context)
+			goto err;
+
+		bridge->context = context;
+		context->bridge = bridge;
+		/* Get a reference to the parent bridge. */
+		get_bridge(context->func.parent);
+	}
+	acpi_unlock_hp_context();
+
+	/* Must be added to the list prior to calling acpiphp_add_context(). */
+	mutex_lock(&bridge_mutex);
+	list_add(&bridge->list, &bridge_list);
+	mutex_unlock(&bridge_mutex);
+
+	/* register all slot objects under this bridge */
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+				     acpiphp_add_context, NULL, bridge, NULL);
+	if (ACPI_FAILURE(status)) {
+		acpi_handle_err(handle, "failed to register slots\n");
+		cleanup_bridge(bridge);
+		put_bridge(bridge);
+	}
+	return;
+
+ err:
+	acpi_unlock_hp_context();
+	put_device(&bus->dev);
+	pci_dev_put(bridge->pci_dev);
+	kfree(bridge);
+}
+
+static void acpiphp_drop_bridge(struct acpiphp_bridge *bridge)
+{
+	if (pci_is_root_bus(bridge->pci_bus)) {
+		struct acpiphp_root_context *root_context;
+		struct acpi_device *adev;
+
+		acpi_lock_hp_context();
+		adev = ACPI_COMPANION(bridge->pci_bus->bridge);
+		root_context = to_acpiphp_root_context(adev->hp);
+		adev->hp = NULL;
+		acpi_unlock_hp_context();
+		kfree(root_context);
+	}
+	cleanup_bridge(bridge);
+	put_bridge(bridge);
+}
+
+/**
+ * acpiphp_remove_slots - Remove slot objects associated with a given bus.
+ * @bus: PCI bus to remove the slot objects for.
+ */
+void acpiphp_remove_slots(struct pci_bus *bus)
+{
+	struct acpiphp_bridge *bridge;
+
+	if (acpiphp_disabled)
+		return;
+
+	mutex_lock(&bridge_mutex);
+	list_for_each_entry(bridge, &bridge_list, list)
+		if (bridge->pci_bus == bus) {
+			mutex_unlock(&bridge_mutex);
+			acpiphp_drop_bridge(bridge);
+			return;
+		}
+
+	mutex_unlock(&bridge_mutex);
+}
+
+/**
+ * acpiphp_enable_slot - power on slot
+ * @slot: ACPI PHP slot
+ */
+int acpiphp_enable_slot(struct acpiphp_slot *slot)
+{
+	pci_lock_rescan_remove();
+
+	if (slot->flags & SLOT_IS_GOING_AWAY) {
+		pci_unlock_rescan_remove();
+		return -ENODEV;
+	}
+
+	/* configure all functions */
+	if (!(slot->flags & SLOT_ENABLED))
+		enable_slot(slot);
+
+	pci_unlock_rescan_remove();
+	return 0;
+}
+
+/**
+ * acpiphp_disable_and_eject_slot - power off and eject slot
+ * @slot: ACPI PHP slot
+ */
+static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
+{
+	struct acpiphp_func *func;
+
+	if (slot->flags & SLOT_IS_GOING_AWAY)
+		return -ENODEV;
+
+	/* unconfigure all functions */
+	disable_slot(slot);
+
+	list_for_each_entry(func, &slot->funcs, sibling)
+		if (func->flags & FUNC_HAS_EJ0) {
+			acpi_handle handle = func_to_handle(func);
+
+			if (ACPI_FAILURE(acpi_evaluate_ej0(handle)))
+				acpi_handle_err(handle, "_EJ0 failed\n");
+
+			break;
+		}
+
+	return 0;
+}
+
+int acpiphp_disable_slot(struct acpiphp_slot *slot)
+{
+	int ret;
+
+	/*
+	 * Acquire acpi_scan_lock to ensure that the execution of _EJ0 in
+	 * acpiphp_disable_and_eject_slot() will be synchronized properly.
+	 */
+	acpi_scan_lock_acquire();
+	pci_lock_rescan_remove();
+	ret = acpiphp_disable_and_eject_slot(slot);
+	pci_unlock_rescan_remove();
+	acpi_scan_lock_release();
+	return ret;
+}
+
+/*
+ * slot enabled:  1
+ * slot disabled: 0
+ */
+u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
+{
+	return (slot->flags & SLOT_ENABLED);
+}
+
+/*
+ * latch   open:  1
+ * latch closed:  0
+ */
+u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
+{
+	return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
+}
+
+/*
+ * adapter presence : 1
+ *          absence : 0
+ */
+u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
+{
+	return !!get_slot_status(slot);
+}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
new file mode 100644
index 0000000..6ca2399
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -0,0 +1,487 @@
+/*
+ * ACPI PCI Hot Plug IBM Extension
+ *
+ * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
+ * Copyright (C) 2004 IBM Corp.
+ *
+ * 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 <vernux@us.ibm.com>
+ *
+ */
+
+#define pr_fmt(fmt) "acpiphp_ibm: " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+
+#include "acpiphp.h"
+#include "../pci.h"
+
+#define DRIVER_VERSION	"1.0.1"
+#define DRIVER_AUTHOR	"Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
+#define DRIVER_DESC	"ACPI Hot Plug PCI Controller Driver IBM extension"
+
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+#define FOUND_APCI 0x61504349
+/* these are the names for the IBM ACPI pseudo-device */
+#define IBM_HARDWARE_ID1 "IBM37D0"
+#define IBM_HARDWARE_ID2 "IBM37D4"
+
+#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
+
+/* union apci_descriptor - allows access to the
+ * various device descriptors that are embedded in the
+ * aPCI table
+ */
+union apci_descriptor {
+	struct {
+		char sig[4];
+		u8   len;
+	} header;
+	struct {
+		u8  type;
+		u8  len;
+		u16 slot_id;
+		u8  bus_id;
+		u8  dev_num;
+		u8  slot_num;
+		u8  slot_attr[2];
+		u8  attn;
+		u8  status[2];
+		u8  sun;
+		u8  res[3];
+	} slot;
+	struct {
+		u8 type;
+		u8 len;
+	} generic;
+};
+
+/* struct notification - keeps info about the device
+ * that cause the ACPI notification event
+ */
+struct notification {
+	struct acpi_device *device;
+	u8                  event;
+};
+
+static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
+static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
+static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
+static int ibm_get_table_from_acpi(char **bufp);
+static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
+				   struct bin_attribute *bin_attr,
+				   char *buffer, loff_t pos, size_t size);
+static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
+		u32 lvl, void *context, void **rv);
+static int __init ibm_acpiphp_init(void);
+static void __exit ibm_acpiphp_exit(void);
+
+static acpi_handle ibm_acpi_handle;
+static struct notification ibm_note;
+static struct bin_attribute ibm_apci_table_attr = {
+	    .attr = {
+		    .name = "apci_table",
+		    .mode = S_IRUGO,
+	    },
+	    .read = ibm_read_apci_table,
+	    .write = NULL,
+};
+static struct acpiphp_attention_info ibm_attention_info =
+{
+	.set_attn = ibm_set_attention_status,
+	.get_attn = ibm_get_attention_status,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * ibm_slot_from_id - workaround for bad ibm hardware
+ * @id: the slot number that linux refers to the slot by
+ *
+ * Description: This method returns the aCPI slot descriptor
+ * corresponding to the Linux slot number.  This descriptor
+ * has info about the aPCI slot id and attention status.
+ * This descriptor must be freed using kfree when done.
+ */
+static union apci_descriptor *ibm_slot_from_id(int id)
+{
+	int ind = 0, size;
+	union apci_descriptor *ret = NULL, *des;
+	char *table;
+
+	size = ibm_get_table_from_acpi(&table);
+	des = (union apci_descriptor *)table;
+	if (memcmp(des->header.sig, "aPCI", 4) != 0)
+		goto ibm_slot_done;
+
+	des = (union apci_descriptor *)&table[ind += des->header.len];
+	while (ind < size && (des->generic.type != 0x82 ||
+			des->slot.slot_num != id)) {
+		des = (union apci_descriptor *)&table[ind += des->generic.len];
+	}
+
+	if (ind < size && des->slot.slot_num == id)
+		ret = des;
+
+ibm_slot_done:
+	if (ret) {
+		ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
+		memcpy(ret, des, sizeof(union apci_descriptor));
+	}
+	kfree(table);
+	return ret;
+}
+
+/**
+ * ibm_set_attention_status - callback method to set the attention LED
+ * @slot: the hotplug_slot to work with
+ * @status: what to set the LED to (0 or 1)
+ *
+ * Description: This method is registered with the acpiphp module as a
+ * callback to do the device specific task of setting the LED status.
+ */
+static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
+{
+	union acpi_object args[2];
+	struct acpi_object_list params = { .pointer = args, .count = 2 };
+	acpi_status stat;
+	unsigned long long rc;
+	union apci_descriptor *ibm_slot;
+
+	ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+
+	pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
+			ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
+			(status ? 1 : 0));
+
+	args[0].type = ACPI_TYPE_INTEGER;
+	args[0].integer.value = ibm_slot->slot.slot_id;
+	args[1].type = ACPI_TYPE_INTEGER;
+	args[1].integer.value = (status) ? 1 : 0;
+
+	kfree(ibm_slot);
+
+	stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
+	if (ACPI_FAILURE(stat)) {
+		pr_err("APLS evaluation failed:  0x%08x\n", stat);
+		return -ENODEV;
+	} else if (!rc) {
+		pr_err("APLS method failed:  0x%08llx\n", rc);
+		return -ERANGE;
+	}
+	return 0;
+}
+
+/**
+ * ibm_get_attention_status - callback method to get attention LED status
+ * @slot: the hotplug_slot to work with
+ * @status: returns what the LED is set to (0 or 1)
+ *
+ * Description: This method is registered with the acpiphp module as a
+ * callback to do the device specific task of getting the LED status.
+ *
+ * Because there is no direct method of getting the LED status directly
+ * from an ACPI call, we read the aPCI table and parse out our
+ * slot descriptor to read the status from that.
+ */
+static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
+{
+	union apci_descriptor *ibm_slot;
+
+	ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+
+	if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
+		*status = 1;
+	else
+		*status = 0;
+
+	pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__,
+			ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
+			*status);
+
+	kfree(ibm_slot);
+	return 0;
+}
+
+/**
+ * ibm_handle_events - listens for ACPI events for the IBM37D0 device
+ * @handle: an ACPI handle to the device that caused the event
+ * @event: the event info (device specific)
+ * @context: passed context (our notification struct)
+ *
+ * Description: This method is registered as a callback with the ACPI
+ * subsystem it is called when this device has an event to notify the OS of.
+ *
+ * The events actually come from the device as two events that get
+ * synthesized into one event with data by this function.  The event
+ * ID comes first and then the slot number that caused it.  We report
+ * this as one event to the OS.
+ *
+ * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
+ * only re-enable the interrupt that causes this event AFTER this method
+ * has returned, thereby enforcing serial access for the notification struct.
+ */
+static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
+{
+	u8 detail = event & 0x0f;
+	u8 subevent = event & 0xf0;
+	struct notification *note = context;
+
+	pr_debug("%s: Received notification %02x\n", __func__, event);
+
+	if (subevent == 0x80) {
+		pr_debug("%s: generating bus event\n", __func__);
+		acpi_bus_generate_netlink_event(note->device->pnp.device_class,
+						  dev_name(&note->device->dev),
+						  note->event, detail);
+	} else
+		note->event = event;
+}
+
+/**
+ * ibm_get_table_from_acpi - reads the APLS buffer from ACPI
+ * @bufp: address to pointer to allocate for the table
+ *
+ * Description: This method reads the APLS buffer in from ACPI and
+ * stores the "stripped" table into a single buffer
+ * it allocates and passes the address back in bufp.
+ *
+ * If NULL is passed in as buffer, this method only calculates
+ * the size of the table and returns that without filling
+ * in the buffer.
+ *
+ * Returns < 0 on error or the size of the table on success.
+ */
+static int ibm_get_table_from_acpi(char **bufp)
+{
+	union acpi_object *package;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	acpi_status status;
+	char *lbuf = NULL;
+	int i, size = -EIO;
+
+	status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		pr_err("%s:  APCI evaluation failed\n", __func__);
+		return -ENODEV;
+	}
+
+	package = (union acpi_object *) buffer.pointer;
+	if (!(package) ||
+			(package->type != ACPI_TYPE_PACKAGE) ||
+			!(package->package.elements)) {
+		pr_err("%s:  Invalid APCI object\n", __func__);
+		goto read_table_done;
+	}
+
+	for (size = 0, i = 0; i < package->package.count; i++) {
+		if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
+			pr_err("%s:  Invalid APCI element %d\n", __func__, i);
+			goto read_table_done;
+		}
+		size += package->package.elements[i].buffer.length;
+	}
+
+	if (bufp == NULL)
+		goto read_table_done;
+
+	lbuf = kzalloc(size, GFP_KERNEL);
+	pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
+			__func__, package->package.count, size, lbuf);
+
+	if (lbuf) {
+		*bufp = lbuf;
+	} else {
+		size = -ENOMEM;
+		goto read_table_done;
+	}
+
+	size = 0;
+	for (i=0; i<package->package.count; i++) {
+		memcpy(&lbuf[size],
+				package->package.elements[i].buffer.pointer,
+				package->package.elements[i].buffer.length);
+		size += package->package.elements[i].buffer.length;
+	}
+
+read_table_done:
+	kfree(buffer.pointer);
+	return size;
+}
+
+/**
+ * ibm_read_apci_table - callback for the sysfs apci_table file
+ * @filp: the open sysfs file
+ * @kobj: the kobject this binary attribute is a part of
+ * @bin_attr: struct bin_attribute for this file
+ * @buffer: the kernel space buffer to fill
+ * @pos: the offset into the file
+ * @size: the number of bytes requested
+ *
+ * Description: Gets registered with sysfs as the reader callback
+ * to be executed when /sys/bus/pci/slots/apci_table gets read.
+ *
+ * Since we don't get notified on open and close for this file,
+ * things get really tricky here...
+ * our solution is to only allow reading the table in all at once.
+ */
+static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
+				   struct bin_attribute *bin_attr,
+				   char *buffer, loff_t pos, size_t size)
+{
+	int bytes_read = -EINVAL;
+	char *table = NULL;
+
+	pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
+
+	if (pos == 0) {
+		bytes_read = ibm_get_table_from_acpi(&table);
+		if (bytes_read > 0 && bytes_read <= size)
+			memcpy(buffer, table, bytes_read);
+		kfree(table);
+	}
+	return bytes_read;
+}
+
+/**
+ * ibm_find_acpi_device - callback to find our ACPI device
+ * @handle: the ACPI handle of the device we are inspecting
+ * @lvl: depth into the namespace tree
+ * @context: a pointer to our handle to fill when we find the device
+ * @rv: a return value to fill if desired
+ *
+ * Description: Used as a callback when calling acpi_walk_namespace
+ * to find our device.  When this method returns non-zero
+ * acpi_walk_namespace quits its search and returns our value.
+ */
+static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
+		u32 lvl, void *context, void **rv)
+{
+	acpi_handle *phandle = (acpi_handle *)context;
+	acpi_status status;
+	struct acpi_device_info *info;
+	int retval = 0;
+
+	status = acpi_get_object_info(handle, &info);
+	if (ACPI_FAILURE(status)) {
+		pr_err("%s:  Failed to get device information status=0x%x\n",
+			__func__, status);
+		return retval;
+	}
+
+	if (info->current_status && (info->valid & ACPI_VALID_HID) &&
+			(!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
+			 !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
+		pr_debug("found hardware: %s, handle: %p\n",
+			info->hardware_id.string, handle);
+		*phandle = handle;
+		/* returning non-zero causes the search to stop
+		 * and returns this value to the caller of
+		 * acpi_walk_namespace, but it also causes some warnings
+		 * in the acpi debug code to print...
+		 */
+		retval = FOUND_APCI;
+	}
+	kfree(info);
+	return retval;
+}
+
+static int __init ibm_acpiphp_init(void)
+{
+	int retval = 0;
+	acpi_status status;
+	struct acpi_device *device;
+	struct kobject *sysdir = &pci_slots_kset->kobj;
+
+	pr_debug("%s\n", __func__);
+
+	if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
+			&ibm_acpi_handle, NULL) != FOUND_APCI) {
+		pr_err("%s: acpi_walk_namespace failed\n", __func__);
+		retval = -ENODEV;
+		goto init_return;
+	}
+	pr_debug("%s: found IBM aPCI device\n", __func__);
+	if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
+		pr_err("%s: acpi_bus_get_device failed\n", __func__);
+		retval = -ENODEV;
+		goto init_return;
+	}
+	if (acpiphp_register_attention(&ibm_attention_info)) {
+		retval = -ENODEV;
+		goto init_return;
+	}
+
+	ibm_note.device = device;
+	status = acpi_install_notify_handler(ibm_acpi_handle,
+			ACPI_DEVICE_NOTIFY, ibm_handle_events,
+			&ibm_note);
+	if (ACPI_FAILURE(status)) {
+		pr_err("%s: Failed to register notification handler\n",
+				__func__);
+		retval = -EBUSY;
+		goto init_cleanup;
+	}
+
+	ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
+	retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
+
+	return retval;
+
+init_cleanup:
+	acpiphp_unregister_attention(&ibm_attention_info);
+init_return:
+	return retval;
+}
+
+static void __exit ibm_acpiphp_exit(void)
+{
+	acpi_status status;
+	struct kobject *sysdir = &pci_slots_kset->kobj;
+
+	pr_debug("%s\n", __func__);
+
+	if (acpiphp_unregister_attention(&ibm_attention_info))
+		pr_err("%s: attention info deregistration failed", __func__);
+
+	status = acpi_remove_notify_handler(
+			   ibm_acpi_handle,
+			   ACPI_DEVICE_NOTIFY,
+			   ibm_handle_events);
+	if (ACPI_FAILURE(status))
+		pr_err("%s: Notification handler removal failed\n", __func__);
+	/* remove the /sys entries */
+	sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
+}
+
+module_init(ibm_acpiphp_init);
+module_exit(ibm_acpiphp_exit);
diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h
new file mode 100644
index 0000000..6a0ddf7
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug.h
@@ -0,0 +1,110 @@
+/*
+ * CompactPCI Hot Plug Core Functions
+ *
+ * Copyright (C) 2002 SOMA Networks, Inc.
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#ifndef _CPCI_HOTPLUG_H
+#define _CPCI_HOTPLUG_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+
+/* PICMG 2.1 R2.0 HS CSR bits: */
+#define HS_CSR_INS	0x0080
+#define HS_CSR_EXT	0x0040
+#define HS_CSR_PI	0x0030
+#define HS_CSR_LOO	0x0008
+#define HS_CSR_PIE	0x0004
+#define HS_CSR_EIM	0x0002
+#define HS_CSR_DHA	0x0001
+
+struct slot {
+	u8 number;
+	unsigned int devfn;
+	struct pci_bus *bus;
+	struct pci_dev *dev;
+	unsigned int extracting;
+	struct hotplug_slot *hotplug_slot;
+	struct list_head slot_list;
+};
+
+struct cpci_hp_controller_ops {
+	int (*query_enum) (void);
+	int (*enable_irq) (void);
+	int (*disable_irq) (void);
+	int (*check_irq) (void *dev_id);
+	int (*hardware_test) (struct slot *slot, u32 value);
+	u8  (*get_power) (struct slot *slot);
+	int (*set_power) (struct slot *slot, int value);
+};
+
+struct cpci_hp_controller {
+	unsigned int irq;
+	unsigned long irq_flags;
+	char *devname;
+	void *dev_id;
+	char *name;
+	struct cpci_hp_controller_ops *ops;
+};
+
+static inline const char *slot_name(struct slot *slot)
+{
+	return hotplug_slot_name(slot->hotplug_slot);
+}
+
+int cpci_hp_register_controller(struct cpci_hp_controller *controller);
+int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
+int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
+int cpci_hp_unregister_bus(struct pci_bus *bus);
+int cpci_hp_start(void);
+int cpci_hp_stop(void);
+
+/*
+ * Internal function prototypes, these functions should not be used by
+ * board/chassis drivers.
+ */
+u8 cpci_get_attention_status(struct slot *slot);
+u8 cpci_get_latch_status(struct slot *slot);
+u8 cpci_get_adapter_status(struct slot *slot);
+u16 cpci_get_hs_csr(struct slot *slot);
+int cpci_set_attention_status(struct slot *slot, int status);
+int cpci_check_and_clear_ins(struct slot *slot);
+int cpci_check_ext(struct slot *slot);
+int cpci_clear_ext(struct slot *slot);
+int cpci_led_on(struct slot *slot);
+int cpci_led_off(struct slot *slot);
+int cpci_configure_slot(struct slot *slot);
+int cpci_unconfigure_slot(struct slot *slot);
+
+#ifdef CONFIG_HOTPLUG_PCI_CPCI
+int cpci_hotplug_init(int debug);
+void cpci_hotplug_exit(void);
+#else
+static inline int cpci_hotplug_init(int debug) { return 0; }
+static inline void cpci_hotplug_exit(void) { }
+#endif
+
+#endif	/* _CPCI_HOTPLUG_H */
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
new file mode 100644
index 0000000..46db293
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -0,0 +1,731 @@
+/*
+ * CompactPCI Hot Plug Driver
+ *
+ * Copyright (C) 2002,2005 SOMA Networks, Inc.
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include "cpci_hotplug.h"
+
+#define DRIVER_AUTHOR	"Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC	"CompactPCI Hot Plug Core"
+
+#define MY_NAME	"cpci_hotplug"
+
+#define dbg(format, arg...)					\
+	do {							\
+		if (cpci_debug)					\
+			printk (KERN_DEBUG "%s: " format "\n",	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static DECLARE_RWSEM(list_rwsem);
+static LIST_HEAD(slot_list);
+static int slots;
+static atomic_t extracting;
+int cpci_debug;
+static struct cpci_hp_controller *controller;
+static struct task_struct *cpci_thread;
+static int thread_finished;
+
+static int enable_slot(struct hotplug_slot *slot);
+static int disable_slot(struct hotplug_slot *slot);
+static int set_attention_status(struct hotplug_slot *slot, u8 value);
+static int get_power_status(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
+	.enable_slot = enable_slot,
+	.disable_slot = disable_slot,
+	.set_attention_status = set_attention_status,
+	.get_power_status = get_power_status,
+	.get_attention_status = get_attention_status,
+	.get_adapter_status = get_adapter_status,
+	.get_latch_status = get_latch_status,
+};
+
+static int
+update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+	struct hotplug_slot_info info;
+
+	memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
+	info.latch_status = value;
+	return pci_hp_change_slot_info(hotplug_slot, &info);
+}
+
+static int
+update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+	struct hotplug_slot_info info;
+
+	memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
+	info.adapter_status = value;
+	return pci_hp_change_slot_info(hotplug_slot, &info);
+}
+
+static int
+enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
+
+	if (controller->ops->set_power)
+		retval = controller->ops->set_power(slot, 1);
+	return retval;
+}
+
+static int
+disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
+
+	down_write(&list_rwsem);
+
+	/* Unconfigure device */
+	dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
+	retval = cpci_unconfigure_slot(slot);
+	if (retval) {
+		err("%s - could not unconfigure slot %s",
+		    __func__, slot_name(slot));
+		goto disable_error;
+	}
+	dbg("%s - finished unconfiguring slot %s", __func__, slot_name(slot));
+
+	/* Clear EXT (by setting it) */
+	if (cpci_clear_ext(slot)) {
+		err("%s - could not clear EXT for slot %s",
+		    __func__, slot_name(slot));
+		retval = -ENODEV;
+		goto disable_error;
+	}
+	cpci_led_on(slot);
+
+	if (controller->ops->set_power) {
+		retval = controller->ops->set_power(slot, 0);
+		if (retval)
+			goto disable_error;
+	}
+
+	if (update_adapter_status(slot->hotplug_slot, 0))
+		warn("failure to update adapter file");
+
+	if (slot->extracting) {
+		slot->extracting = 0;
+		atomic_dec(&extracting);
+	}
+disable_error:
+	up_write(&list_rwsem);
+	return retval;
+}
+
+static u8
+cpci_get_power_status(struct slot *slot)
+{
+	u8 power = 1;
+
+	if (controller->ops->get_power)
+		power = controller->ops->get_power(slot);
+	return power;
+}
+
+static int
+get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	*value = cpci_get_power_status(slot);
+	return 0;
+}
+
+static int
+get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	*value = cpci_get_attention_status(slot);
+	return 0;
+}
+
+static int
+set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+	return cpci_set_attention_status(hotplug_slot->private, status);
+}
+
+static int
+get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	*value = hotplug_slot->info->adapter_status;
+	return 0;
+}
+
+static int
+get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	*value = hotplug_slot->info->latch_status;
+	return 0;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	pci_dev_put(slot->dev);
+	kfree(slot);
+}
+
+#define SLOT_NAME_SIZE	6
+
+int
+cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
+{
+	struct slot *slot;
+	struct hotplug_slot *hotplug_slot;
+	struct hotplug_slot_info *info;
+	char name[SLOT_NAME_SIZE];
+	int status;
+	int i;
+
+	if (!(controller && bus))
+		return -ENODEV;
+
+	/*
+	 * Create a structure for each slot, and register that slot
+	 * with the pci_hotplug subsystem.
+	 */
+	for (i = first; i <= last; ++i) {
+		slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
+		if (!slot) {
+			status = -ENOMEM;
+			goto error;
+		}
+
+		hotplug_slot =
+			kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
+		if (!hotplug_slot) {
+			status = -ENOMEM;
+			goto error_slot;
+		}
+		slot->hotplug_slot = hotplug_slot;
+
+		info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
+		if (!info) {
+			status = -ENOMEM;
+			goto error_hpslot;
+		}
+		hotplug_slot->info = info;
+
+		slot->bus = bus;
+		slot->number = i;
+		slot->devfn = PCI_DEVFN(i, 0);
+
+		snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
+
+		hotplug_slot->private = slot;
+		hotplug_slot->release = &release_slot;
+		hotplug_slot->ops = &cpci_hotplug_slot_ops;
+
+		/*
+		 * Initialize the slot info structure with some known
+		 * good values.
+		 */
+		dbg("initializing slot %s", name);
+		info->power_status = cpci_get_power_status(slot);
+		info->attention_status = cpci_get_attention_status(slot);
+
+		dbg("registering slot %s", name);
+		status = pci_hp_register(slot->hotplug_slot, bus, i, name);
+		if (status) {
+			err("pci_hp_register failed with error %d", status);
+			goto error_info;
+		}
+		dbg("slot registered with name: %s", slot_name(slot));
+
+		/* Add slot to our internal list */
+		down_write(&list_rwsem);
+		list_add(&slot->slot_list, &slot_list);
+		slots++;
+		up_write(&list_rwsem);
+	}
+	return 0;
+error_info:
+	kfree(info);
+error_hpslot:
+	kfree(hotplug_slot);
+error_slot:
+	kfree(slot);
+error:
+	return status;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
+
+int
+cpci_hp_unregister_bus(struct pci_bus *bus)
+{
+	struct slot *slot;
+	struct slot *tmp;
+	int status = 0;
+
+	down_write(&list_rwsem);
+	if (!slots) {
+		up_write(&list_rwsem);
+		return -1;
+	}
+	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
+		if (slot->bus == bus) {
+			list_del(&slot->slot_list);
+			slots--;
+
+			dbg("deregistering slot %s", slot_name(slot));
+			status = pci_hp_deregister(slot->hotplug_slot);
+			if (status) {
+				err("pci_hp_deregister failed with error %d",
+				    status);
+				break;
+			}
+		}
+	}
+	up_write(&list_rwsem);
+	return status;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
+
+/* This is the interrupt mode interrupt handler */
+static irqreturn_t
+cpci_hp_intr(int irq, void *data)
+{
+	dbg("entered cpci_hp_intr");
+
+	/* Check to see if it was our interrupt */
+	if ((controller->irq_flags & IRQF_SHARED) &&
+	    !controller->ops->check_irq(controller->dev_id)) {
+		dbg("exited cpci_hp_intr, not our interrupt");
+		return IRQ_NONE;
+	}
+
+	/* Disable ENUM interrupt */
+	controller->ops->disable_irq();
+
+	/* Trigger processing by the event thread */
+	wake_up_process(cpci_thread);
+	return IRQ_HANDLED;
+}
+
+/*
+ * According to PICMG 2.1 R2.0, section 6.3.2, upon
+ * initialization, the system driver shall clear the
+ * INS bits of the cold-inserted devices.
+ */
+static int
+init_slots(int clear_ins)
+{
+	struct slot *slot;
+	struct pci_dev *dev;
+
+	dbg("%s - enter", __func__);
+	down_read(&list_rwsem);
+	if (!slots) {
+		up_read(&list_rwsem);
+		return -1;
+	}
+	list_for_each_entry(slot, &slot_list, slot_list) {
+		dbg("%s - looking at slot %s", __func__, slot_name(slot));
+		if (clear_ins && cpci_check_and_clear_ins(slot))
+			dbg("%s - cleared INS for slot %s",
+			    __func__, slot_name(slot));
+		dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
+		if (dev) {
+			if (update_adapter_status(slot->hotplug_slot, 1))
+				warn("failure to update adapter file");
+			if (update_latch_status(slot->hotplug_slot, 1))
+				warn("failure to update latch file");
+			slot->dev = dev;
+		}
+	}
+	up_read(&list_rwsem);
+	dbg("%s - exit", __func__);
+	return 0;
+}
+
+static int
+check_slots(void)
+{
+	struct slot *slot;
+	int extracted;
+	int inserted;
+	u16 hs_csr;
+
+	down_read(&list_rwsem);
+	if (!slots) {
+		up_read(&list_rwsem);
+		err("no slots registered, shutting down");
+		return -1;
+	}
+	extracted = inserted = 0;
+	list_for_each_entry(slot, &slot_list, slot_list) {
+		dbg("%s - looking at slot %s", __func__, slot_name(slot));
+		if (cpci_check_and_clear_ins(slot)) {
+			/*
+			 * Some broken hardware (e.g. PLX 9054AB) asserts
+			 * ENUM# twice...
+			 */
+			if (slot->dev) {
+				warn("slot %s already inserted",
+				     slot_name(slot));
+				inserted++;
+				continue;
+			}
+
+			/* Process insertion */
+			dbg("%s - slot %s inserted", __func__, slot_name(slot));
+
+			/* GSM, debug */
+			hs_csr = cpci_get_hs_csr(slot);
+			dbg("%s - slot %s HS_CSR (1) = %04x",
+			    __func__, slot_name(slot), hs_csr);
+
+			/* Configure device */
+			dbg("%s - configuring slot %s",
+			    __func__, slot_name(slot));
+			if (cpci_configure_slot(slot)) {
+				err("%s - could not configure slot %s",
+				    __func__, slot_name(slot));
+				continue;
+			}
+			dbg("%s - finished configuring slot %s",
+			    __func__, slot_name(slot));
+
+			/* GSM, debug */
+			hs_csr = cpci_get_hs_csr(slot);
+			dbg("%s - slot %s HS_CSR (2) = %04x",
+			    __func__, slot_name(slot), hs_csr);
+
+			if (update_latch_status(slot->hotplug_slot, 1))
+				warn("failure to update latch file");
+
+			if (update_adapter_status(slot->hotplug_slot, 1))
+				warn("failure to update adapter file");
+
+			cpci_led_off(slot);
+
+			/* GSM, debug */
+			hs_csr = cpci_get_hs_csr(slot);
+			dbg("%s - slot %s HS_CSR (3) = %04x",
+			    __func__, slot_name(slot), hs_csr);
+
+			inserted++;
+		} else if (cpci_check_ext(slot)) {
+			/* Process extraction request */
+			dbg("%s - slot %s extracted",
+			    __func__, slot_name(slot));
+
+			/* GSM, debug */
+			hs_csr = cpci_get_hs_csr(slot);
+			dbg("%s - slot %s HS_CSR = %04x",
+			    __func__, slot_name(slot), hs_csr);
+
+			if (!slot->extracting) {
+				if (update_latch_status(slot->hotplug_slot, 0))
+					warn("failure to update latch file");
+
+				slot->extracting = 1;
+				atomic_inc(&extracting);
+			}
+			extracted++;
+		} else if (slot->extracting) {
+			hs_csr = cpci_get_hs_csr(slot);
+			if (hs_csr == 0xffff) {
+				/*
+				 * Hmmm, we're likely hosed at this point, should we
+				 * bother trying to tell the driver or not?
+				 */
+				err("card in slot %s was improperly removed",
+				    slot_name(slot));
+				if (update_adapter_status(slot->hotplug_slot, 0))
+					warn("failure to update adapter file");
+				slot->extracting = 0;
+				atomic_dec(&extracting);
+			}
+		}
+	}
+	up_read(&list_rwsem);
+	dbg("inserted=%d, extracted=%d, extracting=%d",
+	    inserted, extracted, atomic_read(&extracting));
+	if (inserted || extracted)
+		return extracted;
+	else if (!atomic_read(&extracting)) {
+		err("cannot find ENUM# source, shutting down");
+		return -1;
+	}
+	return 0;
+}
+
+/* This is the interrupt mode worker thread body */
+static int
+event_thread(void *data)
+{
+	int rc;
+
+	dbg("%s - event thread started", __func__);
+	while (1) {
+		dbg("event thread sleeping");
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (kthread_should_stop())
+			break;
+		do {
+			rc = check_slots();
+			if (rc > 0) {
+				/* Give userspace a chance to handle extraction */
+				msleep(500);
+			} else if (rc < 0) {
+				dbg("%s - error checking slots", __func__);
+				thread_finished = 1;
+				goto out;
+			}
+		} while (atomic_read(&extracting) && !kthread_should_stop());
+		if (kthread_should_stop())
+			break;
+
+		/* Re-enable ENUM# interrupt */
+		dbg("%s - re-enabling irq", __func__);
+		controller->ops->enable_irq();
+	}
+ out:
+	return 0;
+}
+
+/* This is the polling mode worker thread body */
+static int
+poll_thread(void *data)
+{
+	int rc;
+
+	while (1) {
+		if (kthread_should_stop() || signal_pending(current))
+			break;
+		if (controller->ops->query_enum()) {
+			do {
+				rc = check_slots();
+				if (rc > 0) {
+					/* Give userspace a chance to handle extraction */
+					msleep(500);
+				} else if (rc < 0) {
+					dbg("%s - error checking slots", __func__);
+					thread_finished = 1;
+					goto out;
+				}
+			} while (atomic_read(&extracting) && !kthread_should_stop());
+		}
+		msleep(100);
+	}
+ out:
+	return 0;
+}
+
+static int
+cpci_start_thread(void)
+{
+	if (controller->irq)
+		cpci_thread = kthread_run(event_thread, NULL, "cpci_hp_eventd");
+	else
+		cpci_thread = kthread_run(poll_thread, NULL, "cpci_hp_polld");
+	if (IS_ERR(cpci_thread)) {
+		err("Can't start up our thread");
+		return PTR_ERR(cpci_thread);
+	}
+	thread_finished = 0;
+	return 0;
+}
+
+static void
+cpci_stop_thread(void)
+{
+	kthread_stop(cpci_thread);
+	thread_finished = 1;
+}
+
+int
+cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
+{
+	int status = 0;
+
+	if (controller)
+		return -1;
+	if (!(new_controller && new_controller->ops))
+		return -EINVAL;
+	if (new_controller->irq) {
+		if (!(new_controller->ops->enable_irq &&
+		     new_controller->ops->disable_irq))
+			status = -EINVAL;
+		if (request_irq(new_controller->irq,
+			       cpci_hp_intr,
+			       new_controller->irq_flags,
+			       MY_NAME,
+			       new_controller->dev_id)) {
+			err("Can't get irq %d for the hotplug cPCI controller",
+			    new_controller->irq);
+			status = -ENODEV;
+		}
+		dbg("%s - acquired controller irq %d",
+		    __func__, new_controller->irq);
+	}
+	if (!status)
+		controller = new_controller;
+	return status;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
+
+static void
+cleanup_slots(void)
+{
+	struct slot *slot;
+	struct slot *tmp;
+
+	/*
+	 * Unregister all of our slots with the pci_hotplug subsystem,
+	 * and free up all memory that we had allocated.
+	 */
+	down_write(&list_rwsem);
+	if (!slots)
+		goto cleanup_null;
+	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
+		list_del(&slot->slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+cleanup_null:
+	up_write(&list_rwsem);
+	return;
+}
+
+int
+cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
+{
+	int status = 0;
+
+	if (controller) {
+		if (!thread_finished)
+			cpci_stop_thread();
+		if (controller->irq)
+			free_irq(controller->irq, controller->dev_id);
+		controller = NULL;
+		cleanup_slots();
+	} else
+		status = -ENODEV;
+	return status;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
+
+int
+cpci_hp_start(void)
+{
+	static int first = 1;
+	int status;
+
+	dbg("%s - enter", __func__);
+	if (!controller)
+		return -ENODEV;
+
+	down_read(&list_rwsem);
+	if (list_empty(&slot_list)) {
+		up_read(&list_rwsem);
+		return -ENODEV;
+	}
+	up_read(&list_rwsem);
+
+	status = init_slots(first);
+	if (first)
+		first = 0;
+	if (status)
+		return status;
+
+	status = cpci_start_thread();
+	if (status)
+		return status;
+	dbg("%s - thread started", __func__);
+
+	if (controller->irq) {
+		/* Start enum interrupt processing */
+		dbg("%s - enabling irq", __func__);
+		controller->ops->enable_irq();
+	}
+	dbg("%s - exit", __func__);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_start);
+
+int
+cpci_hp_stop(void)
+{
+	if (!controller)
+		return -ENODEV;
+	if (controller->irq) {
+		/* Stop enum interrupt processing */
+		dbg("%s - disabling irq", __func__);
+		controller->ops->disable_irq();
+	}
+	cpci_stop_thread();
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cpci_hp_stop);
+
+int __init
+cpci_hotplug_init(int debug)
+{
+	cpci_debug = debug;
+	return 0;
+}
+
+void __exit
+cpci_hotplug_exit(void)
+{
+	/*
+	 * Clean everything up.
+	 */
+	cpci_hp_stop();
+	cpci_hp_unregister_controller(controller);
+}
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c
new file mode 100644
index 0000000..788db48
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug_pci.c
@@ -0,0 +1,333 @@
+/*
+ * CompactPCI Hot Plug Driver PCI functions
+ *
+ * Copyright (C) 2002,2005 by SOMA Networks, Inc.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/proc_fs.h>
+#include "../pci.h"
+#include "cpci_hotplug.h"
+
+#define MY_NAME	"cpci_hotplug"
+
+extern int cpci_debug;
+
+#define dbg(format, arg...)					\
+	do {							\
+		if (cpci_debug)					\
+			printk (KERN_DEBUG "%s: " format "\n",	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+
+u8 cpci_get_attention_status(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return 0;
+
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return 0;
+
+	return hs_csr & 0x0008 ? 1 : 0;
+}
+
+int cpci_set_attention_status(struct slot *slot, int status)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return 0;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return 0;
+	if (status)
+		hs_csr |= HS_CSR_LOO;
+	else
+		hs_csr &= ~HS_CSR_LOO;
+	if (pci_bus_write_config_word(slot->bus,
+				      slot->devfn,
+				      hs_cap + 2,
+				      hs_csr))
+		return 0;
+	return 1;
+}
+
+u16 cpci_get_hs_csr(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return 0xFFFF;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return 0xFFFF;
+	return hs_csr;
+}
+
+int cpci_check_and_clear_ins(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+	int ins = 0;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return 0;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return 0;
+	if (hs_csr & HS_CSR_INS) {
+		/* Clear INS (by setting it) */
+		if (pci_bus_write_config_word(slot->bus,
+					      slot->devfn,
+					      hs_cap + 2,
+					      hs_csr))
+			ins = 0;
+		else
+			ins = 1;
+	}
+	return ins;
+}
+
+int cpci_check_ext(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+	int ext = 0;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return 0;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return 0;
+	if (hs_csr & HS_CSR_EXT)
+		ext = 1;
+	return ext;
+}
+
+int cpci_clear_ext(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return -ENODEV;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return -ENODEV;
+	if (hs_csr & HS_CSR_EXT) {
+		/* Clear EXT (by setting it) */
+		if (pci_bus_write_config_word(slot->bus,
+					      slot->devfn,
+					      hs_cap + 2,
+					      hs_csr))
+			return -ENODEV;
+	}
+	return 0;
+}
+
+int cpci_led_on(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return -ENODEV;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return -ENODEV;
+	if ((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) {
+		hs_csr |= HS_CSR_LOO;
+		if (pci_bus_write_config_word(slot->bus,
+					      slot->devfn,
+					      hs_cap + 2,
+					      hs_csr)) {
+			err("Could not set LOO for slot %s",
+			    hotplug_slot_name(slot->hotplug_slot));
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+int cpci_led_off(struct slot *slot)
+{
+	int hs_cap;
+	u16 hs_csr;
+
+	hs_cap = pci_bus_find_capability(slot->bus,
+					 slot->devfn,
+					 PCI_CAP_ID_CHSWP);
+	if (!hs_cap)
+		return -ENODEV;
+	if (pci_bus_read_config_word(slot->bus,
+				     slot->devfn,
+				     hs_cap + 2,
+				     &hs_csr))
+		return -ENODEV;
+	if (hs_csr & HS_CSR_LOO) {
+		hs_csr &= ~HS_CSR_LOO;
+		if (pci_bus_write_config_word(slot->bus,
+					      slot->devfn,
+					      hs_cap + 2,
+					      hs_csr)) {
+			err("Could not clear LOO for slot %s",
+			    hotplug_slot_name(slot->hotplug_slot));
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * Device configuration functions
+ */
+
+int cpci_configure_slot(struct slot *slot)
+{
+	struct pci_dev *dev;
+	struct pci_bus *parent;
+	int ret = 0;
+
+	dbg("%s - enter", __func__);
+
+	pci_lock_rescan_remove();
+
+	if (slot->dev == NULL) {
+		dbg("pci_dev null, finding %02x:%02x:%x",
+		    slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
+		slot->dev = pci_get_slot(slot->bus, slot->devfn);
+	}
+
+	/* Still NULL? Well then scan for it! */
+	if (slot->dev == NULL) {
+		int n;
+		dbg("pci_dev still null");
+
+		/*
+		 * This will generate pci_dev structures for all functions, but
+		 * we will only call this case when lookup fails.
+		 */
+		n = pci_scan_slot(slot->bus, slot->devfn);
+		dbg("%s: pci_scan_slot returned %d", __func__, n);
+		slot->dev = pci_get_slot(slot->bus, slot->devfn);
+		if (slot->dev == NULL) {
+			err("Could not find PCI device for slot %02x", slot->number);
+			ret = -ENODEV;
+			goto out;
+		}
+	}
+	parent = slot->dev->bus;
+
+	list_for_each_entry(dev, &parent->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
+			continue;
+		if (pci_is_bridge(dev))
+			pci_hp_add_bridge(dev);
+	}
+
+
+	pci_assign_unassigned_bridge_resources(parent->self);
+
+	pci_bus_add_devices(parent);
+
+ out:
+	pci_unlock_rescan_remove();
+	dbg("%s - exit", __func__);
+	return ret;
+}
+
+int cpci_unconfigure_slot(struct slot *slot)
+{
+	struct pci_dev *dev, *temp;
+
+	dbg("%s - enter", __func__);
+	if (!slot->dev) {
+		err("No device for slot %02x\n", slot->number);
+		return -ENODEV;
+	}
+
+	pci_lock_rescan_remove();
+
+	list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
+			continue;
+		pci_dev_get(dev);
+		pci_stop_and_remove_bus_device(dev);
+		pci_dev_put(dev);
+	}
+	pci_dev_put(slot->dev);
+	slot->dev = NULL;
+
+	pci_unlock_rescan_remove();
+
+	dbg("%s - exit", __func__);
+	return 0;
+}
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
new file mode 100644
index 0000000..66b7bbe
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -0,0 +1,226 @@
+/*
+ * cpcihp_generic.c
+ *
+ * Generic port I/O CompactPCI driver
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * This generic CompactPCI hotplug driver should allow using the PCI hotplug
+ * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit
+ * in a system register that can be read through standard port I/O.
+ *
+ * Send feedback to <scottm@somanetworks.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include "cpci_hotplug.h"
+
+#define DRIVER_VERSION	"0.1"
+#define DRIVER_AUTHOR	"Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC	"Generic port I/O CompactPCI Hot Plug Driver"
+
+#if !defined(MODULE)
+#define MY_NAME	"cpcihp_generic"
+#else
+#define MY_NAME	THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...)					\
+	do {							\
+		if (debug)					\
+			printk (KERN_DEBUG "%s: " format "\n",	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static bool debug;
+static char *bridge;
+static u8 bridge_busnr;
+static u8 bridge_slot;
+static struct pci_bus *bus;
+static u8 first_slot;
+static u8 last_slot;
+static u16 port;
+static unsigned int enum_bit;
+static u8 enum_mask;
+
+static struct cpci_hp_controller_ops generic_hpc_ops;
+static struct cpci_hp_controller generic_hpc;
+
+static int __init validate_parameters(void)
+{
+	char *str;
+	char *p;
+	unsigned long tmp;
+
+	if (!bridge) {
+		info("not configured, disabling.");
+		return -EINVAL;
+	}
+	str = bridge;
+	if (!*str)
+		return -EINVAL;
+
+	tmp = simple_strtoul(str, &p, 16);
+	if (p == str || tmp > 0xff) {
+		err("Invalid hotplug bus bridge device bus number");
+		return -EINVAL;
+	}
+	bridge_busnr = (u8) tmp;
+	dbg("bridge_busnr = 0x%02x", bridge_busnr);
+	if (*p != ':') {
+		err("Invalid hotplug bus bridge device");
+		return -EINVAL;
+	}
+	str = p + 1;
+	tmp = simple_strtoul(str, &p, 16);
+	if (p == str || tmp > 0x1f) {
+		err("Invalid hotplug bus bridge device slot number");
+		return -EINVAL;
+	}
+	bridge_slot = (u8) tmp;
+	dbg("bridge_slot = 0x%02x", bridge_slot);
+
+	dbg("first_slot = 0x%02x", first_slot);
+	dbg("last_slot = 0x%02x", last_slot);
+	if (!(first_slot && last_slot)) {
+		err("Need to specify first_slot and last_slot");
+		return -EINVAL;
+	}
+	if (last_slot < first_slot) {
+		err("first_slot must be less than last_slot");
+		return -EINVAL;
+	}
+
+	dbg("port = 0x%04x", port);
+	dbg("enum_bit = 0x%02x", enum_bit);
+	if (enum_bit > 7) {
+		err("Invalid #ENUM bit");
+		return -EINVAL;
+	}
+	enum_mask = 1 << enum_bit;
+	return 0;
+}
+
+static int query_enum(void)
+{
+	u8 value;
+
+	value = inb_p(port);
+	return ((value & enum_mask) == enum_mask);
+}
+
+static int __init cpcihp_generic_init(void)
+{
+	int status;
+	struct resource *r;
+	struct pci_dev *dev;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION);
+	status = validate_parameters();
+	if (status)
+		return status;
+
+	r = request_region(port, 1, "#ENUM hotswap signal register");
+	if (!r)
+		return -EBUSY;
+
+	dev = pci_get_domain_bus_and_slot(0, bridge_busnr,
+					  PCI_DEVFN(bridge_slot, 0));
+	if (!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+		err("Invalid bridge device %s", bridge);
+		pci_dev_put(dev);
+		return -EINVAL;
+	}
+	bus = dev->subordinate;
+	pci_dev_put(dev);
+
+	memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
+	generic_hpc_ops.query_enum = query_enum;
+	generic_hpc.ops = &generic_hpc_ops;
+
+	status = cpci_hp_register_controller(&generic_hpc);
+	if (status != 0) {
+		err("Could not register cPCI hotplug controller");
+		return -ENODEV;
+	}
+	dbg("registered controller");
+
+	status = cpci_hp_register_bus(bus, first_slot, last_slot);
+	if (status != 0) {
+		err("Could not register cPCI hotplug bus");
+		goto init_bus_register_error;
+	}
+	dbg("registered bus");
+
+	status = cpci_hp_start();
+	if (status != 0) {
+		err("Could not started cPCI hotplug system");
+		goto init_start_error;
+	}
+	dbg("started cpci hp system");
+	return 0;
+init_start_error:
+	cpci_hp_unregister_bus(bus);
+init_bus_register_error:
+	cpci_hp_unregister_controller(&generic_hpc);
+	err("status = %d", status);
+	return status;
+
+}
+
+static void __exit cpcihp_generic_exit(void)
+{
+	cpci_hp_stop();
+	cpci_hp_unregister_bus(bus);
+	cpci_hp_unregister_controller(&generic_hpc);
+	release_region(port, 1);
+}
+
+module_init(cpcihp_generic_init);
+module_exit(cpcihp_generic_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(bridge, charp, 0);
+MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)");
+module_param(first_slot, byte, 0);
+MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number");
+module_param(last_slot, byte, 0);
+MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number");
+module_param(port, ushort, 0);
+MODULE_PARM_DESC(port, "#ENUM signal I/O port");
+module_param(enum_bit, uint, 0);
+MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)");
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c
new file mode 100644
index 0000000..7ecf34e
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_zt5550.c
@@ -0,0 +1,328 @@
+/*
+ * cpcihp_zt5550.c
+ *
+ * Intel/Ziatech ZT5550 CompactPCI Host Controller driver
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/signal.h>	/* IRQF_SHARED */
+#include "cpci_hotplug.h"
+#include "cpcihp_zt5550.h"
+
+#define DRIVER_VERSION	"0.2"
+#define DRIVER_AUTHOR	"Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC	"ZT5550 CompactPCI Hot Plug Driver"
+
+#define MY_NAME	"cpcihp_zt5550"
+
+#define dbg(format, arg...)					\
+	do {							\
+		if (debug)					\
+			printk (KERN_DEBUG "%s: " format "\n",	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static bool debug;
+static bool poll;
+static struct cpci_hp_controller_ops zt5550_hpc_ops;
+static struct cpci_hp_controller zt5550_hpc;
+
+/* Primary cPCI bus bridge device */
+static struct pci_dev *bus0_dev;
+static struct pci_bus *bus0;
+
+/* Host controller device */
+static struct pci_dev *hc_dev;
+
+/* Host controller register addresses */
+static void __iomem *hc_registers;
+static void __iomem *csr_hc_index;
+static void __iomem *csr_hc_data;
+static void __iomem *csr_int_status;
+static void __iomem *csr_int_mask;
+
+
+static int zt5550_hc_config(struct pci_dev *pdev)
+{
+	int ret;
+
+	/* Since we know that no boards exist with two HC chips, treat it as an error */
+	if (hc_dev) {
+		err("too many host controller devices?");
+		return -EBUSY;
+	}
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		err("cannot enable %s\n", pci_name(pdev));
+		return ret;
+	}
+
+	hc_dev = pdev;
+	dbg("hc_dev = %p", hc_dev);
+	dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1));
+	dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1));
+
+	if (!request_mem_region(pci_resource_start(hc_dev, 1),
+				pci_resource_len(hc_dev, 1), MY_NAME)) {
+		err("cannot reserve MMIO region");
+		ret = -ENOMEM;
+		goto exit_disable_device;
+	}
+
+	hc_registers =
+	    ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
+	if (!hc_registers) {
+		err("cannot remap MMIO region %llx @ %llx",
+			(unsigned long long)pci_resource_len(hc_dev, 1),
+			(unsigned long long)pci_resource_start(hc_dev, 1));
+		ret = -ENODEV;
+		goto exit_release_region;
+	}
+
+	csr_hc_index = hc_registers + CSR_HCINDEX;
+	csr_hc_data = hc_registers + CSR_HCDATA;
+	csr_int_status = hc_registers + CSR_INTSTAT;
+	csr_int_mask = hc_registers + CSR_INTMASK;
+
+	/*
+	 * Disable host control, fault and serial interrupts
+	 */
+	dbg("disabling host control, fault and serial interrupts");
+	writeb((u8) HC_INT_MASK_REG, csr_hc_index);
+	writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data);
+	dbg("disabled host control, fault and serial interrupts");
+
+	/*
+	 * Disable timer0, timer1 and ENUM interrupts
+	 */
+	dbg("disabling timer0, timer1 and ENUM interrupts");
+	writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask);
+	dbg("disabled timer0, timer1 and ENUM interrupts");
+	return 0;
+
+exit_release_region:
+	release_mem_region(pci_resource_start(hc_dev, 1),
+			   pci_resource_len(hc_dev, 1));
+exit_disable_device:
+	pci_disable_device(hc_dev);
+	return ret;
+}
+
+static int zt5550_hc_cleanup(void)
+{
+	if (!hc_dev)
+		return -ENODEV;
+
+	iounmap(hc_registers);
+	release_mem_region(pci_resource_start(hc_dev, 1),
+			   pci_resource_len(hc_dev, 1));
+	pci_disable_device(hc_dev);
+	return 0;
+}
+
+static int zt5550_hc_query_enum(void)
+{
+	u8 value;
+
+	value = inb_p(ENUM_PORT);
+	return ((value & ENUM_MASK) == ENUM_MASK);
+}
+
+static int zt5550_hc_check_irq(void *dev_id)
+{
+	int ret;
+	u8 reg;
+
+	ret = 0;
+	if (dev_id == zt5550_hpc.dev_id) {
+		reg = readb(csr_int_status);
+		if (reg)
+			ret = 1;
+	}
+	return ret;
+}
+
+static int zt5550_hc_enable_irq(void)
+{
+	u8 reg;
+
+	if (hc_dev == NULL)
+		return -ENODEV;
+
+	reg = readb(csr_int_mask);
+	reg = reg & ~ENUM_INT_MASK;
+	writeb(reg, csr_int_mask);
+	return 0;
+}
+
+static int zt5550_hc_disable_irq(void)
+{
+	u8 reg;
+
+	if (hc_dev == NULL)
+		return -ENODEV;
+
+	reg = readb(csr_int_mask);
+	reg = reg | ENUM_INT_MASK;
+	writeb(reg, csr_int_mask);
+	return 0;
+}
+
+static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int status;
+
+	status = zt5550_hc_config(pdev);
+	if (status != 0)
+		return status;
+
+	dbg("returned from zt5550_hc_config");
+
+	memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
+	zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
+	zt5550_hpc.ops = &zt5550_hpc_ops;
+	if (!poll) {
+		zt5550_hpc.irq = hc_dev->irq;
+		zt5550_hpc.irq_flags = IRQF_SHARED;
+		zt5550_hpc.dev_id = hc_dev;
+
+		zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq;
+		zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq;
+		zt5550_hpc_ops.check_irq = zt5550_hc_check_irq;
+	} else {
+		info("using ENUM# polling mode");
+	}
+
+	status = cpci_hp_register_controller(&zt5550_hpc);
+	if (status != 0) {
+		err("could not register cPCI hotplug controller");
+		goto init_hc_error;
+	}
+	dbg("registered controller");
+
+	/* Look for first device matching cPCI bus's bridge vendor and device IDs */
+	bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
+				  PCI_DEVICE_ID_DEC_21154, NULL);
+	if (!bus0_dev) {
+		status = -ENODEV;
+		goto init_register_error;
+	}
+	bus0 = bus0_dev->subordinate;
+	pci_dev_put(bus0_dev);
+
+	status = cpci_hp_register_bus(bus0, 0x0a, 0x0f);
+	if (status != 0) {
+		err("could not register cPCI hotplug bus");
+		goto init_register_error;
+	}
+	dbg("registered bus");
+
+	status = cpci_hp_start();
+	if (status != 0) {
+		err("could not started cPCI hotplug system");
+		cpci_hp_unregister_bus(bus0);
+		goto init_register_error;
+	}
+	dbg("started cpci hp system");
+
+	return 0;
+init_register_error:
+	cpci_hp_unregister_controller(&zt5550_hpc);
+init_hc_error:
+	err("status = %d", status);
+	zt5550_hc_cleanup();
+	return status;
+
+}
+
+static void zt5550_hc_remove_one(struct pci_dev *pdev)
+{
+	cpci_hp_stop();
+	cpci_hp_unregister_bus(bus0);
+	cpci_hp_unregister_controller(&zt5550_hpc);
+	zt5550_hc_cleanup();
+}
+
+
+static struct pci_device_id zt5550_hc_pci_tbl[] = {
+	{ PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
+
+static struct pci_driver zt5550_hc_driver = {
+	.name		= "zt5550_hc",
+	.id_table	= zt5550_hc_pci_tbl,
+	.probe		= zt5550_hc_init_one,
+	.remove		= zt5550_hc_remove_one,
+};
+
+static int __init zt5550_init(void)
+{
+	struct resource *r;
+	int rc;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION);
+	r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register");
+	if (!r)
+		return -EBUSY;
+
+	rc = pci_register_driver(&zt5550_hc_driver);
+	if (rc < 0)
+		release_region(ENUM_PORT, 1);
+	return rc;
+}
+
+static void __exit
+zt5550_exit(void)
+{
+	pci_unregister_driver(&zt5550_hc_driver);
+	release_region(ENUM_PORT, 1);
+}
+
+module_init(zt5550_init);
+module_exit(zt5550_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(poll, bool, 0644);
+MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not");
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.h b/drivers/pci/hotplug/cpcihp_zt5550.h
new file mode 100644
index 0000000..9a57fda
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_zt5550.h
@@ -0,0 +1,79 @@
+/*
+ * cpcihp_zt5550.h
+ *
+ * Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#ifndef _CPCIHP_ZT5550_H
+#define _CPCIHP_ZT5550_H
+
+/* Direct registers */
+#define CSR_HCINDEX		0x00
+#define CSR_HCDATA		0x04
+#define CSR_INTSTAT		0x08
+#define CSR_INTMASK		0x09
+#define CSR_CNT0CMD		0x0C
+#define CSR_CNT1CMD		0x0E
+#define CSR_CNT0		0x10
+#define CSR_CNT1		0x14
+
+/* Masks for interrupt bits in CSR_INTMASK direct register */
+#define CNT0_INT_MASK		0x01
+#define CNT1_INT_MASK		0x02
+#define ENUM_INT_MASK		0x04
+#define ALL_DIRECT_INTS_MASK	0x07
+
+/* Indexed registers (through CSR_INDEX, CSR_DATA) */
+#define HC_INT_MASK_REG		0x04
+#define HC_STATUS_REG		0x08
+#define HC_CMD_REG		0x0C
+#define ARB_CONFIG_GNT_REG	0x10
+#define ARB_CONFIG_CFG_REG	0x12
+#define ARB_CONFIG_REG		0x10
+#define ISOL_CONFIG_REG		0x18
+#define FAULT_STATUS_REG	0x20
+#define FAULT_CONFIG_REG	0x24
+#define WD_CONFIG_REG		0x2C
+#define HC_DIAG_REG		0x30
+#define SERIAL_COMM_REG		0x34
+#define SERIAL_OUT_REG		0x38
+#define SERIAL_IN_REG		0x3C
+
+/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */
+#define SERIAL_INT_MASK		0x01
+#define FAULT_INT_MASK		0x02
+#define HCF_INT_MASK		0x04
+#define ALL_INDEXED_INTS_MASK	0x07
+
+/* Digital I/O port storing ENUM# */
+#define ENUM_PORT	0xE1
+/* Mask to get to the ENUM# bit on the bus */
+#define ENUM_MASK	0x40
+
+#endif				/* _CPCIHP_ZT5550_H */
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
new file mode 100644
index 0000000..b28b2d2
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -0,0 +1,738 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+#ifndef _CPQPHP_H
+#define _CPQPHP_H
+
+#include <linux/interrupt.h>
+#include <asm/io.h>		/* for read? and write? functions */
+#include <linux/delay.h>	/* for delays */
+#include <linux/mutex.h>
+#include <linux/sched.h>	/* for signal_pending() */
+
+#define MY_NAME	"cpqphp"
+
+#define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
+
+
+
+struct smbios_system_slot {
+	u8 type;
+	u8 length;
+	u16 handle;
+	u8 name_string_num;
+	u8 slot_type;
+	u8 slot_width;
+	u8 slot_current_usage;
+	u8 slot_length;
+	u16 slot_number;
+	u8 properties1;
+	u8 properties2;
+} __attribute__ ((packed));
+
+/* offsets to the smbios generic type based on the above structure layout */
+enum smbios_system_slot_offsets {
+	SMBIOS_SLOT_GENERIC_TYPE =	offsetof(struct smbios_system_slot, type),
+	SMBIOS_SLOT_GENERIC_LENGTH =	offsetof(struct smbios_system_slot, length),
+	SMBIOS_SLOT_GENERIC_HANDLE =	offsetof(struct smbios_system_slot, handle),
+	SMBIOS_SLOT_NAME_STRING_NUM =	offsetof(struct smbios_system_slot, name_string_num),
+	SMBIOS_SLOT_TYPE =		offsetof(struct smbios_system_slot, slot_type),
+	SMBIOS_SLOT_WIDTH =		offsetof(struct smbios_system_slot, slot_width),
+	SMBIOS_SLOT_CURRENT_USAGE =	offsetof(struct smbios_system_slot, slot_current_usage),
+	SMBIOS_SLOT_LENGTH =		offsetof(struct smbios_system_slot, slot_length),
+	SMBIOS_SLOT_NUMBER =		offsetof(struct smbios_system_slot, slot_number),
+	SMBIOS_SLOT_PROPERTIES1 =	offsetof(struct smbios_system_slot, properties1),
+	SMBIOS_SLOT_PROPERTIES2 =	offsetof(struct smbios_system_slot, properties2),
+};
+
+struct smbios_generic {
+	u8 type;
+	u8 length;
+	u16 handle;
+} __attribute__ ((packed));
+
+/* offsets to the smbios generic type based on the above structure layout */
+enum smbios_generic_offsets {
+	SMBIOS_GENERIC_TYPE =	offsetof(struct smbios_generic, type),
+	SMBIOS_GENERIC_LENGTH =	offsetof(struct smbios_generic, length),
+	SMBIOS_GENERIC_HANDLE =	offsetof(struct smbios_generic, handle),
+};
+
+struct smbios_entry_point {
+	char anchor[4];
+	u8 ep_checksum;
+	u8 ep_length;
+	u8 major_version;
+	u8 minor_version;
+	u16 max_size_entry;
+	u8 ep_rev;
+	u8 reserved[5];
+	char int_anchor[5];
+	u8 int_checksum;
+	u16 st_length;
+	u32 st_address;
+	u16 number_of_entrys;
+	u8 bcd_rev;
+} __attribute__ ((packed));
+
+/* offsets to the smbios entry point based on the above structure layout */
+enum smbios_entry_point_offsets {
+	ANCHOR =		offsetof(struct smbios_entry_point, anchor[0]),
+	EP_CHECKSUM =		offsetof(struct smbios_entry_point, ep_checksum),
+	EP_LENGTH =		offsetof(struct smbios_entry_point, ep_length),
+	MAJOR_VERSION =		offsetof(struct smbios_entry_point, major_version),
+	MINOR_VERSION =		offsetof(struct smbios_entry_point, minor_version),
+	MAX_SIZE_ENTRY =	offsetof(struct smbios_entry_point, max_size_entry),
+	EP_REV =		offsetof(struct smbios_entry_point, ep_rev),
+	INT_ANCHOR =		offsetof(struct smbios_entry_point, int_anchor[0]),
+	INT_CHECKSUM =		offsetof(struct smbios_entry_point, int_checksum),
+	ST_LENGTH =		offsetof(struct smbios_entry_point, st_length),
+	ST_ADDRESS =		offsetof(struct smbios_entry_point, st_address),
+	NUMBER_OF_ENTRYS =	offsetof(struct smbios_entry_point, number_of_entrys),
+	BCD_REV =		offsetof(struct smbios_entry_point, bcd_rev),
+};
+
+struct ctrl_reg {			/* offset */
+	u8	slot_RST;		/* 0x00 */
+	u8	slot_enable;		/* 0x01 */
+	u16	misc;			/* 0x02 */
+	u32	led_control;		/* 0x04 */
+	u32	int_input_clear;	/* 0x08 */
+	u32	int_mask;		/* 0x0a */
+	u8	reserved0;		/* 0x10 */
+	u8	reserved1;		/* 0x11 */
+	u8	reserved2;		/* 0x12 */
+	u8	gen_output_AB;		/* 0x13 */
+	u32	non_int_input;		/* 0x14 */
+	u32	reserved3;		/* 0x18 */
+	u32	reserved4;		/* 0x1a */
+	u32	reserved5;		/* 0x20 */
+	u8	reserved6;		/* 0x24 */
+	u8	reserved7;		/* 0x25 */
+	u16	reserved8;		/* 0x26 */
+	u8	slot_mask;		/* 0x28 */
+	u8	reserved9;		/* 0x29 */
+	u8	reserved10;		/* 0x2a */
+	u8	reserved11;		/* 0x2b */
+	u8	slot_SERR;		/* 0x2c */
+	u8	slot_power;		/* 0x2d */
+	u8	reserved12;		/* 0x2e */
+	u8	reserved13;		/* 0x2f */
+	u8	next_curr_freq;		/* 0x30 */
+	u8	reset_freq_mode;	/* 0x31 */
+} __attribute__ ((packed));
+
+/* offsets to the controller registers based on the above structure layout */
+enum ctrl_offsets {
+	SLOT_RST =		offsetof(struct ctrl_reg, slot_RST),
+	SLOT_ENABLE =		offsetof(struct ctrl_reg, slot_enable),
+	MISC =			offsetof(struct ctrl_reg, misc),
+	LED_CONTROL =		offsetof(struct ctrl_reg, led_control),
+	INT_INPUT_CLEAR =	offsetof(struct ctrl_reg, int_input_clear),
+	INT_MASK =		offsetof(struct ctrl_reg, int_mask),
+	CTRL_RESERVED0 =	offsetof(struct ctrl_reg, reserved0),
+	CTRL_RESERVED1 =	offsetof(struct ctrl_reg, reserved1),
+	CTRL_RESERVED2 =	offsetof(struct ctrl_reg, reserved1),
+	GEN_OUTPUT_AB =		offsetof(struct ctrl_reg, gen_output_AB),
+	NON_INT_INPUT =		offsetof(struct ctrl_reg, non_int_input),
+	CTRL_RESERVED3 =	offsetof(struct ctrl_reg, reserved3),
+	CTRL_RESERVED4 =	offsetof(struct ctrl_reg, reserved4),
+	CTRL_RESERVED5 =	offsetof(struct ctrl_reg, reserved5),
+	CTRL_RESERVED6 =	offsetof(struct ctrl_reg, reserved6),
+	CTRL_RESERVED7 =	offsetof(struct ctrl_reg, reserved7),
+	CTRL_RESERVED8 =	offsetof(struct ctrl_reg, reserved8),
+	SLOT_MASK =		offsetof(struct ctrl_reg, slot_mask),
+	CTRL_RESERVED9 =	offsetof(struct ctrl_reg, reserved9),
+	CTRL_RESERVED10 =	offsetof(struct ctrl_reg, reserved10),
+	CTRL_RESERVED11 =	offsetof(struct ctrl_reg, reserved11),
+	SLOT_SERR =		offsetof(struct ctrl_reg, slot_SERR),
+	SLOT_POWER =		offsetof(struct ctrl_reg, slot_power),
+	NEXT_CURR_FREQ =	offsetof(struct ctrl_reg, next_curr_freq),
+	RESET_FREQ_MODE =	offsetof(struct ctrl_reg, reset_freq_mode),
+};
+
+struct hrt {
+	char sig0;
+	char sig1;
+	char sig2;
+	char sig3;
+	u16 unused_IRQ;
+	u16 PCIIRQ;
+	u8 number_of_entries;
+	u8 revision;
+	u16 reserved1;
+	u32 reserved2;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug resource table registers based on the above
+ * structure layout
+ */
+enum hrt_offsets {
+	SIG0 =			offsetof(struct hrt, sig0),
+	SIG1 =			offsetof(struct hrt, sig1),
+	SIG2 =			offsetof(struct hrt, sig2),
+	SIG3 =			offsetof(struct hrt, sig3),
+	UNUSED_IRQ =		offsetof(struct hrt, unused_IRQ),
+	PCIIRQ =		offsetof(struct hrt, PCIIRQ),
+	NUMBER_OF_ENTRIES =	offsetof(struct hrt, number_of_entries),
+	REVISION =		offsetof(struct hrt, revision),
+	HRT_RESERVED1 =		offsetof(struct hrt, reserved1),
+	HRT_RESERVED2 =		offsetof(struct hrt, reserved2),
+};
+
+struct slot_rt {
+	u8 dev_func;
+	u8 primary_bus;
+	u8 secondary_bus;
+	u8 max_bus;
+	u16 io_base;
+	u16 io_length;
+	u16 mem_base;
+	u16 mem_length;
+	u16 pre_mem_base;
+	u16 pre_mem_length;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug slot resource table registers based on the above
+ * structure layout
+ */
+enum slot_rt_offsets {
+	DEV_FUNC =		offsetof(struct slot_rt, dev_func),
+	PRIMARY_BUS =		offsetof(struct slot_rt, primary_bus),
+	SECONDARY_BUS =		offsetof(struct slot_rt, secondary_bus),
+	MAX_BUS =		offsetof(struct slot_rt, max_bus),
+	IO_BASE =		offsetof(struct slot_rt, io_base),
+	IO_LENGTH =		offsetof(struct slot_rt, io_length),
+	MEM_BASE =		offsetof(struct slot_rt, mem_base),
+	MEM_LENGTH =		offsetof(struct slot_rt, mem_length),
+	PRE_MEM_BASE =		offsetof(struct slot_rt, pre_mem_base),
+	PRE_MEM_LENGTH =	offsetof(struct slot_rt, pre_mem_length),
+};
+
+struct pci_func {
+	struct pci_func *next;
+	u8 bus;
+	u8 device;
+	u8 function;
+	u8 is_a_board;
+	u16 status;
+	u8 configured;
+	u8 switch_save;
+	u8 presence_save;
+	u32 base_length[0x06];
+	u8 base_type[0x06];
+	u16 reserved2;
+	u32 config_space[0x20];
+	struct pci_resource *mem_head;
+	struct pci_resource *p_mem_head;
+	struct pci_resource *io_head;
+	struct pci_resource *bus_head;
+	struct timer_list *p_task_event;
+	struct pci_dev *pci_dev;
+};
+
+struct slot {
+	struct slot *next;
+	u8 bus;
+	u8 device;
+	u8 number;
+	u8 is_a_board;
+	u8 configured;
+	u8 state;
+	u8 switch_save;
+	u8 presence_save;
+	u32 capabilities;
+	u16 reserved2;
+	struct timer_list task_event;
+	u8 hp_slot;
+	struct controller *ctrl;
+	void __iomem *p_sm_slot;
+	struct hotplug_slot *hotplug_slot;
+};
+
+struct pci_resource {
+	struct pci_resource *next;
+	u32 base;
+	u32 length;
+};
+
+struct event_info {
+	u32 event_type;
+	u8 hp_slot;
+};
+
+struct controller {
+	struct controller *next;
+	u32 ctrl_int_comp;
+	struct mutex crit_sect;	/* critical section mutex */
+	void __iomem *hpc_reg;	/* cookie for our pci controller location */
+	struct pci_resource *mem_head;
+	struct pci_resource *p_mem_head;
+	struct pci_resource *io_head;
+	struct pci_resource *bus_head;
+	struct pci_dev *pci_dev;
+	struct pci_bus *pci_bus;
+	struct event_info event_queue[10];
+	struct slot *slot;
+	u8 next_event;
+	u8 interrupt;
+	u8 cfgspc_irq;
+	u8 bus;			/* bus number for the pci hotplug controller */
+	u8 rev;
+	u8 slot_device_offset;
+	u8 first_slot;
+	u8 add_support;
+	u8 push_flag;
+	u8 push_button;			/* 0 = no pushbutton, 1 = pushbutton present */
+	u8 slot_switch_type;		/* 0 = no switch, 1 = switch present */
+	u8 defeature_PHP;		/* 0 = PHP not supported, 1 = PHP supported */
+	u8 alternate_base_address;	/* 0 = not supported, 1 = supported */
+	u8 pci_config_space;		/* Index/data access to working registers 0 = not supported, 1 = supported */
+	u8 pcix_speed_capability;	/* PCI-X */
+	u8 pcix_support;		/* PCI-X */
+	u16 vendor_id;
+	struct work_struct int_task_event;
+	wait_queue_head_t queue;	/* sleep & wake process */
+	struct dentry *dentry;		/* debugfs dentry */
+};
+
+struct irq_mapping {
+	u8 barber_pole;
+	u8 valid_INT;
+	u8 interrupt[4];
+};
+
+struct resource_lists {
+	struct pci_resource *mem_head;
+	struct pci_resource *p_mem_head;
+	struct pci_resource *io_head;
+	struct pci_resource *bus_head;
+	struct irq_mapping *irqs;
+};
+
+#define ROM_PHY_ADDR			0x0F0000
+#define ROM_PHY_LEN			0x00ffff
+
+#define PCI_HPC_ID			0xA0F7
+#define PCI_SUB_HPC_ID			0xA2F7
+#define PCI_SUB_HPC_ID2			0xA2F8
+#define PCI_SUB_HPC_ID3			0xA2F9
+#define PCI_SUB_HPC_ID_INTC		0xA2FA
+#define PCI_SUB_HPC_ID4			0xA2FD
+
+#define INT_BUTTON_IGNORE		0
+#define INT_PRESENCE_ON			1
+#define INT_PRESENCE_OFF		2
+#define INT_SWITCH_CLOSE		3
+#define INT_SWITCH_OPEN			4
+#define INT_POWER_FAULT			5
+#define INT_POWER_FAULT_CLEAR		6
+#define INT_BUTTON_PRESS		7
+#define INT_BUTTON_RELEASE		8
+#define INT_BUTTON_CANCEL		9
+
+#define STATIC_STATE			0
+#define BLINKINGON_STATE		1
+#define BLINKINGOFF_STATE		2
+#define POWERON_STATE			3
+#define POWEROFF_STATE			4
+
+#define PCISLOT_INTERLOCK_CLOSED	0x00000001
+#define PCISLOT_ADAPTER_PRESENT		0x00000002
+#define PCISLOT_POWERED			0x00000004
+#define PCISLOT_66_MHZ_OPERATION	0x00000008
+#define PCISLOT_64_BIT_OPERATION	0x00000010
+#define PCISLOT_REPLACE_SUPPORTED	0x00000020
+#define PCISLOT_ADD_SUPPORTED		0x00000040
+#define PCISLOT_INTERLOCK_SUPPORTED	0x00000080
+#define PCISLOT_66_MHZ_SUPPORTED	0x00000100
+#define PCISLOT_64_BIT_SUPPORTED	0x00000200
+
+#define PCI_TO_PCI_BRIDGE_CLASS		0x00060400
+
+#define INTERLOCK_OPEN			0x00000002
+#define ADD_NOT_SUPPORTED		0x00000003
+#define CARD_FUNCTIONING		0x00000005
+#define ADAPTER_NOT_SAME		0x00000006
+#define NO_ADAPTER_PRESENT		0x00000009
+#define NOT_ENOUGH_RESOURCES		0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED	0x0000000C
+#define POWER_FAILURE			0x0000000E
+
+#define REMOVE_NOT_SUPPORTED		0x00000003
+
+
+/*
+ * error Messages
+ */
+#define msg_initialization_err	"Initialization failure, error=%d\n"
+#define msg_HPC_rev_error	"Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_compaq_or_intel	"The PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_supported	"this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n"
+#define msg_unable_to_save	"unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+#define msg_button_on		"PCI slot #%d - powering on due to button press.\n"
+#define msg_button_off		"PCI slot #%d - powering off due to button press.\n"
+#define msg_button_cancel	"PCI slot #%d - action canceled due to button press.\n"
+#define msg_button_ignore	"PCI slot #%d - button press ignored.  (action in progress...)\n"
+
+
+/* debugfs functions for the hotplug controller info */
+void cpqhp_initialize_debugfs(void);
+void cpqhp_shutdown_debugfs(void);
+void cpqhp_create_debugfs_files(struct controller *ctrl);
+void cpqhp_remove_debugfs_files(struct controller *ctrl);
+
+/* controller functions */
+void cpqhp_pushbutton_thread(unsigned long event_pointer);
+irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data);
+int cpqhp_find_available_resources(struct controller *ctrl,
+				   void __iomem *rom_start);
+int cpqhp_event_start_thread(void);
+void cpqhp_event_stop_thread(void);
+struct pci_func *cpqhp_slot_create(unsigned char busnumber);
+struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device,
+				 unsigned char index);
+int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func);
+int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func);
+int cpqhp_hardware_test(struct controller *ctrl, int test_num);
+
+/* resource functions */
+int	cpqhp_resource_sort_and_combine	(struct pci_resource **head);
+
+/* pci functions */
+int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
+int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num,
+		      u8 slot);
+int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug);
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func);
+int cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func);
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func);
+int cpqhp_save_slot_config(struct controller *ctrl, struct pci_func *new_slot);
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func);
+void cpqhp_destroy_board_resources(struct pci_func *func);
+int cpqhp_return_board_resources(struct pci_func *func,
+				 struct resource_lists *resources);
+void cpqhp_destroy_resource_list(struct resource_lists *resources);
+int cpqhp_configure_device(struct controller *ctrl, struct pci_func *func);
+int cpqhp_unconfigure_device(struct pci_func *func);
+
+/* Global variables */
+extern int cpqhp_debug;
+extern int cpqhp_legacy_mode;
+extern struct controller *cpqhp_ctrl_list;
+extern struct pci_func *cpqhp_slot_list[256];
+extern struct irq_routing_table *cpqhp_routing_table;
+
+/* these can be gotten rid of, but for debugging they are purty */
+extern u8 cpqhp_nic_irq;
+extern u8 cpqhp_disk_irq;
+
+
+/* inline functions */
+
+static inline const char *slot_name(struct slot *slot)
+{
+	return hotplug_slot_name(slot->hotplug_slot);
+}
+
+/*
+ * return_resource
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+static inline void return_resource(struct pci_resource **head,
+				   struct pci_resource *node)
+{
+	if (!node || !head)
+		return;
+	node->next = *head;
+	*head = node;
+}
+
+static inline void set_SOGO(struct controller *ctrl)
+{
+	u16 misc;
+
+	misc = readw(ctrl->hpc_reg + MISC);
+	misc = (misc | 0x0001) & 0xFFFB;
+	writew(misc, ctrl->hpc_reg + MISC);
+}
+
+
+static inline void amber_LED_on(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control |= (0x01010000L << slot);
+	writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void amber_LED_off(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control &= ~(0x01010000L << slot);
+	writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline int read_amber_LED(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control &= (0x01010000L << slot);
+
+	return led_control ? 1 : 0;
+}
+
+
+static inline void green_LED_on(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control |= 0x0101L << slot;
+	writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+static inline void green_LED_off(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control &= ~(0x0101L << slot);
+	writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void green_LED_blink(struct controller *ctrl, u8 slot)
+{
+	u32 led_control;
+
+	led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+	led_control &= ~(0x0101L << slot);
+	led_control |= (0x0001L << slot);
+	writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void slot_disable(struct controller *ctrl, u8 slot)
+{
+	u8 slot_enable;
+
+	slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+	slot_enable &= ~(0x01 << slot);
+	writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+static inline void slot_enable(struct controller *ctrl, u8 slot)
+{
+	u8 slot_enable;
+
+	slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+	slot_enable |= (0x01 << slot);
+	writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+static inline u8 is_slot_enabled(struct controller *ctrl, u8 slot)
+{
+	u8 slot_enable;
+
+	slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+	slot_enable &= (0x01 << slot);
+	return slot_enable ? 1 : 0;
+}
+
+
+static inline u8 read_slot_enable(struct controller *ctrl)
+{
+	return readb(ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+/**
+ * get_controller_speed - find the current frequency/mode of controller.
+ *
+ * @ctrl: controller to get frequency/mode for.
+ *
+ * Returns controller speed.
+ */
+static inline u8 get_controller_speed(struct controller *ctrl)
+{
+	u8 curr_freq;
+	u16 misc;
+
+	if (ctrl->pcix_support) {
+		curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
+		if ((curr_freq & 0xB0) == 0xB0)
+			return PCI_SPEED_133MHz_PCIX;
+		if ((curr_freq & 0xA0) == 0xA0)
+			return PCI_SPEED_100MHz_PCIX;
+		if ((curr_freq & 0x90) == 0x90)
+			return PCI_SPEED_66MHz_PCIX;
+		if (curr_freq & 0x10)
+			return PCI_SPEED_66MHz;
+
+		return PCI_SPEED_33MHz;
+	}
+
+	misc = readw(ctrl->hpc_reg + MISC);
+	return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+}
+
+
+/**
+ * get_adapter_speed - find the max supported frequency/mode of adapter.
+ *
+ * @ctrl: hotplug controller.
+ * @hp_slot: hotplug slot where adapter is installed.
+ *
+ * Returns adapter speed.
+ */
+static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
+{
+	u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT);
+	dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword);
+	if (ctrl->pcix_support) {
+		if (temp_dword & (0x10000 << hp_slot))
+			return PCI_SPEED_133MHz_PCIX;
+		if (temp_dword & (0x100 << hp_slot))
+			return PCI_SPEED_66MHz_PCIX;
+	}
+
+	if (temp_dword & (0x01 << hp_slot))
+		return PCI_SPEED_66MHz;
+
+	return PCI_SPEED_33MHz;
+}
+
+static inline void enable_slot_power(struct controller *ctrl, u8 slot)
+{
+	u8 slot_power;
+
+	slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+	slot_power |= (0x01 << slot);
+	writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
+}
+
+static inline void disable_slot_power(struct controller *ctrl, u8 slot)
+{
+	u8 slot_power;
+
+	slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+	slot_power &= ~(0x01 << slot);
+	writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
+}
+
+
+static inline int cpq_get_attention_status(struct controller *ctrl, struct slot *slot)
+{
+	u8 hp_slot;
+
+	hp_slot = slot->device - ctrl->slot_device_offset;
+
+	return read_amber_LED(ctrl, hp_slot);
+}
+
+
+static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
+{
+	u8 hp_slot;
+
+	hp_slot = slot->device - ctrl->slot_device_offset;
+
+	return is_slot_enabled(ctrl, hp_slot);
+}
+
+
+static inline int cpq_get_latch_status(struct controller *ctrl,
+				       struct slot *slot)
+{
+	u32 status;
+	u8 hp_slot;
+
+	hp_slot = slot->device - ctrl->slot_device_offset;
+	dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
+	    __func__, slot->device, ctrl->slot_device_offset);
+
+	status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
+
+	return (status == 0) ? 1 : 0;
+}
+
+
+static inline int get_presence_status(struct controller *ctrl,
+				      struct slot *slot)
+{
+	int presence_save = 0;
+	u8 hp_slot;
+	u32 tempdword;
+
+	hp_slot = slot->device - ctrl->slot_device_offset;
+
+	tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+	presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15))
+				>> hp_slot) & 0x02;
+
+	return presence_save;
+}
+
+static inline int wait_for_ctrl_irq(struct controller *ctrl)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	int retval = 0;
+
+	dbg("%s - start\n", __func__);
+	add_wait_queue(&ctrl->queue, &wait);
+	/* Sleep for up to 1 second to wait for the LED to change. */
+	msleep_interruptible(1000);
+	remove_wait_queue(&ctrl->queue, &wait);
+	if (signal_pending(current))
+		retval =  -EINTR;
+
+	dbg("%s - end\n", __func__);
+	return retval;
+}
+
+#include <asm/pci_x86.h>
+static inline int cpqhp_routing_table_length(void)
+{
+	BUG_ON(cpqhp_routing_table == NULL);
+	return ((cpqhp_routing_table->size - sizeof(struct irq_routing_table)) /
+		sizeof(struct irq_info));
+}
+
+#endif
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
new file mode 100644
index 0000000..a53084d
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -0,0 +1,1467 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ * Jan 12, 2003 -	Added 66/100/133MHz PCI-X support,
+ *			Torben Mathiasen <torben.mathiasen@hp.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+
+
+/* Global variables */
+int cpqhp_debug;
+int cpqhp_legacy_mode;
+struct controller *cpqhp_ctrl_list;	/* = NULL */
+struct pci_func *cpqhp_slot_list[256];
+struct irq_routing_table *cpqhp_routing_table;
+
+/* local variables */
+static void __iomem *smbios_table;
+static void __iomem *smbios_start;
+static void __iomem *cpqhp_rom_start;
+static bool power_mode;
+static bool debug;
+static int initialized;
+
+#define DRIVER_VERSION	"0.9.8"
+#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC	"Compaq Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(power_mode, bool, 0644);
+MODULE_PARM_DESC(power_mode, "Power mode enabled or not");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+#define CPQHPC_MODULE_MINOR 208
+
+static inline int is_slot64bit(struct slot *slot)
+{
+	return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0;
+}
+
+static inline int is_slot66mhz(struct slot *slot)
+{
+	return (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0;
+}
+
+/**
+ * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region.
+ * @begin: begin pointer for region to be scanned.
+ * @end: end pointer for region to be scanned.
+ *
+ * Returns pointer to the head of the SMBIOS tables (or %NULL).
+ */
+static void __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
+{
+	void __iomem *fp;
+	void __iomem *endp;
+	u8 temp1, temp2, temp3, temp4;
+	int status = 0;
+
+	endp = (end - sizeof(u32) + 1);
+
+	for (fp = begin; fp <= endp; fp += 16) {
+		temp1 = readb(fp);
+		temp2 = readb(fp+1);
+		temp3 = readb(fp+2);
+		temp4 = readb(fp+3);
+		if (temp1 == '_' &&
+		    temp2 == 'S' &&
+		    temp3 == 'M' &&
+		    temp4 == '_') {
+			status = 1;
+			break;
+		}
+	}
+
+	if (!status)
+		fp = NULL;
+
+	dbg("Discovered SMBIOS Entry point at %p\n", fp);
+
+	return fp;
+}
+
+/**
+ * init_SERR - Initializes the per slot SERR generation.
+ * @ctrl: controller to use
+ *
+ * For unexpected switch opens
+ */
+static int init_SERR(struct controller *ctrl)
+{
+	u32 tempdword;
+	u32 number_of_slots;
+	u8 physical_slot;
+
+	if (!ctrl)
+		return 1;
+
+	tempdword = ctrl->first_slot;
+
+	number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+	/* Loop through slots */
+	while (number_of_slots) {
+		physical_slot = tempdword;
+		writeb(0, ctrl->hpc_reg + SLOT_SERR);
+		tempdword++;
+		number_of_slots--;
+	}
+
+	return 0;
+}
+
+static int init_cpqhp_routing_table(void)
+{
+	int len;
+
+	cpqhp_routing_table = pcibios_get_irq_routing_table();
+	if (cpqhp_routing_table == NULL)
+		return -ENOMEM;
+
+	len = cpqhp_routing_table_length();
+	if (len == 0) {
+		kfree(cpqhp_routing_table);
+		cpqhp_routing_table = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+/* nice debugging output */
+static void pci_print_IRQ_route(void)
+{
+	int len;
+	int loop;
+	u8 tbus, tdevice, tslot;
+
+	len = cpqhp_routing_table_length();
+
+	dbg("bus dev func slot\n");
+	for (loop = 0; loop < len; ++loop) {
+		tbus = cpqhp_routing_table->slots[loop].bus;
+		tdevice = cpqhp_routing_table->slots[loop].devfn;
+		tslot = cpqhp_routing_table->slots[loop].slot;
+		dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot);
+
+	}
+	return;
+}
+
+
+/**
+ * get_subsequent_smbios_entry: get the next entry from bios table.
+ * @smbios_start: where to start in the SMBIOS table
+ * @smbios_table: location of the SMBIOS table
+ * @curr: %NULL or pointer to previously returned structure
+ *
+ * Gets the first entry if previous == NULL;
+ * otherwise, returns the next entry.
+ * Uses global SMBIOS Table pointer.
+ *
+ * Returns a pointer to an SMBIOS structure or NULL if none found.
+ */
+static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,
+						void __iomem *smbios_table,
+						void __iomem *curr)
+{
+	u8 bail = 0;
+	u8 previous_byte = 1;
+	void __iomem *p_temp;
+	void __iomem *p_max;
+
+	if (!smbios_table || !curr)
+		return NULL;
+
+	/* set p_max to the end of the table */
+	p_max = smbios_start + readw(smbios_table + ST_LENGTH);
+
+	p_temp = curr;
+	p_temp += readb(curr + SMBIOS_GENERIC_LENGTH);
+
+	while ((p_temp < p_max) && !bail) {
+		/* Look for the double NULL terminator
+		 * The first condition is the previous byte
+		 * and the second is the curr
+		 */
+		if (!previous_byte && !(readb(p_temp)))
+			bail = 1;
+
+		previous_byte = readb(p_temp);
+		p_temp++;
+	}
+
+	if (p_temp < p_max)
+		return p_temp;
+	else
+		return NULL;
+}
+
+
+/**
+ * get_SMBIOS_entry - return the requested SMBIOS entry or %NULL
+ * @smbios_start: where to start in the SMBIOS table
+ * @smbios_table: location of the SMBIOS table
+ * @type: SMBIOS structure type to be returned
+ * @previous: %NULL or pointer to previously returned structure
+ *
+ * Gets the first entry of the specified type if previous == %NULL;
+ * Otherwise, returns the next entry of the given type.
+ * Uses global SMBIOS Table pointer.
+ * Uses get_subsequent_smbios_entry.
+ *
+ * Returns a pointer to an SMBIOS structure or %NULL if none found.
+ */
+static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
+					void __iomem *smbios_table,
+					u8 type,
+					void __iomem *previous)
+{
+	if (!smbios_table)
+		return NULL;
+
+	if (!previous)
+		previous = smbios_start;
+	else
+		previous = get_subsequent_smbios_entry(smbios_start,
+					smbios_table, previous);
+
+	while (previous)
+		if (readb(previous + SMBIOS_GENERIC_TYPE) != type)
+			previous = get_subsequent_smbios_entry(smbios_start,
+						smbios_table, previous);
+		else
+			break;
+
+	return previous;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+static int ctrl_slot_cleanup (struct controller *ctrl)
+{
+	struct slot *old_slot, *next_slot;
+
+	old_slot = ctrl->slot;
+	ctrl->slot = NULL;
+
+	while (old_slot) {
+		/* memory will be freed by the release_slot callback */
+		next_slot = old_slot->next;
+		pci_hp_deregister (old_slot->hotplug_slot);
+		old_slot = next_slot;
+	}
+
+	cpqhp_remove_debugfs_files(ctrl);
+
+	/* Free IRQ associated with hot plug device */
+	free_irq(ctrl->interrupt, ctrl);
+	/* Unmap the memory */
+	iounmap(ctrl->hpc_reg);
+	/* Finally reclaim PCI mem */
+	release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
+			   pci_resource_len(ctrl->pci_dev, 0));
+
+	return 0;
+}
+
+
+/**
+ * get_slot_mapping - determine logical slot mapping for PCI device
+ *
+ * Won't work for more than one PCI-PCI bridge in a slot.
+ *
+ * @bus_num - bus number of PCI device
+ * @dev_num - device number of PCI device
+ * @slot - Pointer to u8 where slot number will	be returned
+ *
+ * Output:	SUCCESS or FAILURE
+ */
+static int
+get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot)
+{
+	u32 work;
+	long len;
+	long loop;
+
+	u8 tbus, tdevice, tslot, bridgeSlot;
+
+	dbg("%s: %p, %d, %d, %p\n", __func__, bus, bus_num, dev_num, slot);
+
+	bridgeSlot = 0xFF;
+
+	len = cpqhp_routing_table_length();
+	for (loop = 0; loop < len; ++loop) {
+		tbus = cpqhp_routing_table->slots[loop].bus;
+		tdevice = cpqhp_routing_table->slots[loop].devfn >> 3;
+		tslot = cpqhp_routing_table->slots[loop].slot;
+
+		if ((tbus == bus_num) && (tdevice == dev_num)) {
+			*slot = tslot;
+			return 0;
+		} else {
+			/* Did not get a match on the target PCI device. Check
+			 * if the current IRQ table entry is a PCI-to-PCI
+			 * bridge device.  If so, and it's secondary bus
+			 * matches the bus number for the target device, I need
+			 * to save the bridge's slot number.  If I can not find
+			 * an entry for the target device, I will have to
+			 * assume it's on the other side of the bridge, and
+			 * assign it the bridge's slot.
+			 */
+			bus->number = tbus;
+			pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0),
+						PCI_CLASS_REVISION, &work);
+
+			if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+				pci_bus_read_config_dword(bus,
+							PCI_DEVFN(tdevice, 0),
+							PCI_PRIMARY_BUS, &work);
+				// See if bridge's secondary bus matches target bus.
+				if (((work >> 8) & 0x000000FF) == (long) bus_num)
+					bridgeSlot = tslot;
+			}
+		}
+
+	}
+
+	/* If we got here, we didn't find an entry in the IRQ mapping table for
+	 * the target PCI device.  If we did determine that the target device
+	 * is on the other side of a PCI-to-PCI bridge, return the slot number
+	 * for the bridge.
+	 */
+	if (bridgeSlot != 0xFF) {
+		*slot = bridgeSlot;
+		return 0;
+	}
+	/* Couldn't find an entry in the routing table for this PCI device */
+	return -1;
+}
+
+
+/**
+ * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off
+ * @ctrl: struct controller to use
+ * @func: PCI device/function info
+ * @status: LED control flag: 1 = LED on, 0 = LED off
+ */
+static int
+cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
+				u32 status)
+{
+	u8 hp_slot;
+
+	if (func == NULL)
+		return 1;
+
+	hp_slot = func->device - ctrl->slot_device_offset;
+
+	/* Wait for exclusive access to hardware */
+	mutex_lock(&ctrl->crit_sect);
+
+	if (status == 1)
+		amber_LED_on (ctrl, hp_slot);
+	else if (status == 0)
+		amber_LED_off (ctrl, hp_slot);
+	else {
+		/* Done with exclusive hardware access */
+		mutex_unlock(&ctrl->crit_sect);
+		return 1;
+	}
+
+	set_SOGO(ctrl);
+
+	/* Wait for SOBS to be unset */
+	wait_for_ctrl_irq (ctrl);
+
+	/* Done with exclusive hardware access */
+	mutex_unlock(&ctrl->crit_sect);
+
+	return 0;
+}
+
+
+/**
+ * set_attention_status - Turns the Amber LED for a slot on or off
+ * @hotplug_slot: slot to change LED on
+ * @status: LED control flag
+ */
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+	struct pci_func *slot_func;
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+	u8 bus;
+	u8 devfn;
+	u8 device;
+	u8 function;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+		return -ENODEV;
+
+	device = devfn >> 3;
+	function = devfn & 0x7;
+	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+	slot_func = cpqhp_slot_find(bus, device, function);
+	if (!slot_func)
+		return -ENODEV;
+
+	return cpqhp_set_attention_status(ctrl, slot_func, status);
+}
+
+
+static int process_SI(struct hotplug_slot *hotplug_slot)
+{
+	struct pci_func *slot_func;
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+	u8 bus;
+	u8 devfn;
+	u8 device;
+	u8 function;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+		return -ENODEV;
+
+	device = devfn >> 3;
+	function = devfn & 0x7;
+	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+	slot_func = cpqhp_slot_find(bus, device, function);
+	if (!slot_func)
+		return -ENODEV;
+
+	slot_func->bus = bus;
+	slot_func->device = device;
+	slot_func->function = function;
+	slot_func->configured = 0;
+	dbg("board_added(%p, %p)\n", slot_func, ctrl);
+	return cpqhp_process_SI(ctrl, slot_func);
+}
+
+
+static int process_SS(struct hotplug_slot *hotplug_slot)
+{
+	struct pci_func *slot_func;
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+	u8 bus;
+	u8 devfn;
+	u8 device;
+	u8 function;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+		return -ENODEV;
+
+	device = devfn >> 3;
+	function = devfn & 0x7;
+	dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+	slot_func = cpqhp_slot_find(bus, device, function);
+	if (!slot_func)
+		return -ENODEV;
+
+	dbg("In %s, slot_func = %p, ctrl = %p\n", __func__, slot_func, ctrl);
+	return cpqhp_process_SS(ctrl, slot_func);
+}
+
+
+static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
+{
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	return cpqhp_hardware_test(ctrl, value);
+}
+
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = get_slot_enabled(ctrl, slot);
+	return 0;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = cpq_get_attention_status(ctrl, slot);
+	return 0;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = cpq_get_latch_status(ctrl, slot);
+
+	return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	struct controller *ctrl = slot->ctrl;
+
+	dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
+
+	*value = get_presence_status(ctrl, slot);
+
+	return 0;
+}
+
+static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
+	.set_attention_status =	set_attention_status,
+	.enable_slot =		process_SI,
+	.disable_slot =		process_SS,
+	.hardware_test =	hardware_test,
+	.get_power_status =	get_power_status,
+	.get_attention_status =	get_attention_status,
+	.get_latch_status =	get_latch_status,
+	.get_adapter_status =	get_adapter_status,
+};
+
+#define SLOT_NAME_SIZE 10
+
+static int ctrl_slot_setup(struct controller *ctrl,
+			void __iomem *smbios_start,
+			void __iomem *smbios_table)
+{
+	struct slot *slot;
+	struct hotplug_slot *hotplug_slot;
+	struct hotplug_slot_info *hotplug_slot_info;
+	struct pci_bus *bus = ctrl->pci_bus;
+	u8 number_of_slots;
+	u8 slot_device;
+	u8 slot_number;
+	u8 ctrl_slot;
+	u32 tempdword;
+	char name[SLOT_NAME_SIZE];
+	void __iomem *slot_entry= NULL;
+	int result;
+
+	dbg("%s\n", __func__);
+
+	tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+	number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+	slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+	slot_number = ctrl->first_slot;
+
+	while (number_of_slots) {
+		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+		if (!slot) {
+			result = -ENOMEM;
+			goto error;
+		}
+
+		slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)),
+						GFP_KERNEL);
+		if (!slot->hotplug_slot) {
+			result = -ENOMEM;
+			goto error_slot;
+		}
+		hotplug_slot = slot->hotplug_slot;
+
+		hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)),
+							GFP_KERNEL);
+		if (!hotplug_slot->info) {
+			result = -ENOMEM;
+			goto error_hpslot;
+		}
+		hotplug_slot_info = hotplug_slot->info;
+
+		slot->ctrl = ctrl;
+		slot->bus = ctrl->bus;
+		slot->device = slot_device;
+		slot->number = slot_number;
+		dbg("slot->number = %u\n", slot->number);
+
+		slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9,
+					slot_entry);
+
+		while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) !=
+				slot->number)) {
+			slot_entry = get_SMBIOS_entry(smbios_start,
+						smbios_table, 9, slot_entry);
+		}
+
+		slot->p_sm_slot = slot_entry;
+
+		init_timer(&slot->task_event);
+		slot->task_event.expires = jiffies + 5 * HZ;
+		slot->task_event.function = cpqhp_pushbutton_thread;
+
+		/*FIXME: these capabilities aren't used but if they are
+		 *	 they need to be correctly implemented
+		 */
+		slot->capabilities |= PCISLOT_REPLACE_SUPPORTED;
+		slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED;
+
+		if (is_slot64bit(slot))
+			slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
+		if (is_slot66mhz(slot))
+			slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
+		if (bus->cur_bus_speed == PCI_SPEED_66MHz)
+			slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
+
+		ctrl_slot =
+			slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4);
+
+		/* Check presence */
+		slot->capabilities |=
+			((((~tempdword) >> 23) |
+			 ((~tempdword) >> 15)) >> ctrl_slot) & 0x02;
+		/* Check the switch state */
+		slot->capabilities |=
+			((~tempdword & 0xFF) >> ctrl_slot) & 0x01;
+		/* Check the slot enable */
+		slot->capabilities |=
+			((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
+
+		/* register this slot with the hotplug pci core */
+		hotplug_slot->release = &release_slot;
+		hotplug_slot->private = slot;
+		snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
+		hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
+
+		hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot);
+		hotplug_slot_info->attention_status =
+			cpq_get_attention_status(ctrl, slot);
+		hotplug_slot_info->latch_status =
+			cpq_get_latch_status(ctrl, slot);
+		hotplug_slot_info->adapter_status =
+			get_presence_status(ctrl, slot);
+
+		dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
+				slot->bus, slot->device,
+				slot->number, ctrl->slot_device_offset,
+				slot_number);
+		result = pci_hp_register(hotplug_slot,
+					 ctrl->pci_dev->bus,
+					 slot->device,
+					 name);
+		if (result) {
+			err("pci_hp_register failed with error %d\n", result);
+			goto error_info;
+		}
+
+		slot->next = ctrl->slot;
+		ctrl->slot = slot;
+
+		number_of_slots--;
+		slot_device++;
+		slot_number++;
+	}
+
+	return 0;
+error_info:
+	kfree(hotplug_slot_info);
+error_hpslot:
+	kfree(hotplug_slot);
+error_slot:
+	kfree(slot);
+error:
+	return result;
+}
+
+static int one_time_init(void)
+{
+	int loop;
+	int retval = 0;
+
+	if (initialized)
+		return 0;
+
+	power_mode = 0;
+
+	retval = init_cpqhp_routing_table();
+	if (retval)
+		goto error;
+
+	if (cpqhp_debug)
+		pci_print_IRQ_route();
+
+	dbg("Initialize + Start the notification mechanism \n");
+
+	retval = cpqhp_event_start_thread();
+	if (retval)
+		goto error;
+
+	dbg("Initialize slot lists\n");
+	for (loop = 0; loop < 256; loop++)
+		cpqhp_slot_list[loop] = NULL;
+
+	/* FIXME: We also need to hook the NMI handler eventually.
+	 * this also needs to be worked with Christoph
+	 * register_NMI_handler();
+	 */
+	/* Map rom address */
+	cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+	if (!cpqhp_rom_start) {
+		err ("Could not ioremap memory region for ROM\n");
+		retval = -EIO;
+		goto error;
+	}
+
+	/* Now, map the int15 entry point if we are on compaq specific
+	 * hardware
+	 */
+	compaq_nvram_init(cpqhp_rom_start);
+
+	/* Map smbios table entry point structure */
+	smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
+					cpqhp_rom_start + ROM_PHY_LEN);
+	if (!smbios_table) {
+		err ("Could not find the SMBIOS pointer in memory\n");
+		retval = -EIO;
+		goto error_rom_start;
+	}
+
+	smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
+					readw(smbios_table + ST_LENGTH));
+	if (!smbios_start) {
+		err ("Could not ioremap memory region taken from SMBIOS values\n");
+		retval = -EIO;
+		goto error_smbios_start;
+	}
+
+	initialized = 1;
+
+	return retval;
+
+error_smbios_start:
+	iounmap(smbios_start);
+error_rom_start:
+	iounmap(cpqhp_rom_start);
+error:
+	return retval;
+}
+
+static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	u8 num_of_slots = 0;
+	u8 hp_slot = 0;
+	u8 device;
+	u8 bus_cap;
+	u16 temp_word;
+	u16 vendor_id;
+	u16 subsystem_vid;
+	u16 subsystem_deviceid;
+	u32 rc;
+	struct controller *ctrl;
+	struct pci_func *func;
+	struct pci_bus *bus;
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR MY_NAME ": cannot enable PCI device %s (%d)\n",
+			pci_name(pdev), err);
+		return err;
+	}
+
+	bus = pdev->subordinate;
+	if (!bus) {
+		dev_notice(&pdev->dev, "the device is not a bridge, skipping\n");
+		rc = -ENODEV;
+		goto err_disable_device;
+	}
+
+	/* Need to read VID early b/c it's used to differentiate CPQ and INTC
+	 * discovery
+	 */
+	vendor_id = pdev->vendor;
+	if ((vendor_id != PCI_VENDOR_ID_COMPAQ) &&
+	    (vendor_id != PCI_VENDOR_ID_INTEL)) {
+		err(msg_HPC_non_compaq_or_intel);
+		rc = -ENODEV;
+		goto err_disable_device;
+	}
+	dbg("Vendor ID: %x\n", vendor_id);
+
+	dbg("revision: %d\n", pdev->revision);
+	if ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!pdev->revision)) {
+		err(msg_HPC_rev_error);
+		rc = -ENODEV;
+		goto err_disable_device;
+	}
+
+	/* Check for the proper subsystem IDs
+	 * Intel uses a different SSID programming model than Compaq.
+	 * For Intel, each SSID bit identifies a PHP capability.
+	 * Also Intel HPCs may have RID=0.
+	 */
+	if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
+		err(msg_HPC_not_supported);
+		return -ENODEV;
+	}
+
+	/* TODO: This code can be made to support non-Compaq or Intel
+	 * subsystem IDs
+	 */
+	subsystem_vid = pdev->subsystem_vendor;
+	dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
+	if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
+		err(msg_HPC_non_compaq_or_intel);
+		rc = -ENODEV;
+		goto err_disable_device;
+	}
+
+	ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL);
+	if (!ctrl) {
+		err("%s : out of memory\n", __func__);
+		rc = -ENOMEM;
+		goto err_disable_device;
+	}
+
+	subsystem_deviceid = pdev->subsystem_device;
+
+	info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
+
+	/* Set Vendor ID, so it can be accessed later from other
+	 * functions
+	 */
+	ctrl->vendor_id = vendor_id;
+
+	switch (subsystem_vid) {
+	case PCI_VENDOR_ID_COMPAQ:
+		if (pdev->revision >= 0x13) { /* CIOBX */
+			ctrl->push_flag = 1;
+			ctrl->slot_switch_type = 1;
+			ctrl->push_button = 1;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 1;
+			ctrl->pcix_speed_capability = 1;
+			pci_read_config_byte(pdev, 0x41, &bus_cap);
+			if (bus_cap & 0x80) {
+				dbg("bus max supports 133MHz PCI-X\n");
+				bus->max_bus_speed = PCI_SPEED_133MHz_PCIX;
+				break;
+			}
+			if (bus_cap & 0x40) {
+				dbg("bus max supports 100MHz PCI-X\n");
+				bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
+				break;
+			}
+			if (bus_cap & 0x20) {
+				dbg("bus max supports 66MHz PCI-X\n");
+				bus->max_bus_speed = PCI_SPEED_66MHz_PCIX;
+				break;
+			}
+			if (bus_cap & 0x10) {
+				dbg("bus max supports 66MHz PCI\n");
+				bus->max_bus_speed = PCI_SPEED_66MHz;
+				break;
+			}
+
+			break;
+		}
+
+		switch (subsystem_deviceid) {
+		case PCI_SUB_HPC_ID:
+			/* Original 6500/7000 implementation */
+			ctrl->slot_switch_type = 1;
+			bus->max_bus_speed = PCI_SPEED_33MHz;
+			ctrl->push_button = 0;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 0;
+			ctrl->pcix_speed_capability = 0;
+			break;
+		case PCI_SUB_HPC_ID2:
+			/* First Pushbutton implementation */
+			ctrl->push_flag = 1;
+			ctrl->slot_switch_type = 1;
+			bus->max_bus_speed = PCI_SPEED_33MHz;
+			ctrl->push_button = 1;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 0;
+			ctrl->pcix_speed_capability = 0;
+			break;
+		case PCI_SUB_HPC_ID_INTC:
+			/* Third party (6500/7000) */
+			ctrl->slot_switch_type = 1;
+			bus->max_bus_speed = PCI_SPEED_33MHz;
+			ctrl->push_button = 0;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 0;
+			ctrl->pcix_speed_capability = 0;
+			break;
+		case PCI_SUB_HPC_ID3:
+			/* First 66 Mhz implementation */
+			ctrl->push_flag = 1;
+			ctrl->slot_switch_type = 1;
+			bus->max_bus_speed = PCI_SPEED_66MHz;
+			ctrl->push_button = 1;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 0;
+			ctrl->pcix_speed_capability = 0;
+			break;
+		case PCI_SUB_HPC_ID4:
+			/* First PCI-X implementation, 100MHz */
+			ctrl->push_flag = 1;
+			ctrl->slot_switch_type = 1;
+			bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
+			ctrl->push_button = 1;
+			ctrl->pci_config_space = 1;
+			ctrl->defeature_PHP = 1;
+			ctrl->pcix_support = 1;
+			ctrl->pcix_speed_capability = 0;
+			break;
+		default:
+			err(msg_HPC_not_supported);
+			rc = -ENODEV;
+			goto err_free_ctrl;
+		}
+		break;
+
+	case PCI_VENDOR_ID_INTEL:
+		/* Check for speed capability (0=33, 1=66) */
+		if (subsystem_deviceid & 0x0001)
+			bus->max_bus_speed = PCI_SPEED_66MHz;
+		else
+			bus->max_bus_speed = PCI_SPEED_33MHz;
+
+		/* Check for push button */
+		if (subsystem_deviceid & 0x0002)
+			ctrl->push_button = 0;
+		else
+			ctrl->push_button = 1;
+
+		/* Check for slot switch type (0=mechanical, 1=not mechanical) */
+		if (subsystem_deviceid & 0x0004)
+			ctrl->slot_switch_type = 0;
+		else
+			ctrl->slot_switch_type = 1;
+
+		/* PHP Status (0=De-feature PHP, 1=Normal operation) */
+		if (subsystem_deviceid & 0x0008)
+			ctrl->defeature_PHP = 1;	/* PHP supported */
+		else
+			ctrl->defeature_PHP = 0;	/* PHP not supported */
+
+		/* Alternate Base Address Register Interface
+		 * (0=not supported, 1=supported)
+		 */
+		if (subsystem_deviceid & 0x0010)
+			ctrl->alternate_base_address = 1;
+		else
+			ctrl->alternate_base_address = 0;
+
+		/* PCI Config Space Index (0=not supported, 1=supported) */
+		if (subsystem_deviceid & 0x0020)
+			ctrl->pci_config_space = 1;
+		else
+			ctrl->pci_config_space = 0;
+
+		/* PCI-X support */
+		if (subsystem_deviceid & 0x0080) {
+			ctrl->pcix_support = 1;
+			if (subsystem_deviceid & 0x0040)
+				/* 133MHz PCI-X if bit 7 is 1 */
+				ctrl->pcix_speed_capability = 1;
+			else
+				/* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
+				/* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
+				ctrl->pcix_speed_capability = 0;
+		} else {
+			/* Conventional PCI */
+			ctrl->pcix_support = 0;
+			ctrl->pcix_speed_capability = 0;
+		}
+		break;
+
+	default:
+		err(msg_HPC_not_supported);
+		rc = -ENODEV;
+		goto err_free_ctrl;
+	}
+
+	/* Tell the user that we found one. */
+	info("Initializing the PCI hot plug controller residing on PCI bus %d\n",
+					pdev->bus->number);
+
+	dbg("Hotplug controller capabilities:\n");
+	dbg("    speed_capability       %d\n", bus->max_bus_speed);
+	dbg("    slot_switch_type       %s\n", ctrl->slot_switch_type ?
+					"switch present" : "no switch");
+	dbg("    defeature_PHP          %s\n", ctrl->defeature_PHP ?
+					"PHP supported" : "PHP not supported");
+	dbg("    alternate_base_address %s\n", ctrl->alternate_base_address ?
+					"supported" : "not supported");
+	dbg("    pci_config_space       %s\n", ctrl->pci_config_space ?
+					"supported" : "not supported");
+	dbg("    pcix_speed_capability  %s\n", ctrl->pcix_speed_capability ?
+					"supported" : "not supported");
+	dbg("    pcix_support           %s\n", ctrl->pcix_support ?
+					"supported" : "not supported");
+
+	ctrl->pci_dev = pdev;
+	pci_set_drvdata(pdev, ctrl);
+
+	/* make our own copy of the pci bus structure,
+	 * as we like tweaking it a lot */
+	ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL);
+	if (!ctrl->pci_bus) {
+		err("out of memory\n");
+		rc = -ENOMEM;
+		goto err_free_ctrl;
+	}
+
+	ctrl->bus = pdev->bus->number;
+	ctrl->rev = pdev->revision;
+	dbg("bus device function rev: %d %d %d %d\n", ctrl->bus,
+		PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev);
+
+	mutex_init(&ctrl->crit_sect);
+	init_waitqueue_head(&ctrl->queue);
+
+	/* initialize our threads if they haven't already been started up */
+	rc = one_time_init();
+	if (rc)
+		goto err_free_bus;
+
+	dbg("pdev = %p\n", pdev);
+	dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
+	dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0));
+
+	if (!request_mem_region(pci_resource_start(pdev, 0),
+				pci_resource_len(pdev, 0), MY_NAME)) {
+		err("cannot reserve MMIO region\n");
+		rc = -ENOMEM;
+		goto err_free_bus;
+	}
+
+	ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0),
+					pci_resource_len(pdev, 0));
+	if (!ctrl->hpc_reg) {
+		err("cannot remap MMIO region %llx @ %llx\n",
+		    (unsigned long long)pci_resource_len(pdev, 0),
+		    (unsigned long long)pci_resource_start(pdev, 0));
+		rc = -ENODEV;
+		goto err_free_mem_region;
+	}
+
+	/* Check for 66Mhz operation */
+	bus->cur_bus_speed = get_controller_speed(ctrl);
+
+
+	/********************************************************
+	 *
+	 *              Save configuration headers for this and
+	 *              subordinate PCI buses
+	 *
+	 ********************************************************/
+
+	/* find the physical slot number of the first hot plug slot */
+
+	/* Get slot won't work for devices behind bridges, but
+	 * in this case it will always be called for the "base"
+	 * bus/dev/func of a slot.
+	 * CS: this is leveraging the PCIIRQ routing code from the kernel
+	 * (pci-pc.c: get_irq_routing_table) */
+	rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number,
+				(readb(ctrl->hpc_reg + SLOT_MASK) >> 4),
+				&(ctrl->first_slot));
+	dbg("get_slot_mapping: first_slot = %d, returned = %d\n",
+				ctrl->first_slot, rc);
+	if (rc) {
+		err(msg_initialization_err, rc);
+		goto err_iounmap;
+	}
+
+	/* Store PCI Config Space for all devices on this bus */
+	rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK));
+	if (rc) {
+		err("%s: unable to save PCI configuration data, error %d\n",
+				__func__, rc);
+		goto err_iounmap;
+	}
+
+	/*
+	 * Get IO, memory, and IRQ resources for new devices
+	 */
+	/* The next line is required for cpqhp_find_available_resources */
+	ctrl->interrupt = pdev->irq;
+	if (ctrl->interrupt < 0x10) {
+		cpqhp_legacy_mode = 1;
+		dbg("System seems to be configured for Full Table Mapped MPS mode\n");
+	}
+
+	ctrl->cfgspc_irq = 0;
+	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq);
+
+	rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start);
+	ctrl->add_support = !rc;
+	if (rc) {
+		dbg("cpqhp_find_available_resources = 0x%x\n", rc);
+		err("unable to locate PCI configuration resources for hot plug add.\n");
+		goto err_iounmap;
+	}
+
+	/*
+	 * Finish setting up the hot plug ctrl device
+	 */
+	ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+	dbg("NumSlots %d \n", ctrl->slot_device_offset);
+
+	ctrl->next_event = 0;
+
+	/* Setup the slot information structures */
+	rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table);
+	if (rc) {
+		err(msg_initialization_err, 6);
+		err("%s: unable to save PCI configuration data, error %d\n",
+			__func__, rc);
+		goto err_iounmap;
+	}
+
+	/* Mask all general input interrupts */
+	writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
+
+	/* set up the interrupt */
+	dbg("HPC interrupt = %d \n", ctrl->interrupt);
+	if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr,
+			IRQF_SHARED, MY_NAME, ctrl)) {
+		err("Can't get irq %d for the hotplug pci controller\n",
+			ctrl->interrupt);
+		rc = -ENODEV;
+		goto err_iounmap;
+	}
+
+	/* Enable Shift Out interrupt and clear it, also enable SERR on power
+	 * fault
+	 */
+	temp_word = readw(ctrl->hpc_reg + MISC);
+	temp_word |= 0x4006;
+	writew(temp_word, ctrl->hpc_reg + MISC);
+
+	/* Changed 05/05/97 to clear all interrupts at start */
+	writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+	ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+	writel(0x0L, ctrl->hpc_reg + INT_MASK);
+
+	if (!cpqhp_ctrl_list) {
+		cpqhp_ctrl_list = ctrl;
+		ctrl->next = NULL;
+	} else {
+		ctrl->next = cpqhp_ctrl_list;
+		cpqhp_ctrl_list = ctrl;
+	}
+
+	/* turn off empty slots here unless command line option "ON" set
+	 * Wait for exclusive access to hardware
+	 */
+	mutex_lock(&ctrl->crit_sect);
+
+	num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+
+	/* find first device number for the ctrl */
+	device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+
+	while (num_of_slots) {
+		dbg("num_of_slots: %d\n", num_of_slots);
+		func = cpqhp_slot_find(ctrl->bus, device, 0);
+		if (!func)
+			break;
+
+		hp_slot = func->device - ctrl->slot_device_offset;
+		dbg("hp_slot: %d\n", hp_slot);
+
+		/* We have to save the presence info for these slots */
+		temp_word = ctrl->ctrl_int_comp >> 16;
+		func->presence_save = (temp_word >> hp_slot) & 0x01;
+		func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+		if (ctrl->ctrl_int_comp & (0x1L << hp_slot))
+			func->switch_save = 0;
+		else
+			func->switch_save = 0x10;
+
+		if (!power_mode)
+			if (!func->is_a_board) {
+				green_LED_off(ctrl, hp_slot);
+				slot_disable(ctrl, hp_slot);
+			}
+
+		device++;
+		num_of_slots--;
+	}
+
+	if (!power_mode) {
+		set_SOGO(ctrl);
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq(ctrl);
+	}
+
+	rc = init_SERR(ctrl);
+	if (rc) {
+		err("init_SERR failed\n");
+		mutex_unlock(&ctrl->crit_sect);
+		goto err_free_irq;
+	}
+
+	/* Done with exclusive hardware access */
+	mutex_unlock(&ctrl->crit_sect);
+
+	cpqhp_create_debugfs_files(ctrl);
+
+	return 0;
+
+err_free_irq:
+	free_irq(ctrl->interrupt, ctrl);
+err_iounmap:
+	iounmap(ctrl->hpc_reg);
+err_free_mem_region:
+	release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_free_bus:
+	kfree(ctrl->pci_bus);
+err_free_ctrl:
+	kfree(ctrl);
+err_disable_device:
+	pci_disable_device(pdev);
+	return rc;
+}
+
+static void __exit unload_cpqphpd(void)
+{
+	struct pci_func *next;
+	struct pci_func *TempSlot;
+	int loop;
+	u32 rc;
+	struct controller *ctrl;
+	struct controller *tctrl;
+	struct pci_resource *res;
+	struct pci_resource *tres;
+
+	rc = compaq_nvram_store(cpqhp_rom_start);
+
+	ctrl = cpqhp_ctrl_list;
+
+	while (ctrl) {
+		if (ctrl->hpc_reg) {
+			u16 misc;
+			rc = read_slot_enable (ctrl);
+
+			writeb(0, ctrl->hpc_reg + SLOT_SERR);
+			writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK);
+
+			misc = readw(ctrl->hpc_reg + MISC);
+			misc &= 0xFFFD;
+			writew(misc, ctrl->hpc_reg + MISC);
+		}
+
+		ctrl_slot_cleanup(ctrl);
+
+		res = ctrl->io_head;
+		while (res) {
+			tres = res;
+			res = res->next;
+			kfree(tres);
+		}
+
+		res = ctrl->mem_head;
+		while (res) {
+			tres = res;
+			res = res->next;
+			kfree(tres);
+		}
+
+		res = ctrl->p_mem_head;
+		while (res) {
+			tres = res;
+			res = res->next;
+			kfree(tres);
+		}
+
+		res = ctrl->bus_head;
+		while (res) {
+			tres = res;
+			res = res->next;
+			kfree(tres);
+		}
+
+		kfree (ctrl->pci_bus);
+
+		tctrl = ctrl;
+		ctrl = ctrl->next;
+		kfree(tctrl);
+	}
+
+	for (loop = 0; loop < 256; loop++) {
+		next = cpqhp_slot_list[loop];
+		while (next != NULL) {
+			res = next->io_head;
+			while (res) {
+				tres = res;
+				res = res->next;
+				kfree(tres);
+			}
+
+			res = next->mem_head;
+			while (res) {
+				tres = res;
+				res = res->next;
+				kfree(tres);
+			}
+
+			res = next->p_mem_head;
+			while (res) {
+				tres = res;
+				res = res->next;
+				kfree(tres);
+			}
+
+			res = next->bus_head;
+			while (res) {
+				tres = res;
+				res = res->next;
+				kfree(tres);
+			}
+
+			TempSlot = next;
+			next = next->next;
+			kfree(TempSlot);
+		}
+	}
+
+	/* Stop the notification mechanism */
+	if (initialized)
+		cpqhp_event_stop_thread();
+
+	/* unmap the rom address */
+	if (cpqhp_rom_start)
+		iounmap(cpqhp_rom_start);
+	if (smbios_start)
+		iounmap(smbios_start);
+}
+
+static struct pci_device_id hpcd_pci_tbl[] = {
+	{
+	/* handle any PCI Hotplug controller */
+	.class =        ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+	.class_mask =   ~0,
+
+	/* no matter who makes it */
+	.vendor =       PCI_ANY_ID,
+	.device =       PCI_ANY_ID,
+	.subvendor =    PCI_ANY_ID,
+	.subdevice =    PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
+
+static struct pci_driver cpqhpc_driver = {
+	.name =		"compaq_pci_hotplug",
+	.id_table =	hpcd_pci_tbl,
+	.probe =	cpqhpc_probe,
+	/* remove:	cpqhpc_remove_one, */
+};
+
+static int __init cpqhpc_init(void)
+{
+	int result;
+
+	cpqhp_debug = debug;
+
+	info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+	cpqhp_initialize_debugfs();
+	result = pci_register_driver(&cpqhpc_driver);
+	dbg("pci_register_driver = %d\n", result);
+	return result;
+}
+
+static void __exit cpqhpc_cleanup(void)
+{
+	dbg("unload_cpqphpd()\n");
+	unload_cpqphpd();
+
+	dbg("pci_unregister_driver\n");
+	pci_unregister_driver(&cpqhpc_driver);
+	cpqhp_shutdown_debugfs();
+}
+
+module_init(cpqhpc_init);
+module_exit(cpqhpc_cleanup);
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
new file mode 100644
index 0000000..c5cbefe
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -0,0 +1,2971 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/kthread.h>
+#include "cpqphp.h"
+
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
+			u8 behind_bridge, struct resource_lists *resources);
+static int configure_new_function(struct controller *ctrl, struct pci_func *func,
+			u8 behind_bridge, struct resource_lists *resources);
+static void interrupt_event_handler(struct controller *ctrl);
+
+
+static struct task_struct *cpqhp_event_thread;
+static unsigned long pushbutton_pending;	/* = 0 */
+
+/* delay is in jiffies to wait for */
+static void long_delay(int delay)
+{
+	/*
+	 * XXX(hch): if someone is bored please convert all callers
+	 * to call msleep_interruptible directly.  They really want
+	 * to specify timeouts in natural units and spend a lot of
+	 * effort converting them to jiffies..
+	 */
+	msleep_interruptible(jiffies_to_msecs(delay));
+}
+
+
+/* FIXME: The following line needs to be somewhere else... */
+#define WRONG_BUS_FREQUENCY 0x07
+static u8 handle_switch_change(u8 change, struct controller *ctrl)
+{
+	int hp_slot;
+	u8 rc = 0;
+	u16 temp_word;
+	struct pci_func *func;
+	struct event_info *taskInfo;
+
+	if (!change)
+		return 0;
+
+	/* Switch Change */
+	dbg("cpqsbd:  Switch interrupt received.\n");
+
+	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+		if (change & (0x1L << hp_slot)) {
+			/*
+			 * this one changed.
+			 */
+			func = cpqhp_slot_find(ctrl->bus,
+				(hp_slot + ctrl->slot_device_offset), 0);
+
+			/* this is the structure that tells the worker thread
+			 * what to do
+			 */
+			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+			ctrl->next_event = (ctrl->next_event + 1) % 10;
+			taskInfo->hp_slot = hp_slot;
+
+			rc++;
+
+			temp_word = ctrl->ctrl_int_comp >> 16;
+			func->presence_save = (temp_word >> hp_slot) & 0x01;
+			func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+			if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+				/*
+				 * Switch opened
+				 */
+
+				func->switch_save = 0;
+
+				taskInfo->event_type = INT_SWITCH_OPEN;
+			} else {
+				/*
+				 * Switch closed
+				 */
+
+				func->switch_save = 0x10;
+
+				taskInfo->event_type = INT_SWITCH_CLOSE;
+			}
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * cpqhp_find_slot - find the struct slot of given device
+ * @ctrl: scan lots of this controller
+ * @device: the device id to find
+ */
+static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
+{
+	struct slot *slot = ctrl->slot;
+
+	while (slot && (slot->device != device))
+		slot = slot->next;
+
+	return slot;
+}
+
+
+static u8 handle_presence_change(u16 change, struct controller *ctrl)
+{
+	int hp_slot;
+	u8 rc = 0;
+	u8 temp_byte;
+	u16 temp_word;
+	struct pci_func *func;
+	struct event_info *taskInfo;
+	struct slot *p_slot;
+
+	if (!change)
+		return 0;
+
+	/*
+	 * Presence Change
+	 */
+	dbg("cpqsbd:  Presence/Notify input change.\n");
+	dbg("         Changed bits are 0x%4.4x\n", change );
+
+	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+		if (change & (0x0101 << hp_slot)) {
+			/*
+			 * this one changed.
+			 */
+			func = cpqhp_slot_find(ctrl->bus,
+				(hp_slot + ctrl->slot_device_offset), 0);
+
+			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+			ctrl->next_event = (ctrl->next_event + 1) % 10;
+			taskInfo->hp_slot = hp_slot;
+
+			rc++;
+
+			p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
+			if (!p_slot)
+				return 0;
+
+			/* If the switch closed, must be a button
+			 * If not in button mode, nevermind
+			 */
+			if (func->switch_save && (ctrl->push_button == 1)) {
+				temp_word = ctrl->ctrl_int_comp >> 16;
+				temp_byte = (temp_word >> hp_slot) & 0x01;
+				temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+				if (temp_byte != func->presence_save) {
+					/*
+					 * button Pressed (doesn't do anything)
+					 */
+					dbg("hp_slot %d button pressed\n", hp_slot);
+					taskInfo->event_type = INT_BUTTON_PRESS;
+				} else {
+					/*
+					 * button Released - TAKE ACTION!!!!
+					 */
+					dbg("hp_slot %d button released\n", hp_slot);
+					taskInfo->event_type = INT_BUTTON_RELEASE;
+
+					/* Cancel if we are still blinking */
+					if ((p_slot->state == BLINKINGON_STATE)
+					    || (p_slot->state == BLINKINGOFF_STATE)) {
+						taskInfo->event_type = INT_BUTTON_CANCEL;
+						dbg("hp_slot %d button cancel\n", hp_slot);
+					} else if ((p_slot->state == POWERON_STATE)
+						   || (p_slot->state == POWEROFF_STATE)) {
+						/* info(msg_button_ignore, p_slot->number); */
+						taskInfo->event_type = INT_BUTTON_IGNORE;
+						dbg("hp_slot %d button ignore\n", hp_slot);
+					}
+				}
+			} else {
+				/* Switch is open, assume a presence change
+				 * Save the presence state
+				 */
+				temp_word = ctrl->ctrl_int_comp >> 16;
+				func->presence_save = (temp_word >> hp_slot) & 0x01;
+				func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+				if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) ||
+				    (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) {
+					/* Present */
+					taskInfo->event_type = INT_PRESENCE_ON;
+				} else {
+					/* Not Present */
+					taskInfo->event_type = INT_PRESENCE_OFF;
+				}
+			}
+		}
+	}
+
+	return rc;
+}
+
+
+static u8 handle_power_fault(u8 change, struct controller *ctrl)
+{
+	int hp_slot;
+	u8 rc = 0;
+	struct pci_func *func;
+	struct event_info *taskInfo;
+
+	if (!change)
+		return 0;
+
+	/*
+	 * power fault
+	 */
+
+	info("power fault interrupt\n");
+
+	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+		if (change & (0x01 << hp_slot)) {
+			/*
+			 * this one changed.
+			 */
+			func = cpqhp_slot_find(ctrl->bus,
+				(hp_slot + ctrl->slot_device_offset), 0);
+
+			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+			ctrl->next_event = (ctrl->next_event + 1) % 10;
+			taskInfo->hp_slot = hp_slot;
+
+			rc++;
+
+			if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
+				/*
+				 * power fault Cleared
+				 */
+				func->status = 0x00;
+
+				taskInfo->event_type = INT_POWER_FAULT_CLEAR;
+			} else {
+				/*
+				 * power fault
+				 */
+				taskInfo->event_type = INT_POWER_FAULT;
+
+				if (ctrl->rev < 4) {
+					amber_LED_on (ctrl, hp_slot);
+					green_LED_off (ctrl, hp_slot);
+					set_SOGO (ctrl);
+
+					/* this is a fatal condition, we want
+					 * to crash the machine to protect from
+					 * data corruption. simulated_NMI
+					 * shouldn't ever return */
+					/* FIXME
+					simulated_NMI(hp_slot, ctrl); */
+
+					/* The following code causes a software
+					 * crash just in case simulated_NMI did
+					 * return */
+					/*FIXME
+					panic(msg_power_fault); */
+				} else {
+					/* set power fault status for this board */
+					func->status = 0xFF;
+					info("power fault bit %x set\n", hp_slot);
+				}
+			}
+		}
+	}
+
+	return rc;
+}
+
+
+/**
+ * sort_by_size - sort nodes on the list by their length, smallest first.
+ * @head: list to sort
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+	struct pci_resource *current_res;
+	struct pci_resource *next_res;
+	int out_of_order = 1;
+
+	if (!(*head))
+		return 1;
+
+	if (!((*head)->next))
+		return 0;
+
+	while (out_of_order) {
+		out_of_order = 0;
+
+		/* Special case for swapping list head */
+		if (((*head)->next) &&
+		    ((*head)->length > (*head)->next->length)) {
+			out_of_order++;
+			current_res = *head;
+			*head = (*head)->next;
+			current_res->next = (*head)->next;
+			(*head)->next = current_res;
+		}
+
+		current_res = *head;
+
+		while (current_res->next && current_res->next->next) {
+			if (current_res->next->length > current_res->next->next->length) {
+				out_of_order++;
+				next_res = current_res->next;
+				current_res->next = current_res->next->next;
+				current_res = current_res->next;
+				next_res->next = current_res->next;
+				current_res->next = next_res;
+			} else
+				current_res = current_res->next;
+		}
+	}  /* End of out_of_order loop */
+
+	return 0;
+}
+
+
+/**
+ * sort_by_max_size - sort nodes on the list by their length, largest first.
+ * @head: list to sort
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+	struct pci_resource *current_res;
+	struct pci_resource *next_res;
+	int out_of_order = 1;
+
+	if (!(*head))
+		return 1;
+
+	if (!((*head)->next))
+		return 0;
+
+	while (out_of_order) {
+		out_of_order = 0;
+
+		/* Special case for swapping list head */
+		if (((*head)->next) &&
+		    ((*head)->length < (*head)->next->length)) {
+			out_of_order++;
+			current_res = *head;
+			*head = (*head)->next;
+			current_res->next = (*head)->next;
+			(*head)->next = current_res;
+		}
+
+		current_res = *head;
+
+		while (current_res->next && current_res->next->next) {
+			if (current_res->next->length < current_res->next->next->length) {
+				out_of_order++;
+				next_res = current_res->next;
+				current_res->next = current_res->next->next;
+				current_res = current_res->next;
+				next_res->next = current_res->next;
+				current_res->next = next_res;
+			} else
+				current_res = current_res->next;
+		}
+	}  /* End of out_of_order loop */
+
+	return 0;
+}
+
+
+/**
+ * do_pre_bridge_resource_split - find node of resources that are unused
+ * @head: new list head
+ * @orig_head: original list head
+ * @alignment: max node size (?)
+ */
+static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head,
+				struct pci_resource **orig_head, u32 alignment)
+{
+	struct pci_resource *prevnode = NULL;
+	struct pci_resource *node;
+	struct pci_resource *split_node;
+	u32 rc;
+	u32 temp_dword;
+	dbg("do_pre_bridge_resource_split\n");
+
+	if (!(*head) || !(*orig_head))
+		return NULL;
+
+	rc = cpqhp_resource_sort_and_combine(head);
+
+	if (rc)
+		return NULL;
+
+	if ((*head)->base != (*orig_head)->base)
+		return NULL;
+
+	if ((*head)->length == (*orig_head)->length)
+		return NULL;
+
+
+	/* If we got here, there the bridge requires some of the resource, but
+	 * we may be able to split some off of the front
+	 */
+
+	node = *head;
+
+	if (node->length & (alignment -1)) {
+		/* this one isn't an aligned length, so we'll make a new entry
+		 * and split it up.
+		 */
+		split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+		if (!split_node)
+			return NULL;
+
+		temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+		split_node->base = node->base;
+		split_node->length = temp_dword;
+
+		node->length -= temp_dword;
+		node->base += split_node->length;
+
+		/* Put it in the list */
+		*head = split_node;
+		split_node->next = node;
+	}
+
+	if (node->length < alignment)
+		return NULL;
+
+	/* Now unlink it */
+	if (*head == node) {
+		*head = node->next;
+	} else {
+		prevnode = *head;
+		while (prevnode->next != node)
+			prevnode = prevnode->next;
+
+		prevnode->next = node->next;
+	}
+	node->next = NULL;
+
+	return node;
+}
+
+
+/**
+ * do_bridge_resource_split - find one node of resources that aren't in use
+ * @head: list head
+ * @alignment: max node size (?)
+ */
+static struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment)
+{
+	struct pci_resource *prevnode = NULL;
+	struct pci_resource *node;
+	u32 rc;
+	u32 temp_dword;
+
+	rc = cpqhp_resource_sort_and_combine(head);
+
+	if (rc)
+		return NULL;
+
+	node = *head;
+
+	while (node->next) {
+		prevnode = node;
+		node = node->next;
+		kfree(prevnode);
+	}
+
+	if (node->length < alignment)
+		goto error;
+
+	if (node->base & (alignment - 1)) {
+		/* Short circuit if adjusted size is too small */
+		temp_dword = (node->base | (alignment-1)) + 1;
+		if ((node->length - (temp_dword - node->base)) < alignment)
+			goto error;
+
+		node->length -= (temp_dword - node->base);
+		node->base = temp_dword;
+	}
+
+	if (node->length & (alignment - 1))
+		/* There's stuff in use after this node */
+		goto error;
+
+	return node;
+error:
+	kfree(node);
+	return NULL;
+}
+
+
+/**
+ * get_io_resource - find first node of given size not in ISA aliasing window.
+ * @head: list to search
+ * @size: size of node to find, must be a power of two.
+ *
+ * Description: This function sorts the resource list by size and then returns
+ * returns the first node of "size" length that is not in the ISA aliasing
+ * window.  If it finds a node larger than "size" it will split it up.
+ */
+static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
+{
+	struct pci_resource *prevnode;
+	struct pci_resource *node;
+	struct pci_resource *split_node;
+	u32 temp_dword;
+
+	if (!(*head))
+		return NULL;
+
+	if (cpqhp_resource_sort_and_combine(head))
+		return NULL;
+
+	if (sort_by_size(head))
+		return NULL;
+
+	for (node = *head; node; node = node->next) {
+		if (node->length < size)
+			continue;
+
+		if (node->base & (size - 1)) {
+			/* this one isn't base aligned properly
+			 * so we'll make a new entry and split it up
+			 */
+			temp_dword = (node->base | (size-1)) + 1;
+
+			/* Short circuit if adjusted size is too small */
+			if ((node->length - (temp_dword - node->base)) < size)
+				continue;
+
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+
+			split_node->base = node->base;
+			split_node->length = temp_dword - node->base;
+			node->base = temp_dword;
+			node->length -= split_node->length;
+
+			/* Put it in the list */
+			split_node->next = node->next;
+			node->next = split_node;
+		} /* End of non-aligned base */
+
+		/* Don't need to check if too small since we already did */
+		if (node->length > size) {
+			/* this one is longer than we need
+			 * so we'll make a new entry and split it up
+			 */
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+
+			split_node->base = node->base + size;
+			split_node->length = node->length - size;
+			node->length = size;
+
+			/* Put it in the list */
+			split_node->next = node->next;
+			node->next = split_node;
+		}  /* End of too big on top end */
+
+		/* For IO make sure it's not in the ISA aliasing space */
+		if (node->base & 0x300L)
+			continue;
+
+		/* If we got here, then it is the right size
+		 * Now take it out of the list and break
+		 */
+		if (*head == node) {
+			*head = node->next;
+		} else {
+			prevnode = *head;
+			while (prevnode->next != node)
+				prevnode = prevnode->next;
+
+			prevnode->next = node->next;
+		}
+		node->next = NULL;
+		break;
+	}
+
+	return node;
+}
+
+
+/**
+ * get_max_resource - get largest node which has at least the given size.
+ * @head: the list to search the node in
+ * @size: the minimum size of the node to find
+ *
+ * Description: Gets the largest node that is at least "size" big from the
+ * list pointed to by head.  It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ */
+static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
+{
+	struct pci_resource *max;
+	struct pci_resource *temp;
+	struct pci_resource *split_node;
+	u32 temp_dword;
+
+	if (cpqhp_resource_sort_and_combine(head))
+		return NULL;
+
+	if (sort_by_max_size(head))
+		return NULL;
+
+	for (max = *head; max; max = max->next) {
+		/* If not big enough we could probably just bail,
+		 * instead we'll continue to the next.
+		 */
+		if (max->length < size)
+			continue;
+
+		if (max->base & (size - 1)) {
+			/* this one isn't base aligned properly
+			 * so we'll make a new entry and split it up
+			 */
+			temp_dword = (max->base | (size-1)) + 1;
+
+			/* Short circuit if adjusted size is too small */
+			if ((max->length - (temp_dword - max->base)) < size)
+				continue;
+
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+
+			split_node->base = max->base;
+			split_node->length = temp_dword - max->base;
+			max->base = temp_dword;
+			max->length -= split_node->length;
+
+			split_node->next = max->next;
+			max->next = split_node;
+		}
+
+		if ((max->base + max->length) & (size - 1)) {
+			/* this one isn't end aligned properly at the top
+			 * so we'll make a new entry and split it up
+			 */
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+			temp_dword = ((max->base + max->length) & ~(size - 1));
+			split_node->base = temp_dword;
+			split_node->length = max->length + max->base
+					     - split_node->base;
+			max->length -= split_node->length;
+
+			split_node->next = max->next;
+			max->next = split_node;
+		}
+
+		/* Make sure it didn't shrink too much when we aligned it */
+		if (max->length < size)
+			continue;
+
+		/* Now take it out of the list */
+		temp = *head;
+		if (temp == max) {
+			*head = max->next;
+		} else {
+			while (temp && temp->next != max)
+				temp = temp->next;
+
+			if (temp)
+				temp->next = max->next;
+		}
+
+		max->next = NULL;
+		break;
+	}
+
+	return max;
+}
+
+
+/**
+ * get_resource - find resource of given size and split up larger ones.
+ * @head: the list to search for resources
+ * @size: the size limit to use
+ *
+ * Description: This function sorts the resource list by size and then
+ * returns the first node of "size" length.  If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
+{
+	struct pci_resource *prevnode;
+	struct pci_resource *node;
+	struct pci_resource *split_node;
+	u32 temp_dword;
+
+	if (cpqhp_resource_sort_and_combine(head))
+		return NULL;
+
+	if (sort_by_size(head))
+		return NULL;
+
+	for (node = *head; node; node = node->next) {
+		dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
+		    __func__, size, node, node->base, node->length);
+		if (node->length < size)
+			continue;
+
+		if (node->base & (size - 1)) {
+			dbg("%s: not aligned\n", __func__);
+			/* this one isn't base aligned properly
+			 * so we'll make a new entry and split it up
+			 */
+			temp_dword = (node->base | (size-1)) + 1;
+
+			/* Short circuit if adjusted size is too small */
+			if ((node->length - (temp_dword - node->base)) < size)
+				continue;
+
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+
+			split_node->base = node->base;
+			split_node->length = temp_dword - node->base;
+			node->base = temp_dword;
+			node->length -= split_node->length;
+
+			split_node->next = node->next;
+			node->next = split_node;
+		} /* End of non-aligned base */
+
+		/* Don't need to check if too small since we already did */
+		if (node->length > size) {
+			dbg("%s: too big\n", __func__);
+			/* this one is longer than we need
+			 * so we'll make a new entry and split it up
+			 */
+			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+			if (!split_node)
+				return NULL;
+
+			split_node->base = node->base + size;
+			split_node->length = node->length - size;
+			node->length = size;
+
+			/* Put it in the list */
+			split_node->next = node->next;
+			node->next = split_node;
+		}  /* End of too big on top end */
+
+		dbg("%s: got one!!!\n", __func__);
+		/* If we got here, then it is the right size
+		 * Now take it out of the list */
+		if (*head == node) {
+			*head = node->next;
+		} else {
+			prevnode = *head;
+			while (prevnode->next != node)
+				prevnode = prevnode->next;
+
+			prevnode->next = node->next;
+		}
+		node->next = NULL;
+		break;
+	}
+	return node;
+}
+
+
+/**
+ * cpqhp_resource_sort_and_combine - sort nodes by base addresses and clean up
+ * @head: the list to sort and clean up
+ *
+ * Description: Sorts all of the nodes in the list in ascending order by
+ * their base addresses.  Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * Returns %0 if success.
+ */
+int cpqhp_resource_sort_and_combine(struct pci_resource **head)
+{
+	struct pci_resource *node1;
+	struct pci_resource *node2;
+	int out_of_order = 1;
+
+	dbg("%s: head = %p, *head = %p\n", __func__, head, *head);
+
+	if (!(*head))
+		return 1;
+
+	dbg("*head->next = %p\n",(*head)->next);
+
+	if (!(*head)->next)
+		return 0;	/* only one item on the list, already sorted! */
+
+	dbg("*head->base = 0x%x\n",(*head)->base);
+	dbg("*head->next->base = 0x%x\n",(*head)->next->base);
+	while (out_of_order) {
+		out_of_order = 0;
+
+		/* Special case for swapping list head */
+		if (((*head)->next) &&
+		    ((*head)->base > (*head)->next->base)) {
+			node1 = *head;
+			(*head) = (*head)->next;
+			node1->next = (*head)->next;
+			(*head)->next = node1;
+			out_of_order++;
+		}
+
+		node1 = (*head);
+
+		while (node1->next && node1->next->next) {
+			if (node1->next->base > node1->next->next->base) {
+				out_of_order++;
+				node2 = node1->next;
+				node1->next = node1->next->next;
+				node1 = node1->next;
+				node2->next = node1->next;
+				node1->next = node2;
+			} else
+				node1 = node1->next;
+		}
+	}  /* End of out_of_order loop */
+
+	node1 = *head;
+
+	while (node1 && node1->next) {
+		if ((node1->base + node1->length) == node1->next->base) {
+			/* Combine */
+			dbg("8..\n");
+			node1->length += node1->next->length;
+			node2 = node1->next;
+			node1->next = node1->next->next;
+			kfree(node2);
+		} else
+			node1 = node1->next;
+	}
+
+	return 0;
+}
+
+
+irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
+{
+	struct controller *ctrl = data;
+	u8 schedule_flag = 0;
+	u8 reset;
+	u16 misc;
+	u32 Diff;
+	u32 temp_dword;
+
+
+	misc = readw(ctrl->hpc_reg + MISC);
+	/*
+	 * Check to see if it was our interrupt
+	 */
+	if (!(misc & 0x000C))
+		return IRQ_NONE;
+
+	if (misc & 0x0004) {
+		/*
+		 * Serial Output interrupt Pending
+		 */
+
+		/* Clear the interrupt */
+		misc |= 0x0004;
+		writew(misc, ctrl->hpc_reg + MISC);
+
+		/* Read to clear posted writes */
+		misc = readw(ctrl->hpc_reg + MISC);
+
+		dbg ("%s - waking up\n", __func__);
+		wake_up_interruptible(&ctrl->queue);
+	}
+
+	if (misc & 0x0008) {
+		/* General-interrupt-input interrupt Pending */
+		Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp;
+
+		ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+		/* Clear the interrupt */
+		writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+		/* Read it back to clear any posted writes */
+		temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+		if (!Diff)
+			/* Clear all interrupts */
+			writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+		schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl);
+		schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl);
+		schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl);
+	}
+
+	reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
+	if (reset & 0x40) {
+		/* Bus reset has completed */
+		reset &= 0xCF;
+		writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE);
+		reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
+		wake_up_interruptible(&ctrl->queue);
+	}
+
+	if (schedule_flag) {
+		wake_up_process(cpqhp_event_thread);
+		dbg("Waking even thread");
+	}
+	return IRQ_HANDLED;
+}
+
+
+/**
+ * cpqhp_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber: bus where new node is to be located
+ *
+ * Returns pointer to the new node or %NULL if unsuccessful.
+ */
+struct pci_func *cpqhp_slot_create(u8 busnumber)
+{
+	struct pci_func *new_slot;
+	struct pci_func *next;
+
+	new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
+	if (new_slot == NULL)
+		return new_slot;
+
+	new_slot->next = NULL;
+	new_slot->configured = 1;
+
+	if (cpqhp_slot_list[busnumber] == NULL) {
+		cpqhp_slot_list[busnumber] = new_slot;
+	} else {
+		next = cpqhp_slot_list[busnumber];
+		while (next->next != NULL)
+			next = next->next;
+		next->next = new_slot;
+	}
+	return new_slot;
+}
+
+
+/**
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns %0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func *old_slot)
+{
+	struct pci_func *next;
+
+	if (old_slot == NULL)
+		return 1;
+
+	next = cpqhp_slot_list[old_slot->bus];
+	if (next == NULL)
+		return 1;
+
+	if (next == old_slot) {
+		cpqhp_slot_list[old_slot->bus] = old_slot->next;
+		cpqhp_destroy_board_resources(old_slot);
+		kfree(old_slot);
+		return 0;
+	}
+
+	while ((next->next != old_slot) && (next->next != NULL))
+		next = next->next;
+
+	if (next->next == old_slot) {
+		next->next = old_slot->next;
+		cpqhp_destroy_board_resources(old_slot);
+		kfree(old_slot);
+		return 0;
+	} else
+		return 2;
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns %0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+	u8 subordinateBus, secondaryBus;
+	u8 tempBus;
+	struct pci_func *next;
+
+	secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+	subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+	for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+		next = cpqhp_slot_list[tempBus];
+
+		while (!slot_remove(next))
+			next = cpqhp_slot_list[tempBus];
+	}
+
+	next = cpqhp_slot_list[bridge->bus];
+
+	if (next == NULL)
+		return 1;
+
+	if (next == bridge) {
+		cpqhp_slot_list[bridge->bus] = bridge->next;
+		goto out;
+	}
+
+	while ((next->next != bridge) && (next->next != NULL))
+		next = next->next;
+
+	if (next->next != bridge)
+		return 2;
+	next->next = bridge->next;
+out:
+	kfree(bridge);
+	return 0;
+}
+
+
+/**
+ * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is %0 for first function found, %1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
+{
+	int found = -1;
+	struct pci_func *func;
+
+	func = cpqhp_slot_list[bus];
+
+	if ((func == NULL) || ((func->device == device) && (index == 0)))
+		return func;
+
+	if (func->device == device)
+		found++;
+
+	while (func->next != NULL) {
+		func = func->next;
+
+		if (func->device == device)
+			found++;
+
+		if (found == index)
+			return func;
+	}
+
+	return NULL;
+}
+
+
+/* DJZ: I don't think is_bridge will work as is.
+ * FIXME */
+static int is_bridge(struct pci_func *func)
+{
+	/* Check the header type */
+	if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+		return 1;
+	else
+		return 0;
+}
+
+
+/**
+ * set_controller_speed - set the frequency and/or mode of a specific controller segment.
+ * @ctrl: controller to change frequency/mode for.
+ * @adapter_speed: the speed of the adapter we want to match.
+ * @hp_slot: the slot number where the adapter is installed.
+ *
+ * Returns %0 if we successfully change frequency and/or mode to match the
+ * adapter speed.
+ */
+static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
+{
+	struct slot *slot;
+	struct pci_bus *bus = ctrl->pci_bus;
+	u8 reg;
+	u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+	u16 reg16;
+	u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
+
+	if (bus->cur_bus_speed == adapter_speed)
+		return 0;
+
+	/* We don't allow freq/mode changes if we find another adapter running
+	 * in another slot on this controller
+	 */
+	for (slot = ctrl->slot; slot; slot = slot->next) {
+		if (slot->device == (hp_slot + ctrl->slot_device_offset))
+			continue;
+		if (!slot->hotplug_slot || !slot->hotplug_slot->info)
+			continue;
+		if (slot->hotplug_slot->info->adapter_status == 0)
+			continue;
+		/* If another adapter is running on the same segment but at a
+		 * lower speed/mode, we allow the new adapter to function at
+		 * this rate if supported
+		 */
+		if (bus->cur_bus_speed < adapter_speed)
+			return 0;
+
+		return 1;
+	}
+
+	/* If the controller doesn't support freq/mode changes and the
+	 * controller is running at a higher mode, we bail
+	 */
+	if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability))
+		return 1;
+
+	/* But we allow the adapter to run at a lower rate if possible */
+	if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability))
+		return 0;
+
+	/* We try to set the max speed supported by both the adapter and
+	 * controller
+	 */
+	if (bus->max_bus_speed < adapter_speed) {
+		if (bus->cur_bus_speed == bus->max_bus_speed)
+			return 0;
+		adapter_speed = bus->max_bus_speed;
+	}
+
+	writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
+	writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
+
+	set_SOGO(ctrl);
+	wait_for_ctrl_irq(ctrl);
+
+	if (adapter_speed != PCI_SPEED_133MHz_PCIX)
+		reg = 0xF5;
+	else
+		reg = 0xF4;
+	pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+	reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
+	reg16 &= ~0x000F;
+	switch (adapter_speed) {
+		case(PCI_SPEED_133MHz_PCIX):
+			reg = 0x75;
+			reg16 |= 0xB;
+			break;
+		case(PCI_SPEED_100MHz_PCIX):
+			reg = 0x74;
+			reg16 |= 0xA;
+			break;
+		case(PCI_SPEED_66MHz_PCIX):
+			reg = 0x73;
+			reg16 |= 0x9;
+			break;
+		case(PCI_SPEED_66MHz):
+			reg = 0x73;
+			reg16 |= 0x1;
+			break;
+		default: /* 33MHz PCI 2.2 */
+			reg = 0x71;
+			break;
+
+	}
+	reg16 |= 0xB << 12;
+	writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
+
+	mdelay(5);
+
+	/* Reenable interrupts */
+	writel(0, ctrl->hpc_reg + INT_MASK);
+
+	pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+	/* Restart state machine */
+	reg = ~0xF;
+	pci_read_config_byte(ctrl->pci_dev, 0x43, &reg);
+	pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
+
+	/* Only if mode change...*/
+	if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
+		((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
+			set_SOGO(ctrl);
+
+	wait_for_ctrl_irq(ctrl);
+	mdelay(1100);
+
+	/* Restore LED/Slot state */
+	writel(leds, ctrl->hpc_reg + LED_CONTROL);
+	writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
+
+	set_SOGO(ctrl);
+	wait_for_ctrl_irq(ctrl);
+
+	bus->cur_bus_speed = adapter_speed;
+	slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+	info("Successfully changed frequency/mode for adapter in slot %d\n",
+			slot->number);
+	return 0;
+}
+
+/* the following routines constitute the bulk of the
+ * hotplug controller logic
+ */
+
+
+/**
+ * board_replaced - Called after a board has been replaced in the system.
+ * @func: PCI device/function information
+ * @ctrl: hotplug controller
+ *
+ * This is only used if we don't have resources for hot add.
+ * Turns power on for the board.
+ * Checks to see if board is the same.
+ * If board is same, reconfigures it.
+ * If board isn't same, turns it back off.
+ */
+static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
+{
+	struct pci_bus *bus = ctrl->pci_bus;
+	u8 hp_slot;
+	u8 temp_byte;
+	u8 adapter_speed;
+	u32 rc = 0;
+
+	hp_slot = func->device - ctrl->slot_device_offset;
+
+	/*
+	 * The switch is open.
+	 */
+	if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot))
+		rc = INTERLOCK_OPEN;
+	/*
+	 * The board is already on
+	 */
+	else if (is_slot_enabled (ctrl, hp_slot))
+		rc = CARD_FUNCTIONING;
+	else {
+		mutex_lock(&ctrl->crit_sect);
+
+		/* turn on board without attaching to the bus */
+		enable_slot_power (ctrl, hp_slot);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		/* Change bits in slot power register to force another shift out
+		 * NOTE: this is to work around the timer bug */
+		temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
+		writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
+		writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		adapter_speed = get_adapter_speed(ctrl, hp_slot);
+		if (bus->cur_bus_speed != adapter_speed)
+			if (set_controller_speed(ctrl, adapter_speed, hp_slot))
+				rc = WRONG_BUS_FREQUENCY;
+
+		/* turn off board without attaching to the bus */
+		disable_slot_power (ctrl, hp_slot);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		mutex_unlock(&ctrl->crit_sect);
+
+		if (rc)
+			return rc;
+
+		mutex_lock(&ctrl->crit_sect);
+
+		slot_enable (ctrl, hp_slot);
+		green_LED_blink (ctrl, hp_slot);
+
+		amber_LED_off (ctrl, hp_slot);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		mutex_unlock(&ctrl->crit_sect);
+
+		/* Wait for ~1 second because of hot plug spec */
+		long_delay(1*HZ);
+
+		/* Check for a power fault */
+		if (func->status == 0xFF) {
+			/* power fault occurred, but it was benign */
+			rc = POWER_FAILURE;
+			func->status = 0;
+		} else
+			rc = cpqhp_valid_replace(ctrl, func);
+
+		if (!rc) {
+			/* It must be the same board */
+
+			rc = cpqhp_configure_board(ctrl, func);
+
+			/* If configuration fails, turn it off
+			 * Get slot won't work for devices behind
+			 * bridges, but in this case it will always be
+			 * called for the "base" bus/dev/func of an
+			 * adapter.
+			 */
+
+			mutex_lock(&ctrl->crit_sect);
+
+			amber_LED_on (ctrl, hp_slot);
+			green_LED_off (ctrl, hp_slot);
+			slot_disable (ctrl, hp_slot);
+
+			set_SOGO(ctrl);
+
+			/* Wait for SOBS to be unset */
+			wait_for_ctrl_irq (ctrl);
+
+			mutex_unlock(&ctrl->crit_sect);
+
+			if (rc)
+				return rc;
+			else
+				return 1;
+
+		} else {
+			/* Something is wrong
+
+			 * Get slot won't work for devices behind bridges, but
+			 * in this case it will always be called for the "base"
+			 * bus/dev/func of an adapter.
+			 */
+
+			mutex_lock(&ctrl->crit_sect);
+
+			amber_LED_on (ctrl, hp_slot);
+			green_LED_off (ctrl, hp_slot);
+			slot_disable (ctrl, hp_slot);
+
+			set_SOGO(ctrl);
+
+			/* Wait for SOBS to be unset */
+			wait_for_ctrl_irq (ctrl);
+
+			mutex_unlock(&ctrl->crit_sect);
+		}
+
+	}
+	return rc;
+
+}
+
+
+/**
+ * board_added - Called after a board has been added to the system.
+ * @func: PCI device/function info
+ * @ctrl: hotplug controller
+ *
+ * Turns power on for the board.
+ * Configures board.
+ */
+static u32 board_added(struct pci_func *func, struct controller *ctrl)
+{
+	u8 hp_slot;
+	u8 temp_byte;
+	u8 adapter_speed;
+	int index;
+	u32 temp_register = 0xFFFFFFFF;
+	u32 rc = 0;
+	struct pci_func *new_slot = NULL;
+	struct pci_bus *bus = ctrl->pci_bus;
+	struct slot *p_slot;
+	struct resource_lists res_lists;
+
+	hp_slot = func->device - ctrl->slot_device_offset;
+	dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n",
+	    __func__, func->device, ctrl->slot_device_offset, hp_slot);
+
+	mutex_lock(&ctrl->crit_sect);
+
+	/* turn on board without attaching to the bus */
+	enable_slot_power(ctrl, hp_slot);
+
+	set_SOGO(ctrl);
+
+	/* Wait for SOBS to be unset */
+	wait_for_ctrl_irq (ctrl);
+
+	/* Change bits in slot power register to force another shift out
+	 * NOTE: this is to work around the timer bug
+	 */
+	temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
+	writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
+	writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
+
+	set_SOGO(ctrl);
+
+	/* Wait for SOBS to be unset */
+	wait_for_ctrl_irq (ctrl);
+
+	adapter_speed = get_adapter_speed(ctrl, hp_slot);
+	if (bus->cur_bus_speed != adapter_speed)
+		if (set_controller_speed(ctrl, adapter_speed, hp_slot))
+			rc = WRONG_BUS_FREQUENCY;
+
+	/* turn off board without attaching to the bus */
+	disable_slot_power (ctrl, hp_slot);
+
+	set_SOGO(ctrl);
+
+	/* Wait for SOBS to be unset */
+	wait_for_ctrl_irq(ctrl);
+
+	mutex_unlock(&ctrl->crit_sect);
+
+	if (rc)
+		return rc;
+
+	p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+	/* turn on board and blink green LED */
+
+	dbg("%s: before down\n", __func__);
+	mutex_lock(&ctrl->crit_sect);
+	dbg("%s: after down\n", __func__);
+
+	dbg("%s: before slot_enable\n", __func__);
+	slot_enable (ctrl, hp_slot);
+
+	dbg("%s: before green_LED_blink\n", __func__);
+	green_LED_blink (ctrl, hp_slot);
+
+	dbg("%s: before amber_LED_blink\n", __func__);
+	amber_LED_off (ctrl, hp_slot);
+
+	dbg("%s: before set_SOGO\n", __func__);
+	set_SOGO(ctrl);
+
+	/* Wait for SOBS to be unset */
+	dbg("%s: before wait_for_ctrl_irq\n", __func__);
+	wait_for_ctrl_irq (ctrl);
+	dbg("%s: after wait_for_ctrl_irq\n", __func__);
+
+	dbg("%s: before up\n", __func__);
+	mutex_unlock(&ctrl->crit_sect);
+	dbg("%s: after up\n", __func__);
+
+	/* Wait for ~1 second because of hot plug spec */
+	dbg("%s: before long_delay\n", __func__);
+	long_delay(1*HZ);
+	dbg("%s: after long_delay\n", __func__);
+
+	dbg("%s: func status = %x\n", __func__, func->status);
+	/* Check for a power fault */
+	if (func->status == 0xFF) {
+		/* power fault occurred, but it was benign */
+		temp_register = 0xFFFFFFFF;
+		dbg("%s: temp register set to %x by power fault\n", __func__, temp_register);
+		rc = POWER_FAILURE;
+		func->status = 0;
+	} else {
+		/* Get vendor/device ID u32 */
+		ctrl->pci_bus->number = func->bus;
+		rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
+		dbg("%s: pci_read_config_dword returns %d\n", __func__, rc);
+		dbg("%s: temp_register is %x\n", __func__, temp_register);
+
+		if (rc != 0) {
+			/* Something's wrong here */
+			temp_register = 0xFFFFFFFF;
+			dbg("%s: temp register set to %x by error\n", __func__, temp_register);
+		}
+		/* Preset return code.  It will be changed later if things go okay. */
+		rc = NO_ADAPTER_PRESENT;
+	}
+
+	/* All F's is an empty slot or an invalid board */
+	if (temp_register != 0xFFFFFFFF) {
+		res_lists.io_head = ctrl->io_head;
+		res_lists.mem_head = ctrl->mem_head;
+		res_lists.p_mem_head = ctrl->p_mem_head;
+		res_lists.bus_head = ctrl->bus_head;
+		res_lists.irqs = NULL;
+
+		rc = configure_new_device(ctrl, func, 0, &res_lists);
+
+		dbg("%s: back from configure_new_device\n", __func__);
+		ctrl->io_head = res_lists.io_head;
+		ctrl->mem_head = res_lists.mem_head;
+		ctrl->p_mem_head = res_lists.p_mem_head;
+		ctrl->bus_head = res_lists.bus_head;
+
+		cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+		cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+		cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+		cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+		if (rc) {
+			mutex_lock(&ctrl->crit_sect);
+
+			amber_LED_on (ctrl, hp_slot);
+			green_LED_off (ctrl, hp_slot);
+			slot_disable (ctrl, hp_slot);
+
+			set_SOGO(ctrl);
+
+			/* Wait for SOBS to be unset */
+			wait_for_ctrl_irq (ctrl);
+
+			mutex_unlock(&ctrl->crit_sect);
+			return rc;
+		} else {
+			cpqhp_save_slot_config(ctrl, func);
+		}
+
+
+		func->status = 0;
+		func->switch_save = 0x10;
+		func->is_a_board = 0x01;
+
+		/* next, we will instantiate the linux pci_dev structures (with
+		 * appropriate driver notification, if already present) */
+		dbg("%s: configure linux pci_dev structure\n", __func__);
+		index = 0;
+		do {
+			new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
+			if (new_slot && !new_slot->pci_dev)
+				cpqhp_configure_device(ctrl, new_slot);
+		} while (new_slot);
+
+		mutex_lock(&ctrl->crit_sect);
+
+		green_LED_on (ctrl, hp_slot);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		mutex_unlock(&ctrl->crit_sect);
+	} else {
+		mutex_lock(&ctrl->crit_sect);
+
+		amber_LED_on (ctrl, hp_slot);
+		green_LED_off (ctrl, hp_slot);
+		slot_disable (ctrl, hp_slot);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+
+		mutex_unlock(&ctrl->crit_sect);
+
+		return rc;
+	}
+	return 0;
+}
+
+
+/**
+ * remove_board - Turns off slot and LEDs
+ * @func: PCI device/function info
+ * @replace_flag: whether replacing or adding a new device
+ * @ctrl: target controller
+ */
+static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl)
+{
+	int index;
+	u8 skip = 0;
+	u8 device;
+	u8 hp_slot;
+	u8 temp_byte;
+	u32 rc;
+	struct resource_lists res_lists;
+	struct pci_func *temp_func;
+
+	if (cpqhp_unconfigure_device(func))
+		return 1;
+
+	device = func->device;
+
+	hp_slot = func->device - ctrl->slot_device_offset;
+	dbg("In %s, hp_slot = %d\n", __func__, hp_slot);
+
+	/* When we get here, it is safe to change base address registers.
+	 * We will attempt to save the base address register lengths */
+	if (replace_flag || !ctrl->add_support)
+		rc = cpqhp_save_base_addr_length(ctrl, func);
+	else if (!func->bus_head && !func->mem_head &&
+		 !func->p_mem_head && !func->io_head) {
+		/* Here we check to see if we've saved any of the board's
+		 * resources already.  If so, we'll skip the attempt to
+		 * determine what's being used. */
+		index = 0;
+		temp_func = cpqhp_slot_find(func->bus, func->device, index++);
+		while (temp_func) {
+			if (temp_func->bus_head || temp_func->mem_head
+			    || temp_func->p_mem_head || temp_func->io_head) {
+				skip = 1;
+				break;
+			}
+			temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++);
+		}
+
+		if (!skip)
+			rc = cpqhp_save_used_resources(ctrl, func);
+	}
+	/* Change status to shutdown */
+	if (func->is_a_board)
+		func->status = 0x01;
+	func->configured = 0;
+
+	mutex_lock(&ctrl->crit_sect);
+
+	green_LED_off (ctrl, hp_slot);
+	slot_disable (ctrl, hp_slot);
+
+	set_SOGO(ctrl);
+
+	/* turn off SERR for slot */
+	temp_byte = readb(ctrl->hpc_reg + SLOT_SERR);
+	temp_byte &= ~(0x01 << hp_slot);
+	writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR);
+
+	/* Wait for SOBS to be unset */
+	wait_for_ctrl_irq (ctrl);
+
+	mutex_unlock(&ctrl->crit_sect);
+
+	if (!replace_flag && ctrl->add_support) {
+		while (func) {
+			res_lists.io_head = ctrl->io_head;
+			res_lists.mem_head = ctrl->mem_head;
+			res_lists.p_mem_head = ctrl->p_mem_head;
+			res_lists.bus_head = ctrl->bus_head;
+
+			cpqhp_return_board_resources(func, &res_lists);
+
+			ctrl->io_head = res_lists.io_head;
+			ctrl->mem_head = res_lists.mem_head;
+			ctrl->p_mem_head = res_lists.p_mem_head;
+			ctrl->bus_head = res_lists.bus_head;
+
+			cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+			cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+			cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+			cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+			if (is_bridge(func)) {
+				bridge_slot_remove(func);
+			} else
+				slot_remove(func);
+
+			func = cpqhp_slot_find(ctrl->bus, device, 0);
+		}
+
+		/* Setup slot structure with entry for empty slot */
+		func = cpqhp_slot_create(ctrl->bus);
+
+		if (func == NULL)
+			return 1;
+
+		func->bus = ctrl->bus;
+		func->device = device;
+		func->function = 0;
+		func->configured = 0;
+		func->switch_save = 0x10;
+		func->is_a_board = 0;
+		func->p_task_event = NULL;
+	}
+
+	return 0;
+}
+
+static void pushbutton_helper_thread(unsigned long data)
+{
+	pushbutton_pending = data;
+	wake_up_process(cpqhp_event_thread);
+}
+
+
+/* this is the main worker thread */
+static int event_thread(void *data)
+{
+	struct controller *ctrl;
+
+	while (1) {
+		dbg("!!!!event_thread sleeping\n");
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+
+		if (kthread_should_stop())
+			break;
+		/* Do stuff here */
+		if (pushbutton_pending)
+			cpqhp_pushbutton_thread(pushbutton_pending);
+		else
+			for (ctrl = cpqhp_ctrl_list; ctrl; ctrl=ctrl->next)
+				interrupt_event_handler(ctrl);
+	}
+	dbg("event_thread signals exit\n");
+	return 0;
+}
+
+int cpqhp_event_start_thread(void)
+{
+	cpqhp_event_thread = kthread_run(event_thread, NULL, "phpd_event");
+	if (IS_ERR(cpqhp_event_thread)) {
+		err ("Can't start up our event thread\n");
+		return PTR_ERR(cpqhp_event_thread);
+	}
+
+	return 0;
+}
+
+
+void cpqhp_event_stop_thread(void)
+{
+	kthread_stop(cpqhp_event_thread);
+}
+
+
+static int update_slot_info(struct controller *ctrl, struct slot *slot)
+{
+	struct hotplug_slot_info *info;
+	int result;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->power_status = get_slot_enabled(ctrl, slot);
+	info->attention_status = cpq_get_attention_status(ctrl, slot);
+	info->latch_status = cpq_get_latch_status(ctrl, slot);
+	info->adapter_status = get_presence_status(ctrl, slot);
+	result = pci_hp_change_slot_info(slot->hotplug_slot, info);
+	kfree (info);
+	return result;
+}
+
+static void interrupt_event_handler(struct controller *ctrl)
+{
+	int loop = 0;
+	int change = 1;
+	struct pci_func *func;
+	u8 hp_slot;
+	struct slot *p_slot;
+
+	while (change) {
+		change = 0;
+
+		for (loop = 0; loop < 10; loop++) {
+			/* dbg("loop %d\n", loop); */
+			if (ctrl->event_queue[loop].event_type != 0) {
+				hp_slot = ctrl->event_queue[loop].hp_slot;
+
+				func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0);
+				if (!func)
+					return;
+
+				p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+				if (!p_slot)
+					return;
+
+				dbg("hp_slot %d, func %p, p_slot %p\n",
+				    hp_slot, func, p_slot);
+
+				if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
+					dbg("button pressed\n");
+				} else if (ctrl->event_queue[loop].event_type ==
+					   INT_BUTTON_CANCEL) {
+					dbg("button cancel\n");
+					del_timer(&p_slot->task_event);
+
+					mutex_lock(&ctrl->crit_sect);
+
+					if (p_slot->state == BLINKINGOFF_STATE) {
+						/* slot is on */
+						dbg("turn on green LED\n");
+						green_LED_on (ctrl, hp_slot);
+					} else if (p_slot->state == BLINKINGON_STATE) {
+						/* slot is off */
+						dbg("turn off green LED\n");
+						green_LED_off (ctrl, hp_slot);
+					}
+
+					info(msg_button_cancel, p_slot->number);
+
+					p_slot->state = STATIC_STATE;
+
+					amber_LED_off (ctrl, hp_slot);
+
+					set_SOGO(ctrl);
+
+					/* Wait for SOBS to be unset */
+					wait_for_ctrl_irq (ctrl);
+
+					mutex_unlock(&ctrl->crit_sect);
+				}
+				/*** button Released (No action on press...) */
+				else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) {
+					dbg("button release\n");
+
+					if (is_slot_enabled (ctrl, hp_slot)) {
+						dbg("slot is on\n");
+						p_slot->state = BLINKINGOFF_STATE;
+						info(msg_button_off, p_slot->number);
+					} else {
+						dbg("slot is off\n");
+						p_slot->state = BLINKINGON_STATE;
+						info(msg_button_on, p_slot->number);
+					}
+					mutex_lock(&ctrl->crit_sect);
+
+					dbg("blink green LED and turn off amber\n");
+
+					amber_LED_off (ctrl, hp_slot);
+					green_LED_blink (ctrl, hp_slot);
+
+					set_SOGO(ctrl);
+
+					/* Wait for SOBS to be unset */
+					wait_for_ctrl_irq (ctrl);
+
+					mutex_unlock(&ctrl->crit_sect);
+					init_timer(&p_slot->task_event);
+					p_slot->hp_slot = hp_slot;
+					p_slot->ctrl = ctrl;
+/*					p_slot->physical_slot = physical_slot; */
+					p_slot->task_event.expires = jiffies + 5 * HZ;   /* 5 second delay */
+					p_slot->task_event.function = pushbutton_helper_thread;
+					p_slot->task_event.data = (u32) p_slot;
+
+					dbg("add_timer p_slot = %p\n", p_slot);
+					add_timer(&p_slot->task_event);
+				}
+				/***********POWER FAULT */
+				else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
+					dbg("power fault\n");
+				} else {
+					/* refresh notification */
+					update_slot_info(ctrl, p_slot);
+				}
+
+				ctrl->event_queue[loop].event_type = 0;
+
+				change = 1;
+			}
+		}		/* End of FOR loop */
+	}
+
+	return;
+}
+
+
+/**
+ * cpqhp_pushbutton_thread - handle pushbutton events
+ * @slot: target slot (struct)
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons.
+ * Handles all pending events and exits.
+ */
+void cpqhp_pushbutton_thread(unsigned long slot)
+{
+	u8 hp_slot;
+	u8 device;
+	struct pci_func *func;
+	struct slot *p_slot = (struct slot *) slot;
+	struct controller *ctrl = (struct controller *) p_slot->ctrl;
+
+	pushbutton_pending = 0;
+	hp_slot = p_slot->hp_slot;
+
+	device = p_slot->device;
+
+	if (is_slot_enabled(ctrl, hp_slot)) {
+		p_slot->state = POWEROFF_STATE;
+		/* power Down board */
+		func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
+		dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl);
+		if (!func) {
+			dbg("Error! func NULL in %s\n", __func__);
+			return ;
+		}
+
+		if (cpqhp_process_SS(ctrl, func) != 0) {
+			amber_LED_on(ctrl, hp_slot);
+			green_LED_on(ctrl, hp_slot);
+
+			set_SOGO(ctrl);
+
+			/* Wait for SOBS to be unset */
+			wait_for_ctrl_irq(ctrl);
+		}
+
+		p_slot->state = STATIC_STATE;
+	} else {
+		p_slot->state = POWERON_STATE;
+		/* slot is off */
+
+		func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
+		dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl);
+		if (!func) {
+			dbg("Error! func NULL in %s\n", __func__);
+			return ;
+		}
+
+		if (ctrl != NULL) {
+			if (cpqhp_process_SI(ctrl, func) != 0) {
+				amber_LED_on(ctrl, hp_slot);
+				green_LED_off(ctrl, hp_slot);
+
+				set_SOGO(ctrl);
+
+				/* Wait for SOBS to be unset */
+				wait_for_ctrl_irq (ctrl);
+			}
+		}
+
+		p_slot->state = STATIC_STATE;
+	}
+
+	return;
+}
+
+
+int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
+{
+	u8 device, hp_slot;
+	u16 temp_word;
+	u32 tempdword;
+	int rc;
+	struct slot *p_slot;
+	int physical_slot = 0;
+
+	tempdword = 0;
+
+	device = func->device;
+	hp_slot = device - ctrl->slot_device_offset;
+	p_slot = cpqhp_find_slot(ctrl, device);
+	if (p_slot)
+		physical_slot = p_slot->number;
+
+	/* Check to see if the interlock is closed */
+	tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+	if (tempdword & (0x01 << hp_slot))
+		return 1;
+
+	if (func->is_a_board) {
+		rc = board_replaced(func, ctrl);
+	} else {
+		/* add board */
+		slot_remove(func);
+
+		func = cpqhp_slot_create(ctrl->bus);
+		if (func == NULL)
+			return 1;
+
+		func->bus = ctrl->bus;
+		func->device = device;
+		func->function = 0;
+		func->configured = 0;
+		func->is_a_board = 1;
+
+		/* We have to save the presence info for these slots */
+		temp_word = ctrl->ctrl_int_comp >> 16;
+		func->presence_save = (temp_word >> hp_slot) & 0x01;
+		func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+		if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+			func->switch_save = 0;
+		} else {
+			func->switch_save = 0x10;
+		}
+
+		rc = board_added(func, ctrl);
+		if (rc) {
+			if (is_bridge(func)) {
+				bridge_slot_remove(func);
+			} else
+				slot_remove(func);
+
+			/* Setup slot structure with entry for empty slot */
+			func = cpqhp_slot_create(ctrl->bus);
+
+			if (func == NULL)
+				return 1;
+
+			func->bus = ctrl->bus;
+			func->device = device;
+			func->function = 0;
+			func->configured = 0;
+			func->is_a_board = 0;
+
+			/* We have to save the presence info for these slots */
+			temp_word = ctrl->ctrl_int_comp >> 16;
+			func->presence_save = (temp_word >> hp_slot) & 0x01;
+			func->presence_save |=
+			(temp_word >> (hp_slot + 7)) & 0x02;
+
+			if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+				func->switch_save = 0;
+			} else {
+				func->switch_save = 0x10;
+			}
+		}
+	}
+
+	if (rc)
+		dbg("%s: rc = %d\n", __func__, rc);
+
+	if (p_slot)
+		update_slot_info(ctrl, p_slot);
+
+	return rc;
+}
+
+
+int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
+{
+	u8 device, class_code, header_type, BCR;
+	u8 index = 0;
+	u8 replace_flag;
+	u32 rc = 0;
+	unsigned int devfn;
+	struct slot *p_slot;
+	struct pci_bus *pci_bus = ctrl->pci_bus;
+	int physical_slot=0;
+
+	device = func->device;
+	func = cpqhp_slot_find(ctrl->bus, device, index++);
+	p_slot = cpqhp_find_slot(ctrl, device);
+	if (p_slot)
+		physical_slot = p_slot->number;
+
+	/* Make sure there are no video controllers here */
+	while (func && !rc) {
+		pci_bus->number = func->bus;
+		devfn = PCI_DEVFN(func->device, func->function);
+
+		/* Check the Class Code */
+		rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+		if (rc)
+			return rc;
+
+		if (class_code == PCI_BASE_CLASS_DISPLAY) {
+			/* Display/Video adapter (not supported) */
+			rc = REMOVE_NOT_SUPPORTED;
+		} else {
+			/* See if it's a bridge */
+			rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+			if (rc)
+				return rc;
+
+			/* If it's a bridge, check the VGA Enable bit */
+			if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+				rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
+				if (rc)
+					return rc;
+
+				/* If the VGA Enable bit is set, remove isn't
+				 * supported */
+				if (BCR & PCI_BRIDGE_CTL_VGA)
+					rc = REMOVE_NOT_SUPPORTED;
+			}
+		}
+
+		func = cpqhp_slot_find(ctrl->bus, device, index++);
+	}
+
+	func = cpqhp_slot_find(ctrl->bus, device, 0);
+	if ((func != NULL) && !rc) {
+		/* FIXME: Replace flag should be passed into process_SS */
+		replace_flag = !(ctrl->add_support);
+		rc = remove_board(func, replace_flag, ctrl);
+	} else if (!rc) {
+		rc = 1;
+	}
+
+	if (p_slot)
+		update_slot_info(ctrl, p_slot);
+
+	return rc;
+}
+
+/**
+ * switch_leds - switch the leds, go from one site to the other.
+ * @ctrl: controller to use
+ * @num_of_slots: number of slots to use
+ * @work_LED: LED control value
+ * @direction: 1 to start from the left side, 0 to start right.
+ */
+static void switch_leds(struct controller *ctrl, const int num_of_slots,
+			u32 *work_LED, const int direction)
+{
+	int loop;
+
+	for (loop = 0; loop < num_of_slots; loop++) {
+		if (direction)
+			*work_LED = *work_LED >> 1;
+		else
+			*work_LED = *work_LED << 1;
+		writel(*work_LED, ctrl->hpc_reg + LED_CONTROL);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOGO interrupt */
+		wait_for_ctrl_irq(ctrl);
+
+		/* Get ready for next iteration */
+		long_delay((2*HZ)/10);
+	}
+}
+
+/**
+ * cpqhp_hardware_test - runs hardware tests
+ * @ctrl: target controller
+ * @test_num: the number written to the "test" file in sysfs.
+ *
+ * For hot plug ctrl folks to play with.
+ */
+int cpqhp_hardware_test(struct controller *ctrl, int test_num)
+{
+	u32 save_LED;
+	u32 work_LED;
+	int loop;
+	int num_of_slots;
+
+	num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
+
+	switch (test_num) {
+	case 1:
+		/* Do stuff here! */
+
+		/* Do that funky LED thing */
+		/* so we can restore them later */
+		save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
+		work_LED = 0x01010101;
+		switch_leds(ctrl, num_of_slots, &work_LED, 0);
+		switch_leds(ctrl, num_of_slots, &work_LED, 1);
+		switch_leds(ctrl, num_of_slots, &work_LED, 0);
+		switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+		work_LED = 0x01010000;
+		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+		switch_leds(ctrl, num_of_slots, &work_LED, 0);
+		switch_leds(ctrl, num_of_slots, &work_LED, 1);
+		work_LED = 0x00000101;
+		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+		switch_leds(ctrl, num_of_slots, &work_LED, 0);
+		switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+		work_LED = 0x01010000;
+		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+		for (loop = 0; loop < num_of_slots; loop++) {
+			set_SOGO(ctrl);
+
+			/* Wait for SOGO interrupt */
+			wait_for_ctrl_irq (ctrl);
+
+			/* Get ready for next iteration */
+			long_delay((3*HZ)/10);
+			work_LED = work_LED >> 16;
+			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+
+			set_SOGO(ctrl);
+
+			/* Wait for SOGO interrupt */
+			wait_for_ctrl_irq (ctrl);
+
+			/* Get ready for next iteration */
+			long_delay((3*HZ)/10);
+			work_LED = work_LED << 16;
+			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+			work_LED = work_LED << 1;
+			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+		}
+
+		/* put it back the way it was */
+		writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
+
+		set_SOGO(ctrl);
+
+		/* Wait for SOBS to be unset */
+		wait_for_ctrl_irq (ctrl);
+		break;
+	case 2:
+		/* Do other stuff here! */
+		break;
+	case 3:
+		/* and more... */
+		break;
+	}
+	return 0;
+}
+
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success.
+ */
+static u32 configure_new_device(struct controller  *ctrl, struct pci_func  *func,
+				 u8 behind_bridge, struct resource_lists  *resources)
+{
+	u8 temp_byte, function, max_functions, stop_it;
+	int rc;
+	u32 ID;
+	struct pci_func *new_slot;
+	int index;
+
+	new_slot = func;
+
+	dbg("%s\n", __func__);
+	/* Check for Multi-function device */
+	ctrl->pci_bus->number = func->bus;
+	rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+	if (rc) {
+		dbg("%s: rc = %d\n", __func__, rc);
+		return rc;
+	}
+
+	if (temp_byte & 0x80)	/* Multi-function device */
+		max_functions = 8;
+	else
+		max_functions = 1;
+
+	function = 0;
+
+	do {
+		rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
+
+		if (rc) {
+			dbg("configure_new_function failed %d\n",rc);
+			index = 0;
+
+			while (new_slot) {
+				new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++);
+
+				if (new_slot)
+					cpqhp_return_board_resources(new_slot, resources);
+			}
+
+			return rc;
+		}
+
+		function++;
+
+		stop_it = 0;
+
+		/* The following loop skips to the next present function
+		 * and creates a board structure */
+
+		while ((function < max_functions) && (!stop_it)) {
+			pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+			if (ID == 0xFFFFFFFF) {
+				function++;
+			} else {
+				/* Setup slot structure. */
+				new_slot = cpqhp_slot_create(func->bus);
+
+				if (new_slot == NULL)
+					return 1;
+
+				new_slot->bus = func->bus;
+				new_slot->device = func->device;
+				new_slot->function = function;
+				new_slot->is_a_board = 1;
+				new_slot->status = 0;
+
+				stop_it++;
+			}
+		}
+
+	} while (function < max_functions);
+	dbg("returning from configure_new_device\n");
+
+	return 0;
+}
+
+
+/*
+ * Configuration logic that involves the hotplug data structures and
+ * their bookkeeping
+ */
+
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success.
+ */
+static int configure_new_function(struct controller *ctrl, struct pci_func *func,
+				   u8 behind_bridge,
+				   struct resource_lists *resources)
+{
+	int cloop;
+	u8 IRQ = 0;
+	u8 temp_byte;
+	u8 device;
+	u8 class_code;
+	u16 command;
+	u16 temp_word;
+	u32 temp_dword;
+	u32 rc;
+	u32 temp_register;
+	u32 base;
+	u32 ID;
+	unsigned int devfn;
+	struct pci_resource *mem_node;
+	struct pci_resource *p_mem_node;
+	struct pci_resource *io_node;
+	struct pci_resource *bus_node;
+	struct pci_resource *hold_mem_node;
+	struct pci_resource *hold_p_mem_node;
+	struct pci_resource *hold_IO_node;
+	struct pci_resource *hold_bus_node;
+	struct irq_mapping irqs;
+	struct pci_func *new_slot;
+	struct pci_bus *pci_bus;
+	struct resource_lists temp_resources;
+
+	pci_bus = ctrl->pci_bus;
+	pci_bus->number = func->bus;
+	devfn = PCI_DEVFN(func->device, func->function);
+
+	/* Check for Bridge */
+	rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+	if (rc)
+		return rc;
+
+	if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+		/* set Primary bus */
+		dbg("set Primary bus = %d\n", func->bus);
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+		if (rc)
+			return rc;
+
+		/* find range of buses to use */
+		dbg("find ranges of buses to use\n");
+		bus_node = get_max_resource(&(resources->bus_head), 1);
+
+		/* If we don't have any buses to allocate, we can't continue */
+		if (!bus_node)
+			return -ENOMEM;
+
+		/* set Secondary bus */
+		temp_byte = bus_node->base;
+		dbg("set Secondary bus = %d\n", bus_node->base);
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+		if (rc)
+			return rc;
+
+		/* set subordinate bus */
+		temp_byte = bus_node->base + bus_node->length - 1;
+		dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1);
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+		if (rc)
+			return rc;
+
+		/* set subordinate Latency Timer and base Latency Timer */
+		temp_byte = 0x40;
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+		if (rc)
+			return rc;
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+		if (rc)
+			return rc;
+
+		/* set Cache Line size */
+		temp_byte = 0x08;
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+		if (rc)
+			return rc;
+
+		/* Setup the IO, memory, and prefetchable windows */
+		io_node = get_max_resource(&(resources->io_head), 0x1000);
+		if (!io_node)
+			return -ENOMEM;
+		mem_node = get_max_resource(&(resources->mem_head), 0x100000);
+		if (!mem_node)
+			return -ENOMEM;
+		p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
+		if (!p_mem_node)
+			return -ENOMEM;
+		dbg("Setup the IO, memory, and prefetchable windows\n");
+		dbg("io_node\n");
+		dbg("(base, len, next) (%x, %x, %p)\n", io_node->base,
+					io_node->length, io_node->next);
+		dbg("mem_node\n");
+		dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base,
+					mem_node->length, mem_node->next);
+		dbg("p_mem_node\n");
+		dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base,
+					p_mem_node->length, p_mem_node->next);
+
+		/* set up the IRQ info */
+		if (!resources->irqs) {
+			irqs.barber_pole = 0;
+			irqs.interrupt[0] = 0;
+			irqs.interrupt[1] = 0;
+			irqs.interrupt[2] = 0;
+			irqs.interrupt[3] = 0;
+			irqs.valid_INT = 0;
+		} else {
+			irqs.barber_pole = resources->irqs->barber_pole;
+			irqs.interrupt[0] = resources->irqs->interrupt[0];
+			irqs.interrupt[1] = resources->irqs->interrupt[1];
+			irqs.interrupt[2] = resources->irqs->interrupt[2];
+			irqs.interrupt[3] = resources->irqs->interrupt[3];
+			irqs.valid_INT = resources->irqs->valid_INT;
+		}
+
+		/* set up resource lists that are now aligned on top and bottom
+		 * for anything behind the bridge. */
+		temp_resources.bus_head = bus_node;
+		temp_resources.io_head = io_node;
+		temp_resources.mem_head = mem_node;
+		temp_resources.p_mem_head = p_mem_node;
+		temp_resources.irqs = &irqs;
+
+		/* Make copies of the nodes we are going to pass down so that
+		 * if there is a problem,we can just use these to free resources
+		 */
+		hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
+		hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
+		hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
+		hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL);
+
+		if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+			kfree(hold_bus_node);
+			kfree(hold_IO_node);
+			kfree(hold_mem_node);
+			kfree(hold_p_mem_node);
+
+			return 1;
+		}
+
+		memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+		bus_node->base += 1;
+		bus_node->length -= 1;
+		bus_node->next = NULL;
+
+		/* If we have IO resources copy them and fill in the bridge's
+		 * IO range registers */
+		memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+		io_node->next = NULL;
+
+		/* set IO base and Limit registers */
+		temp_byte = io_node->base >> 8;
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+		temp_byte = (io_node->base + io_node->length - 1) >> 8;
+		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+		/* Copy the memory resources and fill in the bridge's memory
+		 * range registers.
+		 */
+		memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+		mem_node->next = NULL;
+
+		/* set Mem base and Limit registers */
+		temp_word = mem_node->base >> 16;
+		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+		temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+		memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
+		p_mem_node->next = NULL;
+
+		/* set Pre Mem base and Limit registers */
+		temp_word = p_mem_node->base >> 16;
+		rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+		temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
+		rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+		/* Adjust this to compensate for extra adjustment in first loop
+		 */
+		irqs.barber_pole--;
+
+		rc = 0;
+
+		/* Here we actually find the devices and configure them */
+		for (device = 0; (device <= 0x1F) && !rc; device++) {
+			irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+			ID = 0xFFFFFFFF;
+			pci_bus->number = hold_bus_node->base;
+			pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
+			pci_bus->number = func->bus;
+
+			if (ID != 0xFFFFFFFF) {	  /*  device present */
+				/* Setup slot structure. */
+				new_slot = cpqhp_slot_create(hold_bus_node->base);
+
+				if (new_slot == NULL) {
+					rc = -ENOMEM;
+					continue;
+				}
+
+				new_slot->bus = hold_bus_node->base;
+				new_slot->device = device;
+				new_slot->function = 0;
+				new_slot->is_a_board = 1;
+				new_slot->status = 0;
+
+				rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
+				dbg("configure_new_device rc=0x%x\n",rc);
+			}	/* End of IF (device in slot?) */
+		}		/* End of FOR loop */
+
+		if (rc)
+			goto free_and_out;
+		/* save the interrupt routing information */
+		if (resources->irqs) {
+			resources->irqs->interrupt[0] = irqs.interrupt[0];
+			resources->irqs->interrupt[1] = irqs.interrupt[1];
+			resources->irqs->interrupt[2] = irqs.interrupt[2];
+			resources->irqs->interrupt[3] = irqs.interrupt[3];
+			resources->irqs->valid_INT = irqs.valid_INT;
+		} else if (!behind_bridge) {
+			/* We need to hook up the interrupts here */
+			for (cloop = 0; cloop < 4; cloop++) {
+				if (irqs.valid_INT & (0x01 << cloop)) {
+					rc = cpqhp_set_irq(func->bus, func->device,
+							   cloop + 1, irqs.interrupt[cloop]);
+					if (rc)
+						goto free_and_out;
+				}
+			}	/* end of for loop */
+		}
+		/* Return unused bus resources
+		 * First use the temporary node to store information for
+		 * the board */
+		if (bus_node && temp_resources.bus_head) {
+			hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+			hold_bus_node->next = func->bus_head;
+			func->bus_head = hold_bus_node;
+
+			temp_byte = temp_resources.bus_head->base - 1;
+
+			/* set subordinate bus */
+			rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+			if (temp_resources.bus_head->length == 0) {
+				kfree(temp_resources.bus_head);
+				temp_resources.bus_head = NULL;
+			} else {
+				return_resource(&(resources->bus_head), temp_resources.bus_head);
+			}
+		}
+
+		/* If we have IO space available and there is some left,
+		 * return the unused portion */
+		if (hold_IO_node && temp_resources.io_head) {
+			io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+							       &hold_IO_node, 0x1000);
+
+			/* Check if we were able to split something off */
+			if (io_node) {
+				hold_IO_node->base = io_node->base + io_node->length;
+
+				temp_byte = (hold_IO_node->base) >> 8;
+				rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+				return_resource(&(resources->io_head), io_node);
+			}
+
+			io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+			/* Check if we were able to split something off */
+			if (io_node) {
+				/* First use the temporary node to store
+				 * information for the board */
+				hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+				/* If we used any, add it to the board's list */
+				if (hold_IO_node->length) {
+					hold_IO_node->next = func->io_head;
+					func->io_head = hold_IO_node;
+
+					temp_byte = (io_node->base - 1) >> 8;
+					rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+					return_resource(&(resources->io_head), io_node);
+				} else {
+					/* it doesn't need any IO */
+					temp_word = 0x0000;
+					rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_LIMIT, temp_word);
+
+					return_resource(&(resources->io_head), io_node);
+					kfree(hold_IO_node);
+				}
+			} else {
+				/* it used most of the range */
+				hold_IO_node->next = func->io_head;
+				func->io_head = hold_IO_node;
+			}
+		} else if (hold_IO_node) {
+			/* it used the whole range */
+			hold_IO_node->next = func->io_head;
+			func->io_head = hold_IO_node;
+		}
+		/* If we have memory space available and there is some left,
+		 * return the unused portion */
+		if (hold_mem_node && temp_resources.mem_head) {
+			mem_node = do_pre_bridge_resource_split(&(temp_resources.  mem_head),
+								&hold_mem_node, 0x100000);
+
+			/* Check if we were able to split something off */
+			if (mem_node) {
+				hold_mem_node->base = mem_node->base + mem_node->length;
+
+				temp_word = (hold_mem_node->base) >> 16;
+				rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+				return_resource(&(resources->mem_head), mem_node);
+			}
+
+			mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
+
+			/* Check if we were able to split something off */
+			if (mem_node) {
+				/* First use the temporary node to store
+				 * information for the board */
+				hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+				if (hold_mem_node->length) {
+					hold_mem_node->next = func->mem_head;
+					func->mem_head = hold_mem_node;
+
+					/* configure end address */
+					temp_word = (mem_node->base - 1) >> 16;
+					rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+					/* Return unused resources to the pool */
+					return_resource(&(resources->mem_head), mem_node);
+				} else {
+					/* it doesn't need any Mem */
+					temp_word = 0x0000;
+					rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+					return_resource(&(resources->mem_head), mem_node);
+					kfree(hold_mem_node);
+				}
+			} else {
+				/* it used most of the range */
+				hold_mem_node->next = func->mem_head;
+				func->mem_head = hold_mem_node;
+			}
+		} else if (hold_mem_node) {
+			/* it used the whole range */
+			hold_mem_node->next = func->mem_head;
+			func->mem_head = hold_mem_node;
+		}
+		/* If we have prefetchable memory space available and there
+		 * is some left at the end, return the unused portion */
+		if (temp_resources.p_mem_head) {
+			p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+								  &hold_p_mem_node, 0x100000);
+
+			/* Check if we were able to split something off */
+			if (p_mem_node) {
+				hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+				temp_word = (hold_p_mem_node->base) >> 16;
+				rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+				return_resource(&(resources->p_mem_head), p_mem_node);
+			}
+
+			p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
+
+			/* Check if we were able to split something off */
+			if (p_mem_node) {
+				/* First use the temporary node to store
+				 * information for the board */
+				hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+				/* If we used any, add it to the board's list */
+				if (hold_p_mem_node->length) {
+					hold_p_mem_node->next = func->p_mem_head;
+					func->p_mem_head = hold_p_mem_node;
+
+					temp_word = (p_mem_node->base - 1) >> 16;
+					rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+					return_resource(&(resources->p_mem_head), p_mem_node);
+				} else {
+					/* it doesn't need any PMem */
+					temp_word = 0x0000;
+					rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+					return_resource(&(resources->p_mem_head), p_mem_node);
+					kfree(hold_p_mem_node);
+				}
+			} else {
+				/* it used the most of the range */
+				hold_p_mem_node->next = func->p_mem_head;
+				func->p_mem_head = hold_p_mem_node;
+			}
+		} else if (hold_p_mem_node) {
+			/* it used the whole range */
+			hold_p_mem_node->next = func->p_mem_head;
+			func->p_mem_head = hold_p_mem_node;
+		}
+		/* We should be configuring an IRQ and the bridge's base address
+		 * registers if it needs them.  Although we have never seen such
+		 * a device */
+
+		/* enable card */
+		command = 0x0157;	/* = PCI_COMMAND_IO |
+					 *   PCI_COMMAND_MEMORY |
+					 *   PCI_COMMAND_MASTER |
+					 *   PCI_COMMAND_INVALIDATE |
+					 *   PCI_COMMAND_PARITY |
+					 *   PCI_COMMAND_SERR */
+		rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command);
+
+		/* set Bridge Control Register */
+		command = 0x07;		/* = PCI_BRIDGE_CTL_PARITY |
+					 *   PCI_BRIDGE_CTL_SERR |
+					 *   PCI_BRIDGE_CTL_NO_ISA */
+		rc = pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+	} else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+		/* Standard device */
+		rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+		if (class_code == PCI_BASE_CLASS_DISPLAY) {
+			/* Display (video) adapter (not supported) */
+			return DEVICE_TYPE_NOT_SUPPORTED;
+		}
+		/* Figure out IO and memory needs */
+		for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+			temp_register = 0xFFFFFFFF;
+
+			dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop);
+			rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+
+			rc = pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register);
+			dbg("CND: base = 0x%x\n", temp_register);
+
+			if (temp_register) {	  /* If this register is implemented */
+				if ((temp_register & 0x03L) == 0x01) {
+					/* Map IO */
+
+					/* set base = amount of IO space */
+					base = temp_register & 0xFFFFFFFC;
+					base = ~base + 1;
+
+					dbg("CND:      length = 0x%x\n", base);
+					io_node = get_io_resource(&(resources->io_head), base);
+					dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
+					    io_node->base, io_node->length, io_node->next);
+					dbg("func (%p) io_head (%p)\n", func, func->io_head);
+
+					/* allocate the resource to the board */
+					if (io_node) {
+						base = io_node->base;
+
+						io_node->next = func->io_head;
+						func->io_head = io_node;
+					} else
+						return -ENOMEM;
+				} else if ((temp_register & 0x0BL) == 0x08) {
+					/* Map prefetchable memory */
+					base = temp_register & 0xFFFFFFF0;
+					base = ~base + 1;
+
+					dbg("CND:      length = 0x%x\n", base);
+					p_mem_node = get_resource(&(resources->p_mem_head), base);
+
+					/* allocate the resource to the board */
+					if (p_mem_node) {
+						base = p_mem_node->base;
+
+						p_mem_node->next = func->p_mem_head;
+						func->p_mem_head = p_mem_node;
+					} else
+						return -ENOMEM;
+				} else if ((temp_register & 0x0BL) == 0x00) {
+					/* Map memory */
+					base = temp_register & 0xFFFFFFF0;
+					base = ~base + 1;
+
+					dbg("CND:      length = 0x%x\n", base);
+					mem_node = get_resource(&(resources->mem_head), base);
+
+					/* allocate the resource to the board */
+					if (mem_node) {
+						base = mem_node->base;
+
+						mem_node->next = func->mem_head;
+						func->mem_head = mem_node;
+					} else
+						return -ENOMEM;
+				} else {
+					/* Reserved bits or requesting space below 1M */
+					return NOT_ENOUGH_RESOURCES;
+				}
+
+				rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+
+				/* Check for 64-bit base */
+				if ((temp_register & 0x07L) == 0x04) {
+					cloop += 4;
+
+					/* Upper 32 bits of address always zero
+					 * on today's systems */
+					/* FIXME this is probably not true on
+					 * Alpha and ia64??? */
+					base = 0;
+					rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+				}
+			}
+		}		/* End of base register loop */
+		if (cpqhp_legacy_mode) {
+			/* Figure out which interrupt pin this function uses */
+			rc = pci_bus_read_config_byte (pci_bus, devfn,
+				PCI_INTERRUPT_PIN, &temp_byte);
+
+			/* If this function needs an interrupt and we are behind
+			 * a bridge and the pin is tied to something that's
+			 * already mapped, set this one the same */
+			if (temp_byte && resources->irqs &&
+			    (resources->irqs->valid_INT &
+			     (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
+				/* We have to share with something already set up */
+				IRQ = resources->irqs->interrupt[(temp_byte +
+					resources->irqs->barber_pole - 1) & 0x03];
+			} else {
+				/* Program IRQ based on card type */
+				rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+				if (class_code == PCI_BASE_CLASS_STORAGE)
+					IRQ = cpqhp_disk_irq;
+				else
+					IRQ = cpqhp_nic_irq;
+			}
+
+			/* IRQ Line */
+			rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
+		}
+
+		if (!behind_bridge) {
+			rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ);
+			if (rc)
+				return 1;
+		} else {
+			/* TBD - this code may also belong in the other clause
+			 * of this If statement */
+			resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
+			resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
+		}
+
+		/* Latency Timer */
+		temp_byte = 0x40;
+		rc = pci_bus_write_config_byte(pci_bus, devfn,
+					PCI_LATENCY_TIMER, temp_byte);
+
+		/* Cache Line size */
+		temp_byte = 0x08;
+		rc = pci_bus_write_config_byte(pci_bus, devfn,
+					PCI_CACHE_LINE_SIZE, temp_byte);
+
+		/* disable ROM base Address */
+		temp_dword = 0x00L;
+		rc = pci_bus_write_config_word(pci_bus, devfn,
+					PCI_ROM_ADDRESS, temp_dword);
+
+		/* enable card */
+		temp_word = 0x0157;	/* = PCI_COMMAND_IO |
+					 *   PCI_COMMAND_MEMORY |
+					 *   PCI_COMMAND_MASTER |
+					 *   PCI_COMMAND_INVALIDATE |
+					 *   PCI_COMMAND_PARITY |
+					 *   PCI_COMMAND_SERR */
+		rc = pci_bus_write_config_word (pci_bus, devfn,
+					PCI_COMMAND, temp_word);
+	} else {		/* End of Not-A-Bridge else */
+		/* It's some strange type of PCI adapter (Cardbus?) */
+		return DEVICE_TYPE_NOT_SUPPORTED;
+	}
+
+	func->configured = 1;
+
+	return 0;
+free_and_out:
+	cpqhp_destroy_resource_list (&temp_resources);
+
+	return_resource(&(resources-> bus_head), hold_bus_node);
+	return_resource(&(resources-> io_head), hold_IO_node);
+	return_resource(&(resources-> mem_head), hold_mem_node);
+	return_resource(&(resources-> p_mem_head), hold_p_mem_node);
+	return rc;
+}
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
new file mode 100644
index 0000000..1e08ff8
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -0,0 +1,667 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <asm/uaccess.h>
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+
+
+#define ROM_INT15_PHY_ADDR		0x0FF859
+#define READ_EV				0xD8A4
+#define WRITE_EV			0xD8A5
+
+struct register_foo {
+	union {
+		unsigned long lword;		/* eax */
+		unsigned short word;		/* ax */
+
+		struct {
+			unsigned char low;	/* al */
+			unsigned char high;	/* ah */
+		} byte;
+	} data;
+
+	unsigned char opcode;	/* see below */
+	unsigned long length;	/* if the reg. is a pointer, how much data */
+} __attribute__ ((packed));
+
+struct all_reg {
+	struct register_foo eax_reg;
+	struct register_foo ebx_reg;
+	struct register_foo ecx_reg;
+	struct register_foo edx_reg;
+	struct register_foo edi_reg;
+	struct register_foo esi_reg;
+	struct register_foo eflags_reg;
+} __attribute__ ((packed));
+
+
+struct ev_hrt_header {
+	u8 Version;
+	u8 num_of_ctrl;
+	u8 next;
+};
+
+struct ev_hrt_ctrl {
+	u8 bus;
+	u8 device;
+	u8 function;
+	u8 mem_avail;
+	u8 p_mem_avail;
+	u8 io_avail;
+	u8 bus_avail;
+	u8 next;
+};
+
+
+static u8 evbuffer_init;
+static u8 evbuffer_length;
+static u8 evbuffer[1024];
+
+static void __iomem *compaq_int15_entry_point;
+
+/* lock for ordering int15_bios_call() */
+static spinlock_t int15_lock;
+
+
+/* This is a series of function that deals with
+ * setting & getting the hotplug resource table in some environment variable.
+ */
+
+/*
+ * We really shouldn't be doing this unless there is a _very_ good reason to!!!
+ * greg k-h
+ */
+
+
+static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail)
+{
+	u8 **tByte;
+
+	if ((*used + 1) > *avail)
+		return(1);
+
+	*((u8*)*p_buffer) = value;
+	tByte = (u8**)p_buffer;
+	(*tByte)++;
+	*used+=1;
+	return(0);
+}
+
+
+static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
+{
+	if ((*used + 4) > *avail)
+		return(1);
+
+	**p_buffer = value;
+	(*p_buffer)++;
+	*used+=4;
+	return(0);
+}
+
+
+/*
+ * check_for_compaq_ROM
+ *
+ * this routine verifies that the ROM OEM string is 'COMPAQ'
+ *
+ * returns 0 for non-Compaq ROM, 1 for Compaq ROM
+ */
+static int check_for_compaq_ROM (void __iomem *rom_start)
+{
+	u8 temp1, temp2, temp3, temp4, temp5, temp6;
+	int result = 0;
+
+	temp1 = readb(rom_start + 0xffea + 0);
+	temp2 = readb(rom_start + 0xffea + 1);
+	temp3 = readb(rom_start + 0xffea + 2);
+	temp4 = readb(rom_start + 0xffea + 3);
+	temp5 = readb(rom_start + 0xffea + 4);
+	temp6 = readb(rom_start + 0xffea + 5);
+	if ((temp1 == 'C') &&
+	    (temp2 == 'O') &&
+	    (temp3 == 'M') &&
+	    (temp4 == 'P') &&
+	    (temp5 == 'A') &&
+	    (temp6 == 'Q')) {
+		result = 1;
+	}
+	dbg ("%s - returned %d\n", __func__, result);
+	return result;
+}
+
+
+static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
+{
+	unsigned long flags;
+	int op = operation;
+	int ret_val;
+
+	if (!compaq_int15_entry_point)
+		return -ENODEV;
+
+	spin_lock_irqsave(&int15_lock, flags);
+	__asm__ (
+		"xorl   %%ebx,%%ebx\n" \
+		"xorl    %%edx,%%edx\n" \
+		"pushf\n" \
+		"push %%cs\n" \
+		"cli\n" \
+		"call *%6\n"
+		: "=c" (*buf_size), "=a" (ret_val)
+		: "a" (op), "c" (*buf_size), "S" (ev_name),
+		"D" (buffer), "m" (compaq_int15_entry_point)
+		: "%ebx", "%edx");
+	spin_unlock_irqrestore(&int15_lock, flags);
+
+	return((ret_val & 0xFF00) >> 8);
+}
+
+
+/*
+ * load_HRT
+ *
+ * Read the hot plug Resource Table from NVRAM
+ */
+static int load_HRT (void __iomem *rom_start)
+{
+	u32 available;
+	u32 temp_dword;
+	u8 temp_byte = 0xFF;
+	u32 rc;
+
+	if (!check_for_compaq_ROM(rom_start))
+		return -ENODEV;
+
+	available = 1024;
+
+	/* Now load the EV */
+	temp_dword = available;
+
+	rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
+
+	evbuffer_length = temp_dword;
+
+	/* We're maintaining the resource lists so write FF to invalidate old
+	 * info
+	 */
+	temp_dword = 1;
+
+	rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
+
+	return rc;
+}
+
+
+/*
+ * store_HRT
+ *
+ * Save the hot plug Resource Table in NVRAM
+ */
+static u32 store_HRT (void __iomem *rom_start)
+{
+	u32 *buffer;
+	u32 *pFill;
+	u32 usedbytes;
+	u32 available;
+	u32 temp_dword;
+	u32 rc;
+	u8 loop;
+	u8 numCtrl = 0;
+	struct controller *ctrl;
+	struct pci_resource *resNode;
+	struct ev_hrt_header *p_EV_header;
+	struct ev_hrt_ctrl *p_ev_ctrl;
+
+	available = 1024;
+
+	if (!check_for_compaq_ROM(rom_start))
+		return(1);
+
+	buffer = (u32*) evbuffer;
+
+	if (!buffer)
+		return(1);
+
+	pFill = buffer;
+	usedbytes = 0;
+
+	p_EV_header = (struct ev_hrt_header *) pFill;
+
+	ctrl = cpqhp_ctrl_list;
+
+	/* The revision of this structure */
+	rc = add_byte(&pFill, 1 + ctrl->push_flag, &usedbytes, &available);
+	if (rc)
+		return(rc);
+
+	/* The number of controllers */
+	rc = add_byte(&pFill, 1, &usedbytes, &available);
+	if (rc)
+		return(rc);
+
+	while (ctrl) {
+		p_ev_ctrl = (struct ev_hrt_ctrl *) pFill;
+
+		numCtrl++;
+
+		/* The bus number */
+		rc = add_byte(&pFill, ctrl->bus, &usedbytes, &available);
+		if (rc)
+			return(rc);
+
+		/* The device Number */
+		rc = add_byte(&pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
+		if (rc)
+			return(rc);
+
+		/* The function Number */
+		rc = add_byte(&pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
+		if (rc)
+			return(rc);
+
+		/* Skip the number of available entries */
+		rc = add_dword(&pFill, 0, &usedbytes, &available);
+		if (rc)
+			return(rc);
+
+		/* Figure out memory Available */
+
+		resNode = ctrl->mem_head;
+
+		loop = 0;
+
+		while (resNode) {
+			loop ++;
+
+			/* base */
+			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			/* length */
+			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			resNode = resNode->next;
+		}
+
+		/* Fill in the number of entries */
+		p_ev_ctrl->mem_avail = loop;
+
+		/* Figure out prefetchable memory Available */
+
+		resNode = ctrl->p_mem_head;
+
+		loop = 0;
+
+		while (resNode) {
+			loop ++;
+
+			/* base */
+			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			/* length */
+			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			resNode = resNode->next;
+		}
+
+		/* Fill in the number of entries */
+		p_ev_ctrl->p_mem_avail = loop;
+
+		/* Figure out IO Available */
+
+		resNode = ctrl->io_head;
+
+		loop = 0;
+
+		while (resNode) {
+			loop ++;
+
+			/* base */
+			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			/* length */
+			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			resNode = resNode->next;
+		}
+
+		/* Fill in the number of entries */
+		p_ev_ctrl->io_avail = loop;
+
+		/* Figure out bus Available */
+
+		resNode = ctrl->bus_head;
+
+		loop = 0;
+
+		while (resNode) {
+			loop ++;
+
+			/* base */
+			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			/* length */
+			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
+			if (rc)
+				return(rc);
+
+			resNode = resNode->next;
+		}
+
+		/* Fill in the number of entries */
+		p_ev_ctrl->bus_avail = loop;
+
+		ctrl = ctrl->next;
+	}
+
+	p_EV_header->num_of_ctrl = numCtrl;
+
+	/* Now store the EV */
+
+	temp_dword = usedbytes;
+
+	rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword);
+
+	dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
+
+	evbuffer_length = temp_dword;
+
+	if (rc) {
+		err(msg_unable_to_save);
+		return(1);
+	}
+
+	return(0);
+}
+
+
+void compaq_nvram_init (void __iomem *rom_start)
+{
+	if (rom_start)
+		compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
+
+	dbg("int15 entry  = %p\n", compaq_int15_entry_point);
+
+	/* initialize our int15 lock */
+	spin_lock_init(&int15_lock);
+}
+
+
+int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
+{
+	u8 bus, device, function;
+	u8 nummem, numpmem, numio, numbus;
+	u32 rc;
+	u8 *p_byte;
+	struct pci_resource *mem_node;
+	struct pci_resource *p_mem_node;
+	struct pci_resource *io_node;
+	struct pci_resource *bus_node;
+	struct ev_hrt_ctrl *p_ev_ctrl;
+	struct ev_hrt_header *p_EV_header;
+
+	if (!evbuffer_init) {
+		/* Read the resource list information in from NVRAM */
+		if (load_HRT(rom_start))
+			memset (evbuffer, 0, 1024);
+
+		evbuffer_init = 1;
+	}
+
+	/* If we saved information in NVRAM, use it now */
+	p_EV_header = (struct ev_hrt_header *) evbuffer;
+
+	/* The following code is for systems where version 1.0 of this
+	 * driver has been loaded, but doesn't support the hardware.
+	 * In that case, the driver would incorrectly store something
+	 * in NVRAM.
+	 */
+	if ((p_EV_header->Version == 2) ||
+	    ((p_EV_header->Version == 1) && !ctrl->push_flag)) {
+		p_byte = &(p_EV_header->next);
+
+		p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next);
+
+		p_byte += 3;
+
+		if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+			return 2;
+
+		bus = p_ev_ctrl->bus;
+		device = p_ev_ctrl->device;
+		function = p_ev_ctrl->function;
+
+		while ((bus != ctrl->bus) ||
+		       (device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
+		       (function != PCI_FUNC(ctrl->pci_dev->devfn))) {
+			nummem = p_ev_ctrl->mem_avail;
+			numpmem = p_ev_ctrl->p_mem_avail;
+			numio = p_ev_ctrl->io_avail;
+			numbus = p_ev_ctrl->bus_avail;
+
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+				return 2;
+
+			/* Skip forward to the next entry */
+			p_byte += (nummem + numpmem + numio + numbus) * 8;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+				return 2;
+
+			p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
+
+			p_byte += 3;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+				return 2;
+
+			bus = p_ev_ctrl->bus;
+			device = p_ev_ctrl->device;
+			function = p_ev_ctrl->function;
+		}
+
+		nummem = p_ev_ctrl->mem_avail;
+		numpmem = p_ev_ctrl->p_mem_avail;
+		numio = p_ev_ctrl->io_avail;
+		numbus = p_ev_ctrl->bus_avail;
+
+		p_byte += 4;
+
+		if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+			return 2;
+
+		while (nummem--) {
+			mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+			if (!mem_node)
+				break;
+
+			mem_node->base = *(u32*)p_byte;
+			dbg("mem base = %8.8x\n",mem_node->base);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(mem_node);
+				return 2;
+			}
+
+			mem_node->length = *(u32*)p_byte;
+			dbg("mem length = %8.8x\n",mem_node->length);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(mem_node);
+				return 2;
+			}
+
+			mem_node->next = ctrl->mem_head;
+			ctrl->mem_head = mem_node;
+		}
+
+		while (numpmem--) {
+			p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+			if (!p_mem_node)
+				break;
+
+			p_mem_node->base = *(u32*)p_byte;
+			dbg("pre-mem base = %8.8x\n",p_mem_node->base);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(p_mem_node);
+				return 2;
+			}
+
+			p_mem_node->length = *(u32*)p_byte;
+			dbg("pre-mem length = %8.8x\n",p_mem_node->length);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(p_mem_node);
+				return 2;
+			}
+
+			p_mem_node->next = ctrl->p_mem_head;
+			ctrl->p_mem_head = p_mem_node;
+		}
+
+		while (numio--) {
+			io_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+			if (!io_node)
+				break;
+
+			io_node->base = *(u32*)p_byte;
+			dbg("io base = %8.8x\n",io_node->base);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(io_node);
+				return 2;
+			}
+
+			io_node->length = *(u32*)p_byte;
+			dbg("io length = %8.8x\n",io_node->length);
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(io_node);
+				return 2;
+			}
+
+			io_node->next = ctrl->io_head;
+			ctrl->io_head = io_node;
+		}
+
+		while (numbus--) {
+			bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+			if (!bus_node)
+				break;
+
+			bus_node->base = *(u32*)p_byte;
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(bus_node);
+				return 2;
+			}
+
+			bus_node->length = *(u32*)p_byte;
+			p_byte += 4;
+
+			if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+				kfree(bus_node);
+				return 2;
+			}
+
+			bus_node->next = ctrl->bus_head;
+			ctrl->bus_head = bus_node;
+		}
+
+		/* If all of the following fail, we don't have any resources for
+		 * hot plug add
+		 */
+		rc = 1;
+		rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+		rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+		rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+		rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+		if (rc)
+			return(rc);
+	} else {
+		if ((evbuffer[0] != 0) && (!ctrl->push_flag))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+int compaq_nvram_store (void __iomem *rom_start)
+{
+	int rc = 1;
+
+	if (rom_start == NULL)
+		return -ENODEV;
+
+	if (evbuffer_init) {
+		rc = store_HRT(rom_start);
+		if (rc)
+			err(msg_unable_to_save);
+	}
+	return rc;
+}
+
diff --git a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h
new file mode 100644
index 0000000..34e4e54
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_nvram.h
@@ -0,0 +1,57 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#ifndef _CPQPHP_NVRAM_H
+#define _CPQPHP_NVRAM_H
+
+#ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
+
+static inline void compaq_nvram_init(void __iomem *rom_start)
+{
+	return;
+}
+
+static inline int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl)
+{
+	return 0;
+}
+
+static inline int compaq_nvram_store(void __iomem *rom_start)
+{
+	return 0;
+}
+
+#else
+
+void compaq_nvram_init(void __iomem *rom_start);
+int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl);
+int compaq_nvram_store(void __iomem *rom_start);
+
+#endif
+
+#endif
+
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c
new file mode 100644
index 0000000..1c8c2f1
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_pci.c
@@ -0,0 +1,1564 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include "../pci.h"
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+
+
+u8 cpqhp_nic_irq;
+u8 cpqhp_disk_irq;
+
+static u16 unused_IRQ;
+
+/*
+ * detect_HRT_floating_pointer
+ *
+ * find the Hot Plug Resource Table in the specified region of memory.
+ *
+ */
+static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end)
+{
+	void __iomem *fp;
+	void __iomem *endp;
+	u8 temp1, temp2, temp3, temp4;
+	int status = 0;
+
+	endp = (end - sizeof(struct hrt) + 1);
+
+	for (fp = begin; fp <= endp; fp += 16) {
+		temp1 = readb(fp + SIG0);
+		temp2 = readb(fp + SIG1);
+		temp3 = readb(fp + SIG2);
+		temp4 = readb(fp + SIG3);
+		if (temp1 == '$' &&
+		    temp2 == 'H' &&
+		    temp3 == 'R' &&
+		    temp4 == 'T') {
+			status = 1;
+			break;
+		}
+	}
+
+	if (!status)
+		fp = NULL;
+
+	dbg("Discovered Hotplug Resource Table at %p\n", fp);
+	return fp;
+}
+
+
+int cpqhp_configure_device (struct controller *ctrl, struct pci_func *func)
+{
+	struct pci_bus *child;
+	int num;
+
+	pci_lock_rescan_remove();
+
+	if (func->pci_dev == NULL)
+		func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function));
+
+	/* No pci device, we need to create it then */
+	if (func->pci_dev == NULL) {
+		dbg("INFO: pci_dev still null\n");
+
+		num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function));
+		if (num)
+			pci_bus_add_devices(ctrl->pci_dev->bus);
+
+		func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function));
+		if (func->pci_dev == NULL) {
+			dbg("ERROR: pci_dev still null\n");
+			goto out;
+		}
+	}
+
+	if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+		pci_hp_add_bridge(func->pci_dev);
+		child = func->pci_dev->subordinate;
+		if (child)
+			pci_bus_add_devices(child);
+	}
+
+	pci_dev_put(func->pci_dev);
+
+ out:
+	pci_unlock_rescan_remove();
+	return 0;
+}
+
+
+int cpqhp_unconfigure_device(struct pci_func *func)
+{
+	int j;
+
+	dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function);
+
+	pci_lock_rescan_remove();
+	for (j=0; j<8 ; j++) {
+		struct pci_dev *temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
+		if (temp) {
+			pci_dev_put(temp);
+			pci_stop_and_remove_bus_device(temp);
+		}
+	}
+	pci_unlock_rescan_remove();
+	return 0;
+}
+
+static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value)
+{
+	u32 vendID = 0;
+
+	if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1)
+		return -1;
+	if (vendID == 0xffffffff)
+		return -1;
+	return pci_bus_read_config_dword (bus, devfn, offset, value);
+}
+
+
+/*
+ * cpqhp_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ * @slot: pointer to u8 where slot number will be returned
+ */
+int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+	int rc = 0;
+
+	if (cpqhp_legacy_mode) {
+		struct pci_dev *fakedev;
+		struct pci_bus *fakebus;
+		u16 temp_word;
+
+		fakedev = kmalloc(sizeof(*fakedev), GFP_KERNEL);
+		fakebus = kmalloc(sizeof(*fakebus), GFP_KERNEL);
+		if (!fakedev || !fakebus) {
+			kfree(fakedev);
+			kfree(fakebus);
+			return -ENOMEM;
+		}
+
+		fakedev->devfn = dev_num << 3;
+		fakedev->bus = fakebus;
+		fakebus->number = bus_num;
+		dbg("%s: dev %d, bus %d, pin %d, num %d\n",
+		    __func__, dev_num, bus_num, int_pin, irq_num);
+		rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num);
+		kfree(fakedev);
+		kfree(fakebus);
+		dbg("%s: rc %d\n", __func__, rc);
+		if (!rc)
+			return !rc;
+
+		/* set the Edge Level Control Register (ELCR) */
+		temp_word = inb(0x4d0);
+		temp_word |= inb(0x4d1) << 8;
+
+		temp_word |= 0x01 << irq_num;
+
+		/* This should only be for x86 as it sets the Edge Level
+		 * Control Register
+		 */
+		outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word &
+		0xFF00) >> 8), 0x4d1); rc = 0; }
+
+	return rc;
+}
+
+
+static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_num)
+{
+	u16 tdevice;
+	u32 work;
+	u8 tbus;
+
+	ctrl->pci_bus->number = bus_num;
+
+	for (tdevice = 0; tdevice < 0xFF; tdevice++) {
+		/* Scan for access first */
+		if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
+			continue;
+		dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
+		/* Yep we got one. Not a bridge ? */
+		if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
+			*dev_num = tdevice;
+			dbg("found it !\n");
+			return 0;
+		}
+	}
+	for (tdevice = 0; tdevice < 0xFF; tdevice++) {
+		/* Scan for access first */
+		if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
+			continue;
+		dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
+		/* Yep we got one. bridge ? */
+		if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+			pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus);
+			/* XXX: no recursion, wtf? */
+			dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
+{
+	int loop, len;
+	u32 work;
+	u8 tbus, tdevice, tslot;
+
+	len = cpqhp_routing_table_length();
+	for (loop = 0; loop < len; ++loop) {
+		tbus = cpqhp_routing_table->slots[loop].bus;
+		tdevice = cpqhp_routing_table->slots[loop].devfn;
+		tslot = cpqhp_routing_table->slots[loop].slot;
+
+		if (tslot == slot) {
+			*bus_num = tbus;
+			*dev_num = tdevice;
+			ctrl->pci_bus->number = tbus;
+			pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work);
+			if (!nobridge || (work == 0xffffffff))
+				return 0;
+
+			dbg("bus_num %d devfn %d\n", *bus_num, *dev_num);
+			pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work);
+			dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS);
+
+			if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+				pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus);
+				dbg("Scan bus for Non Bridge: bus %d\n", tbus);
+				if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
+					*bus_num = tbus;
+					return 0;
+				}
+			} else
+				return 0;
+		}
+	}
+	return -1;
+}
+
+
+int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot)
+{
+	/* plain (bridges allowed) */
+	return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0);
+}
+
+
+/* More PCI configuration routines; this time centered around hotplug
+ * controller
+ */
+
+
+/*
+ * cpqhp_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note:  For non-hot plug buses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
+{
+	long rc;
+	u8 class_code;
+	u8 header_type;
+	u32 ID;
+	u8 secondary_bus;
+	struct pci_func *new_slot;
+	int sub_bus;
+	int FirstSupported;
+	int LastSupported;
+	int max_functions;
+	int function;
+	u8 DevError;
+	int device = 0;
+	int cloop = 0;
+	int stop_it;
+	int index;
+
+	/* Decide which slots are supported */
+
+	if (is_hot_plug) {
+		/*
+		 * is_hot_plug is the slot mask
+		 */
+		FirstSupported = is_hot_plug >> 4;
+		LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1;
+	} else {
+		FirstSupported = 0;
+		LastSupported = 0x1F;
+	}
+
+	/* Save PCI configuration space for all devices in supported slots */
+	ctrl->pci_bus->number = busnumber;
+	for (device = FirstSupported; device <= LastSupported; device++) {
+		ID = 0xFFFFFFFF;
+		rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
+
+		if (ID == 0xFFFFFFFF) {
+			if (is_hot_plug) {
+				/* Setup slot structure with entry for empty
+				 * slot
+				 */
+				new_slot = cpqhp_slot_create(busnumber);
+				if (new_slot == NULL)
+					return 1;
+
+				new_slot->bus = (u8) busnumber;
+				new_slot->device = (u8) device;
+				new_slot->function = 0;
+				new_slot->is_a_board = 0;
+				new_slot->presence_save = 0;
+				new_slot->switch_save = 0;
+			}
+			continue;
+		}
+
+		rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
+		if (rc)
+			return rc;
+
+		rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
+		if (rc)
+			return rc;
+
+		/* If multi-function device, set max_functions to 8 */
+		if (header_type & 0x80)
+			max_functions = 8;
+		else
+			max_functions = 1;
+
+		function = 0;
+
+		do {
+			DevError = 0;
+			if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+				/* Recurse the subordinate bus
+				 * get the subordinate bus number
+				 */
+				rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
+				if (rc) {
+					return rc;
+				} else {
+					sub_bus = (int) secondary_bus;
+
+					/* Save secondary bus cfg spc
+					 * with this recursive call.
+					 */
+					rc = cpqhp_save_config(ctrl, sub_bus, 0);
+					if (rc)
+						return rc;
+					ctrl->pci_bus->number = busnumber;
+				}
+			}
+
+			index = 0;
+			new_slot = cpqhp_slot_find(busnumber, device, index++);
+			while (new_slot &&
+			       (new_slot->function != (u8) function))
+				new_slot = cpqhp_slot_find(busnumber, device, index++);
+
+			if (!new_slot) {
+				/* Setup slot structure. */
+				new_slot = cpqhp_slot_create(busnumber);
+				if (new_slot == NULL)
+					return 1;
+			}
+
+			new_slot->bus = (u8) busnumber;
+			new_slot->device = (u8) device;
+			new_slot->function = (u8) function;
+			new_slot->is_a_board = 1;
+			new_slot->switch_save = 0x10;
+			/* In case of unsupported board */
+			new_slot->status = DevError;
+			new_slot->pci_dev = pci_get_bus_and_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
+
+			for (cloop = 0; cloop < 0x20; cloop++) {
+				rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+				if (rc)
+					return rc;
+			}
+
+			pci_dev_put(new_slot->pci_dev);
+
+			function++;
+
+			stop_it = 0;
+
+			/* this loop skips to the next present function
+			 * reading in Class Code and Header type.
+			 */
+			while ((function < max_functions) && (!stop_it)) {
+				rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
+				if (ID == 0xFFFFFFFF) {
+					function++;
+					continue;
+				}
+				rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
+				if (rc)
+					return rc;
+
+				rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
+				if (rc)
+					return rc;
+
+				stop_it++;
+			}
+
+		} while (function < max_functions);
+	}			/* End of FOR loop */
+
+	return 0;
+}
+
+
+/*
+ * cpqhp_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate buses.
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func *new_slot)
+{
+	long rc;
+	u8 class_code;
+	u8 header_type;
+	u32 ID;
+	u8 secondary_bus;
+	int sub_bus;
+	int max_functions;
+	int function = 0;
+	int cloop = 0;
+	int stop_it;
+
+	ID = 0xFFFFFFFF;
+
+	ctrl->pci_bus->number = new_slot->bus;
+	pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
+
+	if (ID == 0xFFFFFFFF)
+		return 2;
+
+	pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
+	pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
+
+	if (header_type & 0x80)	/* Multi-function device */
+		max_functions = 8;
+	else
+		max_functions = 1;
+
+	while (function < max_functions) {
+		if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+			/*  Recurse the subordinate bus */
+			pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
+
+			sub_bus = (int) secondary_bus;
+
+			/* Save the config headers for the secondary
+			 * bus.
+			 */
+			rc = cpqhp_save_config(ctrl, sub_bus, 0);
+			if (rc)
+				return(rc);
+			ctrl->pci_bus->number = new_slot->bus;
+
+		}
+
+		new_slot->status = 0;
+
+		for (cloop = 0; cloop < 0x20; cloop++)
+			pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+
+		function++;
+
+		stop_it = 0;
+
+		/* this loop skips to the next present function
+		 * reading in the Class Code and the Header type.
+		 */
+		while ((function < max_functions) && (!stop_it)) {
+			pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
+
+			if (ID == 0xFFFFFFFF)
+				function++;
+			else {
+				pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+				pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+				stop_it++;
+			}
+		}
+
+	}
+
+	return 0;
+}
+
+
+/*
+ * cpqhp_save_base_addr_length
+ *
+ * Saves the length of all base address registers for the
+ * specified slot.  this is for hot plug REPLACE
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func)
+{
+	u8 cloop;
+	u8 header_type;
+	u8 secondary_bus;
+	u8 type;
+	int sub_bus;
+	u32 temp_register;
+	u32 base;
+	u32 rc;
+	struct pci_func *next;
+	int index = 0;
+	struct pci_bus *pci_bus = ctrl->pci_bus;
+	unsigned int devfn;
+
+	func = cpqhp_slot_find(func->bus, func->device, index++);
+
+	while (func != NULL) {
+		pci_bus->number = func->bus;
+		devfn = PCI_DEVFN(func->device, func->function);
+
+		/* Check for Bridge */
+		pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+		if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+			pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+			sub_bus = (int) secondary_bus;
+
+			next = cpqhp_slot_list[sub_bus];
+
+			while (next != NULL) {
+				rc = cpqhp_save_base_addr_length(ctrl, next);
+				if (rc)
+					return rc;
+
+				next = next->next;
+			}
+			pci_bus->number = func->bus;
+
+			/* FIXME: this loop is duplicated in the non-bridge
+			 * case.  The two could be rolled together Figure out
+			 * IO and memory base lengths
+			 */
+			for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+				temp_register = 0xFFFFFFFF;
+				pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+				pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+				/* If this register is implemented */
+				if (base) {
+					if (base & 0x01L) {
+						/* IO base
+						 * set base = amount of IO space
+						 * requested
+						 */
+						base = base & 0xFFFFFFFE;
+						base = (~base) + 1;
+
+						type = 1;
+					} else {
+						/* memory base */
+						base = base & 0xFFFFFFF0;
+						base = (~base) + 1;
+
+						type = 0;
+					}
+				} else {
+					base = 0x0L;
+					type = 0;
+				}
+
+				/* Save information in slot structure */
+				func->base_length[(cloop - 0x10) >> 2] =
+				base;
+				func->base_type[(cloop - 0x10) >> 2] = type;
+
+			}	/* End of base register loop */
+
+		} else if ((header_type & 0x7F) == 0x00) {
+			/* Figure out IO and memory base lengths */
+			for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+				temp_register = 0xFFFFFFFF;
+				pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+				pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+
+				/* If this register is implemented */
+				if (base) {
+					if (base & 0x01L) {
+						/* IO base
+						 * base = amount of IO space
+						 * requested
+						 */
+						base = base & 0xFFFFFFFE;
+						base = (~base) + 1;
+
+						type = 1;
+					} else {
+						/* memory base
+						 * base = amount of memory
+						 * space requested
+						 */
+						base = base & 0xFFFFFFF0;
+						base = (~base) + 1;
+
+						type = 0;
+					}
+				} else {
+					base = 0x0L;
+					type = 0;
+				}
+
+				/* Save information in slot structure */
+				func->base_length[(cloop - 0x10) >> 2] = base;
+				func->base_type[(cloop - 0x10) >> 2] = type;
+
+			}	/* End of base register loop */
+
+		} else {	  /* Some other unknown header type */
+		}
+
+		/* find the next device in this slot */
+		func = cpqhp_slot_find(func->bus, func->device, index++);
+	}
+
+	return(0);
+}
+
+
+/*
+ * cpqhp_save_used_resources
+ *
+ * Stores used resource information for existing boards.  this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func *func)
+{
+	u8 cloop;
+	u8 header_type;
+	u8 secondary_bus;
+	u8 temp_byte;
+	u8 b_base;
+	u8 b_length;
+	u16 command;
+	u16 save_command;
+	u16 w_base;
+	u16 w_length;
+	u32 temp_register;
+	u32 save_base;
+	u32 base;
+	int index = 0;
+	struct pci_resource *mem_node;
+	struct pci_resource *p_mem_node;
+	struct pci_resource *io_node;
+	struct pci_resource *bus_node;
+	struct pci_bus *pci_bus = ctrl->pci_bus;
+	unsigned int devfn;
+
+	func = cpqhp_slot_find(func->bus, func->device, index++);
+
+	while ((func != NULL) && func->is_a_board) {
+		pci_bus->number = func->bus;
+		devfn = PCI_DEVFN(func->device, func->function);
+
+		/* Save the command register */
+		pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
+
+		/* disable card */
+		command = 0x00;
+		pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+
+		/* Check for Bridge */
+		pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+		if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+			/* Clear Bridge Control Register */
+			command = 0x00;
+			pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+			pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+			pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+			bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
+			if (!bus_node)
+				return -ENOMEM;
+
+			bus_node->base = secondary_bus;
+			bus_node->length = temp_byte - secondary_bus + 1;
+
+			bus_node->next = func->bus_head;
+			func->bus_head = bus_node;
+
+			/* Save IO base and Limit registers */
+			pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base);
+			pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length);
+
+			if ((b_base <= b_length) && (save_command & 0x01)) {
+				io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
+				if (!io_node)
+					return -ENOMEM;
+
+				io_node->base = (b_base & 0xF0) << 8;
+				io_node->length = (b_length - b_base + 0x10) << 8;
+
+				io_node->next = func->io_head;
+				func->io_head = io_node;
+			}
+
+			/* Save memory base and Limit registers */
+			pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+			pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+			if ((w_base <= w_length) && (save_command & 0x02)) {
+				mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
+				if (!mem_node)
+					return -ENOMEM;
+
+				mem_node->base = w_base << 16;
+				mem_node->length = (w_length - w_base + 0x10) << 16;
+
+				mem_node->next = func->mem_head;
+				func->mem_head = mem_node;
+			}
+
+			/* Save prefetchable memory base and Limit registers */
+			pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+			pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+			if ((w_base <= w_length) && (save_command & 0x02)) {
+				p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
+				if (!p_mem_node)
+					return -ENOMEM;
+
+				p_mem_node->base = w_base << 16;
+				p_mem_node->length = (w_length - w_base + 0x10) << 16;
+
+				p_mem_node->next = func->p_mem_head;
+				func->p_mem_head = p_mem_node;
+			}
+			/* Figure out IO and memory base lengths */
+			for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+				pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base);
+
+				temp_register = 0xFFFFFFFF;
+				pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+				pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
+
+				temp_register = base;
+
+				/* If this register is implemented */
+				if (base) {
+					if (((base & 0x03L) == 0x01)
+					    && (save_command & 0x01)) {
+						/* IO base
+						 * set temp_register = amount
+						 * of IO space requested
+						 */
+						temp_register = base & 0xFFFFFFFE;
+						temp_register = (~temp_register) + 1;
+
+						io_node = kmalloc(sizeof(*io_node),
+								GFP_KERNEL);
+						if (!io_node)
+							return -ENOMEM;
+
+						io_node->base =
+						save_base & (~0x03L);
+						io_node->length = temp_register;
+
+						io_node->next = func->io_head;
+						func->io_head = io_node;
+					} else
+						if (((base & 0x0BL) == 0x08)
+						    && (save_command & 0x02)) {
+						/* prefetchable memory base */
+						temp_register = base & 0xFFFFFFF0;
+						temp_register = (~temp_register) + 1;
+
+						p_mem_node = kmalloc(sizeof(*p_mem_node),
+								GFP_KERNEL);
+						if (!p_mem_node)
+							return -ENOMEM;
+
+						p_mem_node->base = save_base & (~0x0FL);
+						p_mem_node->length = temp_register;
+
+						p_mem_node->next = func->p_mem_head;
+						func->p_mem_head = p_mem_node;
+					} else
+						if (((base & 0x0BL) == 0x00)
+						    && (save_command & 0x02)) {
+						/* prefetchable memory base */
+						temp_register = base & 0xFFFFFFF0;
+						temp_register = (~temp_register) + 1;
+
+						mem_node = kmalloc(sizeof(*mem_node),
+								GFP_KERNEL);
+						if (!mem_node)
+							return -ENOMEM;
+
+						mem_node->base = save_base & (~0x0FL);
+						mem_node->length = temp_register;
+
+						mem_node->next = func->mem_head;
+						func->mem_head = mem_node;
+					} else
+						return(1);
+				}
+			}	/* End of base register loop */
+		/* Standard header */
+		} else if ((header_type & 0x7F) == 0x00) {
+			/* Figure out IO and memory base lengths */
+			for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+				pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
+
+				temp_register = 0xFFFFFFFF;
+				pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+				pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
+
+				temp_register = base;
+
+				/* If this register is implemented */
+				if (base) {
+					if (((base & 0x03L) == 0x01)
+					    && (save_command & 0x01)) {
+						/* IO base
+						 * set temp_register = amount
+						 * of IO space requested
+						 */
+						temp_register = base & 0xFFFFFFFE;
+						temp_register = (~temp_register) + 1;
+
+						io_node = kmalloc(sizeof(*io_node),
+								GFP_KERNEL);
+						if (!io_node)
+							return -ENOMEM;
+
+						io_node->base = save_base & (~0x01L);
+						io_node->length = temp_register;
+
+						io_node->next = func->io_head;
+						func->io_head = io_node;
+					} else
+						if (((base & 0x0BL) == 0x08)
+						    && (save_command & 0x02)) {
+						/* prefetchable memory base */
+						temp_register = base & 0xFFFFFFF0;
+						temp_register = (~temp_register) + 1;
+
+						p_mem_node = kmalloc(sizeof(*p_mem_node),
+								GFP_KERNEL);
+						if (!p_mem_node)
+							return -ENOMEM;
+
+						p_mem_node->base = save_base & (~0x0FL);
+						p_mem_node->length = temp_register;
+
+						p_mem_node->next = func->p_mem_head;
+						func->p_mem_head = p_mem_node;
+					} else
+						if (((base & 0x0BL) == 0x00)
+						    && (save_command & 0x02)) {
+						/* prefetchable memory base */
+						temp_register = base & 0xFFFFFFF0;
+						temp_register = (~temp_register) + 1;
+
+						mem_node = kmalloc(sizeof(*mem_node),
+								GFP_KERNEL);
+						if (!mem_node)
+							return -ENOMEM;
+
+						mem_node->base = save_base & (~0x0FL);
+						mem_node->length = temp_register;
+
+						mem_node->next = func->mem_head;
+						func->mem_head = mem_node;
+					} else
+						return(1);
+				}
+			}	/* End of base register loop */
+		}
+
+		/* find the next device in this slot */
+		func = cpqhp_slot_find(func->bus, func->device, index++);
+	}
+
+	return 0;
+}
+
+
+/*
+ * cpqhp_configure_board
+ *
+ * Copies saved configuration information to one slot.
+ * this is called recursively for bridge devices.
+ * this is for hot plug REPLACE!
+ *
+ * returns 0 if success
+ */
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func)
+{
+	int cloop;
+	u8 header_type;
+	u8 secondary_bus;
+	int sub_bus;
+	struct pci_func *next;
+	u32 temp;
+	u32 rc;
+	int index = 0;
+	struct pci_bus *pci_bus = ctrl->pci_bus;
+	unsigned int devfn;
+
+	func = cpqhp_slot_find(func->bus, func->device, index++);
+
+	while (func != NULL) {
+		pci_bus->number = func->bus;
+		devfn = PCI_DEVFN(func->device, func->function);
+
+		/* Start at the top of config space so that the control
+		 * registers are programmed last
+		 */
+		for (cloop = 0x3C; cloop > 0; cloop -= 4)
+			pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
+
+		pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+		/* If this is a bridge device, restore subordinate devices */
+		if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+			pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+			sub_bus = (int) secondary_bus;
+
+			next = cpqhp_slot_list[sub_bus];
+
+			while (next != NULL) {
+				rc = cpqhp_configure_board(ctrl, next);
+				if (rc)
+					return rc;
+
+				next = next->next;
+			}
+		} else {
+
+			/* Check all the base Address Registers to make sure
+			 * they are the same.  If not, the board is different.
+			 */
+
+			for (cloop = 16; cloop < 40; cloop += 4) {
+				pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp);
+
+				if (temp != func->config_space[cloop >> 2]) {
+					dbg("Config space compare failure!!! offset = %x\n", cloop);
+					dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function);
+					dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]);
+					return 1;
+				}
+			}
+		}
+
+		func->configured = 1;
+
+		func = cpqhp_slot_find(func->bus, func->device, index++);
+	}
+
+	return 0;
+}
+
+
+/*
+ * cpqhp_valid_replace
+ *
+ * this function checks to see if a board is the same as the
+ * one it is replacing.  this check will detect if the device's
+ * vendor or device id's are the same
+ *
+ * returns 0 if the board is the same nonzero otherwise
+ */
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func)
+{
+	u8 cloop;
+	u8 header_type;
+	u8 secondary_bus;
+	u8 type;
+	u32 temp_register = 0;
+	u32 base;
+	u32 rc;
+	struct pci_func *next;
+	int index = 0;
+	struct pci_bus *pci_bus = ctrl->pci_bus;
+	unsigned int devfn;
+
+	if (!func->is_a_board)
+		return(ADD_NOT_SUPPORTED);
+
+	func = cpqhp_slot_find(func->bus, func->device, index++);
+
+	while (func != NULL) {
+		pci_bus->number = func->bus;
+		devfn = PCI_DEVFN(func->device, func->function);
+
+		pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
+
+		/* No adapter present */
+		if (temp_register == 0xFFFFFFFF)
+			return(NO_ADAPTER_PRESENT);
+
+		if (temp_register != func->config_space[0])
+			return(ADAPTER_NOT_SAME);
+
+		/* Check for same revision number and class code */
+		pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
+
+		/* Adapter not the same */
+		if (temp_register != func->config_space[0x08 >> 2])
+			return(ADAPTER_NOT_SAME);
+
+		/* Check for Bridge */
+		pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+		if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+			/* In order to continue checking, we must program the
+			 * bus registers in the bridge to respond to accesses
+			 * for its subordinate bus(es)
+			 */
+
+			temp_register = func->config_space[0x18 >> 2];
+			pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
+
+			secondary_bus = (temp_register >> 8) & 0xFF;
+
+			next = cpqhp_slot_list[secondary_bus];
+
+			while (next != NULL) {
+				rc = cpqhp_valid_replace(ctrl, next);
+				if (rc)
+					return rc;
+
+				next = next->next;
+			}
+
+		}
+		/* Check to see if it is a standard config header */
+		else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+			/* Check subsystem vendor and ID */
+			pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
+
+			if (temp_register != func->config_space[0x2C >> 2]) {
+				/* If it's a SMART-2 and the register isn't
+				 * filled in, ignore the difference because
+				 * they just have an old rev of the firmware
+				 */
+				if (!((func->config_space[0] == 0xAE100E11)
+				      && (temp_register == 0x00L)))
+					return(ADAPTER_NOT_SAME);
+			}
+			/* Figure out IO and memory base lengths */
+			for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+				temp_register = 0xFFFFFFFF;
+				pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+				pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+
+				/* If this register is implemented */
+				if (base) {
+					if (base & 0x01L) {
+						/* IO base
+						 * set base = amount of IO
+						 * space requested
+						 */
+						base = base & 0xFFFFFFFE;
+						base = (~base) + 1;
+
+						type = 1;
+					} else {
+						/* memory base */
+						base = base & 0xFFFFFFF0;
+						base = (~base) + 1;
+
+						type = 0;
+					}
+				} else {
+					base = 0x0L;
+					type = 0;
+				}
+
+				/* Check information in slot structure */
+				if (func->base_length[(cloop - 0x10) >> 2] != base)
+					return(ADAPTER_NOT_SAME);
+
+				if (func->base_type[(cloop - 0x10) >> 2] != type)
+					return(ADAPTER_NOT_SAME);
+
+			}	/* End of base register loop */
+
+		}		/* End of (type 0 config space) else */
+		else {
+			/* this is not a type 0 or 1 config space header so
+			 * we don't know how to do it
+			 */
+			return(DEVICE_TYPE_NOT_SUPPORTED);
+		}
+
+		/* Get the next function */
+		func = cpqhp_slot_find(func->bus, func->device, index++);
+	}
+
+
+	return 0;
+}
+
+
+/*
+ * cpqhp_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start)
+{
+	u8 temp;
+	u8 populated_slot;
+	u8 bridged_slot;
+	void __iomem *one_slot;
+	void __iomem *rom_resource_table;
+	struct pci_func *func = NULL;
+	int i = 10, index;
+	u32 temp_dword, rc;
+	struct pci_resource *mem_node;
+	struct pci_resource *p_mem_node;
+	struct pci_resource *io_node;
+	struct pci_resource *bus_node;
+
+	rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
+	dbg("rom_resource_table = %p\n", rom_resource_table);
+
+	if (rom_resource_table == NULL)
+		return -ENODEV;
+
+	/* Sum all resources and setup resource maps */
+	unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
+	dbg("unused_IRQ = %x\n", unused_IRQ);
+
+	temp = 0;
+	while (unused_IRQ) {
+		if (unused_IRQ & 1) {
+			cpqhp_disk_irq = temp;
+			break;
+		}
+		unused_IRQ = unused_IRQ >> 1;
+		temp++;
+	}
+
+	dbg("cpqhp_disk_irq= %d\n", cpqhp_disk_irq);
+	unused_IRQ = unused_IRQ >> 1;
+	temp++;
+
+	while (unused_IRQ) {
+		if (unused_IRQ & 1) {
+			cpqhp_nic_irq = temp;
+			break;
+		}
+		unused_IRQ = unused_IRQ >> 1;
+		temp++;
+	}
+
+	dbg("cpqhp_nic_irq= %d\n", cpqhp_nic_irq);
+	unused_IRQ = readl(rom_resource_table + PCIIRQ);
+
+	temp = 0;
+
+	if (!cpqhp_nic_irq)
+		cpqhp_nic_irq = ctrl->cfgspc_irq;
+
+	if (!cpqhp_disk_irq)
+		cpqhp_disk_irq = ctrl->cfgspc_irq;
+
+	dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq);
+
+	rc = compaq_nvram_load(rom_start, ctrl);
+	if (rc)
+		return rc;
+
+	one_slot = rom_resource_table + sizeof (struct hrt);
+
+	i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
+	dbg("number_of_entries = %d\n", i);
+
+	if (!readb(one_slot + SECONDARY_BUS))
+		return 1;
+
+	dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n");
+
+	while (i && readb(one_slot + SECONDARY_BUS)) {
+		u8 dev_func = readb(one_slot + DEV_FUNC);
+		u8 primary_bus = readb(one_slot + PRIMARY_BUS);
+		u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
+		u8 max_bus = readb(one_slot + MAX_BUS);
+		u16 io_base = readw(one_slot + IO_BASE);
+		u16 io_length = readw(one_slot + IO_LENGTH);
+		u16 mem_base = readw(one_slot + MEM_BASE);
+		u16 mem_length = readw(one_slot + MEM_LENGTH);
+		u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
+		u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
+
+		dbg("%2.2x | %4.4x  | %4.4x | %4.4x   | %4.4x | %4.4x   | %4.4x |%2.2x %2.2x %2.2x\n",
+		    dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
+		    primary_bus, secondary_bus, max_bus);
+
+		/* If this entry isn't for our controller's bus, ignore it */
+		if (primary_bus != ctrl->bus) {
+			i--;
+			one_slot += sizeof (struct slot_rt);
+			continue;
+		}
+		/* find out if this entry is for an occupied slot */
+		ctrl->pci_bus->number = primary_bus;
+		pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
+		dbg("temp_D_word = %x\n", temp_dword);
+
+		if (temp_dword != 0xFFFFFFFF) {
+			index = 0;
+			func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0);
+
+			while (func && (func->function != (dev_func & 0x07))) {
+				dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index);
+				func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++);
+			}
+
+			/* If we can't find a match, skip this table entry */
+			if (!func) {
+				i--;
+				one_slot += sizeof (struct slot_rt);
+				continue;
+			}
+			/* this may not work and shouldn't be used */
+			if (secondary_bus != primary_bus)
+				bridged_slot = 1;
+			else
+				bridged_slot = 0;
+
+			populated_slot = 1;
+		} else {
+			populated_slot = 0;
+			bridged_slot = 0;
+		}
+
+
+		/* If we've got a valid IO base, use it */
+
+		temp_dword = io_base + io_length;
+
+		if ((io_base) && (temp_dword < 0x10000)) {
+			io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
+			if (!io_node)
+				return -ENOMEM;
+
+			io_node->base = io_base;
+			io_node->length = io_length;
+
+			dbg("found io_node(base, length) = %x, %x\n",
+					io_node->base, io_node->length);
+			dbg("populated slot =%d \n", populated_slot);
+			if (!populated_slot) {
+				io_node->next = ctrl->io_head;
+				ctrl->io_head = io_node;
+			} else {
+				io_node->next = func->io_head;
+				func->io_head = io_node;
+			}
+		}
+
+		/* If we've got a valid memory base, use it */
+		temp_dword = mem_base + mem_length;
+		if ((mem_base) && (temp_dword < 0x10000)) {
+			mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
+			if (!mem_node)
+				return -ENOMEM;
+
+			mem_node->base = mem_base << 16;
+
+			mem_node->length = mem_length << 16;
+
+			dbg("found mem_node(base, length) = %x, %x\n",
+					mem_node->base, mem_node->length);
+			dbg("populated slot =%d \n", populated_slot);
+			if (!populated_slot) {
+				mem_node->next = ctrl->mem_head;
+				ctrl->mem_head = mem_node;
+			} else {
+				mem_node->next = func->mem_head;
+				func->mem_head = mem_node;
+			}
+		}
+
+		/* If we've got a valid prefetchable memory base, and
+		 * the base + length isn't greater than 0xFFFF
+		 */
+		temp_dword = pre_mem_base + pre_mem_length;
+		if ((pre_mem_base) && (temp_dword < 0x10000)) {
+			p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
+			if (!p_mem_node)
+				return -ENOMEM;
+
+			p_mem_node->base = pre_mem_base << 16;
+
+			p_mem_node->length = pre_mem_length << 16;
+			dbg("found p_mem_node(base, length) = %x, %x\n",
+					p_mem_node->base, p_mem_node->length);
+			dbg("populated slot =%d \n", populated_slot);
+
+			if (!populated_slot) {
+				p_mem_node->next = ctrl->p_mem_head;
+				ctrl->p_mem_head = p_mem_node;
+			} else {
+				p_mem_node->next = func->p_mem_head;
+				func->p_mem_head = p_mem_node;
+			}
+		}
+
+		/* If we've got a valid bus number, use it
+		 * The second condition is to ignore bus numbers on
+		 * populated slots that don't have PCI-PCI bridges
+		 */
+		if (secondary_bus && (secondary_bus != primary_bus)) {
+			bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
+			if (!bus_node)
+				return -ENOMEM;
+
+			bus_node->base = secondary_bus;
+			bus_node->length = max_bus - secondary_bus + 1;
+			dbg("found bus_node(base, length) = %x, %x\n",
+					bus_node->base, bus_node->length);
+			dbg("populated slot =%d \n", populated_slot);
+			if (!populated_slot) {
+				bus_node->next = ctrl->bus_head;
+				ctrl->bus_head = bus_node;
+			} else {
+				bus_node->next = func->bus_head;
+				func->bus_head = bus_node;
+			}
+		}
+
+		i--;
+		one_slot += sizeof (struct slot_rt);
+	}
+
+	/* If all of the following fail, we don't have any resources for
+	 * hot plug add
+	 */
+	rc = 1;
+	rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+	rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+	rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+	rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+	return rc;
+}
+
+
+/*
+ * cpqhp_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int cpqhp_return_board_resources(struct pci_func *func, struct resource_lists *resources)
+{
+	int rc = 0;
+	struct pci_resource *node;
+	struct pci_resource *t_node;
+	dbg("%s\n", __func__);
+
+	if (!func)
+		return 1;
+
+	node = func->io_head;
+	func->io_head = NULL;
+	while (node) {
+		t_node = node->next;
+		return_resource(&(resources->io_head), node);
+		node = t_node;
+	}
+
+	node = func->mem_head;
+	func->mem_head = NULL;
+	while (node) {
+		t_node = node->next;
+		return_resource(&(resources->mem_head), node);
+		node = t_node;
+	}
+
+	node = func->p_mem_head;
+	func->p_mem_head = NULL;
+	while (node) {
+		t_node = node->next;
+		return_resource(&(resources->p_mem_head), node);
+		node = t_node;
+	}
+
+	node = func->bus_head;
+	func->bus_head = NULL;
+	while (node) {
+		t_node = node->next;
+		return_resource(&(resources->bus_head), node);
+		node = t_node;
+	}
+
+	rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head));
+	rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head));
+	rc |= cpqhp_resource_sort_and_combine(&(resources->io_head));
+	rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head));
+
+	return rc;
+}
+
+
+/*
+ * cpqhp_destroy_resource_list
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void cpqhp_destroy_resource_list (struct resource_lists *resources)
+{
+	struct pci_resource *res, *tres;
+
+	res = resources->io_head;
+	resources->io_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = resources->mem_head;
+	resources->mem_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = resources->p_mem_head;
+	resources->p_mem_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = resources->bus_head;
+	resources->bus_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+}
+
+
+/*
+ * cpqhp_destroy_board_resources
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void cpqhp_destroy_board_resources (struct pci_func *func)
+{
+	struct pci_resource *res, *tres;
+
+	res = func->io_head;
+	func->io_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = func->mem_head;
+	func->mem_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = func->p_mem_head;
+	func->p_mem_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+
+	res = func->bus_head;
+	func->bus_head = NULL;
+
+	while (res) {
+		tres = res;
+		res = res->next;
+		kfree(tres);
+	}
+}
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
new file mode 100644
index 0000000..d81648f
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -0,0 +1,222 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include "cpqphp.h"
+
+static DEFINE_MUTEX(cpqphp_mutex);
+static int show_ctrl (struct controller *ctrl, char *buf)
+{
+	char *out = buf;
+	int index;
+	struct pci_resource *res;
+
+	out += sprintf(buf, "Free resources: memory\n");
+	index = 11;
+	res = ctrl->mem_head;
+	while (res && index--) {
+		out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+		res = res->next;
+	}
+	out += sprintf(out, "Free resources: prefetchable memory\n");
+	index = 11;
+	res = ctrl->p_mem_head;
+	while (res && index--) {
+		out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+		res = res->next;
+	}
+	out += sprintf(out, "Free resources: IO\n");
+	index = 11;
+	res = ctrl->io_head;
+	while (res && index--) {
+		out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+		res = res->next;
+	}
+	out += sprintf(out, "Free resources: bus numbers\n");
+	index = 11;
+	res = ctrl->bus_head;
+	while (res && index--) {
+		out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+		res = res->next;
+	}
+
+	return out - buf;
+}
+
+static int show_dev (struct controller *ctrl, char *buf)
+{
+	char *out = buf;
+	int index;
+	struct pci_resource *res;
+	struct pci_func *new_slot;
+	struct slot *slot;
+
+	slot = ctrl->slot;
+
+	while (slot) {
+		new_slot = cpqhp_slot_find(slot->bus, slot->device, 0);
+		if (!new_slot)
+			break;
+		out += sprintf(out, "assigned resources: memory\n");
+		index = 11;
+		res = new_slot->mem_head;
+		while (res && index--) {
+			out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+			res = res->next;
+		}
+		out += sprintf(out, "assigned resources: prefetchable memory\n");
+		index = 11;
+		res = new_slot->p_mem_head;
+		while (res && index--) {
+			out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+			res = res->next;
+		}
+		out += sprintf(out, "assigned resources: IO\n");
+		index = 11;
+		res = new_slot->io_head;
+		while (res && index--) {
+			out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+			res = res->next;
+		}
+		out += sprintf(out, "assigned resources: bus numbers\n");
+		index = 11;
+		res = new_slot->bus_head;
+		while (res && index--) {
+			out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+			res = res->next;
+		}
+		slot=slot->next;
+	}
+
+	return out - buf;
+}
+
+static int spew_debug_info(struct controller *ctrl, char *data, int size)
+{
+	int used;
+
+	used = size - show_ctrl(ctrl, data);
+	used = (size - used) - show_dev(ctrl, &data[used]);
+	return used;
+}
+
+struct ctrl_dbg {
+	int size;
+	char *data;
+	struct controller *ctrl;
+};
+
+#define MAX_OUTPUT	(4*PAGE_SIZE)
+
+static int open(struct inode *inode, struct file *file)
+{
+	struct controller *ctrl = inode->i_private;
+	struct ctrl_dbg *dbg;
+	int retval = -ENOMEM;
+
+	mutex_lock(&cpqphp_mutex);
+	dbg = kmalloc(sizeof(*dbg), GFP_KERNEL);
+	if (!dbg)
+		goto exit;
+	dbg->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+	if (!dbg->data) {
+		kfree(dbg);
+		goto exit;
+	}
+	dbg->size = spew_debug_info(ctrl, dbg->data, MAX_OUTPUT);
+	file->private_data = dbg;
+	retval = 0;
+exit:
+	mutex_unlock(&cpqphp_mutex);
+	return retval;
+}
+
+static loff_t lseek(struct file *file, loff_t off, int whence)
+{
+	struct ctrl_dbg *dbg = file->private_data;
+	return fixed_size_llseek(file, off, whence, dbg->size);
+}
+
+static ssize_t read(struct file *file, char __user *buf,
+		    size_t nbytes, loff_t *ppos)
+{
+	struct ctrl_dbg *dbg = file->private_data;
+	return simple_read_from_buffer(buf, nbytes, ppos, dbg->data, dbg->size);
+}
+
+static int release(struct inode *inode, struct file *file)
+{
+	struct ctrl_dbg *dbg = file->private_data;
+
+	kfree(dbg->data);
+	kfree(dbg);
+	return 0;
+}
+
+static const struct file_operations debug_ops = {
+	.owner = THIS_MODULE,
+	.open = open,
+	.llseek = lseek,
+	.read = read,
+	.release = release,
+};
+
+static struct dentry *root;
+
+void cpqhp_initialize_debugfs(void)
+{
+	if (!root)
+		root = debugfs_create_dir("cpqhp", NULL);
+}
+
+void cpqhp_shutdown_debugfs(void)
+{
+	debugfs_remove(root);
+}
+
+void cpqhp_create_debugfs_files(struct controller *ctrl)
+{
+	ctrl->dentry = debugfs_create_file(dev_name(&ctrl->pci_dev->dev),
+					   S_IRUGO, root, ctrl, &debug_ops);
+}
+
+void cpqhp_remove_debugfs_files(struct controller *ctrl)
+{
+	debugfs_remove(ctrl->dentry);
+	ctrl->dentry = NULL;
+}
+
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h
new file mode 100644
index 0000000..e3e46a7
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp.h
@@ -0,0 +1,760 @@
+#ifndef __IBMPHP_H
+#define __IBMPHP_H
+
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
+ *
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/pci_hotplug.h>
+
+extern int ibmphp_debug;
+
+#if !defined(MODULE)
+	#define MY_NAME "ibmphpd"
+#else
+	#define MY_NAME THIS_MODULE->name
+#endif
+#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
+#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
+
+
+/* EBDA stuff */
+
+/***********************************************************
+* SLOT CAPABILITY                                          *
+***********************************************************/
+
+#define EBDA_SLOT_133_MAX		0x20
+#define EBDA_SLOT_100_MAX		0x10
+#define EBDA_SLOT_66_MAX		0x02
+#define EBDA_SLOT_PCIX_CAP		0x08
+
+
+/************************************************************
+*  RESOURCE TYPE                                             *
+************************************************************/
+
+#define EBDA_RSRC_TYPE_MASK		0x03
+#define EBDA_IO_RSRC_TYPE		0x00
+#define EBDA_MEM_RSRC_TYPE		0x01
+#define EBDA_PFM_RSRC_TYPE		0x03
+#define EBDA_RES_RSRC_TYPE		0x02
+
+
+/*************************************************************
+*  IO RESTRICTION TYPE                                       *
+*************************************************************/
+
+#define EBDA_IO_RESTRI_MASK		0x0c
+#define EBDA_NO_RESTRI			0x00
+#define EBDA_AVO_VGA_ADDR		0x04
+#define EBDA_AVO_VGA_ADDR_AND_ALIA	0x08
+#define EBDA_AVO_ISA_ADDR		0x0c
+
+
+/**************************************************************
+*  DEVICE TYPE DEF                                            *
+**************************************************************/
+
+#define EBDA_DEV_TYPE_MASK		0x10
+#define EBDA_PCI_DEV			0x10
+#define EBDA_NON_PCI_DEV		0x00
+
+
+/***************************************************************
+*  PRIMARY DEF DEFINITION                                      *
+***************************************************************/
+
+#define EBDA_PRI_DEF_MASK		0x20
+#define EBDA_PRI_PCI_BUS_INFO		0x20
+#define EBDA_NORM_DEV_RSRC_INFO		0x00
+
+
+//--------------------------------------------------------------
+// RIO TABLE DATA STRUCTURE
+//--------------------------------------------------------------
+
+struct rio_table_hdr {
+	u8 ver_num;
+	u8 scal_count;
+	u8 riodev_count;
+	u16 offset;
+};
+
+//-------------------------------------------------------------
+// SCALABILITY DETAIL
+//-------------------------------------------------------------
+
+struct scal_detail {
+	u8 node_id;
+	u32 cbar;
+	u8 port0_node_connect;
+	u8 port0_port_connect;
+	u8 port1_node_connect;
+	u8 port1_port_connect;
+	u8 port2_node_connect;
+	u8 port2_port_connect;
+	u8 chassis_num;
+//	struct list_head scal_detail_list;
+};
+
+//--------------------------------------------------------------
+// RIO DETAIL
+//--------------------------------------------------------------
+
+struct rio_detail {
+	u8 rio_node_id;
+	u32 bbar;
+	u8 rio_type;
+	u8 owner_id;
+	u8 port0_node_connect;
+	u8 port0_port_connect;
+	u8 port1_node_connect;
+	u8 port1_port_connect;
+	u8 first_slot_num;
+	u8 status;
+	u8 wpindex;
+	u8 chassis_num;
+	struct list_head rio_detail_list;
+};
+
+struct opt_rio {
+	u8 rio_type;
+	u8 chassis_num;
+	u8 first_slot_num;
+	u8 middle_num;
+	struct list_head opt_rio_list;
+};
+
+struct opt_rio_lo {
+	u8 rio_type;
+	u8 chassis_num;
+	u8 first_slot_num;
+	u8 middle_num;
+	u8 pack_count;
+	struct list_head opt_rio_lo_list;
+};
+
+/****************************************************************
+*  HPC DESCRIPTOR NODE                                          *
+****************************************************************/
+
+struct ebda_hpc_list {
+	u8 format;
+	u16 num_ctlrs;
+	short phys_addr;
+//      struct list_head ebda_hpc_list;
+};
+/*****************************************************************
+*   IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS           *
+*   STRUCTURE                                                    *
+*****************************************************************/
+
+struct ebda_hpc_slot {
+	u8 slot_num;
+	u32 slot_bus_num;
+	u8 ctl_index;
+	u8 slot_cap;
+};
+
+struct ebda_hpc_bus {
+	u32 bus_num;
+	u8 slots_at_33_conv;
+	u8 slots_at_66_conv;
+	u8 slots_at_66_pcix;
+	u8 slots_at_100_pcix;
+	u8 slots_at_133_pcix;
+};
+
+
+/********************************************************************
+*   THREE TYPE OF HOT PLUG CONTROLLER                                *
+********************************************************************/
+
+struct isa_ctlr_access {
+	u16 io_start;
+	u16 io_end;
+};
+
+struct pci_ctlr_access {
+	u8 bus;
+	u8 dev_fun;
+};
+
+struct wpeg_i2c_ctlr_access {
+	ulong wpegbbar;
+	u8 i2c_addr;
+};
+
+#define HPC_DEVICE_ID		0x0246
+#define HPC_SUBSYSTEM_ID	0x0247
+#define HPC_PCI_OFFSET		0x40
+/*************************************************************************
+*   RSTC DESCRIPTOR NODE                                                 *
+*************************************************************************/
+
+struct ebda_rsrc_list {
+	u8 format;
+	u16 num_entries;
+	u16 phys_addr;
+	struct ebda_rsrc_list *next;
+};
+
+
+/***************************************************************************
+*   PCI RSRC NODE                                                          *
+***************************************************************************/
+
+struct ebda_pci_rsrc {
+	u8 rsrc_type;
+	u8 bus_num;
+	u8 dev_fun;
+	u32 start_addr;
+	u32 end_addr;
+	u8 marked;	/* for NVRAM */
+	struct list_head ebda_pci_rsrc_list;
+};
+
+
+/***********************************************************
+* BUS_INFO DATE STRUCTURE                                  *
+***********************************************************/
+
+struct bus_info {
+	u8 slot_min;
+	u8 slot_max;
+	u8 slot_count;
+	u8 busno;
+	u8 controller_id;
+	u8 current_speed;
+	u8 current_bus_mode;
+	u8 index;
+	u8 slots_at_33_conv;
+	u8 slots_at_66_conv;
+	u8 slots_at_66_pcix;
+	u8 slots_at_100_pcix;
+	u8 slots_at_133_pcix;
+	struct list_head bus_info_list;
+};
+
+
+/***********************************************************
+* GLOBAL VARIABLES                                         *
+***********************************************************/
+extern struct list_head ibmphp_ebda_pci_rsrc_head;
+extern struct list_head ibmphp_slot_head;
+/***********************************************************
+* FUNCTION PROTOTYPES                                      *
+***********************************************************/
+
+void ibmphp_free_ebda_hpc_queue(void);
+int ibmphp_access_ebda(void);
+struct slot *ibmphp_get_slot_from_physical_num(u8);
+int ibmphp_get_total_hp_slots(void);
+void ibmphp_free_ibm_slot(struct slot *);
+void ibmphp_free_bus_info_queue(void);
+void ibmphp_free_ebda_pci_rsrc_queue(void);
+struct bus_info *ibmphp_find_same_bus_num(u32);
+int ibmphp_get_bus_index(u8);
+u16 ibmphp_get_total_controllers(void);
+int ibmphp_register_pci(void);
+
+/* passed parameters */
+#define MEM		0
+#define IO		1
+#define PFMEM		2
+
+/* bit masks */
+#define RESTYPE		0x03
+#define IOMASK		0x00	/* will need to take its complement */
+#define MMASK		0x01
+#define PFMASK		0x03
+#define PCIDEVMASK	0x10	/* we should always have PCI devices */
+#define PRIMARYBUSMASK	0x20
+
+/* pci specific defines */
+#define PCI_VENDOR_ID_NOTVALID		0xFFFF
+#define PCI_HEADER_TYPE_MULTIDEVICE	0x80
+#define PCI_HEADER_TYPE_MULTIBRIDGE	0x81
+
+#define LATENCY		0x64
+#define CACHE		64
+#define DEVICEENABLE	0x015F		/* CPQ has 0x0157 */
+
+#define IOBRIDGE	0x1000		/* 4k */
+#define MEMBRIDGE	0x100000	/* 1M */
+
+/* irqs */
+#define SCSI_IRQ	0x09
+#define LAN_IRQ		0x0A
+#define OTHER_IRQ	0x0B
+
+/* Data Structures */
+
+/* type is of the form x x xx xx
+ *                     | |  |  |_ 00 - I/O, 01 - Memory, 11 - PFMemory
+ *                     | |  - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid
+ *                     | |    VGA and their aliases, 11 - Avoid ISA
+ *                     | - 1 - PCI device, 0 - non pci device
+ *                     - 1 - Primary PCI Bus Information (0 if Normal device)
+ * the IO restrictions [2:3] are only for primary buses
+ */
+
+
+/* we need this struct because there could be several resource blocks
+ * allocated per primary bus in the EBDA
+ */
+struct range_node {
+	int rangeno;
+	u32 start;
+	u32 end;
+	struct range_node *next;
+};
+
+struct bus_node {
+	u8 busno;
+	int noIORanges;
+	struct range_node *rangeIO;
+	int noMemRanges;
+	struct range_node *rangeMem;
+	int noPFMemRanges;
+	struct range_node *rangePFMem;
+	int needIOUpdate;
+	int needMemUpdate;
+	int needPFMemUpdate;
+	struct resource_node *firstIO;	/* first IO resource on the Bus */
+	struct resource_node *firstMem;	/* first memory resource on the Bus */
+	struct resource_node *firstPFMem;	/* first prefetchable memory resource on the Bus */
+	struct resource_node *firstPFMemFromMem;	/* when run out of pfmem available, taking from Mem */
+	struct list_head bus_list;
+};
+
+struct resource_node {
+	int rangeno;
+	u8 busno;
+	u8 devfunc;
+	u32 start;
+	u32 end;
+	u32 len;
+	int type;		/* MEM, IO, PFMEM */
+	u8 fromMem;		/* this is to indicate that the range is from
+				 * from the Memory bucket rather than from PFMem */
+	struct resource_node *next;
+	struct resource_node *nextRange;	/* for the other mem range on bus */
+};
+
+struct res_needed {
+	u32 mem;
+	u32 pfmem;
+	u32 io;
+	u8 not_correct;		/* needed for return */
+	int devices[32];	/* for device numbers behind this bridge */
+};
+
+/* functions */
+
+int ibmphp_rsrc_init(void);
+int ibmphp_add_resource(struct resource_node *);
+int ibmphp_remove_resource(struct resource_node *);
+int ibmphp_find_resource(struct bus_node *, u32, struct resource_node **, int);
+int ibmphp_check_resource(struct resource_node *, u8);
+int ibmphp_remove_bus(struct bus_node *, u8);
+void ibmphp_free_resources(void);
+int ibmphp_add_pfmem_from_mem(struct resource_node *);
+struct bus_node *ibmphp_find_res_bus(u8);
+void ibmphp_print_test(void);	/* for debugging purposes */
+
+void ibmphp_hpc_initvars(void);
+int ibmphp_hpc_readslot(struct slot *, u8, u8 *);
+int ibmphp_hpc_writeslot(struct slot *, u8);
+void ibmphp_lock_operations(void);
+void ibmphp_unlock_operations(void);
+int ibmphp_hpc_start_poll_thread(void);
+void ibmphp_hpc_stop_poll_thread(void);
+
+//----------------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------
+// HPC return codes
+//----------------------------------------------------------------------------
+#define HPC_ERROR			0xFF
+
+//-----------------------------------------------------------------------------
+// BUS INFO
+//-----------------------------------------------------------------------------
+#define BUS_SPEED			0x30
+#define BUS_MODE			0x40
+#define BUS_MODE_PCIX			0x01
+#define BUS_MODE_PCI			0x00
+#define BUS_SPEED_2			0x20
+#define BUS_SPEED_1			0x10
+#define BUS_SPEED_33			0x00
+#define BUS_SPEED_66			0x01
+#define BUS_SPEED_100			0x02
+#define BUS_SPEED_133			0x03
+#define BUS_SPEED_66PCIX		0x04
+#define BUS_SPEED_66UNKNOWN		0x05
+#define BUS_STATUS_AVAILABLE		0x01
+#define BUS_CONTROL_AVAILABLE		0x02
+#define SLOT_LATCH_REGS_SUPPORTED	0x10
+
+#define PRGM_MODEL_REV_LEVEL		0xF0
+#define MAX_ADAPTER_NONE		0x09
+
+//----------------------------------------------------------------------------
+// HPC 'write' operations/commands
+//----------------------------------------------------------------------------
+//	Command			Code	State	Write to reg
+//					Machine	at index
+//-------------------------	----	-------	------------
+#define HPC_CTLR_ENABLEIRQ	0x00	// N	15
+#define HPC_CTLR_DISABLEIRQ	0x01	// N	15
+#define HPC_SLOT_OFF		0x02	// Y	0-14
+#define HPC_SLOT_ON		0x03	// Y	0-14
+#define HPC_SLOT_ATTNOFF	0x04	// N	0-14
+#define HPC_SLOT_ATTNON		0x05	// N	0-14
+#define HPC_CTLR_CLEARIRQ	0x06	// N	15
+#define HPC_CTLR_RESET		0x07	// Y	15
+#define HPC_CTLR_IRQSTEER	0x08	// N	15
+#define HPC_BUS_33CONVMODE	0x09	// Y	31-34
+#define HPC_BUS_66CONVMODE	0x0A	// Y	31-34
+#define HPC_BUS_66PCIXMODE	0x0B	// Y	31-34
+#define HPC_BUS_100PCIXMODE	0x0C	// Y	31-34
+#define HPC_BUS_133PCIXMODE	0x0D	// Y	31-34
+#define HPC_ALLSLOT_OFF		0x11	// Y	15
+#define HPC_ALLSLOT_ON		0x12	// Y	15
+#define HPC_SLOT_BLINKLED	0x13	// N	0-14
+
+//----------------------------------------------------------------------------
+// read commands
+//----------------------------------------------------------------------------
+#define READ_SLOTSTATUS		0x01
+#define READ_EXTSLOTSTATUS	0x02
+#define READ_BUSSTATUS		0x03
+#define READ_CTLRSTATUS		0x04
+#define READ_ALLSTAT		0x05
+#define READ_ALLSLOT		0x06
+#define READ_SLOTLATCHLOWREG	0x07
+#define READ_REVLEVEL		0x08
+#define READ_HPCOPTIONS		0x09
+//----------------------------------------------------------------------------
+// slot status
+//----------------------------------------------------------------------------
+#define HPC_SLOT_POWER		0x01
+#define HPC_SLOT_CONNECT	0x02
+#define HPC_SLOT_ATTN		0x04
+#define HPC_SLOT_PRSNT2		0x08
+#define HPC_SLOT_PRSNT1		0x10
+#define HPC_SLOT_PWRGD		0x20
+#define HPC_SLOT_BUS_SPEED	0x40
+#define HPC_SLOT_LATCH		0x80
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_POWER status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_POWER_OFF	0x00
+#define HPC_SLOT_POWER_ON	0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_CONNECT status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_CONNECTED	0x00
+#define HPC_SLOT_DISCONNECTED	0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_ATTN status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_ATTN_OFF	0x00
+#define HPC_SLOT_ATTN_ON	0x01
+#define HPC_SLOT_ATTN_BLINK	0x02
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_PRSNT status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_EMPTY		0x00
+#define HPC_SLOT_PRSNT_7	0x01
+#define HPC_SLOT_PRSNT_15	0x02
+#define HPC_SLOT_PRSNT_25	0x03
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_PWRGD status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PWRGD_FAULT_NONE	0x00
+#define HPC_SLOT_PWRGD_GOOD		0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_BUS_SPEED status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_BUS_SPEED_OK	0x00
+#define HPC_SLOT_BUS_SPEED_MISM	0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_LATCH status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_LATCH_OPEN	0x01	// NOTE : in PCI spec bit off = open
+#define HPC_SLOT_LATCH_CLOSED	0x00	// NOTE : in PCI spec bit on  = closed
+
+
+//----------------------------------------------------------------------------
+// extended slot status
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PCIX		0x01
+#define HPC_SLOT_SPEED1		0x02
+#define HPC_SLOT_SPEED2		0x04
+#define HPC_SLOT_BLINK_ATTN	0x08
+#define HPC_SLOT_RSRVD1		0x10
+#define HPC_SLOT_RSRVD2		0x20
+#define HPC_SLOT_BUS_MODE	0x40
+#define HPC_SLOT_RSRVD3		0x80
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_PCIX_CAP status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PCIX_NO	0x00
+#define HPC_SLOT_PCIX_YES	0x01
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_SPEED status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_SPEED_33	0x00
+#define HPC_SLOT_SPEED_66	0x01
+#define HPC_SLOT_SPEED_133	0x02
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_ATTN_BLINK status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_ATTN_BLINK_OFF	0x00
+#define HPC_SLOT_ATTN_BLINK_ON	0x01
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_BUS_MODE status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_BUS_MODE_OK	0x00
+#define HPC_SLOT_BUS_MODE_MISM	0x01
+
+//----------------------------------------------------------------------------
+// Controller status
+//----------------------------------------------------------------------------
+#define HPC_CTLR_WORKING	0x01
+#define HPC_CTLR_FINISHED	0x02
+#define HPC_CTLR_RESULT0	0x04
+#define HPC_CTLR_RESULT1	0x08
+#define HPC_CTLR_RESULE2	0x10
+#define HPC_CTLR_RESULT3	0x20
+#define HPC_CTLR_IRQ_ROUTG	0x40
+#define HPC_CTLR_IRQ_PENDG	0x80
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_WORKING status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_WORKING_NO	0x00
+#define HPC_CTLR_WORKING_YES	0x01
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_FINISHED status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_FINISHED_NO	0x00
+#define HPC_CTLR_FINISHED_YES	0x01
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_RESULT status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_RESULT_SUCCESS	0x00
+#define HPC_CTLR_RESULT_FAILED	0x01
+#define HPC_CTLR_RESULT_RSVD	0x02
+#define HPC_CTLR_RESULT_NORESP	0x03
+
+
+//----------------------------------------------------------------------------
+// macro for slot info
+//----------------------------------------------------------------------------
+#define SLOT_POWER(s)	((u8) ((s & HPC_SLOT_POWER) \
+	? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF))
+
+#define SLOT_CONNECT(s)	((u8) ((s & HPC_SLOT_CONNECT) \
+	? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
+
+#define SLOT_ATTN(s,es)	((u8) ((es & HPC_SLOT_BLINK_ATTN) \
+	? HPC_SLOT_ATTN_BLINK \
+	: ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
+
+#define SLOT_PRESENT(s)	((u8) ((s & HPC_SLOT_PRSNT1) \
+	? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \
+	: ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7)))
+
+#define SLOT_PWRGD(s)	((u8) ((s & HPC_SLOT_PWRGD) \
+	? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE))
+
+#define SLOT_BUS_SPEED(s)	((u8) ((s & HPC_SLOT_BUS_SPEED) \
+	? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK))
+
+#define SLOT_LATCH(s)	((u8) ((s & HPC_SLOT_LATCH) \
+	? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN))
+
+#define SLOT_PCIX(es)	((u8) ((es & HPC_SLOT_PCIX) \
+	? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO))
+
+#define SLOT_SPEED(es)	((u8) ((es & HPC_SLOT_SPEED2) \
+	? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133   \
+				: HPC_SLOT_SPEED_66)   \
+	: HPC_SLOT_SPEED_33))
+
+#define SLOT_BUS_MODE(es)	((u8) ((es & HPC_SLOT_BUS_MODE) \
+	? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK))
+
+//--------------------------------------------------------------------------
+// macro for bus info
+//---------------------------------------------------------------------------
+#define CURRENT_BUS_SPEED(s)	((u8) (s & BUS_SPEED_2) \
+	? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \
+	: ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33))
+
+#define CURRENT_BUS_MODE(s)	((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI)
+
+#define READ_BUS_STATUS(s)	((u8) (s->options & BUS_STATUS_AVAILABLE))
+
+#define READ_BUS_MODE(s)	((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20)
+
+#define SET_BUS_STATUS(s)	((u8) (s->options & BUS_CONTROL_AVAILABLE))
+
+#define READ_SLOT_LATCH(s)	((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED))
+
+//----------------------------------------------------------------------------
+// macro for controller info
+//----------------------------------------------------------------------------
+#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \
+	? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO))
+#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \
+	? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO))
+#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1)  \
+	? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \
+				: HPC_CTLR_RESULT_RSVD)  \
+	: ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \
+				: HPC_CTLR_RESULT_SUCCESS)))
+
+// command that affect the state machine of HPC
+#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF)        || \
+				  (c == HPC_SLOT_ON)         || \
+				  (c == HPC_CTLR_RESET)      || \
+				  (c == HPC_BUS_33CONVMODE)  || \
+				  (c == HPC_BUS_66CONVMODE)  || \
+				  (c == HPC_BUS_66PCIXMODE)  || \
+				  (c == HPC_BUS_100PCIXMODE) || \
+				  (c == HPC_BUS_133PCIXMODE) || \
+				  (c == HPC_ALLSLOT_OFF)     || \
+				  (c == HPC_ALLSLOT_ON))
+
+
+/* Core part of the driver */
+
+#define ENABLE		1
+#define DISABLE		0
+
+#define CARD_INFO	0x07
+#define PCIX133		0x07
+#define PCIX66		0x05
+#define PCI66		0x04
+
+extern struct pci_bus *ibmphp_pci_bus;
+
+/* Variables */
+
+struct pci_func {
+	struct pci_dev *dev;	/* from the OS */
+	u8 busno;
+	u8 device;
+	u8 function;
+	struct resource_node *io[6];
+	struct resource_node *mem[6];
+	struct resource_node *pfmem[6];
+	struct pci_func *next;
+	int devices[32];	/* for bridge config */
+	u8 irq[4];		/* for interrupt config */
+	u8 bus;			/* flag for unconfiguring, to say if PPB */
+};
+
+struct slot {
+	u8 bus;
+	u8 device;
+	u8 number;
+	u8 real_physical_slot_num;
+	u32 capabilities;
+	u8 supported_speed;
+	u8 supported_bus_mode;
+	u8 flag;		/* this is for disable slot and polling */
+	u8 ctlr_index;
+	struct hotplug_slot *hotplug_slot;
+	struct controller *ctrl;
+	struct pci_func *func;
+	u8 irq[4];
+	int bit_mode;		/* 0 = 32, 1 = 64 */
+	struct bus_info *bus_on;
+	struct list_head ibm_slot_list;
+	u8 status;
+	u8 ext_status;
+	u8 busstatus;
+};
+
+struct controller {
+	struct ebda_hpc_slot *slots;
+	struct ebda_hpc_bus *buses;
+	struct pci_dev *ctrl_dev; /* in case where controller is PCI */
+	u8 starting_slot_num;	/* starting and ending slot #'s this ctrl controls*/
+	u8 ending_slot_num;
+	u8 revision;
+	u8 options;		/* which options HPC supports */
+	u8 status;
+	u8 ctlr_id;
+	u8 slot_count;
+	u8 bus_count;
+	u8 ctlr_relative_id;
+	u32 irq;
+	union {
+		struct isa_ctlr_access isa_ctlr;
+		struct pci_ctlr_access pci_ctlr;
+		struct wpeg_i2c_ctlr_access wpeg_ctlr;
+	} u;
+	u8 ctlr_type;
+	struct list_head ebda_hpc_list;
+};
+
+/* Functions */
+
+int ibmphp_init_devno(struct slot **);	/* This function is called from EBDA, so we need it not be static */
+int ibmphp_do_disable_slot(struct slot *slot_cur);
+int ibmphp_update_slot_info(struct slot *);	/* This function is called from HPC, so we need it to not be be static */
+int ibmphp_configure_card(struct pci_func *, u8);
+int ibmphp_unconfigure_card(struct slot **, int);
+extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
+
+#endif				//__IBMPHP_H
+
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
new file mode 100644
index 0000000..1530247
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -0,0 +1,1384 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include "../pci.h"
+#include <asm/pci_x86.h>		/* for struct irq_routing_table */
+#include <asm/io_apic.h>
+#include "ibmphp.h"
+
+#define attn_on(sl)  ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON)
+#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF)
+#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED)
+#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev)
+#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt)
+
+#define DRIVER_VERSION	"0.6"
+#define DRIVER_DESC	"IBM Hot Plug PCI Controller Driver"
+
+int ibmphp_debug;
+
+static bool debug;
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
+MODULE_LICENSE ("GPL");
+MODULE_DESCRIPTION (DRIVER_DESC);
+
+struct pci_bus *ibmphp_pci_bus;
+static int max_slots;
+
+static int irqs[16];    /* PIC mode IRQs we're using so far (in case MPS
+			 * tables don't provide default info for empty slots */
+
+static int init_flag;
+
+/*
+static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8);
+
+static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
+{
+	return get_max_adapter_speed_1 (hs, value, 1);
+}
+*/
+static inline int get_cur_bus_info(struct slot **sl)
+{
+	int rc = 1;
+	struct slot *slot_cur = *sl;
+
+	debug("options = %x\n", slot_cur->ctrl->options);
+	debug("revision = %x\n", slot_cur->ctrl->revision);
+
+	if (READ_BUS_STATUS(slot_cur->ctrl))
+		rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL);
+
+	if (rc)
+		return rc;
+
+	slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus);
+	if (READ_BUS_MODE(slot_cur->ctrl))
+		slot_cur->bus_on->current_bus_mode =
+				CURRENT_BUS_MODE(slot_cur->busstatus);
+	else
+		slot_cur->bus_on->current_bus_mode = 0xFF;
+
+	debug("busstatus = %x, bus_speed = %x, bus_mode = %x\n",
+			slot_cur->busstatus,
+			slot_cur->bus_on->current_speed,
+			slot_cur->bus_on->current_bus_mode);
+
+	*sl = slot_cur;
+	return 0;
+}
+
+static inline int slot_update(struct slot **sl)
+{
+	int rc;
+	rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);
+	if (rc)
+		return rc;
+	if (!init_flag)
+		rc = get_cur_bus_info(sl);
+	return rc;
+}
+
+static int __init get_max_slots (void)
+{
+	struct slot *slot_cur;
+	struct list_head *tmp;
+	u8 slot_count = 0;
+
+	list_for_each(tmp, &ibmphp_slot_head) {
+		slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+		/* sometimes the hot-pluggable slots start with 4 (not always from 1) */
+		slot_count = max(slot_count, slot_cur->number);
+	}
+	return slot_count;
+}
+
+/* This routine will put the correct slot->device information per slot.  It's
+ * called from initialization of the slot structures. It will also assign
+ * interrupt numbers per each slot.
+ * Parameters: struct slot
+ * Returns 0 or errors
+ */
+int ibmphp_init_devno(struct slot **cur_slot)
+{
+	struct irq_routing_table *rtable;
+	int len;
+	int loop;
+	int i;
+
+	rtable = pcibios_get_irq_routing_table();
+	if (!rtable) {
+		err("no BIOS routing table...\n");
+		return -ENOMEM;
+	}
+
+	len = (rtable->size - sizeof(struct irq_routing_table)) /
+			sizeof(struct irq_info);
+
+	if (!len) {
+		kfree(rtable);
+		return -1;
+	}
+	for (loop = 0; loop < len; loop++) {
+		if ((*cur_slot)->number == rtable->slots[loop].slot &&
+		    (*cur_slot)->bus == rtable->slots[loop].bus) {
+			(*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn);
+			for (i = 0; i < 4; i++)
+				(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector((int) (*cur_slot)->bus,
+						(int) (*cur_slot)->device, i);
+
+			debug("(*cur_slot)->irq[0] = %x\n",
+					(*cur_slot)->irq[0]);
+			debug("(*cur_slot)->irq[1] = %x\n",
+					(*cur_slot)->irq[1]);
+			debug("(*cur_slot)->irq[2] = %x\n",
+					(*cur_slot)->irq[2]);
+			debug("(*cur_slot)->irq[3] = %x\n",
+					(*cur_slot)->irq[3]);
+
+			debug("rtable->exclusive_irqs = %x\n",
+					rtable->exclusive_irqs);
+			debug("rtable->slots[loop].irq[0].bitmap = %x\n",
+					rtable->slots[loop].irq[0].bitmap);
+			debug("rtable->slots[loop].irq[1].bitmap = %x\n",
+					rtable->slots[loop].irq[1].bitmap);
+			debug("rtable->slots[loop].irq[2].bitmap = %x\n",
+					rtable->slots[loop].irq[2].bitmap);
+			debug("rtable->slots[loop].irq[3].bitmap = %x\n",
+					rtable->slots[loop].irq[3].bitmap);
+
+			debug("rtable->slots[loop].irq[0].link = %x\n",
+					rtable->slots[loop].irq[0].link);
+			debug("rtable->slots[loop].irq[1].link = %x\n",
+					rtable->slots[loop].irq[1].link);
+			debug("rtable->slots[loop].irq[2].link = %x\n",
+					rtable->slots[loop].irq[2].link);
+			debug("rtable->slots[loop].irq[3].link = %x\n",
+					rtable->slots[loop].irq[3].link);
+			debug("end of init_devno\n");
+			kfree(rtable);
+			return 0;
+		}
+	}
+
+	kfree(rtable);
+	return -1;
+}
+
+static inline int power_on(struct slot *slot_cur)
+{
+	u8 cmd = HPC_SLOT_ON;
+	int retval;
+
+	retval = ibmphp_hpc_writeslot(slot_cur, cmd);
+	if (retval) {
+		err("power on failed\n");
+		return retval;
+	}
+	if (CTLR_RESULT(slot_cur->ctrl->status)) {
+		err("command not completed successfully in power_on\n");
+		return -EIO;
+	}
+	msleep(3000);	/* For ServeRAID cards, and some 66 PCI */
+	return 0;
+}
+
+static inline int power_off(struct slot *slot_cur)
+{
+	u8 cmd = HPC_SLOT_OFF;
+	int retval;
+
+	retval = ibmphp_hpc_writeslot(slot_cur, cmd);
+	if (retval) {
+		err("power off failed\n");
+		return retval;
+	}
+	if (CTLR_RESULT(slot_cur->ctrl->status)) {
+		err("command not completed successfully in power_off\n");
+		retval = -EIO;
+	}
+	return retval;
+}
+
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+	int rc = 0;
+	struct slot *pslot;
+	u8 cmd = 0x00;     /* avoid compiler warning */
+
+	debug("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n",
+			(ulong) hotplug_slot, value);
+	ibmphp_lock_operations();
+
+
+	if (hotplug_slot) {
+		switch (value) {
+		case HPC_SLOT_ATTN_OFF:
+			cmd = HPC_SLOT_ATTNOFF;
+			break;
+		case HPC_SLOT_ATTN_ON:
+			cmd = HPC_SLOT_ATTNON;
+			break;
+		case HPC_SLOT_ATTN_BLINK:
+			cmd = HPC_SLOT_BLINKLED;
+			break;
+		default:
+			rc = -ENODEV;
+			err("set_attention_status - Error : invalid input [%x]\n",
+					value);
+			break;
+		}
+		if (rc == 0) {
+			pslot = hotplug_slot->private;
+			if (pslot)
+				rc = ibmphp_hpc_writeslot(pslot, cmd);
+			else
+				rc = -ENODEV;
+		}
+	} else
+		rc = -ENODEV;
+
+	ibmphp_unlock_operations();
+
+	debug("set_attention_status - Exit rc[%d]\n", rc);
+	return rc;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int rc = -ENODEV;
+	struct slot *pslot;
+	struct slot myslot;
+
+	debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+					(ulong) hotplug_slot, (ulong) value);
+
+	ibmphp_lock_operations();
+	if (hotplug_slot) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			memcpy(&myslot, pslot, sizeof(struct slot));
+			rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+						&(myslot.status));
+			if (!rc)
+				rc = ibmphp_hpc_readslot(pslot,
+						READ_EXTSLOTSTATUS,
+						&(myslot.ext_status));
+			if (!rc)
+				*value = SLOT_ATTN(myslot.status,
+						myslot.ext_status);
+		}
+	}
+
+	ibmphp_unlock_operations();
+	debug("get_attention_status - Exit rc[%d] value[%x]\n", rc, *value);
+	return rc;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int rc = -ENODEV;
+	struct slot *pslot;
+	struct slot myslot;
+
+	debug("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+					(ulong) hotplug_slot, (ulong) value);
+	ibmphp_lock_operations();
+	if (hotplug_slot) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			memcpy(&myslot, pslot, sizeof(struct slot));
+			rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+						&(myslot.status));
+			if (!rc)
+				*value = SLOT_LATCH(myslot.status);
+		}
+	}
+
+	ibmphp_unlock_operations();
+	debug("get_latch_status - Exit rc[%d] rc[%x] value[%x]\n",
+			rc, rc, *value);
+	return rc;
+}
+
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int rc = -ENODEV;
+	struct slot *pslot;
+	struct slot myslot;
+
+	debug("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+					(ulong) hotplug_slot, (ulong) value);
+	ibmphp_lock_operations();
+	if (hotplug_slot) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			memcpy(&myslot, pslot, sizeof(struct slot));
+			rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+						&(myslot.status));
+			if (!rc)
+				*value = SLOT_PWRGD(myslot.status);
+		}
+	}
+
+	ibmphp_unlock_operations();
+	debug("get_power_status - Exit rc[%d] rc[%x] value[%x]\n",
+			rc, rc, *value);
+	return rc;
+}
+
+static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int rc = -ENODEV;
+	struct slot *pslot;
+	u8 present;
+	struct slot myslot;
+
+	debug("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+					(ulong) hotplug_slot, (ulong) value);
+	ibmphp_lock_operations();
+	if (hotplug_slot) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			memcpy(&myslot, pslot, sizeof(struct slot));
+			rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+						&(myslot.status));
+			if (!rc) {
+				present = SLOT_PRESENT(myslot.status);
+				if (present == HPC_SLOT_EMPTY)
+					*value = 0;
+				else
+					*value = 1;
+			}
+		}
+	}
+
+	ibmphp_unlock_operations();
+	debug("get_adapter_present - Exit rc[%d] value[%x]\n", rc, *value);
+	return rc;
+}
+
+static int get_max_bus_speed(struct slot *slot)
+{
+	int rc;
+	u8 mode = 0;
+	enum pci_bus_speed speed;
+	struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;
+
+	debug("%s - Entry slot[%p]\n", __func__, slot);
+
+	ibmphp_lock_operations();
+	mode = slot->supported_bus_mode;
+	speed = slot->supported_speed;
+	ibmphp_unlock_operations();
+
+	switch (speed) {
+	case BUS_SPEED_33:
+		break;
+	case BUS_SPEED_66:
+		if (mode == BUS_MODE_PCIX)
+			speed += 0x01;
+		break;
+	case BUS_SPEED_100:
+	case BUS_SPEED_133:
+		speed += 0x01;
+		break;
+	default:
+		/* Note (will need to change): there would be soon 256, 512 also */
+		rc = -ENODEV;
+	}
+
+	if (!rc)
+		bus->max_bus_speed = speed;
+
+	debug("%s - Exit rc[%d] speed[%x]\n", __func__, rc, speed);
+	return rc;
+}
+
+/*
+static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 *value, u8 flag)
+{
+	int rc = -ENODEV;
+	struct slot *pslot;
+	struct slot myslot;
+
+	debug("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+						(ulong)hotplug_slot, (ulong) value);
+
+	if (flag)
+		ibmphp_lock_operations();
+
+	if (hotplug_slot && value) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			memcpy(&myslot, pslot, sizeof(struct slot));
+			rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+						&(myslot.status));
+
+			if (!(SLOT_LATCH (myslot.status)) &&
+					(SLOT_PRESENT (myslot.status))) {
+				rc = ibmphp_hpc_readslot(pslot,
+						READ_EXTSLOTSTATUS,
+						&(myslot.ext_status));
+				if (!rc)
+					*value = SLOT_SPEED(myslot.ext_status);
+			} else
+				*value = MAX_ADAPTER_NONE;
+                }
+	}
+
+	if (flag)
+		ibmphp_unlock_operations();
+
+	debug("get_max_adapter_speed_1 - Exit rc[%d] value[%x]\n", rc, *value);
+	return rc;
+}
+
+static int get_bus_name(struct hotplug_slot *hotplug_slot, char *value)
+{
+	int rc = -ENODEV;
+	struct slot *pslot = NULL;
+
+	debug("get_bus_name - Entry hotplug_slot[%lx]\n", (ulong)hotplug_slot);
+
+	ibmphp_lock_operations();
+
+	if (hotplug_slot) {
+		pslot = hotplug_slot->private;
+		if (pslot) {
+			rc = 0;
+			snprintf(value, 100, "Bus %x", pslot->bus);
+		}
+	} else
+		rc = -ENODEV;
+
+	ibmphp_unlock_operations();
+	debug("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value);
+	return rc;
+}
+*/
+
+/****************************************************************************
+ * This routine will initialize the ops data structure used in the validate
+ * function. It will also power off empty slots that are powered on since BIOS
+ * leaves those on, albeit disconnected
+ ****************************************************************************/
+static int __init init_ops(void)
+{
+	struct slot *slot_cur;
+	struct list_head *tmp;
+	int retval;
+	int rc;
+
+	list_for_each(tmp, &ibmphp_slot_head) {
+		slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+
+		if (!slot_cur)
+			return -ENODEV;
+
+		debug("BEFORE GETTING SLOT STATUS, slot # %x\n",
+							slot_cur->number);
+		if (slot_cur->ctrl->revision == 0xFF)
+			if (get_ctrl_revision(slot_cur,
+						&slot_cur->ctrl->revision))
+				return -1;
+
+		if (slot_cur->bus_on->current_speed == 0xFF)
+			if (get_cur_bus_info(&slot_cur))
+				return -1;
+		get_max_bus_speed(slot_cur);
+
+		if (slot_cur->ctrl->options == 0xFF)
+			if (get_hpc_options(slot_cur, &slot_cur->ctrl->options))
+				return -1;
+
+		retval = slot_update(&slot_cur);
+		if (retval)
+			return retval;
+
+		debug("status = %x\n", slot_cur->status);
+		debug("ext_status = %x\n", slot_cur->ext_status);
+		debug("SLOT_POWER = %x\n", SLOT_POWER(slot_cur->status));
+		debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status));
+		debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status));
+
+		if ((SLOT_PWRGD(slot_cur->status)) &&
+		    !(SLOT_PRESENT(slot_cur->status)) &&
+		    !(SLOT_LATCH(slot_cur->status))) {
+			debug("BEFORE POWER OFF COMMAND\n");
+				rc = power_off(slot_cur);
+				if (rc)
+					return rc;
+
+	/*		retval = slot_update(&slot_cur);
+	 *		if (retval)
+	 *			return retval;
+	 *		ibmphp_update_slot_info(slot_cur);
+	 */
+		}
+	}
+	init_flag = 0;
+	return 0;
+}
+
+/* This operation will check whether the slot is within the bounds and
+ * the operation is valid to perform on that slot
+ * Parameters: slot, operation
+ * Returns: 0 or error codes
+ */
+static int validate(struct slot *slot_cur, int opn)
+{
+	int number;
+	int retval;
+
+	if (!slot_cur)
+		return -ENODEV;
+	number = slot_cur->number;
+	if ((number > max_slots) || (number < 0))
+		return -EBADSLT;
+	debug("slot_number in validate is %d\n", slot_cur->number);
+
+	retval = slot_update(&slot_cur);
+	if (retval)
+		return retval;
+
+	switch (opn) {
+		case ENABLE:
+			if (!(SLOT_PWRGD(slot_cur->status)) &&
+			     (SLOT_PRESENT(slot_cur->status)) &&
+			     !(SLOT_LATCH(slot_cur->status)))
+				return 0;
+			break;
+		case DISABLE:
+			if ((SLOT_PWRGD(slot_cur->status)) &&
+			    (SLOT_PRESENT(slot_cur->status)) &&
+			    !(SLOT_LATCH(slot_cur->status)))
+				return 0;
+			break;
+		default:
+			break;
+	}
+	err("validate failed....\n");
+	return -EINVAL;
+}
+
+/****************************************************************************
+ * This routine is for updating the data structures in the hotplug core
+ * Parameters: struct slot
+ * Returns: 0 or error
+ ****************************************************************************/
+int ibmphp_update_slot_info(struct slot *slot_cur)
+{
+	struct hotplug_slot_info *info;
+	struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus;
+	int rc;
+	u8 bus_speed;
+	u8 mode;
+
+	info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+	if (!info) {
+		err("out of system memory\n");
+		return -ENOMEM;
+	}
+
+	info->power_status = SLOT_PWRGD(slot_cur->status);
+	info->attention_status = SLOT_ATTN(slot_cur->status,
+						slot_cur->ext_status);
+	info->latch_status = SLOT_LATCH(slot_cur->status);
+        if (!SLOT_PRESENT(slot_cur->status)) {
+                info->adapter_status = 0;
+/*		info->max_adapter_speed_status = MAX_ADAPTER_NONE; */
+	} else {
+                info->adapter_status = 1;
+/*		get_max_adapter_speed_1(slot_cur->hotplug_slot,
+					&info->max_adapter_speed_status, 0); */
+	}
+
+	bus_speed = slot_cur->bus_on->current_speed;
+	mode = slot_cur->bus_on->current_bus_mode;
+
+	switch (bus_speed) {
+		case BUS_SPEED_33:
+			break;
+		case BUS_SPEED_66:
+			if (mode == BUS_MODE_PCIX)
+				bus_speed += 0x01;
+			else if (mode == BUS_MODE_PCI)
+				;
+			else
+				bus_speed = PCI_SPEED_UNKNOWN;
+			break;
+		case BUS_SPEED_100:
+		case BUS_SPEED_133:
+			bus_speed += 0x01;
+			break;
+		default:
+			bus_speed = PCI_SPEED_UNKNOWN;
+	}
+
+	bus->cur_bus_speed = bus_speed;
+	// To do: bus_names
+
+	rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
+	kfree(info);
+	return rc;
+}
+
+
+/******************************************************************************
+ * This function will return the pci_func, given bus and devfunc, or NULL.  It
+ * is called from visit routines
+ ******************************************************************************/
+
+static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
+{
+	struct pci_func *func_cur;
+	struct slot *slot_cur;
+	struct list_head *tmp;
+	list_for_each(tmp, &ibmphp_slot_head) {
+		slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+		if (slot_cur->func) {
+			func_cur = slot_cur->func;
+			while (func_cur) {
+				if ((func_cur->busno == busno) &&
+						(func_cur->device == device) &&
+						(func_cur->function == function))
+					return func_cur;
+				func_cur = func_cur->next;
+			}
+		}
+	}
+	return NULL;
+}
+
+/*************************************************************
+ * This routine frees up memory used by struct slot, including
+ * the pointers to pci_func, bus, hotplug_slot, controller,
+ * and deregistering from the hotplug core
+ *************************************************************/
+static void free_slots(void)
+{
+	struct slot *slot_cur;
+	struct list_head *tmp;
+	struct list_head *next;
+
+	debug("%s -- enter\n", __func__);
+
+	list_for_each_safe(tmp, next, &ibmphp_slot_head) {
+		slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+		pci_hp_deregister(slot_cur->hotplug_slot);
+	}
+	debug("%s -- exit\n", __func__);
+}
+
+static void ibm_unconfigure_device(struct pci_func *func)
+{
+	struct pci_dev *temp;
+	u8 j;
+
+	debug("inside %s\n", __func__);
+	debug("func->device = %x, func->function = %x\n",
+					func->device, func->function);
+	debug("func->device << 3 | 0x0  = %x\n", func->device << 3 | 0x0);
+
+	pci_lock_rescan_remove();
+
+	for (j = 0; j < 0x08; j++) {
+		temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);
+		if (temp) {
+			pci_stop_and_remove_bus_device(temp);
+			pci_dev_put(temp);
+		}
+	}
+
+	pci_dev_put(func->dev);
+
+	pci_unlock_rescan_remove();
+}
+
+/*
+ * The following function is to fix kernel bug regarding
+ * getting bus entries, here we manually add those primary
+ * bus entries to kernel bus structure whenever apply
+ */
+static u8 bus_structure_fixup(u8 busno)
+{
+	struct pci_bus *bus, *b;
+	struct pci_dev *dev;
+	u16 l;
+
+	if (pci_find_bus(0, busno) || !(ibmphp_find_same_bus_num(busno)))
+		return 1;
+
+	bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus) {
+		err("%s - out of memory\n", __func__);
+		return 1;
+	}
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		kfree(bus);
+		err("%s - out of memory\n", __func__);
+		return 1;
+	}
+
+	bus->number = busno;
+	bus->ops = ibmphp_pci_bus->ops;
+	dev->bus = bus;
+	for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) {
+		if (!pci_read_config_word(dev, PCI_VENDOR_ID, &l) &&
+					(l != 0x0000) && (l != 0xffff)) {
+			debug("%s - Inside bus_structure_fixup()\n",
+							__func__);
+			b = pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL);
+			if (!b)
+				continue;
+
+			pci_bus_add_devices(b);
+			break;
+		}
+	}
+
+	kfree(dev);
+	kfree(bus);
+
+	return 0;
+}
+
+static int ibm_configure_device(struct pci_func *func)
+{
+	struct pci_bus *child;
+	int num;
+	int flag = 0;	/* this is to make sure we don't double scan the bus,
+					for bridged devices primarily */
+
+	pci_lock_rescan_remove();
+
+	if (!(bus_structure_fixup(func->busno)))
+		flag = 1;
+	if (func->dev == NULL)
+		func->dev = pci_get_bus_and_slot(func->busno,
+				PCI_DEVFN(func->device, func->function));
+
+	if (func->dev == NULL) {
+		struct pci_bus *bus = pci_find_bus(0, func->busno);
+		if (!bus)
+			goto out;
+
+		num = pci_scan_slot(bus,
+				PCI_DEVFN(func->device, func->function));
+		if (num)
+			pci_bus_add_devices(bus);
+
+		func->dev = pci_get_bus_and_slot(func->busno,
+				PCI_DEVFN(func->device, func->function));
+		if (func->dev == NULL) {
+			err("ERROR... : pci_dev still NULL\n");
+			goto out;
+		}
+	}
+	if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
+		pci_hp_add_bridge(func->dev);
+		child = func->dev->subordinate;
+		if (child)
+			pci_bus_add_devices(child);
+	}
+
+ out:
+	pci_unlock_rescan_remove();
+	return 0;
+}
+
+/*******************************************************
+ * Returns whether the bus is empty or not
+ *******************************************************/
+static int is_bus_empty(struct slot *slot_cur)
+{
+	int rc;
+	struct slot *tmp_slot;
+	u8 i = slot_cur->bus_on->slot_min;
+
+	while (i <= slot_cur->bus_on->slot_max) {
+		if (i == slot_cur->number) {
+			i++;
+			continue;
+		}
+		tmp_slot = ibmphp_get_slot_from_physical_num(i);
+		if (!tmp_slot)
+			return 0;
+		rc = slot_update(&tmp_slot);
+		if (rc)
+			return 0;
+		if (SLOT_PRESENT(tmp_slot->status) &&
+					SLOT_PWRGD(tmp_slot->status))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+/***********************************************************
+ * If the HPC permits and the bus currently empty, tries to set the
+ * bus speed and mode at the maximum card and bus capability
+ * Parameters: slot
+ * Returns: bus is set (0) or error code
+ ***********************************************************/
+static int set_bus(struct slot *slot_cur)
+{
+	int rc;
+	u8 speed;
+	u8 cmd = 0x0;
+	int retval;
+	static struct pci_device_id ciobx[] = {
+		{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) },
+	        { },
+	};
+
+	debug("%s - entry slot # %d\n", __func__, slot_cur->number);
+	if (SET_BUS_STATUS(slot_cur->ctrl) && is_bus_empty(slot_cur)) {
+		rc = slot_update(&slot_cur);
+		if (rc)
+			return rc;
+		speed = SLOT_SPEED(slot_cur->ext_status);
+		debug("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed);
+		switch (speed) {
+		case HPC_SLOT_SPEED_33:
+			cmd = HPC_BUS_33CONVMODE;
+			break;
+		case HPC_SLOT_SPEED_66:
+			if (SLOT_PCIX(slot_cur->ext_status)) {
+				if ((slot_cur->supported_speed >= BUS_SPEED_66) &&
+						(slot_cur->supported_bus_mode == BUS_MODE_PCIX))
+					cmd = HPC_BUS_66PCIXMODE;
+				else if (!SLOT_BUS_MODE(slot_cur->ext_status))
+					/* if max slot/bus capability is 66 pci
+					and there's no bus mode mismatch, then
+					the adapter supports 66 pci */
+					cmd = HPC_BUS_66CONVMODE;
+				else
+					cmd = HPC_BUS_33CONVMODE;
+			} else {
+				if (slot_cur->supported_speed >= BUS_SPEED_66)
+					cmd = HPC_BUS_66CONVMODE;
+				else
+					cmd = HPC_BUS_33CONVMODE;
+			}
+			break;
+		case HPC_SLOT_SPEED_133:
+			switch (slot_cur->supported_speed) {
+			case BUS_SPEED_33:
+				cmd = HPC_BUS_33CONVMODE;
+				break;
+			case BUS_SPEED_66:
+				if (slot_cur->supported_bus_mode == BUS_MODE_PCIX)
+					cmd = HPC_BUS_66PCIXMODE;
+				else
+					cmd = HPC_BUS_66CONVMODE;
+				break;
+			case BUS_SPEED_100:
+				cmd = HPC_BUS_100PCIXMODE;
+				break;
+			case BUS_SPEED_133:
+				/* This is to take care of the bug in CIOBX chip */
+				if (pci_dev_present(ciobx))
+					ibmphp_hpc_writeslot(slot_cur,
+							HPC_BUS_100PCIXMODE);
+				cmd = HPC_BUS_133PCIXMODE;
+				break;
+			default:
+				err("Wrong bus speed\n");
+				return -ENODEV;
+			}
+			break;
+		default:
+			err("wrong slot speed\n");
+			return -ENODEV;
+		}
+		debug("setting bus speed for slot %d, cmd %x\n",
+						slot_cur->number, cmd);
+		retval = ibmphp_hpc_writeslot(slot_cur, cmd);
+		if (retval) {
+			err("setting bus speed failed\n");
+			return retval;
+		}
+		if (CTLR_RESULT(slot_cur->ctrl->status)) {
+			err("command not completed successfully in set_bus\n");
+			return -EIO;
+		}
+	}
+	/* This is for x440, once Brandon fixes the firmware,
+	will not need this delay */
+	msleep(1000);
+	debug("%s -Exit\n", __func__);
+	return 0;
+}
+
+/* This routine checks the bus limitations that the slot is on from the BIOS.
+ * This is used in deciding whether or not to power up the slot.
+ * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on
+ * same bus)
+ * Parameters: slot
+ * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus
+ */
+static int check_limitations(struct slot *slot_cur)
+{
+	u8 i;
+	struct slot *tmp_slot;
+	u8 count = 0;
+	u8 limitation = 0;
+
+	for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) {
+		tmp_slot = ibmphp_get_slot_from_physical_num(i);
+		if (!tmp_slot)
+			return -ENODEV;
+		if ((SLOT_PWRGD(tmp_slot->status)) &&
+					!(SLOT_CONNECT(tmp_slot->status)))
+			count++;
+	}
+	get_cur_bus_info(&slot_cur);
+	switch (slot_cur->bus_on->current_speed) {
+	case BUS_SPEED_33:
+		limitation = slot_cur->bus_on->slots_at_33_conv;
+		break;
+	case BUS_SPEED_66:
+		if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX)
+			limitation = slot_cur->bus_on->slots_at_66_pcix;
+		else
+			limitation = slot_cur->bus_on->slots_at_66_conv;
+		break;
+	case BUS_SPEED_100:
+		limitation = slot_cur->bus_on->slots_at_100_pcix;
+		break;
+	case BUS_SPEED_133:
+		limitation = slot_cur->bus_on->slots_at_133_pcix;
+		break;
+	}
+
+	if ((count + 1) > limitation)
+		return -EINVAL;
+	return 0;
+}
+
+static inline void print_card_capability(struct slot *slot_cur)
+{
+	info("capability of the card is ");
+	if ((slot_cur->ext_status & CARD_INFO) == PCIX133)
+		info("   133 MHz PCI-X\n");
+	else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
+		info("    66 MHz PCI-X\n");
+	else if ((slot_cur->ext_status & CARD_INFO) == PCI66)
+		info("    66 MHz PCI\n");
+	else
+		info("    33 MHz PCI\n");
+
+}
+
+/* This routine will power on the slot, configure the device(s) and find the
+ * drivers for them.
+ * Parameters: hotplug_slot
+ * Returns: 0 or failure codes
+ */
+static int enable_slot(struct hotplug_slot *hs)
+{
+	int rc, i, rcpr;
+	struct slot *slot_cur;
+	u8 function;
+	struct pci_func *tmp_func;
+
+	ibmphp_lock_operations();
+
+	debug("ENABLING SLOT........\n");
+	slot_cur = hs->private;
+
+	rc = validate(slot_cur, ENABLE);
+	if (rc) {
+		err("validate function failed\n");
+		goto error_nopower;
+	}
+
+	attn_LED_blink(slot_cur);
+
+	rc = set_bus(slot_cur);
+	if (rc) {
+		err("was not able to set the bus\n");
+		goto error_nopower;
+	}
+
+	/*-----------------debugging------------------------------*/
+	get_cur_bus_info(&slot_cur);
+	debug("the current bus speed right after set_bus = %x\n",
+					slot_cur->bus_on->current_speed);
+	/*----------------------------------------------------------*/
+
+	rc = check_limitations(slot_cur);
+	if (rc) {
+		err("Adding this card exceeds the limitations of this bus.\n");
+		err("(i.e., >1 133MHz cards running on same bus, or >2 66 PCI cards running on same bus.\n");
+		err("Try hot-adding into another bus\n");
+		rc = -EINVAL;
+		goto error_nopower;
+	}
+
+	rc = power_on(slot_cur);
+
+	if (rc) {
+		err("something wrong when powering up... please see below for details\n");
+		/* need to turn off before on, otherwise, blinking overwrites */
+		attn_off(slot_cur);
+		attn_on(slot_cur);
+		if (slot_update(&slot_cur)) {
+			attn_off(slot_cur);
+			attn_on(slot_cur);
+			rc = -ENODEV;
+			goto exit;
+		}
+		/* Check to see the error of why it failed */
+		if ((SLOT_POWER(slot_cur->status)) &&
+					!(SLOT_PWRGD(slot_cur->status)))
+			err("power fault occurred trying to power up\n");
+		else if (SLOT_BUS_SPEED(slot_cur->status)) {
+			err("bus speed mismatch occurred.  please check current bus speed and card capability\n");
+			print_card_capability(slot_cur);
+		} else if (SLOT_BUS_MODE(slot_cur->ext_status)) {
+			err("bus mode mismatch occurred.  please check current bus mode and card capability\n");
+			print_card_capability(slot_cur);
+		}
+		ibmphp_update_slot_info(slot_cur);
+		goto exit;
+	}
+	debug("after power_on\n");
+	/*-----------------------debugging---------------------------*/
+	get_cur_bus_info(&slot_cur);
+	debug("the current bus speed right after power_on = %x\n",
+					slot_cur->bus_on->current_speed);
+	/*----------------------------------------------------------*/
+
+	rc = slot_update(&slot_cur);
+	if (rc)
+		goto error_power;
+
+	rc = -EINVAL;
+	if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) {
+		err("power fault occurred trying to power up...\n");
+		goto error_power;
+	}
+	if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) {
+		err("bus speed mismatch occurred.  please check current bus speed and card capability\n");
+		print_card_capability(slot_cur);
+		goto error_power;
+	}
+	/* Don't think this case will happen after above checks...
+	 * but just in case, for paranoia sake */
+	if (!(SLOT_POWER(slot_cur->status))) {
+		err("power on failed...\n");
+		goto error_power;
+	}
+
+	slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
+	if (!slot_cur->func) {
+		/* We cannot do update_slot_info here, since no memory for
+		 * kmalloc n.e.ways, and update_slot_info allocates some */
+		err("out of system memory\n");
+		rc = -ENOMEM;
+		goto error_power;
+	}
+	slot_cur->func->busno = slot_cur->bus;
+	slot_cur->func->device = slot_cur->device;
+	for (i = 0; i < 4; i++)
+		slot_cur->func->irq[i] = slot_cur->irq[i];
+
+	debug("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n",
+					slot_cur->bus, slot_cur->device);
+
+	if (ibmphp_configure_card(slot_cur->func, slot_cur->number)) {
+		err("configure_card was unsuccessful...\n");
+		/* true because don't need to actually deallocate resources,
+		 * just remove references */
+		ibmphp_unconfigure_card(&slot_cur, 1);
+		debug("after unconfigure_card\n");
+		slot_cur->func = NULL;
+		rc = -ENOMEM;
+		goto error_power;
+	}
+
+	function = 0x00;
+	do {
+		tmp_func = ibm_slot_find(slot_cur->bus, slot_cur->func->device,
+							function++);
+		if (tmp_func && !(tmp_func->dev))
+			ibm_configure_device(tmp_func);
+	} while (tmp_func);
+
+	attn_off(slot_cur);
+	if (slot_update(&slot_cur)) {
+		rc = -EFAULT;
+		goto exit;
+	}
+	ibmphp_print_test();
+	rc = ibmphp_update_slot_info(slot_cur);
+exit:
+	ibmphp_unlock_operations();
+	return rc;
+
+error_nopower:
+	attn_off(slot_cur);	/* need to turn off if was blinking b4 */
+	attn_on(slot_cur);
+error_cont:
+	rcpr = slot_update(&slot_cur);
+	if (rcpr) {
+		rc = rcpr;
+		goto exit;
+	}
+	ibmphp_update_slot_info(slot_cur);
+	goto exit;
+
+error_power:
+	attn_off(slot_cur);	/* need to turn off if was blinking b4 */
+	attn_on(slot_cur);
+	rcpr = power_off(slot_cur);
+	if (rcpr) {
+		rc = rcpr;
+		goto exit;
+	}
+	goto error_cont;
+}
+
+/**************************************************************
+* HOT REMOVING ADAPTER CARD                                   *
+* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE                *
+* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE         *
+          DISABLE POWER ,                                    *
+**************************************************************/
+static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int rc;
+
+	ibmphp_lock_operations();
+	rc = ibmphp_do_disable_slot(slot);
+	ibmphp_unlock_operations();
+	return rc;
+}
+
+int ibmphp_do_disable_slot(struct slot *slot_cur)
+{
+	int rc;
+	u8 flag;
+
+	debug("DISABLING SLOT...\n");
+
+	if ((slot_cur == NULL) || (slot_cur->ctrl == NULL))
+		return -ENODEV;
+
+	flag = slot_cur->flag;
+	slot_cur->flag = 1;
+
+	if (flag == 1) {
+		rc = validate(slot_cur, DISABLE);
+			/* checking if powered off already & valid slot # */
+		if (rc)
+			goto error;
+	}
+	attn_LED_blink(slot_cur);
+
+	if (slot_cur->func == NULL) {
+		/* We need this for functions that were there on bootup */
+		slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
+		if (!slot_cur->func) {
+			err("out of system memory\n");
+			rc = -ENOMEM;
+			goto error;
+		}
+		slot_cur->func->busno = slot_cur->bus;
+		slot_cur->func->device = slot_cur->device;
+	}
+
+	ibm_unconfigure_device(slot_cur->func);
+
+	/*
+	 * If we got here from latch suddenly opening on operating card or
+	 * a power fault, there's no power to the card, so cannot
+	 * read from it to determine what resources it occupied.  This operation
+	 * is forbidden anyhow.  The best we can do is remove it from kernel
+	 * lists at least */
+
+	if (!flag) {
+		attn_off(slot_cur);
+		return 0;
+	}
+
+	rc = ibmphp_unconfigure_card(&slot_cur, 0);
+	slot_cur->func = NULL;
+	debug("in disable_slot. after unconfigure_card\n");
+	if (rc) {
+		err("could not unconfigure card.\n");
+		goto error;
+	}
+
+	rc = ibmphp_hpc_writeslot(slot_cur, HPC_SLOT_OFF);
+	if (rc)
+		goto error;
+
+	attn_off(slot_cur);
+	rc = slot_update(&slot_cur);
+	if (rc)
+		goto exit;
+
+	rc = ibmphp_update_slot_info(slot_cur);
+	ibmphp_print_test();
+exit:
+	return rc;
+
+error:
+	/*  Need to turn off if was blinking b4 */
+	attn_off(slot_cur);
+	attn_on(slot_cur);
+	if (slot_update(&slot_cur)) {
+		rc = -EFAULT;
+		goto exit;
+	}
+	if (flag)
+		ibmphp_update_slot_info(slot_cur);
+	goto exit;
+}
+
+struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
+	.set_attention_status =		set_attention_status,
+	.enable_slot =			enable_slot,
+	.disable_slot =			ibmphp_disable_slot,
+	.hardware_test =		NULL,
+	.get_power_status =		get_power_status,
+	.get_attention_status =		get_attention_status,
+	.get_latch_status =		get_latch_status,
+	.get_adapter_status =		get_adapter_present,
+/*	.get_max_adapter_speed =	get_max_adapter_speed,
+	.get_bus_name_status =		get_bus_name,
+*/
+};
+
+static void ibmphp_unload(void)
+{
+	free_slots();
+	debug("after slots\n");
+	ibmphp_free_resources();
+	debug("after resources\n");
+	ibmphp_free_bus_info_queue();
+	debug("after bus info\n");
+	ibmphp_free_ebda_hpc_queue();
+	debug("after ebda hpc\n");
+	ibmphp_free_ebda_pci_rsrc_queue();
+	debug("after ebda pci rsrc\n");
+	kfree(ibmphp_pci_bus);
+}
+
+static int __init ibmphp_init(void)
+{
+	struct pci_bus *bus;
+	int i = 0;
+	int rc = 0;
+
+	init_flag = 1;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+	ibmphp_pci_bus = kmalloc(sizeof(*ibmphp_pci_bus), GFP_KERNEL);
+	if (!ibmphp_pci_bus) {
+		err("out of memory\n");
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	bus = pci_find_bus(0, 0);
+	if (!bus) {
+		err("Can't find the root pci bus, can not continue\n");
+		rc = -ENODEV;
+		goto error;
+	}
+	memcpy(ibmphp_pci_bus, bus, sizeof(*ibmphp_pci_bus));
+
+	ibmphp_debug = debug;
+
+	ibmphp_hpc_initvars();
+
+	for (i = 0; i < 16; i++)
+		irqs[i] = 0;
+
+	rc = ibmphp_access_ebda();
+	if (rc)
+		goto error;
+	debug("after ibmphp_access_ebda()\n");
+
+	rc = ibmphp_rsrc_init();
+	if (rc)
+		goto error;
+	debug("AFTER Resource & EBDA INITIALIZATIONS\n");
+
+	max_slots = get_max_slots();
+
+	rc = ibmphp_register_pci();
+	if (rc)
+		goto error;
+
+	if (init_ops()) {
+		rc = -ENODEV;
+		goto error;
+	}
+
+	ibmphp_print_test();
+	rc = ibmphp_hpc_start_poll_thread();
+	if (rc)
+		goto error;
+
+exit:
+	return rc;
+
+error:
+	ibmphp_unload();
+	goto exit;
+}
+
+static void __exit ibmphp_exit(void)
+{
+	ibmphp_hpc_stop_poll_thread();
+	debug("after polling\n");
+	ibmphp_unload();
+	debug("done\n");
+}
+
+module_init(ibmphp_init);
+module_exit(ibmphp_exit);
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
new file mode 100644
index 0000000..d9b197d
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -0,0 +1,1211 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Tong Yu, IBM Corporation
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include "ibmphp.h"
+
+/*
+ * POST builds data blocks(in this data block definition, a char-1
+ * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended
+ * BIOS Data Area which describe the configuration of the hot-plug
+ * controllers and resources used by the PCI Hot-Plug devices.
+ *
+ * This file walks EBDA, maps data block from physical addr,
+ * reconstruct linked lists about all system resource(MEM, PFM, IO)
+ * already assigned by POST, as well as linked lists about hot plug
+ * controllers (ctlr#, slot#, bus&slot features...)
+ */
+
+/* Global lists */
+LIST_HEAD (ibmphp_ebda_pci_rsrc_head);
+LIST_HEAD (ibmphp_slot_head);
+
+/* Local variables */
+static struct ebda_hpc_list *hpc_list_ptr;
+static struct ebda_rsrc_list *rsrc_list_ptr;
+static struct rio_table_hdr *rio_table_ptr = NULL;
+static LIST_HEAD (ebda_hpc_head);
+static LIST_HEAD (bus_info_head);
+static LIST_HEAD (rio_vg_head);
+static LIST_HEAD (rio_lo_head);
+static LIST_HEAD (opt_vg_head);
+static LIST_HEAD (opt_lo_head);
+static void __iomem *io_mem;
+
+/* Local functions */
+static int ebda_rsrc_controller (void);
+static int ebda_rsrc_rsrc (void);
+static int ebda_rio_table (void);
+
+static struct ebda_hpc_list * __init alloc_ebda_hpc_list (void)
+{
+	return kzalloc(sizeof(struct ebda_hpc_list), GFP_KERNEL);
+}
+
+static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count)
+{
+	struct controller *controller;
+	struct ebda_hpc_slot *slots;
+	struct ebda_hpc_bus *buses;
+
+	controller = kzalloc(sizeof(struct controller), GFP_KERNEL);
+	if (!controller)
+		goto error;
+
+	slots = kcalloc(slot_count, sizeof(struct ebda_hpc_slot), GFP_KERNEL);
+	if (!slots)
+		goto error_contr;
+	controller->slots = slots;
+
+	buses = kcalloc(bus_count, sizeof(struct ebda_hpc_bus), GFP_KERNEL);
+	if (!buses)
+		goto error_slots;
+	controller->buses = buses;
+
+	return controller;
+error_slots:
+	kfree(controller->slots);
+error_contr:
+	kfree(controller);
+error:
+	return NULL;
+}
+
+static void free_ebda_hpc (struct controller *controller)
+{
+	kfree (controller->slots);
+	kfree (controller->buses);
+	kfree (controller);
+}
+
+static struct ebda_rsrc_list * __init alloc_ebda_rsrc_list (void)
+{
+	return kzalloc(sizeof(struct ebda_rsrc_list), GFP_KERNEL);
+}
+
+static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void)
+{
+	return kzalloc(sizeof(struct ebda_pci_rsrc), GFP_KERNEL);
+}
+
+static void __init print_bus_info (void)
+{
+	struct bus_info *ptr;
+
+	list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+		debug ("%s - slot_min = %x\n", __func__, ptr->slot_min);
+		debug ("%s - slot_max = %x\n", __func__, ptr->slot_max);
+		debug ("%s - slot_count = %x\n", __func__, ptr->slot_count);
+		debug ("%s - bus# = %x\n", __func__, ptr->busno);
+		debug ("%s - current_speed = %x\n", __func__, ptr->current_speed);
+		debug ("%s - controller_id = %x\n", __func__, ptr->controller_id);
+
+		debug ("%s - slots_at_33_conv = %x\n", __func__, ptr->slots_at_33_conv);
+		debug ("%s - slots_at_66_conv = %x\n", __func__, ptr->slots_at_66_conv);
+		debug ("%s - slots_at_66_pcix = %x\n", __func__, ptr->slots_at_66_pcix);
+		debug ("%s - slots_at_100_pcix = %x\n", __func__, ptr->slots_at_100_pcix);
+		debug ("%s - slots_at_133_pcix = %x\n", __func__, ptr->slots_at_133_pcix);
+
+	}
+}
+
+static void print_lo_info (void)
+{
+	struct rio_detail *ptr;
+	debug ("print_lo_info ----\n");
+	list_for_each_entry(ptr, &rio_lo_head, rio_detail_list) {
+		debug ("%s - rio_node_id = %x\n", __func__, ptr->rio_node_id);
+		debug ("%s - rio_type = %x\n", __func__, ptr->rio_type);
+		debug ("%s - owner_id = %x\n", __func__, ptr->owner_id);
+		debug ("%s - first_slot_num = %x\n", __func__, ptr->first_slot_num);
+		debug ("%s - wpindex = %x\n", __func__, ptr->wpindex);
+		debug ("%s - chassis_num = %x\n", __func__, ptr->chassis_num);
+
+	}
+}
+
+static void print_vg_info (void)
+{
+	struct rio_detail *ptr;
+	debug ("%s ---\n", __func__);
+	list_for_each_entry(ptr, &rio_vg_head, rio_detail_list) {
+		debug ("%s - rio_node_id = %x\n", __func__, ptr->rio_node_id);
+		debug ("%s - rio_type = %x\n", __func__, ptr->rio_type);
+		debug ("%s - owner_id = %x\n", __func__, ptr->owner_id);
+		debug ("%s - first_slot_num = %x\n", __func__, ptr->first_slot_num);
+		debug ("%s - wpindex = %x\n", __func__, ptr->wpindex);
+		debug ("%s - chassis_num = %x\n", __func__, ptr->chassis_num);
+
+	}
+}
+
+static void __init print_ebda_pci_rsrc (void)
+{
+	struct ebda_pci_rsrc *ptr;
+
+	list_for_each_entry(ptr, &ibmphp_ebda_pci_rsrc_head, ebda_pci_rsrc_list) {
+		debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
+			__func__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
+	}
+}
+
+static void __init print_ibm_slot (void)
+{
+	struct slot *ptr;
+
+	list_for_each_entry(ptr, &ibmphp_slot_head, ibm_slot_list) {
+		debug ("%s - slot_number: %x\n", __func__, ptr->number);
+	}
+}
+
+static void __init print_opt_vg (void)
+{
+	struct opt_rio *ptr;
+	debug ("%s ---\n", __func__);
+	list_for_each_entry(ptr, &opt_vg_head, opt_rio_list) {
+		debug ("%s - rio_type %x\n", __func__, ptr->rio_type);
+		debug ("%s - chassis_num: %x\n", __func__, ptr->chassis_num);
+		debug ("%s - first_slot_num: %x\n", __func__, ptr->first_slot_num);
+		debug ("%s - middle_num: %x\n", __func__, ptr->middle_num);
+	}
+}
+
+static void __init print_ebda_hpc (void)
+{
+	struct controller *hpc_ptr;
+	u16 index;
+
+	list_for_each_entry(hpc_ptr, &ebda_hpc_head, ebda_hpc_list) {
+		for (index = 0; index < hpc_ptr->slot_count; index++) {
+			debug ("%s - physical slot#: %x\n", __func__, hpc_ptr->slots[index].slot_num);
+			debug ("%s - pci bus# of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_bus_num);
+			debug ("%s - index into ctlr addr: %x\n", __func__, hpc_ptr->slots[index].ctl_index);
+			debug ("%s - cap of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_cap);
+		}
+
+		for (index = 0; index < hpc_ptr->bus_count; index++)
+			debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __func__, hpc_ptr->buses[index].bus_num);
+
+		debug ("%s - type of hpc: %x\n", __func__, hpc_ptr->ctlr_type);
+		switch (hpc_ptr->ctlr_type) {
+		case 1:
+			debug ("%s - bus: %x\n", __func__, hpc_ptr->u.pci_ctlr.bus);
+			debug ("%s - dev_fun: %x\n", __func__, hpc_ptr->u.pci_ctlr.dev_fun);
+			debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
+			break;
+
+		case 0:
+			debug ("%s - io_start: %x\n", __func__, hpc_ptr->u.isa_ctlr.io_start);
+			debug ("%s - io_end: %x\n", __func__, hpc_ptr->u.isa_ctlr.io_end);
+			debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
+			break;
+
+		case 2:
+		case 4:
+			debug ("%s - wpegbbar: %lx\n", __func__, hpc_ptr->u.wpeg_ctlr.wpegbbar);
+			debug ("%s - i2c_addr: %x\n", __func__, hpc_ptr->u.wpeg_ctlr.i2c_addr);
+			debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
+			break;
+		}
+	}
+}
+
+int __init ibmphp_access_ebda (void)
+{
+	u8 format, num_ctlrs, rio_complete, hs_complete, ebda_sz;
+	u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base;
+	int rc = 0;
+
+
+	rio_complete = 0;
+	hs_complete = 0;
+
+	io_mem = ioremap ((0x40 << 4) + 0x0e, 2);
+	if (!io_mem )
+		return -ENOMEM;
+	ebda_seg = readw (io_mem);
+	iounmap (io_mem);
+	debug ("returned ebda segment: %x\n", ebda_seg);
+
+	io_mem = ioremap(ebda_seg<<4, 1);
+	if (!io_mem)
+		return -ENOMEM;
+	ebda_sz = readb(io_mem);
+	iounmap(io_mem);
+	debug("ebda size: %d(KiB)\n", ebda_sz);
+	if (ebda_sz == 0)
+		return -ENOMEM;
+
+	io_mem = ioremap(ebda_seg<<4, (ebda_sz * 1024));
+	if (!io_mem )
+		return -ENOMEM;
+	next_offset = 0x180;
+
+	for (;;) {
+		offset = next_offset;
+
+		/* Make sure what we read is still in the mapped section */
+		if (WARN(offset > (ebda_sz * 1024 - 4),
+			 "ibmphp_ebda: next read is beyond ebda_sz\n"))
+			break;
+
+		next_offset = readw (io_mem + offset);	/* offset of next blk */
+
+		offset += 2;
+		if (next_offset == 0)	/* 0 indicate it's last blk */
+			break;
+		blk_id = readw (io_mem + offset);	/* this blk id */
+
+		offset += 2;
+		/* check if it is hot swap block or rio block */
+		if (blk_id != 0x4853 && blk_id != 0x4752)
+			continue;
+		/* found hs table */
+		if (blk_id == 0x4853) {
+			debug ("now enter hot swap block---\n");
+			debug ("hot blk id: %x\n", blk_id);
+			format = readb (io_mem + offset);
+
+			offset += 1;
+			if (format != 4)
+				goto error_nodev;
+			debug ("hot blk format: %x\n", format);
+			/* hot swap sub blk */
+			base = offset;
+
+			sub_addr = base;
+			re = readw (io_mem + sub_addr);	/* next sub blk */
+
+			sub_addr += 2;
+			rc_id = readw (io_mem + sub_addr);	/* sub blk id */
+
+			sub_addr += 2;
+			if (rc_id != 0x5243)
+				goto error_nodev;
+			/* rc sub blk signature  */
+			num_ctlrs = readb (io_mem + sub_addr);
+
+			sub_addr += 1;
+			hpc_list_ptr = alloc_ebda_hpc_list ();
+			if (!hpc_list_ptr) {
+				rc = -ENOMEM;
+				goto out;
+			}
+			hpc_list_ptr->format = format;
+			hpc_list_ptr->num_ctlrs = num_ctlrs;
+			hpc_list_ptr->phys_addr = sub_addr;	/*  offset of RSRC_CONTROLLER blk */
+			debug ("info about hpc descriptor---\n");
+			debug ("hot blk format: %x\n", format);
+			debug ("num of controller: %x\n", num_ctlrs);
+			debug ("offset of hpc data structure entries: %x\n ", sub_addr);
+
+			sub_addr = base + re;	/* re sub blk */
+			/* FIXME: rc is never used/checked */
+			rc = readw (io_mem + sub_addr);	/* next sub blk */
+
+			sub_addr += 2;
+			re_id = readw (io_mem + sub_addr);	/* sub blk id */
+
+			sub_addr += 2;
+			if (re_id != 0x5245)
+				goto error_nodev;
+
+			/* signature of re */
+			num_entries = readw (io_mem + sub_addr);
+
+			sub_addr += 2;	/* offset of RSRC_ENTRIES blk */
+			rsrc_list_ptr = alloc_ebda_rsrc_list ();
+			if (!rsrc_list_ptr ) {
+				rc = -ENOMEM;
+				goto out;
+			}
+			rsrc_list_ptr->format = format;
+			rsrc_list_ptr->num_entries = num_entries;
+			rsrc_list_ptr->phys_addr = sub_addr;
+
+			debug ("info about rsrc descriptor---\n");
+			debug ("format: %x\n", format);
+			debug ("num of rsrc: %x\n", num_entries);
+			debug ("offset of rsrc data structure entries: %x\n ", sub_addr);
+
+			hs_complete = 1;
+		} else {
+		/* found rio table, blk_id == 0x4752 */
+			debug ("now enter io table ---\n");
+			debug ("rio blk id: %x\n", blk_id);
+
+			rio_table_ptr = kzalloc(sizeof(struct rio_table_hdr), GFP_KERNEL);
+			if (!rio_table_ptr) {
+				rc = -ENOMEM;
+				goto out;
+			}
+			rio_table_ptr->ver_num = readb (io_mem + offset);
+			rio_table_ptr->scal_count = readb (io_mem + offset + 1);
+			rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
+			rio_table_ptr->offset = offset +3 ;
+
+			debug("info about rio table hdr ---\n");
+			debug("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ",
+				rio_table_ptr->ver_num, rio_table_ptr->scal_count,
+				rio_table_ptr->riodev_count, rio_table_ptr->offset);
+
+			rio_complete = 1;
+		}
+	}
+
+	if (!hs_complete && !rio_complete)
+		goto error_nodev;
+
+	if (rio_table_ptr) {
+		if (rio_complete && rio_table_ptr->ver_num == 3) {
+			rc = ebda_rio_table ();
+			if (rc)
+				goto out;
+		}
+	}
+	rc = ebda_rsrc_controller ();
+	if (rc)
+		goto out;
+
+	rc = ebda_rsrc_rsrc ();
+	goto out;
+error_nodev:
+	rc = -ENODEV;
+out:
+	iounmap (io_mem);
+	return rc;
+}
+
+/*
+ * map info of scalability details and rio details from physical address
+ */
+static int __init ebda_rio_table (void)
+{
+	u16 offset;
+	u8 i;
+	struct rio_detail *rio_detail_ptr;
+
+	offset = rio_table_ptr->offset;
+	offset += 12 * rio_table_ptr->scal_count;
+
+	// we do concern about rio details
+	for (i = 0; i < rio_table_ptr->riodev_count; i++) {
+		rio_detail_ptr = kzalloc(sizeof(struct rio_detail), GFP_KERNEL);
+		if (!rio_detail_ptr)
+			return -ENOMEM;
+		rio_detail_ptr->rio_node_id = readb (io_mem + offset);
+		rio_detail_ptr->bbar = readl (io_mem + offset + 1);
+		rio_detail_ptr->rio_type = readb (io_mem + offset + 5);
+		rio_detail_ptr->owner_id = readb (io_mem + offset + 6);
+		rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7);
+		rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8);
+		rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9);
+		rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10);
+		rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11);
+		rio_detail_ptr->status = readb (io_mem + offset + 12);
+		rio_detail_ptr->wpindex = readb (io_mem + offset + 13);
+		rio_detail_ptr->chassis_num = readb (io_mem + offset + 14);
+//		debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status);
+		//create linked list of chassis
+		if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5)
+			list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head);
+		//create linked list of expansion box
+		else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7)
+			list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head);
+		else
+			// not in my concern
+			kfree (rio_detail_ptr);
+		offset += 15;
+	}
+	print_lo_info ();
+	print_vg_info ();
+	return 0;
+}
+
+/*
+ * reorganizing linked list of chassis
+ */
+static struct opt_rio *search_opt_vg (u8 chassis_num)
+{
+	struct opt_rio *ptr;
+	list_for_each_entry(ptr, &opt_vg_head, opt_rio_list) {
+		if (ptr->chassis_num == chassis_num)
+			return ptr;
+	}
+	return NULL;
+}
+
+static int __init combine_wpg_for_chassis (void)
+{
+	struct opt_rio *opt_rio_ptr = NULL;
+	struct rio_detail *rio_detail_ptr = NULL;
+
+	list_for_each_entry(rio_detail_ptr, &rio_vg_head, rio_detail_list) {
+		opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num);
+		if (!opt_rio_ptr) {
+			opt_rio_ptr = kzalloc(sizeof(struct opt_rio), GFP_KERNEL);
+			if (!opt_rio_ptr)
+				return -ENOMEM;
+			opt_rio_ptr->rio_type = rio_detail_ptr->rio_type;
+			opt_rio_ptr->chassis_num = rio_detail_ptr->chassis_num;
+			opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
+			opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num;
+			list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head);
+		} else {
+			opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
+			opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num);
+		}
+	}
+	print_opt_vg ();
+	return 0;
+}
+
+/*
+ * reorganizing linked list of expansion box
+ */
+static struct opt_rio_lo *search_opt_lo (u8 chassis_num)
+{
+	struct opt_rio_lo *ptr;
+	list_for_each_entry(ptr, &opt_lo_head, opt_rio_lo_list) {
+		if (ptr->chassis_num == chassis_num)
+			return ptr;
+	}
+	return NULL;
+}
+
+static int combine_wpg_for_expansion (void)
+{
+	struct opt_rio_lo *opt_rio_lo_ptr = NULL;
+	struct rio_detail *rio_detail_ptr = NULL;
+
+	list_for_each_entry(rio_detail_ptr, &rio_lo_head, rio_detail_list) {
+		opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num);
+		if (!opt_rio_lo_ptr) {
+			opt_rio_lo_ptr = kzalloc(sizeof(struct opt_rio_lo), GFP_KERNEL);
+			if (!opt_rio_lo_ptr)
+				return -ENOMEM;
+			opt_rio_lo_ptr->rio_type = rio_detail_ptr->rio_type;
+			opt_rio_lo_ptr->chassis_num = rio_detail_ptr->chassis_num;
+			opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
+			opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num;
+			opt_rio_lo_ptr->pack_count = 1;
+
+			list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head);
+		} else {
+			opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
+			opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num);
+			opt_rio_lo_ptr->pack_count = 2;
+		}
+	}
+	return 0;
+}
+
+
+/* Since we don't know the max slot number per each chassis, hence go
+ * through the list of all chassis to find out the range
+ * Arguments: slot_num, 1st slot number of the chassis we think we are on,
+ * var (0 = chassis, 1 = expansion box)
+ */
+static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
+{
+	struct opt_rio *opt_vg_ptr = NULL;
+	struct opt_rio_lo *opt_lo_ptr = NULL;
+	int rc = 0;
+
+	if (!var) {
+		list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
+			if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) {
+				rc = -ENODEV;
+				break;
+			}
+		}
+	} else {
+		list_for_each_entry(opt_lo_ptr, &opt_lo_head, opt_rio_lo_list) {
+			if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) {
+				rc = -ENODEV;
+				break;
+			}
+		}
+	}
+	return rc;
+}
+
+static struct opt_rio_lo *find_rxe_num (u8 slot_num)
+{
+	struct opt_rio_lo *opt_lo_ptr;
+
+	list_for_each_entry(opt_lo_ptr, &opt_lo_head, opt_rio_lo_list) {
+		//check to see if this slot_num belongs to expansion box
+		if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1)))
+			return opt_lo_ptr;
+	}
+	return NULL;
+}
+
+static struct opt_rio *find_chassis_num (u8 slot_num)
+{
+	struct opt_rio *opt_vg_ptr;
+
+	list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
+		//check to see if this slot_num belongs to chassis
+		if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0)))
+			return opt_vg_ptr;
+	}
+	return NULL;
+}
+
+/* This routine will find out how many slots are in the chassis, so that
+ * the slot numbers for rxe100 would start from 1, and not from 7, or 6 etc
+ */
+static u8 calculate_first_slot (u8 slot_num)
+{
+	u8 first_slot = 1;
+	struct slot *slot_cur;
+
+	list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
+		if (slot_cur->ctrl) {
+			if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num))
+				first_slot = slot_cur->ctrl->ending_slot_num;
+		}
+	}
+	return first_slot + 1;
+
+}
+
+#define SLOT_NAME_SIZE 30
+
+static char *create_file_name (struct slot *slot_cur)
+{
+	struct opt_rio *opt_vg_ptr = NULL;
+	struct opt_rio_lo *opt_lo_ptr = NULL;
+	static char str[SLOT_NAME_SIZE];
+	int which = 0; /* rxe = 1, chassis = 0 */
+	u8 number = 1; /* either chassis or rxe # */
+	u8 first_slot = 1;
+	u8 slot_num;
+	u8 flag = 0;
+
+	if (!slot_cur) {
+		err ("Structure passed is empty\n");
+		return NULL;
+	}
+
+	slot_num = slot_cur->number;
+
+	memset (str, 0, sizeof(str));
+
+	if (rio_table_ptr) {
+		if (rio_table_ptr->ver_num == 3) {
+			opt_vg_ptr = find_chassis_num (slot_num);
+			opt_lo_ptr = find_rxe_num (slot_num);
+		}
+	}
+	if (opt_vg_ptr) {
+		if (opt_lo_ptr) {
+			if ((slot_num - opt_vg_ptr->first_slot_num) > (slot_num - opt_lo_ptr->first_slot_num)) {
+				number = opt_lo_ptr->chassis_num;
+				first_slot = opt_lo_ptr->first_slot_num;
+				which = 1; /* it is RXE */
+			} else {
+				first_slot = opt_vg_ptr->first_slot_num;
+				number = opt_vg_ptr->chassis_num;
+				which = 0;
+			}
+		} else {
+			first_slot = opt_vg_ptr->first_slot_num;
+			number = opt_vg_ptr->chassis_num;
+			which = 0;
+		}
+		++flag;
+	} else if (opt_lo_ptr) {
+		number = opt_lo_ptr->chassis_num;
+		first_slot = opt_lo_ptr->first_slot_num;
+		which = 1;
+		++flag;
+	} else if (rio_table_ptr) {
+		if (rio_table_ptr->ver_num == 3) {
+			/* if both NULL and we DO have correct RIO table in BIOS */
+			return NULL;
+		}
+	}
+	if (!flag) {
+		if (slot_cur->ctrl->ctlr_type == 4) {
+			first_slot = calculate_first_slot (slot_num);
+			which = 1;
+		} else {
+			which = 0;
+		}
+	}
+
+	sprintf(str, "%s%dslot%d",
+		which == 0 ? "chassis" : "rxe",
+		number, slot_num - first_slot + 1);
+	return str;
+}
+
+static int fillslotinfo(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot;
+	int rc = 0;
+
+	if (!hotplug_slot || !hotplug_slot->private)
+		return -EINVAL;
+
+	slot = hotplug_slot->private;
+	rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL);
+	if (rc)
+		return rc;
+
+	// power - enabled:1  not:0
+	hotplug_slot->info->power_status = SLOT_POWER(slot->status);
+
+	// attention - off:0, on:1, blinking:2
+	hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status);
+
+	// latch - open:1 closed:0
+	hotplug_slot->info->latch_status = SLOT_LATCH(slot->status);
+
+	// pci board - present:1 not:0
+	if (SLOT_PRESENT (slot->status))
+		hotplug_slot->info->adapter_status = 1;
+	else
+		hotplug_slot->info->adapter_status = 0;
+/*
+	if (slot->bus_on->supported_bus_mode
+		&& (slot->bus_on->supported_speed == BUS_SPEED_66))
+		hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
+	else
+		hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed;
+*/
+
+	return rc;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot;
+
+	if (!hotplug_slot || !hotplug_slot->private)
+		return;
+
+	slot = hotplug_slot->private;
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	slot->ctrl = NULL;
+	slot->bus_on = NULL;
+
+	/* we don't want to actually remove the resources, since free_resources will do just that */
+	ibmphp_unconfigure_card(&slot, -1);
+
+	kfree (slot);
+}
+
+static struct pci_driver ibmphp_driver;
+
+/*
+ * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of
+ * each hpc from physical address to a list of hot plug controllers based on
+ * hpc descriptors.
+ */
+static int __init ebda_rsrc_controller (void)
+{
+	u16 addr, addr_slot, addr_bus;
+	u8 ctlr_id, temp, bus_index;
+	u16 ctlr, slot, bus;
+	u16 slot_num, bus_num, index;
+	struct hotplug_slot *hp_slot_ptr;
+	struct controller *hpc_ptr;
+	struct ebda_hpc_bus *bus_ptr;
+	struct ebda_hpc_slot *slot_ptr;
+	struct bus_info *bus_info_ptr1, *bus_info_ptr2;
+	int rc;
+	struct slot *tmp_slot;
+	char name[SLOT_NAME_SIZE];
+
+	addr = hpc_list_ptr->phys_addr;
+	for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) {
+		bus_index = 1;
+		ctlr_id = readb (io_mem + addr);
+		addr += 1;
+		slot_num = readb (io_mem + addr);
+
+		addr += 1;
+		addr_slot = addr;	/* offset of slot structure */
+		addr += (slot_num * 4);
+
+		bus_num = readb (io_mem + addr);
+
+		addr += 1;
+		addr_bus = addr;	/* offset of bus */
+		addr += (bus_num * 9);	/* offset of ctlr_type */
+		temp = readb (io_mem + addr);
+
+		addr += 1;
+		/* init hpc structure */
+		hpc_ptr = alloc_ebda_hpc (slot_num, bus_num);
+		if (!hpc_ptr ) {
+			rc = -ENOMEM;
+			goto error_no_hpc;
+		}
+		hpc_ptr->ctlr_id = ctlr_id;
+		hpc_ptr->ctlr_relative_id = ctlr;
+		hpc_ptr->slot_count = slot_num;
+		hpc_ptr->bus_count = bus_num;
+		debug ("now enter ctlr data structure ---\n");
+		debug ("ctlr id: %x\n", ctlr_id);
+		debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id);
+		debug ("count of slots controlled by this ctlr: %x\n", slot_num);
+		debug ("count of buses controlled by this ctlr: %x\n", bus_num);
+
+		/* init slot structure, fetch slot, bus, cap... */
+		slot_ptr = hpc_ptr->slots;
+		for (slot = 0; slot < slot_num; slot++) {
+			slot_ptr->slot_num = readb (io_mem + addr_slot);
+			slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num);
+			slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num);
+			slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num);
+
+			// create bus_info lined list --- if only one slot per bus: slot_min = slot_max
+
+			bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num);
+			if (!bus_info_ptr2) {
+				bus_info_ptr1 = kzalloc(sizeof(struct bus_info), GFP_KERNEL);
+				if (!bus_info_ptr1) {
+					rc = -ENOMEM;
+					goto error_no_hp_slot;
+				}
+				bus_info_ptr1->slot_min = slot_ptr->slot_num;
+				bus_info_ptr1->slot_max = slot_ptr->slot_num;
+				bus_info_ptr1->slot_count += 1;
+				bus_info_ptr1->busno = slot_ptr->slot_bus_num;
+				bus_info_ptr1->index = bus_index++;
+				bus_info_ptr1->current_speed = 0xff;
+				bus_info_ptr1->current_bus_mode = 0xff;
+
+				bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
+
+				list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head);
+
+			} else {
+				bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num);
+				bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num);
+				bus_info_ptr2->slot_count += 1;
+
+			}
+
+			// end of creating the bus_info linked list
+
+			slot_ptr++;
+			addr_slot += 1;
+		}
+
+		/* init bus structure */
+		bus_ptr = hpc_ptr->buses;
+		for (bus = 0; bus < bus_num; bus++) {
+			bus_ptr->bus_num = readb (io_mem + addr_bus + bus);
+			bus_ptr->slots_at_33_conv = readb (io_mem + addr_bus + bus_num + 8 * bus);
+			bus_ptr->slots_at_66_conv = readb (io_mem + addr_bus + bus_num + 8 * bus + 1);
+
+			bus_ptr->slots_at_66_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 2);
+
+			bus_ptr->slots_at_100_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 3);
+
+			bus_ptr->slots_at_133_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 4);
+
+			bus_info_ptr2 = ibmphp_find_same_bus_num (bus_ptr->bus_num);
+			if (bus_info_ptr2) {
+				bus_info_ptr2->slots_at_33_conv = bus_ptr->slots_at_33_conv;
+				bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv;
+				bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix;
+				bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix;
+				bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix;
+			}
+			bus_ptr++;
+		}
+
+		hpc_ptr->ctlr_type = temp;
+
+		switch (hpc_ptr->ctlr_type) {
+			case 1:
+				hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr);
+				hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1);
+				hpc_ptr->irq = readb (io_mem + addr + 2);
+				addr += 3;
+				debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n",
+					hpc_ptr->u.pci_ctlr.bus,
+					hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq);
+				break;
+
+			case 0:
+				hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr);
+				hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2);
+				if (!request_region (hpc_ptr->u.isa_ctlr.io_start,
+						     (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1),
+						     "ibmphp")) {
+					rc = -ENODEV;
+					goto error_no_hp_slot;
+				}
+				hpc_ptr->irq = readb (io_mem + addr + 4);
+				addr += 5;
+				break;
+
+			case 2:
+			case 4:
+				hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr);
+				hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4);
+				hpc_ptr->irq = readb (io_mem + addr + 5);
+				addr += 6;
+				break;
+			default:
+				rc = -ENODEV;
+				goto error_no_hp_slot;
+		}
+
+		//reorganize chassis' linked list
+		combine_wpg_for_chassis ();
+		combine_wpg_for_expansion ();
+		hpc_ptr->revision = 0xff;
+		hpc_ptr->options = 0xff;
+		hpc_ptr->starting_slot_num = hpc_ptr->slots[0].slot_num;
+		hpc_ptr->ending_slot_num = hpc_ptr->slots[slot_num-1].slot_num;
+
+		// register slots with hpc core as well as create linked list of ibm slot
+		for (index = 0; index < hpc_ptr->slot_count; index++) {
+
+			hp_slot_ptr = kzalloc(sizeof(*hp_slot_ptr), GFP_KERNEL);
+			if (!hp_slot_ptr) {
+				rc = -ENOMEM;
+				goto error_no_hp_slot;
+			}
+
+			hp_slot_ptr->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+			if (!hp_slot_ptr->info) {
+				rc = -ENOMEM;
+				goto error_no_hp_info;
+			}
+
+			tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL);
+			if (!tmp_slot) {
+				rc = -ENOMEM;
+				goto error_no_slot;
+			}
+
+			tmp_slot->flag = 1;
+
+			tmp_slot->capabilities = hpc_ptr->slots[index].slot_cap;
+			if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX)
+				tmp_slot->supported_speed =  3;
+			else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX)
+				tmp_slot->supported_speed =  2;
+			else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX)
+				tmp_slot->supported_speed =  1;
+
+			if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP)
+				tmp_slot->supported_bus_mode = 1;
+			else
+				tmp_slot->supported_bus_mode = 0;
+
+
+			tmp_slot->bus = hpc_ptr->slots[index].slot_bus_num;
+
+			bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num);
+			if (!bus_info_ptr1) {
+				kfree(tmp_slot);
+				rc = -ENODEV;
+				goto error;
+			}
+			tmp_slot->bus_on = bus_info_ptr1;
+			bus_info_ptr1 = NULL;
+			tmp_slot->ctrl = hpc_ptr;
+
+			tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index;
+			tmp_slot->number = hpc_ptr->slots[index].slot_num;
+			tmp_slot->hotplug_slot = hp_slot_ptr;
+
+			hp_slot_ptr->private = tmp_slot;
+			hp_slot_ptr->release = release_slot;
+
+			rc = fillslotinfo(hp_slot_ptr);
+			if (rc)
+				goto error;
+
+			rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private);
+			if (rc)
+				goto error;
+			hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
+
+			// end of registering ibm slot with hotplug core
+
+			list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head);
+		}
+
+		print_bus_info ();
+		list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head );
+
+	}			/* each hpc  */
+
+	list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) {
+		snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot));
+		pci_hp_register(tmp_slot->hotplug_slot,
+			pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name);
+	}
+
+	print_ebda_hpc ();
+	print_ibm_slot ();
+	return 0;
+
+error:
+	kfree (hp_slot_ptr->private);
+error_no_slot:
+	kfree (hp_slot_ptr->info);
+error_no_hp_info:
+	kfree (hp_slot_ptr);
+error_no_hp_slot:
+	free_ebda_hpc (hpc_ptr);
+error_no_hpc:
+	iounmap (io_mem);
+	return rc;
+}
+
+/*
+ * map info (bus, devfun, start addr, end addr..) of i/o, memory,
+ * pfm from the physical addr to a list of resource.
+ */
+static int __init ebda_rsrc_rsrc (void)
+{
+	u16 addr;
+	short rsrc;
+	u8 type, rsrc_type;
+	struct ebda_pci_rsrc *rsrc_ptr;
+
+	addr = rsrc_list_ptr->phys_addr;
+	debug ("now entering rsrc land\n");
+	debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr);
+
+	for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) {
+		type = readb (io_mem + addr);
+
+		addr += 1;
+		rsrc_type = type & EBDA_RSRC_TYPE_MASK;
+
+		if (rsrc_type == EBDA_IO_RSRC_TYPE) {
+			rsrc_ptr = alloc_ebda_pci_rsrc ();
+			if (!rsrc_ptr) {
+				iounmap (io_mem);
+				return -ENOMEM;
+			}
+			rsrc_ptr->rsrc_type = type;
+
+			rsrc_ptr->bus_num = readb (io_mem + addr);
+			rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
+			rsrc_ptr->start_addr = readw (io_mem + addr + 2);
+			rsrc_ptr->end_addr = readw (io_mem + addr + 4);
+			addr += 6;
+
+			debug ("rsrc from io type ----\n");
+			debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
+				rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
+
+			list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
+		}
+
+		if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) {
+			rsrc_ptr = alloc_ebda_pci_rsrc ();
+			if (!rsrc_ptr ) {
+				iounmap (io_mem);
+				return -ENOMEM;
+			}
+			rsrc_ptr->rsrc_type = type;
+
+			rsrc_ptr->bus_num = readb (io_mem + addr);
+			rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
+			rsrc_ptr->start_addr = readl (io_mem + addr + 2);
+			rsrc_ptr->end_addr = readl (io_mem + addr + 6);
+			addr += 10;
+
+			debug ("rsrc from mem or pfm ---\n");
+			debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
+				rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
+
+			list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
+		}
+	}
+	kfree (rsrc_list_ptr);
+	rsrc_list_ptr = NULL;
+	print_ebda_pci_rsrc ();
+	return 0;
+}
+
+u16 ibmphp_get_total_controllers (void)
+{
+	return hpc_list_ptr->num_ctlrs;
+}
+
+struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num)
+{
+	struct slot *slot;
+
+	list_for_each_entry(slot, &ibmphp_slot_head, ibm_slot_list) {
+		if (slot->number == physical_num)
+			return slot;
+	}
+	return NULL;
+}
+
+/* To find:
+ *	- the smallest slot number
+ *	- the largest slot number
+ *	- the total number of the slots based on each bus
+ *	  (if only one slot per bus slot_min = slot_max )
+ */
+struct bus_info *ibmphp_find_same_bus_num (u32 num)
+{
+	struct bus_info *ptr;
+
+	list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+		if (ptr->busno == num)
+			 return ptr;
+	}
+	return NULL;
+}
+
+/*  Finding relative bus number, in order to map corresponding
+ *  bus register
+ */
+int ibmphp_get_bus_index (u8 num)
+{
+	struct bus_info *ptr;
+
+	list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+		if (ptr->busno == num)
+			return ptr->index;
+	}
+	return -ENODEV;
+}
+
+void ibmphp_free_bus_info_queue (void)
+{
+	struct bus_info *bus_info;
+	struct list_head *list;
+	struct list_head *next;
+
+	list_for_each_safe (list, next, &bus_info_head ) {
+		bus_info = list_entry (list, struct bus_info, bus_info_list);
+		kfree (bus_info);
+	}
+}
+
+void ibmphp_free_ebda_hpc_queue (void)
+{
+	struct controller *controller = NULL;
+	struct list_head *list;
+	struct list_head *next;
+	int pci_flag = 0;
+
+	list_for_each_safe (list, next, &ebda_hpc_head) {
+		controller = list_entry (list, struct controller, ebda_hpc_list);
+		if (controller->ctlr_type == 0)
+			release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1));
+		else if ((controller->ctlr_type == 1) && (!pci_flag)) {
+			++pci_flag;
+			pci_unregister_driver (&ibmphp_driver);
+		}
+		free_ebda_hpc (controller);
+	}
+}
+
+void ibmphp_free_ebda_pci_rsrc_queue (void)
+{
+	struct ebda_pci_rsrc *resource;
+	struct list_head *list;
+	struct list_head *next;
+
+	list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) {
+		resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+		kfree (resource);
+		resource = NULL;
+	}
+}
+
+static struct pci_device_id id_table[] = {
+	{
+		.vendor		= PCI_VENDOR_ID_IBM,
+		.device		= HPC_DEVICE_ID,
+		.subvendor	= PCI_VENDOR_ID_IBM,
+		.subdevice	= HPC_SUBSYSTEM_ID,
+		.class		= ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+	}, {}
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static int ibmphp_probe (struct pci_dev *, const struct pci_device_id *);
+static struct pci_driver ibmphp_driver = {
+	.name		= "ibmphp",
+	.id_table	= id_table,
+	.probe		= ibmphp_probe,
+};
+
+int ibmphp_register_pci (void)
+{
+	struct controller *ctrl;
+	int rc = 0;
+
+	list_for_each_entry(ctrl, &ebda_hpc_head, ebda_hpc_list) {
+		if (ctrl->ctlr_type == 1) {
+			rc = pci_register_driver(&ibmphp_driver);
+			break;
+		}
+	}
+	return rc;
+}
+static int ibmphp_probe (struct pci_dev *dev, const struct pci_device_id *ids)
+{
+	struct controller *ctrl;
+
+	debug ("inside ibmphp_probe\n");
+
+	list_for_each_entry(ctrl, &ebda_hpc_head, ebda_hpc_list) {
+		if (ctrl->ctlr_type == 1) {
+			if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) {
+				ctrl->ctrl_dev = dev;
+				debug ("found device!!!\n");
+				debug ("dev->device = %x, dev->subsystem_device = %x\n", dev->device, dev->subsystem_device);
+				return 0;
+			}
+		}
+	}
+	return -ENODEV;
+}
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
new file mode 100644
index 0000000..2208767
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -0,0 +1,1131 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Jyoti Shah, IBM Corporation
+ *
+ * Copyright (C) 2001-2003 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *                  <jshah@us.ibm.com>
+ *
+ */
+
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/kthread.h>
+#include "ibmphp.h"
+
+static int to_debug = 0;
+#define debug_polling(fmt, arg...)	do { if (to_debug) debug (fmt, arg); } while (0)
+
+//----------------------------------------------------------------------------
+// timeout values
+//----------------------------------------------------------------------------
+#define CMD_COMPLETE_TOUT_SEC	60	// give HPC 60 sec to finish cmd
+#define HPC_CTLR_WORKING_TOUT	60	// give HPC 60 sec to finish cmd
+#define HPC_GETACCESS_TIMEOUT	60	// seconds
+#define POLL_INTERVAL_SEC	2	// poll HPC every 2 seconds
+#define POLL_LATCH_CNT		5	// poll latch 5 times, then poll slots
+
+//----------------------------------------------------------------------------
+// Winnipeg Architected Register Offsets
+//----------------------------------------------------------------------------
+#define WPG_I2CMBUFL_OFFSET	0x08	// I2C Message Buffer Low
+#define WPG_I2CMOSUP_OFFSET	0x10	// I2C Master Operation Setup Reg
+#define WPG_I2CMCNTL_OFFSET	0x20	// I2C Master Control Register
+#define WPG_I2CPARM_OFFSET	0x40	// I2C Parameter Register
+#define WPG_I2CSTAT_OFFSET	0x70	// I2C Status Register
+
+//----------------------------------------------------------------------------
+// Winnipeg Store Type commands (Add this commands to the register offset)
+//----------------------------------------------------------------------------
+#define WPG_I2C_AND		0x1000	// I2C AND operation
+#define WPG_I2C_OR		0x2000	// I2C OR operation
+
+//----------------------------------------------------------------------------
+// Command set for I2C Master Operation Setup Register
+//----------------------------------------------------------------------------
+#define WPG_READATADDR_MASK	0x00010000	// read,bytes,I2C shifted,index
+#define WPG_WRITEATADDR_MASK	0x40010000	// write,bytes,I2C shifted,index
+#define WPG_READDIRECT_MASK	0x10010000
+#define WPG_WRITEDIRECT_MASK	0x60010000
+
+
+//----------------------------------------------------------------------------
+// bit masks for I2C Master Control Register
+//----------------------------------------------------------------------------
+#define WPG_I2CMCNTL_STARTOP_MASK	0x00000002	// Start the Operation
+
+//----------------------------------------------------------------------------
+//
+//----------------------------------------------------------------------------
+#define WPG_I2C_IOREMAP_SIZE	0x2044	// size of linear address interval
+
+//----------------------------------------------------------------------------
+// command index
+//----------------------------------------------------------------------------
+#define WPG_1ST_SLOT_INDEX	0x01	// index - 1st slot for ctlr
+#define WPG_CTLR_INDEX		0x0F	// index - ctlr
+#define WPG_1ST_EXTSLOT_INDEX	0x10	// index - 1st ext slot for ctlr
+#define WPG_1ST_BUS_INDEX	0x1F	// index - 1st bus for ctlr
+
+//----------------------------------------------------------------------------
+// macro utilities
+//----------------------------------------------------------------------------
+// if bits 20,22,25,26,27,29,30 are OFF return 1
+#define HPC_I2CSTATUS_CHECK(s)	((u8)((s & 0x00000A76) ? 0 : 1))
+
+//----------------------------------------------------------------------------
+// global variables
+//----------------------------------------------------------------------------
+static struct mutex sem_hpcaccess;	// lock access to HPC
+static struct semaphore semOperations;	// lock all operations and
+					// access to data structures
+static struct semaphore sem_exit;	// make sure polling thread goes away
+static struct task_struct *ibmphp_poll_thread;
+//----------------------------------------------------------------------------
+// local function prototypes
+//----------------------------------------------------------------------------
+static u8 i2c_ctrl_read (struct controller *, void __iomem *, u8);
+static u8 i2c_ctrl_write (struct controller *, void __iomem *, u8, u8);
+static u8 hpc_writecmdtoindex (u8, u8);
+static u8 hpc_readcmdtoindex (u8, u8);
+static void get_hpc_access (void);
+static void free_hpc_access (void);
+static int poll_hpc(void *data);
+static int process_changeinstatus (struct slot *, struct slot *);
+static int process_changeinlatch (u8, u8, struct controller *);
+static int hpc_wait_ctlr_notworking (int, struct controller *, void __iomem *, u8 *);
+//----------------------------------------------------------------------------
+
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_initvars
+*
+* Action:  initialize semaphores and variables
+*---------------------------------------------------------------------*/
+void __init ibmphp_hpc_initvars (void)
+{
+	debug ("%s - Entry\n", __func__);
+
+	mutex_init(&sem_hpcaccess);
+	sema_init(&semOperations, 1);
+	sema_init(&sem_exit, 0);
+	to_debug = 0;
+
+	debug ("%s - Exit\n", __func__);
+}
+
+/*----------------------------------------------------------------------
+* Name:    i2c_ctrl_read
+*
+* Action:  read from HPC over I2C
+*
+*---------------------------------------------------------------------*/
+static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index)
+{
+	u8 status;
+	int i;
+	void __iomem *wpg_addr;	// base addr + offset
+	unsigned long wpg_data;	// data to/from WPG LOHI format
+	unsigned long ultemp;
+	unsigned long data;	// actual data HILO format
+
+	debug_polling ("%s - Entry WPGBbar[%p] index[%x] \n", __func__, WPGBbar, index);
+
+	//--------------------------------------------------------------------
+	// READ - step 1
+	// read at address, byte length, I2C address (shifted), index
+	// or read direct, byte length, index
+	if (ctlr_ptr->ctlr_type == 0x02) {
+		data = WPG_READATADDR_MASK;
+		// fill in I2C address
+		ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+		ultemp = ultemp >> 1;
+		data |= (ultemp << 8);
+
+		// fill in index
+		data |= (unsigned long)index;
+	} else if (ctlr_ptr->ctlr_type == 0x04) {
+		data = WPG_READDIRECT_MASK;
+
+		// fill in index
+		ultemp = (unsigned long)index;
+		ultemp = ultemp << 8;
+		data |= ultemp;
+	} else {
+		err ("this controller type is not supported \n");
+		return HPC_ERROR;
+	}
+
+	wpg_data = swab32 (data);	// swap data before writing
+	wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// READ - step 2 : clear the message buffer
+	data = 0x00000000;
+	wpg_data = swab32 (data);
+	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// READ - step 3 : issue start operation, I2C master control bit 30:ON
+	//                 2020 : [20] OR operation at [20] offset 0x20
+	data = WPG_I2CMCNTL_STARTOP_MASK;
+	wpg_data = swab32 (data);
+	wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// READ - step 4 : wait until start operation bit clears
+	i = CMD_COMPLETE_TOUT_SEC;
+	while (i) {
+		msleep(10);
+		wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET;
+		wpg_data = readl (wpg_addr);
+		data = swab32 (wpg_data);
+		if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
+			break;
+		i--;
+	}
+	if (i == 0) {
+		debug ("%s - Error : WPG timeout\n", __func__);
+		return HPC_ERROR;
+	}
+	//--------------------------------------------------------------------
+	// READ - step 5 : read I2C status register
+	i = CMD_COMPLETE_TOUT_SEC;
+	while (i) {
+		msleep(10);
+		wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET;
+		wpg_data = readl (wpg_addr);
+		data = swab32 (wpg_data);
+		if (HPC_I2CSTATUS_CHECK (data))
+			break;
+		i--;
+	}
+	if (i == 0) {
+		debug ("ctrl_read - Exit Error:I2C timeout\n");
+		return HPC_ERROR;
+	}
+
+	//--------------------------------------------------------------------
+	// READ - step 6 : get DATA
+	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;
+	wpg_data = readl (wpg_addr);
+	data = swab32 (wpg_data);
+
+	status = (u8) data;
+
+	debug_polling ("%s - Exit index[%x] status[%x]\n", __func__, index, status);
+
+	return (status);
+}
+
+/*----------------------------------------------------------------------
+* Name:    i2c_ctrl_write
+*
+* Action:  write to HPC over I2C
+*
+* Return   0 or error codes
+*---------------------------------------------------------------------*/
+static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index, u8 cmd)
+{
+	u8 rc;
+	void __iomem *wpg_addr;	// base addr + offset
+	unsigned long wpg_data;	// data to/from WPG LOHI format
+	unsigned long ultemp;
+	unsigned long data;	// actual data HILO format
+	int i;
+
+	debug_polling ("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __func__, WPGBbar, index, cmd);
+
+	rc = 0;
+	//--------------------------------------------------------------------
+	// WRITE - step 1
+	// write at address, byte length, I2C address (shifted), index
+	// or write direct, byte length, index
+	data = 0x00000000;
+
+	if (ctlr_ptr->ctlr_type == 0x02) {
+		data = WPG_WRITEATADDR_MASK;
+		// fill in I2C address
+		ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+		ultemp = ultemp >> 1;
+		data |= (ultemp << 8);
+
+		// fill in index
+		data |= (unsigned long)index;
+	} else if (ctlr_ptr->ctlr_type == 0x04) {
+		data = WPG_WRITEDIRECT_MASK;
+
+		// fill in index
+		ultemp = (unsigned long)index;
+		ultemp = ultemp << 8;
+		data |= ultemp;
+	} else {
+		err ("this controller type is not supported \n");
+		return HPC_ERROR;
+	}
+
+	wpg_data = swab32 (data);	// swap data before writing
+	wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// WRITE - step 2 : clear the message buffer
+	data = 0x00000000 | (unsigned long)cmd;
+	wpg_data = swab32 (data);
+	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// WRITE - step 3 : issue start operation,I2C master control bit 30:ON
+	//                 2020 : [20] OR operation at [20] offset 0x20
+	data = WPG_I2CMCNTL_STARTOP_MASK;
+	wpg_data = swab32 (data);
+	wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;
+	writel (wpg_data, wpg_addr);
+
+	//--------------------------------------------------------------------
+	// WRITE - step 4 : wait until start operation bit clears
+	i = CMD_COMPLETE_TOUT_SEC;
+	while (i) {
+		msleep(10);
+		wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET;
+		wpg_data = readl (wpg_addr);
+		data = swab32 (wpg_data);
+		if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
+			break;
+		i--;
+	}
+	if (i == 0) {
+		debug ("%s - Exit Error:WPG timeout\n", __func__);
+		rc = HPC_ERROR;
+	}
+
+	//--------------------------------------------------------------------
+	// WRITE - step 5 : read I2C status register
+	i = CMD_COMPLETE_TOUT_SEC;
+	while (i) {
+		msleep(10);
+		wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET;
+		wpg_data = readl (wpg_addr);
+		data = swab32 (wpg_data);
+		if (HPC_I2CSTATUS_CHECK (data))
+			break;
+		i--;
+	}
+	if (i == 0) {
+		debug ("ctrl_read - Error : I2C timeout\n");
+		rc = HPC_ERROR;
+	}
+
+	debug_polling ("%s Exit rc[%x]\n", __func__, rc);
+	return (rc);
+}
+
+//------------------------------------------------------------
+//  Read from ISA type HPC
+//------------------------------------------------------------
+static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset)
+{
+	u16 start_address;
+	u16 end_address;
+	u8 data;
+
+	start_address = ctlr_ptr->u.isa_ctlr.io_start;
+	end_address = ctlr_ptr->u.isa_ctlr.io_end;
+	data = inb (start_address + offset);
+	return data;
+}
+
+//--------------------------------------------------------------
+// Write to ISA type HPC
+//--------------------------------------------------------------
+static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data)
+{
+	u16 start_address;
+	u16 port_address;
+
+	start_address = ctlr_ptr->u.isa_ctlr.io_start;
+	port_address = start_address + (u16) offset;
+	outb (data, port_address);
+}
+
+static u8 pci_ctrl_read (struct controller *ctrl, u8 offset)
+{
+	u8 data = 0x00;
+	debug ("inside pci_ctrl_read\n");
+	if (ctrl->ctrl_dev)
+		pci_read_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data);
+	return data;
+}
+
+static u8 pci_ctrl_write (struct controller *ctrl, u8 offset, u8 data)
+{
+	u8 rc = -ENODEV;
+	debug ("inside pci_ctrl_write\n");
+	if (ctrl->ctrl_dev) {
+		pci_write_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data);
+		rc = 0;
+	}
+	return rc;
+}
+
+static u8 ctrl_read (struct controller *ctlr, void __iomem *base, u8 offset)
+{
+	u8 rc;
+	switch (ctlr->ctlr_type) {
+	case 0:
+		rc = isa_ctrl_read (ctlr, offset);
+		break;
+	case 1:
+		rc = pci_ctrl_read (ctlr, offset);
+		break;
+	case 2:
+	case 4:
+		rc = i2c_ctrl_read (ctlr, base, offset);
+		break;
+	default:
+		return -ENODEV;
+	}
+	return rc;
+}
+
+static u8 ctrl_write (struct controller *ctlr, void __iomem *base, u8 offset, u8 data)
+{
+	u8 rc = 0;
+	switch (ctlr->ctlr_type) {
+	case 0:
+		isa_ctrl_write(ctlr, offset, data);
+		break;
+	case 1:
+		rc = pci_ctrl_write (ctlr, offset, data);
+		break;
+	case 2:
+	case 4:
+		rc = i2c_ctrl_write(ctlr, base, offset, data);
+		break;
+	default:
+		return -ENODEV;
+	}
+	return rc;
+}
+/*----------------------------------------------------------------------
+* Name:    hpc_writecmdtoindex()
+*
+* Action:  convert a write command to proper index within a controller
+*
+* Return   index, HPC_ERROR
+*---------------------------------------------------------------------*/
+static u8 hpc_writecmdtoindex (u8 cmd, u8 index)
+{
+	u8 rc;
+
+	switch (cmd) {
+	case HPC_CTLR_ENABLEIRQ:	// 0x00.N.15
+	case HPC_CTLR_CLEARIRQ:	// 0x06.N.15
+	case HPC_CTLR_RESET:	// 0x07.N.15
+	case HPC_CTLR_IRQSTEER:	// 0x08.N.15
+	case HPC_CTLR_DISABLEIRQ:	// 0x01.N.15
+	case HPC_ALLSLOT_ON:	// 0x11.N.15
+	case HPC_ALLSLOT_OFF:	// 0x12.N.15
+		rc = 0x0F;
+		break;
+
+	case HPC_SLOT_OFF:	// 0x02.Y.0-14
+	case HPC_SLOT_ON:	// 0x03.Y.0-14
+	case HPC_SLOT_ATTNOFF:	// 0x04.N.0-14
+	case HPC_SLOT_ATTNON:	// 0x05.N.0-14
+	case HPC_SLOT_BLINKLED:	// 0x13.N.0-14
+		rc = index;
+		break;
+
+	case HPC_BUS_33CONVMODE:
+	case HPC_BUS_66CONVMODE:
+	case HPC_BUS_66PCIXMODE:
+	case HPC_BUS_100PCIXMODE:
+	case HPC_BUS_133PCIXMODE:
+		rc = index + WPG_1ST_BUS_INDEX - 1;
+		break;
+
+	default:
+		err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd);
+		rc = HPC_ERROR;
+	}
+
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_readcmdtoindex()
+*
+* Action:  convert a read command to proper index within a controller
+*
+* Return   index, HPC_ERROR
+*---------------------------------------------------------------------*/
+static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
+{
+	u8 rc;
+
+	switch (cmd) {
+	case READ_CTLRSTATUS:
+		rc = 0x0F;
+		break;
+	case READ_SLOTSTATUS:
+	case READ_ALLSTAT:
+		rc = index;
+		break;
+	case READ_EXTSLOTSTATUS:
+		rc = index + WPG_1ST_EXTSLOT_INDEX;
+		break;
+	case READ_BUSSTATUS:
+		rc = index + WPG_1ST_BUS_INDEX - 1;
+		break;
+	case READ_SLOTLATCHLOWREG:
+		rc = 0x28;
+		break;
+	case READ_REVLEVEL:
+		rc = 0x25;
+		break;
+	case READ_HPCOPTIONS:
+		rc = 0x27;
+		break;
+	default:
+		rc = HPC_ERROR;
+	}
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    HPCreadslot()
+*
+* Action:  issue a READ command to HPC
+*
+* Input:   pslot   - cannot be NULL for READ_ALLSTAT
+*          pstatus - can be NULL for READ_ALLSTAT
+*
+* Return   0 or error codes
+*---------------------------------------------------------------------*/
+int ibmphp_hpc_readslot (struct slot *pslot, u8 cmd, u8 *pstatus)
+{
+	void __iomem *wpg_bbar = NULL;
+	struct controller *ctlr_ptr;
+	struct list_head *pslotlist;
+	u8 index, status;
+	int rc = 0;
+	int busindex;
+
+	debug_polling ("%s - Entry pslot[%p] cmd[%x] pstatus[%p]\n", __func__, pslot, cmd, pstatus);
+
+	if ((pslot == NULL)
+	    || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) {
+		rc = -EINVAL;
+		err ("%s - Error invalid pointer, rc[%d]\n", __func__, rc);
+		return rc;
+	}
+
+	if (cmd == READ_BUSSTATUS) {
+		busindex = ibmphp_get_bus_index (pslot->bus);
+		if (busindex < 0) {
+			rc = -EINVAL;
+			err ("%s - Exit Error:invalid bus, rc[%d]\n", __func__, rc);
+			return rc;
+		} else
+			index = (u8) busindex;
+	} else
+		index = pslot->ctlr_index;
+
+	index = hpc_readcmdtoindex (cmd, index);
+
+	if (index == HPC_ERROR) {
+		rc = -EINVAL;
+		err ("%s - Exit Error:invalid index, rc[%d]\n", __func__, rc);
+		return rc;
+	}
+
+	ctlr_ptr = pslot->ctrl;
+
+	get_hpc_access ();
+
+	//--------------------------------------------------------------------
+	// map physical address to logical address
+	//--------------------------------------------------------------------
+	if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+		wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
+
+	//--------------------------------------------------------------------
+	// check controller status before reading
+	//--------------------------------------------------------------------
+	rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
+	if (!rc) {
+		switch (cmd) {
+		case READ_ALLSTAT:
+			// update the slot structure
+			pslot->ctrl->status = status;
+			pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
+						       &status);
+			if (!rc)
+				pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX);
+
+			break;
+
+		case READ_SLOTSTATUS:
+			// DO NOT update the slot structure
+			*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+
+		case READ_EXTSLOTSTATUS:
+			// DO NOT update the slot structure
+			*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+
+		case READ_CTLRSTATUS:
+			// DO NOT update the slot structure
+			*pstatus = status;
+			break;
+
+		case READ_BUSSTATUS:
+			pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+		case READ_REVLEVEL:
+			*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+		case READ_HPCOPTIONS:
+			*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+		case READ_SLOTLATCHLOWREG:
+			// DO NOT update the slot structure
+			*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+			break;
+
+			// Not used
+		case READ_ALLSLOT:
+			list_for_each (pslotlist, &ibmphp_slot_head) {
+				pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+				index = pslot->ctlr_index;
+				rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr,
+								wpg_bbar, &status);
+				if (!rc) {
+					pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
+					rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT,
+									ctlr_ptr, wpg_bbar, &status);
+					if (!rc)
+						pslot->ext_status =
+						    ctrl_read (ctlr_ptr, wpg_bbar,
+								index + WPG_1ST_EXTSLOT_INDEX);
+				} else {
+					err ("%s - Error ctrl_read failed\n", __func__);
+					rc = -EINVAL;
+					break;
+				}
+			}
+			break;
+		default:
+			rc = -EINVAL;
+			break;
+		}
+	}
+	//--------------------------------------------------------------------
+	// cleanup
+	//--------------------------------------------------------------------
+
+	// remove physical to logical address mapping
+	if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+		iounmap (wpg_bbar);
+
+	free_hpc_access ();
+
+	debug_polling ("%s - Exit rc[%d]\n", __func__, rc);
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_writeslot()
+*
+* Action: issue a WRITE command to HPC
+*---------------------------------------------------------------------*/
+int ibmphp_hpc_writeslot (struct slot *pslot, u8 cmd)
+{
+	void __iomem *wpg_bbar = NULL;
+	struct controller *ctlr_ptr;
+	u8 index, status;
+	int busindex;
+	u8 done;
+	int rc = 0;
+	int timeout;
+
+	debug_polling ("%s - Entry pslot[%p] cmd[%x]\n", __func__, pslot, cmd);
+	if (pslot == NULL) {
+		rc = -EINVAL;
+		err ("%s - Error Exit rc[%d]\n", __func__, rc);
+		return rc;
+	}
+
+	if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) ||
+		(cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) ||
+		(cmd == HPC_BUS_133PCIXMODE)) {
+		busindex = ibmphp_get_bus_index (pslot->bus);
+		if (busindex < 0) {
+			rc = -EINVAL;
+			err ("%s - Exit Error:invalid bus, rc[%d]\n", __func__, rc);
+			return rc;
+		} else
+			index = (u8) busindex;
+	} else
+		index = pslot->ctlr_index;
+
+	index = hpc_writecmdtoindex (cmd, index);
+
+	if (index == HPC_ERROR) {
+		rc = -EINVAL;
+		err ("%s - Error Exit rc[%d]\n", __func__, rc);
+		return rc;
+	}
+
+	ctlr_ptr = pslot->ctrl;
+
+	get_hpc_access ();
+
+	//--------------------------------------------------------------------
+	// map physical address to logical address
+	//--------------------------------------------------------------------
+	if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) {
+		wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
+
+		debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __func__,
+		ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar,
+		ctlr_ptr->u.wpeg_ctlr.i2c_addr);
+	}
+	//--------------------------------------------------------------------
+	// check controller status before writing
+	//--------------------------------------------------------------------
+	rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
+	if (!rc) {
+
+		ctrl_write (ctlr_ptr, wpg_bbar, index, cmd);
+
+		//--------------------------------------------------------------------
+		// check controller is still not working on the command
+		//--------------------------------------------------------------------
+		timeout = CMD_COMPLETE_TOUT_SEC;
+		done = 0;
+		while (!done) {
+			rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
+							&status);
+			if (!rc) {
+				if (NEEDTOCHECK_CMDSTATUS (cmd)) {
+					if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES)
+						done = 1;
+				} else
+					done = 1;
+			}
+			if (!done) {
+				msleep(1000);
+				if (timeout < 1) {
+					done = 1;
+					err ("%s - Error command complete timeout\n", __func__);
+					rc = -EFAULT;
+				} else
+					timeout--;
+			}
+		}
+		ctlr_ptr->status = status;
+	}
+	// cleanup
+
+	// remove physical to logical address mapping
+	if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+		iounmap (wpg_bbar);
+	free_hpc_access ();
+
+	debug_polling ("%s - Exit rc[%d]\n", __func__, rc);
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    get_hpc_access()
+*
+* Action: make sure only one process can access HPC at one time
+*---------------------------------------------------------------------*/
+static void get_hpc_access (void)
+{
+	mutex_lock(&sem_hpcaccess);
+}
+
+/*----------------------------------------------------------------------
+* Name:    free_hpc_access()
+*---------------------------------------------------------------------*/
+void free_hpc_access (void)
+{
+	mutex_unlock(&sem_hpcaccess);
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_lock_operations()
+*
+* Action: make sure only one process can change the data structure
+*---------------------------------------------------------------------*/
+void ibmphp_lock_operations (void)
+{
+	down (&semOperations);
+	to_debug = 1;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_unlock_operations()
+*---------------------------------------------------------------------*/
+void ibmphp_unlock_operations (void)
+{
+	debug ("%s - Entry\n", __func__);
+	up (&semOperations);
+	to_debug = 0;
+	debug ("%s - Exit\n", __func__);
+}
+
+/*----------------------------------------------------------------------
+* Name:    poll_hpc()
+*---------------------------------------------------------------------*/
+#define POLL_LATCH_REGISTER	0
+#define POLL_SLOTS		1
+#define POLL_SLEEP		2
+static int poll_hpc(void *data)
+{
+	struct slot myslot;
+	struct slot *pslot = NULL;
+	struct list_head *pslotlist;
+	int rc;
+	int poll_state = POLL_LATCH_REGISTER;
+	u8 oldlatchlow = 0x00;
+	u8 curlatchlow = 0x00;
+	int poll_count = 0;
+	u8 ctrl_count = 0x00;
+
+	debug ("%s - Entry\n", __func__);
+
+	while (!kthread_should_stop()) {
+		/* try to get the lock to do some kind of hardware access */
+		down (&semOperations);
+
+		switch (poll_state) {
+		case POLL_LATCH_REGISTER:
+			oldlatchlow = curlatchlow;
+			ctrl_count = 0x00;
+			list_for_each (pslotlist, &ibmphp_slot_head) {
+				if (ctrl_count >= ibmphp_get_total_controllers())
+					break;
+				pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+				if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
+					ctrl_count++;
+					if (READ_SLOT_LATCH (pslot->ctrl)) {
+						rc = ibmphp_hpc_readslot (pslot,
+									  READ_SLOTLATCHLOWREG,
+									  &curlatchlow);
+						if (oldlatchlow != curlatchlow)
+							process_changeinlatch (oldlatchlow,
+									       curlatchlow,
+									       pslot->ctrl);
+					}
+				}
+			}
+			++poll_count;
+			poll_state = POLL_SLEEP;
+			break;
+		case POLL_SLOTS:
+			list_for_each (pslotlist, &ibmphp_slot_head) {
+				pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+				// make a copy of the old status
+				memcpy ((void *) &myslot, (void *) pslot,
+					sizeof (struct slot));
+				rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
+				if ((myslot.status != pslot->status)
+				    || (myslot.ext_status != pslot->ext_status))
+					process_changeinstatus (pslot, &myslot);
+			}
+			ctrl_count = 0x00;
+			list_for_each (pslotlist, &ibmphp_slot_head) {
+				if (ctrl_count >= ibmphp_get_total_controllers())
+					break;
+				pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+				if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
+					ctrl_count++;
+					if (READ_SLOT_LATCH (pslot->ctrl))
+						rc = ibmphp_hpc_readslot (pslot,
+									  READ_SLOTLATCHLOWREG,
+									  &curlatchlow);
+				}
+			}
+			++poll_count;
+			poll_state = POLL_SLEEP;
+			break;
+		case POLL_SLEEP:
+			/* don't sleep with a lock on the hardware */
+			up (&semOperations);
+			msleep(POLL_INTERVAL_SEC * 1000);
+
+			if (kthread_should_stop())
+				goto out_sleep;
+
+			down (&semOperations);
+
+			if (poll_count >= POLL_LATCH_CNT) {
+				poll_count = 0;
+				poll_state = POLL_SLOTS;
+			} else
+				poll_state = POLL_LATCH_REGISTER;
+			break;
+		}
+		/* give up the hardware semaphore */
+		up (&semOperations);
+		/* sleep for a short time just for good measure */
+out_sleep:
+		msleep(100);
+	}
+	up (&sem_exit);
+	debug ("%s - Exit\n", __func__);
+	return 0;
+}
+
+
+/*----------------------------------------------------------------------
+* Name:    process_changeinstatus
+*
+* Action:  compare old and new slot status, process the change in status
+*
+* Input:   pointer to slot struct, old slot struct
+*
+* Return   0 or error codes
+* Value:
+*
+* Side
+* Effects: None.
+*
+* Notes:
+*---------------------------------------------------------------------*/
+static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
+{
+	u8 status;
+	int rc = 0;
+	u8 disable = 0;
+	u8 update = 0;
+
+	debug ("process_changeinstatus - Entry pslot[%p], poldslot[%p]\n", pslot, poldslot);
+
+	// bit 0 - HPC_SLOT_POWER
+	if ((pslot->status & 0x01) != (poldslot->status & 0x01))
+		update = 1;
+
+	// bit 1 - HPC_SLOT_CONNECT
+	// ignore
+
+	// bit 2 - HPC_SLOT_ATTN
+	if ((pslot->status & 0x04) != (poldslot->status & 0x04))
+		update = 1;
+
+	// bit 3 - HPC_SLOT_PRSNT2
+	// bit 4 - HPC_SLOT_PRSNT1
+	if (((pslot->status & 0x08) != (poldslot->status & 0x08))
+		|| ((pslot->status & 0x10) != (poldslot->status & 0x10)))
+		update = 1;
+
+	// bit 5 - HPC_SLOT_PWRGD
+	if ((pslot->status & 0x20) != (poldslot->status & 0x20))
+		// OFF -> ON: ignore, ON -> OFF: disable slot
+		if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status)))
+			disable = 1;
+
+	// bit 6 - HPC_SLOT_BUS_SPEED
+	// ignore
+
+	// bit 7 - HPC_SLOT_LATCH
+	if ((pslot->status & 0x80) != (poldslot->status & 0x80)) {
+		update = 1;
+		// OPEN -> CLOSE
+		if (pslot->status & 0x80) {
+			if (SLOT_PWRGD (pslot->status)) {
+				// power goes on and off after closing latch
+				// check again to make sure power is still ON
+				msleep(1000);
+				rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status);
+				if (SLOT_PWRGD (status))
+					update = 1;
+				else	// overwrite power in pslot to OFF
+					pslot->status &= ~HPC_SLOT_POWER;
+			}
+		}
+		// CLOSE -> OPEN
+		else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD)
+			&& (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) {
+			disable = 1;
+		}
+		// else - ignore
+	}
+	// bit 4 - HPC_SLOT_BLINK_ATTN
+	if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08))
+		update = 1;
+
+	if (disable) {
+		debug ("process_changeinstatus - disable slot\n");
+		pslot->flag = 0;
+		rc = ibmphp_do_disable_slot (pslot);
+	}
+
+	if (update || disable)
+		ibmphp_update_slot_info (pslot);
+
+	debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __func__, rc, disable, update);
+
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    process_changeinlatch
+*
+* Action:  compare old and new latch reg status, process the change
+*
+* Input:   old and current latch register status
+*
+* Return   0 or error codes
+* Value:
+*---------------------------------------------------------------------*/
+static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl)
+{
+	struct slot myslot, *pslot;
+	u8 i;
+	u8 mask;
+	int rc = 0;
+
+	debug ("%s - Entry old[%x], new[%x]\n", __func__, old, new);
+	// bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots
+
+	for (i = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; i++) {
+		mask = 0x01 << i;
+		if ((mask & old) != (mask & new)) {
+			pslot = ibmphp_get_slot_from_physical_num (i);
+			if (pslot) {
+				memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+				rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
+				debug ("%s - call process_changeinstatus for slot[%d]\n", __func__, i);
+				process_changeinstatus (pslot, &myslot);
+			} else {
+				rc = -EINVAL;
+				err ("%s - Error bad pointer for slot[%d]\n", __func__, i);
+			}
+		}
+	}
+	debug ("%s - Exit rc[%d]\n", __func__, rc);
+	return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_start_poll_thread
+*
+* Action:  start polling thread
+*---------------------------------------------------------------------*/
+int __init ibmphp_hpc_start_poll_thread (void)
+{
+	debug ("%s - Entry\n", __func__);
+
+	ibmphp_poll_thread = kthread_run(poll_hpc, NULL, "hpc_poll");
+	if (IS_ERR(ibmphp_poll_thread)) {
+		err ("%s - Error, thread not started\n", __func__);
+		return PTR_ERR(ibmphp_poll_thread);
+	}
+	return 0;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_stop_poll_thread
+*
+* Action:  stop polling thread and cleanup
+*---------------------------------------------------------------------*/
+void __exit ibmphp_hpc_stop_poll_thread (void)
+{
+	debug ("%s - Entry\n", __func__);
+
+	kthread_stop(ibmphp_poll_thread);
+	debug ("before locking operations \n");
+	ibmphp_lock_operations ();
+	debug ("after locking operations \n");
+
+	// wait for poll thread to exit
+	debug ("before sem_exit down \n");
+	down (&sem_exit);
+	debug ("after sem_exit down \n");
+
+	// cleanup
+	debug ("before free_hpc_access \n");
+	free_hpc_access ();
+	debug ("after free_hpc_access \n");
+	ibmphp_unlock_operations ();
+	debug ("after unlock operations \n");
+	up (&sem_exit);
+	debug ("after sem exit up\n");
+
+	debug ("%s - Exit\n", __func__);
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_wait_ctlr_notworking
+*
+* Action:  wait until the controller is in a not working state
+*
+* Return   0, HPC_ERROR
+* Value:
+*---------------------------------------------------------------------*/
+static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void __iomem *wpg_bbar,
+				    u8 *pstatus)
+{
+	int rc = 0;
+	u8 done = 0;
+
+	debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout);
+
+	while (!done) {
+		*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX);
+		if (*pstatus == HPC_ERROR) {
+			rc = HPC_ERROR;
+			done = 1;
+		}
+		if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO)
+			done = 1;
+		if (!done) {
+			msleep(1000);
+			if (timeout < 1) {
+				done = 1;
+				err ("HPCreadslot - Error ctlr timeout\n");
+				rc = HPC_ERROR;
+			} else
+				timeout--;
+		}
+	}
+	debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus);
+	return rc;
+}
diff --git a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c
new file mode 100644
index 0000000..814cea2
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_pci.c
@@ -0,0 +1,1722 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Irene Zubarev, IBM Corporation
+ *
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2002 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include "ibmphp.h"
+
+
+static int configure_device(struct pci_func *);
+static int configure_bridge(struct pci_func **, u8);
+static struct res_needed *scan_behind_bridge(struct pci_func *, u8);
+static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8);
+static u8 find_sec_number (u8 primary_busno, u8 slotno);
+
+/*
+ * NOTE..... If BIOS doesn't provide default routing, we assign:
+ * 9 for SCSI, 10 for LAN adapters, and 11 for everything else.
+ * If adapter is bridged, then we assign 11 to it and devices behind it.
+ * We also assign the same irq numbers for multi function devices.
+ * These are PIC mode, so shouldn't matter n.e.ways (hopefully)
+ */
+static void assign_alt_irq (struct pci_func *cur_func, u8 class_code)
+{
+	int j;
+	for (j = 0; j < 4; j++) {
+		if (cur_func->irq[j] == 0xff) {
+			switch (class_code) {
+				case PCI_BASE_CLASS_STORAGE:
+					cur_func->irq[j] = SCSI_IRQ;
+					break;
+				case PCI_BASE_CLASS_NETWORK:
+					cur_func->irq[j] = LAN_IRQ;
+					break;
+				default:
+					cur_func->irq[j] = OTHER_IRQ;
+					break;
+			}
+		}
+	}
+}
+
+/*
+ * Configures the device to be added (will allocate needed resources if it
+ * can), the device can be a bridge or a regular pci device, can also be
+ * multi-functional
+ *
+ * Input: function to be added
+ *
+ * TO DO:  The error case with Multifunction device or multi function bridge,
+ * if there is an error, will need to go through all previous functions and
+ * unconfigure....or can add some code into unconfigure_card....
+ */
+int ibmphp_configure_card (struct pci_func *func, u8 slotno)
+{
+	u16 vendor_id;
+	u32 class;
+	u8 class_code;
+	u8 hdr_type, device, sec_number;
+	u8 function;
+	struct pci_func *newfunc;	/* for multi devices */
+	struct pci_func *cur_func, *prev_func;
+	int rc, i, j;
+	int cleanup_count;
+	u8 flag;
+	u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */
+
+	debug ("inside configure_card, func->busno = %x\n", func->busno);
+
+	device = func->device;
+	cur_func = func;
+
+	/* We only get bus and device from IRQ routing table.  So at this point,
+	 * func->busno is correct, and func->device contains only device (at the 5
+	 * highest bits)
+	 */
+
+	/* For every function on the card */
+	for (function = 0x00; function < 0x08; function++) {
+		unsigned int devfn = PCI_DEVFN(device, function);
+		ibmphp_pci_bus->number = cur_func->busno;
+
+		cur_func->function = function;
+
+		debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->function = %x\n",
+			cur_func->busno, cur_func->device, cur_func->function);
+
+		pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+		debug ("vendor_id is %x\n", vendor_id);
+		if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+			/* found correct device!!! */
+			debug ("found valid device, vendor_id = %x\n", vendor_id);
+
+			++valid_device;
+
+			/* header: x x x x x x x x
+			 *         | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
+			 *         |_=> 0 = single function device, 1 = multi-function device
+			 */
+
+			pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+			pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class);
+
+			class_code = class >> 24;
+			debug ("hrd_type = %x, class = %x, class_code %x\n", hdr_type, class, class_code);
+			class >>= 8;	/* to take revision out, class = class.subclass.prog i/f */
+			if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+				err ("The device %x is VGA compatible and as is not supported for hot plugging. "
+				     "Please choose another device.\n", cur_func->device);
+				return -ENODEV;
+			} else if (class == PCI_CLASS_DISPLAY_VGA) {
+				err ("The device %x is not supported for hot plugging. Please choose another device.\n",
+				     cur_func->device);
+				return -ENODEV;
+			}
+			switch (hdr_type) {
+				case PCI_HEADER_TYPE_NORMAL:
+					debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class);
+					assign_alt_irq (cur_func, class_code);
+					rc = configure_device(cur_func);
+					if (rc < 0) {
+						/* We need to do this in case some other BARs were properly inserted */
+						err ("was not able to configure devfunc %x on bus %x.\n",
+						     cur_func->device, cur_func->busno);
+						cleanup_count = 6;
+						goto error;
+					}
+					cur_func->next = NULL;
+					function = 0x8;
+					break;
+				case PCI_HEADER_TYPE_MULTIDEVICE:
+					assign_alt_irq (cur_func, class_code);
+					rc = configure_device(cur_func);
+					if (rc < 0) {
+						/* We need to do this in case some other BARs were properly inserted */
+						err ("was not able to configure devfunc %x on bus %x...bailing out\n",
+						     cur_func->device, cur_func->busno);
+						cleanup_count = 6;
+						goto error;
+					}
+					newfunc = kzalloc(sizeof(*newfunc), GFP_KERNEL);
+					if (!newfunc) {
+						err ("out of system memory\n");
+						return -ENOMEM;
+					}
+					newfunc->busno = cur_func->busno;
+					newfunc->device = device;
+					cur_func->next = newfunc;
+					cur_func = newfunc;
+					for (j = 0; j < 4; j++)
+						newfunc->irq[j] = cur_func->irq[j];
+					break;
+				case PCI_HEADER_TYPE_MULTIBRIDGE:
+					class >>= 8;
+					if (class != PCI_CLASS_BRIDGE_PCI) {
+						err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging.  Please insert another card.\n",
+						     cur_func->device);
+						return -ENODEV;
+					}
+					assign_alt_irq (cur_func, class_code);
+					rc = configure_bridge (&cur_func, slotno);
+					if (rc == -ENODEV) {
+						err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
+						err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device);
+						return rc;
+					}
+					if (rc) {
+						/* We need to do this in case some other BARs were properly inserted */
+						err ("was not able to hot-add PPB properly.\n");
+						func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+						cleanup_count = 2;
+						goto error;
+					}
+
+					pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+					flag = 0;
+					for (i = 0; i < 32; i++) {
+						if (func->devices[i]) {
+							newfunc = kzalloc(sizeof(*newfunc), GFP_KERNEL);
+							if (!newfunc) {
+								err ("out of system memory\n");
+								return -ENOMEM;
+							}
+							newfunc->busno = sec_number;
+							newfunc->device = (u8) i;
+							for (j = 0; j < 4; j++)
+								newfunc->irq[j] = cur_func->irq[j];
+
+							if (flag) {
+								for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+								prev_func->next = newfunc;
+							} else
+								cur_func->next = newfunc;
+
+							rc = ibmphp_configure_card (newfunc, slotno);
+							/* This could only happen if kmalloc failed */
+							if (rc) {
+								/* We need to do this in case bridge itself got configured properly, but devices behind it failed */
+								func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+								cleanup_count = 2;
+								goto error;
+							}
+							flag = 1;
+						}
+					}
+
+					newfunc = kzalloc(sizeof(*newfunc), GFP_KERNEL);
+					if (!newfunc) {
+						err ("out of system memory\n");
+						return -ENOMEM;
+					}
+					newfunc->busno = cur_func->busno;
+					newfunc->device = device;
+					for (j = 0; j < 4; j++)
+						newfunc->irq[j] = cur_func->irq[j];
+					for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+					prev_func->next = newfunc;
+					cur_func = newfunc;
+					break;
+				case PCI_HEADER_TYPE_BRIDGE:
+					class >>= 8;
+					debug ("class now is %x\n", class);
+					if (class != PCI_CLASS_BRIDGE_PCI) {
+						err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging.  Please insert another card.\n",
+						     cur_func->device);
+						return -ENODEV;
+					}
+
+					assign_alt_irq (cur_func, class_code);
+
+					debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno);
+					rc = configure_bridge (&cur_func, slotno);
+					if (rc == -ENODEV) {
+						err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
+						err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device);
+						return rc;
+					}
+					if (rc) {
+						/* We need to do this in case some other BARs were properly inserted */
+						func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+						err ("was not able to hot-add PPB properly.\n");
+						cleanup_count = 2;
+						goto error;
+					}
+					debug ("cur_func->busno = %x, device = %x, function = %x\n",
+						cur_func->busno, device, function);
+					pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+					debug ("after configuring bridge..., sec_number = %x\n", sec_number);
+					flag = 0;
+					for (i = 0; i < 32; i++) {
+						if (func->devices[i]) {
+							debug ("inside for loop, device is %x\n", i);
+							newfunc = kzalloc(sizeof(*newfunc), GFP_KERNEL);
+							if (!newfunc) {
+								err (" out of system memory\n");
+								return -ENOMEM;
+							}
+							newfunc->busno = sec_number;
+							newfunc->device = (u8) i;
+							for (j = 0; j < 4; j++)
+								newfunc->irq[j] = cur_func->irq[j];
+
+							if (flag) {
+								for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+								prev_func->next = newfunc;
+							} else
+								cur_func->next = newfunc;
+
+							rc = ibmphp_configure_card (newfunc, slotno);
+
+							/* Again, this case should not happen... For complete paranoia, will need to call remove_bus */
+							if (rc) {
+								/* We need to do this in case some other BARs were properly inserted */
+								func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+								cleanup_count = 2;
+								goto error;
+							}
+							flag = 1;
+						}
+					}
+
+					function = 0x8;
+					break;
+				default:
+					err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type);
+					return -ENXIO;
+					break;
+			}	/* end of switch */
+		}	/* end of valid device */
+	}	/* end of for */
+
+	if (!valid_device) {
+		err ("Cannot find any valid devices on the card.  Or unable to read from card.\n");
+		return -ENODEV;
+	}
+
+	return 0;
+
+error:
+	for (i = 0; i < cleanup_count; i++) {
+		if (cur_func->io[i]) {
+			ibmphp_remove_resource (cur_func->io[i]);
+			cur_func->io[i] = NULL;
+		} else if (cur_func->pfmem[i]) {
+			ibmphp_remove_resource (cur_func->pfmem[i]);
+			cur_func->pfmem[i] = NULL;
+		} else if (cur_func->mem[i]) {
+			ibmphp_remove_resource (cur_func->mem[i]);
+			cur_func->mem[i] = NULL;
+		}
+	}
+	return rc;
+}
+
+/*
+ * This function configures the pci BARs of a single device.
+ * Input: pointer to the pci_func
+ * Output: configured PCI, 0, or error
+ */
+static int configure_device (struct pci_func *func)
+{
+	u32 bar[6];
+	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
+	};
+	u8 irq;
+	int count;
+	int len[6];
+	struct resource_node *io[6];
+	struct resource_node *mem[6];
+	struct resource_node *mem_tmp;
+	struct resource_node *pfmem[6];
+	unsigned int devfn;
+
+	debug ("%s - inside\n", __func__);
+
+	devfn = PCI_DEVFN(func->device, func->function);
+	ibmphp_pci_bus->number = func->busno;
+
+	for (count = 0; address[count]; count++) {	/* for 6 BARs */
+
+		/* not sure if i need this.  per scott, said maybe need * something like this
+		   if devices don't adhere 100% to the spec, so don't want to write
+		   to the reserved bits
+
+		pcibios_read_config_byte(cur_func->busno, cur_func->device,
+		PCI_BASE_ADDRESS_0 + 4 * count, &tmp);
+		if (tmp & 0x01) // IO
+			pcibios_write_config_dword(cur_func->busno, cur_func->device,
+			PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD);
+		else  // Memory
+			pcibios_write_config_dword(cur_func->busno, cur_func->device,
+			PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF);
+		 */
+		pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+		pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+
+		if (!bar[count])	/* This BAR is not implemented */
+			continue;
+
+		debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]);
+
+		if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			debug ("inside IO SPACE\n");
+
+			len[count] = bar[count] & 0xFFFFFFFC;
+			len[count] = ~len[count] + 1;
+
+			debug ("len[count] in IO %x, count %d\n", len[count], count);
+
+			io[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+
+			if (!io[count]) {
+				err ("out of system memory\n");
+				return -ENOMEM;
+			}
+			io[count]->type = IO;
+			io[count]->busno = func->busno;
+			io[count]->devfunc = PCI_DEVFN(func->device, func->function);
+			io[count]->len = len[count];
+			if (ibmphp_check_resource(io[count], 0) == 0) {
+				ibmphp_add_resource (io[count]);
+				func->io[count] = io[count];
+			} else {
+				err ("cannot allocate requested io for bus %x device %x function %x len %x\n",
+				     func->busno, func->device, func->function, len[count]);
+				kfree (io[count]);
+				return -EIO;
+			}
+			pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start);
+
+			/* _______________This is for debugging purposes only_____________________ */
+			debug ("b4 writing, the IO address is %x\n", func->io[count]->start);
+			pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+			debug ("after writing.... the start address is %x\n", bar[count]);
+			/* _________________________________________________________________________*/
+
+		} else {
+			/* This is Memory */
+			if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+				debug ("PFMEM SPACE\n");
+
+				len[count] = bar[count] & 0xFFFFFFF0;
+				len[count] = ~len[count] + 1;
+
+				debug ("len[count] in PFMEM %x, count %d\n", len[count], count);
+
+				pfmem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+				if (!pfmem[count]) {
+					err ("out of system memory\n");
+					return -ENOMEM;
+				}
+				pfmem[count]->type = PFMEM;
+				pfmem[count]->busno = func->busno;
+				pfmem[count]->devfunc = PCI_DEVFN(func->device,
+							func->function);
+				pfmem[count]->len = len[count];
+				pfmem[count]->fromMem = 0;
+				if (ibmphp_check_resource (pfmem[count], 0) == 0) {
+					ibmphp_add_resource (pfmem[count]);
+					func->pfmem[count] = pfmem[count];
+				} else {
+					mem_tmp = kzalloc(sizeof(*mem_tmp), GFP_KERNEL);
+					if (!mem_tmp) {
+						err ("out of system memory\n");
+						kfree (pfmem[count]);
+						return -ENOMEM;
+					}
+					mem_tmp->type = MEM;
+					mem_tmp->busno = pfmem[count]->busno;
+					mem_tmp->devfunc = pfmem[count]->devfunc;
+					mem_tmp->len = pfmem[count]->len;
+					debug ("there's no pfmem... going into mem.\n");
+					if (ibmphp_check_resource (mem_tmp, 0) == 0) {
+						ibmphp_add_resource (mem_tmp);
+						pfmem[count]->fromMem = 1;
+						pfmem[count]->rangeno = mem_tmp->rangeno;
+						pfmem[count]->start = mem_tmp->start;
+						pfmem[count]->end = mem_tmp->end;
+						ibmphp_add_pfmem_from_mem (pfmem[count]);
+						func->pfmem[count] = pfmem[count];
+					} else {
+						err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
+						     func->busno, func->device, len[count]);
+						kfree (mem_tmp);
+						kfree (pfmem[count]);
+						return -EIO;
+					}
+				}
+
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);
+
+				/*_______________This is for debugging purposes only______________________________*/
+				debug ("b4 writing, start address is %x\n", func->pfmem[count]->start);
+				pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+				debug ("after writing, start address is %x\n", bar[count]);
+				/*_________________________________________________________________________________*/
+
+				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {	/* takes up another dword */
+					debug ("inside the mem 64 case, count %d\n", count);
+					count += 1;
+					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+					pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000);
+				}
+			} else {
+				/* regular memory */
+				debug ("REGULAR MEM SPACE\n");
+
+				len[count] = bar[count] & 0xFFFFFFF0;
+				len[count] = ~len[count] + 1;
+
+				debug ("len[count] in Mem %x, count %d\n", len[count], count);
+
+				mem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+				if (!mem[count]) {
+					err ("out of system memory\n");
+					return -ENOMEM;
+				}
+				mem[count]->type = MEM;
+				mem[count]->busno = func->busno;
+				mem[count]->devfunc = PCI_DEVFN(func->device,
+							func->function);
+				mem[count]->len = len[count];
+				if (ibmphp_check_resource (mem[count], 0) == 0) {
+					ibmphp_add_resource (mem[count]);
+					func->mem[count] = mem[count];
+				} else {
+					err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
+					     func->busno, func->device, len[count]);
+					kfree (mem[count]);
+					return -EIO;
+				}
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start);
+				/* _______________________This is for debugging purposes only _______________________*/
+				debug ("b4 writing, start address is %x\n", func->mem[count]->start);
+				pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+				debug ("after writing, the address is %x\n", bar[count]);
+				/* __________________________________________________________________________________*/
+
+				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					debug ("inside mem 64 case, reg. mem, count %d\n", count);
+					count += 1;
+					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+					pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000);
+				}
+			}
+		}		/* end of mem */
+	}			/* end of for */
+
+	func->bus = 0;		/* To indicate that this is not a PPB */
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
+	if ((irq > 0x00) && (irq < 0x05))
+		pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE);
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY);
+
+	pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_ROM_ADDRESS, 0x00L);
+	pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE);
+
+	return 0;
+}
+
+/******************************************************************************
+ * This routine configures a PCI-2-PCI bridge and the functions behind it
+ * Parameters: pci_func
+ * Returns:
+ ******************************************************************************/
+static int configure_bridge (struct pci_func **func_passed, u8 slotno)
+{
+	int count;
+	int i;
+	int rc;
+	u8 sec_number;
+	u8 io_base;
+	u16 pfmem_base;
+	u32 bar[2];
+	u32 len[2];
+	u8 flag_io = 0;
+	u8 flag_mem = 0;
+	u8 flag_pfmem = 0;
+	u8 need_io_upper = 0;
+	u8 need_pfmem_upper = 0;
+	struct res_needed *amount_needed = NULL;
+	struct resource_node *io = NULL;
+	struct resource_node *bus_io[2] = {NULL, NULL};
+	struct resource_node *mem = NULL;
+	struct resource_node *bus_mem[2] = {NULL, NULL};
+	struct resource_node *mem_tmp = NULL;
+	struct resource_node *pfmem = NULL;
+	struct resource_node *bus_pfmem[2] = {NULL, NULL};
+	struct bus_node *bus;
+	u32 address[] = {
+		PCI_BASE_ADDRESS_0,
+		PCI_BASE_ADDRESS_1,
+		0
+	};
+	struct pci_func *func = *func_passed;
+	unsigned int devfn;
+	u8 irq;
+	int retval;
+
+	debug ("%s - enter\n", __func__);
+
+	devfn = PCI_DEVFN(func->function, func->device);
+	ibmphp_pci_bus->number = func->busno;
+
+	/* Configuring necessary info for the bridge so that we could see the devices
+	 * behind it
+	 */
+
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno);
+
+	/* _____________________For debugging purposes only __________________________
+	pci_bus_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number);
+	debug ("primary # written into the bridge is %x\n", pri_number);
+	 ___________________________________________________________________________*/
+
+	/* in EBDA, only get allocated 1 additional bus # per slot */
+	sec_number = find_sec_number (func->busno, slotno);
+	if (sec_number == 0xff) {
+		err ("cannot allocate secondary bus number for the bridged device\n");
+		return -EINVAL;
+	}
+
+	debug ("after find_sec_number, the number we got is %x\n", sec_number);
+	debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);
+
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number);
+
+	/* __________________For debugging purposes only __________________________________
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+	debug ("sec_number after write/read is %x\n", sec_number);
+	 ________________________________________________________________________________*/
+
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number);
+
+	/* __________________For debugging purposes only ____________________________________
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number);
+	debug ("subordinate number after write/read is %x\n", sec_number);
+	 __________________________________________________________________________________*/
+
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE);
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY);
+	pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SEC_LATENCY_TIMER, LATENCY);
+
+	debug ("func->busno is %x\n", func->busno);
+	debug ("sec_number after writing is %x\n", sec_number);
+
+
+	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	   !!!!!!!!!!!!!!!NEED TO ADD!!!  FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
+	   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
+
+
+	/* First we need to allocate mem/io for the bridge itself in case it needs it */
+	for (count = 0; address[count]; count++) {	/* for 2 BARs */
+		pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+		pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+
+		if (!bar[count]) {
+			/* This BAR is not implemented */
+			debug ("so we come here then, eh?, count = %d\n", count);
+			continue;
+		}
+		//  tmp_bar = bar[count];
+
+		debug ("Bar %d wants %x\n", count, bar[count]);
+
+		if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			len[count] = bar[count] & 0xFFFFFFFC;
+			len[count] = ~len[count] + 1;
+
+			debug ("len[count] in IO = %x\n", len[count]);
+
+			bus_io[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+
+			if (!bus_io[count]) {
+				err ("out of system memory\n");
+				retval = -ENOMEM;
+				goto error;
+			}
+			bus_io[count]->type = IO;
+			bus_io[count]->busno = func->busno;
+			bus_io[count]->devfunc = PCI_DEVFN(func->device,
+							func->function);
+			bus_io[count]->len = len[count];
+			if (ibmphp_check_resource (bus_io[count], 0) == 0) {
+				ibmphp_add_resource (bus_io[count]);
+				func->io[count] = bus_io[count];
+			} else {
+				err ("cannot allocate requested io for bus %x, device %x, len %x\n",
+				     func->busno, func->device, len[count]);
+				kfree (bus_io[count]);
+				return -EIO;
+			}
+
+			pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start);
+
+		} else {
+			/* This is Memory */
+			if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+				len[count] = bar[count] & 0xFFFFFFF0;
+				len[count] = ~len[count] + 1;
+
+				debug ("len[count] in PFMEM = %x\n", len[count]);
+
+				bus_pfmem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+				if (!bus_pfmem[count]) {
+					err ("out of system memory\n");
+					retval = -ENOMEM;
+					goto error;
+				}
+				bus_pfmem[count]->type = PFMEM;
+				bus_pfmem[count]->busno = func->busno;
+				bus_pfmem[count]->devfunc = PCI_DEVFN(func->device,
+							func->function);
+				bus_pfmem[count]->len = len[count];
+				bus_pfmem[count]->fromMem = 0;
+				if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) {
+					ibmphp_add_resource (bus_pfmem[count]);
+					func->pfmem[count] = bus_pfmem[count];
+				} else {
+					mem_tmp = kzalloc(sizeof(*mem_tmp), GFP_KERNEL);
+					if (!mem_tmp) {
+						err ("out of system memory\n");
+						retval = -ENOMEM;
+						goto error;
+					}
+					mem_tmp->type = MEM;
+					mem_tmp->busno = bus_pfmem[count]->busno;
+					mem_tmp->devfunc = bus_pfmem[count]->devfunc;
+					mem_tmp->len = bus_pfmem[count]->len;
+					if (ibmphp_check_resource (mem_tmp, 0) == 0) {
+						ibmphp_add_resource (mem_tmp);
+						bus_pfmem[count]->fromMem = 1;
+						bus_pfmem[count]->rangeno = mem_tmp->rangeno;
+						ibmphp_add_pfmem_from_mem (bus_pfmem[count]);
+						func->pfmem[count] = bus_pfmem[count];
+					} else {
+						err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
+						     func->busno, func->device, len[count]);
+						kfree (mem_tmp);
+						kfree (bus_pfmem[count]);
+						return -EIO;
+					}
+				}
+
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);
+
+				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					count += 1;
+					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+					pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000);
+
+				}
+			} else {
+				/* regular memory */
+				len[count] = bar[count] & 0xFFFFFFF0;
+				len[count] = ~len[count] + 1;
+
+				debug ("len[count] in Memory is %x\n", len[count]);
+
+				bus_mem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+				if (!bus_mem[count]) {
+					err ("out of system memory\n");
+					retval = -ENOMEM;
+					goto error;
+				}
+				bus_mem[count]->type = MEM;
+				bus_mem[count]->busno = func->busno;
+				bus_mem[count]->devfunc = PCI_DEVFN(func->device,
+							func->function);
+				bus_mem[count]->len = len[count];
+				if (ibmphp_check_resource (bus_mem[count], 0) == 0) {
+					ibmphp_add_resource (bus_mem[count]);
+					func->mem[count] = bus_mem[count];
+				} else {
+					err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
+					     func->busno, func->device, len[count]);
+					kfree (bus_mem[count]);
+					return -EIO;
+				}
+
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start);
+
+				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+					/* takes up another dword */
+					count += 1;
+					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+					pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000);
+
+				}
+			}
+		}		/* end of mem */
+	}			/* end of for  */
+
+	/* Now need to see how much space the devices behind the bridge needed */
+	amount_needed = scan_behind_bridge (func, sec_number);
+	if (amount_needed == NULL)
+		return -ENOMEM;
+
+	ibmphp_pci_bus->number = func->busno;
+	debug ("after coming back from scan_behind_bridge\n");
+	debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct);
+	debug ("amount_needed->io = %x\n", amount_needed->io);
+	debug ("amount_needed->mem = %x\n", amount_needed->mem);
+	debug ("amount_needed->pfmem =  %x\n", amount_needed->pfmem);
+
+	if (amount_needed->not_correct) {
+		debug ("amount_needed is not correct\n");
+		for (count = 0; address[count]; count++) {
+			/* for 2 BARs */
+			if (bus_io[count]) {
+				ibmphp_remove_resource (bus_io[count]);
+				func->io[count] = NULL;
+			} else if (bus_pfmem[count]) {
+				ibmphp_remove_resource (bus_pfmem[count]);
+				func->pfmem[count] = NULL;
+			} else if (bus_mem[count]) {
+				ibmphp_remove_resource (bus_mem[count]);
+				func->mem[count] = NULL;
+			}
+		}
+		kfree (amount_needed);
+		return -ENODEV;
+	}
+
+	if (!amount_needed->io) {
+		debug ("it doesn't want IO?\n");
+		flag_io = 1;
+	} else {
+		debug ("it wants %x IO behind the bridge\n", amount_needed->io);
+		io = kzalloc(sizeof(*io), GFP_KERNEL);
+
+		if (!io) {
+			err ("out of system memory\n");
+			retval = -ENOMEM;
+			goto error;
+		}
+		io->type = IO;
+		io->busno = func->busno;
+		io->devfunc = PCI_DEVFN(func->device, func->function);
+		io->len = amount_needed->io;
+		if (ibmphp_check_resource (io, 1) == 0) {
+			debug ("were we able to add io\n");
+			ibmphp_add_resource (io);
+			flag_io = 1;
+		}
+	}
+
+	if (!amount_needed->mem) {
+		debug ("it doesn't want n.e.memory?\n");
+		flag_mem = 1;
+	} else {
+		debug ("it wants %x memory behind the bridge\n", amount_needed->mem);
+		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+		if (!mem) {
+			err ("out of system memory\n");
+			retval = -ENOMEM;
+			goto error;
+		}
+		mem->type = MEM;
+		mem->busno = func->busno;
+		mem->devfunc = PCI_DEVFN(func->device, func->function);
+		mem->len = amount_needed->mem;
+		if (ibmphp_check_resource (mem, 1) == 0) {
+			ibmphp_add_resource (mem);
+			flag_mem = 1;
+			debug ("were we able to add mem\n");
+		}
+	}
+
+	if (!amount_needed->pfmem) {
+		debug ("it doesn't want n.e.pfmem mem?\n");
+		flag_pfmem = 1;
+	} else {
+		debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem);
+		pfmem = kzalloc(sizeof(*pfmem), GFP_KERNEL);
+		if (!pfmem) {
+			err ("out of system memory\n");
+			retval = -ENOMEM;
+			goto error;
+		}
+		pfmem->type = PFMEM;
+		pfmem->busno = func->busno;
+		pfmem->devfunc = PCI_DEVFN(func->device, func->function);
+		pfmem->len = amount_needed->pfmem;
+		pfmem->fromMem = 0;
+		if (ibmphp_check_resource (pfmem, 1) == 0) {
+			ibmphp_add_resource (pfmem);
+			flag_pfmem = 1;
+		} else {
+			mem_tmp = kzalloc(sizeof(*mem_tmp), GFP_KERNEL);
+			if (!mem_tmp) {
+				err ("out of system memory\n");
+				retval = -ENOMEM;
+				goto error;
+			}
+			mem_tmp->type = MEM;
+			mem_tmp->busno = pfmem->busno;
+			mem_tmp->devfunc = pfmem->devfunc;
+			mem_tmp->len = pfmem->len;
+			if (ibmphp_check_resource (mem_tmp, 1) == 0) {
+				ibmphp_add_resource (mem_tmp);
+				pfmem->fromMem = 1;
+				pfmem->rangeno = mem_tmp->rangeno;
+				ibmphp_add_pfmem_from_mem (pfmem);
+				flag_pfmem = 1;
+			}
+		}
+	}
+
+	debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n");
+	debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem);
+
+	if (flag_io && flag_mem && flag_pfmem) {
+		/* If on bootup, there was a bridged card in this slot,
+		 * then card was removed and ibmphp got unloaded and loaded
+		 * back again, there's no way for us to remove the bus
+		 * struct, so no need to kmalloc, can use existing node
+		 */
+		bus = ibmphp_find_res_bus (sec_number);
+		if (!bus) {
+			bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+			if (!bus) {
+				err ("out of system memory\n");
+				retval = -ENOMEM;
+				goto error;
+			}
+			bus->busno = sec_number;
+			debug ("b4 adding new bus\n");
+			rc = add_new_bus (bus, io, mem, pfmem, func->busno);
+		} else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem))
+			rc = add_new_bus (bus, io, mem, pfmem, 0xFF);
+		else {
+			err ("expected bus structure not empty?\n");
+			retval = -EIO;
+			goto error;
+		}
+		if (rc) {
+			if (rc == -ENOMEM) {
+				ibmphp_remove_bus (bus, func->busno);
+				kfree (amount_needed);
+				return rc;
+			}
+			retval = rc;
+			goto error;
+		}
+		pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base);
+		pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &pfmem_base);
+
+		if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
+			debug ("io 32\n");
+			need_io_upper = 1;
+		}
+		if ((pfmem_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
+			debug ("pfmem 64\n");
+			need_pfmem_upper = 1;
+		}
+
+		if (bus->noIORanges) {
+			pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
+			pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
+
+			/* _______________This is for debugging purposes only ____________________
+			pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp);
+			debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
+			pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &temp);
+			debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
+			 ________________________________________________________________________*/
+
+			if (need_io_upper) {	/* since can't support n.e.ways */
+				pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000);
+				pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000);
+			}
+		} else {
+			pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00);
+			pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00);
+		}
+
+		if (bus->noMemRanges) {
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
+
+			/* ____________________This is for debugging purposes only ________________________
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp);
+			debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp);
+			debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+			 __________________________________________________________________________________*/
+
+		} else {
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff);
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000);
+		}
+		if (bus->noPFMemRanges) {
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16);
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16);
+
+			/* __________________________This is for debugging purposes only _______________________
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp);
+			debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &temp);
+			debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+			 ______________________________________________________________________________________*/
+
+			if (need_pfmem_upper) {	/* since can't support n.e.ways */
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000);
+				pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000);
+			}
+		} else {
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff);
+			pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000);
+		}
+
+		debug ("b4 writing control information\n");
+
+		pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
+		if ((irq > 0x00) && (irq < 0x05))
+			pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+		/*
+		pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl);
+		pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
+		pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
+		 */
+
+		pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE);
+		pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, 0x07);
+		for (i = 0; i < 32; i++) {
+			if (amount_needed->devices[i]) {
+				debug ("device where devices[i] is 1 = %x\n", i);
+				func->devices[i] = 1;
+			}
+		}
+		func->bus = 1;	/* For unconfiguring, to indicate it's PPB */
+		func_passed = &func;
+		debug ("func->busno b4 returning is %x\n", func->busno);
+		debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno);
+		kfree (amount_needed);
+		return 0;
+	} else {
+		err ("Configuring bridge was unsuccessful...\n");
+		mem_tmp = NULL;
+		retval = -EIO;
+		goto error;
+	}
+
+error:
+	kfree(amount_needed);
+	if (pfmem)
+		ibmphp_remove_resource (pfmem);
+	if (io)
+		ibmphp_remove_resource (io);
+	if (mem)
+		ibmphp_remove_resource (mem);
+	for (i = 0; i < 2; i++) {	/* for 2 BARs */
+		if (bus_io[i]) {
+			ibmphp_remove_resource (bus_io[i]);
+			func->io[i] = NULL;
+		} else if (bus_pfmem[i]) {
+			ibmphp_remove_resource (bus_pfmem[i]);
+			func->pfmem[i] = NULL;
+		} else if (bus_mem[i]) {
+			ibmphp_remove_resource (bus_mem[i]);
+			func->mem[i] = NULL;
+		}
+	}
+	return retval;
+}
+
+/*****************************************************************************
+ * This function adds up the amount of resources needed behind the PPB bridge
+ * and passes it to the configure_bridge function
+ * Input: bridge function
+ * Output: amount of resources needed
+ *****************************************************************************/
+static struct res_needed *scan_behind_bridge (struct pci_func *func, u8 busno)
+{
+	int count, len[6];
+	u16 vendor_id;
+	u8 hdr_type;
+	u8 device, function;
+	unsigned int devfn;
+	int howmany = 0;	/*this is to see if there are any devices behind the bridge */
+
+	u32 bar[6], class;
+	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
+	};
+	struct res_needed *amount;
+
+	amount = kzalloc(sizeof(*amount), GFP_KERNEL);
+	if (amount == NULL)
+		return NULL;
+
+	ibmphp_pci_bus->number = busno;
+
+	debug ("the bus_no behind the bridge is %x\n", busno);
+	debug ("scanning devices behind the bridge...\n");
+	for (device = 0; device < 32; device++) {
+		amount->devices[device] = 0;
+		for (function = 0; function < 8; function++) {
+			devfn = PCI_DEVFN(device, function);
+
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+			if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+				/* found correct device!!! */
+				howmany++;
+
+				pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+				pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class);
+
+				debug ("hdr_type behind the bridge is %x\n", hdr_type);
+				if (hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+					err ("embedded bridges not supported for hot-plugging.\n");
+					amount->not_correct = 1;
+					return amount;
+				}
+
+				class >>= 8;	/* to take revision out, class = class.subclass.prog i/f */
+				if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+					err ("The device %x is VGA compatible and as is not supported for hot plugging.  Please choose another device.\n", device);
+					amount->not_correct = 1;
+					return amount;
+				} else if (class == PCI_CLASS_DISPLAY_VGA) {
+					err ("The device %x is not supported for hot plugging.  Please choose another device.\n", device);
+					amount->not_correct = 1;
+					return amount;
+				}
+
+				amount->devices[device] = 1;
+
+				for (count = 0; address[count]; count++) {
+					/* for 6 BARs */
+					/*
+					pci_bus_read_config_byte (ibmphp_pci_bus, devfn, address[count], &tmp);
+					if (tmp & 0x01) // IO
+						pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFD);
+					else // MEMORY
+						pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+					*/
+					pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+					pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
+
+					debug ("what is bar[count]? %x, count = %d\n", bar[count], count);
+
+					if (!bar[count])	/* This BAR is not implemented */
+						continue;
+
+					//tmp_bar = bar[count];
+
+					debug ("count %d device %x function %x wants %x resources\n", count, device, function, bar[count]);
+
+					if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+						/* This is IO */
+						len[count] = bar[count] & 0xFFFFFFFC;
+						len[count] = ~len[count] + 1;
+						amount->io += len[count];
+					} else {
+						/* This is Memory */
+						if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+							/* pfmem */
+							len[count] = bar[count] & 0xFFFFFFF0;
+							len[count] = ~len[count] + 1;
+							amount->pfmem += len[count];
+							if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64)
+								/* takes up another dword */
+								count += 1;
+
+						} else {
+							/* regular memory */
+							len[count] = bar[count] & 0xFFFFFFF0;
+							len[count] = ~len[count] + 1;
+							amount->mem += len[count];
+							if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+								/* takes up another dword */
+								count += 1;
+							}
+						}
+					}
+				}	/* end for */
+			}	/* end if (valid) */
+		}	/* end for */
+	}	/* end for */
+
+	if (!howmany)
+		amount->not_correct = 1;
+	else
+		amount->not_correct = 0;
+	if ((amount->io) && (amount->io < IOBRIDGE))
+		amount->io = IOBRIDGE;
+	if ((amount->mem) && (amount->mem < MEMBRIDGE))
+		amount->mem = MEMBRIDGE;
+	if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE))
+		amount->pfmem = MEMBRIDGE;
+	return amount;
+}
+
+/* The following 3 unconfigure_boot_ routines deal with the case when we had the card
+ * upon bootup in the system, since we don't allocate func to such case, we need to read
+ * the start addresses from pci config space and then find the corresponding entries in
+ * our resource lists.  The functions return either 0, -ENODEV, or -1 (general failure)
+ * Change: we also call these functions even if we configured the card ourselves (i.e., not
+ * the bootup case), since it should work same way
+ */
+static int unconfigure_boot_device (u8 busno, u8 device, u8 function)
+{
+	u32 start_address;
+	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 resource_node *io;
+	struct resource_node *mem;
+	struct resource_node *pfmem;
+	struct bus_node *bus;
+	u32 end_address;
+	u32 temp_end;
+	u32 size;
+	u32 tmp_address;
+	unsigned int devfn;
+
+	debug ("%s - enter\n", __func__);
+
+	bus = ibmphp_find_res_bus (busno);
+	if (!bus) {
+		debug ("cannot find corresponding bus.\n");
+		return -EINVAL;
+	}
+
+	devfn = PCI_DEVFN(device, function);
+	ibmphp_pci_bus->number = busno;
+	for (count = 0; address[count]; count++) {	/* for 6 BARs */
+		pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address);
+
+		/* We can do this here, b/c by that time the device driver of the card has been stopped */
+
+		pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+		pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &size);
+		pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], start_address);
+
+		debug ("start_address is %x\n", start_address);
+		debug ("busno, device, function %x %x %x\n", busno, device, function);
+		if (!size) {
+			/* This BAR is not implemented */
+			debug ("is this bar no implemented?, count = %d\n", count);
+			continue;
+		}
+		tmp_address = start_address;
+		if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			start_address &= PCI_BASE_ADDRESS_IO_MASK;
+			size = size & 0xFFFFFFFC;
+			size = ~size + 1;
+			end_address = start_address + size - 1;
+			if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+				err ("cannot find corresponding IO resource to remove\n");
+				return -EIO;
+			}
+			debug ("io->start = %x\n", io->start);
+			temp_end = io->end;
+			start_address = io->end + 1;
+			ibmphp_remove_resource (io);
+			/* This is needed b/c of the old I/O restrictions in the BIOS */
+			while (temp_end < end_address) {
+				if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+					err ("cannot find corresponding IO resource to remove\n");
+					return -EIO;
+				}
+				debug ("io->start = %x\n", io->start);
+				temp_end = io->end;
+				start_address = io->end + 1;
+				ibmphp_remove_resource (io);
+			}
+
+			/* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
+		} else {
+			/* This is Memory */
+			if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+				debug ("start address of pfmem is %x\n", start_address);
+				start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+
+				if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
+					err ("cannot find corresponding PFMEM resource to remove\n");
+					return -EIO;
+				}
+				if (pfmem) {
+					debug ("pfmem->start = %x\n", pfmem->start);
+
+					ibmphp_remove_resource(pfmem);
+				}
+			} else {
+				/* regular memory */
+				debug ("start address of mem is %x\n", start_address);
+				start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+
+				if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
+					err ("cannot find corresponding MEM resource to remove\n");
+					return -EIO;
+				}
+				if (mem) {
+					debug ("mem->start = %x\n", mem->start);
+
+					ibmphp_remove_resource(mem);
+				}
+			}
+			if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+				/* takes up another dword */
+				count += 1;
+			}
+		}	/* end of mem */
+	}	/* end of for */
+
+	return 0;
+}
+
+static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
+{
+	int count;
+	int bus_no, pri_no, sub_no, sec_no = 0;
+	u32 start_address, tmp_address;
+	u8 sec_number, sub_number, pri_number;
+	struct resource_node *io = NULL;
+	struct resource_node *mem = NULL;
+	struct resource_node *pfmem = NULL;
+	struct bus_node *bus;
+	u32 address[] = {
+		PCI_BASE_ADDRESS_0,
+		PCI_BASE_ADDRESS_1,
+		0
+	};
+	unsigned int devfn;
+
+	devfn = PCI_DEVFN(device, function);
+	ibmphp_pci_bus->number = busno;
+	bus_no = (int) busno;
+	debug ("busno is %x\n", busno);
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number);
+	debug ("%s - busno = %x, primary_number = %x\n", __func__, busno, pri_number);
+
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+	debug ("sec_number is %x\n", sec_number);
+	sec_no = (int) sec_number;
+	pri_no = (int) pri_number;
+	if (pri_no != bus_no) {
+		err ("primary numbers in our structures and pci config space don't match.\n");
+		return -EINVAL;
+	}
+
+	pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sub_number);
+	sub_no = (int) sub_number;
+	debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no);
+	if (sec_no != sub_number) {
+		err ("there're more buses behind this bridge.  Hot removal is not supported.  Please choose another card\n");
+		return -ENODEV;
+	}
+
+	bus = ibmphp_find_res_bus (sec_number);
+	if (!bus) {
+		err ("cannot find Bus structure for the bridged device\n");
+		return -EINVAL;
+	}
+	debug("bus->busno is %x\n", bus->busno);
+	debug("sec_number is %x\n", sec_number);
+
+	ibmphp_remove_bus (bus, busno);
+
+	for (count = 0; address[count]; count++) {
+		/* for 2 BARs */
+		pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address);
+
+		if (!start_address) {
+			/* This BAR is not implemented */
+			continue;
+		}
+
+		tmp_address = start_address;
+
+		if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
+			/* This is IO */
+			start_address &= PCI_BASE_ADDRESS_IO_MASK;
+			if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+				err ("cannot find corresponding IO resource to remove\n");
+				return -EIO;
+			}
+			if (io)
+				debug ("io->start = %x\n", io->start);
+
+			ibmphp_remove_resource (io);
+
+			/* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
+		} else {
+			/* This is Memory */
+			if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+				/* pfmem */
+				start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+				if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
+					err ("cannot find corresponding PFMEM resource to remove\n");
+					return -EINVAL;
+				}
+				if (pfmem) {
+					debug ("pfmem->start = %x\n", pfmem->start);
+
+					ibmphp_remove_resource(pfmem);
+				}
+			} else {
+				/* regular memory */
+				start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+				if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
+					err ("cannot find corresponding MEM resource to remove\n");
+					return -EINVAL;
+				}
+				if (mem) {
+					debug ("mem->start = %x\n", mem->start);
+
+					ibmphp_remove_resource(mem);
+				}
+			}
+			if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+				/* takes up another dword */
+				count += 1;
+			}
+		}	/* end of mem */
+	}	/* end of for */
+	debug ("%s - exiting, returning success\n", __func__);
+	return 0;
+}
+
+static int unconfigure_boot_card (struct slot *slot_cur)
+{
+	u16 vendor_id;
+	u32 class;
+	u8 hdr_type;
+	u8 device;
+	u8 busno;
+	u8 function;
+	int rc;
+	unsigned int devfn;
+	u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */
+
+	debug ("%s - enter\n", __func__);
+
+	device = slot_cur->device;
+	busno = slot_cur->bus;
+
+	debug ("b4 for loop, device is %x\n", device);
+	/* For every function on the card */
+	for (function = 0x0; function < 0x08; function++) {
+		devfn = PCI_DEVFN(device, function);
+		ibmphp_pci_bus->number = busno;
+
+		pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+		if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+			/* found correct device!!! */
+			++valid_device;
+
+			debug ("%s - found correct device\n", __func__);
+
+			/* header: x x x x x x x x
+			 *         | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
+			 *         |_=> 0 = single function device, 1 = multi-function device
+			 */
+
+			pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+			pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class);
+
+			debug ("hdr_type %x, class %x\n", hdr_type, class);
+			class >>= 8;	/* to take revision out, class = class.subclass.prog i/f */
+			if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+				err ("The device %x function %x is VGA compatible and is not supported for hot removing.  Please choose another device.\n", device, function);
+				return -ENODEV;
+			} else if (class == PCI_CLASS_DISPLAY_VGA) {
+				err ("The device %x function %x is not supported for hot removing.  Please choose another device.\n", device, function);
+				return -ENODEV;
+			}
+
+			switch (hdr_type) {
+				case PCI_HEADER_TYPE_NORMAL:
+					rc = unconfigure_boot_device (busno, device, function);
+					if (rc) {
+						err ("was not able to unconfigure device %x func %x on bus %x. bailing out...\n",
+						     device, function, busno);
+						return rc;
+					}
+					function = 0x8;
+					break;
+				case PCI_HEADER_TYPE_MULTIDEVICE:
+					rc = unconfigure_boot_device (busno, device, function);
+					if (rc) {
+						err ("was not able to unconfigure device %x func %x on bus %x. bailing out...\n",
+						     device, function, busno);
+						return rc;
+					}
+					break;
+				case PCI_HEADER_TYPE_BRIDGE:
+					class >>= 8;
+					if (class != PCI_CLASS_BRIDGE_PCI) {
+						err ("This device %x function %x is not PCI-to-PCI bridge, and is not supported for hot-removing.  Please try another card.\n", device, function);
+						return -ENODEV;
+					}
+					rc = unconfigure_boot_bridge (busno, device, function);
+					if (rc != 0) {
+						err ("was not able to hot-remove PPB properly.\n");
+						return rc;
+					}
+
+					function = 0x8;
+					break;
+				case PCI_HEADER_TYPE_MULTIBRIDGE:
+					class >>= 8;
+					if (class != PCI_CLASS_BRIDGE_PCI) {
+						err ("This device %x function %x is not PCI-to-PCI bridge,  and is not supported for hot-removing.  Please try another card.\n", device, function);
+						return -ENODEV;
+					}
+					rc = unconfigure_boot_bridge (busno, device, function);
+					if (rc != 0) {
+						err ("was not able to hot-remove PPB properly.\n");
+						return rc;
+					}
+					break;
+				default:
+					err ("MAJOR PROBLEM!!!! Cannot read device's header\n");
+					return -1;
+					break;
+			}	/* end of switch */
+		}	/* end of valid device */
+	}	/* end of for */
+
+	if (!valid_device) {
+		err ("Could not find device to unconfigure.  Or could not read the card.\n");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * free the resources of the card (multi, single, or bridged)
+ * Parameters: slot, flag to say if this is for removing entire module or just
+ * unconfiguring the device
+ * TO DO:  will probably need to add some code in case there was some resource,
+ * to remove it... this is from when we have errors in the configure_card...
+ *			!!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
+ * Returns: 0, -1, -ENODEV
+ */
+int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
+{
+	int i;
+	int count;
+	int rc;
+	struct slot *sl = *slot_cur;
+	struct pci_func *cur_func = NULL;
+	struct pci_func *temp_func;
+
+	debug ("%s - enter\n", __func__);
+
+	if (!the_end) {
+		/* Need to unconfigure the card */
+		rc = unconfigure_boot_card (sl);
+		if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) {
+			/* In all other cases, will still need to get rid of func structure if it exists */
+			return rc;
+		}
+	}
+
+	if (sl->func) {
+		cur_func = sl->func;
+		while (cur_func) {
+			/* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */
+			if (cur_func->bus) {
+				/* in other words, it's a PPB */
+				count = 2;
+			} else {
+				count = 6;
+			}
+
+			for (i = 0; i < count; i++) {
+				if (cur_func->io[i]) {
+					debug ("io[%d] exists\n", i);
+					if (the_end > 0)
+						ibmphp_remove_resource (cur_func->io[i]);
+					cur_func->io[i] = NULL;
+				}
+				if (cur_func->mem[i]) {
+					debug ("mem[%d] exists\n", i);
+					if (the_end > 0)
+						ibmphp_remove_resource (cur_func->mem[i]);
+					cur_func->mem[i] = NULL;
+				}
+				if (cur_func->pfmem[i]) {
+					debug ("pfmem[%d] exists\n", i);
+					if (the_end > 0)
+						ibmphp_remove_resource (cur_func->pfmem[i]);
+					cur_func->pfmem[i] = NULL;
+				}
+			}
+
+			temp_func = cur_func->next;
+			kfree (cur_func);
+			cur_func = temp_func;
+		}
+	}
+
+	sl->func = NULL;
+	*slot_cur = sl;
+	debug ("%s - exit\n", __func__);
+	return 0;
+}
+
+/*
+ * add a new bus resulting from hot-plugging a PPB bridge with devices
+ *
+ * Input: bus and the amount of resources needed (we know we can assign those,
+ *        since they've been checked already
+ * Output: bus added to the correct spot
+ *         0, -1, error
+ */
+static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno)
+{
+	struct range_node *io_range = NULL;
+	struct range_node *mem_range = NULL;
+	struct range_node *pfmem_range = NULL;
+	struct bus_node *cur_bus = NULL;
+
+	/* Trying to find the parent bus number */
+	if (parent_busno != 0xFF) {
+		cur_bus	= ibmphp_find_res_bus (parent_busno);
+		if (!cur_bus) {
+			err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n");
+			return -ENODEV;
+		}
+
+		list_add (&bus->bus_list, &cur_bus->bus_list);
+	}
+	if (io) {
+		io_range = kzalloc(sizeof(*io_range), GFP_KERNEL);
+		if (!io_range) {
+			err ("out of system memory\n");
+			return -ENOMEM;
+		}
+		io_range->start = io->start;
+		io_range->end = io->end;
+		io_range->rangeno = 1;
+		bus->noIORanges = 1;
+		bus->rangeIO = io_range;
+	}
+	if (mem) {
+		mem_range = kzalloc(sizeof(*mem_range), GFP_KERNEL);
+		if (!mem_range) {
+			err ("out of system memory\n");
+			return -ENOMEM;
+		}
+		mem_range->start = mem->start;
+		mem_range->end = mem->end;
+		mem_range->rangeno = 1;
+		bus->noMemRanges = 1;
+		bus->rangeMem = mem_range;
+	}
+	if (pfmem) {
+		pfmem_range = kzalloc(sizeof(*pfmem_range), GFP_KERNEL);
+		if (!pfmem_range) {
+			err ("out of system memory\n");
+			return -ENOMEM;
+		}
+		pfmem_range->start = pfmem->start;
+		pfmem_range->end = pfmem->end;
+		pfmem_range->rangeno = 1;
+		bus->noPFMemRanges = 1;
+		bus->rangePFMem = pfmem_range;
+	}
+	return 0;
+}
+
+/*
+ * find the 1st available bus number for PPB to set as its secondary bus
+ * Parameters: bus_number of the primary bus
+ * Returns: bus_number of the secondary bus or 0xff in case of failure
+ */
+static u8 find_sec_number (u8 primary_busno, u8 slotno)
+{
+	int min, max;
+	u8 busno;
+	struct bus_info *bus;
+	struct bus_node *bus_cur;
+
+	bus = ibmphp_find_same_bus_num (primary_busno);
+	if (!bus) {
+		err ("cannot get slot range of the bus from the BIOS\n");
+		return 0xff;
+	}
+	max = bus->slot_max;
+	min = bus->slot_min;
+	if ((slotno > max) || (slotno < min)) {
+		err ("got the wrong range\n");
+		return 0xff;
+	}
+	busno = (u8) (slotno - (u8) min);
+	busno += primary_busno + 0x01;
+	bus_cur = ibmphp_find_res_bus (busno);
+	/* either there is no such bus number, or there are no ranges, which
+	 * can only happen if we removed the bridged device in previous load
+	 * of the driver, and now only have the skeleton bus struct
+	 */
+	if ((!bus_cur) || (!(bus_cur->rangeIO) && !(bus_cur->rangeMem) && !(bus_cur->rangePFMem)))
+		return busno;
+	return 0xff;
+}
diff --git a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c
new file mode 100644
index 0000000..f279060
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_res.c
@@ -0,0 +1,2158 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Irene Zubarev, IBM Corporation
+ *
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2002 IBM Corp.
+ *
+ * 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 <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include "ibmphp.h"
+
+static int flags = 0;		/* for testing */
+
+static void update_resources (struct bus_node *bus_cur, int type, int rangeno);
+static int once_over (void);
+static int remove_ranges (struct bus_node *, struct bus_node *);
+static int update_bridge_ranges (struct bus_node **);
+static int add_bus_range (int type, struct range_node *, struct bus_node *);
+static void fix_resources (struct bus_node *);
+static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
+
+static LIST_HEAD(gbuses);
+
+static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc *curr, u8 busno, int flag)
+{
+	struct bus_node *newbus;
+
+	if (!(curr) && !(flag)) {
+		err ("NULL pointer passed\n");
+		return NULL;
+	}
+
+	newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
+	if (!newbus) {
+		err ("out of system memory\n");
+		return NULL;
+	}
+
+	if (flag)
+		newbus->busno = busno;
+	else
+		newbus->busno = curr->bus_num;
+	list_add_tail (&newbus->bus_list, &gbuses);
+	return newbus;
+}
+
+static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc *curr)
+{
+	struct resource_node *rs;
+
+	if (!curr) {
+		err ("NULL passed to allocate\n");
+		return NULL;
+	}
+
+	rs = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+	if (!rs) {
+		err ("out of system memory\n");
+		return NULL;
+	}
+	rs->busno = curr->bus_num;
+	rs->devfunc = curr->dev_fun;
+	rs->start = curr->start_addr;
+	rs->end = curr->end_addr;
+	rs->len = curr->end_addr - curr->start_addr + 1;
+	return rs;
+}
+
+static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus)
+{
+	struct bus_node *newbus;
+	struct range_node *newrange;
+	u8 num_ranges = 0;
+
+	if (first_bus) {
+		newbus = kzalloc(sizeof(struct bus_node), GFP_KERNEL);
+		if (!newbus) {
+			err ("out of system memory.\n");
+			return -ENOMEM;
+		}
+		newbus->busno = curr->bus_num;
+	} else {
+		newbus = *new_bus;
+		switch (flag) {
+			case MEM:
+				num_ranges = newbus->noMemRanges;
+				break;
+			case PFMEM:
+				num_ranges = newbus->noPFMemRanges;
+				break;
+			case IO:
+				num_ranges = newbus->noIORanges;
+				break;
+		}
+	}
+
+	newrange = kzalloc(sizeof(struct range_node), GFP_KERNEL);
+	if (!newrange) {
+		if (first_bus)
+			kfree (newbus);
+		err ("out of system memory\n");
+		return -ENOMEM;
+	}
+	newrange->start = curr->start_addr;
+	newrange->end = curr->end_addr;
+
+	if (first_bus || (!num_ranges))
+		newrange->rangeno = 1;
+	else {
+		/* need to insert our range */
+		add_bus_range (flag, newrange, newbus);
+		debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
+	}
+
+	switch (flag) {
+		case MEM:
+			newbus->rangeMem = newrange;
+			if (first_bus)
+				newbus->noMemRanges = 1;
+			else {
+				debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				++newbus->noMemRanges;
+				fix_resources (newbus);
+			}
+			break;
+		case IO:
+			newbus->rangeIO = newrange;
+			if (first_bus)
+				newbus->noIORanges = 1;
+			else {
+				debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				++newbus->noIORanges;
+				fix_resources (newbus);
+			}
+			break;
+		case PFMEM:
+			newbus->rangePFMem = newrange;
+			if (first_bus)
+				newbus->noPFMemRanges = 1;
+			else {
+				debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				++newbus->noPFMemRanges;
+				fix_resources (newbus);
+			}
+
+			break;
+	}
+
+	*new_bus = newbus;
+	*new_range = newrange;
+	return 0;
+}
+
+
+/* Notes:
+ * 1. The ranges are ordered.  The buses are not ordered.  (First come)
+ *
+ * 2. If cannot allocate out of PFMem range, allocate from Mem ranges.  PFmemFromMem
+ * are not sorted. (no need since use mem node). To not change the entire code, we
+ * also add mem node whenever this case happens so as not to change
+ * ibmphp_check_mem_resource etc (and since it really is taking Mem resource)
+ */
+
+/*****************************************************************************
+ * This is the Resource Management initialization function.  It will go through
+ * the Resource list taken from EBDA and fill in this module's data structures
+ *
+ * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
+ * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
+ *
+ * Input: ptr to the head of the resource list from EBDA
+ * Output: 0, -1 or error codes
+ ***************************************************************************/
+int __init ibmphp_rsrc_init (void)
+{
+	struct ebda_pci_rsrc *curr;
+	struct range_node *newrange = NULL;
+	struct bus_node *newbus = NULL;
+	struct bus_node *bus_cur;
+	struct bus_node *bus_prev;
+	struct list_head *tmp;
+	struct resource_node *new_io = NULL;
+	struct resource_node *new_mem = NULL;
+	struct resource_node *new_pfmem = NULL;
+	int rc;
+	struct list_head *tmp_ebda;
+
+	list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) {
+		curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+		if (!(curr->rsrc_type & PCIDEVMASK)) {
+			/* EBDA still lists non PCI devices, so ignore... */
+			debug ("this is not a PCI DEVICE in rsrc_init, please take care\n");
+			// continue;
+		}
+
+		/* this is a primary bus resource */
+		if (curr->rsrc_type & PRIMARYBUSMASK) {
+			/* memory */
+			if ((curr->rsrc_type & RESTYPE) == MMASK) {
+				/* no bus structure exists in place yet */
+				if (list_empty (&gbuses)) {
+					rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
+					if (rc)
+						return rc;
+					list_add_tail (&newbus->bus_list, &gbuses);
+					debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				} else {
+					bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+					/* found our bus */
+					if (bus_cur) {
+						rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0);
+						if (rc)
+							return rc;
+					} else {
+						/* went through all the buses and didn't find ours, need to create a new bus node */
+						rc = alloc_bus_range(&newbus, &newrange, curr, MEM, 1);
+						if (rc)
+							return rc;
+
+						list_add_tail (&newbus->bus_list, &gbuses);
+						debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+					}
+				}
+			} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
+				/* prefetchable memory */
+				if (list_empty (&gbuses)) {
+					/* no bus structure exists in place yet */
+					rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
+					if (rc)
+						return rc;
+					list_add_tail (&newbus->bus_list, &gbuses);
+					debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				} else {
+					bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+					if (bus_cur) {
+						/* found our bus */
+						rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0);
+						if (rc)
+							return rc;
+					} else {
+						/* went through all the buses and didn't find ours, need to create a new bus node */
+						rc = alloc_bus_range(&newbus, &newrange, curr, PFMEM, 1);
+						if (rc)
+							return rc;
+						list_add_tail (&newbus->bus_list, &gbuses);
+						debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+					}
+				}
+			} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
+				/* IO */
+				if (list_empty (&gbuses)) {
+					/* no bus structure exists in place yet */
+					rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
+					if (rc)
+						return rc;
+					list_add_tail (&newbus->bus_list, &gbuses);
+					debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+				} else {
+					bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+					if (bus_cur) {
+						rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0);
+						if (rc)
+							return rc;
+					} else {
+						/* went through all the buses and didn't find ours, need to create a new bus node */
+						rc = alloc_bus_range(&newbus, &newrange, curr, IO, 1);
+						if (rc)
+							return rc;
+						list_add_tail (&newbus->bus_list, &gbuses);
+						debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+					}
+				}
+
+			} else {
+				;	/* type is reserved  WHAT TO DO IN THIS CASE???
+					   NOTHING TO DO??? */
+			}
+		} else {
+			/* regular pci device resource */
+			if ((curr->rsrc_type & RESTYPE) == MMASK) {
+				/* Memory resource */
+				new_mem = alloc_resources (curr);
+				if (!new_mem)
+					return -ENOMEM;
+				new_mem->type = MEM;
+				/*
+				 * if it didn't find the bus, means PCI dev
+				 * came b4 the Primary Bus info, so need to
+				 * create a bus rangeno becomes a problem...
+				 * assign a -1 and then update once the range
+				 * actually appears...
+				 */
+				if (ibmphp_add_resource (new_mem) < 0) {
+					newbus = alloc_error_bus (curr, 0, 0);
+					if (!newbus)
+						return -ENOMEM;
+					newbus->firstMem = new_mem;
+					++newbus->needMemUpdate;
+					new_mem->rangeno = -1;
+				}
+				debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end);
+
+			} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
+				/* PFMemory resource */
+				new_pfmem = alloc_resources (curr);
+				if (!new_pfmem)
+					return -ENOMEM;
+				new_pfmem->type = PFMEM;
+				new_pfmem->fromMem = 0;
+				if (ibmphp_add_resource (new_pfmem) < 0) {
+					newbus = alloc_error_bus (curr, 0, 0);
+					if (!newbus)
+						return -ENOMEM;
+					newbus->firstPFMem = new_pfmem;
+					++newbus->needPFMemUpdate;
+					new_pfmem->rangeno = -1;
+				}
+
+				debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end);
+			} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
+				/* IO resource */
+				new_io = alloc_resources (curr);
+				if (!new_io)
+					return -ENOMEM;
+				new_io->type = IO;
+
+				/*
+				 * if it didn't find the bus, means PCI dev
+				 * came b4 the Primary Bus info, so need to
+				 * create a bus rangeno becomes a problem...
+				 * Can assign a -1 and then update once the
+				 * range actually appears...
+				 */
+				if (ibmphp_add_resource (new_io) < 0) {
+					newbus = alloc_error_bus (curr, 0, 0);
+					if (!newbus)
+						return -ENOMEM;
+					newbus->firstIO = new_io;
+					++newbus->needIOUpdate;
+					new_io->rangeno = -1;
+				}
+				debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end);
+			}
+		}
+	}
+
+	list_for_each (tmp, &gbuses) {
+		bus_cur = list_entry (tmp, struct bus_node, bus_list);
+		/* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
+		rc = update_bridge_ranges (&bus_cur);
+		if (rc)
+			return rc;
+	}
+	return once_over ();	/* This is to align ranges (so no -1) */
+}
+
+/********************************************************************************
+ * This function adds a range into a sorted list of ranges per bus for a particular
+ * range type, it then calls another routine to update the range numbers on the
+ * pci devices' resources for the appropriate resource
+ *
+ * Input: type of the resource, range to add, current bus
+ * Output: 0 or -1, bus and range ptrs
+ ********************************************************************************/
+static int add_bus_range (int type, struct range_node *range, struct bus_node *bus_cur)
+{
+	struct range_node *range_cur = NULL;
+	struct range_node *range_prev;
+	int count = 0, i_init;
+	int noRanges = 0;
+
+	switch (type) {
+		case MEM:
+			range_cur = bus_cur->rangeMem;
+			noRanges = bus_cur->noMemRanges;
+			break;
+		case PFMEM:
+			range_cur = bus_cur->rangePFMem;
+			noRanges = bus_cur->noPFMemRanges;
+			break;
+		case IO:
+			range_cur = bus_cur->rangeIO;
+			noRanges = bus_cur->noIORanges;
+			break;
+	}
+
+	range_prev = NULL;
+	while (range_cur) {
+		if (range->start < range_cur->start)
+			break;
+		range_prev = range_cur;
+		range_cur = range_cur->next;
+		count = count + 1;
+	}
+	if (!count) {
+		/* our range will go at the beginning of the list */
+		switch (type) {
+			case MEM:
+				bus_cur->rangeMem = range;
+				break;
+			case PFMEM:
+				bus_cur->rangePFMem = range;
+				break;
+			case IO:
+				bus_cur->rangeIO = range;
+				break;
+		}
+		range->next = range_cur;
+		range->rangeno = 1;
+		i_init = 0;
+	} else if (!range_cur) {
+		/* our range will go at the end of the list */
+		range->next = NULL;
+		range_prev->next = range;
+		range->rangeno = range_prev->rangeno + 1;
+		return 0;
+	} else {
+		/* the range is in the middle */
+		range_prev->next = range;
+		range->next = range_cur;
+		range->rangeno = range_cur->rangeno;
+		i_init = range_prev->rangeno;
+	}
+
+	for (count = i_init; count < noRanges; ++count) {
+		++range_cur->rangeno;
+		range_cur = range_cur->next;
+	}
+
+	update_resources (bus_cur, type, i_init + 1);
+	return 0;
+}
+
+/*******************************************************************************
+ * This routine goes through the list of resources of type 'type' and updates
+ * the range numbers that they correspond to.  It was called from add_bus_range fnc
+ *
+ * Input: bus, type of the resource, the rangeno starting from which to update
+ ******************************************************************************/
+static void update_resources (struct bus_node *bus_cur, int type, int rangeno)
+{
+	struct resource_node *res = NULL;
+	u8 eol = 0;	/* end of list indicator */
+
+	switch (type) {
+		case MEM:
+			if (bus_cur->firstMem)
+				res = bus_cur->firstMem;
+			break;
+		case PFMEM:
+			if (bus_cur->firstPFMem)
+				res = bus_cur->firstPFMem;
+			break;
+		case IO:
+			if (bus_cur->firstIO)
+				res = bus_cur->firstIO;
+			break;
+	}
+
+	if (res) {
+		while (res) {
+			if (res->rangeno == rangeno)
+				break;
+			if (res->next)
+				res = res->next;
+			else if (res->nextRange)
+				res = res->nextRange;
+			else {
+				eol = 1;
+				break;
+			}
+		}
+
+		if (!eol) {
+			/* found the range */
+			while (res) {
+				++res->rangeno;
+				res = res->next;
+			}
+		}
+	}
+}
+
+static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range)
+{
+	char * str = "";
+	switch (res->type) {
+		case IO:
+			str = "io";
+			break;
+		case MEM:
+			str = "mem";
+			break;
+		case PFMEM:
+			str = "pfmem";
+			break;
+	}
+
+	while (res) {
+		if (res->rangeno == -1) {
+			while (range) {
+				if ((res->start >= range->start) && (res->end <= range->end)) {
+					res->rangeno = range->rangeno;
+					debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno);
+					switch (res->type) {
+						case IO:
+							--bus_cur->needIOUpdate;
+							break;
+						case MEM:
+							--bus_cur->needMemUpdate;
+							break;
+						case PFMEM:
+							--bus_cur->needPFMemUpdate;
+							break;
+					}
+					break;
+				}
+				range = range->next;
+			}
+		}
+		if (res->next)
+			res = res->next;
+		else
+			res = res->nextRange;
+	}
+
+}
+
+/*****************************************************************************
+ * This routine reassigns the range numbers to the resources that had a -1
+ * This case can happen only if upon initialization, resources taken by pci dev
+ * appear in EBDA before the resources allocated for that bus, since we don't
+ * know the range, we assign -1, and this routine is called after a new range
+ * is assigned to see the resources with unknown range belong to the added range
+ *
+ * Input: current bus
+ * Output: none, list of resources for that bus are fixed if can be
+ *******************************************************************************/
+static void fix_resources (struct bus_node *bus_cur)
+{
+	struct range_node *range;
+	struct resource_node *res;
+
+	debug ("%s - bus_cur->busno = %d\n", __func__, bus_cur->busno);
+
+	if (bus_cur->needIOUpdate) {
+		res = bus_cur->firstIO;
+		range = bus_cur->rangeIO;
+		fix_me (res, bus_cur, range);
+	}
+	if (bus_cur->needMemUpdate) {
+		res = bus_cur->firstMem;
+		range = bus_cur->rangeMem;
+		fix_me (res, bus_cur, range);
+	}
+	if (bus_cur->needPFMemUpdate) {
+		res = bus_cur->firstPFMem;
+		range = bus_cur->rangePFMem;
+		fix_me (res, bus_cur, range);
+	}
+}
+
+/*******************************************************************************
+ * This routine adds a resource to the list of resources to the appropriate bus
+ * based on their resource type and sorted by their starting addresses.  It assigns
+ * the ptrs to next and nextRange if needed.
+ *
+ * Input: resource ptr
+ * Output: ptrs assigned (to the node)
+ * 0 or -1
+ *******************************************************************************/
+int ibmphp_add_resource (struct resource_node *res)
+{
+	struct resource_node *res_cur;
+	struct resource_node *res_prev;
+	struct bus_node *bus_cur;
+	struct range_node *range_cur = NULL;
+	struct resource_node *res_start = NULL;
+
+	debug ("%s - enter\n", __func__);
+
+	if (!res) {
+		err ("NULL passed to add\n");
+		return -ENODEV;
+	}
+
+	bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+	if (!bus_cur) {
+		/* didn't find a bus, something's wrong!!! */
+		debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
+		return -ENODEV;
+	}
+
+	/* Normal case */
+	switch (res->type) {
+		case IO:
+			range_cur = bus_cur->rangeIO;
+			res_start = bus_cur->firstIO;
+			break;
+		case MEM:
+			range_cur = bus_cur->rangeMem;
+			res_start = bus_cur->firstMem;
+			break;
+		case PFMEM:
+			range_cur = bus_cur->rangePFMem;
+			res_start = bus_cur->firstPFMem;
+			break;
+		default:
+			err ("cannot read the type of the resource to add... problem\n");
+			return -EINVAL;
+	}
+	while (range_cur) {
+		if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) {
+			res->rangeno = range_cur->rangeno;
+			break;
+		}
+		range_cur = range_cur->next;
+	}
+
+	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	 * this is again the case of rangeno = -1
+	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	 */
+
+	if (!range_cur) {
+		switch (res->type) {
+			case IO:
+				++bus_cur->needIOUpdate;
+				break;
+			case MEM:
+				++bus_cur->needMemUpdate;
+				break;
+			case PFMEM:
+				++bus_cur->needPFMemUpdate;
+				break;
+		}
+		res->rangeno = -1;
+	}
+
+	debug ("The range is %d\n", res->rangeno);
+	if (!res_start) {
+		/* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
+		switch (res->type) {
+			case IO:
+				bus_cur->firstIO = res;
+				break;
+			case MEM:
+				bus_cur->firstMem = res;
+				break;
+			case PFMEM:
+				bus_cur->firstPFMem = res;
+				break;
+		}
+		res->next = NULL;
+		res->nextRange = NULL;
+	} else {
+		res_cur = res_start;
+		res_prev = NULL;
+
+		debug ("res_cur->rangeno is %d\n", res_cur->rangeno);
+
+		while (res_cur) {
+			if (res_cur->rangeno >= res->rangeno)
+				break;
+			res_prev = res_cur;
+			if (res_cur->next)
+				res_cur = res_cur->next;
+			else
+				res_cur = res_cur->nextRange;
+		}
+
+		if (!res_cur) {
+			/* at the end of the resource list */
+			debug ("i should be here, [%x - %x]\n", res->start, res->end);
+			res_prev->nextRange = res;
+			res->next = NULL;
+			res->nextRange = NULL;
+		} else if (res_cur->rangeno == res->rangeno) {
+			/* in the same range */
+			while (res_cur) {
+				if (res->start < res_cur->start)
+					break;
+				res_prev = res_cur;
+				res_cur = res_cur->next;
+			}
+			if (!res_cur) {
+				/* the last resource in this range */
+				res_prev->next = res;
+				res->next = NULL;
+				res->nextRange = res_prev->nextRange;
+				res_prev->nextRange = NULL;
+			} else if (res->start < res_cur->start) {
+				/* at the beginning or middle of the range */
+				if (!res_prev)	{
+					switch (res->type) {
+						case IO:
+							bus_cur->firstIO = res;
+							break;
+						case MEM:
+							bus_cur->firstMem = res;
+							break;
+						case PFMEM:
+							bus_cur->firstPFMem = res;
+							break;
+					}
+				} else if (res_prev->rangeno == res_cur->rangeno)
+					res_prev->next = res;
+				else
+					res_prev->nextRange = res;
+
+				res->next = res_cur;
+				res->nextRange = NULL;
+			}
+		} else {
+			/* this is the case where it is 1st occurrence of the range */
+			if (!res_prev) {
+				/* at the beginning of the resource list */
+				res->next = NULL;
+				switch (res->type) {
+					case IO:
+						res->nextRange = bus_cur->firstIO;
+						bus_cur->firstIO = res;
+						break;
+					case MEM:
+						res->nextRange = bus_cur->firstMem;
+						bus_cur->firstMem = res;
+						break;
+					case PFMEM:
+						res->nextRange = bus_cur->firstPFMem;
+						bus_cur->firstPFMem = res;
+						break;
+				}
+			} else if (res_cur->rangeno > res->rangeno) {
+				/* in the middle of the resource list */
+				res_prev->nextRange = res;
+				res->next = NULL;
+				res->nextRange = res_cur;
+			}
+		}
+	}
+
+	debug ("%s - exit\n", __func__);
+	return 0;
+}
+
+/****************************************************************************
+ * This routine will remove the resource from the list of resources
+ *
+ * Input: io, mem, and/or pfmem resource to be deleted
+ * Output: modified resource list
+ *        0 or error code
+ ****************************************************************************/
+int ibmphp_remove_resource (struct resource_node *res)
+{
+	struct bus_node *bus_cur;
+	struct resource_node *res_cur = NULL;
+	struct resource_node *res_prev;
+	struct resource_node *mem_cur;
+	char * type = "";
+
+	if (!res)  {
+		err ("resource to remove is NULL\n");
+		return -ENODEV;
+	}
+
+	bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+	if (!bus_cur) {
+		err ("cannot find corresponding bus of the io resource to remove  bailing out...\n");
+		return -ENODEV;
+	}
+
+	switch (res->type) {
+		case IO:
+			res_cur = bus_cur->firstIO;
+			type = "io";
+			break;
+		case MEM:
+			res_cur = bus_cur->firstMem;
+			type = "mem";
+			break;
+		case PFMEM:
+			res_cur = bus_cur->firstPFMem;
+			type = "pfmem";
+			break;
+		default:
+			err ("unknown type for resource to remove\n");
+			return -EINVAL;
+	}
+	res_prev = NULL;
+
+	while (res_cur) {
+		if ((res_cur->start == res->start) && (res_cur->end == res->end))
+			break;
+		res_prev = res_cur;
+		if (res_cur->next)
+			res_cur = res_cur->next;
+		else
+			res_cur = res_cur->nextRange;
+	}
+
+	if (!res_cur) {
+		if (res->type == PFMEM) {
+			/*
+			 * case where pfmem might be in the PFMemFromMem list
+			 * so will also need to remove the corresponding mem
+			 * entry
+			 */
+			res_cur = bus_cur->firstPFMemFromMem;
+			res_prev = NULL;
+
+			while (res_cur) {
+				if ((res_cur->start == res->start) && (res_cur->end == res->end)) {
+					mem_cur = bus_cur->firstMem;
+					while (mem_cur) {
+						if ((mem_cur->start == res_cur->start)
+						    && (mem_cur->end == res_cur->end))
+							break;
+						if (mem_cur->next)
+							mem_cur = mem_cur->next;
+						else
+							mem_cur = mem_cur->nextRange;
+					}
+					if (!mem_cur) {
+						err ("cannot find corresponding mem node for pfmem...\n");
+						return -EINVAL;
+					}
+
+					ibmphp_remove_resource (mem_cur);
+					if (!res_prev)
+						bus_cur->firstPFMemFromMem = res_cur->next;
+					else
+						res_prev->next = res_cur->next;
+					kfree (res_cur);
+					return 0;
+				}
+				res_prev = res_cur;
+				if (res_cur->next)
+					res_cur = res_cur->next;
+				else
+					res_cur = res_cur->nextRange;
+			}
+			if (!res_cur) {
+				err ("cannot find pfmem to delete...\n");
+				return -EINVAL;
+			}
+		} else {
+			err ("the %s resource is not in the list to be deleted...\n", type);
+			return -EINVAL;
+		}
+	}
+	if (!res_prev) {
+		/* first device to be deleted */
+		if (res_cur->next) {
+			switch (res->type) {
+				case IO:
+					bus_cur->firstIO = res_cur->next;
+					break;
+				case MEM:
+					bus_cur->firstMem = res_cur->next;
+					break;
+				case PFMEM:
+					bus_cur->firstPFMem = res_cur->next;
+					break;
+			}
+		} else if (res_cur->nextRange) {
+			switch (res->type) {
+				case IO:
+					bus_cur->firstIO = res_cur->nextRange;
+					break;
+				case MEM:
+					bus_cur->firstMem = res_cur->nextRange;
+					break;
+				case PFMEM:
+					bus_cur->firstPFMem = res_cur->nextRange;
+					break;
+			}
+		} else {
+			switch (res->type) {
+				case IO:
+					bus_cur->firstIO = NULL;
+					break;
+				case MEM:
+					bus_cur->firstMem = NULL;
+					break;
+				case PFMEM:
+					bus_cur->firstPFMem = NULL;
+					break;
+			}
+		}
+		kfree (res_cur);
+		return 0;
+	} else {
+		if (res_cur->next) {
+			if (res_prev->rangeno == res_cur->rangeno)
+				res_prev->next = res_cur->next;
+			else
+				res_prev->nextRange = res_cur->next;
+		} else if (res_cur->nextRange) {
+			res_prev->next = NULL;
+			res_prev->nextRange = res_cur->nextRange;
+		} else {
+			res_prev->next = NULL;
+			res_prev->nextRange = NULL;
+		}
+		kfree (res_cur);
+		return 0;
+	}
+
+	return 0;
+}
+
+static struct range_node *find_range (struct bus_node *bus_cur, struct resource_node *res)
+{
+	struct range_node *range = NULL;
+
+	switch (res->type) {
+		case IO:
+			range = bus_cur->rangeIO;
+			break;
+		case MEM:
+			range = bus_cur->rangeMem;
+			break;
+		case PFMEM:
+			range = bus_cur->rangePFMem;
+			break;
+		default:
+			err ("cannot read resource type in find_range\n");
+	}
+
+	while (range) {
+		if (res->rangeno == range->rangeno)
+			break;
+		range = range->next;
+	}
+	return range;
+}
+
+/*****************************************************************************
+ * This routine will check to make sure the io/mem/pfmem->len that the device asked for
+ * can fit w/i our list of available IO/MEM/PFMEM resources.  If cannot, returns -EINVAL,
+ * otherwise, returns 0
+ *
+ * Input: resource
+ * Output: the correct start and end address are inputted into the resource node,
+ *        0 or -EINVAL
+ *****************************************************************************/
+int ibmphp_check_resource (struct resource_node *res, u8 bridge)
+{
+	struct bus_node *bus_cur;
+	struct range_node *range = NULL;
+	struct resource_node *res_prev;
+	struct resource_node *res_cur = NULL;
+	u32 len_cur = 0, start_cur = 0, len_tmp = 0;
+	int noranges = 0;
+	u32 tmp_start;		/* this is to make sure start address is divisible by the length needed */
+	u32 tmp_divide;
+	u8 flag = 0;
+
+	if (!res)
+		return -EINVAL;
+
+	if (bridge) {
+		/* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
+		if (res->type == IO)
+			tmp_divide = IOBRIDGE;
+		else
+			tmp_divide = MEMBRIDGE;
+	} else
+		tmp_divide = res->len;
+
+	bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+	if (!bus_cur) {
+		/* didn't find a bus, something's wrong!!! */
+		debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
+		return -EINVAL;
+	}
+
+	debug ("%s - enter\n", __func__);
+	debug ("bus_cur->busno is %d\n", bus_cur->busno);
+
+	/* This is a quick fix to not mess up with the code very much.  i.e.,
+	 * 2000-2fff, len = 1000, but when we compare, we need it to be fff */
+	res->len -= 1;
+
+	switch (res->type) {
+		case IO:
+			res_cur = bus_cur->firstIO;
+			noranges = bus_cur->noIORanges;
+			break;
+		case MEM:
+			res_cur = bus_cur->firstMem;
+			noranges = bus_cur->noMemRanges;
+			break;
+		case PFMEM:
+			res_cur = bus_cur->firstPFMem;
+			noranges = bus_cur->noPFMemRanges;
+			break;
+		default:
+			err ("wrong type of resource to check\n");
+			return -EINVAL;
+	}
+	res_prev = NULL;
+
+	while (res_cur) {
+		range = find_range (bus_cur, res_cur);
+		debug ("%s - rangeno = %d\n", __func__, res_cur->rangeno);
+
+		if (!range) {
+			err ("no range for the device exists... bailing out...\n");
+			return -EINVAL;
+		}
+
+		/* found our range */
+		if (!res_prev) {
+			/* first time in the loop */
+			len_tmp = res_cur->start - 1 - range->start;
+
+			if ((res_cur->start != range->start) && (len_tmp >= res->len)) {
+				debug ("len_tmp = %x\n", len_tmp);
+
+				if ((len_tmp < len_cur) || (len_cur == 0)) {
+
+					if ((range->start % tmp_divide) == 0) {
+						/* just perfect, starting address is divisible by length */
+						flag = 1;
+						len_cur = len_tmp;
+						start_cur = range->start;
+					} else {
+						/* Needs adjusting */
+						tmp_start = range->start;
+						flag = 0;
+
+						while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+							if ((tmp_start % tmp_divide) == 0) {
+								flag = 1;
+								len_cur = len_tmp;
+								start_cur = tmp_start;
+								break;
+							}
+							tmp_start += tmp_divide - tmp_start % tmp_divide;
+							if (tmp_start >= res_cur->start - 1)
+								break;
+						}
+					}
+
+					if (flag && len_cur == res->len) {
+						debug ("but we are not here, right?\n");
+						res->start = start_cur;
+						res->len += 1; /* To restore the balance */
+						res->end = res->start + res->len - 1;
+						return 0;
+					}
+				}
+			}
+		}
+		if (!res_cur->next) {
+			/* last device on the range */
+			len_tmp = range->end - (res_cur->end + 1);
+
+			if ((range->end != res_cur->end) && (len_tmp >= res->len)) {
+				debug ("len_tmp = %x\n", len_tmp);
+				if ((len_tmp < len_cur) || (len_cur == 0)) {
+
+					if (((res_cur->end + 1) % tmp_divide) == 0) {
+						/* just perfect, starting address is divisible by length */
+						flag = 1;
+						len_cur = len_tmp;
+						start_cur = res_cur->end + 1;
+					} else {
+						/* Needs adjusting */
+						tmp_start = res_cur->end + 1;
+						flag = 0;
+
+						while ((len_tmp = range->end - tmp_start) >= res->len) {
+							if ((tmp_start % tmp_divide) == 0) {
+								flag = 1;
+								len_cur = len_tmp;
+								start_cur = tmp_start;
+								break;
+							}
+							tmp_start += tmp_divide - tmp_start % tmp_divide;
+							if (tmp_start >= range->end)
+								break;
+						}
+					}
+					if (flag && len_cur == res->len) {
+						res->start = start_cur;
+						res->len += 1; /* To restore the balance */
+						res->end = res->start + res->len - 1;
+						return 0;
+					}
+				}
+			}
+		}
+
+		if (res_prev) {
+			if (res_prev->rangeno != res_cur->rangeno) {
+				/* 1st device on this range */
+				len_tmp = res_cur->start - 1 - range->start;
+
+				if ((res_cur->start != range->start) &&	(len_tmp >= res->len)) {
+					if ((len_tmp < len_cur) || (len_cur == 0)) {
+						if ((range->start % tmp_divide) == 0) {
+							/* just perfect, starting address is divisible by length */
+							flag = 1;
+							len_cur = len_tmp;
+							start_cur = range->start;
+						} else {
+							/* Needs adjusting */
+							tmp_start = range->start;
+							flag = 0;
+
+							while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+								if ((tmp_start % tmp_divide) == 0) {
+									flag = 1;
+									len_cur = len_tmp;
+									start_cur = tmp_start;
+									break;
+								}
+								tmp_start += tmp_divide - tmp_start % tmp_divide;
+								if (tmp_start >= res_cur->start - 1)
+									break;
+							}
+						}
+
+						if (flag && len_cur == res->len) {
+							res->start = start_cur;
+							res->len += 1; /* To restore the balance */
+							res->end = res->start + res->len - 1;
+							return 0;
+						}
+					}
+				}
+			} else {
+				/* in the same range */
+				len_tmp = res_cur->start - 1 - res_prev->end - 1;
+
+				if (len_tmp >= res->len) {
+					if ((len_tmp < len_cur) || (len_cur == 0)) {
+						if (((res_prev->end + 1) % tmp_divide) == 0) {
+							/* just perfect, starting address's divisible by length */
+							flag = 1;
+							len_cur = len_tmp;
+							start_cur = res_prev->end + 1;
+						} else {
+							/* Needs adjusting */
+							tmp_start = res_prev->end + 1;
+							flag = 0;
+
+							while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+								if ((tmp_start % tmp_divide) == 0) {
+									flag = 1;
+									len_cur = len_tmp;
+									start_cur = tmp_start;
+									break;
+								}
+								tmp_start += tmp_divide - tmp_start % tmp_divide;
+								if (tmp_start >= res_cur->start - 1)
+									break;
+							}
+						}
+
+						if (flag && len_cur == res->len) {
+							res->start = start_cur;
+							res->len += 1; /* To restore the balance */
+							res->end = res->start + res->len - 1;
+							return 0;
+						}
+					}
+				}
+			}
+		}
+		/* end if (res_prev) */
+		res_prev = res_cur;
+		if (res_cur->next)
+			res_cur = res_cur->next;
+		else
+			res_cur = res_cur->nextRange;
+	}	/* end of while */
+
+
+	if (!res_prev) {
+		/* 1st device ever */
+		/* need to find appropriate range */
+		switch (res->type) {
+			case IO:
+				range = bus_cur->rangeIO;
+				break;
+			case MEM:
+				range = bus_cur->rangeMem;
+				break;
+			case PFMEM:
+				range = bus_cur->rangePFMem;
+				break;
+		}
+		while (range) {
+			len_tmp = range->end - range->start;
+
+			if (len_tmp >= res->len) {
+				if ((len_tmp < len_cur) || (len_cur == 0)) {
+					if ((range->start % tmp_divide) == 0) {
+						/* just perfect, starting address's divisible by length */
+						flag = 1;
+						len_cur = len_tmp;
+						start_cur = range->start;
+					} else {
+						/* Needs adjusting */
+						tmp_start = range->start;
+						flag = 0;
+
+						while ((len_tmp = range->end - tmp_start) >= res->len) {
+							if ((tmp_start % tmp_divide) == 0) {
+								flag = 1;
+								len_cur = len_tmp;
+								start_cur = tmp_start;
+								break;
+							}
+							tmp_start += tmp_divide - tmp_start % tmp_divide;
+							if (tmp_start >= range->end)
+								break;
+						}
+					}
+
+					if (flag && len_cur == res->len) {
+						res->start = start_cur;
+						res->len += 1; /* To restore the balance */
+						res->end = res->start + res->len - 1;
+						return 0;
+					}
+				}
+			}
+			range = range->next;
+		}		/* end of while */
+
+		if ((!range) && (len_cur == 0)) {
+			/* have gone through the list of devices and ranges and haven't found n.e.thing */
+			err ("no appropriate range.. bailing out...\n");
+			return -EINVAL;
+		} else if (len_cur) {
+			res->start = start_cur;
+			res->len += 1; /* To restore the balance */
+			res->end = res->start + res->len - 1;
+			return 0;
+		}
+	}
+
+	if (!res_cur) {
+		debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
+		if (res_prev->rangeno < noranges) {
+			/* if there're more ranges out there to check */
+			switch (res->type) {
+				case IO:
+					range = bus_cur->rangeIO;
+					break;
+				case MEM:
+					range = bus_cur->rangeMem;
+					break;
+				case PFMEM:
+					range = bus_cur->rangePFMem;
+					break;
+			}
+			while (range) {
+				len_tmp = range->end - range->start;
+
+				if (len_tmp >= res->len) {
+					if ((len_tmp < len_cur) || (len_cur == 0)) {
+						if ((range->start % tmp_divide) == 0) {
+							/* just perfect, starting address's divisible by length */
+							flag = 1;
+							len_cur = len_tmp;
+							start_cur = range->start;
+						} else {
+							/* Needs adjusting */
+							tmp_start = range->start;
+							flag = 0;
+
+							while ((len_tmp = range->end - tmp_start) >= res->len) {
+								if ((tmp_start % tmp_divide) == 0) {
+									flag = 1;
+									len_cur = len_tmp;
+									start_cur = tmp_start;
+									break;
+								}
+								tmp_start += tmp_divide - tmp_start % tmp_divide;
+								if (tmp_start >= range->end)
+									break;
+							}
+						}
+
+						if (flag && len_cur == res->len) {
+							res->start = start_cur;
+							res->len += 1; /* To restore the balance */
+							res->end = res->start + res->len - 1;
+							return 0;
+						}
+					}
+				}
+				range = range->next;
+			}	/* end of while */
+
+			if ((!range) && (len_cur == 0)) {
+				/* have gone through the list of devices and ranges and haven't found n.e.thing */
+				err ("no appropriate range.. bailing out...\n");
+				return -EINVAL;
+			} else if (len_cur) {
+				res->start = start_cur;
+				res->len += 1; /* To restore the balance */
+				res->end = res->start + res->len - 1;
+				return 0;
+			}
+		} else {
+			/* no more ranges to check on */
+			if (len_cur) {
+				res->start = start_cur;
+				res->len += 1; /* To restore the balance */
+				res->end = res->start + res->len - 1;
+				return 0;
+			} else {
+				/* have gone through the list of devices and haven't found n.e.thing */
+				err ("no appropriate range.. bailing out...\n");
+				return -EINVAL;
+			}
+		}
+	}	/* end if (!res_cur) */
+	return -EINVAL;
+}
+
+/********************************************************************************
+ * This routine is called from remove_card if the card contained PPB.
+ * It will remove all the resources on the bus as well as the bus itself
+ * Input: Bus
+ * Output: 0, -ENODEV
+ ********************************************************************************/
+int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
+{
+	struct resource_node *res_cur;
+	struct resource_node *res_tmp;
+	struct bus_node *prev_bus;
+	int rc;
+
+	prev_bus = find_bus_wprev (parent_busno, NULL, 0);
+
+	if (!prev_bus) {
+		debug ("something terribly wrong. Cannot find parent bus to the one to remove\n");
+		return -ENODEV;
+	}
+
+	debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno);
+
+	rc = remove_ranges (bus, prev_bus);
+	if (rc)
+		return rc;
+
+	if (bus->firstIO) {
+		res_cur = bus->firstIO;
+		while (res_cur) {
+			res_tmp = res_cur;
+			if (res_cur->next)
+				res_cur = res_cur->next;
+			else
+				res_cur = res_cur->nextRange;
+			kfree (res_tmp);
+			res_tmp = NULL;
+		}
+		bus->firstIO = NULL;
+	}
+	if (bus->firstMem) {
+		res_cur = bus->firstMem;
+		while (res_cur) {
+			res_tmp = res_cur;
+			if (res_cur->next)
+				res_cur = res_cur->next;
+			else
+				res_cur = res_cur->nextRange;
+			kfree (res_tmp);
+			res_tmp = NULL;
+		}
+		bus->firstMem = NULL;
+	}
+	if (bus->firstPFMem) {
+		res_cur = bus->firstPFMem;
+		while (res_cur) {
+			res_tmp = res_cur;
+			if (res_cur->next)
+				res_cur = res_cur->next;
+			else
+				res_cur = res_cur->nextRange;
+			kfree (res_tmp);
+			res_tmp = NULL;
+		}
+		bus->firstPFMem = NULL;
+	}
+
+	if (bus->firstPFMemFromMem) {
+		res_cur = bus->firstPFMemFromMem;
+		while (res_cur) {
+			res_tmp = res_cur;
+			res_cur = res_cur->next;
+
+			kfree (res_tmp);
+			res_tmp = NULL;
+		}
+		bus->firstPFMemFromMem = NULL;
+	}
+
+	list_del (&bus->bus_list);
+	kfree (bus);
+	return 0;
+}
+
+/******************************************************************************
+ * This routine deletes the ranges from a given bus, and the entries from the
+ * parent's bus in the resources
+ * Input: current bus, previous bus
+ * Output: 0, -EINVAL
+ ******************************************************************************/
+static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
+{
+	struct range_node *range_cur;
+	struct range_node *range_tmp;
+	int i;
+	struct resource_node *res = NULL;
+
+	if (bus_cur->noIORanges) {
+		range_cur = bus_cur->rangeIO;
+		for (i = 0; i < bus_cur->noIORanges; i++) {
+			if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0)
+				return -EINVAL;
+			ibmphp_remove_resource (res);
+
+			range_tmp = range_cur;
+			range_cur = range_cur->next;
+			kfree (range_tmp);
+			range_tmp = NULL;
+		}
+		bus_cur->rangeIO = NULL;
+	}
+	if (bus_cur->noMemRanges) {
+		range_cur = bus_cur->rangeMem;
+		for (i = 0; i < bus_cur->noMemRanges; i++) {
+			if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0)
+				return -EINVAL;
+
+			ibmphp_remove_resource (res);
+			range_tmp = range_cur;
+			range_cur = range_cur->next;
+			kfree (range_tmp);
+			range_tmp = NULL;
+		}
+		bus_cur->rangeMem = NULL;
+	}
+	if (bus_cur->noPFMemRanges) {
+		range_cur = bus_cur->rangePFMem;
+		for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+			if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0)
+				return -EINVAL;
+
+			ibmphp_remove_resource (res);
+			range_tmp = range_cur;
+			range_cur = range_cur->next;
+			kfree (range_tmp);
+			range_tmp = NULL;
+		}
+		bus_cur->rangePFMem = NULL;
+	}
+	return 0;
+}
+
+/*
+ * find the resource node in the bus
+ * Input: Resource needed, start address of the resource, type of resource
+ */
+int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
+{
+	struct resource_node *res_cur = NULL;
+	char * type = "";
+
+	if (!bus) {
+		err ("The bus passed in NULL to find resource\n");
+		return -ENODEV;
+	}
+
+	switch (flag) {
+		case IO:
+			res_cur = bus->firstIO;
+			type = "io";
+			break;
+		case MEM:
+			res_cur = bus->firstMem;
+			type = "mem";
+			break;
+		case PFMEM:
+			res_cur = bus->firstPFMem;
+			type = "pfmem";
+			break;
+		default:
+			err ("wrong type of flag\n");
+			return -EINVAL;
+	}
+
+	while (res_cur) {
+		if (res_cur->start == start_address) {
+			*res = res_cur;
+			break;
+		}
+		if (res_cur->next)
+			res_cur = res_cur->next;
+		else
+			res_cur = res_cur->nextRange;
+	}
+
+	if (!res_cur) {
+		if (flag == PFMEM) {
+			res_cur = bus->firstPFMemFromMem;
+			while (res_cur) {
+				if (res_cur->start == start_address) {
+					*res = res_cur;
+					break;
+				}
+				res_cur = res_cur->next;
+			}
+			if (!res_cur) {
+				debug ("SOS...cannot find %s resource in the bus.\n", type);
+				return -EINVAL;
+			}
+		} else {
+			debug ("SOS... cannot find %s resource in the bus.\n", type);
+			return -EINVAL;
+		}
+	}
+
+	if (*res)
+		debug ("*res->start = %x\n", (*res)->start);
+
+	return 0;
+}
+
+/***********************************************************************
+ * This routine will free the resource structures used by the
+ * system.  It is called from cleanup routine for the module
+ * Parameters: none
+ * Returns: none
+ ***********************************************************************/
+void ibmphp_free_resources (void)
+{
+	struct bus_node *bus_cur = NULL;
+	struct bus_node *bus_tmp;
+	struct range_node *range_cur;
+	struct range_node *range_tmp;
+	struct resource_node *res_cur;
+	struct resource_node *res_tmp;
+	struct list_head *tmp;
+	struct list_head *next;
+	int i = 0;
+	flags = 1;
+
+	list_for_each_safe (tmp, next, &gbuses) {
+		bus_cur = list_entry (tmp, struct bus_node, bus_list);
+		if (bus_cur->noIORanges) {
+			range_cur = bus_cur->rangeIO;
+			for (i = 0; i < bus_cur->noIORanges; i++) {
+				if (!range_cur)
+					break;
+				range_tmp = range_cur;
+				range_cur = range_cur->next;
+				kfree (range_tmp);
+				range_tmp = NULL;
+			}
+		}
+		if (bus_cur->noMemRanges) {
+			range_cur = bus_cur->rangeMem;
+			for (i = 0; i < bus_cur->noMemRanges; i++) {
+				if (!range_cur)
+					break;
+				range_tmp = range_cur;
+				range_cur = range_cur->next;
+				kfree (range_tmp);
+				range_tmp = NULL;
+			}
+		}
+		if (bus_cur->noPFMemRanges) {
+			range_cur = bus_cur->rangePFMem;
+			for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+				if (!range_cur)
+					break;
+				range_tmp = range_cur;
+				range_cur = range_cur->next;
+				kfree (range_tmp);
+				range_tmp = NULL;
+			}
+		}
+
+		if (bus_cur->firstIO) {
+			res_cur = bus_cur->firstIO;
+			while (res_cur) {
+				res_tmp = res_cur;
+				if (res_cur->next)
+					res_cur = res_cur->next;
+				else
+					res_cur = res_cur->nextRange;
+				kfree (res_tmp);
+				res_tmp = NULL;
+			}
+			bus_cur->firstIO = NULL;
+		}
+		if (bus_cur->firstMem) {
+			res_cur = bus_cur->firstMem;
+			while (res_cur) {
+				res_tmp = res_cur;
+				if (res_cur->next)
+					res_cur = res_cur->next;
+				else
+					res_cur = res_cur->nextRange;
+				kfree (res_tmp);
+				res_tmp = NULL;
+			}
+			bus_cur->firstMem = NULL;
+		}
+		if (bus_cur->firstPFMem) {
+			res_cur = bus_cur->firstPFMem;
+			while (res_cur) {
+				res_tmp = res_cur;
+				if (res_cur->next)
+					res_cur = res_cur->next;
+				else
+					res_cur = res_cur->nextRange;
+				kfree (res_tmp);
+				res_tmp = NULL;
+			}
+			bus_cur->firstPFMem = NULL;
+		}
+
+		if (bus_cur->firstPFMemFromMem) {
+			res_cur = bus_cur->firstPFMemFromMem;
+			while (res_cur) {
+				res_tmp = res_cur;
+				res_cur = res_cur->next;
+
+				kfree (res_tmp);
+				res_tmp = NULL;
+			}
+			bus_cur->firstPFMemFromMem = NULL;
+		}
+
+		bus_tmp = bus_cur;
+		list_del (&bus_cur->bus_list);
+		kfree (bus_tmp);
+		bus_tmp = NULL;
+	}
+}
+
+/*********************************************************************************
+ * This function will go over the PFmem resources to check if the EBDA allocated
+ * pfmem out of memory buckets of the bus.  If so, it will change the range numbers
+ * and a flag to indicate that this resource is out of memory. It will also move the
+ * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create
+ * a new Mem node
+ * This routine is called right after initialization
+ *******************************************************************************/
+static int __init once_over (void)
+{
+	struct resource_node *pfmem_cur;
+	struct resource_node *pfmem_prev;
+	struct resource_node *mem;
+	struct bus_node *bus_cur;
+	struct list_head *tmp;
+
+	list_for_each (tmp, &gbuses) {
+		bus_cur = list_entry (tmp, struct bus_node, bus_list);
+		if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
+			for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
+				pfmem_cur->fromMem = 1;
+				if (pfmem_prev)
+					pfmem_prev->next = pfmem_cur->next;
+				else
+					bus_cur->firstPFMem = pfmem_cur->next;
+
+				if (!bus_cur->firstPFMemFromMem)
+					pfmem_cur->next = NULL;
+				else
+					/* we don't need to sort PFMemFromMem since we're using mem node for
+					   all the real work anyways, so just insert at the beginning of the
+					   list
+					 */
+					pfmem_cur->next = bus_cur->firstPFMemFromMem;
+
+				bus_cur->firstPFMemFromMem = pfmem_cur;
+
+				mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+				if (!mem) {
+					err ("out of system memory\n");
+					return -ENOMEM;
+				}
+				mem->type = MEM;
+				mem->busno = pfmem_cur->busno;
+				mem->devfunc = pfmem_cur->devfunc;
+				mem->start = pfmem_cur->start;
+				mem->end = pfmem_cur->end;
+				mem->len = pfmem_cur->len;
+				if (ibmphp_add_resource (mem) < 0)
+					err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n");
+				pfmem_cur->rangeno = mem->rangeno;
+			}	/* end for pfmem */
+		}	/* end if */
+	}	/* end list_for_each bus */
+	return 0;
+}
+
+int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem)
+{
+	struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0);
+
+	if (!bus_cur) {
+		err ("cannot find bus of pfmem to add...\n");
+		return -ENODEV;
+	}
+
+	if (bus_cur->firstPFMemFromMem)
+		pfmem->next = bus_cur->firstPFMemFromMem;
+	else
+		pfmem->next = NULL;
+
+	bus_cur->firstPFMemFromMem = pfmem;
+
+	return 0;
+}
+
+/* This routine just goes through the buses to see if the bus already exists.
+ * It is called from ibmphp_find_sec_number, to find out a secondary bus number for
+ * bridged cards
+ * Parameters: bus_number
+ * Returns: Bus pointer or NULL
+ */
+struct bus_node *ibmphp_find_res_bus (u8 bus_number)
+{
+	return find_bus_wprev (bus_number, NULL, 0);
+}
+
+static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag)
+{
+	struct bus_node *bus_cur;
+	struct list_head *tmp;
+	struct list_head *tmp_prev;
+
+	list_for_each (tmp, &gbuses) {
+		tmp_prev = tmp->prev;
+		bus_cur = list_entry (tmp, struct bus_node, bus_list);
+		if (flag)
+			*prev = list_entry (tmp_prev, struct bus_node, bus_list);
+		if (bus_cur->busno == bus_number)
+			return bus_cur;
+	}
+
+	return NULL;
+}
+
+void ibmphp_print_test (void)
+{
+	int i = 0;
+	struct bus_node *bus_cur = NULL;
+	struct range_node *range;
+	struct resource_node *res;
+	struct list_head *tmp;
+
+	debug_pci ("*****************START**********************\n");
+
+	if ((!list_empty(&gbuses)) && flags) {
+		err ("The GBUSES is not NULL?!?!?!?!?\n");
+		return;
+	}
+
+	list_for_each (tmp, &gbuses) {
+		bus_cur = list_entry (tmp, struct bus_node, bus_list);
+		debug_pci ("This is bus # %d.  There are\n", bus_cur->busno);
+		debug_pci ("IORanges = %d\t", bus_cur->noIORanges);
+		debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges);
+		debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
+		debug_pci ("The IO Ranges are as follows:\n");
+		if (bus_cur->rangeIO) {
+			range = bus_cur->rangeIO;
+			for (i = 0; i < bus_cur->noIORanges; i++) {
+				debug_pci ("rangeno is %d\n", range->rangeno);
+				debug_pci ("[%x - %x]\n", range->start, range->end);
+				range = range->next;
+			}
+		}
+
+		debug_pci ("The Mem Ranges are as follows:\n");
+		if (bus_cur->rangeMem) {
+			range = bus_cur->rangeMem;
+			for (i = 0; i < bus_cur->noMemRanges; i++) {
+				debug_pci ("rangeno is %d\n", range->rangeno);
+				debug_pci ("[%x - %x]\n", range->start, range->end);
+				range = range->next;
+			}
+		}
+
+		debug_pci ("The PFMem Ranges are as follows:\n");
+
+		if (bus_cur->rangePFMem) {
+			range = bus_cur->rangePFMem;
+			for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+				debug_pci ("rangeno is %d\n", range->rangeno);
+				debug_pci ("[%x - %x]\n", range->start, range->end);
+				range = range->next;
+			}
+		}
+
+		debug_pci ("The resources on this bus are as follows\n");
+
+		debug_pci ("IO...\n");
+		if (bus_cur->firstIO) {
+			res = bus_cur->firstIO;
+			while (res) {
+				debug_pci ("The range # is %d\n", res->rangeno);
+				debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+				debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+				if (res->next)
+					res = res->next;
+				else if (res->nextRange)
+					res = res->nextRange;
+				else
+					break;
+			}
+		}
+		debug_pci ("Mem...\n");
+		if (bus_cur->firstMem) {
+			res = bus_cur->firstMem;
+			while (res) {
+				debug_pci ("The range # is %d\n", res->rangeno);
+				debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+				debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+				if (res->next)
+					res = res->next;
+				else if (res->nextRange)
+					res = res->nextRange;
+				else
+					break;
+			}
+		}
+		debug_pci ("PFMem...\n");
+		if (bus_cur->firstPFMem) {
+			res = bus_cur->firstPFMem;
+			while (res) {
+				debug_pci ("The range # is %d\n", res->rangeno);
+				debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+				debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+				if (res->next)
+					res = res->next;
+				else if (res->nextRange)
+					res = res->nextRange;
+				else
+					break;
+			}
+		}
+
+		debug_pci ("PFMemFromMem...\n");
+		if (bus_cur->firstPFMemFromMem) {
+			res = bus_cur->firstPFMemFromMem;
+			while (res) {
+				debug_pci ("The range # is %d\n", res->rangeno);
+				debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+				debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+				res = res->next;
+			}
+		}
+	}
+	debug_pci ("***********************END***********************\n");
+}
+
+static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type)
+{
+	struct range_node * range_cur = NULL;
+	switch (type) {
+		case IO:
+			range_cur = bus_cur->rangeIO;
+			break;
+		case MEM:
+			range_cur = bus_cur->rangeMem;
+			break;
+		case PFMEM:
+			range_cur = bus_cur->rangePFMem;
+			break;
+		default:
+			err ("wrong type passed to find out if range already exists\n");
+			return -ENODEV;
+	}
+
+	while (range_cur) {
+		if ((range_cur->start == range->start) && (range_cur->end == range->end))
+			return 1;
+		range_cur = range_cur->next;
+	}
+
+	return 0;
+}
+
+/* This routine will read the windows for any PPB we have and update the
+ * range info for the secondary bus, and will also input this info into
+ * primary bus, since BIOS doesn't. This is for PPB that are in the system
+ * on bootup.  For bridged cards that were added during previous load of the
+ * driver, only the ranges and the bus structure are added, the devices are
+ * added from NVRAM
+ * Input: primary busno
+ * Returns: none
+ * Note: this function doesn't take into account IO restrictions etc,
+ *	 so will only work for bridges with no video/ISA devices behind them It
+ *	 also will not work for onboard PPBs that can have more than 1 *bus
+ *	 behind them All these are TO DO.
+ *	 Also need to add more error checkings... (from fnc returns etc)
+ */
+static int __init update_bridge_ranges (struct bus_node **bus)
+{
+	u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address;
+	u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
+	u32 start_address, end_address, upper_start, upper_end;
+	struct bus_node *bus_sec;
+	struct bus_node *bus_cur;
+	struct resource_node *io;
+	struct resource_node *mem;
+	struct resource_node *pfmem;
+	struct range_node *range;
+	unsigned int devfn;
+
+	bus_cur = *bus;
+	if (!bus_cur)
+		return -ENODEV;
+	ibmphp_pci_bus->number = bus_cur->busno;
+
+	debug ("inside %s\n", __func__);
+	debug ("bus_cur->busno = %x\n", bus_cur->busno);
+
+	for (device = 0; device < 32; device++) {
+		for (function = 0x00; function < 0x08; function++) {
+			devfn = PCI_DEVFN(device, function);
+			pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+			if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+				/* found correct device!!! */
+				pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+
+				switch (hdr_type) {
+					case PCI_HEADER_TYPE_NORMAL:
+						function = 0x8;
+						break;
+					case PCI_HEADER_TYPE_MULTIDEVICE:
+						break;
+					case PCI_HEADER_TYPE_BRIDGE:
+						function = 0x8;
+					case PCI_HEADER_TYPE_MULTIBRIDGE:
+						/* We assume here that only 1 bus behind the bridge
+						   TO DO: add functionality for several:
+						   temp = secondary;
+						   while (temp < subordinate) {
+						   ...
+						   temp++;
+						   }
+						 */
+						pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
+						bus_sec = find_bus_wprev (sec_busno, NULL, 0);
+						/* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
+						if (!bus_sec) {
+							bus_sec = alloc_error_bus (NULL, sec_busno, 1);
+							/* the rest will be populated during NVRAM call */
+							return 0;
+						}
+						pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address);
+						pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address);
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start);
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end);
+						start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
+						start_address |= (upper_io_start << 16);
+						end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
+						end_address |= (upper_io_end << 16);
+
+						if ((start_address) && (start_address <= end_address)) {
+							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
+							if (!range) {
+								err ("out of system memory\n");
+								return -ENOMEM;
+							}
+							range->start = start_address;
+							range->end = end_address + 0xfff;
+
+							if (bus_sec->noIORanges > 0) {
+								if (!range_exists_already (range, bus_sec, IO)) {
+									add_bus_range (IO, range, bus_sec);
+									++bus_sec->noIORanges;
+								} else {
+									kfree (range);
+									range = NULL;
+								}
+							} else {
+								/* 1st IO Range on the bus */
+								range->rangeno = 1;
+								bus_sec->rangeIO = range;
+								++bus_sec->noIORanges;
+							}
+							fix_resources (bus_sec);
+
+							if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) {
+								io = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+								if (!io) {
+									kfree (range);
+									err ("out of system memory\n");
+									return -ENOMEM;
+								}
+								io->type = IO;
+								io->busno = bus_cur->busno;
+								io->devfunc = ((device << 3) | (function & 0x7));
+								io->start = start_address;
+								io->end = end_address + 0xfff;
+								io->len = io->end - io->start + 1;
+								ibmphp_add_resource (io);
+							}
+						}
+
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
+
+						start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+						end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+
+						if ((start_address) && (start_address <= end_address)) {
+
+							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
+							if (!range) {
+								err ("out of system memory\n");
+								return -ENOMEM;
+							}
+							range->start = start_address;
+							range->end = end_address + 0xfffff;
+
+							if (bus_sec->noMemRanges > 0) {
+								if (!range_exists_already (range, bus_sec, MEM)) {
+									add_bus_range (MEM, range, bus_sec);
+									++bus_sec->noMemRanges;
+								} else {
+									kfree (range);
+									range = NULL;
+								}
+							} else {
+								/* 1st Mem Range on the bus */
+								range->rangeno = 1;
+								bus_sec->rangeMem = range;
+								++bus_sec->noMemRanges;
+							}
+
+							fix_resources (bus_sec);
+
+							if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) {
+								mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+								if (!mem) {
+									kfree (range);
+									err ("out of system memory\n");
+									return -ENOMEM;
+								}
+								mem->type = MEM;
+								mem->busno = bus_cur->busno;
+								mem->devfunc = ((device << 3) | (function & 0x7));
+								mem->start = start_address;
+								mem->end = end_address + 0xfffff;
+								mem->len = mem->end - mem->start + 1;
+								ibmphp_add_resource (mem);
+							}
+						}
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address);
+						pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
+						pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start);
+						pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end);
+						start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+						end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+#if BITS_PER_LONG == 64
+						start_address |= ((long) upper_start) << 32;
+						end_address |= ((long) upper_end) << 32;
+#endif
+
+						if ((start_address) && (start_address <= end_address)) {
+
+							range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
+							if (!range) {
+								err ("out of system memory\n");
+								return -ENOMEM;
+							}
+							range->start = start_address;
+							range->end = end_address + 0xfffff;
+
+							if (bus_sec->noPFMemRanges > 0) {
+								if (!range_exists_already (range, bus_sec, PFMEM)) {
+									add_bus_range (PFMEM, range, bus_sec);
+									++bus_sec->noPFMemRanges;
+								} else {
+									kfree (range);
+									range = NULL;
+								}
+							} else {
+								/* 1st PFMem Range on the bus */
+								range->rangeno = 1;
+								bus_sec->rangePFMem = range;
+								++bus_sec->noPFMemRanges;
+							}
+
+							fix_resources (bus_sec);
+							if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) {
+								pfmem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
+								if (!pfmem) {
+									kfree (range);
+									err ("out of system memory\n");
+									return -ENOMEM;
+								}
+								pfmem->type = PFMEM;
+								pfmem->busno = bus_cur->busno;
+								pfmem->devfunc = ((device << 3) | (function & 0x7));
+								pfmem->start = start_address;
+								pfmem->end = end_address + 0xfffff;
+								pfmem->len = pfmem->end - pfmem->start + 1;
+								pfmem->fromMem = 0;
+
+								ibmphp_add_resource (pfmem);
+							}
+						}
+						break;
+				}	/* end of switch */
+			}	/* end if vendor */
+		}	/* end for function */
+	}	/* end for device */
+
+	bus = &bus_cur;
+	return 0;
+}
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
new file mode 100644
index 0000000..d1fab97
--- /dev/null
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -0,0 +1,555 @@
+/*
+ * PCI HotPlug Controller Core
+ *
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2002 IBM Corp.
+ *
+ * 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 <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <asm/uaccess.h>
+#include "../pci.h"
+#include "cpci_hotplug.h"
+
+#define MY_NAME	"pci_hotplug"
+
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
+
+
+/* local variables */
+static bool debug;
+
+#define DRIVER_VERSION	"0.5"
+#define DRIVER_AUTHOR	"Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC	"PCI Hot Plug PCI Core"
+
+
+static LIST_HEAD(pci_hotplug_slot_list);
+static DEFINE_MUTEX(pci_hp_mutex);
+
+/* Weee, fun with macros... */
+#define GET_STATUS(name, type)	\
+static int get_##name(struct hotplug_slot *slot, type *value)		\
+{									\
+	struct hotplug_slot_ops *ops = slot->ops;			\
+	int retval = 0;							\
+	if (!try_module_get(ops->owner))				\
+		return -ENODEV;						\
+	if (ops->get_##name)						\
+		retval = ops->get_##name(slot, value);			\
+	else								\
+		*value = slot->info->name;				\
+	module_put(ops->owner);						\
+	return retval;							\
+}
+
+GET_STATUS(power_status, u8)
+GET_STATUS(attention_status, u8)
+GET_STATUS(latch_status, u8)
+GET_STATUS(adapter_status, u8)
+
+static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
+{
+	int retval;
+	u8 value;
+
+	retval = get_power_status(pci_slot->hotplug, &value);
+	if (retval)
+		return retval;
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
+				size_t count)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+	unsigned long lpower;
+	u8 power;
+	int retval = 0;
+
+	lpower = simple_strtoul(buf, NULL, 10);
+	power = (u8)(lpower & 0xff);
+	dbg("power = %d\n", power);
+
+	if (!try_module_get(slot->ops->owner)) {
+		retval = -ENODEV;
+		goto exit;
+	}
+	switch (power) {
+	case 0:
+		if (slot->ops->disable_slot)
+			retval = slot->ops->disable_slot(slot);
+		break;
+
+	case 1:
+		if (slot->ops->enable_slot)
+			retval = slot->ops->enable_slot(slot);
+		break;
+
+	default:
+		err("Illegal value specified for power\n");
+		retval = -EINVAL;
+	}
+	module_put(slot->ops->owner);
+
+exit:
+	if (retval)
+		return retval;
+	return count;
+}
+
+static struct pci_slot_attribute hotplug_slot_attr_power = {
+	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+	.show = power_read_file,
+	.store = power_write_file
+};
+
+static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
+{
+	int retval;
+	u8 value;
+
+	retval = get_attention_status(pci_slot->hotplug, &value);
+	if (retval)
+		return retval;
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
+				    size_t count)
+{
+	struct hotplug_slot_ops *ops = pci_slot->hotplug->ops;
+	unsigned long lattention;
+	u8 attention;
+	int retval = 0;
+
+	lattention = simple_strtoul(buf, NULL, 10);
+	attention = (u8)(lattention & 0xff);
+	dbg(" - attention = %d\n", attention);
+
+	if (!try_module_get(ops->owner)) {
+		retval = -ENODEV;
+		goto exit;
+	}
+	if (ops->set_attention_status)
+		retval = ops->set_attention_status(pci_slot->hotplug, attention);
+	module_put(ops->owner);
+
+exit:
+	if (retval)
+		return retval;
+	return count;
+}
+
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
+	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+	.show = attention_read_file,
+	.store = attention_write_file
+};
+
+static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
+{
+	int retval;
+	u8 value;
+
+	retval = get_latch_status(pci_slot->hotplug, &value);
+	if (retval)
+		return retval;
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
+	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
+	.show = latch_read_file,
+};
+
+static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
+{
+	int retval;
+	u8 value;
+
+	retval = get_adapter_status(pci_slot->hotplug, &value);
+	if (retval)
+		return retval;
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
+	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
+	.show = presence_read_file,
+};
+
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
+			       size_t count)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+	unsigned long ltest;
+	u32 test;
+	int retval = 0;
+
+	ltest = simple_strtoul (buf, NULL, 10);
+	test = (u32)(ltest & 0xffffffff);
+	dbg("test = %d\n", test);
+
+	if (!try_module_get(slot->ops->owner)) {
+		retval = -ENODEV;
+		goto exit;
+	}
+	if (slot->ops->hardware_test)
+		retval = slot->ops->hardware_test(slot, test);
+	module_put(slot->ops->owner);
+
+exit:
+	if (retval)
+		return retval;
+	return count;
+}
+
+static struct pci_slot_attribute hotplug_slot_attr_test = {
+	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+	.store = test_write_file
+};
+
+static bool has_power_file(struct pci_slot *pci_slot)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+
+	if ((!slot) || (!slot->ops))
+		return false;
+	if ((slot->ops->enable_slot) ||
+	    (slot->ops->disable_slot) ||
+	    (slot->ops->get_power_status))
+		return true;
+	return false;
+}
+
+static bool has_attention_file(struct pci_slot *pci_slot)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+
+	if ((!slot) || (!slot->ops))
+		return false;
+	if ((slot->ops->set_attention_status) ||
+	    (slot->ops->get_attention_status))
+		return true;
+	return false;
+}
+
+static bool has_latch_file(struct pci_slot *pci_slot)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+
+	if ((!slot) || (!slot->ops))
+		return false;
+	if (slot->ops->get_latch_status)
+		return true;
+	return false;
+}
+
+static bool has_adapter_file(struct pci_slot *pci_slot)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+
+	if ((!slot) || (!slot->ops))
+		return false;
+	if (slot->ops->get_adapter_status)
+		return true;
+	return false;
+}
+
+static bool has_test_file(struct pci_slot *pci_slot)
+{
+	struct hotplug_slot *slot = pci_slot->hotplug;
+
+	if ((!slot) || (!slot->ops))
+		return false;
+	if (slot->ops->hardware_test)
+		return true;
+	return false;
+}
+
+static int fs_add_slot(struct pci_slot *pci_slot)
+{
+	int retval = 0;
+
+	/* Create symbolic link to the hotplug driver module */
+	pci_hp_create_module_link(pci_slot);
+
+	if (has_power_file(pci_slot)) {
+		retval = sysfs_create_file(&pci_slot->kobj,
+					   &hotplug_slot_attr_power.attr);
+		if (retval)
+			goto exit_power;
+	}
+
+	if (has_attention_file(pci_slot)) {
+		retval = sysfs_create_file(&pci_slot->kobj,
+					   &hotplug_slot_attr_attention.attr);
+		if (retval)
+			goto exit_attention;
+	}
+
+	if (has_latch_file(pci_slot)) {
+		retval = sysfs_create_file(&pci_slot->kobj,
+					   &hotplug_slot_attr_latch.attr);
+		if (retval)
+			goto exit_latch;
+	}
+
+	if (has_adapter_file(pci_slot)) {
+		retval = sysfs_create_file(&pci_slot->kobj,
+					   &hotplug_slot_attr_presence.attr);
+		if (retval)
+			goto exit_adapter;
+	}
+
+	if (has_test_file(pci_slot)) {
+		retval = sysfs_create_file(&pci_slot->kobj,
+					   &hotplug_slot_attr_test.attr);
+		if (retval)
+			goto exit_test;
+	}
+
+	goto exit;
+
+exit_test:
+	if (has_adapter_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj,
+				  &hotplug_slot_attr_presence.attr);
+exit_adapter:
+	if (has_latch_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
+exit_latch:
+	if (has_attention_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj,
+				  &hotplug_slot_attr_attention.attr);
+exit_attention:
+	if (has_power_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
+exit_power:
+	pci_hp_remove_module_link(pci_slot);
+exit:
+	return retval;
+}
+
+static void fs_remove_slot(struct pci_slot *pci_slot)
+{
+	if (has_power_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
+
+	if (has_attention_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj,
+				  &hotplug_slot_attr_attention.attr);
+
+	if (has_latch_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
+
+	if (has_adapter_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj,
+				  &hotplug_slot_attr_presence.attr);
+
+	if (has_test_file(pci_slot))
+		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
+
+	pci_hp_remove_module_link(pci_slot);
+}
+
+static struct hotplug_slot *get_slot_from_name(const char *name)
+{
+	struct hotplug_slot *slot;
+	struct list_head *tmp;
+
+	list_for_each(tmp, &pci_hotplug_slot_list) {
+		slot = list_entry(tmp, struct hotplug_slot, slot_list);
+		if (strcmp(hotplug_slot_name(slot), name) == 0)
+			return slot;
+	}
+	return NULL;
+}
+
+/**
+ * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
+ * @bus: bus this slot is on
+ * @slot: pointer to the &struct hotplug_slot to register
+ * @devnr: device number
+ * @name: name registered with kobject core
+ * @owner: caller module owner
+ * @mod_name: caller module name
+ *
+ * Registers a hotplug slot with the pci hotplug subsystem, which will allow
+ * userspace interaction to the slot.
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
+		      int devnr, const char *name,
+		      struct module *owner, const char *mod_name)
+{
+	int result;
+	struct pci_slot *pci_slot;
+
+	if (slot == NULL)
+		return -ENODEV;
+	if ((slot->info == NULL) || (slot->ops == NULL))
+		return -EINVAL;
+	if (slot->release == NULL) {
+		dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
+		return -EINVAL;
+	}
+
+	slot->ops->owner = owner;
+	slot->ops->mod_name = mod_name;
+
+	mutex_lock(&pci_hp_mutex);
+	/*
+	 * No problems if we call this interface from both ACPI_PCI_SLOT
+	 * driver and call it here again. If we've already created the
+	 * pci_slot, the interface will simply bump the refcount.
+	 */
+	pci_slot = pci_create_slot(bus, devnr, name, slot);
+	if (IS_ERR(pci_slot)) {
+		result = PTR_ERR(pci_slot);
+		goto out;
+	}
+
+	slot->pci_slot = pci_slot;
+	pci_slot->hotplug = slot;
+
+	list_add(&slot->slot_list, &pci_hotplug_slot_list);
+
+	result = fs_add_slot(pci_slot);
+	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
+	dbg("Added slot %s to the list\n", name);
+out:
+	mutex_unlock(&pci_hp_mutex);
+	return result;
+}
+EXPORT_SYMBOL_GPL(__pci_hp_register);
+
+/**
+ * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
+ * @slot: pointer to the &struct hotplug_slot to deregister
+ *
+ * The @slot must have been registered with the pci hotplug subsystem
+ * previously with a call to pci_hp_register().
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_deregister(struct hotplug_slot *slot)
+{
+	struct hotplug_slot *temp;
+	struct pci_slot *pci_slot;
+
+	if (!slot)
+		return -ENODEV;
+
+	mutex_lock(&pci_hp_mutex);
+	temp = get_slot_from_name(hotplug_slot_name(slot));
+	if (temp != slot) {
+		mutex_unlock(&pci_hp_mutex);
+		return -ENODEV;
+	}
+
+	list_del(&slot->slot_list);
+
+	pci_slot = slot->pci_slot;
+	fs_remove_slot(pci_slot);
+	dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
+
+	slot->release(slot);
+	pci_slot->hotplug = NULL;
+	pci_destroy_slot(pci_slot);
+	mutex_unlock(&pci_hp_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
+
+/**
+ * pci_hp_change_slot_info - changes the slot's information structure in the core
+ * @slot: pointer to the slot whose info has changed
+ * @info: pointer to the info copy into the slot's info structure
+ *
+ * @slot must have been registered with the pci
+ * hotplug subsystem previously with a call to pci_hp_register().
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_change_slot_info(struct hotplug_slot *slot,
+			    struct hotplug_slot_info *info)
+{
+	if (!slot || !info)
+		return -ENODEV;
+
+	memcpy(slot->info, info, sizeof(struct hotplug_slot_info));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
+
+static int __init pci_hotplug_init(void)
+{
+	int result;
+
+	result = cpci_hotplug_init(debug);
+	if (result) {
+		err("cpci_hotplug_init with error %d\n", result);
+		return result;
+	}
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+	return result;
+}
+
+static void __exit pci_hotplug_exit(void)
+{
+	cpci_hotplug_exit();
+}
+
+module_init(pci_hotplug_init);
+module_exit(pci_hotplug_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
new file mode 100644
index 0000000..62d6fe6
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp.h
@@ -0,0 +1,160 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+#ifndef _PCIEHP_H
+#define _PCIEHP_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/delay.h>
+#include <linux/sched.h>		/* signal_pending() */
+#include <linux/pcieport_if.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define MY_NAME	"pciehp"
+
+extern bool pciehp_poll_mode;
+extern int pciehp_poll_time;
+extern bool pciehp_debug;
+
+#define dbg(format, arg...)						\
+do {									\
+	if (pciehp_debug)						\
+		printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg);	\
+} while (0)
+#define err(format, arg...)						\
+	printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
+#define info(format, arg...)						\
+	printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
+#define warn(format, arg...)						\
+	printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
+
+#define ctrl_dbg(ctrl, format, arg...)					\
+	do {								\
+		if (pciehp_debug)					\
+			dev_printk(KERN_DEBUG, &ctrl->pcie->device,	\
+					format, ## arg);		\
+	} while (0)
+#define ctrl_err(ctrl, format, arg...)					\
+	dev_err(&ctrl->pcie->device, format, ## arg)
+#define ctrl_info(ctrl, format, arg...)					\
+	dev_info(&ctrl->pcie->device, format, ## arg)
+#define ctrl_warn(ctrl, format, arg...)					\
+	dev_warn(&ctrl->pcie->device, format, ## arg)
+
+#define SLOT_NAME_SIZE 10
+struct slot {
+	u8 state;
+	struct controller *ctrl;
+	struct hotplug_slot *hotplug_slot;
+	struct delayed_work work;	/* work for button event */
+	struct mutex lock;
+	struct mutex hotplug_lock;
+	struct workqueue_struct *wq;
+};
+
+struct event_info {
+	u32 event_type;
+	struct slot *p_slot;
+	struct work_struct work;
+};
+
+struct controller {
+	struct mutex ctrl_lock;		/* controller lock */
+	struct pcie_device *pcie;	/* PCI Express port service */
+	struct slot *slot;
+	wait_queue_head_t queue;	/* sleep & wake process */
+	u32 slot_cap;
+	u16 slot_ctrl;
+	struct timer_list poll_timer;
+	unsigned long cmd_started;	/* jiffies */
+	unsigned int cmd_busy:1;
+	unsigned int link_active_reporting:1;
+	unsigned int notification_enabled:1;
+	unsigned int power_fault_detected;
+};
+
+#define INT_PRESENCE_ON			1
+#define INT_PRESENCE_OFF		2
+#define INT_POWER_FAULT			3
+#define INT_BUTTON_PRESS		4
+#define INT_LINK_UP			5
+#define INT_LINK_DOWN			6
+
+#define STATIC_STATE			0
+#define BLINKINGON_STATE		1
+#define BLINKINGOFF_STATE		2
+#define POWERON_STATE			3
+#define POWEROFF_STATE			4
+
+#define ATTN_BUTTN(ctrl)	((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
+#define POWER_CTRL(ctrl)	((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
+#define MRL_SENS(ctrl)		((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP)
+#define ATTN_LED(ctrl)		((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP)
+#define PWR_LED(ctrl)		((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP)
+#define HP_SUPR_RM(ctrl)	((ctrl)->slot_cap & PCI_EXP_SLTCAP_HPS)
+#define EMI(ctrl)		((ctrl)->slot_cap & PCI_EXP_SLTCAP_EIP)
+#define NO_CMD_CMPL(ctrl)	((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
+#define PSN(ctrl)		(((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
+
+int pciehp_sysfs_enable_slot(struct slot *slot);
+int pciehp_sysfs_disable_slot(struct slot *slot);
+void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type);
+int pciehp_configure_device(struct slot *p_slot);
+int pciehp_unconfigure_device(struct slot *p_slot);
+void pciehp_queue_pushbutton_work(struct work_struct *work);
+struct controller *pcie_init(struct pcie_device *dev);
+int pcie_init_notification(struct controller *ctrl);
+int pciehp_enable_slot(struct slot *p_slot);
+int pciehp_disable_slot(struct slot *p_slot);
+void pcie_enable_notification(struct controller *ctrl);
+int pciehp_power_on_slot(struct slot *slot);
+void pciehp_power_off_slot(struct slot *slot);
+void pciehp_get_power_status(struct slot *slot, u8 *status);
+void pciehp_get_attention_status(struct slot *slot, u8 *status);
+
+void pciehp_set_attention_status(struct slot *slot, u8 status);
+void pciehp_get_latch_status(struct slot *slot, u8 *status);
+void pciehp_get_adapter_status(struct slot *slot, u8 *status);
+int pciehp_query_power_fault(struct slot *slot);
+void pciehp_green_led_on(struct slot *slot);
+void pciehp_green_led_off(struct slot *slot);
+void pciehp_green_led_blink(struct slot *slot);
+int pciehp_check_link_status(struct controller *ctrl);
+bool pciehp_check_link_active(struct controller *ctrl);
+void pciehp_release_ctrl(struct controller *ctrl);
+int pciehp_reset_slot(struct slot *slot, int probe);
+
+static inline const char *slot_name(struct slot *slot)
+{
+	return hotplug_slot_name(slot->hotplug_slot);
+}
+
+#endif				/* _PCIEHP_H */
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
new file mode 100644
index 0000000..612b21a
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -0,0 +1,349 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "pciehp.h"
+#include <linux/interrupt.h>
+#include <linux/time.h>
+
+/* Global variables */
+bool pciehp_debug;
+bool pciehp_poll_mode;
+int pciehp_poll_time;
+static bool pciehp_force;
+
+#define DRIVER_VERSION	"0.4"
+#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
+#define DRIVER_DESC	"PCI Express Hot Plug Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(pciehp_debug, bool, 0644);
+module_param(pciehp_poll_mode, bool, 0644);
+module_param(pciehp_poll_time, int, 0644);
+module_param(pciehp_force, bool, 0644);
+MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
+MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
+MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
+
+#define PCIE_MODULE_NAME "pciehp"
+
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int enable_slot		(struct hotplug_slot *slot);
+static int disable_slot		(struct hotplug_slot *slot);
+static int get_power_status	(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status	(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);
+static int reset_slot		(struct hotplug_slot *slot, int probe);
+
+/**
+ * release_slot - free up the memory used by a slot
+ * @hotplug_slot: slot to free
+ */
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	kfree(hotplug_slot->ops);
+	kfree(hotplug_slot->info);
+	kfree(hotplug_slot);
+}
+
+static int init_slot(struct controller *ctrl)
+{
+	struct slot *slot = ctrl->slot;
+	struct hotplug_slot *hotplug = NULL;
+	struct hotplug_slot_info *info = NULL;
+	struct hotplug_slot_ops *ops = NULL;
+	char name[SLOT_NAME_SIZE];
+	int retval = -ENOMEM;
+
+	hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL);
+	if (!hotplug)
+		goto out;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		goto out;
+
+	/* Setup hotplug slot ops */
+	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		goto out;
+
+	ops->enable_slot = enable_slot;
+	ops->disable_slot = disable_slot;
+	ops->get_power_status = get_power_status;
+	ops->get_adapter_status = get_adapter_status;
+	ops->reset_slot = reset_slot;
+	if (MRL_SENS(ctrl))
+		ops->get_latch_status = get_latch_status;
+	if (ATTN_LED(ctrl)) {
+		ops->get_attention_status = get_attention_status;
+		ops->set_attention_status = set_attention_status;
+	}
+
+	/* register this slot with the hotplug pci core */
+	hotplug->info = info;
+	hotplug->private = slot;
+	hotplug->release = &release_slot;
+	hotplug->ops = ops;
+	slot->hotplug_slot = hotplug;
+	snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
+
+	retval = pci_hp_register(hotplug,
+				 ctrl->pcie->port->subordinate, 0, name);
+	if (retval)
+		ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval);
+out:
+	if (retval) {
+		kfree(ops);
+		kfree(info);
+		kfree(hotplug);
+	}
+	return retval;
+}
+
+static void cleanup_slot(struct controller *ctrl)
+{
+	pci_hp_deregister(ctrl->slot->hotplug_slot);
+}
+
+/*
+ * set_attention_status - Turns the Amber LED for a slot on, off or blink
+ */
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pciehp_set_attention_status(slot, status);
+	return 0;
+}
+
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	return pciehp_sysfs_enable_slot(slot);
+}
+
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	return pciehp_sysfs_disable_slot(slot);
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pciehp_get_power_status(slot, value);
+	return 0;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pciehp_get_attention_status(slot, value);
+	return 0;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pciehp_get_latch_status(slot, value);
+	return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	pciehp_get_adapter_status(slot, value);
+	return 0;
+}
+
+static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	return pciehp_reset_slot(slot, probe);
+}
+
+static int pciehp_probe(struct pcie_device *dev)
+{
+	int rc;
+	struct controller *ctrl;
+	struct slot *slot;
+	u8 occupied, poweron;
+
+	/* If this is not a "hotplug" service, we have no business here. */
+	if (dev->service != PCIE_PORT_SERVICE_HP)
+		return -ENODEV;
+
+	if (!dev->port->subordinate) {
+		/* Can happen if we run out of bus numbers during probe */
+		dev_err(&dev->device,
+			"Hotplug bridge without secondary bus, ignoring\n");
+		return -ENODEV;
+	}
+
+	ctrl = pcie_init(dev);
+	if (!ctrl) {
+		dev_err(&dev->device, "Controller initialization failed\n");
+		return -ENODEV;
+	}
+	set_service_data(dev, ctrl);
+
+	/* Setup the slot information structures */
+	rc = init_slot(ctrl);
+	if (rc) {
+		if (rc == -EBUSY)
+			ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
+		else
+			ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
+		goto err_out_release_ctlr;
+	}
+
+	/* Enable events after we have setup the data structures */
+	rc = pcie_init_notification(ctrl);
+	if (rc) {
+		ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
+		goto err_out_free_ctrl_slot;
+	}
+
+	/* Check if slot is occupied */
+	slot = ctrl->slot;
+	pciehp_get_adapter_status(slot, &occupied);
+	pciehp_get_power_status(slot, &poweron);
+	if (occupied && pciehp_force) {
+		mutex_lock(&slot->hotplug_lock);
+		pciehp_enable_slot(slot);
+		mutex_unlock(&slot->hotplug_lock);
+	}
+	/* If empty slot's power status is on, turn power off */
+	if (!occupied && poweron && POWER_CTRL(ctrl))
+		pciehp_power_off_slot(slot);
+
+	return 0;
+
+err_out_free_ctrl_slot:
+	cleanup_slot(ctrl);
+err_out_release_ctlr:
+	pciehp_release_ctrl(ctrl);
+	return -ENODEV;
+}
+
+static void pciehp_remove(struct pcie_device *dev)
+{
+	struct controller *ctrl = get_service_data(dev);
+
+	cleanup_slot(ctrl);
+	pciehp_release_ctrl(ctrl);
+}
+
+#ifdef CONFIG_PM
+static int pciehp_suspend(struct pcie_device *dev)
+{
+	return 0;
+}
+
+static int pciehp_resume(struct pcie_device *dev)
+{
+	struct controller *ctrl;
+	struct slot *slot;
+	u8 status;
+
+	ctrl = get_service_data(dev);
+
+	/* reinitialize the chipset's event detection logic */
+	pcie_enable_notification(ctrl);
+
+	slot = ctrl->slot;
+
+	/* Check if slot is occupied */
+	pciehp_get_adapter_status(slot, &status);
+	mutex_lock(&slot->hotplug_lock);
+	if (status)
+		pciehp_enable_slot(slot);
+	else
+		pciehp_disable_slot(slot);
+	mutex_unlock(&slot->hotplug_lock);
+	return 0;
+}
+#endif /* PM */
+
+static struct pcie_port_service_driver hpdriver_portdrv = {
+	.name		= PCIE_MODULE_NAME,
+	.port_type	= PCIE_ANY_PORT,
+	.service	= PCIE_PORT_SERVICE_HP,
+
+	.probe		= pciehp_probe,
+	.remove		= pciehp_remove,
+
+#ifdef	CONFIG_PM
+	.suspend	= pciehp_suspend,
+	.resume		= pciehp_resume,
+#endif	/* PM */
+};
+
+static int __init pcied_init(void)
+{
+	int retval = 0;
+
+	retval = pcie_port_service_register(&hpdriver_portdrv);
+	dbg("pcie_port_service_register = %d\n", retval);
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+	if (retval)
+		dbg("Failure to register service\n");
+
+	return retval;
+}
+
+static void __exit pcied_cleanup(void)
+{
+	dbg("unload_pciehpd()\n");
+	pcie_port_service_unregister(&hpdriver_portdrv);
+	info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
+}
+
+module_init(pcied_init);
+module_exit(pcied_cleanup);
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
new file mode 100644
index 0000000..4c8f4cd
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -0,0 +1,535 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "pciehp.h"
+
+static void interrupt_event_handler(struct work_struct *work);
+
+void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
+{
+	struct event_info *info;
+
+	info = kmalloc(sizeof(*info), GFP_ATOMIC);
+	if (!info) {
+		ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
+		return;
+	}
+
+	INIT_WORK(&info->work, interrupt_event_handler);
+	info->event_type = event_type;
+	info->p_slot = p_slot;
+	queue_work(p_slot->wq, &info->work);
+}
+
+/* The following routines constitute the bulk of the
+   hotplug controller logic
+ */
+
+static void set_slot_off(struct controller *ctrl, struct slot *pslot)
+{
+	/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
+	if (POWER_CTRL(ctrl)) {
+		pciehp_power_off_slot(pslot);
+
+		/*
+		 * After turning power off, we must wait for at least 1 second
+		 * before taking any action that relies on power having been
+		 * removed from the slot/adapter.
+		 */
+		msleep(1000);
+	}
+
+	pciehp_green_led_off(pslot);
+	pciehp_set_attention_status(pslot, 1);
+}
+
+/**
+ * board_added - Called after a board has been added to the system.
+ * @p_slot: &slot where board is added
+ *
+ * Turns power on for the board.
+ * Configures board.
+ */
+static int board_added(struct slot *p_slot)
+{
+	int retval = 0;
+	struct controller *ctrl = p_slot->ctrl;
+	struct pci_bus *parent = ctrl->pcie->port->subordinate;
+
+	if (POWER_CTRL(ctrl)) {
+		/* Power on slot */
+		retval = pciehp_power_on_slot(p_slot);
+		if (retval)
+			return retval;
+	}
+
+	pciehp_green_led_blink(p_slot);
+
+	/* Check link training status */
+	retval = pciehp_check_link_status(ctrl);
+	if (retval) {
+		ctrl_err(ctrl, "Failed to check link status\n");
+		goto err_exit;
+	}
+
+	/* Check for a power fault */
+	if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
+		ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
+		retval = -EIO;
+		goto err_exit;
+	}
+
+	retval = pciehp_configure_device(p_slot);
+	if (retval) {
+		ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
+			 pci_domain_nr(parent), parent->number);
+		if (retval != -EEXIST)
+			goto err_exit;
+	}
+
+	pciehp_green_led_on(p_slot);
+	return 0;
+
+err_exit:
+	set_slot_off(ctrl, p_slot);
+	return retval;
+}
+
+/**
+ * remove_board - Turns off slot and LEDs
+ * @p_slot: slot where board is being removed
+ */
+static int remove_board(struct slot *p_slot)
+{
+	int retval;
+	struct controller *ctrl = p_slot->ctrl;
+
+	retval = pciehp_unconfigure_device(p_slot);
+	if (retval)
+		return retval;
+
+	if (POWER_CTRL(ctrl)) {
+		pciehp_power_off_slot(p_slot);
+
+		/*
+		 * After turning power off, we must wait for at least 1 second
+		 * before taking any action that relies on power having been
+		 * removed from the slot/adapter.
+		 */
+		msleep(1000);
+	}
+
+	/* turn off Green LED */
+	pciehp_green_led_off(p_slot);
+	return 0;
+}
+
+struct power_work_info {
+	struct slot *p_slot;
+	struct work_struct work;
+	unsigned int req;
+#define DISABLE_REQ 0
+#define ENABLE_REQ  1
+};
+
+/**
+ * pciehp_power_thread - handle pushbutton events
+ * @work: &struct work_struct describing work to be done
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons.
+ * Handles all pending events and exits.
+ */
+static void pciehp_power_thread(struct work_struct *work)
+{
+	struct power_work_info *info =
+		container_of(work, struct power_work_info, work);
+	struct slot *p_slot = info->p_slot;
+	int ret;
+
+	switch (info->req) {
+	case DISABLE_REQ:
+		mutex_lock(&p_slot->hotplug_lock);
+		pciehp_disable_slot(p_slot);
+		mutex_unlock(&p_slot->hotplug_lock);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		mutex_unlock(&p_slot->lock);
+		break;
+	case ENABLE_REQ:
+		mutex_lock(&p_slot->hotplug_lock);
+		ret = pciehp_enable_slot(p_slot);
+		mutex_unlock(&p_slot->hotplug_lock);
+		if (ret)
+			pciehp_green_led_off(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		mutex_unlock(&p_slot->lock);
+		break;
+	default:
+		break;
+	}
+
+	kfree(info);
+}
+
+static void pciehp_queue_power_work(struct slot *p_slot, int req)
+{
+	struct power_work_info *info;
+
+	p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		ctrl_err(p_slot->ctrl, "no memory to queue %s request\n",
+			 (req == ENABLE_REQ) ? "poweron" : "poweroff");
+		return;
+	}
+	info->p_slot = p_slot;
+	INIT_WORK(&info->work, pciehp_power_thread);
+	info->req = req;
+	queue_work(p_slot->wq, &info->work);
+}
+
+void pciehp_queue_pushbutton_work(struct work_struct *work)
+{
+	struct slot *p_slot = container_of(work, struct slot, work.work);
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGOFF_STATE:
+		pciehp_queue_power_work(p_slot, DISABLE_REQ);
+		break;
+	case BLINKINGON_STATE:
+		pciehp_queue_power_work(p_slot, ENABLE_REQ);
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+}
+
+/*
+ * Note: This function must be called with slot->lock held
+ */
+static void handle_button_press_event(struct slot *p_slot)
+{
+	struct controller *ctrl = p_slot->ctrl;
+	u8 getstatus;
+
+	switch (p_slot->state) {
+	case STATIC_STATE:
+		pciehp_get_power_status(p_slot, &getstatus);
+		if (getstatus) {
+			p_slot->state = BLINKINGOFF_STATE;
+			ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+				  slot_name(p_slot));
+		} else {
+			p_slot->state = BLINKINGON_STATE;
+			ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+				  slot_name(p_slot));
+		}
+		/* blink green LED and turn off amber */
+		pciehp_green_led_blink(p_slot);
+		pciehp_set_attention_status(p_slot, 0);
+		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
+		break;
+	case BLINKINGOFF_STATE:
+	case BLINKINGON_STATE:
+		/*
+		 * Cancel if we are still blinking; this means that we
+		 * press the attention again before the 5 sec. limit
+		 * expires to cancel hot-add or hot-remove
+		 */
+		ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
+		cancel_delayed_work(&p_slot->work);
+		if (p_slot->state == BLINKINGOFF_STATE)
+			pciehp_green_led_on(p_slot);
+		else
+			pciehp_green_led_off(p_slot);
+		pciehp_set_attention_status(p_slot, 0);
+		ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+			  slot_name(p_slot));
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWEROFF_STATE:
+	case POWERON_STATE:
+		/*
+		 * Ignore if the slot is on power-on or power-off state;
+		 * this means that the previous attention button action
+		 * to hot-add or hot-remove is undergoing
+		 */
+		ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
+		break;
+	default:
+		ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state);
+		break;
+	}
+}
+
+/*
+ * Note: This function must be called with slot->lock held
+ */
+static void handle_surprise_event(struct slot *p_slot)
+{
+	u8 getstatus;
+
+	pciehp_get_adapter_status(p_slot, &getstatus);
+	if (!getstatus)
+		pciehp_queue_power_work(p_slot, DISABLE_REQ);
+	else
+		pciehp_queue_power_work(p_slot, ENABLE_REQ);
+}
+
+/*
+ * Note: This function must be called with slot->lock held
+ */
+static void handle_link_event(struct slot *p_slot, u32 event)
+{
+	struct controller *ctrl = p_slot->ctrl;
+
+	switch (p_slot->state) {
+	case BLINKINGON_STATE:
+	case BLINKINGOFF_STATE:
+		cancel_delayed_work(&p_slot->work);
+		/* Fall through */
+	case STATIC_STATE:
+		pciehp_queue_power_work(p_slot, event == INT_LINK_UP ?
+					ENABLE_REQ : DISABLE_REQ);
+		break;
+	case POWERON_STATE:
+		if (event == INT_LINK_UP) {
+			ctrl_info(ctrl,
+				  "Link Up event ignored on slot(%s): already powering on\n",
+				  slot_name(p_slot));
+		} else {
+			ctrl_info(ctrl,
+				  "Link Down event queued on slot(%s): currently getting powered on\n",
+				  slot_name(p_slot));
+			pciehp_queue_power_work(p_slot, DISABLE_REQ);
+		}
+		break;
+	case POWEROFF_STATE:
+		if (event == INT_LINK_UP) {
+			ctrl_info(ctrl,
+				  "Link Up event queued on slot(%s): currently getting powered off\n",
+				  slot_name(p_slot));
+			pciehp_queue_power_work(p_slot, ENABLE_REQ);
+		} else {
+			ctrl_info(ctrl,
+				  "Link Down event ignored on slot(%s): already powering off\n",
+				  slot_name(p_slot));
+		}
+		break;
+	default:
+		ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
+			 p_slot->state, slot_name(p_slot));
+		break;
+	}
+}
+
+static void interrupt_event_handler(struct work_struct *work)
+{
+	struct event_info *info = container_of(work, struct event_info, work);
+	struct slot *p_slot = info->p_slot;
+	struct controller *ctrl = p_slot->ctrl;
+
+	mutex_lock(&p_slot->lock);
+	switch (info->event_type) {
+	case INT_BUTTON_PRESS:
+		handle_button_press_event(p_slot);
+		break;
+	case INT_POWER_FAULT:
+		if (!POWER_CTRL(ctrl))
+			break;
+		pciehp_set_attention_status(p_slot, 1);
+		pciehp_green_led_off(p_slot);
+		break;
+	case INT_PRESENCE_ON:
+		handle_surprise_event(p_slot);
+		break;
+	case INT_PRESENCE_OFF:
+		/*
+		 * Regardless of surprise capability, we need to
+		 * definitely remove a card that has been pulled out!
+		 */
+		handle_surprise_event(p_slot);
+		break;
+	case INT_LINK_UP:
+	case INT_LINK_DOWN:
+		handle_link_event(p_slot, info->event_type);
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	kfree(info);
+}
+
+/*
+ * Note: This function must be called with slot->hotplug_lock held
+ */
+int pciehp_enable_slot(struct slot *p_slot)
+{
+	u8 getstatus = 0;
+	int rc;
+	struct controller *ctrl = p_slot->ctrl;
+
+	pciehp_get_adapter_status(p_slot, &getstatus);
+	if (!getstatus) {
+		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
+		return -ENODEV;
+	}
+	if (MRL_SENS(p_slot->ctrl)) {
+		pciehp_get_latch_status(p_slot, &getstatus);
+		if (getstatus) {
+			ctrl_info(ctrl, "Latch open on slot(%s)\n",
+				  slot_name(p_slot));
+			return -ENODEV;
+		}
+	}
+
+	if (POWER_CTRL(p_slot->ctrl)) {
+		pciehp_get_power_status(p_slot, &getstatus);
+		if (getstatus) {
+			ctrl_info(ctrl, "Already enabled on slot(%s)\n",
+				  slot_name(p_slot));
+			return -EINVAL;
+		}
+	}
+
+	pciehp_get_latch_status(p_slot, &getstatus);
+
+	rc = board_added(p_slot);
+	if (rc)
+		pciehp_get_latch_status(p_slot, &getstatus);
+
+	return rc;
+}
+
+/*
+ * Note: This function must be called with slot->hotplug_lock held
+ */
+int pciehp_disable_slot(struct slot *p_slot)
+{
+	u8 getstatus = 0;
+	struct controller *ctrl = p_slot->ctrl;
+
+	if (!p_slot->ctrl)
+		return 1;
+
+	if (POWER_CTRL(p_slot->ctrl)) {
+		pciehp_get_power_status(p_slot, &getstatus);
+		if (!getstatus) {
+			ctrl_info(ctrl, "Already disabled on slot(%s)\n",
+				  slot_name(p_slot));
+			return -EINVAL;
+		}
+	}
+
+	return remove_board(p_slot);
+}
+
+int pciehp_sysfs_enable_slot(struct slot *p_slot)
+{
+	int retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGON_STATE:
+		cancel_delayed_work(&p_slot->work);
+	case STATIC_STATE:
+		p_slot->state = POWERON_STATE;
+		mutex_unlock(&p_slot->lock);
+		mutex_lock(&p_slot->hotplug_lock);
+		retval = pciehp_enable_slot(p_slot);
+		mutex_unlock(&p_slot->hotplug_lock);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWERON_STATE:
+		ctrl_info(ctrl, "Slot %s is already in powering on state\n",
+			  slot_name(p_slot));
+		break;
+	case BLINKINGOFF_STATE:
+	case POWEROFF_STATE:
+		ctrl_info(ctrl, "Already enabled on slot %s\n",
+			  slot_name(p_slot));
+		break;
+	default:
+		ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+			 p_slot->state, slot_name(p_slot));
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	return retval;
+}
+
+int pciehp_sysfs_disable_slot(struct slot *p_slot)
+{
+	int retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGOFF_STATE:
+		cancel_delayed_work(&p_slot->work);
+	case STATIC_STATE:
+		p_slot->state = POWEROFF_STATE;
+		mutex_unlock(&p_slot->lock);
+		retval = pciehp_disable_slot(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWEROFF_STATE:
+		ctrl_info(ctrl, "Slot %s is already in powering off state\n",
+			  slot_name(p_slot));
+		break;
+	case BLINKINGON_STATE:
+	case POWERON_STATE:
+		ctrl_info(ctrl, "Already disabled on slot %s\n",
+			  slot_name(p_slot));
+		break;
+	default:
+		ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+			 p_slot->state, slot_name(p_slot));
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	return retval;
+}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
new file mode 100644
index 0000000..5c24e93
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -0,0 +1,848 @@
+/*
+ * PCI Express PCI Hot Plug Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>,<kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/signal.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+
+#include "../pci.h"
+#include "pciehp.h"
+
+static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
+{
+	return ctrl->pcie->port;
+}
+
+static irqreturn_t pcie_isr(int irq, void *dev_id);
+static void start_int_poll_timer(struct controller *ctrl, int sec);
+
+/* This is the interrupt polling timeout function. */
+static void int_poll_timeout(unsigned long data)
+{
+	struct controller *ctrl = (struct controller *)data;
+
+	/* Poll for interrupt events.  regs == NULL => polling */
+	pcie_isr(0, ctrl);
+
+	init_timer(&ctrl->poll_timer);
+	if (!pciehp_poll_time)
+		pciehp_poll_time = 2; /* default polling interval is 2 sec */
+
+	start_int_poll_timer(ctrl, pciehp_poll_time);
+}
+
+/* This function starts the interrupt polling timer. */
+static void start_int_poll_timer(struct controller *ctrl, int sec)
+{
+	/* Clamp to sane value */
+	if ((sec <= 0) || (sec > 60))
+		sec = 2;
+
+	ctrl->poll_timer.function = &int_poll_timeout;
+	ctrl->poll_timer.data = (unsigned long)ctrl;
+	ctrl->poll_timer.expires = jiffies + sec * HZ;
+	add_timer(&ctrl->poll_timer);
+}
+
+static inline int pciehp_request_irq(struct controller *ctrl)
+{
+	int retval, irq = ctrl->pcie->irq;
+
+	/* Install interrupt polling timer. Start with 10 sec delay */
+	if (pciehp_poll_mode) {
+		init_timer(&ctrl->poll_timer);
+		start_int_poll_timer(ctrl, 10);
+		return 0;
+	}
+
+	/* Installs the interrupt handler */
+	retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl);
+	if (retval)
+		ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
+			 irq);
+	return retval;
+}
+
+static inline void pciehp_free_irq(struct controller *ctrl)
+{
+	if (pciehp_poll_mode)
+		del_timer_sync(&ctrl->poll_timer);
+	else
+		free_irq(ctrl->pcie->irq, ctrl);
+}
+
+static int pcie_poll_cmd(struct controller *ctrl, int timeout)
+{
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 slot_status;
+
+	while (true) {
+		pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+		if (slot_status == (u16) ~0) {
+			ctrl_info(ctrl, "%s: no response from device\n",
+				  __func__);
+			return 0;
+		}
+
+		if (slot_status & PCI_EXP_SLTSTA_CC) {
+			pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+						   PCI_EXP_SLTSTA_CC);
+			return 1;
+		}
+		if (timeout < 0)
+			break;
+		msleep(10);
+		timeout -= 10;
+	}
+	return 0;	/* timeout */
+}
+
+static void pcie_wait_cmd(struct controller *ctrl)
+{
+	unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
+	unsigned long duration = msecs_to_jiffies(msecs);
+	unsigned long cmd_timeout = ctrl->cmd_started + duration;
+	unsigned long now, timeout;
+	int rc;
+
+	/*
+	 * If the controller does not generate notifications for command
+	 * completions, we never need to wait between writes.
+	 */
+	if (NO_CMD_CMPL(ctrl))
+		return;
+
+	if (!ctrl->cmd_busy)
+		return;
+
+	/*
+	 * Even if the command has already timed out, we want to call
+	 * pcie_poll_cmd() so it can clear PCI_EXP_SLTSTA_CC.
+	 */
+	now = jiffies;
+	if (time_before_eq(cmd_timeout, now))
+		timeout = 1;
+	else
+		timeout = cmd_timeout - now;
+
+	if (ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE &&
+	    ctrl->slot_ctrl & PCI_EXP_SLTCTL_CCIE)
+		rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
+	else
+		rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout));
+
+	/*
+	 * Controllers with errata like Intel CF118 don't generate
+	 * completion notifications unless the power/indicator/interlock
+	 * control bits are changed.  On such controllers, we'll emit this
+	 * timeout message when we wait for completion of commands that
+	 * don't change those bits, e.g., commands that merely enable
+	 * interrupts.
+	 */
+	if (!rc)
+		ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n",
+			  ctrl->slot_ctrl,
+			  jiffies_to_msecs(jiffies - ctrl->cmd_started));
+}
+
+static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
+			      u16 mask, bool wait)
+{
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 slot_ctrl;
+
+	mutex_lock(&ctrl->ctrl_lock);
+
+	/*
+	 * Always wait for any previous command that might still be in progress
+	 */
+	pcie_wait_cmd(ctrl);
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+	if (slot_ctrl == (u16) ~0) {
+		ctrl_info(ctrl, "%s: no response from device\n", __func__);
+		goto out;
+	}
+
+	slot_ctrl &= ~mask;
+	slot_ctrl |= (cmd & mask);
+	ctrl->cmd_busy = 1;
+	smp_mb();
+	pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);
+	ctrl->cmd_started = jiffies;
+	ctrl->slot_ctrl = slot_ctrl;
+
+	/*
+	 * Optionally wait for the hardware to be ready for a new command,
+	 * indicating completion of the above issued command.
+	 */
+	if (wait)
+		pcie_wait_cmd(ctrl);
+
+out:
+	mutex_unlock(&ctrl->ctrl_lock);
+}
+
+/**
+ * pcie_write_cmd - Issue controller command
+ * @ctrl: controller to which the command is issued
+ * @cmd:  command value written to slot control register
+ * @mask: bitmask of slot control register to be modified
+ */
+static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
+{
+	pcie_do_write_cmd(ctrl, cmd, mask, true);
+}
+
+/* Same as above without waiting for the hardware to latch */
+static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask)
+{
+	pcie_do_write_cmd(ctrl, cmd, mask, false);
+}
+
+bool pciehp_check_link_active(struct controller *ctrl)
+{
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 lnk_status;
+	bool ret;
+
+	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+	ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+
+	if (ret)
+		ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
+
+	return ret;
+}
+
+static void __pcie_wait_link_active(struct controller *ctrl, bool active)
+{
+	int timeout = 1000;
+
+	if (pciehp_check_link_active(ctrl) == active)
+		return;
+	while (timeout > 0) {
+		msleep(10);
+		timeout -= 10;
+		if (pciehp_check_link_active(ctrl) == active)
+			return;
+	}
+	ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
+			active ? "set" : "cleared");
+}
+
+static void pcie_wait_link_active(struct controller *ctrl)
+{
+	__pcie_wait_link_active(ctrl, true);
+}
+
+static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
+{
+	u32 l;
+	int count = 0;
+	int delay = 1000, step = 20;
+	bool found = false;
+
+	do {
+		found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0);
+		count++;
+
+		if (found)
+			break;
+
+		msleep(step);
+		delay -= step;
+	} while (delay > 0);
+
+	if (count > 1 && pciehp_debug)
+		printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n",
+			pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+			PCI_FUNC(devfn), count, step, l);
+
+	return found;
+}
+
+int pciehp_check_link_status(struct controller *ctrl)
+{
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	bool found;
+	u16 lnk_status;
+
+	/*
+	 * Data Link Layer Link Active Reporting must be capable for
+	 * hot-plug capable downstream port. But old controller might
+	 * not implement it. In this case, we wait for 1000 ms.
+	*/
+	if (ctrl->link_active_reporting)
+		pcie_wait_link_active(ctrl);
+	else
+		msleep(1000);
+
+	/* wait 100ms before read pci conf, and try in 1s */
+	msleep(100);
+	found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
+					PCI_DEVFN(0, 0));
+
+	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+	ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
+	if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
+	    !(lnk_status & PCI_EXP_LNKSTA_NLW)) {
+		ctrl_err(ctrl, "link training error: status %#06x\n",
+			 lnk_status);
+		return -1;
+	}
+
+	pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
+
+	if (!found)
+		return -1;
+
+	return 0;
+}
+
+static int __pciehp_link_set(struct controller *ctrl, bool enable)
+{
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 lnk_ctrl;
+
+	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl);
+
+	if (enable)
+		lnk_ctrl &= ~PCI_EXP_LNKCTL_LD;
+	else
+		lnk_ctrl |= PCI_EXP_LNKCTL_LD;
+
+	pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl);
+	ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl);
+	return 0;
+}
+
+static int pciehp_link_enable(struct controller *ctrl)
+{
+	return __pciehp_link_set(ctrl, true);
+}
+
+void pciehp_get_attention_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 slot_ctrl;
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
+
+	switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) {
+	case PCI_EXP_SLTCTL_ATTN_IND_ON:
+		*status = 1;	/* On */
+		break;
+	case PCI_EXP_SLTCTL_ATTN_IND_BLINK:
+		*status = 2;	/* Blink */
+		break;
+	case PCI_EXP_SLTCTL_ATTN_IND_OFF:
+		*status = 0;	/* Off */
+		break;
+	default:
+		*status = 0xFF;
+		break;
+	}
+}
+
+void pciehp_get_power_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 slot_ctrl;
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
+
+	switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) {
+	case PCI_EXP_SLTCTL_PWR_ON:
+		*status = 1;	/* On */
+		break;
+	case PCI_EXP_SLTCTL_PWR_OFF:
+		*status = 0;	/* Off */
+		break;
+	default:
+		*status = 0xFF;
+		break;
+	}
+}
+
+void pciehp_get_latch_status(struct slot *slot, u8 *status)
+{
+	struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+	u16 slot_status;
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+	*status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
+}
+
+void pciehp_get_adapter_status(struct slot *slot, u8 *status)
+{
+	struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+	u16 slot_status;
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+	*status = !!(slot_status & PCI_EXP_SLTSTA_PDS);
+}
+
+int pciehp_query_power_fault(struct slot *slot)
+{
+	struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+	u16 slot_status;
+
+	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+	return !!(slot_status & PCI_EXP_SLTSTA_PFD);
+}
+
+void pciehp_set_attention_status(struct slot *slot, u8 value)
+{
+	struct controller *ctrl = slot->ctrl;
+	u16 slot_cmd;
+
+	if (!ATTN_LED(ctrl))
+		return;
+
+	switch (value) {
+	case 0:		/* turn off */
+		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF;
+		break;
+	case 1:		/* turn on */
+		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON;
+		break;
+	case 2:		/* turn blink */
+		slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK;
+		break;
+	default:
+		return;
+	}
+	pcie_write_cmd_nowait(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+}
+
+void pciehp_green_led_on(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	if (!PWR_LED(ctrl))
+		return;
+
+	pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
+			      PCI_EXP_SLTCTL_PIC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+		 PCI_EXP_SLTCTL_PWR_IND_ON);
+}
+
+void pciehp_green_led_off(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	if (!PWR_LED(ctrl))
+		return;
+
+	pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+			      PCI_EXP_SLTCTL_PIC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+		 PCI_EXP_SLTCTL_PWR_IND_OFF);
+}
+
+void pciehp_green_led_blink(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	if (!PWR_LED(ctrl))
+		return;
+
+	pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
+			      PCI_EXP_SLTCTL_PIC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+		 PCI_EXP_SLTCTL_PWR_IND_BLINK);
+}
+
+int pciehp_power_on_slot(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 slot_status;
+	int retval;
+
+	/* Clear sticky power-fault bit from previous power failures */
+	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+	if (slot_status & PCI_EXP_SLTSTA_PFD)
+		pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+					   PCI_EXP_SLTSTA_PFD);
+	ctrl->power_fault_detected = 0;
+
+	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+		 PCI_EXP_SLTCTL_PWR_ON);
+
+	retval = pciehp_link_enable(ctrl);
+	if (retval)
+		ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);
+
+	return retval;
+}
+
+void pciehp_power_off_slot(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+		 PCI_EXP_SLTCTL_PWR_OFF);
+}
+
+static irqreturn_t pcie_isr(int irq, void *dev_id)
+{
+	struct controller *ctrl = (struct controller *)dev_id;
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	struct pci_bus *subordinate = pdev->subordinate;
+	struct pci_dev *dev;
+	struct slot *slot = ctrl->slot;
+	u16 detected, intr_loc;
+	u8 present;
+	bool link;
+
+	/*
+	 * In order to guarantee that all interrupt events are
+	 * serviced, we need to re-inspect Slot Status register after
+	 * clearing what is presumed to be the last pending interrupt.
+	 */
+	intr_loc = 0;
+	do {
+		pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected);
+		if (detected == (u16) ~0) {
+			ctrl_info(ctrl, "%s: no response from device\n",
+				  __func__);
+			return IRQ_HANDLED;
+		}
+
+		detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+			     PCI_EXP_SLTSTA_PDC |
+			     PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
+		detected &= ~intr_loc;
+		intr_loc |= detected;
+		if (!intr_loc)
+			return IRQ_NONE;
+		if (detected)
+			pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+						   intr_loc);
+	} while (detected);
+
+	ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc);
+
+	/* Check Command Complete Interrupt Pending */
+	if (intr_loc & PCI_EXP_SLTSTA_CC) {
+		ctrl->cmd_busy = 0;
+		smp_mb();
+		wake_up(&ctrl->queue);
+	}
+
+	if (subordinate) {
+		list_for_each_entry(dev, &subordinate->devices, bus_list) {
+			if (dev->ignore_hotplug) {
+				ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n",
+					 intr_loc, pci_name(dev));
+				return IRQ_HANDLED;
+			}
+		}
+	}
+
+	if (!(intr_loc & ~PCI_EXP_SLTSTA_CC))
+		return IRQ_HANDLED;
+
+	/* Check Attention Button Pressed */
+	if (intr_loc & PCI_EXP_SLTSTA_ABP) {
+		ctrl_info(ctrl, "Button pressed on Slot(%s)\n",
+			  slot_name(slot));
+		pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+	}
+
+	/* Check Presence Detect Changed */
+	if (intr_loc & PCI_EXP_SLTSTA_PDC) {
+		pciehp_get_adapter_status(slot, &present);
+		ctrl_info(ctrl, "Card %spresent on Slot(%s)\n",
+			  present ? "" : "not ", slot_name(slot));
+		pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
+					     INT_PRESENCE_OFF);
+	}
+
+	/* Check Power Fault Detected */
+	if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
+		ctrl->power_fault_detected = 1;
+		ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot));
+		pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
+	}
+
+	if (intr_loc & PCI_EXP_SLTSTA_DLLSC) {
+		link = pciehp_check_link_active(ctrl);
+		ctrl_info(ctrl, "slot(%s): Link %s event\n",
+			  slot_name(slot), link ? "Up" : "Down");
+		pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
+					     INT_LINK_DOWN);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void pcie_enable_notification(struct controller *ctrl)
+{
+	u16 cmd, mask;
+
+	/*
+	 * TBD: Power fault detected software notification support.
+	 *
+	 * Power fault detected software notification is not enabled
+	 * now, because it caused power fault detected interrupt storm
+	 * on some machines. On those machines, power fault detected
+	 * bit in the slot status register was set again immediately
+	 * when it is cleared in the interrupt service routine, and
+	 * next power fault detected interrupt was notified again.
+	 */
+
+	/*
+	 * Always enable link events: thus link-up and link-down shall
+	 * always be treated as hotplug and unplug respectively. Enable
+	 * presence detect only if Attention Button is not present.
+	 */
+	cmd = PCI_EXP_SLTCTL_DLLSCE;
+	if (ATTN_BUTTN(ctrl))
+		cmd |= PCI_EXP_SLTCTL_ABPE;
+	else
+		cmd |= PCI_EXP_SLTCTL_PDCE;
+	if (!pciehp_poll_mode)
+		cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
+
+	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
+		PCI_EXP_SLTCTL_PFDE |
+		PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
+		PCI_EXP_SLTCTL_DLLSCE);
+
+	pcie_write_cmd_nowait(ctrl, cmd, mask);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
+}
+
+static void pcie_disable_notification(struct controller *ctrl)
+{
+	u16 mask;
+
+	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
+		PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
+		PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
+		PCI_EXP_SLTCTL_DLLSCE);
+	pcie_write_cmd(ctrl, 0, mask);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
+}
+
+/*
+ * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
+ * bus reset of the bridge, but at the same time we want to ensure that it is
+ * not seen as a hot-unplug, followed by the hot-plug of the device. Thus,
+ * disable link state notification and presence detection change notification
+ * momentarily, if we see that they could interfere. Also, clear any spurious
+ * events after.
+ */
+int pciehp_reset_slot(struct slot *slot, int probe)
+{
+	struct controller *ctrl = slot->ctrl;
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+	u16 stat_mask = 0, ctrl_mask = 0;
+
+	if (probe)
+		return 0;
+
+	if (!ATTN_BUTTN(ctrl)) {
+		ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
+		stat_mask |= PCI_EXP_SLTSTA_PDC;
+	}
+	ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
+	stat_mask |= PCI_EXP_SLTSTA_DLLSC;
+
+	pcie_write_cmd(ctrl, 0, ctrl_mask);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
+	if (pciehp_poll_mode)
+		del_timer_sync(&ctrl->poll_timer);
+
+	pci_reset_bridge_secondary_bus(ctrl->pcie->port);
+
+	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
+	pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
+	if (pciehp_poll_mode)
+		int_poll_timeout(ctrl->poll_timer.data);
+
+	return 0;
+}
+
+int pcie_init_notification(struct controller *ctrl)
+{
+	if (pciehp_request_irq(ctrl))
+		return -1;
+	pcie_enable_notification(ctrl);
+	ctrl->notification_enabled = 1;
+	return 0;
+}
+
+static void pcie_shutdown_notification(struct controller *ctrl)
+{
+	if (ctrl->notification_enabled) {
+		pcie_disable_notification(ctrl);
+		pciehp_free_irq(ctrl);
+		ctrl->notification_enabled = 0;
+	}
+}
+
+static int pcie_init_slot(struct controller *ctrl)
+{
+	struct slot *slot;
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (!slot)
+		return -ENOMEM;
+
+	slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl));
+	if (!slot->wq)
+		goto abort;
+
+	slot->ctrl = ctrl;
+	mutex_init(&slot->lock);
+	mutex_init(&slot->hotplug_lock);
+	INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
+	ctrl->slot = slot;
+	return 0;
+abort:
+	kfree(slot);
+	return -ENOMEM;
+}
+
+static void pcie_cleanup_slot(struct controller *ctrl)
+{
+	struct slot *slot = ctrl->slot;
+	cancel_delayed_work(&slot->work);
+	destroy_workqueue(slot->wq);
+	kfree(slot);
+}
+
+static inline void dbg_ctrl(struct controller *ctrl)
+{
+	struct pci_dev *pdev = ctrl->pcie->port;
+	u16 reg16;
+
+	if (!pciehp_debug)
+		return;
+
+	ctrl_info(ctrl, "Slot Capabilities      : 0x%08x\n", ctrl->slot_cap);
+	pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &reg16);
+	ctrl_info(ctrl, "Slot Status            : 0x%04x\n", reg16);
+	pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &reg16);
+	ctrl_info(ctrl, "Slot Control           : 0x%04x\n", reg16);
+}
+
+#define FLAG(x, y)	(((x) & (y)) ? '+' : '-')
+
+struct controller *pcie_init(struct pcie_device *dev)
+{
+	struct controller *ctrl;
+	u32 slot_cap, link_cap;
+	struct pci_dev *pdev = dev->port;
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl) {
+		dev_err(&dev->device, "%s: Out of memory\n", __func__);
+		goto abort;
+	}
+	ctrl->pcie = dev;
+	pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
+	ctrl->slot_cap = slot_cap;
+	mutex_init(&ctrl->ctrl_lock);
+	init_waitqueue_head(&ctrl->queue);
+	dbg_ctrl(ctrl);
+
+	/* Check if Data Link Layer Link Active Reporting is implemented */
+	pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
+	if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
+		ctrl->link_active_reporting = 1;
+
+	/* Clear all remaining event bits in Slot Status register */
+	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+		PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+		PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
+		PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
+
+	ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c\n",
+		(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
+		FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_HPC),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
+		FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
+		FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC));
+
+	if (pcie_init_slot(ctrl))
+		goto abort_ctrl;
+
+	return ctrl;
+
+abort_ctrl:
+	kfree(ctrl);
+abort:
+	return NULL;
+}
+
+void pciehp_release_ctrl(struct controller *ctrl)
+{
+	pcie_shutdown_notification(ctrl);
+	pcie_cleanup_slot(ctrl);
+	kfree(ctrl);
+}
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
new file mode 100644
index 0000000..9e69403
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -0,0 +1,128 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "pciehp.h"
+
+int pciehp_configure_device(struct slot *p_slot)
+{
+	struct pci_dev *dev;
+	struct pci_dev *bridge = p_slot->ctrl->pcie->port;
+	struct pci_bus *parent = bridge->subordinate;
+	int num, ret = 0;
+	struct controller *ctrl = p_slot->ctrl;
+
+	pci_lock_rescan_remove();
+
+	dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
+	if (dev) {
+		ctrl_err(ctrl, "Device %s already exists at %04x:%02x:00, cannot hot-add\n",
+			 pci_name(dev), pci_domain_nr(parent), parent->number);
+		pci_dev_put(dev);
+		ret = -EEXIST;
+		goto out;
+	}
+
+	num = pci_scan_slot(parent, PCI_DEVFN(0, 0));
+	if (num == 0) {
+		ctrl_err(ctrl, "No new device found\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	list_for_each_entry(dev, &parent->devices, bus_list)
+		if (pci_is_bridge(dev))
+			pci_hp_add_bridge(dev);
+
+	pci_assign_unassigned_bridge_resources(bridge);
+	pcie_bus_configure_settings(parent);
+	pci_bus_add_devices(parent);
+
+ out:
+	pci_unlock_rescan_remove();
+	return ret;
+}
+
+int pciehp_unconfigure_device(struct slot *p_slot)
+{
+	int rc = 0;
+	u8 bctl = 0;
+	u8 presence = 0;
+	struct pci_dev *dev, *temp;
+	struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
+	u16 command;
+	struct controller *ctrl = p_slot->ctrl;
+
+	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
+		 __func__, pci_domain_nr(parent), parent->number);
+	pciehp_get_adapter_status(p_slot, &presence);
+
+	pci_lock_rescan_remove();
+
+	/*
+	 * Stopping an SR-IOV PF device removes all the associated VFs,
+	 * which will update the bus->devices list and confuse the
+	 * iterator.  Therefore, iterate in reverse so we remove the VFs
+	 * first, then the PF.  We do the same in pci_stop_bus_device().
+	 */
+	list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
+					 bus_list) {
+		pci_dev_get(dev);
+		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) {
+			pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl);
+			if (bctl & PCI_BRIDGE_CTL_VGA) {
+				ctrl_err(ctrl,
+					 "Cannot remove display device %s\n",
+					 pci_name(dev));
+				pci_dev_put(dev);
+				rc = -EINVAL;
+				break;
+			}
+		}
+		pci_stop_and_remove_bus_device(dev);
+		/*
+		 * Ensure that no new Requests will be generated from
+		 * the device.
+		 */
+		if (presence) {
+			pci_read_config_word(dev, PCI_COMMAND, &command);
+			command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
+			command |= PCI_COMMAND_INTX_DISABLE;
+			pci_write_config_word(dev, PCI_COMMAND, command);
+		}
+		pci_dev_put(dev);
+	}
+
+	pci_unlock_rescan_remove();
+	return rc;
+}
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
new file mode 100644
index 0000000..d062c00
--- /dev/null
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -0,0 +1,365 @@
+/*
+ * PCI Hot Plug Controller Skeleton Driver - 0.3
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ *
+ * 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.
+ *
+ * This driver is to be used as a skeleton driver to show how to interface
+ * with the pci hotplug core easily.
+ *
+ * Send feedback to <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/init.h>
+
+#define SLOT_NAME_SIZE	10
+struct slot {
+	u8 number;
+	struct hotplug_slot *hotplug_slot;
+	struct list_head slot_list;
+	char name[SLOT_NAME_SIZE];
+};
+
+static LIST_HEAD(slot_list);
+
+#define MY_NAME	"pcihp_skeleton"
+
+#define dbg(format, arg...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG "%s: " format "\n",	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static bool debug;
+static int num_slots;
+
+#define DRIVER_VERSION	"0.3"
+#define DRIVER_AUTHOR	"Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC	"Hot Plug PCI Controller Skeleton Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot		(struct hotplug_slot *slot);
+static int disable_slot		(struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int hardware_test	(struct hotplug_slot *slot, u32 value);
+static int get_power_status	(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status	(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops skel_hotplug_slot_ops = {
+	.enable_slot =		enable_slot,
+	.disable_slot =		disable_slot,
+	.set_attention_status =	set_attention_status,
+	.hardware_test =	hardware_test,
+	.get_power_status =	get_power_status,
+	.get_attention_status =	get_attention_status,
+	.get_latch_status =	get_latch_status,
+	.get_adapter_status =	get_adapter_status,
+};
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in code here to enable the specified slot
+	 */
+
+	return retval;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in code here to disable the specified slot
+	 */
+
+	return retval;
+}
+
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	switch (status) {
+	case 0:
+		/*
+		 * Fill in code here to turn light off
+		 */
+		break;
+
+	case 1:
+	default:
+		/*
+		 * Fill in code here to turn light on
+		 */
+		break;
+	}
+
+	return retval;
+}
+
+static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	switch (value) {
+	case 0:
+		/* Specify a test here */
+		break;
+	case 1:
+		/* Specify another test here */
+		break;
+	}
+
+	return retval;
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in logic to get the current power status of the specific
+	 * slot and store it in the *value location.
+	 */
+
+	return retval;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in logic to get the current attention status of the specific
+	 * slot and store it in the *value location.
+	 */
+
+	return retval;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in logic to get the current latch status of the specific
+	 * slot and store it in the *value location.
+	 */
+
+	return retval;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+	int retval = 0;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+
+	/*
+	 * Fill in logic to get the current adapter status of the specific
+	 * slot and store it in the *value location.
+	 */
+
+	return retval;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+static void make_slot_name(struct slot *slot)
+{
+	/*
+	 * Stupid way to make a filename out of the slot name.
+	 * replace this if your hardware provides a better way to name slots.
+	 */
+	snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
+}
+
+/**
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static int __init init_slots(void)
+{
+	struct slot *slot;
+	struct hotplug_slot *hotplug_slot;
+	struct hotplug_slot_info *info;
+	int retval;
+	int i;
+
+	/*
+	 * Create a structure for each slot, and register that slot
+	 * with the pci_hotplug subsystem.
+	 */
+	for (i = 0; i < num_slots; ++i) {
+		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+		if (!slot) {
+			retval = -ENOMEM;
+			goto error;
+		}
+
+		hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+		if (!hotplug_slot) {
+			retval = -ENOMEM;
+			goto error_slot;
+		}
+		slot->hotplug_slot = hotplug_slot;
+
+		info = kzalloc(sizeof(*info), GFP_KERNEL);
+		if (!info) {
+			retval = -ENOMEM;
+			goto error_hpslot;
+		}
+		hotplug_slot->info = info;
+
+		slot->number = i;
+
+		hotplug_slot->name = slot->name;
+		hotplug_slot->private = slot;
+		hotplug_slot->release = &release_slot;
+		make_slot_name(slot);
+		hotplug_slot->ops = &skel_hotplug_slot_ops;
+
+		/*
+		 * Initialize the slot info structure with some known
+		 * good values.
+		 */
+		get_power_status(hotplug_slot, &info->power_status);
+		get_attention_status(hotplug_slot, &info->attention_status);
+		get_latch_status(hotplug_slot, &info->latch_status);
+		get_adapter_status(hotplug_slot, &info->adapter_status);
+
+		dbg("registering slot %d\n", i);
+		retval = pci_hp_register(slot->hotplug_slot);
+		if (retval) {
+			err("pci_hp_register failed with error %d\n", retval);
+			goto error_info;
+		}
+
+		/* add slot to our internal list */
+		list_add(&slot->slot_list, &slot_list);
+	}
+
+	return 0;
+error_info:
+	kfree(info);
+error_hpslot:
+	kfree(hotplug_slot);
+error_slot:
+	kfree(slot);
+error:
+	return retval;
+}
+
+static void __exit cleanup_slots(void)
+{
+	struct list_head *tmp;
+	struct list_head *next;
+	struct slot *slot;
+
+	/*
+	 * Unregister all of our slots with the pci_hotplug subsystem.
+	 * Memory will be freed in release_slot() callback after slot's
+	 * lifespan is finished.
+	 */
+	list_for_each_safe(tmp, next, &slot_list) {
+		slot = list_entry(tmp, struct slot, slot_list);
+		list_del(&slot->slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+}
+
+static int __init pcihp_skel_init(void)
+{
+	int retval;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+	/*
+	 * Do specific initialization stuff for your driver here
+	 * like initializing your controller hardware (if any) and
+	 * determining the number of slots you have in the system
+	 * right now.
+	 */
+	num_slots = 5;
+
+	return init_slots();
+}
+
+static void __exit pcihp_skel_exit(void)
+{
+	/*
+	 * Clean everything up.
+	 */
+	cleanup_slots();
+}
+
+module_init(pcihp_skel_init);
+module_exit(pcihp_skel_exit);
diff --git a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h
new file mode 100644
index 0000000..81df939
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar.h
@@ -0,0 +1,24 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ *      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.
+ */
+#ifndef _RPADLPAR_IO_H_
+#define _RPADLPAR_IO_H_
+
+int dlpar_sysfs_init(void);
+void dlpar_sysfs_exit(void);
+
+int dlpar_add_slot(char *drc_name);
+int dlpar_remove_slot(char *drc_name);
+
+#endif
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
new file mode 100644
index 0000000..f2fcbe9
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -0,0 +1,487 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * Linda Xie <lxie@us.ibm.com>
+ *
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ *      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.
+ */
+
+#undef DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+#include <asm/pci-bridge.h>
+#include <linux/mutex.h>
+#include <asm/rtas.h>
+#include <asm/vio.h>
+
+#include "../pci.h"
+#include "rpaphp.h"
+#include "rpadlpar.h"
+
+static DEFINE_MUTEX(rpadlpar_mutex);
+
+#define DLPAR_MODULE_NAME "rpadlpar_io"
+
+#define NODE_TYPE_VIO  1
+#define NODE_TYPE_SLOT 2
+#define NODE_TYPE_PHB  3
+
+static struct device_node *find_vio_slot_node(char *drc_name)
+{
+	struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
+	struct device_node *dn = NULL;
+	char *name;
+	int rc;
+
+	if (!parent)
+		return NULL;
+
+	while ((dn = of_get_next_child(parent, dn))) {
+		rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
+		if ((rc == 0) && (!strcmp(drc_name, name)))
+			break;
+	}
+
+	return dn;
+}
+
+/* Find dlpar-capable pci node that contains the specified name and type */
+static struct device_node *find_php_slot_pci_node(char *drc_name,
+						  char *drc_type)
+{
+	struct device_node *np = NULL;
+	char *name;
+	char *type;
+	int rc;
+
+	while ((np = of_find_node_by_name(np, "pci"))) {
+		rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
+		if (rc == 0)
+			if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
+				break;
+	}
+
+	return np;
+}
+
+static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
+{
+	struct device_node *dn;
+
+	dn = find_php_slot_pci_node(drc_name, "SLOT");
+	if (dn) {
+		*node_type = NODE_TYPE_SLOT;
+		return dn;
+	}
+
+	dn = find_php_slot_pci_node(drc_name, "PHB");
+	if (dn) {
+		*node_type = NODE_TYPE_PHB;
+		return dn;
+	}
+
+	dn = find_vio_slot_node(drc_name);
+	if (dn) {
+		*node_type = NODE_TYPE_VIO;
+		return dn;
+	}
+
+	return NULL;
+}
+
+/**
+ * find_php_slot - return hotplug slot structure for device node
+ * @dn: target &device_node
+ *
+ * This routine will return the hotplug slot structure
+ * for a given device node. Note that built-in PCI slots
+ * may be dlpar-able, but not hot-pluggable, so this routine
+ * will return NULL for built-in PCI slots.
+ */
+static struct slot *find_php_slot(struct device_node *dn)
+{
+	struct list_head *tmp, *n;
+	struct slot *slot;
+
+	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+		slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+		if (slot->dn == dn)
+			return slot;
+	}
+
+	return NULL;
+}
+
+static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
+					struct device_node *dev_dn)
+{
+	struct pci_dev *tmp = NULL;
+	struct device_node *child_dn;
+
+	list_for_each_entry(tmp, &parent->devices, bus_list) {
+		child_dn = pci_device_to_OF_node(tmp);
+		if (child_dn == dev_dn)
+			return tmp;
+	}
+	return NULL;
+}
+
+static void dlpar_pci_add_bus(struct device_node *dn)
+{
+	struct pci_dn *pdn = PCI_DN(dn);
+	struct pci_controller *phb = pdn->phb;
+	struct pci_dev *dev = NULL;
+
+	eeh_add_device_tree_early(pdn);
+
+	/* Add EADS device to PHB bus, adding new entry to bus->devices */
+	dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
+	if (!dev) {
+		printk(KERN_ERR "%s: failed to create pci dev for %s\n",
+				__func__, dn->full_name);
+		return;
+	}
+
+	/* Scan below the new bridge */
+	if (pci_is_bridge(dev))
+		of_scan_pci_bridge(dev);
+
+	/* Map IO space for child bus, which may or may not succeed */
+	pcibios_map_io_space(dev->subordinate);
+
+	/* Finish adding it : resource allocation, adding devices, etc...
+	 * Note that we need to perform the finish pass on the -parent-
+	 * bus of the EADS bridge so the bridge device itself gets
+	 * properly added
+	 */
+	pcibios_finish_adding_to_bus(phb->bus);
+}
+
+static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
+{
+	struct pci_dev *dev;
+	struct pci_controller *phb;
+
+	if (pcibios_find_pci_bus(dn))
+		return -EINVAL;
+
+	/* Add pci bus */
+	dlpar_pci_add_bus(dn);
+
+	/* Confirm new bridge dev was created */
+	phb = PCI_DN(dn)->phb;
+	dev = dlpar_find_new_dev(phb->bus, dn);
+
+	if (!dev) {
+		printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
+			drc_name);
+		return -EIO;
+	}
+
+	if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+		printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
+			__func__, dev->hdr_type, drc_name);
+		return -EIO;
+	}
+
+	/* Add hotplug slot */
+	if (rpaphp_add_slot(dn)) {
+		printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
+			__func__, drc_name);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
+{
+	struct slot *slot;
+	struct pci_dn *pdn;
+	int rc = 0;
+
+	if (!pcibios_find_pci_bus(dn))
+		return -EINVAL;
+
+	/* If pci slot is hotpluggable, use hotplug to remove it */
+	slot = find_php_slot(dn);
+	if (slot && rpaphp_deregister_slot(slot)) {
+		printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+		       __func__, drc_name);
+		return -EIO;
+	}
+
+	pdn = dn->data;
+	BUG_ON(!pdn || !pdn->phb);
+	rc = remove_phb_dynamic(pdn->phb);
+	if (rc < 0)
+		return rc;
+
+	pdn->phb = NULL;
+
+	return 0;
+}
+
+static int dlpar_add_phb(char *drc_name, struct device_node *dn)
+{
+	struct pci_controller *phb;
+
+	if (PCI_DN(dn) && PCI_DN(dn)->phb) {
+		/* PHB already exists */
+		return -EINVAL;
+	}
+
+	phb = init_phb_dynamic(dn);
+	if (!phb)
+		return -EIO;
+
+	if (rpaphp_add_slot(dn)) {
+		printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
+			__func__, drc_name);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
+{
+	struct vio_dev *vio_dev;
+
+	vio_dev = vio_find_node(dn);
+	if (vio_dev) {
+		put_device(&vio_dev->dev);
+		return -EINVAL;
+	}
+
+	if (!vio_register_device_node(dn)) {
+		printk(KERN_ERR
+			"%s: failed to register vio node %s\n",
+			__func__, drc_name);
+		return -EIO;
+	}
+	return 0;
+}
+
+/**
+ * dlpar_add_slot - DLPAR add an I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Make the hotplug module and the kernel aware of a newly added I/O Slot.
+ * Return Codes:
+ * 0			Success
+ * -ENODEV		Not a valid drc_name
+ * -EINVAL		Slot already added
+ * -ERESTARTSYS		Signalled before obtaining lock
+ * -EIO			Internal PCI Error
+ */
+int dlpar_add_slot(char *drc_name)
+{
+	struct device_node *dn = NULL;
+	int node_type;
+	int rc = -EIO;
+
+	if (mutex_lock_interruptible(&rpadlpar_mutex))
+		return -ERESTARTSYS;
+
+	/* Find newly added node */
+	dn = find_dlpar_node(drc_name, &node_type);
+	if (!dn) {
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	switch (node_type) {
+		case NODE_TYPE_VIO:
+			rc = dlpar_add_vio_slot(drc_name, dn);
+			break;
+		case NODE_TYPE_SLOT:
+			rc = dlpar_add_pci_slot(drc_name, dn);
+			break;
+		case NODE_TYPE_PHB:
+			rc = dlpar_add_phb(drc_name, dn);
+			break;
+	}
+
+	printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
+exit:
+	mutex_unlock(&rpadlpar_mutex);
+	return rc;
+}
+
+/**
+ * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
+ * @drc_name: drc-name of newly added slot
+ * @dn: &device_node
+ *
+ * Remove the kernel and hotplug representations of an I/O Slot.
+ * Return Codes:
+ * 0			Success
+ * -EINVAL		Vio dev doesn't exist
+ */
+static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
+{
+	struct vio_dev *vio_dev;
+
+	vio_dev = vio_find_node(dn);
+	if (!vio_dev)
+		return -EINVAL;
+
+	vio_unregister_device(vio_dev);
+
+	put_device(&vio_dev->dev);
+
+	return 0;
+}
+
+/**
+ * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
+ * @drc_name: drc-name of newly added slot
+ * @dn: &device_node
+ *
+ * Remove the kernel and hotplug representations of a PCI I/O Slot.
+ * Return Codes:
+ * 0			Success
+ * -ENODEV		Not a valid drc_name
+ * -EIO			Internal PCI Error
+ */
+int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
+{
+	struct pci_bus *bus;
+	struct slot *slot;
+	int ret = 0;
+
+	pci_lock_rescan_remove();
+
+	bus = pcibios_find_pci_bus(dn);
+	if (!bus) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
+		 bus->self ? pci_name(bus->self) : "<!PHB!>");
+
+	slot = find_php_slot(dn);
+	if (slot) {
+		pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
+			 pci_domain_nr(bus), bus->number);
+
+		if (rpaphp_deregister_slot(slot)) {
+			printk(KERN_ERR
+				"%s: unable to remove hotplug slot %s\n",
+				__func__, drc_name);
+			ret = -EIO;
+			goto out;
+		}
+	}
+
+	/* Remove all devices below slot */
+	pcibios_remove_pci_devices(bus);
+
+	/* Unmap PCI IO space */
+	if (pcibios_unmap_io_space(bus)) {
+		printk(KERN_ERR "%s: failed to unmap bus range\n",
+			__func__);
+		ret = -ERANGE;
+		goto out;
+	}
+
+	/* Remove the EADS bridge device itself */
+	BUG_ON(!bus->self);
+	pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
+	pci_stop_and_remove_bus_device(bus->self);
+
+ out:
+	pci_unlock_rescan_remove();
+	return ret;
+}
+
+/**
+ * dlpar_remove_slot - DLPAR remove an I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Remove the kernel and hotplug representations of an I/O Slot.
+ * Return Codes:
+ * 0			Success
+ * -ENODEV		Not a valid drc_name
+ * -EINVAL		Slot already removed
+ * -ERESTARTSYS		Signalled before obtaining lock
+ * -EIO			Internal Error
+ */
+int dlpar_remove_slot(char *drc_name)
+{
+	struct device_node *dn;
+	int node_type;
+	int rc = 0;
+
+	if (mutex_lock_interruptible(&rpadlpar_mutex))
+		return -ERESTARTSYS;
+
+	dn = find_dlpar_node(drc_name, &node_type);
+	if (!dn) {
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	switch (node_type) {
+		case NODE_TYPE_VIO:
+			rc = dlpar_remove_vio_slot(drc_name, dn);
+			break;
+		case NODE_TYPE_PHB:
+			rc = dlpar_remove_phb(drc_name, dn);
+			break;
+		case NODE_TYPE_SLOT:
+			rc = dlpar_remove_pci_slot(drc_name, dn);
+			break;
+	}
+	vm_unmap_aliases();
+
+	printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
+exit:
+	mutex_unlock(&rpadlpar_mutex);
+	return rc;
+}
+
+static inline int is_dlpar_capable(void)
+{
+	int rc = rtas_token("ibm,configure-connector");
+
+	return (int) (rc != RTAS_UNKNOWN_SERVICE);
+}
+
+int __init rpadlpar_io_init(void)
+{
+	int rc = 0;
+
+	if (!is_dlpar_capable()) {
+		printk(KERN_WARNING "%s: partition not DLPAR capable\n",
+			__func__);
+		return -EPERM;
+	}
+
+	rc = dlpar_sysfs_init();
+	return rc;
+}
+
+void rpadlpar_io_exit(void)
+{
+	dlpar_sysfs_exit();
+	return;
+}
+
+module_init(rpadlpar_io_init);
+module_exit(rpadlpar_io_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
new file mode 100644
index 0000000..a796301
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -0,0 +1,130 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ *      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.
+ */
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include "rpadlpar.h"
+#include "../pci.h"
+
+#define DLPAR_KOBJ_NAME       "control"
+
+/* Those two have no quotes because they are passed to __ATTR() which
+ * stringifies the argument (yuck !)
+ */
+#define ADD_SLOT_ATTR_NAME    add_slot
+#define REMOVE_SLOT_ATTR_NAME remove_slot
+
+#define MAX_DRC_NAME_LEN 64
+
+static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
+			      const char *buf, size_t nbytes)
+{
+	char drc_name[MAX_DRC_NAME_LEN];
+	char *end;
+	int rc;
+
+	if (nbytes >= MAX_DRC_NAME_LEN)
+		return 0;
+
+	memcpy(drc_name, buf, nbytes);
+
+	end = strchr(drc_name, '\n');
+	if (!end)
+		end = &drc_name[nbytes];
+	*end = '\0';
+
+	rc = dlpar_add_slot(drc_name);
+	if (rc)
+		return rc;
+
+	return nbytes;
+}
+
+static ssize_t add_slot_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "0\n");
+}
+
+static ssize_t remove_slot_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf, size_t nbytes)
+{
+	char drc_name[MAX_DRC_NAME_LEN];
+	int rc;
+	char *end;
+
+	if (nbytes >= MAX_DRC_NAME_LEN)
+		return 0;
+
+	memcpy(drc_name, buf, nbytes);
+
+	end = strchr(drc_name, '\n');
+	if (!end)
+		end = &drc_name[nbytes];
+	*end = '\0';
+
+	rc = dlpar_remove_slot(drc_name);
+	if (rc)
+		return rc;
+
+	return nbytes;
+}
+
+static ssize_t remove_slot_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "0\n");
+}
+
+static struct kobj_attribute add_slot_attr =
+	__ATTR(ADD_SLOT_ATTR_NAME, 0644, add_slot_show, add_slot_store);
+
+static struct kobj_attribute remove_slot_attr =
+	__ATTR(REMOVE_SLOT_ATTR_NAME, 0644, remove_slot_show, remove_slot_store);
+
+static struct attribute *default_attrs[] = {
+	&add_slot_attr.attr,
+	&remove_slot_attr.attr,
+	NULL,
+};
+
+static struct attribute_group dlpar_attr_group = {
+	.attrs = default_attrs,
+};
+
+static struct kobject *dlpar_kobj;
+
+int dlpar_sysfs_init(void)
+{
+	int error;
+
+	dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
+					    &pci_slots_kset->kobj);
+	if (!dlpar_kobj)
+		return -EINVAL;
+
+	error = sysfs_create_group(dlpar_kobj, &dlpar_attr_group);
+	if (error)
+		kobject_put(dlpar_kobj);
+	return error;
+}
+
+void dlpar_sysfs_exit(void)
+{
+	sysfs_remove_group(dlpar_kobj, &dlpar_attr_group);
+	kobject_put(dlpar_kobj);
+}
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
new file mode 100644
index 0000000..b2593e8
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp.h
@@ -0,0 +1,103 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ *
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>,
+ *
+ */
+
+#ifndef _PPC64PHP_H
+#define _PPC64PHP_H
+
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+
+#define DR_INDICATOR 9002
+#define DR_ENTITY_SENSE 9003
+
+#define POWER_ON	100
+#define POWER_OFF	0
+
+#define LED_OFF		0
+#define LED_ON		1	/* continuous on */
+#define LED_ID		2	/* slow blinking */
+#define LED_ACTION	3	/* fast blinking */
+
+/* Sensor values from rtas_get-sensor */
+#define EMPTY           0	/* No card in slot */
+#define PRESENT         1	/* Card in slot */
+
+#define MY_NAME "rpaphp"
+extern bool rpaphp_debug;
+#define dbg(format, arg...)					\
+	do {							\
+		if (rpaphp_debug)				\
+			printk(KERN_DEBUG "%s: " format,	\
+				MY_NAME , ## arg);		\
+	} while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
+
+/* slot states */
+
+#define	NOT_VALID	3
+#define	NOT_CONFIGURED	2
+#define	CONFIGURED	1
+#define	EMPTY		0
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+	struct list_head rpaphp_slot_list;
+	int state;
+	u32 index;
+	u32 type;
+	u32 power_domain;
+	char *name;
+	struct device_node *dn;
+	struct pci_bus *bus;
+	struct list_head *pci_devs;
+	struct hotplug_slot *hotplug_slot;
+};
+
+extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
+extern struct list_head rpaphp_slot_head;
+
+/* function prototypes */
+
+/* rpaphp_pci.c */
+int rpaphp_enable_slot(struct slot *slot);
+int rpaphp_get_sensor_state(struct slot *slot, int *state);
+
+/* rpaphp_core.c */
+int rpaphp_add_slot(struct device_node *dn);
+int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
+		char **drc_name, char **drc_type, int *drc_power_domain);
+
+/* rpaphp_slot.c */
+void dealloc_slot_struct(struct slot *slot);
+struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
+int rpaphp_register_slot(struct slot *slot);
+int rpaphp_deregister_slot(struct slot *slot);
+
+#endif				/* _PPC64PHP_H */
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
new file mode 100644
index 0000000..f2945fa
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -0,0 +1,448 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <asm/eeh.h>       /* for eeh_add_device() */
+#include <asm/rtas.h>		/* rtas_call */
+#include <asm/pci-bridge.h>	/* for pci_controller */
+#include "../pci.h"		/* for pci_add_new_bus */
+				/* and pci_do_scan_bus */
+#include "rpaphp.h"
+
+bool rpaphp_debug;
+LIST_HEAD(rpaphp_slot_head);
+EXPORT_SYMBOL_GPL(rpaphp_slot_head);
+
+#define DRIVER_VERSION	"0.1"
+#define DRIVER_AUTHOR	"Linda Xie <lxie@us.ibm.com>"
+#define DRIVER_DESC	"RPA HOT Plug PCI Controller Driver"
+
+#define MAX_LOC_CODE 128
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param_named(debug, rpaphp_debug, bool, 0644);
+
+/**
+ * set_attention_status - set attention LED
+ * @hotplug_slot: target &hotplug_slot
+ * @value: LED control value
+ *
+ * echo 0 > attention -- set LED OFF
+ * echo 1 > attention -- set LED ON
+ * echo 2 > attention -- set LED ID(identify, light is blinking)
+ */
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+	int rc;
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+
+	switch (value) {
+	case 0:
+	case 1:
+	case 2:
+		break;
+	default:
+		value = 1;
+		break;
+	}
+
+	rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
+	if (!rc)
+		hotplug_slot->info->attention_status = value;
+
+	return rc;
+}
+
+/**
+ * get_power_status - get power status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ */
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int retval, level;
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+
+	retval = rtas_get_power_level (slot->power_domain, &level);
+	if (!retval)
+		*value = level;
+	return retval;
+}
+
+/**
+ * get_attention_status - get attention LED status
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ */
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+	*value = slot->hotplug_slot->info->attention_status;
+	return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+	int rc, state;
+
+	rc = rpaphp_get_sensor_state(slot, &state);
+
+	*value = NOT_VALID;
+	if (rc)
+		return rc;
+
+	if (state == EMPTY)
+		*value = EMPTY;
+	else if (state == PRESENT)
+		*value = slot->state;
+
+	return 0;
+}
+
+static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
+{
+	enum pci_bus_speed speed;
+	switch (slot->type) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+		speed = PCI_SPEED_33MHz;	/* speed for case 1-6 */
+		break;
+	case 7:
+	case 8:
+		speed = PCI_SPEED_66MHz;
+		break;
+	case 11:
+	case 14:
+		speed = PCI_SPEED_66MHz_PCIX;
+		break;
+	case 12:
+	case 15:
+		speed = PCI_SPEED_100MHz_PCIX;
+		break;
+	case 13:
+	case 16:
+		speed = PCI_SPEED_133MHz_PCIX;
+		break;
+	default:
+		speed = PCI_SPEED_UNKNOWN;
+		break;
+	}
+
+	return speed;
+}
+
+static int get_children_props(struct device_node *dn, const int **drc_indexes,
+		const int **drc_names, const int **drc_types,
+		const int **drc_power_domains)
+{
+	const int *indexes, *names, *types, *domains;
+
+	indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
+	names = of_get_property(dn, "ibm,drc-names", NULL);
+	types = of_get_property(dn, "ibm,drc-types", NULL);
+	domains = of_get_property(dn, "ibm,drc-power-domains", NULL);
+
+	if (!indexes || !names || !types || !domains) {
+		/* Slot does not have dynamically-removable children */
+		return -EINVAL;
+	}
+	if (drc_indexes)
+		*drc_indexes = indexes;
+	if (drc_names)
+		/* &drc_names[1] contains NULL terminated slot names */
+		*drc_names = names;
+	if (drc_types)
+		/* &drc_types[1] contains NULL terminated slot types */
+		*drc_types = types;
+	if (drc_power_domains)
+		*drc_power_domains = domains;
+
+	return 0;
+}
+
+/* To get the DRC props describing the current node, first obtain it's
+ * my-drc-index property.  Next obtain the DRC list from it's parent.  Use
+ * the my-drc-index for correlation, and obtain the requested properties.
+ */
+int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
+		char **drc_name, char **drc_type, int *drc_power_domain)
+{
+	const int *indexes, *names;
+	const int *types, *domains;
+	const unsigned int *my_index;
+	char *name_tmp, *type_tmp;
+	int i, rc;
+
+	my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
+	if (!my_index) {
+		/* Node isn't DLPAR/hotplug capable */
+		return -EINVAL;
+	}
+
+	rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
+	if (rc < 0) {
+		return -EINVAL;
+	}
+
+	name_tmp = (char *) &names[1];
+	type_tmp = (char *) &types[1];
+
+	/* Iterate through parent properties, looking for my-drc-index */
+	for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
+		if ((unsigned int) indexes[i + 1] == *my_index) {
+			if (drc_name)
+				*drc_name = name_tmp;
+			if (drc_type)
+				*drc_type = type_tmp;
+			if (drc_index)
+				*drc_index = be32_to_cpu(*my_index);
+			if (drc_power_domain)
+				*drc_power_domain = be32_to_cpu(domains[i+1]);
+			return 0;
+		}
+		name_tmp += (strlen(name_tmp) + 1);
+		type_tmp += (strlen(type_tmp) + 1);
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
+
+static int is_php_type(char *drc_type)
+{
+	unsigned long value;
+	char *endptr;
+
+	/* PCI Hotplug nodes have an integer for drc_type */
+	value = simple_strtoul(drc_type, &endptr, 10);
+	if (endptr == drc_type)
+		return 0;
+
+	return 1;
+}
+
+/**
+ * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0
+ * @dn: target &device_node
+ * @indexes: passed to get_children_props()
+ * @names: passed to get_children_props()
+ * @types: returned from get_children_props()
+ * @power_domains:
+ *
+ * This routine will return true only if the device node is
+ * a hotpluggable slot. This routine will return false
+ * for built-in pci slots (even when the built-in slots are
+ * dlparable.)
+ */
+static int is_php_dn(struct device_node *dn, const int **indexes,
+		const int **names, const int **types, const int **power_domains)
+{
+	const int *drc_types;
+	int rc;
+
+	rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
+	if (rc < 0)
+		return 0;
+
+	if (!is_php_type((char *) &drc_types[1]))
+		return 0;
+
+	*types = drc_types;
+	return 1;
+}
+
+/**
+ * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
+ * @dn: device node of slot
+ *
+ * This subroutine will register a hotpluggable slot with the
+ * PCI hotplug infrastructure. This routine is typically called
+ * during boot time, if the hotplug slots are present at boot time,
+ * or is called later, by the dlpar add code, if the slot is
+ * being dynamically added during runtime.
+ *
+ * If the device node points at an embedded (built-in) slot, this
+ * routine will just return without doing anything, since embedded
+ * slots cannot be hotplugged.
+ *
+ * To remove a slot, it suffices to call rpaphp_deregister_slot().
+ */
+int rpaphp_add_slot(struct device_node *dn)
+{
+	struct slot *slot;
+	int retval = 0;
+	int i;
+	const int *indexes, *names, *types, *power_domains;
+	char *name, *type;
+
+	if (!dn->name || strcmp(dn->name, "pci"))
+		return 0;
+
+	/* If this is not a hotplug slot, return without doing anything. */
+	if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
+		return 0;
+
+	dbg("Entry %s: dn->full_name=%s\n", __func__, dn->full_name);
+
+	/* register PCI devices */
+	name = (char *) &names[1];
+	type = (char *) &types[1];
+	for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
+		int index;
+
+		index = be32_to_cpu(indexes[i + 1]);
+		slot = alloc_slot_struct(dn, index, name,
+					 be32_to_cpu(power_domains[i + 1]));
+		if (!slot)
+			return -ENOMEM;
+
+		slot->type = simple_strtoul(type, NULL, 10);
+
+		dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
+				index, name, type);
+
+		retval = rpaphp_enable_slot(slot);
+		if (!retval)
+			retval = rpaphp_register_slot(slot);
+
+		if (retval)
+			dealloc_slot_struct(slot);
+
+		name += strlen(name) + 1;
+		type += strlen(type) + 1;
+	}
+	dbg("%s - Exit: rc[%d]\n", __func__, retval);
+
+	/* XXX FIXME: reports a failure only if last entry in loop failed */
+	return retval;
+}
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
+
+static void __exit cleanup_slots(void)
+{
+	struct list_head *tmp, *n;
+	struct slot *slot;
+
+	/*
+	 * Unregister all of our slots with the pci_hotplug subsystem,
+	 * and free up all memory that we had allocated.
+	 * memory will be freed in release_slot callback.
+	 */
+
+	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+		slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+		list_del(&slot->rpaphp_slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+	return;
+}
+
+static int __init rpaphp_init(void)
+{
+	struct device_node *dn;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+	for_each_node_by_name(dn, "pci")
+		rpaphp_add_slot(dn);
+
+	return 0;
+}
+
+static void __exit rpaphp_exit(void)
+{
+	cleanup_slots();
+}
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+	int state;
+	int retval;
+
+	if (slot->state == CONFIGURED)
+		return 0;
+
+	retval = rpaphp_get_sensor_state(slot, &state);
+	if (retval)
+		return retval;
+
+	if (state == PRESENT) {
+		pci_lock_rescan_remove();
+		pcibios_add_pci_devices(slot->bus);
+		pci_unlock_rescan_remove();
+		slot->state = CONFIGURED;
+	} else if (state == EMPTY) {
+		slot->state = EMPTY;
+	} else {
+		err("%s: slot[%s] is in invalid state\n", __func__, slot->name);
+		slot->state = NOT_VALID;
+		return -EINVAL;
+	}
+
+	slot->bus->max_bus_speed = get_max_bus_speed(slot);
+	return 0;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = (struct slot *)hotplug_slot->private;
+	if (slot->state == NOT_CONFIGURED)
+		return -EINVAL;
+
+	pci_lock_rescan_remove();
+	pcibios_remove_pci_devices(slot->bus);
+	pci_unlock_rescan_remove();
+	vm_unmap_aliases();
+
+	slot->state = NOT_CONFIGURED;
+	return 0;
+}
+
+struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+	.enable_slot = enable_slot,
+	.disable_slot = disable_slot,
+	.set_attention_status = set_attention_status,
+	.get_power_status = get_power_status,
+	.get_attention_status = get_attention_status,
+	.get_adapter_status = get_adapter_status,
+};
+
+module_init(rpaphp_init);
+module_exit(rpaphp_exit);
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
new file mode 100644
index 0000000..9243f3e
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_pci.c
@@ -0,0 +1,135 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include <asm/pci-bridge.h>
+#include <asm/rtas.h>
+#include <asm/machdep.h>
+
+#include "../pci.h"		/* for pci_add_new_bus */
+#include "rpaphp.h"
+
+int rpaphp_get_sensor_state(struct slot *slot, int *state)
+{
+	int rc;
+	int setlevel;
+
+	rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
+
+	if (rc < 0) {
+		if (rc == -EFAULT || rc == -EEXIST) {
+			dbg("%s: slot must be power up to get sensor-state\n",
+			    __func__);
+
+			/* some slots have to be powered up
+			 * before get-sensor will succeed.
+			 */
+			rc = rtas_set_power_level(slot->power_domain, POWER_ON,
+						  &setlevel);
+			if (rc < 0) {
+				dbg("%s: power on slot[%s] failed rc=%d.\n",
+				    __func__, slot->name, rc);
+			} else {
+				rc = rtas_get_sensor(DR_ENTITY_SENSE,
+						     slot->index, state);
+			}
+		} else if (rc == -ENODEV)
+			info("%s: slot is unusable\n", __func__);
+		else
+			err("%s failed to get sensor state\n", __func__);
+	}
+	return rc;
+}
+
+/**
+ * rpaphp_enable_slot - record slot state, config pci device
+ * @slot: target &slot
+ *
+ * Initialize values in the slot, and the hotplug_slot info
+ * structures to indicate if there is a pci card plugged into
+ * the slot. If the slot is not empty, run the pcibios routine
+ * to get pcibios stuff correctly set up.
+ */
+int rpaphp_enable_slot(struct slot *slot)
+{
+	int rc, level, state;
+	struct pci_bus *bus;
+	struct hotplug_slot_info *info = slot->hotplug_slot->info;
+
+	info->adapter_status = NOT_VALID;
+	slot->state = EMPTY;
+
+	/* Find out if the power is turned on for the slot */
+	rc = rtas_get_power_level(slot->power_domain, &level);
+	if (rc)
+		return rc;
+	info->power_status = level;
+
+	/* Figure out if there is an adapter in the slot */
+	rc = rpaphp_get_sensor_state(slot, &state);
+	if (rc)
+		return rc;
+
+	bus = pcibios_find_pci_bus(slot->dn);
+	if (!bus) {
+		err("%s: no pci_bus for dn %s\n", __func__, slot->dn->full_name);
+		return -EINVAL;
+	}
+
+	info->adapter_status = EMPTY;
+	slot->bus = bus;
+	slot->pci_devs = &bus->devices;
+
+	/* if there's an adapter in the slot, go add the pci devices */
+	if (state == PRESENT) {
+		info->adapter_status = NOT_CONFIGURED;
+		slot->state = NOT_CONFIGURED;
+
+		/* non-empty slot has to have child */
+		if (!slot->dn->child) {
+			err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
+			    __func__, slot->name);
+			return -EINVAL;
+		}
+
+		if (list_empty(&bus->devices))
+			pcibios_add_pci_devices(bus);
+
+		if (!list_empty(&bus->devices)) {
+			info->adapter_status = CONFIGURED;
+			slot->state = CONFIGURED;
+		}
+
+		if (rpaphp_debug) {
+			struct pci_dev *dev;
+			dbg("%s: pci_devs of slot[%s]\n", __func__, slot->dn->full_name);
+			list_for_each_entry (dev, &bus->devices, bus_list)
+				dbg("\t%s\n", pci_name(dev));
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
new file mode 100644
index 0000000..a6082cc
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -0,0 +1,147 @@
+/*
+ * RPA Virtual I/O device functions
+ * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <asm/rtas.h>
+#include "rpaphp.h"
+
+/* free up the memory used by a slot */
+static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = (struct slot *) hotplug_slot->private;
+	dealloc_slot_struct(slot);
+}
+
+void dealloc_slot_struct(struct slot *slot)
+{
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->name);
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+struct slot *alloc_slot_struct(struct device_node *dn,
+                       int drc_index, char *drc_name, int power_domain)
+{
+	struct slot *slot;
+
+	slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
+	if (!slot)
+		goto error_nomem;
+	slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+	if (!slot->hotplug_slot)
+		goto error_slot;
+	slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+					   GFP_KERNEL);
+	if (!slot->hotplug_slot->info)
+		goto error_hpslot;
+	slot->name = kstrdup(drc_name, GFP_KERNEL);
+	if (!slot->name)
+		goto error_info;
+	slot->dn = dn;
+	slot->index = drc_index;
+	slot->power_domain = power_domain;
+	slot->hotplug_slot->private = slot;
+	slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
+	slot->hotplug_slot->release = &rpaphp_release_slot;
+
+	return (slot);
+
+error_info:
+	kfree(slot->hotplug_slot->info);
+error_hpslot:
+	kfree(slot->hotplug_slot);
+error_slot:
+	kfree(slot);
+error_nomem:
+	return NULL;
+}
+
+static int is_registered(struct slot *slot)
+{
+	struct slot *tmp_slot;
+
+	list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
+		if (!strcmp(tmp_slot->name, slot->name))
+			return 1;
+	}
+	return 0;
+}
+
+int rpaphp_deregister_slot(struct slot *slot)
+{
+	int retval = 0;
+	struct hotplug_slot *php_slot = slot->hotplug_slot;
+
+	 dbg("%s - Entry: deregistering slot=%s\n",
+		__func__, slot->name);
+
+	list_del(&slot->rpaphp_slot_list);
+
+	retval = pci_hp_deregister(php_slot);
+	if (retval)
+		err("Problem unregistering a slot %s\n", slot->name);
+
+	dbg("%s - Exit: rc[%d]\n", __func__, retval);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
+
+int rpaphp_register_slot(struct slot *slot)
+{
+	struct hotplug_slot *php_slot = slot->hotplug_slot;
+	int retval;
+	int slotno;
+
+	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
+		__func__, slot->dn->full_name, slot->index, slot->name,
+		slot->power_domain, slot->type);
+
+	/* should not try to register the same slot twice */
+	if (is_registered(slot)) {
+		err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
+		return -EAGAIN;
+	}
+
+	if (slot->dn->child)
+		slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
+	else
+		slotno = -1;
+	retval = pci_hp_register(php_slot, slot->bus, slotno, slot->name);
+	if (retval) {
+		err("pci_hp_register failed with error %d\n", retval);
+		return retval;
+	}
+
+	/* add slot to our internal list */
+	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
+	info("Slot [%s] registered\n", slot->name);
+	return 0;
+}
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
new file mode 100644
index 0000000..d77e46b
--- /dev/null
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -0,0 +1,214 @@
+/*
+ * PCI Hot Plug Controller Driver for System z
+ *
+ * Copyright 2012 IBM Corp.
+ *
+ * Author(s):
+ *   Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "zpci"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <asm/pci_debug.h>
+#include <asm/sclp.h>
+
+#define SLOT_NAME_SIZE	10
+static LIST_HEAD(s390_hotplug_slot_list);
+
+MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
+MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
+MODULE_LICENSE("GPL");
+
+static int zpci_fn_configured(enum zpci_state state)
+{
+	return state == ZPCI_FN_STATE_CONFIGURED ||
+	       state == ZPCI_FN_STATE_ONLINE;
+}
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+	struct list_head slot_list;
+	struct hotplug_slot *hotplug_slot;
+	struct zpci_dev *zdev;
+};
+
+static inline int slot_configure(struct slot *slot)
+{
+	int ret = sclp_pci_configure(slot->zdev->fid);
+
+	zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret);
+	if (!ret)
+		slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;
+
+	return ret;
+}
+
+static inline int slot_deconfigure(struct slot *slot)
+{
+	int ret = sclp_pci_deconfigure(slot->zdev->fid);
+
+	zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret);
+	if (!ret)
+		slot->zdev->state = ZPCI_FN_STATE_STANDBY;
+
+	return ret;
+}
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int rc;
+
+	if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
+		return -EIO;
+
+	rc = slot_configure(slot);
+	if (rc)
+		return rc;
+
+	rc = zpci_enable_device(slot->zdev);
+	if (rc)
+		goto out_deconfigure;
+
+	pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
+	pci_lock_rescan_remove();
+	pci_bus_add_devices(slot->zdev->bus);
+	pci_unlock_rescan_remove();
+
+	return rc;
+
+out_deconfigure:
+	slot_deconfigure(slot);
+	return rc;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+	int rc;
+
+	if (!zpci_fn_configured(slot->zdev->state))
+		return -EIO;
+
+	if (slot->zdev->pdev)
+		pci_stop_and_remove_bus_device_locked(slot->zdev->pdev);
+
+	rc = zpci_disable_device(slot->zdev);
+	if (rc)
+		return rc;
+
+	return slot_deconfigure(slot);
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	switch (slot->zdev->state) {
+	case ZPCI_FN_STATE_STANDBY:
+		*value = 0;
+		break;
+	default:
+		*value = 1;
+		break;
+	}
+	return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	/* if the slot exits it always contains a function */
+	*value = 1;
+	return 0;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+static struct hotplug_slot_ops s390_hotplug_slot_ops = {
+	.enable_slot =		enable_slot,
+	.disable_slot =		disable_slot,
+	.get_power_status =	get_power_status,
+	.get_adapter_status =	get_adapter_status,
+};
+
+int zpci_init_slot(struct zpci_dev *zdev)
+{
+	struct hotplug_slot *hotplug_slot;
+	struct hotplug_slot_info *info;
+	char name[SLOT_NAME_SIZE];
+	struct slot *slot;
+	int rc;
+
+	if (!zdev)
+		return 0;
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (!slot)
+		goto error;
+
+	hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+	if (!hotplug_slot)
+		goto error_hp;
+	hotplug_slot->private = slot;
+
+	slot->hotplug_slot = hotplug_slot;
+	slot->zdev = zdev;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		goto error_info;
+	hotplug_slot->info = info;
+
+	hotplug_slot->ops = &s390_hotplug_slot_ops;
+	hotplug_slot->release = &release_slot;
+
+	get_power_status(hotplug_slot, &info->power_status);
+	get_adapter_status(hotplug_slot, &info->adapter_status);
+
+	snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
+	rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
+			     ZPCI_DEVFN, name);
+	if (rc)
+		goto error_reg;
+
+	list_add(&slot->slot_list, &s390_hotplug_slot_list);
+	return 0;
+
+error_reg:
+	kfree(info);
+error_info:
+	kfree(hotplug_slot);
+error_hp:
+	kfree(slot);
+error:
+	return -ENOMEM;
+}
+
+void zpci_exit_slot(struct zpci_dev *zdev)
+{
+	struct list_head *tmp, *n;
+	struct slot *slot;
+
+	list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
+		slot = list_entry(tmp, struct slot, slot_list);
+		if (slot->zdev != zdev)
+			continue;
+		list_del(&slot->slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+}
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
new file mode 100644
index 0000000..c32fb78
--- /dev/null
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -0,0 +1,717 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005-2006 Silicon Graphics, Inc. All rights reserved.
+ *
+ * This work was based on the 2.4/2.6 kernel development by Dick Reigner.
+ * Work to add BIOS PROM support was completed by Mike Habeck.
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+#include <asm/sn/addrs.h>
+#include <asm/sn/geo.h>
+#include <asm/sn/l1.h>
+#include <asm/sn/module.h>
+#include <asm/sn/pcibr_provider.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/sn_feature_sets.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/types.h>
+#include <asm/sn/acpi.h>
+
+#include "../pci.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)");
+MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver");
+
+
+/* SAL call error codes. Keep in sync with prom header io/include/pcibr.h */
+#define PCI_SLOT_ALREADY_UP		2	/* slot already up */
+#define PCI_SLOT_ALREADY_DOWN		3	/* slot already down */
+#define PCI_L1_ERR			7	/* L1 console command error */
+#define PCI_EMPTY_33MHZ			15	/* empty 33 MHz bus */
+
+
+#define PCIIO_ASIC_TYPE_TIOCA		4
+#define PCI_L1_QSIZE			128	/* our L1 message buffer size */
+#define SN_MAX_HP_SLOTS			32	/* max hotplug slots */
+#define SN_SLOT_NAME_SIZE		33	/* size of name string */
+
+/* internal list head */
+static struct list_head sn_hp_list;
+
+/* hotplug_slot struct's private pointer */
+struct slot {
+	int device_num;
+	struct pci_bus *pci_bus;
+	/* this struct for glue internal only */
+	struct hotplug_slot *hotplug_slot;
+	struct list_head hp_list;
+	char physical_path[SN_SLOT_NAME_SIZE];
+};
+
+struct pcibr_slot_enable_resp {
+	int resp_sub_errno;
+	char resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+struct pcibr_slot_disable_resp {
+	int resp_sub_errno;
+	char resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+enum sn_pci_req_e {
+	PCI_REQ_SLOT_ELIGIBLE,
+	PCI_REQ_SLOT_DISABLE
+};
+
+static int enable_slot(struct hotplug_slot *slot);
+static int disable_slot(struct hotplug_slot *slot);
+static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops sn_hotplug_slot_ops = {
+	.enable_slot            = enable_slot,
+	.disable_slot           = disable_slot,
+	.get_power_status       = get_power_status,
+};
+
+static DEFINE_MUTEX(sn_hotplug_mutex);
+
+static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
+{
+	int retval = -ENOENT;
+	struct slot *slot = pci_slot->hotplug->private;
+
+	if (!slot)
+		return retval;
+
+	retval = sprintf (buf, "%s\n", slot->physical_path);
+	return retval;
+}
+
+static struct pci_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
+
+static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
+{
+	struct pcibus_info *pcibus_info;
+	u16 busnum, segment, ioboard_type;
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+
+	/* Check to see if this is a valid slot on 'pci_bus' */
+	if (!(pcibus_info->pbi_valid_devices & (1 << device)))
+		return -EPERM;
+
+	ioboard_type = sn_ioboard_to_pci_bus(pci_bus);
+	busnum = pcibus_info->pbi_buscommon.bs_persist_busnum;
+	segment = pci_domain_nr(pci_bus) & 0xf;
+
+	/* Do not allow hotplug operations on base I/O cards */
+	if ((ioboard_type == L1_BRICKTYPE_IX ||
+	     ioboard_type == L1_BRICKTYPE_IA) &&
+	    (segment == 1 && busnum == 0 && device != 1))
+		return -EPERM;
+
+	return 1;
+}
+
+static int sn_pci_bus_valid(struct pci_bus *pci_bus)
+{
+	struct pcibus_info *pcibus_info;
+	u32 asic_type;
+	u16 ioboard_type;
+
+	/* Don't register slots hanging off the TIOCA bus */
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+	asic_type = pcibus_info->pbi_buscommon.bs_asic_type;
+	if (asic_type == PCIIO_ASIC_TYPE_TIOCA)
+		return -EPERM;
+
+	/* Only register slots in I/O Bricks that support hotplug */
+	ioboard_type = sn_ioboard_to_pci_bus(pci_bus);
+	switch (ioboard_type) {
+		case L1_BRICKTYPE_IX:
+		case L1_BRICKTYPE_PX:
+		case L1_BRICKTYPE_IA:
+		case L1_BRICKTYPE_PA:
+		case L1_BOARDTYPE_PCIX3SLOT:
+			return 1;
+			break;
+		default:
+			return -EPERM;
+			break;
+	}
+
+	return -EIO;
+}
+
+static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
+				    struct pci_bus *pci_bus, int device,
+				    char *name)
+{
+	struct pcibus_info *pcibus_info;
+	struct slot *slot;
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (!slot)
+		return -ENOMEM;
+	bss_hotplug_slot->private = slot;
+
+	slot->device_num = device;
+	slot->pci_bus = pci_bus;
+	sprintf(name, "%04x:%02x:%02x",
+		pci_domain_nr(pci_bus),
+		((u16)pcibus_info->pbi_buscommon.bs_persist_busnum),
+		device + 1);
+
+	sn_generate_path(pci_bus, slot->physical_path);
+
+	slot->hotplug_slot = bss_hotplug_slot;
+	list_add(&slot->hp_list, &sn_hp_list);
+
+	return 0;
+}
+
+static struct hotplug_slot *sn_hp_destroy(void)
+{
+	struct slot *slot;
+	struct pci_slot *pci_slot;
+	struct hotplug_slot *bss_hotplug_slot = NULL;
+
+	list_for_each_entry(slot, &sn_hp_list, hp_list) {
+		bss_hotplug_slot = slot->hotplug_slot;
+		pci_slot = bss_hotplug_slot->pci_slot;
+		list_del(&((struct slot *)bss_hotplug_slot->private)->
+			 hp_list);
+		sysfs_remove_file(&pci_slot->kobj,
+				  &sn_slot_path_attr.attr);
+		break;
+	}
+	return bss_hotplug_slot;
+}
+
+static void sn_bus_free_data(struct pci_dev *dev)
+{
+	struct pci_bus *subordinate_bus;
+	struct pci_dev *child;
+
+	/* Recursively clean up sn_irq_info structs */
+	if (dev->subordinate) {
+		subordinate_bus = dev->subordinate;
+		list_for_each_entry(child, &subordinate_bus->devices, bus_list)
+			sn_bus_free_data(child);
+	}
+	/*
+	 * Some drivers may use dma accesses during the
+	 * driver remove function. We release the sysdata
+	 * areas after the driver remove functions have
+	 * been called.
+	 */
+	sn_bus_store_sysdata(dev);
+	sn_pci_unfixup_slot(dev);
+}
+
+static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
+			  int device_num, char **ssdt)
+{
+	struct slot *slot = bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	struct pcibr_slot_enable_resp resp;
+	int rc;
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+
+	/*
+	 * Power-on and initialize the slot in the SN
+	 * PCI infrastructure.
+	 */
+	rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp, ssdt);
+
+
+	if (rc == PCI_SLOT_ALREADY_UP) {
+		dev_dbg(&slot->pci_bus->self->dev, "is already active\n");
+		return 1; /* return 1 to user */
+	}
+
+	if (rc == PCI_L1_ERR) {
+		dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message: %s",
+			resp.resp_sub_errno, resp.resp_l1_msg);
+		return -EPERM;
+	}
+
+	if (rc) {
+		dev_dbg(&slot->pci_bus->self->dev, "insert failed with error %d sub-error %d\n",
+			rc, resp.resp_sub_errno);
+		return -EIO;
+	}
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+	pcibus_info->pbi_enabled_devices |= (1 << device_num);
+
+	return 0;
+}
+
+static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
+			   int device_num, int action)
+{
+	struct slot *slot = bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	struct pcibr_slot_disable_resp resp;
+	int rc;
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+
+	rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp);
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) &&
+	    (rc == PCI_SLOT_ALREADY_DOWN)) {
+		dev_dbg(&slot->pci_bus->self->dev, "Slot %s already inactive\n", slot->physical_path);
+		return 1; /* return 1 to user */
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) {
+		dev_dbg(&slot->pci_bus->self->dev, "Cannot remove last 33MHz card\n");
+		return -EPERM;
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) {
+		dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message \n%s\n",
+			resp.resp_sub_errno, resp.resp_l1_msg);
+		return -EPERM;
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) {
+		dev_dbg(&slot->pci_bus->self->dev, "remove failed with error %d sub-error %d\n",
+			rc, resp.resp_sub_errno);
+		return -EIO;
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && !rc)
+		return 0;
+
+	if ((action == PCI_REQ_SLOT_DISABLE) && !rc) {
+		pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+		pcibus_info->pbi_enabled_devices &= ~(1 << device_num);
+		dev_dbg(&slot->pci_bus->self->dev, "remove successful\n");
+		return 0;
+	}
+
+	if ((action == PCI_REQ_SLOT_DISABLE) && rc) {
+		dev_dbg(&slot->pci_bus->self->dev,"remove failed rc = %d\n", rc);
+	}
+
+	return rc;
+}
+
+/*
+ * Power up and configure the slot via a SAL call to PROM.
+ * Scan slot (and any children), do any platform specific fixup,
+ * and find device driver.
+ */
+static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+	struct slot *slot = bss_hotplug_slot->private;
+	struct pci_bus *new_bus = NULL;
+	struct pci_dev *dev;
+	int num_funcs;
+	int new_ppb = 0;
+	int rc;
+	char *ssdt = NULL;
+	void pcibios_fixup_device_resources(struct pci_dev *);
+
+	/* Serialize the Linux PCI infrastructure */
+	mutex_lock(&sn_hotplug_mutex);
+
+	/*
+	 * Power-on and initialize the slot in the SN
+	 * PCI infrastructure. Also, retrieve the ACPI SSDT
+	 * table for the slot (if ACPI capable PROM).
+	 */
+	rc = sn_slot_enable(bss_hotplug_slot, slot->device_num, &ssdt);
+	if (rc) {
+		mutex_unlock(&sn_hotplug_mutex);
+		return rc;
+	}
+
+	if (ssdt)
+		ssdt = __va(ssdt);
+	/* Add the new SSDT for the slot to the ACPI namespace */
+	if (SN_ACPI_BASE_SUPPORT() && ssdt) {
+		acpi_status ret;
+
+		ret = acpi_load_table((struct acpi_table_header *)ssdt);
+		if (ACPI_FAILURE(ret)) {
+			printk(KERN_ERR "%s: acpi_load_table failed (0x%x)\n",
+			       __func__, ret);
+			/* try to continue on */
+		}
+	}
+
+	num_funcs = pci_scan_slot(slot->pci_bus,
+				  PCI_DEVFN(slot->device_num + 1, 0));
+	if (!num_funcs) {
+		dev_dbg(&slot->pci_bus->self->dev, "no device in slot\n");
+		mutex_unlock(&sn_hotplug_mutex);
+		return -ENODEV;
+	}
+
+	/*
+	 * Map SN resources for all functions on the card
+	 * to the Linux PCI interface and tell the drivers
+	 * about them.
+	 */
+	list_for_each_entry(dev, &slot->pci_bus->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != slot->device_num + 1)
+			continue;
+
+		/* Need to do slot fixup on PPB before fixup of children
+		 * (PPB's pcidev_info needs to be in pcidev_info list
+		 * before child's SN_PCIDEV_INFO() call to setup
+		 * pdi_host_pcidev_info).
+		 */
+		pcibios_fixup_device_resources(dev);
+		if (SN_ACPI_BASE_SUPPORT())
+			sn_acpi_slot_fixup(dev);
+		else
+			sn_io_slot_fixup(dev);
+		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+			pci_hp_add_bridge(dev);
+			if (dev->subordinate) {
+				new_bus = dev->subordinate;
+				new_ppb = 1;
+			}
+		}
+	}
+
+	/*
+	 * Add the slot's devices to the ACPI infrastructure */
+	if (SN_ACPI_BASE_SUPPORT() && ssdt) {
+		unsigned long long adr;
+		struct acpi_device *pdevice;
+		acpi_handle phandle;
+		acpi_handle chandle = NULL;
+		acpi_handle rethandle;
+		acpi_status ret;
+
+		phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
+
+		if (acpi_bus_get_device(phandle, &pdevice)) {
+			dev_dbg(&slot->pci_bus->self->dev, "no parent device, assuming NULL\n");
+			pdevice = NULL;
+		}
+
+		acpi_scan_lock_acquire();
+		/*
+		 * Walk the rootbus node's immediate children looking for
+		 * the slot's device node(s). There can be more than
+		 * one for multifunction devices.
+		 */
+		for (;;) {
+			rethandle = NULL;
+			ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
+						   phandle, chandle,
+						   &rethandle);
+
+			if (ret == AE_NOT_FOUND || rethandle == NULL)
+				break;
+
+			chandle = rethandle;
+
+			ret = acpi_evaluate_integer(chandle, METHOD_NAME__ADR,
+						    NULL, &adr);
+
+			if (ACPI_SUCCESS(ret) &&
+			    (adr>>16) == (slot->device_num + 1)) {
+
+				ret = acpi_bus_scan(chandle);
+				if (ACPI_FAILURE(ret)) {
+					printk(KERN_ERR "%s: acpi_bus_scan failed (0x%x) for slot %d func %d\n",
+					       __func__, ret, (int)(adr>>16),
+					       (int)(adr&0xffff));
+					/* try to continue on */
+				}
+			}
+		}
+		acpi_scan_lock_release();
+	}
+
+	pci_lock_rescan_remove();
+
+	/* Call the driver for the new device */
+	pci_bus_add_devices(slot->pci_bus);
+	/* Call the drivers for the new devices subordinate to PPB */
+	if (new_ppb)
+		pci_bus_add_devices(new_bus);
+
+	pci_unlock_rescan_remove();
+	mutex_unlock(&sn_hotplug_mutex);
+
+	if (rc == 0)
+		dev_dbg(&slot->pci_bus->self->dev, "insert operation successful\n");
+	else
+		dev_dbg(&slot->pci_bus->self->dev, "insert operation failed rc = %d\n", rc);
+
+	return rc;
+}
+
+static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+	struct slot *slot = bss_hotplug_slot->private;
+	struct pci_dev *dev, *temp;
+	int rc;
+	acpi_handle ssdt_hdl = NULL;
+
+	/* Acquire update access to the bus */
+	mutex_lock(&sn_hotplug_mutex);
+
+	/* is it okay to bring this slot down? */
+	rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
+			     PCI_REQ_SLOT_ELIGIBLE);
+	if (rc)
+		goto leaving;
+
+	/* free the ACPI resources for the slot */
+	if (SN_ACPI_BASE_SUPPORT() &&
+            PCI_CONTROLLER(slot->pci_bus)->companion) {
+		unsigned long long adr;
+		struct acpi_device *device;
+		acpi_handle phandle;
+		acpi_handle chandle = NULL;
+		acpi_handle rethandle;
+		acpi_status ret;
+
+		/* Get the rootbus node pointer */
+		phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
+
+		acpi_scan_lock_acquire();
+		/*
+		 * Walk the rootbus node's immediate children looking for
+		 * the slot's device node(s). There can be more than
+		 * one for multifunction devices.
+		 */
+		for (;;) {
+			rethandle = NULL;
+			ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
+						   phandle, chandle,
+						   &rethandle);
+
+			if (ret == AE_NOT_FOUND || rethandle == NULL)
+				break;
+
+			chandle = rethandle;
+
+			ret = acpi_evaluate_integer(chandle,
+						    METHOD_NAME__ADR,
+						    NULL, &adr);
+			if (ACPI_SUCCESS(ret) &&
+			    (adr>>16) == (slot->device_num + 1)) {
+				/* retain the owner id */
+				ssdt_hdl = chandle;
+
+				ret = acpi_bus_get_device(chandle,
+							  &device);
+				if (ACPI_SUCCESS(ret))
+					acpi_bus_trim(device);
+			}
+		}
+		acpi_scan_lock_release();
+	}
+
+	pci_lock_rescan_remove();
+	/* Free the SN resources assigned to the Linux device.*/
+	list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != slot->device_num + 1)
+			continue;
+
+		pci_dev_get(dev);
+		sn_bus_free_data(dev);
+		pci_stop_and_remove_bus_device(dev);
+		pci_dev_put(dev);
+	}
+	pci_unlock_rescan_remove();
+
+	/* Remove the SSDT for the slot from the ACPI namespace */
+	if (SN_ACPI_BASE_SUPPORT() && ssdt_hdl) {
+		acpi_status ret;
+		ret = acpi_unload_parent_table(ssdt_hdl);
+		if (ACPI_FAILURE(ret)) {
+			acpi_handle_err(ssdt_hdl,
+					"%s: acpi_unload_parent_table failed (0x%x)\n",
+					__func__, ret);
+			/* try to continue on */
+		}
+	}
+
+	/* free the collected sysdata pointers */
+	sn_bus_free_sysdata();
+
+	/* Deactivate slot */
+	rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
+			     PCI_REQ_SLOT_DISABLE);
+ leaving:
+	/* Release the bus lock */
+	mutex_unlock(&sn_hotplug_mutex);
+
+	return rc;
+}
+
+static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
+				   u8 *value)
+{
+	struct slot *slot = bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	u32 power;
+
+	pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+	mutex_lock(&sn_hotplug_mutex);
+	power = pcibus_info->pbi_enabled_devices & (1 << slot->device_num);
+	*value = power ? 1 : 0;
+	mutex_unlock(&sn_hotplug_mutex);
+	return 0;
+}
+
+static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+	kfree(bss_hotplug_slot->info);
+	kfree(bss_hotplug_slot->private);
+	kfree(bss_hotplug_slot);
+}
+
+static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
+{
+	int device;
+	struct pci_slot *pci_slot;
+	struct hotplug_slot *bss_hotplug_slot;
+	char name[SN_SLOT_NAME_SIZE];
+	int rc = 0;
+
+	/*
+	 * Currently only four devices are supported,
+	 * in the future there maybe more -- up to 32.
+	 */
+
+	for (device = 0; device < SN_MAX_HP_SLOTS ; device++) {
+		if (sn_pci_slot_valid(pci_bus, device) != 1)
+			continue;
+
+		bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot),
+					   GFP_KERNEL);
+		if (!bss_hotplug_slot) {
+			rc = -ENOMEM;
+			goto alloc_err;
+		}
+
+		bss_hotplug_slot->info =
+			kzalloc(sizeof(struct hotplug_slot_info),
+				GFP_KERNEL);
+		if (!bss_hotplug_slot->info) {
+			rc = -ENOMEM;
+			goto alloc_err;
+		}
+
+		if (sn_hp_slot_private_alloc(bss_hotplug_slot,
+					     pci_bus, device, name)) {
+			rc = -ENOMEM;
+			goto alloc_err;
+		}
+		bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
+		bss_hotplug_slot->release = &sn_release_slot;
+
+		rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name);
+		if (rc)
+			goto register_err;
+
+		pci_slot = bss_hotplug_slot->pci_slot;
+		rc = sysfs_create_file(&pci_slot->kobj,
+				       &sn_slot_path_attr.attr);
+		if (rc)
+			goto register_err;
+	}
+	dev_dbg(&pci_bus->self->dev, "Registered bus with hotplug\n");
+	return rc;
+
+register_err:
+	dev_dbg(&pci_bus->self->dev, "bus failed to register with err = %d\n",
+		rc);
+
+alloc_err:
+	if (rc == -ENOMEM)
+		dev_dbg(&pci_bus->self->dev, "Memory allocation error\n");
+
+	/* destroy THIS element */
+	if (bss_hotplug_slot)
+		sn_release_slot(bss_hotplug_slot);
+
+	/* destroy anything else on the list */
+	while ((bss_hotplug_slot = sn_hp_destroy()))
+		pci_hp_deregister(bss_hotplug_slot);
+
+	return rc;
+}
+
+static int __init sn_pci_hotplug_init(void)
+{
+	struct pci_bus *pci_bus = NULL;
+	int rc;
+	int registered = 0;
+
+	if (!sn_prom_feature_available(PRF_HOTPLUG_SUPPORT)) {
+		printk(KERN_ERR "%s: PROM version does not support hotplug.\n",
+		       __func__);
+		return -EPERM;
+	}
+
+	INIT_LIST_HEAD(&sn_hp_list);
+
+	while ((pci_bus = pci_find_next_bus(pci_bus))) {
+		if (!pci_bus->sysdata)
+			continue;
+
+		rc = sn_pci_bus_valid(pci_bus);
+		if (rc != 1) {
+			dev_dbg(&pci_bus->self->dev, "not a valid hotplug bus\n");
+			continue;
+		}
+		dev_dbg(&pci_bus->self->dev, "valid hotplug bus\n");
+
+		rc = sn_hotplug_slot_register(pci_bus);
+		if (!rc) {
+			registered = 1;
+		} else {
+			registered = 0;
+			break;
+		}
+	}
+
+	return registered == 1 ? 0 : -ENODEV;
+}
+
+static void __exit sn_pci_hotplug_exit(void)
+{
+	struct hotplug_slot *bss_hotplug_slot;
+
+	while ((bss_hotplug_slot = sn_hp_destroy()))
+		pci_hp_deregister(bss_hotplug_slot);
+
+	if (!list_empty(&sn_hp_list))
+		printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
+}
+
+module_init(sn_pci_hotplug_init);
+module_exit(sn_pci_hotplug_exit);
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
new file mode 100644
index 0000000..5897d51
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp.h
@@ -0,0 +1,348 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>,<kristen.c.accardi@intel.com>
+ *
+ */
+#ifndef _SHPCHP_H
+#define _SHPCHP_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/delay.h>
+#include <linux/sched.h>	/* signal_pending(), struct timer_list */
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#if !defined(MODULE)
+	#define MY_NAME	"shpchp"
+#else
+	#define MY_NAME	THIS_MODULE->name
+#endif
+
+extern bool shpchp_poll_mode;
+extern int shpchp_poll_time;
+extern bool shpchp_debug;
+
+#define dbg(format, arg...)						\
+do {									\
+	if (shpchp_debug)						\
+		printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg);	\
+} while (0)
+#define err(format, arg...)						\
+	printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
+#define info(format, arg...)						\
+	printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
+#define warn(format, arg...)						\
+	printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
+
+#define ctrl_dbg(ctrl, format, arg...)					\
+	do {								\
+		if (shpchp_debug)					\
+			dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev,	\
+					format, ## arg);		\
+	} while (0)
+#define ctrl_err(ctrl, format, arg...)					\
+	dev_err(&ctrl->pci_dev->dev, format, ## arg)
+#define ctrl_info(ctrl, format, arg...)					\
+	dev_info(&ctrl->pci_dev->dev, format, ## arg)
+#define ctrl_warn(ctrl, format, arg...)					\
+	dev_warn(&ctrl->pci_dev->dev, format, ## arg)
+
+
+#define SLOT_NAME_SIZE 10
+struct slot {
+	u8 bus;
+	u8 device;
+	u16 status;
+	u32 number;
+	u8 is_a_board;
+	u8 state;
+	u8 presence_save;
+	u8 pwr_save;
+	struct controller *ctrl;
+	struct hpc_ops *hpc_ops;
+	struct hotplug_slot *hotplug_slot;
+	struct list_head	slot_list;
+	struct delayed_work work;	/* work for button event */
+	struct mutex lock;
+	struct workqueue_struct *wq;
+	u8 hp_slot;
+};
+
+struct event_info {
+	u32 event_type;
+	struct slot *p_slot;
+	struct work_struct work;
+};
+
+struct controller {
+	struct mutex crit_sect;		/* critical section mutex */
+	struct mutex cmd_lock;		/* command lock */
+	int num_slots;			/* Number of slots on ctlr */
+	int slot_num_inc;		/* 1 or -1 */
+	struct pci_dev *pci_dev;
+	struct list_head slot_list;
+	struct hpc_ops *hpc_ops;
+	wait_queue_head_t queue;	/* sleep & wake process */
+	u8 slot_device_offset;
+	u32 pcix_misc2_reg;	/* for amd pogo errata */
+	u32 first_slot;		/* First physical slot number */
+	u32 cap_offset;
+	unsigned long mmio_base;
+	unsigned long mmio_size;
+	void __iomem *creg;
+	struct timer_list poll_timer;
+};
+
+/* Define AMD SHPC ID  */
+#define PCI_DEVICE_ID_AMD_GOLAM_7450	0x7450
+#define PCI_DEVICE_ID_AMD_POGO_7458	0x7458
+
+/* AMD PCI-X bridge registers */
+#define PCIX_MEM_BASE_LIMIT_OFFSET	0x1C
+#define PCIX_MISCII_OFFSET		0x48
+#define PCIX_MISC_BRIDGE_ERRORS_OFFSET	0x80
+
+/* AMD PCIX_MISCII masks and offsets */
+#define PERRNONFATALENABLE_MASK		0x00040000
+#define PERRFATALENABLE_MASK		0x00080000
+#define PERRFLOODENABLE_MASK		0x00100000
+#define SERRNONFATALENABLE_MASK		0x00200000
+#define SERRFATALENABLE_MASK		0x00400000
+
+/* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */
+#define PERR_OBSERVED_MASK		0x00000001
+
+/* AMD PCIX_MEM_BASE_LIMIT masks */
+#define RSE_MASK			0x40000000
+
+#define INT_BUTTON_IGNORE		0
+#define INT_PRESENCE_ON			1
+#define INT_PRESENCE_OFF		2
+#define INT_SWITCH_CLOSE		3
+#define INT_SWITCH_OPEN			4
+#define INT_POWER_FAULT			5
+#define INT_POWER_FAULT_CLEAR		6
+#define INT_BUTTON_PRESS		7
+#define INT_BUTTON_RELEASE		8
+#define INT_BUTTON_CANCEL		9
+
+#define STATIC_STATE			0
+#define BLINKINGON_STATE		1
+#define BLINKINGOFF_STATE		2
+#define POWERON_STATE			3
+#define POWEROFF_STATE			4
+
+/* Error messages */
+#define INTERLOCK_OPEN			0x00000002
+#define ADD_NOT_SUPPORTED		0x00000003
+#define CARD_FUNCTIONING		0x00000005
+#define ADAPTER_NOT_SAME		0x00000006
+#define NO_ADAPTER_PRESENT		0x00000009
+#define NOT_ENOUGH_RESOURCES		0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED	0x0000000C
+#define WRONG_BUS_FREQUENCY		0x0000000D
+#define POWER_FAILURE			0x0000000E
+
+int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
+void shpchp_remove_ctrl_files(struct controller *ctrl);
+int shpchp_sysfs_enable_slot(struct slot *slot);
+int shpchp_sysfs_disable_slot(struct slot *slot);
+u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
+int shpchp_configure_device(struct slot *p_slot);
+int shpchp_unconfigure_device(struct slot *p_slot);
+void cleanup_slots(struct controller *ctrl);
+void shpchp_queue_pushbutton_work(struct work_struct *work);
+int shpc_init(struct controller *ctrl, struct pci_dev *pdev);
+
+static inline const char *slot_name(struct slot *slot)
+{
+	return hotplug_slot_name(slot->hotplug_slot);
+}
+
+#ifdef CONFIG_ACPI
+#include <linux/pci-acpi.h>
+static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+	u32 flags = OSC_PCI_SHPC_NATIVE_HP_CONTROL;
+	return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
+#else
+#define get_hp_hw_control_from_firmware(dev) (0)
+#endif
+
+struct ctrl_reg {
+	volatile u32 base_offset;
+	volatile u32 slot_avail1;
+	volatile u32 slot_avail2;
+	volatile u32 slot_config;
+	volatile u16 sec_bus_config;
+	volatile u8  msi_ctrl;
+	volatile u8  prog_interface;
+	volatile u16 cmd;
+	volatile u16 cmd_status;
+	volatile u32 intr_loc;
+	volatile u32 serr_loc;
+	volatile u32 serr_intr_enable;
+	volatile u32 slot1;
+} __attribute__ ((packed));
+
+/* offsets to the controller registers based on the above structure layout */
+enum ctrl_offsets {
+	BASE_OFFSET	 = offsetof(struct ctrl_reg, base_offset),
+	SLOT_AVAIL1	 = offsetof(struct ctrl_reg, slot_avail1),
+	SLOT_AVAIL2	 = offsetof(struct ctrl_reg, slot_avail2),
+	SLOT_CONFIG	 = offsetof(struct ctrl_reg, slot_config),
+	SEC_BUS_CONFIG	 = offsetof(struct ctrl_reg, sec_bus_config),
+	MSI_CTRL	 = offsetof(struct ctrl_reg, msi_ctrl),
+	PROG_INTERFACE	 = offsetof(struct ctrl_reg, prog_interface),
+	CMD		 = offsetof(struct ctrl_reg, cmd),
+	CMD_STATUS	 = offsetof(struct ctrl_reg, cmd_status),
+	INTR_LOC	 = offsetof(struct ctrl_reg, intr_loc),
+	SERR_LOC	 = offsetof(struct ctrl_reg, serr_loc),
+	SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable),
+	SLOT1		 = offsetof(struct ctrl_reg, slot1),
+};
+
+static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot)
+{
+	return hotplug_slot->private;
+}
+
+static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
+{
+	struct slot *slot;
+
+	list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
+		if (slot->device == device)
+			return slot;
+	}
+
+	ctrl_err(ctrl, "Slot (device=0x%02x) not found\n", device);
+	return NULL;
+}
+
+static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot)
+{
+	u32 pcix_misc2_temp;
+
+	/* save MiscII register */
+	pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp);
+
+	p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp;
+
+	/* clear SERR/PERR enable bits */
+	pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
+	pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
+	pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
+	pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
+	pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
+	pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
+}
+
+static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
+{
+	u32 pcix_misc2_temp;
+	u32 pcix_bridge_errors_reg;
+	u32 pcix_mem_base_reg;
+	u8  perr_set;
+	u8  rse_set;
+
+	/* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */
+	pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg);
+	perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK;
+	if (perr_set) {
+		ctrl_dbg(p_slot->ctrl,
+			 "Bridge_Errors[ PERR_OBSERVED = %08X] (W1C)\n",
+			 perr_set);
+
+		pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set);
+	}
+
+	/* write-one-to-clear Memory_Base_Limit[ RSE ] */
+	pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg);
+	rse_set = pcix_mem_base_reg & RSE_MASK;
+	if (rse_set) {
+		ctrl_dbg(p_slot->ctrl, "Memory_Base_Limit[ RSE ] (W1C)\n");
+
+		pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set);
+	}
+	/* restore MiscII register */
+	pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
+
+	if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK)
+		pcix_misc2_temp |= SERRFATALENABLE_MASK;
+	else
+		pcix_misc2_temp &= ~SERRFATALENABLE_MASK;
+
+	if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK)
+		pcix_misc2_temp |= SERRNONFATALENABLE_MASK;
+	else
+		pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK;
+
+	if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK)
+		pcix_misc2_temp |= PERRFLOODENABLE_MASK;
+	else
+		pcix_misc2_temp &= ~PERRFLOODENABLE_MASK;
+
+	if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK)
+		pcix_misc2_temp |= PERRFATALENABLE_MASK;
+	else
+		pcix_misc2_temp &= ~PERRFATALENABLE_MASK;
+
+	if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK)
+		pcix_misc2_temp |= PERRNONFATALENABLE_MASK;
+	else
+		pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK;
+	pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp);
+}
+
+struct hpc_ops {
+	int (*power_on_slot)(struct slot *slot);
+	int (*slot_enable)(struct slot *slot);
+	int (*slot_disable)(struct slot *slot);
+	int (*set_bus_speed_mode)(struct slot *slot, enum pci_bus_speed speed);
+	int (*get_power_status)(struct slot *slot, u8 *status);
+	int (*get_attention_status)(struct slot *slot, u8 *status);
+	int (*set_attention_status)(struct slot *slot, u8 status);
+	int (*get_latch_status)(struct slot *slot, u8 *status);
+	int (*get_adapter_status)(struct slot *slot, u8 *status);
+	int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed);
+	int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode);
+	int (*get_prog_int)(struct slot *slot, u8 *prog_int);
+	int (*query_power_fault)(struct slot *slot);
+	void (*green_led_on)(struct slot *slot);
+	void (*green_led_off)(struct slot *slot);
+	void (*green_led_blink)(struct slot *slot);
+	void (*release_ctlr)(struct controller *ctrl);
+	int (*check_cmd_status)(struct controller *ctrl);
+};
+
+#endif				/* _SHPCHP_H */
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
new file mode 100644
index 0000000..294ef4b
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -0,0 +1,389 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include "shpchp.h"
+
+/* Global variables */
+bool shpchp_debug;
+bool shpchp_poll_mode;
+int shpchp_poll_time;
+
+#define DRIVER_VERSION	"0.4"
+#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
+#define DRIVER_DESC	"Standard Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(shpchp_debug, bool, 0644);
+module_param(shpchp_poll_mode, bool, 0644);
+module_param(shpchp_poll_time, int, 0644);
+MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
+MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
+MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
+
+#define SHPC_MODULE_NAME "shpchp"
+
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int enable_slot		(struct hotplug_slot *slot);
+static int disable_slot		(struct hotplug_slot *slot);
+static int get_power_status	(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status	(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
+	.set_attention_status =	set_attention_status,
+	.enable_slot =		enable_slot,
+	.disable_slot =		disable_slot,
+	.get_power_status =	get_power_status,
+	.get_attention_status =	get_attention_status,
+	.get_latch_status =	get_latch_status,
+	.get_adapter_status =	get_adapter_status,
+};
+
+/**
+ * release_slot - free up the memory used by a slot
+ * @hotplug_slot: slot to free
+ */
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot);
+	kfree(slot);
+}
+
+static int init_slots(struct controller *ctrl)
+{
+	struct slot *slot;
+	struct hotplug_slot *hotplug_slot;
+	struct hotplug_slot_info *info;
+	char name[SLOT_NAME_SIZE];
+	int retval;
+	int i;
+
+	for (i = 0; i < ctrl->num_slots; i++) {
+		slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+		if (!slot) {
+			retval = -ENOMEM;
+			goto error;
+		}
+
+		hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+		if (!hotplug_slot) {
+			retval = -ENOMEM;
+			goto error_slot;
+		}
+		slot->hotplug_slot = hotplug_slot;
+
+		info = kzalloc(sizeof(*info), GFP_KERNEL);
+		if (!info) {
+			retval = -ENOMEM;
+			goto error_hpslot;
+		}
+		hotplug_slot->info = info;
+
+		slot->hp_slot = i;
+		slot->ctrl = ctrl;
+		slot->bus = ctrl->pci_dev->subordinate->number;
+		slot->device = ctrl->slot_device_offset + i;
+		slot->hpc_ops = ctrl->hpc_ops;
+		slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
+
+		slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
+		if (!slot->wq) {
+			retval = -ENOMEM;
+			goto error_info;
+		}
+
+		mutex_init(&slot->lock);
+		INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
+
+		/* register this slot with the hotplug pci core */
+		hotplug_slot->private = slot;
+		hotplug_slot->release = &release_slot;
+		snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
+		hotplug_slot->ops = &shpchp_hotplug_slot_ops;
+
+		ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
+			 pci_domain_nr(ctrl->pci_dev->subordinate),
+			 slot->bus, slot->device, slot->hp_slot, slot->number,
+			 ctrl->slot_device_offset);
+		retval = pci_hp_register(slot->hotplug_slot,
+				ctrl->pci_dev->subordinate, slot->device, name);
+		if (retval) {
+			ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
+				 retval);
+			goto error_slotwq;
+		}
+
+		get_power_status(hotplug_slot, &info->power_status);
+		get_attention_status(hotplug_slot, &info->attention_status);
+		get_latch_status(hotplug_slot, &info->latch_status);
+		get_adapter_status(hotplug_slot, &info->adapter_status);
+
+		list_add(&slot->slot_list, &ctrl->slot_list);
+	}
+
+	return 0;
+error_slotwq:
+	destroy_workqueue(slot->wq);
+error_info:
+	kfree(info);
+error_hpslot:
+	kfree(hotplug_slot);
+error_slot:
+	kfree(slot);
+error:
+	return retval;
+}
+
+void cleanup_slots(struct controller *ctrl)
+{
+	struct list_head *tmp;
+	struct list_head *next;
+	struct slot *slot;
+
+	list_for_each_safe(tmp, next, &ctrl->slot_list) {
+		slot = list_entry(tmp, struct slot, slot_list);
+		list_del(&slot->slot_list);
+		cancel_delayed_work(&slot->work);
+		destroy_workqueue(slot->wq);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+}
+
+/*
+ * set_attention_status - Turns the Amber LED for a slot on, off or blink
+ */
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	hotplug_slot->info->attention_status = status;
+	slot->hpc_ops->set_attention_status(slot, status);
+
+	return 0;
+}
+
+static int enable_slot (struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	return shpchp_sysfs_enable_slot(slot);
+}
+
+static int disable_slot (struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	return shpchp_sysfs_disable_slot(slot);
+}
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+	int retval;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	retval = slot->hpc_ops->get_power_status(slot, value);
+	if (retval < 0)
+		*value = hotplug_slot->info->power_status;
+
+	return 0;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+	int retval;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	retval = slot->hpc_ops->get_attention_status(slot, value);
+	if (retval < 0)
+		*value = hotplug_slot->info->attention_status;
+
+	return 0;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+	int retval;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	retval = slot->hpc_ops->get_latch_status(slot, value);
+	if (retval < 0)
+		*value = hotplug_slot->info->latch_status;
+
+	return 0;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot(hotplug_slot);
+	int retval;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	retval = slot->hpc_ops->get_adapter_status(slot, value);
+	if (retval < 0)
+		*value = hotplug_slot->info->adapter_status;
+
+	return 0;
+}
+
+static int is_shpc_capable(struct pci_dev *dev)
+{
+	if (dev->vendor == PCI_VENDOR_ID_AMD &&
+	    dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
+		return 1;
+	if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
+		return 0;
+	if (get_hp_hw_control_from_firmware(dev))
+		return 0;
+	return 1;
+}
+
+static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+	struct controller *ctrl;
+
+	if (!is_shpc_capable(pdev))
+		return -ENODEV;
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl) {
+		dev_err(&pdev->dev, "%s: Out of memory\n", __func__);
+		goto err_out_none;
+	}
+	INIT_LIST_HEAD(&ctrl->slot_list);
+
+	rc = shpc_init(ctrl, pdev);
+	if (rc) {
+		ctrl_dbg(ctrl, "Controller initialization failed\n");
+		goto err_out_free_ctrl;
+	}
+
+	pci_set_drvdata(pdev, ctrl);
+
+	/* Setup the slot information structures */
+	rc = init_slots(ctrl);
+	if (rc) {
+		ctrl_err(ctrl, "Slot initialization failed\n");
+		goto err_out_release_ctlr;
+	}
+
+	rc = shpchp_create_ctrl_files(ctrl);
+	if (rc)
+		goto err_cleanup_slots;
+
+	return 0;
+
+err_cleanup_slots:
+	cleanup_slots(ctrl);
+err_out_release_ctlr:
+	ctrl->hpc_ops->release_ctlr(ctrl);
+err_out_free_ctrl:
+	kfree(ctrl);
+err_out_none:
+	return -ENODEV;
+}
+
+static void shpc_remove(struct pci_dev *dev)
+{
+	struct controller *ctrl = pci_get_drvdata(dev);
+
+	shpchp_remove_ctrl_files(ctrl);
+	ctrl->hpc_ops->release_ctlr(ctrl);
+	kfree(ctrl);
+}
+
+static struct pci_device_id shpcd_pci_tbl[] = {
+	{PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)},
+	{ /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
+
+static struct pci_driver shpc_driver = {
+	.name =		SHPC_MODULE_NAME,
+	.id_table =	shpcd_pci_tbl,
+	.probe =	shpc_probe,
+	.remove =	shpc_remove,
+};
+
+static int __init shpcd_init(void)
+{
+	int retval;
+
+	retval = pci_register_driver(&shpc_driver);
+	dbg("%s: pci_register_driver = %d\n", __func__, retval);
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+	return retval;
+}
+
+static void __exit shpcd_cleanup(void)
+{
+	dbg("unload_shpchpd()\n");
+	pci_unregister_driver(&shpc_driver);
+	info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
+}
+
+module_init(shpcd_init);
+module_exit(shpcd_cleanup);
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
new file mode 100644
index 0000000..10c7927
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -0,0 +1,730 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "shpchp.h"
+
+static void interrupt_event_handler(struct work_struct *work);
+static int shpchp_enable_slot(struct slot *p_slot);
+static int shpchp_disable_slot(struct slot *p_slot);
+
+static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
+{
+	struct event_info *info;
+
+	info = kmalloc(sizeof(*info), GFP_ATOMIC);
+	if (!info)
+		return -ENOMEM;
+
+	info->event_type = event_type;
+	info->p_slot = p_slot;
+	INIT_WORK(&info->work, interrupt_event_handler);
+
+	queue_work(p_slot->wq, &info->work);
+
+	return 0;
+}
+
+u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
+{
+	struct slot *p_slot;
+	u32 event_type;
+
+	/* Attention Button Change */
+	ctrl_dbg(ctrl, "Attention button interrupt received\n");
+
+	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
+
+	/*
+	 *  Button pressed - See if need to TAKE ACTION!!!
+	 */
+	ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
+	event_type = INT_BUTTON_PRESS;
+
+	queue_interrupt_event(p_slot, event_type);
+
+	return 0;
+
+}
+
+u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
+{
+	struct slot *p_slot;
+	u8 getstatus;
+	u32 event_type;
+
+	/* Switch Change */
+	ctrl_dbg(ctrl, "Switch interrupt received\n");
+
+	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
+	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+	ctrl_dbg(ctrl, "Card present %x Power status %x\n",
+		 p_slot->presence_save, p_slot->pwr_save);
+
+	if (getstatus) {
+		/*
+		 * Switch opened
+		 */
+		ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
+		event_type = INT_SWITCH_OPEN;
+		if (p_slot->pwr_save && p_slot->presence_save) {
+			event_type = INT_POWER_FAULT;
+			ctrl_err(ctrl, "Surprise Removal of card\n");
+		}
+	} else {
+		/*
+		 *  Switch closed
+		 */
+		ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
+		event_type = INT_SWITCH_CLOSE;
+	}
+
+	queue_interrupt_event(p_slot, event_type);
+
+	return 1;
+}
+
+u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
+{
+	struct slot *p_slot;
+	u32 event_type;
+
+	/* Presence Change */
+	ctrl_dbg(ctrl, "Presence/Notify input change\n");
+
+	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+	/*
+	 * Save the presence state
+	 */
+	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
+	if (p_slot->presence_save) {
+		/*
+		 * Card Present
+		 */
+		ctrl_info(ctrl, "Card present on Slot(%s)\n",
+			  slot_name(p_slot));
+		event_type = INT_PRESENCE_ON;
+	} else {
+		/*
+		 * Not Present
+		 */
+		ctrl_info(ctrl, "Card not present on Slot(%s)\n",
+			  slot_name(p_slot));
+		event_type = INT_PRESENCE_OFF;
+	}
+
+	queue_interrupt_event(p_slot, event_type);
+
+	return 1;
+}
+
+u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
+{
+	struct slot *p_slot;
+	u32 event_type;
+
+	/* Power fault */
+	ctrl_dbg(ctrl, "Power fault interrupt received\n");
+
+	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+	if (!(p_slot->hpc_ops->query_power_fault(p_slot))) {
+		/*
+		 * Power fault Cleared
+		 */
+		ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
+			  slot_name(p_slot));
+		p_slot->status = 0x00;
+		event_type = INT_POWER_FAULT_CLEAR;
+	} else {
+		/*
+		 *   Power fault
+		 */
+		ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
+		event_type = INT_POWER_FAULT;
+		/* set power fault status for this board */
+		p_slot->status = 0xFF;
+		ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot);
+	}
+
+	queue_interrupt_event(p_slot, event_type);
+
+	return 1;
+}
+
+/* The following routines constitute the bulk of the
+   hotplug controller logic
+ */
+static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
+		enum pci_bus_speed speed)
+{
+	int rc = 0;
+
+	ctrl_dbg(ctrl, "Change speed to %d\n", speed);
+	rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed);
+	if (rc) {
+		ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+			 __func__);
+		return WRONG_BUS_FREQUENCY;
+	}
+	return rc;
+}
+
+static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
+		u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp,
+		enum pci_bus_speed msp)
+{
+	int rc = 0;
+
+	/*
+	 * If other slots on the same bus are occupied, we cannot
+	 * change the bus speed.
+	 */
+	if (flag) {
+		if (asp < bsp) {
+			ctrl_err(ctrl, "Speed of bus %x and adapter %x mismatch\n",
+				 bsp, asp);
+			rc = WRONG_BUS_FREQUENCY;
+		}
+		return rc;
+	}
+
+	if (asp < msp) {
+		if (bsp != asp)
+			rc = change_bus_speed(ctrl, pslot, asp);
+	} else {
+		if (bsp != msp)
+			rc = change_bus_speed(ctrl, pslot, msp);
+	}
+	return rc;
+}
+
+/**
+ * board_added - Called after a board has been added to the system.
+ * @p_slot: target &slot
+ *
+ * Turns power on for the board.
+ * Configures board.
+ */
+static int board_added(struct slot *p_slot)
+{
+	u8 hp_slot;
+	u8 slots_not_empty = 0;
+	int rc = 0;
+	enum pci_bus_speed asp, bsp, msp;
+	struct controller *ctrl = p_slot->ctrl;
+	struct pci_bus *parent = ctrl->pci_dev->subordinate;
+
+	hp_slot = p_slot->device - ctrl->slot_device_offset;
+
+	ctrl_dbg(ctrl, "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
+		 __func__, p_slot->device, ctrl->slot_device_offset, hp_slot);
+
+	/* Power on slot without connecting to bus */
+	rc = p_slot->hpc_ops->power_on_slot(p_slot);
+	if (rc) {
+		ctrl_err(ctrl, "Failed to power on slot\n");
+		return -1;
+	}
+
+	if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
+		rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz);
+		if (rc) {
+			ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+				 __func__);
+			return WRONG_BUS_FREQUENCY;
+		}
+
+		/* turn on board, blink green LED, turn off Amber LED */
+		rc = p_slot->hpc_ops->slot_enable(p_slot);
+		if (rc) {
+			ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
+			return rc;
+		}
+	}
+
+	rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp);
+	if (rc) {
+		ctrl_err(ctrl, "Can't get adapter speed or bus mode mismatch\n");
+		return WRONG_BUS_FREQUENCY;
+	}
+
+	bsp = ctrl->pci_dev->subordinate->cur_bus_speed;
+	msp = ctrl->pci_dev->subordinate->max_bus_speed;
+
+	/* Check if there are other slots or devices on the same bus */
+	if (!list_empty(&ctrl->pci_dev->subordinate->devices))
+		slots_not_empty = 1;
+
+	ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, max_bus_speed %d\n",
+		 __func__, slots_not_empty, asp,
+		 bsp, msp);
+
+	rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);
+	if (rc)
+		return rc;
+
+	/* turn on board, blink green LED, turn off Amber LED */
+	rc = p_slot->hpc_ops->slot_enable(p_slot);
+	if (rc) {
+		ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
+		return rc;
+	}
+
+	/* Wait for ~1 second */
+	msleep(1000);
+
+	ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status);
+	/* Check for a power fault */
+	if (p_slot->status == 0xFF) {
+		/* power fault occurred, but it was benign */
+		ctrl_dbg(ctrl, "%s: Power fault\n", __func__);
+		rc = POWER_FAILURE;
+		p_slot->status = 0;
+		goto err_exit;
+	}
+
+	if (shpchp_configure_device(p_slot)) {
+		ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n",
+			 pci_domain_nr(parent), p_slot->bus, p_slot->device);
+		goto err_exit;
+	}
+
+	p_slot->status = 0;
+	p_slot->is_a_board = 0x01;
+	p_slot->pwr_save = 1;
+
+	p_slot->hpc_ops->green_led_on(p_slot);
+
+	return 0;
+
+err_exit:
+	/* turn off slot, turn on Amber LED, turn off Green LED */
+	rc = p_slot->hpc_ops->slot_disable(p_slot);
+	if (rc) {
+		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
+			 __func__);
+		return rc;
+	}
+
+	return(rc);
+}
+
+
+/**
+ * remove_board - Turns off slot and LEDs
+ * @p_slot: target &slot
+ */
+static int remove_board(struct slot *p_slot)
+{
+	struct controller *ctrl = p_slot->ctrl;
+	u8 hp_slot;
+	int rc;
+
+	if (shpchp_unconfigure_device(p_slot))
+		return(1);
+
+	hp_slot = p_slot->device - ctrl->slot_device_offset;
+	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+	ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot);
+
+	/* Change status to shutdown */
+	if (p_slot->is_a_board)
+		p_slot->status = 0x01;
+
+	/* turn off slot, turn on Amber LED, turn off Green LED */
+	rc = p_slot->hpc_ops->slot_disable(p_slot);
+	if (rc) {
+		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
+			 __func__);
+		return rc;
+	}
+
+	rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
+	if (rc) {
+		ctrl_err(ctrl, "Issue of Set Attention command failed\n");
+		return rc;
+	}
+
+	p_slot->pwr_save = 0;
+	p_slot->is_a_board = 0;
+
+	return 0;
+}
+
+
+struct pushbutton_work_info {
+	struct slot *p_slot;
+	struct work_struct work;
+};
+
+/**
+ * shpchp_pushbutton_thread - handle pushbutton events
+ * @work: &struct work_struct to be handled
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons.
+ * Handles all pending events and exits.
+ */
+static void shpchp_pushbutton_thread(struct work_struct *work)
+{
+	struct pushbutton_work_info *info =
+		container_of(work, struct pushbutton_work_info, work);
+	struct slot *p_slot = info->p_slot;
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case POWEROFF_STATE:
+		mutex_unlock(&p_slot->lock);
+		shpchp_disable_slot(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWERON_STATE:
+		mutex_unlock(&p_slot->lock);
+		if (shpchp_enable_slot(p_slot))
+			p_slot->hpc_ops->green_led_off(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	kfree(info);
+}
+
+void shpchp_queue_pushbutton_work(struct work_struct *work)
+{
+	struct slot *p_slot = container_of(work, struct slot, work.work);
+	struct pushbutton_work_info *info;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
+			 __func__);
+		return;
+	}
+	info->p_slot = p_slot;
+	INIT_WORK(&info->work, shpchp_pushbutton_thread);
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGOFF_STATE:
+		p_slot->state = POWEROFF_STATE;
+		break;
+	case BLINKINGON_STATE:
+		p_slot->state = POWERON_STATE;
+		break;
+	default:
+		kfree(info);
+		goto out;
+	}
+	queue_work(p_slot->wq, &info->work);
+ out:
+	mutex_unlock(&p_slot->lock);
+}
+
+static int update_slot_info (struct slot *slot)
+{
+	struct hotplug_slot_info *info;
+	int result;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	slot->hpc_ops->get_power_status(slot, &(info->power_status));
+	slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
+	slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
+	slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
+
+	result = pci_hp_change_slot_info(slot->hotplug_slot, info);
+	kfree (info);
+	return result;
+}
+
+/*
+ * Note: This function must be called with slot->lock held
+ */
+static void handle_button_press_event(struct slot *p_slot)
+{
+	u8 getstatus;
+	struct controller *ctrl = p_slot->ctrl;
+
+	switch (p_slot->state) {
+	case STATIC_STATE:
+		p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+		if (getstatus) {
+			p_slot->state = BLINKINGOFF_STATE;
+			ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+				  slot_name(p_slot));
+		} else {
+			p_slot->state = BLINKINGON_STATE;
+			ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+				  slot_name(p_slot));
+		}
+		/* blink green LED and turn off amber */
+		p_slot->hpc_ops->green_led_blink(p_slot);
+		p_slot->hpc_ops->set_attention_status(p_slot, 0);
+
+		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
+		break;
+	case BLINKINGOFF_STATE:
+	case BLINKINGON_STATE:
+		/*
+		 * Cancel if we are still blinking; this means that we
+		 * press the attention again before the 5 sec. limit
+		 * expires to cancel hot-add or hot-remove
+		 */
+		ctrl_info(ctrl, "Button cancel on Slot(%s)\n",
+			  slot_name(p_slot));
+		cancel_delayed_work(&p_slot->work);
+		if (p_slot->state == BLINKINGOFF_STATE)
+			p_slot->hpc_ops->green_led_on(p_slot);
+		else
+			p_slot->hpc_ops->green_led_off(p_slot);
+		p_slot->hpc_ops->set_attention_status(p_slot, 0);
+		ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+			  slot_name(p_slot));
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWEROFF_STATE:
+	case POWERON_STATE:
+		/*
+		 * Ignore if the slot is on power-on or power-off state;
+		 * this means that the previous attention button action
+		 * to hot-add or hot-remove is undergoing
+		 */
+		ctrl_info(ctrl, "Button ignore on Slot(%s)\n",
+			  slot_name(p_slot));
+		update_slot_info(p_slot);
+		break;
+	default:
+		ctrl_warn(ctrl, "Not a valid state\n");
+		break;
+	}
+}
+
+static void interrupt_event_handler(struct work_struct *work)
+{
+	struct event_info *info = container_of(work, struct event_info, work);
+	struct slot *p_slot = info->p_slot;
+
+	mutex_lock(&p_slot->lock);
+	switch (info->event_type) {
+	case INT_BUTTON_PRESS:
+		handle_button_press_event(p_slot);
+		break;
+	case INT_POWER_FAULT:
+		ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__);
+		p_slot->hpc_ops->set_attention_status(p_slot, 1);
+		p_slot->hpc_ops->green_led_off(p_slot);
+		break;
+	default:
+		update_slot_info(p_slot);
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	kfree(info);
+}
+
+
+static int shpchp_enable_slot (struct slot *p_slot)
+{
+	u8 getstatus = 0;
+	int rc, retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	/* Check to see if (latch closed, card present, power off) */
+	mutex_lock(&p_slot->ctrl->crit_sect);
+	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+	if (rc || !getstatus) {
+		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
+		goto out;
+	}
+	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+	if (rc || getstatus) {
+		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
+		goto out;
+	}
+	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+	if (rc || getstatus) {
+		ctrl_info(ctrl, "Already enabled on slot(%s)\n",
+			  slot_name(p_slot));
+		goto out;
+	}
+
+	p_slot->is_a_board = 1;
+
+	/* We have to save the presence info for these slots */
+	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
+	p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save));
+	ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save);
+	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+
+	if (((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
+	    (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
+	     && p_slot->ctrl->num_slots == 1) {
+		/* handle amd pogo errata; this must be done before enable  */
+		amd_pogo_errata_save_misc_reg(p_slot);
+		retval = board_added(p_slot);
+		/* handle amd pogo errata; this must be done after enable  */
+		amd_pogo_errata_restore_misc_reg(p_slot);
+	} else
+		retval = board_added(p_slot);
+
+	if (retval) {
+		p_slot->hpc_ops->get_adapter_status(p_slot,
+				&(p_slot->presence_save));
+		p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+	}
+
+	update_slot_info(p_slot);
+ out:
+	mutex_unlock(&p_slot->ctrl->crit_sect);
+	return retval;
+}
+
+
+static int shpchp_disable_slot (struct slot *p_slot)
+{
+	u8 getstatus = 0;
+	int rc, retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	if (!p_slot->ctrl)
+		return -ENODEV;
+
+	/* Check to see if (latch closed, card present, power on) */
+	mutex_lock(&p_slot->ctrl->crit_sect);
+
+	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+	if (rc || !getstatus) {
+		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
+		goto out;
+	}
+	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+	if (rc || getstatus) {
+		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
+		goto out;
+	}
+	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+	if (rc || !getstatus) {
+		ctrl_info(ctrl, "Already disabled on slot(%s)\n",
+			  slot_name(p_slot));
+		goto out;
+	}
+
+	retval = remove_board(p_slot);
+	update_slot_info(p_slot);
+ out:
+	mutex_unlock(&p_slot->ctrl->crit_sect);
+	return retval;
+}
+
+int shpchp_sysfs_enable_slot(struct slot *p_slot)
+{
+	int retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGON_STATE:
+		cancel_delayed_work(&p_slot->work);
+	case STATIC_STATE:
+		p_slot->state = POWERON_STATE;
+		mutex_unlock(&p_slot->lock);
+		retval = shpchp_enable_slot(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWERON_STATE:
+		ctrl_info(ctrl, "Slot %s is already in powering on state\n",
+			  slot_name(p_slot));
+		break;
+	case BLINKINGOFF_STATE:
+	case POWEROFF_STATE:
+		ctrl_info(ctrl, "Already enabled on slot %s\n",
+			  slot_name(p_slot));
+		break;
+	default:
+		ctrl_err(ctrl, "Not a valid state on slot %s\n",
+			 slot_name(p_slot));
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	return retval;
+}
+
+int shpchp_sysfs_disable_slot(struct slot *p_slot)
+{
+	int retval = -ENODEV;
+	struct controller *ctrl = p_slot->ctrl;
+
+	mutex_lock(&p_slot->lock);
+	switch (p_slot->state) {
+	case BLINKINGOFF_STATE:
+		cancel_delayed_work(&p_slot->work);
+	case STATIC_STATE:
+		p_slot->state = POWEROFF_STATE;
+		mutex_unlock(&p_slot->lock);
+		retval = shpchp_disable_slot(p_slot);
+		mutex_lock(&p_slot->lock);
+		p_slot->state = STATIC_STATE;
+		break;
+	case POWEROFF_STATE:
+		ctrl_info(ctrl, "Slot %s is already in powering off state\n",
+			  slot_name(p_slot));
+		break;
+	case BLINKINGON_STATE:
+	case POWERON_STATE:
+		ctrl_info(ctrl, "Already disabled on slot %s\n",
+			  slot_name(p_slot));
+		break;
+	default:
+		ctrl_err(ctrl, "Not a valid state on slot %s\n",
+			 slot_name(p_slot));
+		break;
+	}
+	mutex_unlock(&p_slot->lock);
+
+	return retval;
+}
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
new file mode 100644
index 0000000..77dddee
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -0,0 +1,1113 @@
+/*
+ * Standard PCI Hot Plug Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>,<kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "shpchp.h"
+
+/* Slot Available Register I field definition */
+#define SLOT_33MHZ		0x0000001f
+#define SLOT_66MHZ_PCIX		0x00001f00
+#define SLOT_100MHZ_PCIX	0x001f0000
+#define SLOT_133MHZ_PCIX	0x1f000000
+
+/* Slot Available Register II field definition */
+#define SLOT_66MHZ		0x0000001f
+#define SLOT_66MHZ_PCIX_266	0x00000f00
+#define SLOT_100MHZ_PCIX_266	0x0000f000
+#define SLOT_133MHZ_PCIX_266	0x000f0000
+#define SLOT_66MHZ_PCIX_533	0x00f00000
+#define SLOT_100MHZ_PCIX_533	0x0f000000
+#define SLOT_133MHZ_PCIX_533	0xf0000000
+
+/* Slot Configuration */
+#define SLOT_NUM		0x0000001F
+#define	FIRST_DEV_NUM		0x00001F00
+#define PSN			0x07FF0000
+#define	UPDOWN			0x20000000
+#define	MRLSENSOR		0x40000000
+#define ATTN_BUTTON		0x80000000
+
+/*
+ * Interrupt Locator Register definitions
+ */
+#define CMD_INTR_PENDING	(1 << 0)
+#define SLOT_INTR_PENDING(i)	(1 << (i + 1))
+
+/*
+ * Controller SERR-INT Register
+ */
+#define GLOBAL_INTR_MASK	(1 << 0)
+#define GLOBAL_SERR_MASK	(1 << 1)
+#define COMMAND_INTR_MASK	(1 << 2)
+#define ARBITER_SERR_MASK	(1 << 3)
+#define COMMAND_DETECTED	(1 << 16)
+#define ARBITER_DETECTED	(1 << 17)
+#define SERR_INTR_RSVDZ_MASK	0xfffc0000
+
+/*
+ * Logical Slot Register definitions
+ */
+#define SLOT_REG(i)		(SLOT1 + (4 * i))
+
+#define SLOT_STATE_SHIFT	(0)
+#define SLOT_STATE_MASK		(3 << 0)
+#define SLOT_STATE_PWRONLY	(1)
+#define SLOT_STATE_ENABLED	(2)
+#define SLOT_STATE_DISABLED	(3)
+#define PWR_LED_STATE_SHIFT	(2)
+#define PWR_LED_STATE_MASK	(3 << 2)
+#define ATN_LED_STATE_SHIFT	(4)
+#define ATN_LED_STATE_MASK	(3 << 4)
+#define ATN_LED_STATE_ON	(1)
+#define ATN_LED_STATE_BLINK	(2)
+#define ATN_LED_STATE_OFF	(3)
+#define POWER_FAULT		(1 << 6)
+#define ATN_BUTTON		(1 << 7)
+#define MRL_SENSOR		(1 << 8)
+#define MHZ66_CAP		(1 << 9)
+#define PRSNT_SHIFT		(10)
+#define PRSNT_MASK		(3 << 10)
+#define PCIX_CAP_SHIFT		(12)
+#define PCIX_CAP_MASK_PI1	(3 << 12)
+#define PCIX_CAP_MASK_PI2	(7 << 12)
+#define PRSNT_CHANGE_DETECTED	(1 << 16)
+#define ISO_PFAULT_DETECTED	(1 << 17)
+#define BUTTON_PRESS_DETECTED	(1 << 18)
+#define MRL_CHANGE_DETECTED	(1 << 19)
+#define CON_PFAULT_DETECTED	(1 << 20)
+#define PRSNT_CHANGE_INTR_MASK	(1 << 24)
+#define ISO_PFAULT_INTR_MASK	(1 << 25)
+#define BUTTON_PRESS_INTR_MASK	(1 << 26)
+#define MRL_CHANGE_INTR_MASK	(1 << 27)
+#define CON_PFAULT_INTR_MASK	(1 << 28)
+#define MRL_CHANGE_SERR_MASK	(1 << 29)
+#define CON_PFAULT_SERR_MASK	(1 << 30)
+#define SLOT_REG_RSVDZ_MASK	((1 << 15) | (7 << 21))
+
+/*
+ * SHPC Command Code definitions
+ *
+ *     Slot Operation				00h - 3Fh
+ *     Set Bus Segment Speed/Mode A		40h - 47h
+ *     Power-Only All Slots			48h
+ *     Enable All Slots				49h
+ *     Set Bus Segment Speed/Mode B (PI=2)	50h - 5Fh
+ *     Reserved Command Codes			60h - BFh
+ *     Vendor Specific Commands			C0h - FFh
+ */
+#define SET_SLOT_PWR		0x01	/* Slot Operation */
+#define SET_SLOT_ENABLE		0x02
+#define SET_SLOT_DISABLE	0x03
+#define SET_PWR_ON		0x04
+#define SET_PWR_BLINK		0x08
+#define SET_PWR_OFF		0x0c
+#define SET_ATTN_ON		0x10
+#define SET_ATTN_BLINK		0x20
+#define SET_ATTN_OFF		0x30
+#define SETA_PCI_33MHZ		0x40	/* Set Bus Segment Speed/Mode A */
+#define SETA_PCI_66MHZ		0x41
+#define SETA_PCIX_66MHZ		0x42
+#define SETA_PCIX_100MHZ	0x43
+#define SETA_PCIX_133MHZ	0x44
+#define SETA_RESERVED1		0x45
+#define SETA_RESERVED2		0x46
+#define SETA_RESERVED3		0x47
+#define SET_PWR_ONLY_ALL	0x48	/* Power-Only All Slots */
+#define SET_ENABLE_ALL		0x49	/* Enable All Slots */
+#define	SETB_PCI_33MHZ		0x50	/* Set Bus Segment Speed/Mode B */
+#define SETB_PCI_66MHZ		0x51
+#define SETB_PCIX_66MHZ_PM	0x52
+#define SETB_PCIX_100MHZ_PM	0x53
+#define SETB_PCIX_133MHZ_PM	0x54
+#define SETB_PCIX_66MHZ_EM	0x55
+#define SETB_PCIX_100MHZ_EM	0x56
+#define SETB_PCIX_133MHZ_EM	0x57
+#define SETB_PCIX_66MHZ_266	0x58
+#define SETB_PCIX_100MHZ_266	0x59
+#define SETB_PCIX_133MHZ_266	0x5a
+#define SETB_PCIX_66MHZ_533	0x5b
+#define SETB_PCIX_100MHZ_533	0x5c
+#define SETB_PCIX_133MHZ_533	0x5d
+#define SETB_RESERVED1		0x5e
+#define SETB_RESERVED2		0x5f
+
+/*
+ * SHPC controller command error code
+ */
+#define SWITCH_OPEN		0x1
+#define INVALID_CMD		0x2
+#define INVALID_SPEED_MODE	0x4
+
+/*
+ * For accessing SHPC Working Register Set via PCI Configuration Space
+ */
+#define DWORD_SELECT		0x2
+#define DWORD_DATA		0x4
+
+/* Field Offset in Logical Slot Register - byte boundary */
+#define SLOT_EVENT_LATCH	0x2
+#define SLOT_SERR_INT_MASK	0x3
+
+static irqreturn_t shpc_isr(int irq, void *dev_id);
+static void start_int_poll_timer(struct controller *ctrl, int sec);
+static int hpc_check_cmd_status(struct controller *ctrl);
+
+static inline u8 shpc_readb(struct controller *ctrl, int reg)
+{
+	return readb(ctrl->creg + reg);
+}
+
+static inline void shpc_writeb(struct controller *ctrl, int reg, u8 val)
+{
+	writeb(val, ctrl->creg + reg);
+}
+
+static inline u16 shpc_readw(struct controller *ctrl, int reg)
+{
+	return readw(ctrl->creg + reg);
+}
+
+static inline void shpc_writew(struct controller *ctrl, int reg, u16 val)
+{
+	writew(val, ctrl->creg + reg);
+}
+
+static inline u32 shpc_readl(struct controller *ctrl, int reg)
+{
+	return readl(ctrl->creg + reg);
+}
+
+static inline void shpc_writel(struct controller *ctrl, int reg, u32 val)
+{
+	writel(val, ctrl->creg + reg);
+}
+
+static inline int shpc_indirect_read(struct controller *ctrl, int index,
+				     u32 *value)
+{
+	int rc;
+	u32 cap_offset = ctrl->cap_offset;
+	struct pci_dev *pdev = ctrl->pci_dev;
+
+	rc = pci_write_config_byte(pdev, cap_offset + DWORD_SELECT, index);
+	if (rc)
+		return rc;
+	return pci_read_config_dword(pdev, cap_offset + DWORD_DATA, value);
+}
+
+/*
+ * This is the interrupt polling timeout function.
+ */
+static void int_poll_timeout(unsigned long data)
+{
+	struct controller *ctrl = (struct controller *)data;
+
+	/* Poll for interrupt events.  regs == NULL => polling */
+	shpc_isr(0, ctrl);
+
+	init_timer(&ctrl->poll_timer);
+	if (!shpchp_poll_time)
+		shpchp_poll_time = 2; /* default polling interval is 2 sec */
+
+	start_int_poll_timer(ctrl, shpchp_poll_time);
+}
+
+/*
+ * This function starts the interrupt polling timer.
+ */
+static void start_int_poll_timer(struct controller *ctrl, int sec)
+{
+	/* Clamp to sane value */
+	if ((sec <= 0) || (sec > 60))
+		sec = 2;
+
+	ctrl->poll_timer.function = &int_poll_timeout;
+	ctrl->poll_timer.data = (unsigned long)ctrl;
+	ctrl->poll_timer.expires = jiffies + sec * HZ;
+	add_timer(&ctrl->poll_timer);
+}
+
+static inline int is_ctrl_busy(struct controller *ctrl)
+{
+	u16 cmd_status = shpc_readw(ctrl, CMD_STATUS);
+	return cmd_status & 0x1;
+}
+
+/*
+ * Returns 1 if SHPC finishes executing a command within 1 sec,
+ * otherwise returns 0.
+ */
+static inline int shpc_poll_ctrl_busy(struct controller *ctrl)
+{
+	int i;
+
+	if (!is_ctrl_busy(ctrl))
+		return 1;
+
+	/* Check every 0.1 sec for a total of 1 sec */
+	for (i = 0; i < 10; i++) {
+		msleep(100);
+		if (!is_ctrl_busy(ctrl))
+			return 1;
+	}
+
+	return 0;
+}
+
+static inline int shpc_wait_cmd(struct controller *ctrl)
+{
+	int retval = 0;
+	unsigned long timeout = msecs_to_jiffies(1000);
+	int rc;
+
+	if (shpchp_poll_mode)
+		rc = shpc_poll_ctrl_busy(ctrl);
+	else
+		rc = wait_event_interruptible_timeout(ctrl->queue,
+						!is_ctrl_busy(ctrl), timeout);
+	if (!rc && is_ctrl_busy(ctrl)) {
+		retval = -EIO;
+		ctrl_err(ctrl, "Command not completed in 1000 msec\n");
+	} else if (rc < 0) {
+		retval = -EINTR;
+		ctrl_info(ctrl, "Command was interrupted by a signal\n");
+	}
+
+	return retval;
+}
+
+static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
+{
+	struct controller *ctrl = slot->ctrl;
+	u16 cmd_status;
+	int retval = 0;
+	u16 temp_word;
+
+	mutex_lock(&slot->ctrl->cmd_lock);
+
+	if (!shpc_poll_ctrl_busy(ctrl)) {
+		/* After 1 sec and and the controller is still busy */
+		ctrl_err(ctrl, "Controller is still busy after 1 sec\n");
+		retval = -EBUSY;
+		goto out;
+	}
+
+	++t_slot;
+	temp_word =  (t_slot << 8) | (cmd & 0xFF);
+	ctrl_dbg(ctrl, "%s: t_slot %x cmd %x\n", __func__, t_slot, cmd);
+
+	/* To make sure the Controller Busy bit is 0 before we send out the
+	 * command.
+	 */
+	shpc_writew(ctrl, CMD, temp_word);
+
+	/*
+	 * Wait for command completion.
+	 */
+	retval = shpc_wait_cmd(slot->ctrl);
+	if (retval)
+		goto out;
+
+	cmd_status = hpc_check_cmd_status(slot->ctrl);
+	if (cmd_status) {
+		ctrl_err(ctrl, "Failed to issued command 0x%x (error code = %d)\n",
+			 cmd, cmd_status);
+		retval = -EIO;
+	}
+ out:
+	mutex_unlock(&slot->ctrl->cmd_lock);
+	return retval;
+}
+
+static int hpc_check_cmd_status(struct controller *ctrl)
+{
+	int retval = 0;
+	u16 cmd_status = shpc_readw(ctrl, CMD_STATUS) & 0x000F;
+
+	switch (cmd_status >> 1) {
+	case 0:
+		retval = 0;
+		break;
+	case 1:
+		retval = SWITCH_OPEN;
+		ctrl_err(ctrl, "Switch opened!\n");
+		break;
+	case 2:
+		retval = INVALID_CMD;
+		ctrl_err(ctrl, "Invalid HPC command!\n");
+		break;
+	case 4:
+		retval = INVALID_SPEED_MODE;
+		ctrl_err(ctrl, "Invalid bus speed/mode!\n");
+		break;
+	default:
+		retval = cmd_status;
+	}
+
+	return retval;
+}
+
+
+static int hpc_get_attention_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+	u8 state = (slot_reg & ATN_LED_STATE_MASK) >> ATN_LED_STATE_SHIFT;
+
+	switch (state) {
+	case ATN_LED_STATE_ON:
+		*status = 1;	/* On */
+		break;
+	case ATN_LED_STATE_BLINK:
+		*status = 2;	/* Blink */
+		break;
+	case ATN_LED_STATE_OFF:
+		*status = 0;	/* Off */
+		break;
+	default:
+		*status = 0xFF;	/* Reserved */
+		break;
+	}
+
+	return 0;
+}
+
+static int hpc_get_power_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+	u8 state = (slot_reg & SLOT_STATE_MASK) >> SLOT_STATE_SHIFT;
+
+	switch (state) {
+	case SLOT_STATE_PWRONLY:
+		*status = 2;	/* Powered only */
+		break;
+	case SLOT_STATE_ENABLED:
+		*status = 1;	/* Enabled */
+		break;
+	case SLOT_STATE_DISABLED:
+		*status = 0;	/* Disabled */
+		break;
+	default:
+		*status = 0xFF;	/* Reserved */
+		break;
+	}
+
+	return 0;
+}
+
+
+static int hpc_get_latch_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+
+	*status = !!(slot_reg & MRL_SENSOR);	/* 0 -> close; 1 -> open */
+
+	return 0;
+}
+
+static int hpc_get_adapter_status(struct slot *slot, u8 *status)
+{
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+	u8 state = (slot_reg & PRSNT_MASK) >> PRSNT_SHIFT;
+
+	*status = (state != 0x3) ? 1 : 0;
+
+	return 0;
+}
+
+static int hpc_get_prog_int(struct slot *slot, u8 *prog_int)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	*prog_int = shpc_readb(ctrl, PROG_INTERFACE);
+
+	return 0;
+}
+
+static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
+{
+	int retval = 0;
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+	u8 m66_cap  = !!(slot_reg & MHZ66_CAP);
+	u8 pi, pcix_cap;
+
+	retval = hpc_get_prog_int(slot, &pi);
+	if (retval)
+		return retval;
+
+	switch (pi) {
+	case 1:
+		pcix_cap = (slot_reg & PCIX_CAP_MASK_PI1) >> PCIX_CAP_SHIFT;
+		break;
+	case 2:
+		pcix_cap = (slot_reg & PCIX_CAP_MASK_PI2) >> PCIX_CAP_SHIFT;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	ctrl_dbg(ctrl, "%s: slot_reg = %x, pcix_cap = %x, m66_cap = %x\n",
+		 __func__, slot_reg, pcix_cap, m66_cap);
+
+	switch (pcix_cap) {
+	case 0x0:
+		*value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+		break;
+	case 0x1:
+		*value = PCI_SPEED_66MHz_PCIX;
+		break;
+	case 0x3:
+		*value = PCI_SPEED_133MHz_PCIX;
+		break;
+	case 0x4:
+		*value = PCI_SPEED_133MHz_PCIX_266;
+		break;
+	case 0x5:
+		*value = PCI_SPEED_133MHz_PCIX_533;
+		break;
+	case 0x2:
+	default:
+		*value = PCI_SPEED_UNKNOWN;
+		retval = -ENODEV;
+		break;
+	}
+
+	ctrl_dbg(ctrl, "Adapter speed = %d\n", *value);
+	return retval;
+}
+
+static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
+{
+	int retval = 0;
+	struct controller *ctrl = slot->ctrl;
+	u16 sec_bus_status = shpc_readw(ctrl, SEC_BUS_CONFIG);
+	u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
+
+	if (pi == 2) {
+		*mode = (sec_bus_status & 0x0100) >> 8;
+	} else {
+		retval = -1;
+	}
+
+	ctrl_dbg(ctrl, "Mode 1 ECC cap = %d\n", *mode);
+	return retval;
+}
+
+static int hpc_query_power_fault(struct slot *slot)
+{
+	struct controller *ctrl = slot->ctrl;
+	u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
+
+	/* Note: Logic 0 => fault */
+	return !(slot_reg & POWER_FAULT);
+}
+
+static int hpc_set_attention_status(struct slot *slot, u8 value)
+{
+	u8 slot_cmd = 0;
+
+	switch (value) {
+		case 0 :
+			slot_cmd = SET_ATTN_OFF;	/* OFF */
+			break;
+		case 1:
+			slot_cmd = SET_ATTN_ON;		/* ON */
+			break;
+		case 2:
+			slot_cmd = SET_ATTN_BLINK;	/* BLINK */
+			break;
+		default:
+			return -1;
+	}
+
+	return shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+}
+
+
+static void hpc_set_green_led_on(struct slot *slot)
+{
+	shpc_write_cmd(slot, slot->hp_slot, SET_PWR_ON);
+}
+
+static void hpc_set_green_led_off(struct slot *slot)
+{
+	shpc_write_cmd(slot, slot->hp_slot, SET_PWR_OFF);
+}
+
+static void hpc_set_green_led_blink(struct slot *slot)
+{
+	shpc_write_cmd(slot, slot->hp_slot, SET_PWR_BLINK);
+}
+
+static void hpc_release_ctlr(struct controller *ctrl)
+{
+	int i;
+	u32 slot_reg, serr_int;
+
+	/*
+	 * Mask event interrupts and SERRs of all slots
+	 */
+	for (i = 0; i < ctrl->num_slots; i++) {
+		slot_reg = shpc_readl(ctrl, SLOT_REG(i));
+		slot_reg |= (PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
+			     BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
+			     CON_PFAULT_INTR_MASK   | MRL_CHANGE_SERR_MASK |
+			     CON_PFAULT_SERR_MASK);
+		slot_reg &= ~SLOT_REG_RSVDZ_MASK;
+		shpc_writel(ctrl, SLOT_REG(i), slot_reg);
+	}
+
+	cleanup_slots(ctrl);
+
+	/*
+	 * Mask SERR and System Interrupt generation
+	 */
+	serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
+	serr_int |= (GLOBAL_INTR_MASK  | GLOBAL_SERR_MASK |
+		     COMMAND_INTR_MASK | ARBITER_SERR_MASK);
+	serr_int &= ~SERR_INTR_RSVDZ_MASK;
+	shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
+
+	if (shpchp_poll_mode)
+		del_timer(&ctrl->poll_timer);
+	else {
+		free_irq(ctrl->pci_dev->irq, ctrl);
+		pci_disable_msi(ctrl->pci_dev);
+	}
+
+	iounmap(ctrl->creg);
+	release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
+}
+
+static int hpc_power_on_slot(struct slot *slot)
+{
+	int retval;
+
+	retval = shpc_write_cmd(slot, slot->hp_slot, SET_SLOT_PWR);
+	if (retval)
+		ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
+
+	return retval;
+}
+
+static int hpc_slot_enable(struct slot *slot)
+{
+	int retval;
+
+	/* Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */
+	retval = shpc_write_cmd(slot, slot->hp_slot,
+			SET_SLOT_ENABLE | SET_PWR_BLINK | SET_ATTN_OFF);
+	if (retval)
+		ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
+
+	return retval;
+}
+
+static int hpc_slot_disable(struct slot *slot)
+{
+	int retval;
+
+	/* Slot - Disable, Power Indicator - Off, Attention Indicator - On */
+	retval = shpc_write_cmd(slot, slot->hp_slot,
+			SET_SLOT_DISABLE | SET_PWR_OFF | SET_ATTN_ON);
+	if (retval)
+		ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
+
+	return retval;
+}
+
+static int shpc_get_cur_bus_speed(struct controller *ctrl)
+{
+	int retval = 0;
+	struct pci_bus *bus = ctrl->pci_dev->subordinate;
+	enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+	u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
+	u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
+	u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);
+
+	if ((pi == 1) && (speed_mode > 4)) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	switch (speed_mode) {
+	case 0x0:
+		bus_speed = PCI_SPEED_33MHz;
+		break;
+	case 0x1:
+		bus_speed = PCI_SPEED_66MHz;
+		break;
+	case 0x2:
+		bus_speed = PCI_SPEED_66MHz_PCIX;
+		break;
+	case 0x3:
+		bus_speed = PCI_SPEED_100MHz_PCIX;
+		break;
+	case 0x4:
+		bus_speed = PCI_SPEED_133MHz_PCIX;
+		break;
+	case 0x5:
+		bus_speed = PCI_SPEED_66MHz_PCIX_ECC;
+		break;
+	case 0x6:
+		bus_speed = PCI_SPEED_100MHz_PCIX_ECC;
+		break;
+	case 0x7:
+		bus_speed = PCI_SPEED_133MHz_PCIX_ECC;
+		break;
+	case 0x8:
+		bus_speed = PCI_SPEED_66MHz_PCIX_266;
+		break;
+	case 0x9:
+		bus_speed = PCI_SPEED_100MHz_PCIX_266;
+		break;
+	case 0xa:
+		bus_speed = PCI_SPEED_133MHz_PCIX_266;
+		break;
+	case 0xb:
+		bus_speed = PCI_SPEED_66MHz_PCIX_533;
+		break;
+	case 0xc:
+		bus_speed = PCI_SPEED_100MHz_PCIX_533;
+		break;
+	case 0xd:
+		bus_speed = PCI_SPEED_133MHz_PCIX_533;
+		break;
+	default:
+		retval = -ENODEV;
+		break;
+	}
+
+ out:
+	bus->cur_bus_speed = bus_speed;
+	dbg("Current bus speed = %d\n", bus_speed);
+	return retval;
+}
+
+
+static int hpc_set_bus_speed_mode(struct slot *slot, enum pci_bus_speed value)
+{
+	int retval;
+	struct controller *ctrl = slot->ctrl;
+	u8 pi, cmd;
+
+	pi = shpc_readb(ctrl, PROG_INTERFACE);
+	if ((pi == 1) && (value > PCI_SPEED_133MHz_PCIX))
+		return -EINVAL;
+
+	switch (value) {
+	case PCI_SPEED_33MHz:
+		cmd = SETA_PCI_33MHZ;
+		break;
+	case PCI_SPEED_66MHz:
+		cmd = SETA_PCI_66MHZ;
+		break;
+	case PCI_SPEED_66MHz_PCIX:
+		cmd = SETA_PCIX_66MHZ;
+		break;
+	case PCI_SPEED_100MHz_PCIX:
+		cmd = SETA_PCIX_100MHZ;
+		break;
+	case PCI_SPEED_133MHz_PCIX:
+		cmd = SETA_PCIX_133MHZ;
+		break;
+	case PCI_SPEED_66MHz_PCIX_ECC:
+		cmd = SETB_PCIX_66MHZ_EM;
+		break;
+	case PCI_SPEED_100MHz_PCIX_ECC:
+		cmd = SETB_PCIX_100MHZ_EM;
+		break;
+	case PCI_SPEED_133MHz_PCIX_ECC:
+		cmd = SETB_PCIX_133MHZ_EM;
+		break;
+	case PCI_SPEED_66MHz_PCIX_266:
+		cmd = SETB_PCIX_66MHZ_266;
+		break;
+	case PCI_SPEED_100MHz_PCIX_266:
+		cmd = SETB_PCIX_100MHZ_266;
+		break;
+	case PCI_SPEED_133MHz_PCIX_266:
+		cmd = SETB_PCIX_133MHZ_266;
+		break;
+	case PCI_SPEED_66MHz_PCIX_533:
+		cmd = SETB_PCIX_66MHZ_533;
+		break;
+	case PCI_SPEED_100MHz_PCIX_533:
+		cmd = SETB_PCIX_100MHZ_533;
+		break;
+	case PCI_SPEED_133MHz_PCIX_533:
+		cmd = SETB_PCIX_133MHZ_533;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	retval = shpc_write_cmd(slot, 0, cmd);
+	if (retval)
+		ctrl_err(ctrl, "%s: Write command failed!\n", __func__);
+	else
+		shpc_get_cur_bus_speed(ctrl);
+
+	return retval;
+}
+
+static irqreturn_t shpc_isr(int irq, void *dev_id)
+{
+	struct controller *ctrl = (struct controller *)dev_id;
+	u32 serr_int, slot_reg, intr_loc, intr_loc2;
+	int hp_slot;
+
+	/* Check to see if it was our interrupt */
+	intr_loc = shpc_readl(ctrl, INTR_LOC);
+	if (!intr_loc)
+		return IRQ_NONE;
+
+	ctrl_dbg(ctrl, "%s: intr_loc = %x\n", __func__, intr_loc);
+
+	if (!shpchp_poll_mode) {
+		/*
+		 * Mask Global Interrupt Mask - see implementation
+		 * note on p. 139 of SHPC spec rev 1.0
+		 */
+		serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
+		serr_int |= GLOBAL_INTR_MASK;
+		serr_int &= ~SERR_INTR_RSVDZ_MASK;
+		shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
+
+		intr_loc2 = shpc_readl(ctrl, INTR_LOC);
+		ctrl_dbg(ctrl, "%s: intr_loc2 = %x\n", __func__, intr_loc2);
+	}
+
+	if (intr_loc & CMD_INTR_PENDING) {
+		/*
+		 * Command Complete Interrupt Pending
+		 * RO only - clear by writing 1 to the Command Completion
+		 * Detect bit in Controller SERR-INT register
+		 */
+		serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
+		serr_int &= ~SERR_INTR_RSVDZ_MASK;
+		shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
+
+		wake_up_interruptible(&ctrl->queue);
+	}
+
+	if (!(intr_loc & ~CMD_INTR_PENDING))
+		goto out;
+
+	for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
+		/* To find out which slot has interrupt pending */
+		if (!(intr_loc & SLOT_INTR_PENDING(hp_slot)))
+			continue;
+
+		slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
+		ctrl_dbg(ctrl, "Slot %x with intr, slot register = %x\n",
+			 hp_slot, slot_reg);
+
+		if (slot_reg & MRL_CHANGE_DETECTED)
+			shpchp_handle_switch_change(hp_slot, ctrl);
+
+		if (slot_reg & BUTTON_PRESS_DETECTED)
+			shpchp_handle_attention_button(hp_slot, ctrl);
+
+		if (slot_reg & PRSNT_CHANGE_DETECTED)
+			shpchp_handle_presence_change(hp_slot, ctrl);
+
+		if (slot_reg & (ISO_PFAULT_DETECTED | CON_PFAULT_DETECTED))
+			shpchp_handle_power_fault(hp_slot, ctrl);
+
+		/* Clear all slot events */
+		slot_reg &= ~SLOT_REG_RSVDZ_MASK;
+		shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
+	}
+ out:
+	if (!shpchp_poll_mode) {
+		/* Unmask Global Interrupt Mask */
+		serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE);
+		serr_int &= ~(GLOBAL_INTR_MASK | SERR_INTR_RSVDZ_MASK);
+		shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int shpc_get_max_bus_speed(struct controller *ctrl)
+{
+	int retval = 0;
+	struct pci_bus *bus = ctrl->pci_dev->subordinate;
+	enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+	u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
+	u32 slot_avail1 = shpc_readl(ctrl, SLOT_AVAIL1);
+	u32 slot_avail2 = shpc_readl(ctrl, SLOT_AVAIL2);
+
+	if (pi == 2) {
+		if (slot_avail2 & SLOT_133MHZ_PCIX_533)
+			bus_speed = PCI_SPEED_133MHz_PCIX_533;
+		else if (slot_avail2 & SLOT_100MHZ_PCIX_533)
+			bus_speed = PCI_SPEED_100MHz_PCIX_533;
+		else if (slot_avail2 & SLOT_66MHZ_PCIX_533)
+			bus_speed = PCI_SPEED_66MHz_PCIX_533;
+		else if (slot_avail2 & SLOT_133MHZ_PCIX_266)
+			bus_speed = PCI_SPEED_133MHz_PCIX_266;
+		else if (slot_avail2 & SLOT_100MHZ_PCIX_266)
+			bus_speed = PCI_SPEED_100MHz_PCIX_266;
+		else if (slot_avail2 & SLOT_66MHZ_PCIX_266)
+			bus_speed = PCI_SPEED_66MHz_PCIX_266;
+	}
+
+	if (bus_speed == PCI_SPEED_UNKNOWN) {
+		if (slot_avail1 & SLOT_133MHZ_PCIX)
+			bus_speed = PCI_SPEED_133MHz_PCIX;
+		else if (slot_avail1 & SLOT_100MHZ_PCIX)
+			bus_speed = PCI_SPEED_100MHz_PCIX;
+		else if (slot_avail1 & SLOT_66MHZ_PCIX)
+			bus_speed = PCI_SPEED_66MHz_PCIX;
+		else if (slot_avail2 & SLOT_66MHZ)
+			bus_speed = PCI_SPEED_66MHz;
+		else if (slot_avail1 & SLOT_33MHZ)
+			bus_speed = PCI_SPEED_33MHz;
+		else
+			retval = -ENODEV;
+	}
+
+	bus->max_bus_speed = bus_speed;
+	ctrl_dbg(ctrl, "Max bus speed = %d\n", bus_speed);
+
+	return retval;
+}
+
+static struct hpc_ops shpchp_hpc_ops = {
+	.power_on_slot			= hpc_power_on_slot,
+	.slot_enable			= hpc_slot_enable,
+	.slot_disable			= hpc_slot_disable,
+	.set_bus_speed_mode		= hpc_set_bus_speed_mode,
+	.set_attention_status	= hpc_set_attention_status,
+	.get_power_status		= hpc_get_power_status,
+	.get_attention_status	= hpc_get_attention_status,
+	.get_latch_status		= hpc_get_latch_status,
+	.get_adapter_status		= hpc_get_adapter_status,
+
+	.get_adapter_speed		= hpc_get_adapter_speed,
+	.get_mode1_ECC_cap		= hpc_get_mode1_ECC_cap,
+	.get_prog_int			= hpc_get_prog_int,
+
+	.query_power_fault		= hpc_query_power_fault,
+	.green_led_on			= hpc_set_green_led_on,
+	.green_led_off			= hpc_set_green_led_off,
+	.green_led_blink		= hpc_set_green_led_blink,
+
+	.release_ctlr			= hpc_release_ctlr,
+};
+
+int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
+{
+	int rc = -1, num_slots = 0;
+	u8 hp_slot;
+	u32 shpc_base_offset;
+	u32 tempdword, slot_reg, slot_config;
+	u8 i;
+
+	ctrl->pci_dev = pdev;  /* pci_dev of the P2P bridge */
+	ctrl_dbg(ctrl, "Hotplug Controller:\n");
+
+	if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+	    pdev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) {
+		/* amd shpc driver doesn't use Base Offset; assume 0 */
+		ctrl->mmio_base = pci_resource_start(pdev, 0);
+		ctrl->mmio_size = pci_resource_len(pdev, 0);
+	} else {
+		ctrl->cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC);
+		if (!ctrl->cap_offset) {
+			ctrl_err(ctrl, "Cannot find PCI capability\n");
+			goto abort;
+		}
+		ctrl_dbg(ctrl, " cap_offset = %x\n", ctrl->cap_offset);
+
+		rc = shpc_indirect_read(ctrl, 0, &shpc_base_offset);
+		if (rc) {
+			ctrl_err(ctrl, "Cannot read base_offset\n");
+			goto abort;
+		}
+
+		rc = shpc_indirect_read(ctrl, 3, &tempdword);
+		if (rc) {
+			ctrl_err(ctrl, "Cannot read slot config\n");
+			goto abort;
+		}
+		num_slots = tempdword & SLOT_NUM;
+		ctrl_dbg(ctrl, " num_slots (indirect) %x\n", num_slots);
+
+		for (i = 0; i < 9 + num_slots; i++) {
+			rc = shpc_indirect_read(ctrl, i, &tempdword);
+			if (rc) {
+				ctrl_err(ctrl, "Cannot read creg (index = %d)\n",
+					 i);
+				goto abort;
+			}
+			ctrl_dbg(ctrl, " offset %d: value %x\n", i, tempdword);
+		}
+
+		ctrl->mmio_base =
+			pci_resource_start(pdev, 0) + shpc_base_offset;
+		ctrl->mmio_size = 0x24 + 0x4 * num_slots;
+	}
+
+	ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
+		  pdev->vendor, pdev->device, pdev->subsystem_vendor,
+		  pdev->subsystem_device);
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		ctrl_err(ctrl, "pci_enable_device failed\n");
+		goto abort;
+	}
+
+	if (!request_mem_region(ctrl->mmio_base, ctrl->mmio_size, MY_NAME)) {
+		ctrl_err(ctrl, "Cannot reserve MMIO region\n");
+		rc = -1;
+		goto abort;
+	}
+
+	ctrl->creg = ioremap(ctrl->mmio_base, ctrl->mmio_size);
+	if (!ctrl->creg) {
+		ctrl_err(ctrl, "Cannot remap MMIO region %lx @ %lx\n",
+			 ctrl->mmio_size, ctrl->mmio_base);
+		release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
+		rc = -1;
+		goto abort;
+	}
+	ctrl_dbg(ctrl, "ctrl->creg %p\n", ctrl->creg);
+
+	mutex_init(&ctrl->crit_sect);
+	mutex_init(&ctrl->cmd_lock);
+
+	/* Setup wait queue */
+	init_waitqueue_head(&ctrl->queue);
+
+	ctrl->hpc_ops = &shpchp_hpc_ops;
+
+	/* Return PCI Controller Info */
+	slot_config = shpc_readl(ctrl, SLOT_CONFIG);
+	ctrl->slot_device_offset = (slot_config & FIRST_DEV_NUM) >> 8;
+	ctrl->num_slots = slot_config & SLOT_NUM;
+	ctrl->first_slot = (slot_config & PSN) >> 16;
+	ctrl->slot_num_inc = ((slot_config & UPDOWN) >> 29) ? 1 : -1;
+
+	/* Mask Global Interrupt Mask & Command Complete Interrupt Mask */
+	tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
+	ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
+	tempdword |= (GLOBAL_INTR_MASK  | GLOBAL_SERR_MASK |
+		      COMMAND_INTR_MASK | ARBITER_SERR_MASK);
+	tempdword &= ~SERR_INTR_RSVDZ_MASK;
+	shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
+	tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
+	ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
+
+	/* Mask the MRL sensor SERR Mask of individual slot in
+	 * Slot SERR-INT Mask & clear all the existing event if any
+	 */
+	for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
+		slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
+		ctrl_dbg(ctrl, "Default Logical Slot Register %d value %x\n",
+			 hp_slot, slot_reg);
+		slot_reg |= (PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
+			     BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
+			     CON_PFAULT_INTR_MASK   | MRL_CHANGE_SERR_MASK |
+			     CON_PFAULT_SERR_MASK);
+		slot_reg &= ~SLOT_REG_RSVDZ_MASK;
+		shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
+	}
+
+	if (shpchp_poll_mode) {
+		/* Install interrupt polling timer. Start with 10 sec delay */
+		init_timer(&ctrl->poll_timer);
+		start_int_poll_timer(ctrl, 10);
+	} else {
+		/* Installs the interrupt handler */
+		rc = pci_enable_msi(pdev);
+		if (rc) {
+			ctrl_info(ctrl, "Can't get msi for the hotplug controller\n");
+			ctrl_info(ctrl, "Use INTx for the hotplug controller\n");
+		} else {
+			pci_set_master(pdev);
+		}
+
+		rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
+				 MY_NAME, (void *)ctrl);
+		ctrl_dbg(ctrl, "request_irq %d (returns %d)\n",
+			 ctrl->pci_dev->irq, rc);
+		if (rc) {
+			ctrl_err(ctrl, "Can't get irq %d for the hotplug controller\n",
+				 ctrl->pci_dev->irq);
+			goto abort_iounmap;
+		}
+	}
+	ctrl_dbg(ctrl, "HPC at %s irq=%x\n", pci_name(pdev), pdev->irq);
+
+	shpc_get_max_bus_speed(ctrl);
+	shpc_get_cur_bus_speed(ctrl);
+
+	/*
+	 * Unmask all event interrupts of all slots
+	 */
+	for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
+		slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
+		ctrl_dbg(ctrl, "Default Logical Slot Register %d value %x\n",
+			 hp_slot, slot_reg);
+		slot_reg &= ~(PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
+			      BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
+			      CON_PFAULT_INTR_MASK | SLOT_REG_RSVDZ_MASK);
+		shpc_writel(ctrl, SLOT_REG(hp_slot), slot_reg);
+	}
+	if (!shpchp_poll_mode) {
+		/* Unmask all general input interrupts and SERR */
+		tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
+		tempdword &= ~(GLOBAL_INTR_MASK | COMMAND_INTR_MASK |
+			       SERR_INTR_RSVDZ_MASK);
+		shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
+		tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
+		ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
+	}
+
+	return 0;
+
+	/* We end up here for the many possible ways to fail this API.  */
+abort_iounmap:
+	iounmap(ctrl->creg);
+abort:
+	return rc;
+}
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c
new file mode 100644
index 0000000..f8cd3a2
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_pci.c
@@ -0,0 +1,116 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel 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 <greg@kroah.com>, <kristen.c.accardi@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "shpchp.h"
+
+int shpchp_configure_device(struct slot *p_slot)
+{
+	struct pci_dev *dev;
+	struct controller *ctrl = p_slot->ctrl;
+	struct pci_dev *bridge = ctrl->pci_dev;
+	struct pci_bus *parent = bridge->subordinate;
+	int num, ret = 0;
+
+	pci_lock_rescan_remove();
+
+	dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
+	if (dev) {
+		ctrl_err(ctrl, "Device %s already exists at %04x:%02x:%02x, cannot hot-add\n",
+			 pci_name(dev), pci_domain_nr(parent),
+			 p_slot->bus, p_slot->device);
+		pci_dev_put(dev);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
+	if (num == 0) {
+		ctrl_err(ctrl, "No new device found\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	list_for_each_entry(dev, &parent->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != p_slot->device)
+			continue;
+		if (pci_is_bridge(dev))
+			pci_hp_add_bridge(dev);
+	}
+
+	pci_assign_unassigned_bridge_resources(bridge);
+	pcie_bus_configure_settings(parent);
+	pci_bus_add_devices(parent);
+
+ out:
+	pci_unlock_rescan_remove();
+	return ret;
+}
+
+int shpchp_unconfigure_device(struct slot *p_slot)
+{
+	int rc = 0;
+	u8 bctl = 0;
+	struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
+	struct pci_dev *dev, *temp;
+	struct controller *ctrl = p_slot->ctrl;
+
+	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n",
+		 __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device);
+
+	pci_lock_rescan_remove();
+
+	list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) {
+		if (PCI_SLOT(dev->devfn) != p_slot->device)
+			continue;
+
+		pci_dev_get(dev);
+		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+			pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl);
+			if (bctl & PCI_BRIDGE_CTL_VGA) {
+				ctrl_err(ctrl,
+					 "Cannot remove display device %s\n",
+					 pci_name(dev));
+				pci_dev_put(dev);
+				rc = -EINVAL;
+				break;
+			}
+		}
+		pci_stop_and_remove_bus_device(dev);
+		pci_dev_put(dev);
+	}
+
+	pci_unlock_rescan_remove();
+	return rc;
+}
+
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
new file mode 100644
index 0000000..52875b3
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -0,0 +1,96 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (c) 1995,2001 Compaq Computer Corporation
+ * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001 IBM Corp.
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "shpchp.h"
+
+
+/* A few routines that create sysfs entries for the hot plug controller */
+
+static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev;
+	char *out = buf;
+	int index, busnr;
+	struct resource *res;
+	struct pci_bus *bus;
+
+	pdev = container_of (dev, struct pci_dev, dev);
+	bus = pdev->subordinate;
+
+	out += sprintf(buf, "Free resources: memory\n");
+	pci_bus_for_each_resource(bus, res, index) {
+		if (res && (res->flags & IORESOURCE_MEM) &&
+				!(res->flags & IORESOURCE_PREFETCH)) {
+			out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+				       (unsigned long long)res->start,
+				       (unsigned long long)resource_size(res));
+		}
+	}
+	out += sprintf(out, "Free resources: prefetchable memory\n");
+	pci_bus_for_each_resource(bus, res, index) {
+		if (res && (res->flags & IORESOURCE_MEM) &&
+			       (res->flags & IORESOURCE_PREFETCH)) {
+			out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+				       (unsigned long long)res->start,
+				       (unsigned long long)resource_size(res));
+		}
+	}
+	out += sprintf(out, "Free resources: IO\n");
+	pci_bus_for_each_resource(bus, res, index) {
+		if (res && (res->flags & IORESOURCE_IO)) {
+			out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+				       (unsigned long long)res->start,
+				       (unsigned long long)resource_size(res));
+		}
+	}
+	out += sprintf(out, "Free resources: bus numbers\n");
+	for (busnr = bus->busn_res.start; busnr <= bus->busn_res.end; busnr++) {
+		if (!pci_find_bus(pci_domain_nr(bus), busnr))
+			break;
+	}
+	if (busnr < bus->busn_res.end)
+		out += sprintf(out, "start = %8.8x, length = %8.8x\n",
+				busnr, (int)(bus->busn_res.end - busnr));
+
+	return out - buf;
+}
+static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
+
+int shpchp_create_ctrl_files (struct controller *ctrl)
+{
+	return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+}
+
+void shpchp_remove_ctrl_files(struct controller *ctrl)
+{
+	device_remove_file(&ctrl->pci_dev->dev, &dev_attr_ctrl);
+}