All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] PPC64 PCI Hotplug Driver for RPA
@ 2004-02-11  1:08 johnrose
  2004-02-15  8:58 ` Rusty Russell
  0 siblings, 1 reply; 10+ messages in thread
From: johnrose @ 2004-02-11  1:08 UTC (permalink / raw)
  To: linux-kernel
  Cc: gregkh, greg, lxie, wortman, johnrose, scheel, pcihpd-discuss

Please consider the following patch for inclusion.  This patch contains the
implementation of the PCI Hotplug Driver for PPC64 RISC Platform Architecture.
The patch is made against kernel version 2.6.3-rc2.  

The PCI Hotplug module allows the runtime addition/removal of I/O adapters on
a PPC64 machine.  Due to firmware restrictions, hotplug operations must be
initiated by userspace tools.  These operations are initiated using interface 
files located at:
/sys/bus/pci/pci_hotplug_slots/<bus_name>
Development contact for this module is Linda Xie (lxie@us.ibm.com).

Thanks-
John

diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
--- a/drivers/pci/hotplug/Kconfig	Tue Feb 10 18:50:20 2004
+++ b/drivers/pci/hotplug/Kconfig	Tue Feb 10 18:50:20 2004
@@ -122,5 +122,16 @@
 
 	  When in doubt, say N.
 
+config HOTPLUG_PCI_RPA
+	tristate "RPA PCI Hotplug driver"
+	depends on HOTPLUG_PCI && PPC_PSERIES && PPC64
+	help
+	  Say Y here if you have a 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.
+
 endmenu
 
diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
--- a/drivers/pci/hotplug/Makefile	Tue Feb 10 18:50:20 2004
+++ b/drivers/pci/hotplug/Makefile	Tue Feb 10 18:50:20 2004
@@ -9,6 +9,7 @@
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 
 pci_hotplug-objs	:=	pci_hotplug_core.o
 
@@ -32,6 +33,9 @@
 				acpiphp_glue.o	\
 				acpiphp_pci.o	\
 				acpiphp_res.o
+
+rpaphp-objs		:=	rpaphp_core.o	\
+				rpaphp_pci.o	
 
 ifdef CONFIG_HOTPLUG_PCI_ACPI
   EXTRA_CFLAGS  += -D_LINUX -I$(TOPDIR)/drivers/acpi
diff -Nru a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp.h	Tue Feb 10 18:50:20 2004
@@ -0,0 +1,106 @@
+/*
+ * 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 "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 */
+
+#define SLOT_NAME_SIZE 12
+
+/* Error status from rtas_get-sensor */
+#define NEED_POWER    -9000     /* slot must be power up and unisolated to get state */
+#define PWR_ONLY      -9001     /* slot must be powerd up to get state, leave isolated */
+#define ERR_SENSE_USE -9002     /* No DR operation will succeed, slot is unusable  */
+
+/* Sensor values from rtas_get-sensor */
+#define EMPTY	0       /* No card in slot */
+#define PRESENT	1       /* Card in slot */
+
+#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
+	#define MY_NAME "rpaphp"
+#else
+	#define MY_NAME THIS_MODULE->name
+#endif
+
+
+#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)
+
+#define SLOT_MAGIC	0x67267322
+
+/* 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 {
+	u32	magic;
+	int     state;
+	u32     index;
+	u32     type;
+	u32     power_domain;
+	char    *name;
+	struct	device_node *dn;/* slot's device_node in OFDT		*/
+				/* dn has phb info			*/
+	struct	pci_dev	*bridge;/* slot's pci_dev in pci_devices	*/
+
+	struct	pci_dev	*dev;	/* pci_dev of device in this slot 	*/
+				/* it will be used for unconfig		*/ 
+				/* NULL if slot is empty		*/
+
+	struct  hotplug_slot    *hotplug_slot;
+	struct list_head	rpaphp_slot_list;
+};
+
+extern struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn);
+extern int rpaphp_add_slot(char *slot_name);
+extern int rpaphp_remove_slot(struct slot *slot);
+extern int rpaphp_claim_resource(struct pci_dev *dev, int resource);
+
+#endif /* _PPC64PHP_H */
diff -Nru a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp_core.c	Tue Feb 10 18:50:20 2004
@@ -0,0 +1,964 @@
+/*
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#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"
+#include "pci_hotplug.h"
+
+
+static int debug = 1;
+static struct semaphore rpaphp_sem;
+static int rpaphp_debug;
+static LIST_HEAD (rpaphp_slot_head);
+static int num_slots = 0;
+
+#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_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 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_max_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
+static int get_cur_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
+
+static struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+	.owner			= THIS_MODULE,
+	.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_max_bus_speed	= get_max_bus_speed,
+	.get_cur_bus_speed	= get_cur_bus_speed,
+};
+
+static int rpaphp_get_sensor_state(int index, int *state)
+{
+	int rc;
+
+	rc = rtas_get_sensor(DR_ENTITY_SENSE, index, state);
+
+	if (rc) {
+		if (rc ==  NEED_POWER || rc == PWR_ONLY) {
+			dbg("%s: slot must be power up to get sensor-state\n",
+				__FUNCTION__);
+		} else if (rc == ERR_SENSE_USE)
+			info("%s: slot is unusable\n", __FUNCTION__);
+		   else err("%s failed to get sensor state\n", __FUNCTION__);
+	}
+	return rc;
+}
+
+static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
+{
+	struct pci_dev		*retval_dev = NULL;
+
+	retval_dev = rpaphp_find_pci_dev(slot->dn);
+
+	return retval_dev;
+}
+
+static struct pci_dev *rpaphp_find_adapter_pdev(struct slot *slot)
+{
+	struct pci_dev * retval_dev = NULL;
+
+	retval_dev = rpaphp_find_pci_dev(slot->dn->child);
+
+	return retval_dev;
+}
+
+
+/* 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\n", function);
+		return -1;
+	}
+
+	if (!slot->hotplug_slot) {
+		dbg("%s - slot->hotplug_slot == NULL!\n", 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\n", function);
+		return NULL;
+	}
+
+	slot = (struct slot *)hotplug_slot->private;
+	if (slot_paranoia_check(slot, function))
+		return NULL;
+	return slot;
+}
+
+static inline int rpaphp_set_attention_status(struct slot *slot, u8 status)
+{
+	int	rc;
+
+	/* status: LED_OFF or LED_ON */
+	rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
+	if (rc)
+		err("slot(%s) set attention-status(%d) failed! rc=0x%x\n",
+			slot->name, status, rc);
+	
+	return rc;
+}
+
+static int rpaphp_get_power_status(struct slot *slot, u8 *value)
+{
+	int	rc;
+
+	rc = rtas_get_power_level(slot->power_domain, (int *)value);
+	if (rc)
+		err("failed to get power-level for slot(%s), rc=0x%x\n",
+			slot->name, rc);
+
+	return rc;
+}
+
+static int rpaphp_get_attention_status(struct slot *slot)
+{
+
+	return slot->hotplug_slot->info->attention_status;
+}
+
+/**
+ * set_attention_status - set attention LED
+ * 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 retval = 0;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+	switch (value) {
+		case 0:
+			retval = rpaphp_set_attention_status(slot, LED_OFF);
+			hotplug_slot->info->attention_status = 0;
+			break;
+
+		case 1:
+		default:
+			retval = rpaphp_set_attention_status(slot, LED_ON);
+			hotplug_slot->info->attention_status = 1;
+			break;
+
+		case 2:
+			retval = rpaphp_set_attention_status(slot, LED_ID);
+			hotplug_slot->info->attention_status = 2;
+			break;
+
+	}
+	up(&rpaphp_sem);
+	
+	return retval;
+}
+
+/**
+ * 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;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+	retval = rpaphp_get_power_status(slot, value);
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+/**
+ * get_attention_status - get attention LED status
+ *
+ *
+ */
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int retval = 0;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+
+	down(&rpaphp_sem);
+	*value = rpaphp_get_attention_status(slot);
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+/*
+ * get_adapter_status - get  the status of a slot
+ *
+ * 0-- slot is empty
+ * 1-- adapter is configured
+ * 2-- adapter is not configured
+ * 3-- not valid
+ */
+static int rpaphp_get_adapter_status(struct slot *slot, int is_init, u8 *value)
+{
+	int	state, rc;
+
+	*value 		  = NOT_VALID;
+
+	rc = rpaphp_get_sensor_state(slot->index, &state);
+
+	if (rc)
+		return rc;
+
+	if (state == PRESENT) {
+		dbg("slot is occupied\n");
+
+		if (!is_init) /* at run-time slot->state can be changed by */
+			  /* config/unconfig adapter	 		   */
+			*value = slot->state;
+		else {
+		if (!slot->dn->child)
+			dbg("%s: %s is not valid OFDT node\n",
+				__FUNCTION__, slot->dn->full_name);
+		else
+			if (rpaphp_find_pci_dev(slot->dn->child))
+				*value = CONFIGURED;
+			else {
+				dbg("%s: can't find pdev of adapter in slot[%s]\n",
+					__FUNCTION__, slot->name);
+				*value = NOT_CONFIGURED;
+				}
+		}
+	}
+	else
+		if (state == EMPTY) {
+		dbg("slot is empty\n");
+			*value = state;
+		}
+	
+	return 0;
+}
+
+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;
+
+	down(&rpaphp_sem);
+
+	/*  have to go through this */
+	retval = rpaphp_get_adapter_status(slot, 0, value);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+
+static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+
+	switch (slot->type) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+			*value = PCI_SPEED_33MHz;	/* speed for case 1-6 */
+			break;
+		case 7:
+		case 8:
+			*value = PCI_SPEED_66MHz;
+			break;
+		case 11:
+		case 14:
+			*value = PCI_SPEED_66MHz_PCIX;
+			break;
+		case 12:
+		case 15:
+			*value = PCI_SPEED_100MHz_PCIX;
+			break;
+		case 13:
+		case 16:
+			*value = PCI_SPEED_133MHz_PCIX;
+			break;
+		default:
+			*value = PCI_SPEED_UNKNOWN;
+			break;
+
+	}
+
+	up(&rpaphp_sem);
+
+	return 0;
+}
+
+
+/* return dummy value because not sure if PRA provides any method... */
+static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	*value = PCI_SPEED_UNKNOWN;
+
+	return 0;
+}
+
+/*
+ * rpaphp_validate_slot - make sure the name of the slot matches
+ * 				the location code , if the slots is not
+ *				empty.
+ */
+static int rpaphp_validate_slot(const char *slot_name, const int slot_index)
+{
+	struct device_node	*dn;
+
+	for(dn = find_all_nodes(); dn; dn = dn->next) {
+
+		int 		*index;
+		unsigned char	*loc_code;
+
+		index  = (int *)get_property(dn, "ibm,my-drc-index", NULL);
+
+		if (index && *index == slot_index) {
+			char *slash, *tmp_str;
+
+			loc_code = get_property(dn, "ibm,loc-code", NULL);
+			if (!loc_code) { 
+				return -1;
+			}
+
+			tmp_str = kmalloc(MAX_LOC_CODE, GFP_KERNEL); 
+			if (!tmp_str) {
+				err("%s: out of memory\n", __FUNCTION__);
+				return -1;
+			}
+				
+			strcpy(tmp_str, loc_code);
+			slash = strrchr(tmp_str, '/');
+			if (slash) 
+				*slash = '\0';
+			
+			if (strcmp(slot_name, tmp_str)) {
+				kfree(tmp_str);
+				return -1;
+			}
+
+			kfree(tmp_str);
+			break;
+		}
+	}
+	
+	return 0;
+}
+
+/* Must be called before pci_bus_add_devices */
+static void rpaphp_fixup_new_devices(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+	/*
+	 * Skip already-present devices (which are on the
+	 * global device list.)
+	 */
+		if (list_empty(&dev->global_list)) {
+			int i;
+			pcibios_fixup_device_resources(dev, bus);
+			pci_read_irq_line(dev);
+			for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+				struct resource *r = &dev->resource[i];
+				if (r->parent || !r->start || !r->flags)
+					continue;
+				rpaphp_claim_resource(dev, i);
+			}
+		}
+	}
+}
+
+static struct pci_dev *rpaphp_config_adapter(struct slot *slot)
+{
+	struct pci_bus 		*pci_bus;
+	struct device_node	*dn;
+	int 			num;
+	struct pci_dev		*dev = NULL;
+
+	if (slot->bridge) {
+
+		pci_bus = slot->bridge->subordinate;
+
+		if (!pci_bus) {
+			err("%s: can't find bus structure\n", __FUNCTION__);
+			goto exit;
+		}
+
+		for (dn = slot->dn->child; dn; dn = dn->sibling) {
+			dbg("child dn's devfn=[%x]\n", dn->devfn);
+				num = pci_scan_slot(pci_bus,
+				PCI_DEVFN(PCI_SLOT(dn->devfn),  0));
+
+				dbg("pci_scan_slot return num=%d\n", num);
+
+			if (num) {
+				rpaphp_fixup_new_devices(pci_bus);
+				pci_bus_add_devices(pci_bus);
+			}
+		}
+
+		dev = rpaphp_find_pci_dev(slot->dn->child);
+	}
+	else {
+		/* slot is not enabled */
+		err("slot doesn't have pci_dev structure\n");
+		dev = NULL;
+		goto exit;
+	}
+
+exit:
+	dbg("Exit %s: pci_dev %s\n", __FUNCTION__, dev? "found":"not found");
+
+	return dev;
+}
+
+static int rpaphp_unconfig_adapter(struct slot *slot)
+{
+	if (!slot->dev) {
+		info("%s: no card in slot[%s]\n",
+			__FUNCTION__, slot->name);
+
+		return -EINVAL;
+	}
+
+	/* remove the device from the pci core */
+	pci_remove_bus_device(slot->dev);
+
+	pci_dev_put(slot->dev);
+	slot->state = NOT_CONFIGURED;
+
+	dbg("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__, slot->name);
+
+	return 0;
+}
+
+/* free up the memory user be a slot */
+
+static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return;
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot->name);
+	kfree(slot->hotplug_slot);
+	pci_dev_put(slot->bridge);
+	pci_dev_put(slot->dev);
+	kfree(slot);
+}
+
+int rpaphp_remove_slot(struct slot *slot)
+{
+	int retval = 0;
+
+  	sysfs_remove_link(slot->hotplug_slot->kobj.parent,
+			slot->bridge->slot_name);
+
+	list_del(&slot->rpaphp_slot_list);
+	retval = pci_hp_deregister(slot->hotplug_slot);
+	if (retval)
+		err("Problem unregistering a slot %s\n", slot->name);
+	num_slots--;
+
+	return retval;
+}
+
+static int is_php_dn(struct device_node *dn, int **indexes,  int **names, int **types, int **power_domains)
+{
+	*indexes = (int *)get_property(dn, "ibm,drc-indexes", NULL);
+	if (!*indexes)
+		return(0);
+
+	/* &names[1] contains NULL terminated slot names */
+	*names = (int *)get_property(dn, "ibm,drc-names", NULL);
+	if (!*names)
+		return(0);
+
+	/* &types[1] contains NULL terminated slot types */
+	*types = (int *)get_property(dn, "ibm,drc-types", NULL);
+	if (!*types)
+		return(0);
+
+	/* power_domains[1...n] are the slot power domains */
+	*power_domains = (int *)get_property(dn,
+		"ibm,drc-power-domains", NULL);
+	if (!*power_domains)
+		return(0);
+
+	if (!get_property(dn, "ibm,fw-pci-hot-plug-ctrl", NULL))
+		return(0);
+
+	return(1);
+}
+
+static struct slot *alloc_slot_struct(void)
+{
+	struct slot *slot;
+
+	slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
+	if (!slot)
+		return (NULL);
+	memset(slot, 0, sizeof(struct slot));
+	slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot),
+		GFP_KERNEL);
+	if (!slot->hotplug_slot) {
+		kfree(slot);
+		return (NULL);
+	}
+	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 (NULL);
+	}
+	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 (NULL);
+	}
+	return (slot);
+}
+
+static int setup_hotplug_slot_info(struct slot *slot)
+{
+	rpaphp_get_power_status(slot,
+		&slot->hotplug_slot->info->power_status);
+
+	rpaphp_get_adapter_status(slot, 1,
+		&slot->hotplug_slot->info->adapter_status);
+
+	if (slot->hotplug_slot->info->adapter_status == NOT_VALID) {
+		dbg("%s: NOT_VALID: skip dn->full_name=%s\n",
+			__FUNCTION__, slot->dn->full_name);
+		    kfree(slot->hotplug_slot->info);
+		    kfree(slot->hotplug_slot->name);
+		    kfree(slot->hotplug_slot);
+		    kfree(slot);
+		return (-1);
+	}
+	return (0);
+}
+
+static int register_slot(struct slot *slot)
+{
+	int retval;
+
+	retval = pci_hp_register(slot->hotplug_slot);
+	if (retval) {
+		err("pci_hp_register failed with error %d\n", retval);
+		rpaphp_release_slot(slot->hotplug_slot);
+		return (retval);
+	}
+	/* create symlink between slot->name and it's bus_id */
+	dbg("%s: sysfs_create_link: %s --> %s\n", __FUNCTION__,
+		slot->bridge->slot_name, slot->name);
+	retval = sysfs_create_link(slot->hotplug_slot->kobj.parent,
+			&slot->hotplug_slot->kobj,
+			slot->bridge->slot_name);
+	if (retval) {
+		err("sysfs_create_link failed with error %d\n", retval);
+		rpaphp_release_slot(slot->hotplug_slot);
+		return (retval);
+	}
+	/* add slot to our internal list */
+	dbg("%s adding slot[%s] to rpaphp_slot_list\n",
+		__FUNCTION__, slot->name);
+
+	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
+
+	info("Slot [%s] (bus_id=%s) registered\n",
+		slot->name, slot->bridge->slot_name);
+	return (0);
+}
+
+/*************************************
+ * Add  Hot Plug slot(s) to sysfs
+ *
+ ************************************/
+int rpaphp_add_slot(char *slot_name)
+{
+	struct slot		*slot;
+	int 			retval = 0;
+	int 			i;
+	struct device_node 	*dn;
+	int 			*indexes, *names, *types, *power_domains;
+	char 			*name, *type;
+
+	for (dn = find_all_nodes(); dn; dn = dn->next) {
+
+		if (dn->name != 0 && strcmp(dn->name, "pci") == 0)	{
+			if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
+				continue;
+
+			dbg("%s : found device_node in OFDT full_name=%s, name=%s\n",
+				__FUNCTION__, dn->full_name, dn->name);
+
+			name = (char *)&names[1];
+			type = (char *)&types[1];
+
+			for (i = 0; i < indexes[0];
+				i++,
+				name += (strlen(name) + 1),
+				type += (strlen(type) + 1)) {
+
+				dbg("%s: name[%s] index[%x]\n",
+					__FUNCTION__, name, indexes[i+1]);
+
+				if (slot_name && strcmp(slot_name, name))
+					continue;
+
+				if (rpaphp_validate_slot(name, indexes[i + 1])) {
+					dbg("%s: slot(%s, 0x%x) is invalid.\n",
+						__FUNCTION__, name, indexes[i+ 1]);
+					continue;
+				}
+
+				slot = alloc_slot_struct();
+				if (!slot) {
+					retval = -ENOMEM;
+					goto exit;
+				}
+
+				slot->name = slot->hotplug_slot->name;
+				slot->index = indexes[i + 1];
+				strcpy(slot->name, name);
+				slot->type = simple_strtoul(type, NULL, 10);
+				if (slot->type < 1  || slot->type > 16)
+					slot->type = 0;
+
+				slot->power_domain = power_domains[i + 1];
+				slot->magic = SLOT_MAGIC;
+				slot->hotplug_slot->private = slot;
+				slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
+				slot->hotplug_slot->release = &rpaphp_release_slot;
+				slot->dn = dn;
+
+				/*
+			 	* Initilize the slot info structure with some known
+			 	* good values.
+			 	*/
+				if (setup_hotplug_slot_info(slot))
+					continue;
+
+				slot->bridge = rpaphp_find_bridge_pdev(slot);
+				if (!slot->bridge && slot_name) { /* slot being added doesn't have pci_dev yet*/
+					dbg("%s: no pci_dev for bridge dn %s\n",
+							__FUNCTION__, slot_name);
+					kfree(slot->hotplug_slot->info);
+					kfree(slot->hotplug_slot->name);
+					kfree(slot->hotplug_slot);
+					kfree(slot);
+					continue;
+				}
+
+				/* find slot's pci_dev if it's not empty*/
+				if (slot->hotplug_slot->info->adapter_status == EMPTY) {
+					slot->state = EMPTY;  /* slot is empty */
+					slot->dev = NULL;
+				}
+				else {  /* slot is occupied */
+					if(!(slot->dn->child)) { /* non-empty slot has to have child */
+						err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
+						__FUNCTION__, slot->name);
+						kfree(slot->hotplug_slot->info);
+						kfree(slot->hotplug_slot->name);
+						kfree(slot->hotplug_slot);
+						kfree(slot);
+						continue;
+
+					}
+
+					slot->dev = rpaphp_find_adapter_pdev(slot);
+					if(slot->dev) {
+						slot->state = CONFIGURED;
+						pci_dev_get(slot->dev);
+					}
+					else {
+						/* DLPAR add as opposed to
+						 * boot time */
+						slot->state = NOT_CONFIGURED;
+					}
+				}
+				dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
+					__FUNCTION__, dn->full_name, slot->index, slot->name,
+					slot->power_domain, slot->type);
+
+				retval = register_slot(slot);
+				if (retval)
+					goto exit;
+
+				num_slots++;
+
+				if (slot_name)
+					goto exit;
+
+			}/* for indexes */
+		}/* "pci" */
+	}/* find_all_nodes */
+exit:
+	dbg("%s - Exit: num_slots=%d rc[%d]\n",
+		__FUNCTION__, num_slots, retval);
+	return retval;
+}
+
+/*
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static int init_slots (void)
+{
+	int 			retval = 0;
+
+	retval = rpaphp_add_slot(NULL);
+
+	return retval;
+}
+
+
+static int init_rpa (void)
+{
+	int 			retval = 0;
+
+	init_MUTEX(&rpaphp_sem);
+
+	/* initialize internal data structure etc. */
+	retval = init_slots();
+	if (!num_slots)
+		retval = -ENODEV;
+
+	return retval;
+}
+
+static void 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);
+		sysfs_remove_link(slot->hotplug_slot->kobj.parent,
+			slot->bridge->slot_name);
+		list_del(&slot->rpaphp_slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+
+	return;
+}
+
+
+static int __init rpaphp_init(void)
+{
+	int retval = 0;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+	rpaphp_debug = debug;
+
+	/* read all the PRA info from the system */
+	retval = init_rpa();
+
+	return retval;
+}
+
+
+static void __exit rpaphp_exit(void)
+{
+	cleanup_slots();
+}
+
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	int retval = 0, state;
+
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	if (slot->state == CONFIGURED) {
+		dbg("%s: %s is already enabled\n",
+			__FUNCTION__, slot->name);
+		goto exit;
+	}
+
+	dbg("ENABLING SLOT %s\n", slot->name);
+
+	down(&rpaphp_sem);
+
+	retval = rpaphp_get_sensor_state(slot->index, &state);
+
+	if (retval)
+		goto exit;
+
+	dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
+
+	/* if slot is not empty, enable the adapter */
+	if (state == PRESENT) {
+		dbg("%s : slot[%s] is occupid.\n", __FUNCTION__, slot->name);
+
+		
+		slot->dev = rpaphp_config_adapter(slot);
+		if (slot->dev != NULL) {
+			slot->state = CONFIGURED;
+
+			dbg("%s: adapter %s in slot[%s] has been configured\n",
+				__FUNCTION__, slot->dev->slot_name,
+				slot->name);
+		}
+		else {
+			slot->state = NOT_CONFIGURED;
+
+			dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
+				__FUNCTION__, slot->name);
+		}
+
+	}
+	else if (state == EMPTY) {
+		dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name);
+		slot->state = EMPTY;
+	}
+	else {
+		err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
+		slot->state = NOT_VALID;
+		retval = -EINVAL;
+	}
+
+exit:
+	if (slot->state != NOT_VALID)
+		rpaphp_set_attention_status(slot, LED_ON);
+	else
+		rpaphp_set_attention_status(slot, LED_ID);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	int	retval;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	dbg("DISABLING SLOT %s\n", slot->name);
+
+	down(&rpaphp_sem);
+
+	rpaphp_set_attention_status(slot, LED_ID);
+
+	retval = rpaphp_unconfig_adapter(slot);
+
+	rpaphp_set_attention_status(slot, LED_OFF);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+module_init(rpaphp_init);
+module_exit(rpaphp_exit);
+
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
+EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
diff -Nru a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp_pci.c	Tue Feb 10 18:50:20 2004
@@ -0,0 +1,75 @@
+/*
+ * 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 <asm/pci-bridge.h>	/* for pci_controller */
+#include "rpaphp.h"
+
+
+struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn)
+{
+	struct pci_dev		*retval_dev = NULL, *dev = NULL;
+
+	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+		if(!dev->bus)
+			continue;
+
+		if (dev->devfn != dn->devfn)
+			continue;
+
+		if (dn->phb->global_number == pci_domain_nr(dev->bus) &&
+		    dn->busno == dev->bus->number) {
+			retval_dev = dev;
+			break;
+		}
+	}
+
+	return retval_dev;
+
+}
+
+int rpaphp_claim_resource(struct pci_dev *dev, int resource)
+{
+	struct resource *res = &dev->resource[resource];
+	struct resource *root = pci_find_parent_resource(dev, res);
+	char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
+	int err;
+
+	err = -EINVAL;
+	if (root != NULL) {
+		err = request_resource(root, res);
+	}
+
+	if (err) {
+		err("PCI: %s region %d of %s %s [%lx:%lx]\n",
+			root ? "Address space collision on" :
+			"No parent found for",
+			resource, dtype, pci_name(dev), res->start, res->end);
+	}
+
+	return err;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev);
+EXPORT_SYMBOL_GPL(rpaphp_claim_resource);

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
  2004-02-11  1:08 [PATCH] PPC64 PCI Hotplug Driver for RPA johnrose
@ 2004-02-15  8:58 ` Rusty Russell
  2004-02-16 18:18   ` John Rose
  2004-02-16 18:21   ` John Rose
  0 siblings, 2 replies; 10+ messages in thread
From: Rusty Russell @ 2004-02-15  8:58 UTC (permalink / raw)
  To: johnrose; +Cc: linux-kernel

In message <200402110108.i1B18glq022737@localhost.localdomain> you write:
> +MODULE_PARM(debug, "i");

Please:
	module_param(debug, int, 0644);

It's for your own good... really...

Thanks,
Rusty.
--
  Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
  2004-02-15  8:58 ` Rusty Russell
@ 2004-02-16 18:18   ` John Rose
  2004-02-16 18:21   ` John Rose
  1 sibling, 0 replies; 10+ messages in thread
From: John Rose @ 2004-02-16 18:18 UTC (permalink / raw)
  To: Rusty Russell; +Cc: linux-kernel, gregkh, Mike Wortman

> Please:
> 	module_param(debug, int, 0644);

New patch below for this modification to the RPA PCI Hotplug Driver.

Thanks-
John

diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
--- a/drivers/pci/hotplug/Kconfig	Mon Feb 16 12:12:05 2004
+++ b/drivers/pci/hotplug/Kconfig	Mon Feb 16 12:12:05 2004
@@ -122,5 +122,16 @@
 
 	  When in doubt, say N.
 
+config HOTPLUG_PCI_RPA
+	tristate "RPA PCI Hotplug driver"
+	depends on HOTPLUG_PCI && PPC_PSERIES && PPC64
+	help
+	  Say Y here if you have a 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.
+
 endmenu
 
diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
--- a/drivers/pci/hotplug/Makefile	Mon Feb 16 12:12:05 2004
+++ b/drivers/pci/hotplug/Makefile	Mon Feb 16 12:12:05 2004
@@ -9,6 +9,7 @@
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 
 pci_hotplug-objs	:=	pci_hotplug_core.o
 
@@ -32,6 +33,9 @@
 				acpiphp_glue.o	\
 				acpiphp_pci.o	\
 				acpiphp_res.o
+
+rpaphp-objs		:=	rpaphp_core.o	\
+				rpaphp_pci.o	
 
 ifdef CONFIG_HOTPLUG_PCI_ACPI
   EXTRA_CFLAGS  += -D_LINUX -I$(TOPDIR)/drivers/acpi
diff -Nru a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp.h	Mon Feb 16 12:12:05 2004
@@ -0,0 +1,106 @@
+/*
+ * 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



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
  2004-02-15  8:58 ` Rusty Russell
  2004-02-16 18:18   ` John Rose
@ 2004-02-16 18:21   ` John Rose
  2004-02-16 18:35     ` Christoph Hellwig
  1 sibling, 1 reply; 10+ messages in thread
From: John Rose @ 2004-02-16 18:21 UTC (permalink / raw)
  To: Rusty Russell; +Cc: linux-kernel, gregkh, Mike Wortman

Argh, botched that email.  Apologies.  Patch below, this time the entire
one :)

> Please:
> 	module_param(debug, int, 0644);

diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
--- a/drivers/pci/hotplug/Kconfig	Mon Feb 16 12:12:05 2004
+++ b/drivers/pci/hotplug/Kconfig	Mon Feb 16 12:12:05 2004
@@ -122,5 +122,16 @@
 
 	  When in doubt, say N.
 
+config HOTPLUG_PCI_RPA
+	tristate "RPA PCI Hotplug driver"
+	depends on HOTPLUG_PCI && PPC_PSERIES && PPC64
+	help
+	  Say Y here if you have a 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.
+
 endmenu
 
diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
--- a/drivers/pci/hotplug/Makefile	Mon Feb 16 12:12:05 2004
+++ b/drivers/pci/hotplug/Makefile	Mon Feb 16 12:12:05 2004
@@ -9,6 +9,7 @@
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 
 pci_hotplug-objs	:=	pci_hotplug_core.o
 
@@ -32,6 +33,9 @@
 				acpiphp_glue.o	\
 				acpiphp_pci.o	\
 				acpiphp_res.o
+
+rpaphp-objs		:=	rpaphp_core.o	\
+				rpaphp_pci.o	
 
 ifdef CONFIG_HOTPLUG_PCI_ACPI
   EXTRA_CFLAGS  += -D_LINUX -I$(TOPDIR)/drivers/acpi
diff -Nru a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp.h	Mon Feb 16 12:12:05 2004
@@ -0,0 +1,106 @@
+/*
+ * 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 "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 */
+
+#define SLOT_NAME_SIZE 12
+
+/* Error status from rtas_get-sensor */
+#define NEED_POWER    -9000     /* slot must be power up and unisolated to get state */
+#define PWR_ONLY      -9001     /* slot must be powerd up to get state, leave isolated */
+#define ERR_SENSE_USE -9002     /* No DR operation will succeed, slot is unusable  */
+
+/* Sensor values from rtas_get-sensor */
+#define EMPTY	0       /* No card in slot */
+#define PRESENT	1       /* Card in slot */
+
+#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
+	#define MY_NAME "rpaphp"
+#else
+	#define MY_NAME THIS_MODULE->name
+#endif
+
+
+#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)
+
+#define SLOT_MAGIC	0x67267322
+
+/* 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 {
+	u32	magic;
+	int     state;
+	u32     index;
+	u32     type;
+	u32     power_domain;
+	char    *name;
+	struct	device_node *dn;/* slot's device_node in OFDT		*/
+				/* dn has phb info			*/
+	struct	pci_dev	*bridge;/* slot's pci_dev in pci_devices	*/
+
+	struct	pci_dev	*dev;	/* pci_dev of device in this slot 	*/
+				/* it will be used for unconfig		*/ 
+				/* NULL if slot is empty		*/
+
+	struct  hotplug_slot    *hotplug_slot;
+	struct list_head	rpaphp_slot_list;
+};
+
+extern struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn);
+extern int rpaphp_add_slot(char *slot_name);
+extern int rpaphp_remove_slot(struct slot *slot);
+extern int rpaphp_claim_resource(struct pci_dev *dev, int resource);
+
+#endif /* _PPC64PHP_H */
diff -Nru a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp_core.c	Mon Feb 16 12:12:05 2004
@@ -0,0 +1,965 @@
+/*
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#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"
+#include "pci_hotplug.h"
+
+
+static int debug = 1;
+static struct semaphore rpaphp_sem;
+static int rpaphp_debug;
+static LIST_HEAD (rpaphp_slot_head);
+static int num_slots = 0;
+
+#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(debug, int, 0644);
+
+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_max_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
+static int get_cur_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
+
+static struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+	.owner			= THIS_MODULE,
+	.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_max_bus_speed	= get_max_bus_speed,
+	.get_cur_bus_speed	= get_cur_bus_speed,
+};
+
+static int rpaphp_get_sensor_state(int index, int *state)
+{
+	int rc;
+
+	rc = rtas_get_sensor(DR_ENTITY_SENSE, index, state);
+
+	if (rc) {
+		if (rc ==  NEED_POWER || rc == PWR_ONLY) {
+			dbg("%s: slot must be power up to get sensor-state\n",
+				__FUNCTION__);
+		} else if (rc == ERR_SENSE_USE)
+			info("%s: slot is unusable\n", __FUNCTION__);
+		   else err("%s failed to get sensor state\n", __FUNCTION__);
+	}
+	return rc;
+}
+
+static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
+{
+	struct pci_dev		*retval_dev = NULL;
+
+	retval_dev = rpaphp_find_pci_dev(slot->dn);
+
+	return retval_dev;
+}
+
+static struct pci_dev *rpaphp_find_adapter_pdev(struct slot *slot)
+{
+	struct pci_dev * retval_dev = NULL;
+
+	retval_dev = rpaphp_find_pci_dev(slot->dn->child);
+
+	return retval_dev;
+}
+
+
+/* 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\n", function);
+		return -1;
+	}
+
+	if (!slot->hotplug_slot) {
+		dbg("%s - slot->hotplug_slot == NULL!\n", 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\n", function);
+		return NULL;
+	}
+
+	slot = (struct slot *)hotplug_slot->private;
+	if (slot_paranoia_check(slot, function))
+		return NULL;
+	return slot;
+}
+
+static inline int rpaphp_set_attention_status(struct slot *slot, u8 status)
+{
+	int	rc;
+
+	/* status: LED_OFF or LED_ON */
+	rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
+	if (rc)
+		err("slot(%s) set attention-status(%d) failed! rc=0x%x\n",
+			slot->name, status, rc);
+	
+	return rc;
+}
+
+static int rpaphp_get_power_status(struct slot *slot, u8 *value)
+{
+	int	rc;
+
+	rc = rtas_get_power_level(slot->power_domain, (int *)value);
+	if (rc)
+		err("failed to get power-level for slot(%s), rc=0x%x\n",
+			slot->name, rc);
+
+	return rc;
+}
+
+static int rpaphp_get_attention_status(struct slot *slot)
+{
+
+	return slot->hotplug_slot->info->attention_status;
+}
+
+/**
+ * set_attention_status - set attention LED
+ * 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 retval = 0;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+	switch (value) {
+		case 0:
+			retval = rpaphp_set_attention_status(slot, LED_OFF);
+			hotplug_slot->info->attention_status = 0;
+			break;
+
+		case 1:
+		default:
+			retval = rpaphp_set_attention_status(slot, LED_ON);
+			hotplug_slot->info->attention_status = 1;
+			break;
+
+		case 2:
+			retval = rpaphp_set_attention_status(slot, LED_ID);
+			hotplug_slot->info->attention_status = 2;
+			break;
+
+	}
+	up(&rpaphp_sem);
+	
+	return retval;
+}
+
+/**
+ * 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;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+	retval = rpaphp_get_power_status(slot, value);
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+/**
+ * get_attention_status - get attention LED status
+ *
+ *
+ */
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	int retval = 0;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+
+	down(&rpaphp_sem);
+	*value = rpaphp_get_attention_status(slot);
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+/*
+ * get_adapter_status - get  the status of a slot
+ *
+ * 0-- slot is empty
+ * 1-- adapter is configured
+ * 2-- adapter is not configured
+ * 3-- not valid
+ */
+static int rpaphp_get_adapter_status(struct slot *slot, int is_init, u8 *value)
+{
+	int	state, rc;
+
+	*value 		  = NOT_VALID;
+
+	rc = rpaphp_get_sensor_state(slot->index, &state);
+
+	if (rc)
+		return rc;
+
+	if (state == PRESENT) {
+		dbg("slot is occupied\n");
+
+		if (!is_init) /* at run-time slot->state can be changed by */
+			  /* config/unconfig adapter	 		   */
+			*value = slot->state;
+		else {
+		if (!slot->dn->child)
+			dbg("%s: %s is not valid OFDT node\n",
+				__FUNCTION__, slot->dn->full_name);
+		else
+			if (rpaphp_find_pci_dev(slot->dn->child))
+				*value = CONFIGURED;
+			else {
+				dbg("%s: can't find pdev of adapter in slot[%s]\n",
+					__FUNCTION__, slot->name);
+				*value = NOT_CONFIGURED;
+				}
+		}
+	}
+	else
+		if (state == EMPTY) {
+		dbg("slot is empty\n");
+			*value = state;
+		}
+	
+	return 0;
+}
+
+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;
+
+	down(&rpaphp_sem);
+
+	/*  have to go through this */
+	retval = rpaphp_get_adapter_status(slot, 0, value);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+
+static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	down(&rpaphp_sem);
+
+	switch (slot->type) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+			*value = PCI_SPEED_33MHz;	/* speed for case 1-6 */
+			break;
+		case 7:
+		case 8:
+			*value = PCI_SPEED_66MHz;
+			break;
+		case 11:
+		case 14:
+			*value = PCI_SPEED_66MHz_PCIX;
+			break;
+		case 12:
+		case 15:
+			*value = PCI_SPEED_100MHz_PCIX;
+			break;
+		case 13:
+		case 16:
+			*value = PCI_SPEED_133MHz_PCIX;
+			break;
+		default:
+			*value = PCI_SPEED_UNKNOWN;
+			break;
+
+	}
+
+	up(&rpaphp_sem);
+
+	return 0;
+}
+
+
+/* return dummy value because not sure if PRA provides any method... */
+static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	*value = PCI_SPEED_UNKNOWN;
+
+	return 0;
+}
+
+/*
+ * rpaphp_validate_slot - make sure the name of the slot matches
+ * 				the location code , if the slots is not
+ *				empty.
+ */
+static int rpaphp_validate_slot(const char *slot_name, const int slot_index)
+{
+	struct device_node	*dn;
+
+	for(dn = find_all_nodes(); dn; dn = dn->next) {
+
+		int 		*index;
+		unsigned char	*loc_code;
+
+		index  = (int *)get_property(dn, "ibm,my-drc-index", NULL);
+
+		if (index && *index == slot_index) {
+			char *slash, *tmp_str;
+
+			loc_code = get_property(dn, "ibm,loc-code", NULL);
+			if (!loc_code) { 
+				return -1;
+			}
+
+			tmp_str = kmalloc(MAX_LOC_CODE, GFP_KERNEL); 
+			if (!tmp_str) {
+				err("%s: out of memory\n", __FUNCTION__);
+				return -1;
+			}
+				
+			strcpy(tmp_str, loc_code);
+			slash = strrchr(tmp_str, '/');
+			if (slash) 
+				*slash = '\0';
+			
+			if (strcmp(slot_name, tmp_str)) {
+				kfree(tmp_str);
+				return -1;
+			}
+
+			kfree(tmp_str);
+			break;
+		}
+	}
+	
+	return 0;
+}
+
+/* Must be called before pci_bus_add_devices */
+static void rpaphp_fixup_new_devices(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+	/*
+	 * Skip already-present devices (which are on the
+	 * global device list.)
+	 */
+		if (list_empty(&dev->global_list)) {
+			int i;
+			pcibios_fixup_device_resources(dev, bus);
+			pci_read_irq_line(dev);
+			for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+				struct resource *r = &dev->resource[i];
+				if (r->parent || !r->start || !r->flags)
+					continue;
+				rpaphp_claim_resource(dev, i);
+			}
+		}
+	}
+}
+
+static struct pci_dev *rpaphp_config_adapter(struct slot *slot)
+{
+	struct pci_bus 		*pci_bus;
+	struct device_node	*dn;
+	int 			num;
+	struct pci_dev		*dev = NULL;
+
+	if (slot->bridge) {
+
+		pci_bus = slot->bridge->subordinate;
+
+		if (!pci_bus) {
+			err("%s: can't find bus structure\n", __FUNCTION__);
+			goto exit;
+		}
+
+		for (dn = slot->dn->child; dn; dn = dn->sibling) {
+			dbg("child dn's devfn=[%x]\n", dn->devfn);
+				num = pci_scan_slot(pci_bus,
+				PCI_DEVFN(PCI_SLOT(dn->devfn),  0));
+
+				dbg("pci_scan_slot return num=%d\n", num);
+
+			if (num) {
+				rpaphp_fixup_new_devices(pci_bus);
+				pci_bus_add_devices(pci_bus);
+			}
+		}
+
+		dev = rpaphp_find_pci_dev(slot->dn->child);
+	}
+	else {
+		/* slot is not enabled */
+		err("slot doesn't have pci_dev structure\n");
+		dev = NULL;
+		goto exit;
+	}
+
+exit:
+	dbg("Exit %s: pci_dev %s\n", __FUNCTION__, dev? "found":"not found");
+
+	return dev;
+}
+
+static int rpaphp_unconfig_adapter(struct slot *slot)
+{
+	if (!slot->dev) {
+		info("%s: no card in slot[%s]\n",
+			__FUNCTION__, slot->name);
+
+		return -EINVAL;
+	}
+
+	/* remove the device from the pci core */
+	pci_remove_bus_device(slot->dev);
+
+	pci_dev_put(slot->dev);
+	slot->state = NOT_CONFIGURED;
+
+	dbg("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__, slot->name);
+
+	return 0;
+}
+
+/* free up the memory user be a slot */
+
+static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return;
+
+	kfree(slot->hotplug_slot->info);
+	kfree(slot->hotplug_slot->name);
+	kfree(slot->hotplug_slot);
+	pci_dev_put(slot->bridge);
+	pci_dev_put(slot->dev);
+	kfree(slot);
+}
+
+int rpaphp_remove_slot(struct slot *slot)
+{
+	int retval = 0;
+
+  	sysfs_remove_link(slot->hotplug_slot->kobj.parent,
+			slot->bridge->slot_name);
+
+	list_del(&slot->rpaphp_slot_list);
+	retval = pci_hp_deregister(slot->hotplug_slot);
+	if (retval)
+		err("Problem unregistering a slot %s\n", slot->name);
+	num_slots--;
+
+	return retval;
+}
+
+static int is_php_dn(struct device_node *dn, int **indexes,  int **names, int **types, int **power_domains)
+{
+	*indexes = (int *)get_property(dn, "ibm,drc-indexes", NULL);
+	if (!*indexes)
+		return(0);
+
+	/* &names[1] contains NULL terminated slot names */
+	*names = (int *)get_property(dn, "ibm,drc-names", NULL);
+	if (!*names)
+		return(0);
+
+	/* &types[1] contains NULL terminated slot types */
+	*types = (int *)get_property(dn, "ibm,drc-types", NULL);
+	if (!*types)
+		return(0);
+
+	/* power_domains[1...n] are the slot power domains */
+	*power_domains = (int *)get_property(dn,
+		"ibm,drc-power-domains", NULL);
+	if (!*power_domains)
+		return(0);
+
+	if (!get_property(dn, "ibm,fw-pci-hot-plug-ctrl", NULL))
+		return(0);
+
+	return(1);
+}
+
+static struct slot *alloc_slot_struct(void)
+{
+	struct slot *slot;
+
+	slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
+	if (!slot)
+		return (NULL);
+	memset(slot, 0, sizeof(struct slot));
+	slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot),
+		GFP_KERNEL);
+	if (!slot->hotplug_slot) {
+		kfree(slot);
+		return (NULL);
+	}
+	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 (NULL);
+	}
+	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 (NULL);
+	}
+	return (slot);
+}
+
+static int setup_hotplug_slot_info(struct slot *slot)
+{
+	rpaphp_get_power_status(slot,
+		&slot->hotplug_slot->info->power_status);
+
+	rpaphp_get_adapter_status(slot, 1,
+		&slot->hotplug_slot->info->adapter_status);
+
+	if (slot->hotplug_slot->info->adapter_status == NOT_VALID) {
+		dbg("%s: NOT_VALID: skip dn->full_name=%s\n",
+			__FUNCTION__, slot->dn->full_name);
+		    kfree(slot->hotplug_slot->info);
+		    kfree(slot->hotplug_slot->name);
+		    kfree(slot->hotplug_slot);
+		    kfree(slot);
+		return (-1);
+	}
+	return (0);
+}
+
+static int register_slot(struct slot *slot)
+{
+	int retval;
+
+	retval = pci_hp_register(slot->hotplug_slot);
+	if (retval) {
+		err("pci_hp_register failed with error %d\n", retval);
+		rpaphp_release_slot(slot->hotplug_slot);
+		return (retval);
+	}
+	/* create symlink between slot->name and it's bus_id */
+	dbg("%s: sysfs_create_link: %s --> %s\n", __FUNCTION__,
+		slot->bridge->slot_name, slot->name);
+	retval = sysfs_create_link(slot->hotplug_slot->kobj.parent,
+			&slot->hotplug_slot->kobj,
+			slot->bridge->slot_name);
+	if (retval) {
+		err("sysfs_create_link failed with error %d\n", retval);
+		rpaphp_release_slot(slot->hotplug_slot);
+		return (retval);
+	}
+	/* add slot to our internal list */
+	dbg("%s adding slot[%s] to rpaphp_slot_list\n",
+		__FUNCTION__, slot->name);
+
+	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
+
+	info("Slot [%s] (bus_id=%s) registered\n",
+		slot->name, slot->bridge->slot_name);
+	return (0);
+}
+
+/*************************************
+ * Add  Hot Plug slot(s) to sysfs
+ *
+ ************************************/
+int rpaphp_add_slot(char *slot_name)
+{
+	struct slot		*slot;
+	int 			retval = 0;
+	int 			i;
+	struct device_node 	*dn;
+	int 			*indexes, *names, *types, *power_domains;
+	char 			*name, *type;
+
+	for (dn = find_all_nodes(); dn; dn = dn->next) {
+
+		if (dn->name != 0 && strcmp(dn->name, "pci") == 0)	{
+			if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
+				continue;
+
+			dbg("%s : found device_node in OFDT full_name=%s, name=%s\n",
+				__FUNCTION__, dn->full_name, dn->name);
+
+			name = (char *)&names[1];
+			type = (char *)&types[1];
+
+			for (i = 0; i < indexes[0];
+				i++,
+				name += (strlen(name) + 1),
+				type += (strlen(type) + 1)) {
+
+				dbg("%s: name[%s] index[%x]\n",
+					__FUNCTION__, name, indexes[i+1]);
+
+				if (slot_name && strcmp(slot_name, name))
+					continue;
+
+				if (rpaphp_validate_slot(name, indexes[i + 1])) {
+					dbg("%s: slot(%s, 0x%x) is invalid.\n",
+						__FUNCTION__, name, indexes[i+ 1]);
+					continue;
+				}
+
+				slot = alloc_slot_struct();
+				if (!slot) {
+					retval = -ENOMEM;
+					goto exit;
+				}
+
+				slot->name = slot->hotplug_slot->name;
+				slot->index = indexes[i + 1];
+				strcpy(slot->name, name);
+				slot->type = simple_strtoul(type, NULL, 10);
+				if (slot->type < 1  || slot->type > 16)
+					slot->type = 0;
+
+				slot->power_domain = power_domains[i + 1];
+				slot->magic = SLOT_MAGIC;
+				slot->hotplug_slot->private = slot;
+				slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
+				slot->hotplug_slot->release = &rpaphp_release_slot;
+				slot->dn = dn;
+
+				/*
+			 	* Initilize the slot info structure with some known
+			 	* good values.
+			 	*/
+				if (setup_hotplug_slot_info(slot))
+					continue;
+
+				slot->bridge = rpaphp_find_bridge_pdev(slot);
+				if (!slot->bridge && slot_name) { /* slot being added doesn't have pci_dev yet*/
+					dbg("%s: no pci_dev for bridge dn %s\n",
+							__FUNCTION__, slot_name);
+					kfree(slot->hotplug_slot->info);
+					kfree(slot->hotplug_slot->name);
+					kfree(slot->hotplug_slot);
+					kfree(slot);
+					continue;
+				}
+
+				/* find slot's pci_dev if it's not empty*/
+				if (slot->hotplug_slot->info->adapter_status == EMPTY) {
+					slot->state = EMPTY;  /* slot is empty */
+					slot->dev = NULL;
+				}
+				else {  /* slot is occupied */
+					if(!(slot->dn->child)) { /* non-empty slot has to have child */
+						err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
+						__FUNCTION__, slot->name);
+						kfree(slot->hotplug_slot->info);
+						kfree(slot->hotplug_slot->name);
+						kfree(slot->hotplug_slot);
+						kfree(slot);
+						continue;
+
+					}
+
+					slot->dev = rpaphp_find_adapter_pdev(slot);
+					if(slot->dev) {
+						slot->state = CONFIGURED;
+						pci_dev_get(slot->dev);
+					}
+					else {
+						/* DLPAR add as opposed to
+						 * boot time */
+						slot->state = NOT_CONFIGURED;
+					}
+				}
+				dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
+					__FUNCTION__, dn->full_name, slot->index, slot->name,
+					slot->power_domain, slot->type);
+
+				retval = register_slot(slot);
+				if (retval)
+					goto exit;
+
+				num_slots++;
+
+				if (slot_name)
+					goto exit;
+
+			}/* for indexes */
+		}/* "pci" */
+	}/* find_all_nodes */
+exit:
+	dbg("%s - Exit: num_slots=%d rc[%d]\n",
+		__FUNCTION__, num_slots, retval);
+	return retval;
+}
+
+/*
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static int init_slots (void)
+{
+	int 			retval = 0;
+
+	retval = rpaphp_add_slot(NULL);
+
+	return retval;
+}
+
+
+static int init_rpa (void)
+{
+	int 			retval = 0;
+
+	init_MUTEX(&rpaphp_sem);
+
+	/* initialize internal data structure etc. */
+	retval = init_slots();
+	if (!num_slots)
+		retval = -ENODEV;
+
+	return retval;
+}
+
+static void 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);
+		sysfs_remove_link(slot->hotplug_slot->kobj.parent,
+			slot->bridge->slot_name);
+		list_del(&slot->rpaphp_slot_list);
+		pci_hp_deregister(slot->hotplug_slot);
+	}
+
+	return;
+}
+
+
+static int __init rpaphp_init(void)
+{
+	int retval = 0;
+
+	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+	rpaphp_debug = debug;
+
+	/* read all the PRA info from the system */
+	retval = init_rpa();
+
+	return retval;
+}
+
+
+static void __exit rpaphp_exit(void)
+{
+	cleanup_slots();
+}
+
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	int retval = 0, state;
+
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	if (slot->state == CONFIGURED) {
+		dbg("%s: %s is already enabled\n",
+			__FUNCTION__, slot->name);
+		goto exit;
+	}
+
+	dbg("ENABLING SLOT %s\n", slot->name);
+
+	down(&rpaphp_sem);
+
+	retval = rpaphp_get_sensor_state(slot->index, &state);
+
+	if (retval)
+		goto exit;
+
+	dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
+
+	/* if slot is not empty, enable the adapter */
+	if (state == PRESENT) {
+		dbg("%s : slot[%s] is occupid.\n", __FUNCTION__, slot->name);
+
+		
+		slot->dev = rpaphp_config_adapter(slot);
+		if (slot->dev != NULL) {
+			slot->state = CONFIGURED;
+
+			dbg("%s: adapter %s in slot[%s] has been configured\n",
+				__FUNCTION__, slot->dev->slot_name,
+				slot->name);
+		}
+		else {
+			slot->state = NOT_CONFIGURED;
+
+			dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
+				__FUNCTION__, slot->name);
+		}
+
+	}
+	else if (state == EMPTY) {
+		dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name);
+		slot->state = EMPTY;
+	}
+	else {
+		err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
+		slot->state = NOT_VALID;
+		retval = -EINVAL;
+	}
+
+exit:
+	if (slot->state != NOT_VALID)
+		rpaphp_set_attention_status(slot, LED_ON);
+	else
+		rpaphp_set_attention_status(slot, LED_ID);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	int	retval;
+	struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
+
+	if (slot == NULL)
+		return -ENODEV;
+
+	dbg("DISABLING SLOT %s\n", slot->name);
+
+	down(&rpaphp_sem);
+
+	rpaphp_set_attention_status(slot, LED_ID);
+
+	retval = rpaphp_unconfig_adapter(slot);
+
+	rpaphp_set_attention_status(slot, LED_OFF);
+
+	up(&rpaphp_sem);
+
+	return retval;
+}
+
+module_init(rpaphp_init);
+module_exit(rpaphp_exit);
+
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
+EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
diff -Nru a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpaphp_pci.c	Mon Feb 16 12:12:05 2004
@@ -0,0 +1,75 @@
+/*
+ * 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 <asm/pci-bridge.h>	/* for pci_controller */
+#include "rpaphp.h"
+
+
+struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn)
+{
+	struct pci_dev		*retval_dev = NULL, *dev = NULL;
+
+	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+		if(!dev->bus)
+			continue;
+
+		if (dev->devfn != dn->devfn)
+			continue;
+
+		if (dn->phb->global_number == pci_domain_nr(dev->bus) &&
+		    dn->busno == dev->bus->number) {
+			retval_dev = dev;
+			break;
+		}
+	}
+
+	return retval_dev;
+
+}
+
+int rpaphp_claim_resource(struct pci_dev *dev, int resource)
+{
+	struct resource *res = &dev->resource[resource];
+	struct resource *root = pci_find_parent_resource(dev, res);
+	char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
+	int err;
+
+	err = -EINVAL;
+	if (root != NULL) {
+		err = request_resource(root, res);
+	}
+
+	if (err) {
+		err("PCI: %s region %d of %s %s [%lx:%lx]\n",
+			root ? "Address space collision on" :
+			"No parent found for",
+			resource, dtype, pci_name(dev), res->start, res->end);
+	}
+
+	return err;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev);
+EXPORT_SYMBOL_GPL(rpaphp_claim_resource);



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
  2004-02-16 18:21   ` John Rose
@ 2004-02-16 18:35     ` Christoph Hellwig
       [not found]       ` <4033B983.6060809@ltcfwd.linux.ibm.com>
  0 siblings, 1 reply; 10+ messages in thread
From: Christoph Hellwig @ 2004-02-16 18:35 UTC (permalink / raw)
  To: John Rose; +Cc: Rusty Russell, linux-kernel, gregkh, Mike Wortman

On Mon, Feb 16, 2004 at 12:21:56PM -0600, John Rose wrote:
> +#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
> +	#define MY_NAME "rpaphp"
> +#else
> +	#define MY_NAME THIS_MODULE->name
> +#endif

Umm, what's this?  Checking CONFIG_FOO_MODULE is basically always wrong
and especially in this case.  Just use "rpaphp" always.

> +static int num_slots = 0;

No need to initialized variables to 0

> +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_max_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
> +static int get_cur_bus_speed	(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);

The larger whitespace before the opening brace aren't exatly linux
codingstyle..

> +static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
> +{
> +	struct pci_dev		*retval_dev = NULL;
> +
> +	retval_dev = rpaphp_find_pci_dev(slot->dn);
> +
> +	return retval_dev;
> +}

This is horribly verbose.  Why not simply

static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
{
	return rpaphp_find_pci_dev(slot->dn);
}

dito for rpaphp_find_adapter_pdev

In fact this is only used once so the wrapper looks rather useless.

> +/* 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\n", function);
> +		return -1;
> +	}
> +
> +	if (!slot->hotplug_slot) {
> +		dbg("%s - slot->hotplug_slot == NULL!\n", 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\n", function);
> +		return NULL;
> +	}

If you have a method that per specification doesn't get a NULL pointer adding
these kinds of checks is bad.  Getting a NULL pointer would be against the
codified guaranteeds and your system already is bad trouble - better panic
ASAP by dereferencing the NULL pointer than waiting longer and possibly
corrupting data.

> +static int init_slots (void)
> +{
> +	int 			retval = 0;
> +
> +	retval = rpaphp_add_slot(NULL);
> +
> +	return retval;
> +}

Same strange verbosity as above.

> +static int __init rpaphp_init(void)
> +{
> +	int retval = 0;
> +
> +	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
> +
> +	rpaphp_debug = debug;
> +
> +	/* read all the PRA info from the system */
> +	retval = init_rpa();
> +
> +	return retval;

Again..  Btw, why do you have rpaphp_debug and debug?  Just using one is
much less confusing.

> +static void __exit rpaphp_exit(void)
> +{
> +	cleanup_slots();
> +}

Why the wrapping?

> +	}
> +	else {

linux coding style says this is

	} else {


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
       [not found]       ` <4033B983.6060809@ltcfwd.linux.ibm.com>
@ 2004-02-18 19:17         ` Greg KH
       [not found]           ` <4033CC29.3010508@ltcfwd.linux.ibm.com>
  2004-02-18 22:27         ` Rusty Russell
  1 sibling, 1 reply; 10+ messages in thread
From: Greg KH @ 2004-02-18 19:17 UTC (permalink / raw)
  To: Linda Xie
  Cc: Christoph Hellwig, John Rose, Rusty Russell, linux-kernel, Mike Wortman

On Wed, Feb 18, 2004 at 01:14:11PM -0600, Linda Xie wrote:
> >
> >If you have a method that per specification doesn't get a NULL pointer 
> >adding
> >these kinds of checks is bad.  Getting a NULL pointer would be against the
> >codified guaranteeds and your system already is bad trouble - better panic
> >ASAP by dereferencing the NULL pointer than waiting longer and possibly
> >corrupting data.
> > 
> >
> Well, I understand your point, but other php drivers do the same thing.
> 
> Greg,
> Any thoughts?

I was being overly cautious a long time ago when I wrote that code.
Actually the whole "magic number" stuff can go away too, as that's
pretty pointless...

But don't worry about that too much.  If you want to clean it up, I
don't care.

> updated patch attached.

Um, how about a whole new patch against 2.6.3 as I have not applied this
one...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
       [not found]           ` <4033CC29.3010508@ltcfwd.linux.ibm.com>
@ 2004-02-18 20:57             ` Greg KH
  0 siblings, 0 replies; 10+ messages in thread
From: Greg KH @ 2004-02-18 20:57 UTC (permalink / raw)
  To: Linda Xie
  Cc: Greg KH, Christoph Hellwig, John Rose, Rusty Russell,
	linux-kernel, Mike Wortman

On Wed, Feb 18, 2004 at 02:33:45PM -0600, Linda Xie wrote:
> Hi Greg,
> 
> Greg KH wrote:
> 
> >On Wed, Feb 18, 2004 at 01:14:11PM -0600, Linda Xie wrote:
> > 
> >
> >
> > 
> >
> >Um, how about a whole new patch against 2.6.3 as I have not applied this
> >one...
> >
> Attached is a whole new patch.

Thanks, I've applied this.

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
       [not found]       ` <4033B983.6060809@ltcfwd.linux.ibm.com>
  2004-02-18 19:17         ` Greg KH
@ 2004-02-18 22:27         ` Rusty Russell
  1 sibling, 0 replies; 10+ messages in thread
From: Rusty Russell @ 2004-02-18 22:27 UTC (permalink / raw)
  To: Linda Xie; +Cc: John Rose, Rusty Russell, linux-kernel, gregkh, Mike Wortman

In message <4033B983.6060809@ltcfwd.linux.ibm.com> you write:
> >Umm, what's this?  Checking CONFIG_FOO_MODULE is basically always wrong
> >and especially in this case.  Just use "rpaphp" always.
> >  
> >
> Replaced with
> 
> #define MY_NAME "rpaphp"

Or better, remove #define and use KBUILD_MODNAME, which for this file
will be rpaphp anyway.

Thanks,
Rusty.
--
  Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
  2004-02-11  1:12 johnrose
@ 2004-02-18 20:57 ` Greg KH
  0 siblings, 0 replies; 10+ messages in thread
From: Greg KH @ 2004-02-18 20:57 UTC (permalink / raw)
  To: johnrose; +Cc: linux-kernel, gregkh, lxie, wortman, scheel, pcihpd-discuss

On Tue, Feb 10, 2004 at 07:12:29PM -0600, johnrose@austin.ibm.com wrote:
> Please consider the following patch for submission.  This patch contains the
> implementation of the I/O Slot DLPAR Drivers for PPC64 RISC Platform
> Architecture.  This module depends on the RPA PCI Hotplug Module in the
> previous post.  The patch is made against kernel version 2.6.3-rc2.  
> 
> The Dynamic Logical Partitioning Module allows the runtime movement of I/O
> Slots between logical partitions.  An administrator can logically add/remove
> PCI Buses to/from a PPC64 partition at runtime.  These operations are initiated
> using interface files located at:
> /sys/bus/pci/pci_hotplug_slots/control/
> Development contact for this module is John Rose (johnrose@austin.ibm.com).

Applied, thanks.

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PPC64 PCI Hotplug Driver for RPA
@ 2004-02-11  1:12 johnrose
  2004-02-18 20:57 ` Greg KH
  0 siblings, 1 reply; 10+ messages in thread
From: johnrose @ 2004-02-11  1:12 UTC (permalink / raw)
  To: linux-kernel
  Cc: gregkh, greg, lxie, wortman, johnrose, scheel, pcihpd-discuss

Please consider the following patch for submission.  This patch contains the
implementation of the I/O Slot DLPAR Drivers for PPC64 RISC Platform
Architecture.  This module depends on the RPA PCI Hotplug Module in the
previous post.  The patch is made against kernel version 2.6.3-rc2.  

The Dynamic Logical Partitioning Module allows the runtime movement of I/O
Slots between logical partitions.  An administrator can logically add/remove
PCI Buses to/from a PPC64 partition at runtime.  These operations are initiated
using interface files located at:
/sys/bus/pci/pci_hotplug_slots/control/
Development contact for this module is John Rose (johnrose@austin.ibm.com).

Thanks-
John

diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
--- a/drivers/pci/hotplug/Kconfig	Tue Feb 10 19:01:04 2004
+++ b/drivers/pci/hotplug/Kconfig	Tue Feb 10 19:01:04 2004
@@ -133,5 +133,17 @@
 
 	  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.
+
 endmenu
 
diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
--- a/drivers/pci/hotplug/Makefile	Tue Feb 10 19:01:04 2004
+++ b/drivers/pci/hotplug/Makefile	Tue Feb 10 19:01:04 2004
@@ -10,6 +10,7 @@
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
 
 pci_hotplug-objs	:=	pci_hotplug_core.o
 
@@ -36,6 +37,9 @@
 
 rpaphp-objs		:=	rpaphp_core.o	\
 				rpaphp_pci.o	
+
+rpadlpar_io-objs	:=	rpadlpar_core.o \
+				rpadlpar_sysfs.o
 
 ifdef CONFIG_HOTPLUG_PCI_ACPI
   EXTRA_CFLAGS  += -D_LINUX -I$(TOPDIR)/drivers/acpi
diff -Nru a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpadlpar.h	Tue Feb 10 19:01:04 2004
@@ -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_
+
+extern int dlpar_sysfs_init(void);
+extern void dlpar_sysfs_exit(void);
+
+extern int dlpar_add_slot(char *drc_name);
+extern int dlpar_remove_slot(char *drc_name);
+
+#endif
diff -Nru a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpadlpar_core.c	Tue Feb 10 19:01:04 2004
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/semaphore.h>
+#include "../pci.h"
+#include "rpaphp.h"
+#include "rpadlpar.h"
+
+static DECLARE_MUTEX(rpadlpar_sem);
+
+static inline int is_hotplug_capable(struct device_node *dn)
+{
+	unsigned char *ptr = get_property(dn, "ibm,fw-pci-hot-plug-ctrl", NULL);
+
+	return (int) (ptr != NULL);
+}
+
+static char *get_node_drc_name(struct device_node *dn)
+{
+	char *ptr = NULL;
+	int *drc_names;
+
+	drc_names = (int *) get_property(dn, "ibm,drc-names", NULL);
+	if (drc_names)
+		ptr = (char *) &drc_names[1];
+
+	return ptr;
+}
+
+static struct device_node *find_php_slot_node(char *drc_name)
+{
+	struct device_node *np = NULL;
+	char *name;
+
+	while ((np = of_find_node_by_type(np, "pci")))
+		if (is_hotplug_capable(np)) {
+			name = get_node_drc_name(np);
+			if (name && (!strcmp(drc_name, name)))
+				break;
+		}
+
+	return np;
+}
+
+static inline struct hotplug_slot *find_php_slot(char *drc_name)
+{
+	struct kobject *k;
+
+	k = kset_find_obj(&pci_hotplug_slots_subsys.kset, drc_name);
+	if (!k)
+		return NULL;
+
+	return to_hotplug_slot(k);
+}
+
+static struct slot *find_slot(char *drc_name)
+{
+	struct hotplug_slot *php_slot = find_php_slot(drc_name);
+	
+	if (!php_slot)
+		return NULL;
+
+	return (struct slot *) php_slot->private;
+}
+
+static void rpadlpar_claim_one_bus(struct pci_bus *b)
+{
+	struct list_head *ld;
+	struct pci_bus *child_bus;
+
+	for (ld = b->devices.next; ld != &b->devices; ld = ld->next) {
+		struct pci_dev *dev = pci_dev_b(ld);
+		int i;
+
+		for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+			struct resource *r = &dev->resource[i];
+
+			if (r->parent || !r->start || !r->flags)
+				continue;
+			rpaphp_claim_resource(dev, i);
+		}
+	}
+
+	list_for_each_entry(child_bus, &b->children, node)
+		rpadlpar_claim_one_bus(child_bus);
+}
+
+static int pci_add_secondary_bus(struct device_node *dn,
+		struct pci_dev *bridge_dev)
+{
+	struct pci_controller *hose = dn->phb;
+	struct pci_bus *child;
+	u8 sec_busno;
+
+	/* Get busno of downstream bus */
+	pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno);
+
+	/* Allocate and add to children of bridge_dev->bus */
+	child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno);
+	if (!child) {
+		printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__);
+		return 1;
+	}
+
+	sprintf(child->name, "PCI Bus #%02x", child->number);
+
+	/* Fixup subordinate bridge bases and resources */
+	pcibios_fixup_bus(child);
+
+	/* Claim new bus resources */
+	rpadlpar_claim_one_bus(bridge_dev->bus);
+
+	if (hose->last_busno < child->number)
+	    	hose->last_busno = child->number;
+
+	dn->bussubno = child->number;
+
+	/* ioremap() for child bus */
+	if (remap_bus_range(child)) {
+		printk(KERN_ERR "%s: could not ioremap() child bus\n",
+				__FUNCTION__);
+		return 1;
+	}
+
+	return 0;
+}
+
+static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
+{
+	struct pci_controller *hose = dn->phb;
+	struct pci_dev *dev = NULL;
+
+	/* Scan phb bus for EADS device, adding new one to bus->devices */
+	if (!pci_scan_single_device(hose->bus, dn->devfn)) {
+		printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__);
+		return NULL;
+	}
+
+	/* Add new devices to global lists.  Register in proc, sysfs. */
+	pci_bus_add_devices(hose->bus);
+
+	/* Confirm new bridge dev was created */
+	dev = rpaphp_find_pci_dev(dn);
+	if (!dev) {
+		printk(KERN_ERR "%s: failed to add pci device\n", __FUNCTION__);
+		return NULL;
+	}
+
+	if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)  {
+		printk(KERN_ERR "%s: unexpected header type %d\n",
+				__FUNCTION__, dev->hdr_type);
+		return NULL;
+	}
+
+	if (pci_add_secondary_bus(dn, dev))
+		return NULL;
+
+	return dev;
+}
+
+static int dlpar_pci_remove_bus(struct pci_dev *bridge_dev)
+{
+	struct pci_bus *secondary_bus;
+
+	if (!bridge_dev) {
+		printk(KERN_ERR "%s: unexpected null device\n",
+				__FUNCTION__);
+		return 1;
+	}
+
+	secondary_bus = bridge_dev->subordinate;
+
+	if (unmap_bus_range(secondary_bus)) {
+		printk(KERN_ERR "%s: failed to unmap bus range\n",
+				__FUNCTION__);
+		return 1;
+	}
+
+	pci_remove_bus_device(bridge_dev);
+
+	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 = find_php_slot_node(drc_name);
+	struct pci_dev *dev;
+	int rc = 0;
+
+	if (down_interruptible(&rpadlpar_sem))
+		return -ERESTARTSYS;
+
+	if (!dn) {
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	/* Check for existing hotplug slot */
+	if (find_slot(drc_name)) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Add pci bus */
+	dev = dlpar_pci_add_bus(dn);
+	if (!dev) {
+		printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
+				drc_name);
+		rc = -EIO;
+		goto exit;
+	}
+
+	/* Add hotplug slot for new bus */
+	if (rpaphp_add_slot(drc_name)) {
+		printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
+				__FUNCTION__, drc_name);
+		rc = -EIO;
+	}
+exit:
+	up(&rpadlpar_sem);
+	return rc;
+}
+
+/**
+ * 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 PCI Error
+ */
+int dlpar_remove_slot(char *drc_name)
+{
+	struct device_node *dn = find_php_slot_node(drc_name);
+	struct slot *slot;
+	struct pci_dev *bridge_dev;
+	int rc = 0;
+
+	if (down_interruptible(&rpadlpar_sem))
+		return -ERESTARTSYS;
+
+	if (!dn) {
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	slot = find_slot(drc_name);
+	if (!slot) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	bridge_dev = slot->bridge;
+	if (!bridge_dev) {
+		printk(KERN_ERR "%s: unexpected null bridge device\n",
+				__FUNCTION__);
+		rc = -EIO;
+		goto exit;
+	}
+
+	/* Remove hotplug slot */
+	if (rpaphp_remove_slot(slot)) {
+		printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+				__FUNCTION__, drc_name);
+		rc = -EIO;
+		goto exit;
+	}
+
+	/* Remove pci bus */
+	if (dlpar_pci_remove_bus(bridge_dev)) {
+		printk(KERN_ERR "%s: unable to remove pci bus %s\n",
+				__FUNCTION__, drc_name);
+		rc = -EIO;
+	}
+exit:
+	up(&rpadlpar_sem);
+	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",
+				__FUNCTION__);
+		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 -Nru a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c	Tue Feb 10 19:01:04 2004
@@ -0,0 +1,151 @@
+/*
+ * 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 "pci_hotplug.h"
+#include "rpadlpar.h"
+
+#define DLPAR_KOBJ_NAME       "control"
+#define ADD_SLOT_ATTR_NAME    "add_slot"
+#define REMOVE_SLOT_ATTR_NAME "remove_slot"
+
+#define MAX_DRC_NAME_LEN 64
+
+/* Store return code of dlpar operation in attribute struct */
+struct dlpar_io_attr {
+	int rc;
+	struct attribute attr;
+	ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf,
+		size_t nbytes);
+};
+
+/* Common show callback for all attrs, display the return code
+ * of the dlpar op */
+static ssize_t
+dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+{
+	struct dlpar_io_attr *dlpar_attr = container_of(attr,
+						struct dlpar_io_attr, attr);
+	return sprintf(buf, "%d\n", dlpar_attr->rc);
+}
+
+static ssize_t
+dlpar_attr_store(struct kobject * kobj, struct attribute * attr,
+		 const char *buf, size_t nbytes)
+{
+	struct dlpar_io_attr *dlpar_attr = container_of(attr,
+						struct dlpar_io_attr, attr);
+	return dlpar_attr->store ?
+		dlpar_attr->store(dlpar_attr, buf, nbytes) : 0;
+}
+
+static struct sysfs_ops dlpar_attr_sysfs_ops = {
+	.show = dlpar_attr_show,
+	.store = dlpar_attr_store,
+};
+
+static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
+				const char *buf, size_t nbytes)
+{
+	char drc_name[MAX_DRC_NAME_LEN];
+	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';
+
+	dlpar_attr->rc = dlpar_add_slot(drc_name);
+
+	return nbytes;
+}
+
+static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
+		 		const char *buf, size_t nbytes)
+{
+	char drc_name[MAX_DRC_NAME_LEN];
+	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';
+
+	dlpar_attr->rc = dlpar_remove_slot(drc_name);
+
+	return nbytes;
+}
+
+static struct dlpar_io_attr add_slot_attr = {
+	.rc = 0,
+	.attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, },
+	.store = add_slot_store,
+};
+
+static struct dlpar_io_attr remove_slot_attr = {
+	.rc = 0,
+	.attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
+	.store = remove_slot_store,
+};
+
+static struct attribute *default_attrs[] = {
+	&add_slot_attr.attr,
+	&remove_slot_attr.attr,
+	NULL,
+};
+
+static void dlpar_io_release(struct kobject *kobj)
+{
+	/* noop */
+	return;
+}
+
+struct kobj_type ktype_dlpar_io = {
+	.release = dlpar_io_release,
+	.sysfs_ops = &dlpar_attr_sysfs_ops,
+	.default_attrs = default_attrs,
+};
+
+struct kset dlpar_io_kset = {
+	.subsys = &pci_hotplug_slots_subsys,
+	.kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,},
+	.ktype = &ktype_dlpar_io,
+};
+
+int dlpar_sysfs_init(void)
+{
+	if (kset_register(&dlpar_io_kset)) {
+		printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
+				dlpar_io_kset.kobj.name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void dlpar_sysfs_exit(void)
+{
+	kset_unregister(&dlpar_io_kset);
+}

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2004-02-18 23:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-02-11  1:08 [PATCH] PPC64 PCI Hotplug Driver for RPA johnrose
2004-02-15  8:58 ` Rusty Russell
2004-02-16 18:18   ` John Rose
2004-02-16 18:21   ` John Rose
2004-02-16 18:35     ` Christoph Hellwig
     [not found]       ` <4033B983.6060809@ltcfwd.linux.ibm.com>
2004-02-18 19:17         ` Greg KH
     [not found]           ` <4033CC29.3010508@ltcfwd.linux.ibm.com>
2004-02-18 20:57             ` Greg KH
2004-02-18 22:27         ` Rusty Russell
2004-02-11  1:12 johnrose
2004-02-18 20:57 ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.