# 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 #include #include -#include #include +#include #include #include #include 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 +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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +#include "pci/pcidev.h" +#include "pci/pcibus_provider_defs.h" +#include "pci/pcibr_provider.h" + +#include +#include +#include +#include +#include +#include +#include + +#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__); +}