Subject: [PATCH 5 of 6] PCI Hotplug ACPI driver added
From: Greg KH <[email-masked]>
To: [email-masked]
Cc: [email-masked]

Hi,

Here's a patch against 2.4.19-pre1 that adds support for ACPI PCI
Hotplug controllers.  This driver was written by Hiroshi Aono and
Takayoshi Kochi, with a bit of help from me.

This driver is still quite experimental and has been reported to work on
NEC i386 machines and partially on some IBM ia64 machines.

thanks,

greg k-h

diff -Nru a/drivers/hotplug/pcihp_acpi.c b/drivers/hotplug/pcihp_acpi.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/hotplug/pcihp_acpi.c	Tue Feb 26 13:19:34 2002
@@ -0,0 +1,397 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (c) 2001-2002 Greg Kroah-Hartman ([email-masked])
+ * Copyright (c) 2001-2002 IBM Corp.
+ * Copyright (c) 2002 Hiroshi Aono ([email-masked])
+ * Copyright (c) 2002 Takayoshi Kochi ([email-masked])
+ * Copyright (c) 2002 NEC Corporation
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email-masked]>,
+ *                  <[email-masked]>,
+ *		    <[email-masked]>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+#include "pcihp_acpi.h"
+
+static LIST_HEAD(slot_list);
+
+#if !defined(CONFIG_HOTPLUG_PCI_ACPI_MODULE)
+	#define MY_NAME	"pcihp_acpi"
+#else
+	#define MY_NAME	THIS_MODULE->name
+#endif
+
+/* local variables */
+static int debug = 1;			/* XXX */
+static int num_slots;
+
+#define DRIVER_VERSION	"0.2"
+#define DRIVER_AUTHOR	"Greg Kroah-Hartman <[email-masked]>"
+#define DRIVER_DESC	"ACPI Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
+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 acpi_hotplug_slot_ops = {
+	owner:			THIS_MODULE,
+	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,
+};
+
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int slot_paranoia_check (struct slot *slot, const char *function)
+{
+	if (!slot) {
+		dbg("%s - slot == NULL", function);
+		return -1;
+	}
+	if (slot->magic != SLOT_MAGIC) {
+		dbg("%s - bad magic number for slot", function);
+		return -1;
+	}
+	if (!slot->hotplug_slot) {
+		dbg("%s - slot->hotplug_slot == NULL!", function);
+		return -1;
+	}
+	return 0;
+}
+
+static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
+{ 
+	struct slot *slot;
+
+	if (!hotplug_slot) {
+		dbg("%s - hotplug_slot == NULL", function);
+		return NULL;
+	}
+
+	slot = (struct slot *)hotplug_slot->private;
+	if (slot_paranoia_check (slot, function))
+                return NULL;
+	return slot;
+}
+
+
+static int enable_slot (struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg ("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	/* enable the specified slot */
+	retval = pcihp_acpi_enable_slot (slot->acpi_slot);
+
+	return retval;
+}
+
+static int disable_slot (struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg ("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	/* disable the specified slot */
+	retval = pcihp_acpi_disable_slot (slot->acpi_slot);
+
+	return retval;
+}
+
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg ("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	/* TBD
+	 * ACPI doesn't have known method to manipulate
+	 * attention status LED
+	 */
+	switch (status) {
+		case 0:
+			/* FIXME turn light off */
+			slot->attention_status = 0;
+			break;
+
+		case 1:
+		default:
+			/* FIXME turn light on */
+			slot->attention_status = 1;
+			break;
+	}
+
+	return retval;
+}
+
+static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg ("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	err ("No hardware tests are defined for this driver");
+	retval = -ENODEV;
+
+	return retval;
+}
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	*value = pcihp_acpi_get_power_status (slot->acpi_slot);
+
+	return retval;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	*value = slot->attention_status;
+
+	return retval;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	*value = pcihp_acpi_get_latch_status (slot->acpi_slot);
+
+	return retval;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+	int retval = 0;
+	
+	if (slot == NULL)
+		return -ENODEV;
+	
+	dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+	*value = pcihp_acpi_get_adapter_status (slot->acpi_slot);
+
+	return retval;
+}
+
+static int init_acpi (void)
+{
+	int retval;
+
+	dbg("init_acpi");		/* XXX */
+	/* initialize internal data structure etc. */
+	retval = pcihp_acpi_glue_init();
+
+	/* read initial number of slots */
+	if (!retval) {
+		num_slots = pcihp_acpi_get_num_slots();
+		if (num_slots == 0)
+			retval = -ENODEV;
+	}
+
+	return retval;
+}
+
+static void exit_acpi (void)
+{
+	/* deallocate internal data structures etc. */
+	pcihp_acpi_glue_exit();
+}
+
+#define SLOT_NAME_SIZE	10
+static void make_slot_name (struct slot *slot)
+{
+	/* FIXME - get this from the ACPI representation of the slot */
+	snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "ACPI%d", slot->number);
+}
+
+static int init_slots (void)
+{
+	struct slot *slot;
+	int retval = 0;
+	int i;
+
+	for (i = 0; i < num_slots; ++i) {
+		slot = kmalloc (sizeof (struct slot), GFP_KERNEL);
+		if (!slot)
+			return -ENOMEM;
+		memset(slot, 0, sizeof(struct slot));
+
+		slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+		if (!slot->hotplug_slot) {
+			kfree (slot);
+			return -ENOMEM;
+		}
+		memset(slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
+
+		slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+		if (!slot->hotplug_slot->info) {
+			kfree (slot->hotplug_slot);
+			kfree (slot);
+			return -ENOMEM;
+		}
+		memset(slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
+
+		slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
+		if (!slot->hotplug_slot->name) {
+			kfree (slot->hotplug_slot->info);
+			kfree (slot->hotplug_slot);
+			kfree (slot);
+			return -ENOMEM;
+		}
+
+		slot->magic = SLOT_MAGIC;
+		slot->number = i;
+
+		slot->hotplug_slot->private = slot;
+		make_slot_name (slot);
+		slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
+		
+		slot->acpi_slot = get_slot_from_id (i);
+		slot->hotplug_slot->info->power_status = pcihp_acpi_get_power_status(slot->acpi_slot);
+		slot->hotplug_slot->info->attention_status = pcihp_acpi_get_attention_status(slot->acpi_slot);
+		slot->hotplug_slot->info->latch_status = pcihp_acpi_get_latch_status(slot->acpi_slot);
+		slot->hotplug_slot->info->adapter_status = pcihp_acpi_get_adapter_status(slot->acpi_slot);
+
+		dbg ("registering slot %d", i);
+		retval = pci_hp_register (slot->hotplug_slot);
+		if (retval) {
+			err ("pci_hp_register failed with error %d", retval);
+			kfree (slot->hotplug_slot->info);
+			kfree (slot->hotplug_slot->name);
+			kfree (slot->hotplug_slot);
+			kfree (slot);
+			return retval;
+		}
+
+		/* add slot to our internal list */
+		list_add (&slot->slot_list, &slot_list);
+	}
+
+	return retval;
+}
+		
+static void cleanup_slots (void)
+{
+	struct list_head *tmp;
+	struct slot *slot;
+
+	list_for_each (tmp, &slot_list) {
+		slot = list_entry (tmp, struct slot, slot_list);
+		list_del (&slot->slot_list);
+		pci_hp_deregister (slot->hotplug_slot);
+		kfree (slot->hotplug_slot->info);
+		kfree (slot->hotplug_slot->name);
+		kfree (slot->hotplug_slot);
+		kfree (slot);
+	}
+
+	return;
+}
+
+static int __init pcihp_acpi_init(void)
+{
+	int retval;
+
+	/* read all the ACPI info from the system */
+	retval = init_acpi();
+	if (retval)
+		return retval;
+
+	retval = init_slots();
+	if (retval)
+		return retval;
+
+	info (DRIVER_DESC " version: " DRIVER_VERSION);
+	return 0;
+}
+
+static void __exit pcihp_acpi_exit(void)
+{
+	cleanup_slots();
+	exit_acpi();
+}
+
+module_init(pcihp_acpi_init);
+module_exit(pcihp_acpi_exit);
diff -Nru a/drivers/hotplug/pcihp_acpi.h b/drivers/hotplug/pcihp_acpi.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/hotplug/pcihp_acpi.h	Tue Feb 26 13:19:34 2002
@@ -0,0 +1,231 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (c) 1995,2001 Compaq Computer Corporation
+ * Copyright (c) 2001 Greg Kroah-Hartman ([email-masked])
+ * Copyright (c) 2001 IBM Corp.
+ * Copyright (c) 2002 Hiroshi Aono ([email-masked])
+ * Copyright (c) 2002 Takayoshi Kochi ([email-masked])
+ * Copyright (c) 2002 NEC Corporation
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email-masked]>,
+ *		    <[email-masked]>,
+ *		    <[email-masked]>
+ *
+ */
+
+#ifndef _PCIHP_ACPI_H
+#define _PCIHP_ACPI_H
+
+#include "include/acpi.h"
+
+#if ACPI_CA_VERSION < 0x20020201
+/* until we get a new version of the ACPI driver for both ia32 and ia64 ... */
+#define acpi_util_eval_error(h,p,s)
+
+static acpi_status
+acpi_evaluate_integer (
+	acpi_handle		handle,
+	acpi_string		pathname,
+	acpi_object_list	*arguments,
+	unsigned long		*data)
+{
+	acpi_status             status = AE_OK;
+	acpi_object             element;
+	acpi_buffer		buffer = {sizeof(acpi_object), &element};
+
+	if (!data)
+		return AE_BAD_PARAMETER;
+
+	status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
+	if (ACPI_FAILURE(status)) {
+		acpi_util_eval_error(handle, pathname, status);
+		return status;
+	}
+
+	if (element.type != ACPI_TYPE_INTEGER) {
+		acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
+		return AE_BAD_DATA;
+	}
+
+	*data = element.integer.value;
+
+	return AE_OK;
+}
+#else  /* ACPI_CA_VERSION < 0x20020201 */
+#include "acpi_bus.h"
+#endif /* ACPI_CA_VERSION < 0x20020201 */
+
+/* compatibility stuff */
+#ifndef ACPI_MEMORY_RANGE
+#define ACPI_MEMORY_RANGE MEMORY_RANGE
+#endif
+
+#ifndef ACPI_IO_RANGE
+#define ACPI_IO_RANGE IO_RANGE
+#endif
+
+#ifndef ACPI_BUS_NUMBER_RANGE
+#define ACPI_BUS_NUMBER_RANGE BUS_NUMBER_RANGE
+#endif
+
+#ifndef ACPI_PREFETCHABLE_MEMORY
+#define ACPI_PREFETCHABLE_MEMORY PREFETCHABLE_MEMORY
+#endif
+
+#ifndef ACPI_PRODUCER
+#define ACPI_PRODUCER PRODUCER
+#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)
+
+/*
+ * types and constants
+ */
+
+#define SLOT_MAGIC	0x67267322
+
+struct pcihp_acpi_bridge;
+struct pcihp_acpi_slot;
+
+/* slot information for each *physical* slot */
+
+struct slot {
+	u32 magic;
+	u8 number;
+	struct hotplug_slot	*hotplug_slot;
+	struct list_head	slot_list;
+
+	int			attention_status;
+
+	struct pci_resource	*mem_head;
+	struct pci_resource	*p_mem_head;
+	struct pci_resource	*io_head;
+	struct pci_resource	*bus_head;
+
+	/* if there are multiple corresponding slot objects,
+	   this point to one of them */
+	struct pcihp_acpi_slot	*acpi_slot;
+
+	struct pci_dev		*pci_dev;
+};
+
+#define RESOURCE_TYPE_IO	(1)
+#define RESOURCE_TYPE_MEM	(2)
+#define RESOURCE_TYPE_PREFETCH	(3)
+#define RESOURCE_TYPE_BUS	(4)
+
+/* TBD 64bit resource support */
+struct pci_resource {
+	struct pci_resource * next;
+	u32 base;
+	u32 length;
+};
+
+/* bridge information for each bridge device in ACPI namespace */
+
+struct pcihp_acpi_bridge {
+	struct list_head list;
+	acpi_handle handle;
+	struct pcihp_acpi_slot *slots;
+	int nr_slots;
+	u8 seg;
+	u8 bus;
+	u8 sub;
+	u32 status;
+	u32 flags;
+
+	/* resources on this bus (free resources) */
+	struct pci_resource *free_io;
+	struct pci_resource *free_mem;
+	struct pci_resource *free_prefetch;
+	struct pci_resource *free_bus;
+
+	/* used resources (embedded or owned resources) */
+	struct pci_resource *used_io;
+	struct pci_resource *used_mem;
+	struct pci_resource *used_prefetch;
+	struct pci_resource *used_bus;
+};
+
+/*
+ * slot information for each slot object in ACPI namespace
+ * usually 8 objects per slot (for each PCI function)
+ */
+
+struct pcihp_acpi_slot {
+	struct pcihp_acpi_slot	*next;
+	struct pcihp_acpi_bridge *bridge; /* this slot located on */
+	struct list_head sibling;	/* one slot may have different
+					   objects (i.e. for each function) */
+	acpi_handle	handle;
+	u32		id;		/* slot id (this driver specific) */
+	u8		device;		/* pci device# */
+	u8		function;	/* pci function# */
+	u8		pin;		/* pci interrupt pin */
+	u32		sun;		/* _SUN */
+	u32		flags;		/* see below */
+	u32		status;		/* _STA */
+};
+
+/* PCI bus bridge HID */
+#define ACPI_PCI_ROOT_HID		"PNP0A03"
+
+/* ACPI _STA method value (ignore bit 4; battery present) */
+#define ACPI_STA_PRESENT		(0x00000001)
+#define ACPI_STA_ENABLED		(0x00000002)
+#define ACPI_STA_SHOW_IN_UI		(0x00000004)
+#define ACPI_STA_FUNCTIONAL		(0x00000008)
+#define ACPI_STA_ALL			(0x0000000f)
+
+/* bridge flags */
+#define BRIDGE_HAS_STA	(0x00000001)
+
+/* slot flags */
+
+#define SLOT_HAS_EJ0	(0x00000001)
+#define SLOT_HAS_PS0	(0x00000002)
+#define SLOT_HAS_PS3	(0x00000004)
+
+/* function prototypes */
+
+/* pcihp_acpi_glue.c */
+extern int pcihp_acpi_glue_init (void);
+extern void pcihp_acpi_glue_exit (void);
+extern int pcihp_acpi_get_num_slots (void);
+extern struct pcihp_acpi_slot *get_slot_from_id (int id);
+
+extern int pcihp_acpi_enable_slot (struct pcihp_acpi_slot *slot);
+extern int pcihp_acpi_disable_slot (struct pcihp_acpi_slot *slot);
+extern u8 pcihp_acpi_get_power_status (struct pcihp_acpi_slot *slot);
+extern u8 pcihp_acpi_get_attention_status (struct pcihp_acpi_slot *slot);
+extern u8 pcihp_acpi_get_latch_status (struct pcihp_acpi_slot *slot);
+extern u8 pcihp_acpi_get_adapter_status (struct pcihp_acpi_slot *slot);
+
+#endif /* _PCIHP_ACPI_H */
diff -Nru a/drivers/hotplug/pcihp_acpi_ctrl.c b/drivers/hotplug/pcihp_acpi_ctrl.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/hotplug/pcihp_acpi_ctrl.c	Tue Feb 26 13:19:33 2002
@@ -0,0 +1,642 @@
+/*
+ * ACPI PCI HotPlug Utility functions
+ *
+ * Copyright (c) 1995,2001 Compaq Computer Corporation
+ * Copyright (c) 2001 Greg Kroah-Hartman ([email-masked])
+ * Copyright (c) 2001 IBM Corp.
+ * Copyright (c) 2002 Hiroshi Aono ([email-masked])
+ * Copyright (c) 2002 Takayoshi Kochi ([email-masked])
+ * Copyright (c) 2002 NEC Corporation
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email-masked]>,<[email-masked]>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+
+#include <linux/list.h>
+
+#include <asm/uaccess.h>
+
+#define _LINUX                          /* for acpi subcomponent */
+
+#include "include/acpi.h"
+
+#include "pci_hotplug.h"
+#include "pchihp_acpi.h"
+
+#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
+	#define MY_NAME "pci_hotplug"
+#else
+	#define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_WARNING "%s: "__FUNCTION__": " 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)
+
+
+/* local variables */
+static int debug = 0;
+
+/*
+ * sort_by_size
+ *
+ * Sorts nodes on the list by their length.
+ * Smallest first.
+ *
+ */
+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
+ *
+ * Sorts nodes on the list by their length.
+ * Largest first.
+ *
+ */
+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);
+}
+
+/*
+ * get_io_resource
+ *
+ * this function sorts the resource list by size and then
+ * 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.
+ *
+ * size must be a power of two.
+ */
+struct pci_resource *hotplug_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 ( hotplug_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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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
+		if (*head == node) {
+			*head = node->next;
+		} else {
+			prevnode = *head;
+			while (prevnode->next != node)
+				prevnode = prevnode->next;
+
+			prevnode->next = node->next;
+		}
+		node->next = NULL;
+		// Stop looping
+		break;
+	}
+
+	return(node);
+}
+
+
+/*
+ * get_max_resource
+ *
+ * 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.
+ */
+struct pci_resource *hotplug_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 (!(*head))
+		return(NULL);
+
+	if (hotplug_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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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;
+
+			// Put it next in the list
+			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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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;
+
+			// Put it in the list
+			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 = (struct pci_resource*) *head;
+		if (temp == max) {
+			*head = max->next;
+		} else {
+			while (temp && temp->next != max) {
+				temp = temp->next;
+			}
+
+			temp->next = max->next;
+		}
+
+		max->next = NULL;
+		return(max);
+	}
+
+	// If we get here, we couldn't find one
+	return(NULL);
+}
+
+
+/*
+ * get_resource
+ *
+ * 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.
+ */
+struct pci_resource *hotplug_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 (!(*head))
+		return(NULL);
+
+	if ( hotplug_resource_sort_and_combine(head) )
+		return(NULL);
+
+	if ( sort_by_size(head) )
+		return(NULL);
+
+	for (node = *head; node; node = node->next) {
+		dbg(__FUNCTION__": req_size =%x node=%p, base=%x, length=%x\n",
+		    size, node, node->base, node->length);
+		if (node->length < size)
+			continue;
+
+		if (node->base & (size - 1)) {
+			dbg(__FUNCTION__": not aligned\n");
+			// 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 = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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) {
+			dbg(__FUNCTION__": too big\n");
+			// this one is longer than we need
+			// so we'll make a new entry and split it up
+			split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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(__FUNCTION__": got one!!!\n");
+		// 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;
+		// Stop looping
+		break;
+	}
+	return(node);
+}
+
+/*
+ * get_resource_with_base
+ *
+ * this function 
+ * returns the first node of "size" length located at specified base address.
+ * If it finds a node larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+struct pci_resource *hotplug_get_resource_with_base (struct pci_resource **head, u32 base, u32 size)
+{
+	struct pci_resource *prevnode;
+	struct pci_resource *node;
+	struct pci_resource *split_node;
+	u32 temp_dword;
+
+	if (!(*head))
+		return(NULL);
+
+	if ( hotplug_resource_sort_and_combine(head) )
+		return(NULL);
+
+	for (node = *head; node; node = node->next) {
+		dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
+		    base, size, node, node->base, node->length);
+		if (node->base > base)
+			continue;
+
+		if ((node->base + node->length) < (base + size))
+			continue;
+
+		if (node->base < base) {
+			dbg(": split 1\n");
+			// this one isn't base aligned properly
+			// so we'll make a new entry and split it up
+			temp_dword = base;
+
+			// Short circuit if adjusted size is too small
+			if ((node->length - (temp_dword - node->base)) < size)
+				continue;
+
+			split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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;
+		}
+
+		dbg(": 2st req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
+		    base, size, node, node->base, node->length);
+
+		// Don't need to check if too small since we already did
+		if (node->length >= size) {
+			dbg(": split 2\n");
+			// this one is longer than we need
+			// so we'll make a new entry and split it up
+			split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), 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(": got one!!!\n");
+		// 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;
+		// Stop looping
+		break;
+	}
+	return(node);
+}
+
+/*
+ * hotplug_resource_sort_and_combine
+ *
+ * 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 hotplug_resource_sort_and_combine(struct pci_resource **head)
+{
+	struct pci_resource *node1;
+	struct pci_resource *node2;
+	int out_of_order = 1;
+
+	dbg(__FUNCTION__": head = %p, *head = %p\n", 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);
+}
+
+/*
+EXPORT_SYMBOL(hotplug_get_io_resource);
+EXPORT_SYMBOL(hotplug_get_max_resource);
+EXPORT_SYMBOL(hotplug_get_resource);
+EXPORT_SYMBOL(hotplug_resource_sort_and_combine);
+*/
diff -Nru a/drivers/hotplug/pcihp_acpi_glue.c b/drivers/hotplug/pcihp_acpi_glue.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/hotplug/pcihp_acpi_glue.c	Tue Feb 26 13:19:34 2002
@@ -0,0 +1,757 @@
+/*
+ * ACPI PCI HotPlug glue functions to ACPI CA subsystem
+ *
+ * Copyright (c) 2002 Takayoshi Kochi ([email-masked])
+ * Copyright (c) 2002 Hiroshi Aono ([email-masked])
+ * Copyright (c) 2002 NEC Corporation
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email-masked]>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pcihp_acpi.h"
+
+/*
+ * TODO:
+ * resource management
+ * irq related interface? (_PRT)
+ * consider locking
+ */
+
+static LIST_HEAD(bridge_list);
+
+static int debug = 1;			/* XXX set 0 after debug */
+#define MY_NAME "pcihp_acpi_glue"
+
+static void handle_hotplug_event (acpi_handle, u32, void *);
+
+/*
+ * initialization & terminatation routines
+ */
+
+/*
+ * Ejectable slot satisfies at least these conditions:
+ *  1. has _ADR method
+ *  2. has _STA method
+ *  3. has _EJ0 method
+ *
+ * optionally
+ *  1. has _PS0 method
+ *  2. has _PS3 method
+ *  3. TBD...
+ */
+
+/* callback routine to check the existence of ejectable slots */
+static acpi_status
+is_ejectable_slot (acpi_handle handle, u32 lvl,	void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	int *count = (int *)context;
+
+	status = acpi_get_handle(handle, "_ADR", &tmp);
+
+	if (ACPI_FAILURE(status)) {
+		return AE_OK;
+	}
+
+	status = acpi_get_handle(handle, "_STA", &tmp);
+
+	if (ACPI_FAILURE(status)) {
+		return AE_OK;
+	}
+
+	status = acpi_get_handle(handle, "_EJ0", &tmp);
+
+	if (ACPI_FAILURE(status)) {
+		return AE_OK;
+	}
+
+	(*count)++;
+
+	/* only one ejectable slot is enough */
+	return AE_CTRL_TERMINATE;
+}
+
+
+/* callback routine to register each ACPI PCI slot object */
+static acpi_status
+register_slot (acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	struct pcihp_acpi_bridge *bridge = (struct pcihp_acpi_bridge *)context;
+	struct pcihp_acpi_slot *slot, *newslot;
+	acpi_handle tmp;
+	acpi_status status = AE_OK;
+	static int num_slots = 0;	/* XXX */
+	unsigned long adr, sun, sta;
+
+	status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+
+	if (ACPI_FAILURE(status)) {
+		return AE_OK;
+	}
+
+	status = acpi_get_handle(handle, "_EJ0", &tmp);
+
+	if (ACPI_FAILURE(status)) {
+		dbg("This slot doesn't have _EJ0");
+		//return AE_OK;
+	}
+
+	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+
+	if (ACPI_FAILURE(status)) {
+		dbg("This slot doesn't have _STA");
+		//return AE_OK;
+	}
+
+	newslot = kmalloc(sizeof(struct pcihp_acpi_slot), GFP_KERNEL);
+	if (!newslot) {
+		return AE_NO_MEMORY;
+	}
+
+	memset(newslot, 0, sizeof(struct pcihp_acpi_slot));
+
+	INIT_LIST_HEAD(&newslot->sibling);
+	newslot->bridge = bridge;
+	newslot->handle = handle;
+	newslot->device = (adr >> 16) & 0xffff;
+	newslot->function = adr & 0xffff;
+	newslot->status = sta;
+	newslot->sun = -1;
+	newslot->flags = SLOT_HAS_EJ0;
+	newslot->id = num_slots++;
+	bridge->nr_slots++;
+
+	dbg("new slot id=%d device=0x%d function=0x%x", newslot->id, newslot->device, newslot->function);
+
+	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+	if (ACPI_SUCCESS(status)) {
+		newslot->sun = sun;
+	}
+
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) {
+		newslot->flags |= SLOT_HAS_PS0;
+	}
+
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) {
+		newslot->flags |= SLOT_HAS_PS3;
+	}
+
+	/* search for objects that share the same slot */
+	for (slot = bridge->slots; slot; slot = slot->next)
+		if (slot->device == newslot->device) {
+			dbg("found a sibling slot!");
+			list_add(&slot->sibling, &newslot->sibling);
+			newslot->id = slot->id;
+			num_slots --;
+			bridge->nr_slots --;
+			break;
+		}
+
+	/* link myself to bridge's slot list */
+	newslot->next = bridge->slots;
+	bridge->slots = newslot;
+
+	return AE_OK;
+}
+
+/* see if it's worth managing this brige */
+static int
+detect_ejectable_slots (acpi_handle *root)
+{
+	acpi_status status;
+	int count;
+
+	count = 0;
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root, ACPI_UINT32_MAX,
+				     is_ejectable_slot, (void *)&count, NULL);
+
+	dbg("%s: count=%d", __FUNCTION__, count);
+	return count;
+}
+
+
+/*
+ * push one resource to resource list
+ *
+ * TBD: use hotplug_resource_sort_and_combine
+ * TBD: 64bit resource handling (is it really used?)
+ */
+static void
+push_resource (u32 base, u32 length, struct pci_resource **resource)
+{
+	struct pci_resource *resp, *newres;
+	int coalesced = 0;
+
+	if (length == 0) {
+		dbg("zero sized resource. ignored.");
+		return;
+	}
+
+	for (resp = *resource; resp; resp = resp->next) {
+
+		/* coalesce contiguous region */
+
+		if (resp->base + resp->length == base) {
+			resp->length += length;
+			coalesced = 1;
+			break;
+		}
+	}
+
+	if (!coalesced) {
+		newres = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+		if (!newres) {
+			/* TBD panic? */
+			return;
+		}
+		newres->base = base;
+		newres->length = length;
+		newres->next = (*resource);
+		*resource = newres;
+	}
+}
+
+
+/* decode ACPI _CRS data and convert into our internal resource list */
+static void
+decode_acpi_resource (acpi_resource *resource, struct pcihp_acpi_bridge *bridge)
+{
+	acpi_resource_address16 *address16_data;
+	acpi_resource_address32 *address32_data;
+	//acpi_resource_address64 *address64_data;
+
+	u32 resource_type, producer_consumer, min_address_range, max_address_range, address_length;
+	u16 cache_attribute = 0;
+
+	int done = 0, found;
+
+	/* shut up gcc */
+	resource_type = producer_consumer = min_address_range = max_address_range = address_length = 0;
+
+	while (!done) {
+		found = 0;
+
+		switch (resource->id) {
+		case ACPI_RSTYPE_ADDRESS16:
+			address16_data = (acpi_resource_address16 *)&resource->data;
+			resource_type = address16_data->resource_type;
+			producer_consumer = address16_data->producer_consumer;
+			min_address_range = address16_data->min_address_range;
+			max_address_range = address16_data->max_address_range;
+			address_length = address16_data->address_length;
+			if (resource_type == ACPI_MEMORY_RANGE)
+				cache_attribute = address16_data->attribute.memory.cache_attribute;
+			found = 1;
+			break;
+
+		case ACPI_RSTYPE_ADDRESS32:
+			address32_data = (acpi_resource_address32 *)&resource->data;
+			resource_type = address32_data->resource_type;
+			producer_consumer = address32_data->producer_consumer;
+			min_address_range = address32_data->min_address_range;
+			max_address_range = address32_data->max_address_range;
+			address_length = address32_data->address_length;
+			if (resource_type == ACPI_MEMORY_RANGE)
+				cache_attribute = address32_data->attribute.memory.cache_attribute;
+			found = 1;
+			break;
+/*
+		case ACPI_RSTYPE_ADDRESS64:
+			address64_data = (acpi_resource_address64 *)&resource->data;
+			resource_type = address64_data->resource_type;
+			break;
+*/
+		case ACPI_RSTYPE_END_TAG:
+			done = 1;
+			break;
+
+		default:
+			/* ignore */
+			break;
+		}
+
+		resource = (acpi_resource *)((char*)resource + resource->length);
+		if (found && producer_consumer == ACPI_PRODUCER) {
+			switch (resource_type) {
+			case ACPI_MEMORY_RANGE:
+				if (cache_attribute == ACPI_PREFETCHABLE_MEMORY) {
+					dbg("resource type: prefetchable memory 0x%x - 0x%x", min_address_range, max_address_range);
+					push_resource(min_address_range,
+						      address_length,
+						      &bridge->free_prefetch);
+				} else {
+					dbg("resource type: memory 0x%x - 0x%x", min_address_range, max_address_range);
+					push_resource(min_address_range,
+						      address_length,
+						      &bridge->free_mem);
+				}
+				break;
+			case ACPI_IO_RANGE:
+				dbg("resource type: io 0x%x - 0x%x", min_address_range, max_address_range);
+				push_resource(min_address_range,
+					      address_length,
+					      &bridge->free_io);
+				break;
+			case ACPI_BUS_NUMBER_RANGE:
+				dbg("resource type: bus number %d - %d", min_address_range, max_address_range);
+				push_resource(min_address_range,
+					      address_length,
+					      &bridge->free_bus);
+				break;
+			default:
+				/* invalid type */
+				break;
+			}
+		}
+	}
+}
+
+
+/* allocate and initialize bridge data structure */
+static int add_bridge (acpi_handle *handle)
+{
+	struct pcihp_acpi_bridge *bridge;
+ 	acpi_status status;
+	acpi_buffer buffer;
+	unsigned long tmp;
+	acpi_handle dummy_handle;
+	int sta = -1;
+
+	status = acpi_get_handle(handle, "_STA", &dummy_handle);
+	if (ACPI_SUCCESS(status)) {
+		status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+		if (ACPI_FAILURE(status)) {
+			dbg("%s: _STA evaluation failure", __FUNCTION__);
+			return 0;
+		}
+		sta = tmp;
+	}
+
+	if (sta >= 0 && !(sta & ACPI_STA_PRESENT))
+		/* don't register this object */
+		return 0;
+
+	dbg("%s: _STA: 0x%x", __FUNCTION__, (unsigned int)sta);
+
+	/* check if this bridge has ejectable slots */
+
+	detect_ejectable_slots(handle);
+	//if (detect_ejectable_slots(handle) == 0)
+	//return 0;
+
+	/* allocate per-bridge data structure and fill in */
+
+	bridge = kmalloc(sizeof(struct pcihp_acpi_bridge), GFP_KERNEL);
+	if (bridge == NULL)
+		return -ENOMEM;
+
+	memset(bridge, 0, sizeof(struct pcihp_acpi_bridge));
+
+	if (sta >= 0)
+		bridge->flags |= BRIDGE_HAS_STA;
+
+	/* get PCI segment number */
+	status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+
+	if (ACPI_SUCCESS(status)) {
+		bridge->seg = tmp;
+	} else {
+		bridge->seg = 0;
+	}
+
+	/* get PCI bus number */
+	status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+
+	if (ACPI_SUCCESS(status)) {
+		bridge->bus = tmp;
+	} else {
+		bridge->bus = 0;
+	}
+
+	/* to be overridden when we decode _CRS	*/
+	bridge->sub = bridge->bus;
+
+	/* register all slot objects under this bridge */
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
+				     register_slot, bridge, NULL);
+
+	/* decode resources */
+	buffer.length = 0;
+	buffer.pointer = NULL;
+
+	
+	/* TBD use new ACPI_ALLOCATE_BUFFER */
+	status = acpi_get_current_resources(handle, &buffer);
+	if (status != AE_BUFFER_OVERFLOW) {
+		return -1;
+	}
+
+	buffer.pointer = kmalloc(buffer.length, GFP_KERNEL);
+	if (!buffer.pointer) {
+		return -1;
+	}
+
+	status = acpi_get_current_resources(handle, &buffer);
+	if (ACPI_FAILURE(status)) {
+		return -1;
+	}
+
+	decode_acpi_resource(buffer.pointer, bridge);
+
+	/* TBD decode _HPP (hot plug parameters) */
+	// decode_hpp(bridge);
+
+	kfree(buffer.pointer);
+
+	/* check already allocated resources */
+	/* TBD */
+
+	/* install notify handler */
+	dbg("installing notify handler");
+	status = acpi_install_notify_handler(handle,
+					     ACPI_SYSTEM_NOTIFY,
+					     handle_hotplug_event, NULL);
+
+	if (ACPI_FAILURE(status)) {
+		err("failed to register interrupt notify handler");
+	}
+
+	list_add(&bridge->list, &bridge_list);
+
+	return 0;
+}
+
+
+/* callback routine to enumerate all the bridges in ACPI namespace */
+static acpi_status
+check_pci_bridge (acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_device_info info;
+	char objname[5];
+	acpi_buffer buffer = { sizeof(objname), objname };
+
+	status = acpi_get_object_info(handle, &info);
+	if (ACPI_FAILURE(status)) {
+		dbg("%s: failed to get bridge information", __FUNCTION__);
+		return AE_OK;		/* continue */
+	}
+
+	info.hardware_id[sizeof(info.hardware_id)-1] = '\0';
+
+	if (strcmp(info.hardware_id, ACPI_PCI_ROOT_HID) == 0) {
+
+		acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+		dbg("%s: found PCI root bridge[%s]", __FUNCTION__, objname);
+
+		add_bridge(handle);
+	}
+	return AE_OK;
+}
+
+
+/* interrupt handler */
+static void handle_hotplug_event (acpi_handle handle, u32 type, void *data)
+{
+	char objname[5];
+	acpi_buffer buffer = { sizeof(objname), objname };
+
+	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+
+	switch (type) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		/* hot insertion/surprise removal */
+		/* TBD */
+		dbg("%s: Bus check notify on %s", __FUNCTION__, objname);
+		break;
+
+	case ACPI_NOTIFY_DEVICE_CHECK:
+		/* TBD */
+		dbg("%s: Device check notify on %s", __FUNCTION__, objname);
+		break;
+
+	case ACPI_NOTIFY_EJECT_REQUEST:
+		/* eject button pushed */
+		/* TBD */
+		dbg("%s: Device eject notify on %s", __FUNCTION__, objname);
+		break;
+
+	default:
+		warn("notify_handler: unknown event type 0x%x", type);
+		break;
+	}
+}
+
+
+/*
+ * external interfaces
+ */
+
+int pcihp_acpi_glue_init (void)
+{
+	acpi_status status;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+				     ACPI_UINT32_MAX, check_pci_bridge,
+				     NULL, NULL);
+
+	if (ACPI_FAILURE(status)) {
+		dbg("%s: acpi_walk_namespace() failed", __FUNCTION__);
+	}
+
+	return 0;
+}
+
+static void free_all_resources (struct pcihp_acpi_bridge *bridge)
+{
+	struct pci_resource *res, *next;;
+
+	for (res = bridge->free_io; res; ) {
+		next = res->next;
+		kfree(res);
+		res = next;
+	}
+
+	for (res = bridge->free_mem; res; ) {
+		next = res->next;
+		kfree(res);
+		res = next;
+	}
+
+	for (res = bridge->free_prefetch; res; ) {
+		next = res->next;
+		kfree(res);
+		res = next;
+	}
+
+	for (res = bridge->free_bus; res; ) {
+		next = res->next;
+		kfree(res);
+		res = next;
+	}
+}
+
+
+void pcihp_acpi_glue_exit (void)
+{
+	struct list_head *node;
+	struct pcihp_acpi_bridge *bridge;
+	struct pcihp_acpi_slot *slot, *next;
+
+	list_for_each(node, &bridge_list) {
+		bridge = (struct pcihp_acpi_bridge *)node;
+		slot = bridge->slots;
+		while (slot) {
+			next = slot->next;
+			kfree(slot);
+			slot = next;
+		}
+		free_all_resources(bridge);
+		kfree(bridge);
+	}
+}
+
+
+int pcihp_acpi_get_num_slots (void)
+{
+	struct list_head *node;
+	struct pcihp_acpi_bridge *bridge;
+	int num_slots;
+
+	num_slots = 0;
+
+	list_for_each(node, &bridge_list) {
+		bridge = (struct pcihp_acpi_bridge *)node;
+		dbg("Bus:%d num_slots:%d", bridge->bus, bridge->nr_slots);
+		num_slots += bridge->nr_slots;
+	}
+
+	dbg("num_slots = %d", num_slots);
+	return num_slots;
+}
+
+
+/*  TBD: improve performance */
+struct pcihp_acpi_slot *get_slot_from_id (int id)
+{
+	struct list_head *node;
+	struct pcihp_acpi_bridge *bridge;
+	struct pcihp_acpi_slot *slot;
+
+	list_for_each(node, &bridge_list) {
+		bridge = (struct pcihp_acpi_bridge *)node;
+		for (slot = bridge->slots; slot; slot = slot->next)
+			if (slot->id == id)
+				return slot;
+	}
+
+	/* should never happen! */
+	dbg("%s: no object for id %d",__FUNCTION__, id);
+	return 0;
+}
+
+
+/* power on slot */
+int pcihp_acpi_enable_slot (struct pcihp_acpi_slot *slot)
+{
+	acpi_status status;
+
+	if (slot->flags & SLOT_HAS_PS0) {
+		dbg("%s: powering on bus%d/dev%d.", __FUNCTION__,
+		    slot->bridge->bus, slot->device);
+		status = acpi_evaluate_object(slot->handle, "_PS0", NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			warn("%s: powering on bus%d/dev%d failed",
+			     __FUNCTION__, slot->bridge->bus, slot->device);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* power off slot */
+int pcihp_acpi_disable_slot (struct pcihp_acpi_slot *slot)
+{
+	acpi_status status;
+
+	if (slot->flags & SLOT_HAS_PS3) {
+		dbg("%s: powering off bus%d/dev%d.", __FUNCTION__,
+		    slot->bridge->bus, slot->device);
+		status = acpi_evaluate_object(slot->handle, "_PS3", NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			warn("%s: _PS3 on bus%d/dev%d failed",
+			     __FUNCTION__, slot->bridge->bus, slot->device);
+			return -1;
+		}
+	}
+
+	if (slot->flags & SLOT_HAS_EJ0) {
+		dbg("%s: eject bus%d/dev%d.", __FUNCTION__,
+		    slot->bridge->bus, slot->device);
+		status = acpi_evaluate_object(slot->handle, "_EJ0", NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			warn("%s: _EJ0 bus%d/dev%d failed",
+			     __FUNCTION__, slot->bridge->bus, slot->device);
+			return -1;
+		}
+	}
+
+	/* TBD
+	 * evaluate _STA to check if state is successfully changed
+	 * and update status
+	 */
+
+	return 0;
+}
+
+
+static unsigned int get_slot_status(struct pcihp_acpi_slot *slot)
+{
+	acpi_status status;
+	unsigned long sta;
+
+	status = acpi_evaluate_integer(slot->handle, "_STA", NULL, &sta);
+
+	if (ACPI_FAILURE(status)) {
+		err("%s: _STA evaluation failed", __FUNCTION__);
+		return 0;
+	}
+
+	return (int)sta;
+}
+
+
+/*
+ * slot enabled:  1
+ * slot disabled: 0
+ */
+u8 pcihp_acpi_get_power_status (struct pcihp_acpi_slot *slot)
+{
+	unsigned int sta;
+
+	/* TBD
+	 * . guarantee check _STA on function# 0
+	 * . check configuration space before _STA?
+	 */
+
+	sta = get_slot_status(slot);
+
+	return (sta & ACPI_STA_ENABLED) ? 1 : 0;
+}
+
+
+/* XXX this function is not used */
+/* 
+ * attention LED ON: 1
+ *              OFF: 0
+ */
+u8 pcihp_acpi_get_attention_status (struct pcihp_acpi_slot *slot)
+{
+	/* TBD
+	 * no direct attention led status information via ACPI
+	 */
+
+	return 0;
+}
+
+
+/*
+ * latch closed:  1
+ * latch   open:  0
+ */
+u8 pcihp_acpi_get_latch_status (struct pcihp_acpi_slot *slot)
+{
+	unsigned int sta;
+
+	/* TBD
+	 * no direct latch information via ACPI
+	 */
+
+	sta = get_slot_status(slot);
+
+	return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0;
+}
+
+
+/*
+ * adapter presence : 2
+ *          absence : 0
+ */
+u8 pcihp_acpi_get_adapter_status (struct pcihp_acpi_slot *slot)
+{
+	unsigned int sta;
+
+	/* TBD
+	 * is this information correct?
+	 */
+
+	sta = get_slot_status(slot);
+
+	return (sta == 0) ? 0 : 2;
+}