linux-hotplug.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RFC]: SGI Altix Hotplug Driver
@ 2005-03-15 20:20 Prarit Bhargava
  2005-03-16  4:34 ` Randy.Dunlap
  2005-03-16 12:36 ` Prarit Bhargava
  0 siblings, 2 replies; 3+ messages in thread
From: Prarit Bhargava @ 2005-03-15 20:20 UTC (permalink / raw)
  To: linux-hotplug

[-- Attachment #1: Type: text/plain, Size: 227 bytes --]

Colleages,

Attached are the code and changes to the existing codeset for a 
functional SGI Altix Hotplug Driver.

The code has been tested on several different Altix machines and is 
(obviously) ready for review.

Thanks,

P.

[-- Attachment #2: bkexport.diff --]
[-- Type: text/plain, Size: 28497 bytes --]

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2005/03/15 20:04:09-05:00 prarit@sgi.com 
#   Many files:
#     SGI PCI Hotplug Driver changes
#   sn_pci_hotplug_glue.c, sn_pci_hotplug_core.c, sn_pci_hotplug.h:
#     new file
# 
# drivers/pci/hotplug/sn_pci_hotplug_glue.c
#   2005/03/15 20:01:59-05:00 prarit@sgi.com +418 -0
#   SGI PCI Hotplug Driver changes
# 
# drivers/pci/hotplug/sn_pci_hotplug_glue.c
#   2005/03/15 20:01:59-05:00 prarit@sgi.com +0 -0
#   BitKeeper file /home/linux-2.5-hotplug/drivers/pci/hotplug/sn_pci_hotplug_glue.c
# 
# drivers/pci/hotplug/sn_pci_hotplug_core.c
#   2005/03/15 20:01:54-05:00 prarit@sgi.com +335 -0
#   SGI PCI Hotplug Driver changes
# 
# drivers/pci/hotplug/sn_pci_hotplug_core.c
#   2005/03/15 20:01:54-05:00 prarit@sgi.com +0 -0
#   BitKeeper file /home/linux-2.5-hotplug/drivers/pci/hotplug/sn_pci_hotplug_core.c
# 
# drivers/pci/hotplug/sn_pci_hotplug.h
#   2005/03/15 20:01:02-05:00 prarit@sgi.com +46 -0
# 
# drivers/pci/hotplug/sn_pci_hotplug.h
#   2005/03/15 20:01:02-05:00 prarit@sgi.com +0 -0
#   BitKeeper file /home/linux-2.5-hotplug/drivers/pci/hotplug/sn_pci_hotplug.h
# 
# drivers/pci/hotplug/Makefile
#   2005/03/15 20:00:30-05:00 prarit@sgi.com +9 -0
#   SGI PCI Hotplug Driver changes
# 
# drivers/pci/hotplug/Kconfig
#   2005/03/15 20:00:11-05:00 prarit@sgi.com +1 -1
#   SGI PCI Hotplug Driver changes
# 
# arch/ia64/sn/pci/pcibr/pcibr_provider.c
#   2005/03/15 19:59:29-05:00 prarit@sgi.com +37 -0
#   SGI PCI Hotplug Driver changes
# 
# arch/ia64/sn/include/pci/pcibr_provider.h
#   2005/03/15 19:58:07-05:00 prarit@sgi.com +2 -0
#   SGI PCI Hotplug Driver changes
# 
# arch/ia64/kernel/iosapic.c
#   2005/03/15 19:56:44-05:00 prarit@sgi.com +1 -1
#   SGI PCI Hotplug Driver changes
# 
diff -Nru a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
--- a/arch/ia64/kernel/iosapic.c	2005-03-15 20:09:33 -05:00
+++ b/arch/ia64/kernel/iosapic.c	2005-03-15 20:09:33 -05:00
@@ -83,8 +83,8 @@
 #include <asm/delay.h>
 #include <asm/hw_irq.h>
 #include <asm/io.h>
-#include <asm/iosapic.h>
 #include <asm/machvec.h>
+#include <asm/iosapic.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/system.h>
diff -Nru a/arch/ia64/sn/include/pci/pcibr_provider.h b/arch/ia64/sn/include/pci/pcibr_provider.h
--- a/arch/ia64/sn/include/pci/pcibr_provider.h	2005-03-15 20:09:33 -05:00
+++ b/arch/ia64/sn/include/pci/pcibr_provider.h	2005-03-15 20:09:33 -05:00
@@ -146,4 +146,6 @@
 extern int 		pcibr_ate_alloc(struct pcibus_info *, int);
 extern void 		pcibr_ate_free(struct pcibus_info *, int);
 extern void 		ate_write(struct pcibus_info *, int, int, uint64_t);
+extern int		sal_pcibr_slot_enable(struct pcibus_info *, int, void *);
+extern int		sal_pcibr_slot_disable(struct pcibus_info *, int, int, void *);
 #endif
diff -Nru a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c
--- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c	2005-03-15 20:09:33 -05:00
+++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c	2005-03-15 20:09:33 -05:00
@@ -19,6 +19,40 @@
 #include <asm/sn/addrs.h>
 
 
+int
+sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp)
+    {
+	struct ia64_sal_retval ret_stuff;
+	uint64_t busnum;
+	ret_stuff.status = 0;
+	ret_stuff.v0 = 0;
+
+	busnum = ((struct pcibus_info *)soft)->pbi_buscommon.bs_persist_busnum;
+	SAL_CALL_NOLOCK(ret_stuff,
+			(u64) SN_SAL_IOIF_SLOT_ENABLE,
+			(u64) busnum, (u64) device, (u64) resp, 0, 0, 0, 0);
+
+	return (int)ret_stuff.v0;
+}
+
+
+int
+sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action, void *resp)
+    {
+	struct ia64_sal_retval ret_stuff;
+	uint64_t busnum;
+	ret_stuff.status = 0;
+	ret_stuff.v0 = 0;
+
+	busnum = ((struct pcibus_info *)soft)->pbi_buscommon.bs_persist_busnum;
+	SAL_CALL_NOLOCK(ret_stuff,
+			(u64) SN_SAL_IOIF_SLOT_DISABLE,
+			(u64) busnum, (u64) device, (u64) action, (u64) resp, 0, 0, 0);
+
+	return (int)ret_stuff.v0;
+}
+
+
 static int sal_pcibr_error_interrupt(struct pcibus_info *soft)
 {
 	struct ia64_sal_retval ret_stuff;
@@ -168,3 +202,6 @@
 		pcibr_force_interrupt(sn_irq_info);
 	}
 }
+
+EXPORT_SYMBOL(sal_pcibr_slot_enable);
+EXPORT_SYMBOL(sal_pcibr_slot_disable);
diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
--- a/drivers/pci/hotplug/Kconfig	2005-03-15 20:09:33 -05:00
+++ b/drivers/pci/hotplug/Kconfig	2005-03-15 20:09:33 -05:00
@@ -187,7 +187,7 @@
 
 config HOTPLUG_PCI_SGI
 	tristate "SGI PCI Hotplug Support"
-	depends on HOTPLUG_PCI && IA64_SGI_SN2
+	depends on HOTPLUG_PCI && IA64_GENERIC
 	help
 	  Say Y here if you have an SGI IA64 Altix system.
 
diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
--- a/drivers/pci/hotplug/Makefile	2005-03-15 20:09:33 -05:00
+++ b/drivers/pci/hotplug/Makefile	2005-03-15 20:09:33 -05:00
@@ -2,6 +2,8 @@
 # Makefile for the Linux kernel pci hotplug controller drivers.
 #
 
+CPPFLAGS		+=-Iarch/ia64/sn/include/
+
 obj-$(CONFIG_HOTPLUG_PCI)		+= pci_hotplug.o
 obj-$(CONFIG_HOTPLUG_PCI_FAKE)		+= fakephp.o 
 obj-$(CONFIG_HOTPLUG_PCI_COMPAQ)	+= cpqphp.o
@@ -14,6 +16,7 @@
 obj-$(CONFIG_HOTPLUG_PCI_SHPC)		+= shpchp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
+obj-$(CONFIG_HOTPLUG_PCI_SGI)		+= sgi_hotplug.o
 
 pci_hotplug-objs	:=	pci_hotplug_core.o
 
@@ -72,3 +75,9 @@
 		shpchp-objs += shpchprm_nonacpi.o
 	endif
 endif
+
+ifdef CONFIG_HOTPLUG_PCI_SGI
+sgi_hotplug-objs	:=	sn_pci_hotplug_core.o \
+				sn_pci_hotplug_glue.o
+endif
+
diff -Nru a/drivers/pci/hotplug/sn_pci_hotplug.h b/drivers/pci/hotplug/sn_pci_hotplug.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/hotplug/sn_pci_hotplug.h	2005-03-15 20:09:33 -05:00
@@ -0,0 +1,46 @@
+#ifndef SN_PCI_HOTPLUG_H
+#define SN_PCI_HOTPLUG_H
+
+typedef enum sn_glue_pci_req_e sn_glue_pci_req_t;
+
+enum sn_glue_pci_req_e {
+    PCI_REQ_SLOT_ELIGIBLE,
+    PCI_REQ_SLOT_DISABLE
+};
+
+
+/* hotplug_slot struct's private pointer */
+struct slot {
+	int     device_num;
+	struct pci_bus *pci_bus;
+
+	/* this struct for glue internal only */
+	struct hotplug_slot *hotplug_slot;
+	struct list_head hp_list;
+};
+
+/* init and exit functions for glue */
+int sn_glue_init(void);
+void sn_glue_exit (void);
+
+/* standard hotplug functions */
+extern int sn_glue_slot_disable(struct hotplug_slot *bss_hotplug_slot, 
+						int device_num, int action);
+extern int sn_glue_slot_enable(struct hotplug_slot *bss_hotplug_slot, 
+							int device_num);
+extern u8 sn_glue_power_status_get(struct hotplug_slot *bss_hotplug_slot);
+
+/* functions to determine hp'ability of slots and busses */
+extern int sn_glue_pci_bus_valid(struct pci_bus *pci_bus);
+extern int sn_glue_pci_slot_valid(struct pci_bus *pci_bus, int device);
+
+/* memory alllocation and free */
+extern int  sn_glue_hp_slot_private_alloc(
+			struct hotplug_slot *bss_hotplug_slot, 
+			struct pci_bus *pci_bus, int device);
+extern struct hotplug_slot * sn_glue_hp_destroy(void);
+extern void sn_glue_pci_unfixup_slot(struct pci_dev *dev);
+extern void sn_glue_pci_fixup_slot(struct pci_dev *dev);
+extern void sn_glue_bus_alloc_irq_data(struct pci_dev *dev);
+extern void sn_glue_bus_free_irq_data(struct pci_dev *dev);
+#endif /* SN_PCI_HOTPLUG_H */
diff -Nru a/drivers/pci/hotplug/sn_pci_hotplug_core.c b/drivers/pci/hotplug/sn_pci_hotplug_core.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/hotplug/sn_pci_hotplug_core.c	2005-03-15 20:09:33 -05:00
@@ -0,0 +1,335 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive 
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ * This work was based on the 2.4/2.6 kernel development by Dick Reigner.  Work
+ * to add BIOS PROM support was completed by Mike Habeck. 
+ *
+ * Current maintainer: Prarit Bhargava
+ */
+
+#include <linux/types.h>
+#include <asm/sn/addrs.h>
+
+#include <asm/sn/module.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+
+#include "../pci.h"
+#include "pci_hotplug.h"
+
+#include "sn_pci_hotplug.h"
+
+#define DRIVER_AUTHORS "SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)"
+#define DRIVER_DESC "SGI Altix Hot Plug PCI Controller Driver"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHORS);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+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_address          (struct hotplug_slot *slot, u32 *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 sn_hotplug_slot_ops = {
+	.owner                  = THIS_MODULE,
+	.enable_slot            = enable_slot,
+	.disable_slot           = disable_slot,
+	.set_attention_status   = NULL,
+	.get_power_status       = get_power_status,
+	.get_attention_status   = NULL,
+	.get_latch_status       = NULL,
+	.get_adapter_status     = NULL,
+	.get_address            = NULL,
+};
+
+static struct rw_semaphore sn_hotplug_lock;
+
+unsigned int __devinit pci_scan_child_bus(struct pci_bus *);
+
+/*
+ * enable_slot() - PCI hot-plug enable slot service routine.  Called for
+ * an insert operation.
+ */
+static int
+enable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pci_dev_wrapped wrapped_dev;
+	struct pci_bus_wrapped wrapped_bus;
+	struct pci_bus *new_bus = NULL;
+	struct pci_dev *dev;
+	int func, num_funcs;
+	int new_ppb = 0;
+	int rc = 0;
+
+	/* Serialize the Linux PCI infrastructure */
+	down_write(&sn_hotplug_lock);
+
+	/*Power-on and initialize the slot in the SN
+	  PCI infrastructure */
+	if ((rc = sn_glue_slot_enable(bss_hotplug_slot, 
+				slot->device_num)) != 0)
+		return rc;
+
+	/* This call actually adds the device's functions to 
+	   Linux's knowledge.  It does not traverse bridges in
+	   effort to find devices below the bridge.*/
+	num_funcs = pci_scan_slot(slot->pci_bus, 
+			PCI_DEVFN(slot->device_num+1, PCI_FUNC(0)));
+
+	if (!num_funcs) {
+			printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"Could not find a device in this slot\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, bss_hotplug_slot->name);
+		up_write(&sn_hotplug_lock);
+		return(-ENODEV);
+	}
+
+	memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+	memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+
+	/* Map SN resources for all functions on the card
+	   to the Linux PCI interface and tell the drivers
+	   about them.*/
+	for (func = 0; func < num_funcs;  func++) {
+		dev = pci_get_slot(slot->pci_bus, 
+			PCI_DEVFN(slot->device_num+1, PCI_FUNC(func)));
+
+		if (dev) {
+			wrapped_dev.dev = dev;
+			wrapped_bus.bus = dev->bus;
+
+			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+				unsigned char sec_bus;
+				pci_read_config_byte(dev, PCI_SECONDARY_BUS, 
+							&sec_bus);
+
+				new_bus = pci_add_new_bus(dev->bus, dev, 
+								sec_bus);
+
+				pci_scan_child_bus(new_bus);
+
+				new_ppb = 1;
+			}
+
+			sn_glue_bus_alloc_irq_data(dev);
+
+			pci_dev_put(dev);
+
+		} /* if (dev) */
+	} /* for (func = 0; func < num_funcs;  func++) */
+
+	/* Call the driver for the new device */
+	pci_bus_add_devices(slot->pci_bus);
+
+	/* Call the drivers for the new devices subordinate to PPB */
+	if (new_ppb)
+		pci_bus_add_devices(new_bus);
+
+	/* Release the bus lock */
+	up_write(&sn_hotplug_lock);
+
+	if (rc == 0) 
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+		"Insert operation successful\n"
+		"End PCI Hot-Plug Message for -> %s\n", 
+		bss_hotplug_slot->name, bss_hotplug_slot->name);
+	else
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+		"Insert operation failed, rc = %d\n"
+		"End PCI Hot-Plug Message for -> %s\n", 
+		bss_hotplug_slot->name, rc, bss_hotplug_slot->name);
+
+	return(rc);
+}
+
+
+
+/*
+ * disable_slot() - PCI hot-plug disable slot service routine.
+ * Called for a remove operation.
+ */
+static int
+disable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pci_dev *dev;
+	int func;
+	int rc = 0;
+
+	/* Acquire update access to the bus */
+	down_write(&sn_hotplug_lock);
+
+	/* is it okay to bring this slot down? */
+	if ((rc = sn_glue_slot_disable(bss_hotplug_slot, slot->device_num, 
+					PCI_REQ_SLOT_ELIGIBLE)) != 0)
+		goto leaving;
+
+	/* Free the SN resources assigned to the Linux device.*/
+	for (func = 0; func < 8;  func++) {
+		dev = pci_get_slot(slot->pci_bus, 
+			PCI_DEVFN(slot->device_num+1, PCI_FUNC(func)));
+
+		if (dev) {
+			sn_glue_bus_free_irq_data(dev);
+			pci_remove_bus_device(dev);
+		}
+	}
+
+	rc = sn_glue_slot_disable(bss_hotplug_slot, slot->device_num, 
+					PCI_REQ_SLOT_DISABLE);
+
+	/* Release the bus lock */
+leaving:
+	up_write(&sn_hotplug_lock);
+
+	return(rc);
+}
+
+
+/*
+ * get_power_status() - PCI hot-plug get slot power status service routine.
+ */
+static int
+get_power_status(struct hotplug_slot *bss_hotplug_slot, u8 *value)
+{
+	down_read(&sn_hotplug_lock);
+	*value = sn_glue_power_status_get(bss_hotplug_slot);
+	up_read(&sn_hotplug_lock);
+
+	return(0);
+}
+
+/*
+ * sn_hotplug_slot_register() - register slots on a bus that support the PCI
+ * hot-plug feature with the PCI hotplug core
+ */
+int
+sn_hotplug_slot_register(struct pci_bus *pci_bus)
+{
+	int first_device, last_device;
+	int device;
+	struct hotplug_slot *bss_hotplug_slot;
+	int rc = 0;
+
+	if (sn_glue_pci_bus_valid(pci_bus))
+	{
+		printk("%s: not a valid hotplug bus\n", __FUNCTION__);
+		return 0;
+	}
+		printk("%s: valid hotplug bus\n", __FUNCTION__);
+
+	first_device = 0;
+	last_device = 1;
+
+	for (device = 0; device < 4; device++) {
+
+		if (sn_glue_pci_slot_valid(pci_bus, device))
+			continue; 
+
+		bss_hotplug_slot = (struct hotplug_slot *) 
+					kmalloc(sizeof(struct hotplug_slot), 
+						GFP_KERNEL);
+		if (!bss_hotplug_slot) {
+			rc = -ENOMEM;
+			continue;
+		}
+		memset(bss_hotplug_slot, 0, sizeof(struct hotplug_slot));
+
+		bss_hotplug_slot->info = (struct hotplug_slot_info *) 
+				       kmalloc(sizeof(struct hotplug_slot_info),
+						GFP_KERNEL);
+		if (!bss_hotplug_slot->info) {
+			kfree(bss_hotplug_slot);
+			rc = -ENOMEM;
+			continue; 
+		}
+		memset(bss_hotplug_slot->info, 0, 
+					sizeof(struct hotplug_slot_info));
+
+		if (sn_glue_hp_slot_private_alloc(bss_hotplug_slot, 
+							pci_bus, device))
+		{
+			kfree(bss_hotplug_slot->info);
+			kfree(bss_hotplug_slot);
+			rc = -ENOMEM;
+			continue; 
+		}
+
+		bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
+
+		pci_hp_register(bss_hotplug_slot);
+
+	} /*for (device = 0; device < 4; device++)*/
+
+	return(rc);
+}
+
+/* sn_hotplug_slot_deregister - deregister pci_bus from hotplug subsystem */
+void
+sn_hotplug_slot_deregister(struct hotplug_slot *bss_hotplug_slot)
+{
+	pci_hp_deregister(bss_hotplug_slot);
+
+	if (bss_hotplug_slot->info)
+		kfree(bss_hotplug_slot->info);
+
+	kfree(bss_hotplug_slot);
+}
+
+static int 
+sn_pci_hotplug_init(void)
+{
+	struct list_head *ln;
+	struct pci_bus *pci_bus;
+
+	init_rwsem(&sn_hotplug_lock);
+
+	if (sn_glue_init())
+	{
+		printk(KERN_ERR "%s: internal data structures init fail\n",
+				__FILE__);
+		return -1;
+	}
+
+	for( ln = pci_root_buses.next; ln != &pci_root_buses; ln = ln->next) {
+
+		pci_bus = pci_bus_b(ln);
+		if (!pci_bus->sysdata)
+			continue;
+
+		sn_hotplug_slot_register(pci_bus);
+	}
+
+	return 0;
+}
+
+static void sn_pci_hotplug_exit(void)
+{
+	struct hotplug_slot *bss_hotplug_slot = NULL;
+
+	while ((bss_hotplug_slot = sn_glue_hp_destroy()))
+	{
+		pci_hp_deregister(bss_hotplug_slot);
+		bss_hotplug_slot = NULL;
+	}
+
+	sn_glue_exit();
+}
+
+module_init(sn_pci_hotplug_init);
+module_exit(sn_pci_hotplug_exit);
+
diff -Nru a/drivers/pci/hotplug/sn_pci_hotplug_glue.c b/drivers/pci/hotplug/sn_pci_hotplug_glue.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/hotplug/sn_pci_hotplug_glue.c	2005-03-15 20:09:33 -05:00
@@ -0,0 +1,418 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive 
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <asm/sn/types.h>
+#include <asm/sn/addrs.h>
+
+#include "pci/pcidev.h"
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcibr_provider.h"
+
+#include <asm/sn/l1.h>
+#include <asm/sn/module.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+
+#include "../pci.h"
+#include "pci_hotplug.h"
+
+#include "sn_pci_hotplug.h"
+
+#define PCIIO_ASIC_TYPE_TIOCA   4
+
+#define PCI_SLOT_ALREADY_UP       2     /* slot already up */
+#define PCI_SLOT_ALREADY_DOWN     3     /* slot already down */
+#define PCI_L1_ERR                7     /* L1 console command error */
+#define PCI_EMPTY_33MHZ          15     /* empty 33 MHz bus */
+
+#define PCI_L1_QSIZE                128      /* our L1 message buffer size */
+
+/* internal list head */
+struct list_head sn_glue_hp_list;
+
+/* The list of SN hotplug_slots.  The list is maintained
+   within the private pointer */
+
+struct pcibr_slot_enable_resp_s {
+    int                     resp_sub_errno;
+    char                    resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+struct pcibr_slot_disable_resp_s {
+    int                     resp_sub_errno;
+    char                    resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+/*from arch/ia64/sn/kernel/io_init.c: */
+extern void sn_pci_fixup_slot(struct pci_dev *dev);
+extern void sn_pci_unfixup_slot(struct pci_dev *dev);
+
+int
+sn_glue_pci_slot_valid(struct pci_bus *pci_bus, int device)
+{
+	struct pcibus_info *pcibus_info;
+	int bricktype;
+	int bus_num;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(pci_bus);
+
+	/* Check to see if this is a valid slot on 'pci_bus' */
+	if (!(pcibus_info->pbi_valid_devices & (1 << device)))
+		return -EPERM;
+
+	bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
+	bus_num = pcibus_info->pbi_buscommon.bs_persist_busnum & 0xf;
+
+	/* Do not allow hotplug operations on base I/O cards */
+	if ((bricktype == L1_BRICKTYPE_IX || 
+		bricktype == L1_BRICKTYPE_IA) &&
+		(bus_num == 1 && device != 1)) 
+			return -EPERM;
+	return 0;
+}
+
+int
+sn_glue_pci_bus_valid(struct pci_bus *pci_bus)
+{
+	struct pcibus_info *pcibus_info;
+	int asic_type;
+	int bricktype;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(pci_bus);
+
+	/* Don't register slots hanging off the TIOCA bus */
+	asic_type = pcibus_info->pbi_buscommon.bs_asic_type;
+	if (asic_type == PCIIO_ASIC_TYPE_TIOCA)
+	       return -1;
+
+	bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
+
+	/* Only register slots in I/O Bricks that support hotplug */
+	switch (bricktype) {
+		case L1_BRICKTYPE_IX:
+		case L1_BRICKTYPE_PX:
+		case L1_BRICKTYPE_IA:
+		case L1_BRICKTYPE_PA:
+			return 0;
+			break;
+		default:
+			return -EPERM;
+			break;
+	}
+
+	return -EIO;
+}
+
+int 
+sn_glue_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, struct pci_bus *pci_bus, int device)
+{
+	struct pcibus_info *pcibus_info;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(pci_bus);
+
+	bss_hotplug_slot->private = (struct slot *) 
+					kmalloc(sizeof(struct slot), 
+						GFP_KERNEL);
+	if (!bss_hotplug_slot->private) {
+		return -ENOMEM;
+	}
+	memset(bss_hotplug_slot->private, 0, sizeof(struct slot));
+
+	bss_hotplug_slot->name = (char *) kmalloc (33, GFP_KERNEL);
+	if (!bss_hotplug_slot->name) {
+		kfree(bss_hotplug_slot->private);
+		return -ENOMEM;
+	}
+
+	((struct slot *)bss_hotplug_slot->private)->device_num = device;
+	((struct slot *)bss_hotplug_slot->private)->pci_bus = pci_bus;
+
+	sprintf(bss_hotplug_slot->name, "m_%c%c%c%c%.2d_b_%d_s_%d", 
+	'0'+ RACK_GET_CLASS(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+	'0'+ RACK_GET_GROUP(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+	'0'+ RACK_GET_NUM(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+	MODULE_GET_BTCHAR(pcibus_info->pbi_moduleid),
+	MODULE_GET_BPOS(pcibus_info->pbi_moduleid), 
+	((int)pcibus_info->pbi_buscommon.bs_persist_busnum) & 0xf, device + 1);
+
+printk("%s: adding %s [%p]\n", __FUNCTION__, bss_hotplug_slot->name, bss_hotplug_slot);
+	((struct slot *)bss_hotplug_slot->private)->hotplug_slot = 
+							bss_hotplug_slot;
+	list_add(&((struct slot *)bss_hotplug_slot->private)->hp_list, 
+			&sn_glue_hp_list);
+
+	return 0;
+}
+
+void
+sn_glue_hp_slot_private_free(struct hotplug_slot *bss_hotplug_slot)
+{
+	if (bss_hotplug_slot->name)
+		kfree(bss_hotplug_slot->name);
+
+	if (bss_hotplug_slot->private)
+		kfree(bss_hotplug_slot->private);
+}
+
+struct hotplug_slot * sn_glue_hp_destroy(void)
+{
+	struct slot *slot;
+	struct list_head *list;
+	struct list_head *next;
+	struct hotplug_slot *bss_hotplug_slot = NULL;
+
+	list_for_each_safe( list, next, &sn_glue_hp_list)
+	{
+		slot = list_entry(list, struct slot, hp_list);
+
+		bss_hotplug_slot = slot->hotplug_slot;
+		printk("%s: cleaning %s [%p]\n", __FUNCTION__, bss_hotplug_slot->name, bss_hotplug_slot);
+		sn_glue_hp_slot_private_free(bss_hotplug_slot);
+		list_del(&((struct slot *)bss_hotplug_slot->private)->hp_list);
+		break;
+	}
+
+	return bss_hotplug_slot;
+}
+
+/* This function recursively sets up the sn_irq_info structs */
+void sn_glue_bus_alloc_irq_data(struct pci_dev *dev)
+{
+	struct list_head *node = NULL;
+	struct pci_bus *subordinate_bus = NULL;
+	struct pci_dev *child = NULL;
+
+	if (dev->subordinate) {
+		subordinate_bus = dev->subordinate;
+		list_for_each(node, &subordinate_bus->devices) {
+			child = list_entry(node, struct pci_dev, bus_list); 
+			sn_glue_bus_free_irq_data(child);
+		}
+	}
+
+	/* Increment the dev reference count so pci_remove_bus_device() does
+	   not free the pci_dev structure;  it is needed by unfixup. */
+	pci_dev_get(dev);
+
+	sn_glue_pci_fixup_slot(dev);
+}
+
+/* This function recursively cleans up sn_irq_info structs */
+void sn_glue_bus_free_irq_data(struct pci_dev *dev)
+{
+	struct list_head *node = NULL;
+	struct pci_bus *subordinate_bus = NULL;
+	struct pci_dev *child = NULL;
+
+	if (dev->subordinate) {
+		subordinate_bus = dev->subordinate;
+		list_for_each(node, &subordinate_bus->devices) {
+			child = list_entry(node, struct pci_dev, bus_list); 
+			sn_glue_bus_free_irq_data(child);
+		}
+	}
+
+	/* Decrement dev reference count so pci_dev structure is freed */
+	pci_dev_put(dev);
+
+	sn_glue_pci_unfixup_slot(dev);
+}
+
+u8
+sn_glue_power_status_get(struct hotplug_slot *bss_hotplug_slot)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	u8 retval;
+	
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(slot->pci_bus);
+	retval = pcibus_info->pbi_enabled_devices & (1 << slot->device_num);
+
+	return retval;
+}
+
+void
+sn_glue_slot_mark_enable(struct hotplug_slot *bss_hotplug_slot, int device_num)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(slot->pci_bus);
+	pcibus_info->pbi_enabled_devices |= (1 << device_num);
+}
+
+void
+sn_glue_slot_mark_disable(struct hotplug_slot *bss_hotplug_slot, int device_num)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(slot->pci_bus);
+	pcibus_info->pbi_enabled_devices &= ~(1 << device_num);
+}
+
+int
+sn_glue_slot_enable(struct hotplug_slot *bss_hotplug_slot, int device_num)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	struct pcibr_slot_enable_resp_s resp;
+	int rc = 0;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(slot->pci_bus);
+
+
+	/*Power-on and initialize the slot in the SN
+	  PCI infrastructure */
+	rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp);
+
+	if (rc == PCI_SLOT_ALREADY_UP) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug insert failed because the slot is\n"
+			"already powered-up and active\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, bss_hotplug_slot->name); 
+		return(0);
+	}
+
+	if (rc == PCI_L1_ERR) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug insert failed because of L1 hardware\n"
+			"controller error %d;  the L1 message is:\n%s\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, resp.resp_sub_errno,
+			resp.resp_l1_msg, bss_hotplug_slot->name);
+		return(-EPERM);
+	}
+
+	if (rc) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug insert failed with error %d and "
+			"sub-error %d\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, rc,
+			resp.resp_sub_errno, bss_hotplug_slot->name);
+		return(-EIO);
+	}
+
+	sn_glue_slot_mark_enable(bss_hotplug_slot, device_num);
+
+	return 0;
+}
+
+int 
+sn_glue_slot_disable(struct hotplug_slot *bss_hotplug_slot, int device_num, int action)
+{
+	struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+	struct pcibus_info *pcibus_info;
+	struct pcibr_slot_disable_resp_s resp;
+	int rc;
+
+	pcibus_info = (struct pcibus_info *)SN_PCIBUS_BUSSOFT(slot->pci_bus);
+
+	/* is it okay to bring this slot down? */
+	rc = sal_pcibr_slot_disable(pcibus_info, device_num, 
+					action, &resp);
+
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_SLOT_ALREADY_DOWN)) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug remove failed because the slot is\n"
+			"already powered-down and inactive\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, bss_hotplug_slot->name);
+		return(-ENODEV);
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug remove failed because the last card\n"
+			"cannot be removed from a bus running at 33MHz\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, bss_hotplug_slot->name);
+		return -EPERM;
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug remove failed because of L1 hardware\n"
+			"controller error %d;  the L1 message is:\n%s\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, resp.resp_sub_errno,
+			resp.resp_l1_msg, bss_hotplug_slot->name);
+		return -EPERM;
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc)) {
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+			"PCI hot-plug remove failed with error %d and "
+			"sub-error %d\n"
+			"End PCI Hot-Plug Message for -> %s\n", 
+			bss_hotplug_slot->name, rc,
+			resp.resp_sub_errno, bss_hotplug_slot->name);
+		return(-EIO);
+	}
+
+	if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc)) {
+		return 0;
+	}
+
+	if ((action == PCI_REQ_SLOT_DISABLE) && (rc == 0))
+	{
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+		"Remove operation successful\n"
+		"End PCI Hot-Plug Message for -> %s\n", 
+		bss_hotplug_slot->name, bss_hotplug_slot->name);
+		sn_glue_slot_mark_disable(bss_hotplug_slot, device_num);
+		return rc;
+	}
+
+	if ((action == PCI_REQ_SLOT_DISABLE) && (rc))
+	{
+		printk("Begin PCI Hot-Plug Message for -> %s\n"
+		"Remove operation failed, rc = %d\n"
+		"End PCI Hot-Plug Message for -> %s\n", 
+		bss_hotplug_slot->name, rc, bss_hotplug_slot->name);
+		return rc;
+	}
+
+	return rc;
+}
+
+void
+sn_glue_pci_unfixup_slot(struct pci_dev *dev)
+{
+	sn_pci_unfixup_slot(dev);
+}
+
+void
+sn_glue_pci_fixup_slot(struct pci_dev *dev)
+{
+	sn_pci_fixup_slot(dev);
+}
+
+
+
+int
+sn_glue_init(void)
+{
+	INIT_LIST_HEAD(&sn_glue_hp_list);
+
+	return 0;
+}
+
+void 
+sn_glue_exit(void)
+{
+	if (!list_empty(&sn_glue_hp_list))
+		printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
+}

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

end of thread, other threads:[~2005-03-16 12:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-15 20:20 [PATCH/RFC]: SGI Altix Hotplug Driver Prarit Bhargava
2005-03-16  4:34 ` Randy.Dunlap
2005-03-16 12:36 ` Prarit Bhargava

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).