Index: arch/ia64/sn/kernel/io_init.c =================================================================== RCS file: /usr/local/src/cvsroot/bk/linux-2.5/arch/ia64/sn/kernel/io_init.c,v retrieving revision 1.1.1.1 diff -u -1 -0 -r1.1.1.1 io_init.c --- arch/ia64/sn/kernel/io_init.c 16 Mar 2005 18:48:35 -0000 1.1.1.1 +++ arch/ia64/sn/kernel/io_init.c 18 Mar 2005 12:42:27 -0000 @@ -1,31 +1,32 @@ /* * 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. */ #include #include -#include -#include #include -#include "pci/pcibus_provider_defs.h" -#include "pci/pcidev.h" -#include "pci/pcibr_provider.h" -#include "xtalk/xwidgetdev.h" #include -#include "xtalk/hubdev.h" #include +#include +#include +#include #include +#include +#include + +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" char master_baseio_wid; nasid_t master_nasid = INVALID_NASID; /* Partition Master */ struct slab_info { struct hubdev_info hubdev; }; struct brick { moduleid_t id; /* Module ID of this module */ @@ -186,21 +187,21 @@ } } /* * sn_pci_fixup_slot() - This routine sets up a slot's resources * consistent with the Linux PCI abstraction layer. Resources acquired * from our PCI provider include PIO maps to BAR space and interrupt * objects. */ -static void sn_pci_fixup_slot(struct pci_dev *dev) +void sn_pci_fixup_slot(struct pci_dev *dev) { int idx; int segment = 0; uint64_t size; struct sn_irq_info *sn_irq_info; struct pci_dev *host_pci_dev; int status = 0; dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); if (SN_PCIDEV_INFO(dev) <= 0) @@ -238,20 +239,26 @@ dev->resource[idx].parent = &ioport_resource; else dev->resource[idx].parent = &iomem_resource; } /* set up host bus linkages */ host_pci_dev = pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32, SN_PCIDEV_INFO(dev)-> pdi_slot_host_handle & 0xffffffff); + + if (!host_pci_dev) { + host_pci_dev = pci_get_slot(dev->bus, + SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff); + } + SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info = SN_PCIDEV_INFO(host_pci_dev); SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev; SN_PCIDEV_INFO(dev)->pdi_pcibus_info = SN_PCIBUS_BUSSOFT(dev->bus); /* Only set up IRQ stuff if this device has a host bus context */ if (SN_PCIDEV_BUSSOFT(dev) && sn_irq_info->irq_irq) { SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info; dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq; sn_irq_fixup(dev, sn_irq_info); @@ -313,20 +320,29 @@ bus->sysdata = controller; PCI_CONTROLLER(bus)->platform_data = provider_soft; nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); cnode = nasid_to_cnodeid(nasid); hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); } +/* sn_pci_unfixup_slot - this routine cleans up the slot's + * resources which are allocated in sn_pci_fixup_slot + */ +void sn_pci_unfixup_slot(struct pci_dev *dev) +{ + sn_irq_unfixup(dev); + return; +} + /* * Ugly hack to get PCI setup until we have a proper ACPI namespace. */ #define PCI_BUSES_TO_SCAN 256 static int __init sn_pci_init(void) { int i = 0; struct pci_dev *pci_dev = NULL; @@ -402,10 +427,12 @@ { struct hubdev_info *hubdev; hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); return hubdev->hdi_geoid; } subsys_initcall(sn_pci_init); +EXPORT_SYMBOL(sn_pci_fixup_slot); +EXPORT_SYMBOL(sn_pci_unfixup_slot); Index: arch/ia64/sn/pci/pcibr/pcibr_provider.c =================================================================== RCS file: /usr/local/src/cvsroot/bk/linux-2.5/arch/ia64/sn/pci/pcibr/pcibr_provider.c,v retrieving revision 1.1.1.1 diff -u -1 -0 -r1.1.1.1 pcibr_provider.c --- arch/ia64/sn/pci/pcibr/pcibr_provider.c 16 Mar 2005 18:48:35 -0000 1.1.1.1 +++ arch/ia64/sn/pci/pcibr/pcibr_provider.c 18 Mar 2005 12:42:28 -0000 @@ -1,29 +1,64 @@ /* * 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) 2001-2004 Silicon Graphics, Inc. All rights reserved. */ -#include #include +#include #include -#include -#include "xtalk/xwidgetdev.h" +#include #include +#include +#include +#include +#include + #include "xtalk/hubdev.h" -#include "pci/pcibus_provider_defs.h" -#include "pci/pcidev.h" -#include "pci/pcibr_provider.h" -#include +#include "xtalk/xwidgetdev.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 = 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 = 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; uint64_t busnum; int segment; ret_stuff.status = 0; ret_stuff.v0 = 0; @@ -161,10 +196,13 @@ /* Change the device's IRQ */ pcireg_intr_addr_addr_set(pcibus_info, bit, xtalk_addr); /* Re-enable the device's IRQ */ pcireg_intr_enable_bit_set(pcibus_info, bit); pcibr_force_interrupt(sn_irq_info); } } + +EXPORT_SYMBOL(sal_pcibr_slot_enable); +EXPORT_SYMBOL(sal_pcibr_slot_disable); Index: drivers/pci/hotplug/Kconfig =================================================================== RCS file: /usr/local/src/cvsroot/bk/linux-2.5/drivers/pci/hotplug/Kconfig,v retrieving revision 1.1.1.1 diff -u -1 -0 -r1.1.1.1 Kconfig --- drivers/pci/hotplug/Kconfig 16 Mar 2005 18:48:24 -0000 1.1.1.1 +++ drivers/pci/hotplug/Kconfig 18 Mar 2005 12:42:31 -0000 @@ -180,18 +180,19 @@ Say Y here if your system supports Dynamic Logical Partitioning for I/O slots. To compile this driver as a module, choose M here: the module will be called rpadlpar_io. When in doubt, say N. config HOTPLUG_PCI_SGI tristate "SGI PCI Hotplug Support" - depends on HOTPLUG_PCI && IA64_SGI_SN2 + depends on HOTPLUG_PCI && (IA64_SGI_SN2 || IA64_GENERIC) help - Say Y here if you have an SGI IA64 Altix system. + Say Y here if you want to use the SGI Altix Hotplug + Driver for PCI devices. When in doubt, say N. endmenu Index: drivers/pci/hotplug/Makefile =================================================================== RCS file: /usr/local/src/cvsroot/bk/linux-2.5/drivers/pci/hotplug/Makefile,v retrieving revision 1.1.1.1 diff -u -1 -0 -r1.1.1.1 Makefile --- drivers/pci/hotplug/Makefile 16 Mar 2005 18:48:24 -0000 1.1.1.1 +++ drivers/pci/hotplug/Makefile 18 Mar 2005 12:42:32 -0000 @@ -7,20 +7,21 @@ obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o +obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o pci_hotplug-objs := pci_hotplug_core.o ifdef CONFIG_HOTPLUG_PCI_CPCI pci_hotplug-objs += cpci_hotplug_core.o \ cpci_hotplug_pci.o endif cpqphp-objs := cpqphp_core.o \ cpqphp_ctrl.o \ @@ -65,10 +66,14 @@ shpchp_hpc.o ifdef CONFIG_ACPI_BUS shpchp-objs += shpchprm_acpi.o else ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY shpchp-objs += shpchprm_legacy.o else shpchp-objs += shpchprm_nonacpi.o endif endif + +sgi_hotplug-objs := sn_pci_hotplug_core.o \ + sn_pci_hotplug_glue.o + Index: drivers/pci/hotplug/sn_pci_hotplug.h =================================================================== RCS file: drivers/pci/hotplug/sn_pci_hotplug.h diff -N drivers/pci/hotplug/sn_pci_hotplug.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ drivers/pci/hotplug/sn_pci_hotplug.h 18 Mar 2005 12:42:33 -0000 @@ -0,0 +1,47 @@ +#ifndef SN_PCI_HOTPLUG_H +#define SN_PCI_HOTPLUG_H + +#define SLOT_TO_PCI_DEV(slot) \ + (slot->pci_bus->self) + +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 */ Index: drivers/pci/hotplug/sn_pci_hotplug_core.c =================================================================== RCS file: drivers/pci/hotplug/sn_pci_hotplug_core.c diff -N drivers/pci/hotplug/sn_pci_hotplug_core.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ drivers/pci/hotplug/sn_pci_hotplug_core.c 18 Mar 2005 12:42:33 -0000 @@ -0,0 +1,304 @@ +/* + * 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-2005 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, prarit@sgi.com + */ + +#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); + +#define NUM_SGI_HP_SLOTS 4 +static int enable_slot (struct hotplug_slot *slot); +static int disable_slot (struct hotplug_slot *slot); +static int get_power_status (struct hotplug_slot *slot, u8 *value); + +static struct hotplug_slot_ops sn_hotplug_slot_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .get_power_status = get_power_status, +}; + +static DECLARE_RWSEM(sn_hotplug_lock); + +/* + * 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_bus *new_bus = NULL; + struct pci_dev *dev; + int func, num_funcs; + int new_ppb = 0; + int rc; + + /* Serialize the Linux PCI infrastructure */ + down_write(&sn_hotplug_lock); + + /*Power-on and initialize the slot in the SN + PCI infrastructure */ + rc = sn_glue_slot_enable(bss_hotplug_slot, slot->device_num); + if (rc) { + up_write(&sn_hotplug_lock); + 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) { + dev_dbg(SLOT_TO_PCI_DEV(slot), " no device in slot\n"); + up_write(&sn_hotplug_lock); + return -ENODEV; + } + + /* 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) { + 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); + + } + } + + /* 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) + dev_dbg(SLOT_TO_PCI_DEV(slot), + " insert operation successful\n"); + else + dev_dbg(SLOT_TO_PCI_DEV(slot), + " insert operation failed rc = %d\n", rc); + + 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; + + /* Acquire update access to the bus */ + down_write(&sn_hotplug_lock); + + /* is it okay to bring this slot down? */ + rc = sn_glue_slot_disable(bss_hotplug_slot, slot->device_num, + PCI_REQ_SLOT_ELIGIBLE); + + if (rc) + 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)) { + dev_dbg(pci_bus->self, " not a valid hotplug bus\n"); + return rc; + } + + dev_dbg(pci_bus->self, " valid hotplug bus\n"); + + first_device = 0; + last_device = 1; + + for (device = 0; device < NUM_SGI_HP_SLOTS; device++) { + + if (sn_glue_pci_slot_valid(pci_bus, device)) + continue; + + bss_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 = + 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; + + 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; + + while ((bss_hotplug_slot = sn_glue_hp_destroy())) + pci_hp_deregister(bss_hotplug_slot); + + sn_glue_exit(); +} + +module_init(sn_pci_hotplug_init); +module_exit(sn_pci_hotplug_exit); + Index: drivers/pci/hotplug/sn_pci_hotplug_glue.c =================================================================== RCS file: drivers/pci/hotplug/sn_pci_hotplug_glue.c diff -N drivers/pci/hotplug/sn_pci_hotplug_glue.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ drivers/pci/hotplug/sn_pci_hotplug_glue.c 18 Mar 2005 12:42:34 -0000 @@ -0,0 +1,383 @@ +/* + * 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-2005 Silicon Graphics, Inc. All rights + * reserved. + */ + +#include +#include +#include +#include +#include + +#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 = 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 = 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); + + ((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; + 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; + + 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) { + dev_dbg(SLOT_TO_PCI_DEV(slot), " is already active\n"); + return 0; + } + + if (rc == PCI_L1_ERR) { + dev_dbg(SLOT_TO_PCI_DEV(slot), + " L1 failure %d with message \n%s\n", + resp.resp_sub_errno, resp.resp_l1_msg); + return -EPERM; + } + + if (rc) { + dev_dbg(SLOT_TO_PCI_DEV(slot), + " insert failed with error %d sub-error %d\n", + rc, resp.resp_sub_errno); + 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)) { + dev_dbg(SLOT_TO_PCI_DEV(slot), "Slot %s already inactive\n"); + return -ENODEV; + } + + if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) { + dev_dbg(SLOT_TO_PCI_DEV(slot), + " Cannot remove last 33MHz card\n"); + return -EPERM; + } + + if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) { + dev_dbg(SLOT_TO_PCI_DEV(slot), + " L1 failure %d with message \n%s\n", + resp.resp_sub_errno, resp.resp_l1_msg); + return -EPERM; + } + + if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc)) { + dev_dbg(SLOT_TO_PCI_DEV(slot), + " remove failed with error %d sub-error %d\n", + rc, resp.resp_sub_errno); + return -EIO; + } + + if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc)) { + return 0; + } + + if ((action == PCI_REQ_SLOT_DISABLE) && (rc == 0)) + { + dev_dbg(SLOT_TO_PCI_DEV(slot), " remove successful\n"); + return rc; + } + + if ((action == PCI_REQ_SLOT_DISABLE) && (rc)) + { + dev_dbg(SLOT_TO_PCI_DEV(slot)," remove failed rc = %d\n", rc); + 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__); +}