2005-07-07 02:29:53 +04:00
/*
* 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 .
*
2006-03-08 22:21:34 +03:00
* Copyright ( C ) 2005 - 2006 Silicon Graphics , Inc . All rights reserved .
2005-07-07 02:29:53 +04:00
*
* 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 .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
2006-10-14 07:05:19 +04:00
# include <linux/pci_hotplug.h>
2005-07-07 02:29:53 +04:00
# include <linux/proc_fs.h>
# include <linux/types.h>
2006-03-26 13:37:14 +04:00
# include <linux/mutex.h>
2005-07-07 02:29:53 +04:00
# include <asm/sn/addrs.h>
2006-04-04 17:26:46 +04:00
# include <asm/sn/geo.h>
2005-07-07 02:29:53 +04:00
# include <asm/sn/l1.h>
# include <asm/sn/module.h>
# include <asm/sn/pcibr_provider.h>
# include <asm/sn/pcibus_provider_defs.h>
# include <asm/sn/pcidev.h>
2006-04-04 17:26:46 +04:00
# include <asm/sn/sn_feature_sets.h>
2005-07-07 02:29:53 +04:00
# include <asm/sn/sn_sal.h>
# include <asm/sn/types.h>
# include "../pci.h"
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com) " ) ;
MODULE_DESCRIPTION ( " SGI Altix Hot Plug PCI Controller Driver " ) ;
2005-08-12 18:13:34 +04:00
# 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 */
# define SN_MAX_HP_SLOTS 32 /* max hotplug slots */
# define SGI_HOTPLUG_PROM_REV 0x0430 /* Min. required PROM version */
# define SN_SLOT_NAME_SIZE 33 /* size of name string */
2005-07-07 02:29:53 +04:00
/* internal list head */
static struct list_head sn_hp_list ;
/* 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 ;
2005-08-12 18:13:34 +04:00
char physical_path [ SN_SLOT_NAME_SIZE ] ;
2005-07-07 02:29:53 +04:00
} ;
struct pcibr_slot_enable_resp {
int resp_sub_errno ;
char resp_l1_msg [ PCI_L1_QSIZE + 1 ] ;
} ;
struct pcibr_slot_disable_resp {
int resp_sub_errno ;
char resp_l1_msg [ PCI_L1_QSIZE + 1 ] ;
} ;
enum sn_pci_req_e {
PCI_REQ_SLOT_ELIGIBLE ,
PCI_REQ_SLOT_DISABLE
} ;
static int enable_slot ( struct hotplug_slot * slot ) ;
static int disable_slot ( struct hotplug_slot * slot ) ;
2005-08-12 18:13:34 +04:00
static inline int get_power_status ( struct hotplug_slot * slot , u8 * value ) ;
2005-07-07 02:29:53 +04:00
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 ,
} ;
2006-03-26 13:37:14 +04:00
static DEFINE_MUTEX ( sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
2005-08-12 18:13:34 +04:00
static ssize_t path_show ( struct hotplug_slot * bss_hotplug_slot ,
char * buf )
{
int retval = - ENOENT ;
struct slot * slot = bss_hotplug_slot - > private ;
if ( ! slot )
return retval ;
retval = sprintf ( buf , " %s \n " , slot - > physical_path ) ;
return retval ;
}
static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO ( path ) ;
2005-07-07 02:29:53 +04:00
static int sn_pci_slot_valid ( struct pci_bus * pci_bus , int device )
{
struct pcibus_info * pcibus_info ;
2006-04-04 17:26:46 +04:00
u16 busnum , segment , ioboard_type ;
2005-07-07 02:29:53 +04:00
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( pci_bus ) ;
/* Check to see if this is a valid slot on 'pci_bus' */
if ( ! ( pcibus_info - > pbi_valid_devices & ( 1 < < device ) ) )
return - EPERM ;
2006-04-04 17:26:46 +04:00
ioboard_type = sn_ioboard_to_pci_bus ( pci_bus ) ;
busnum = pcibus_info - > pbi_buscommon . bs_persist_busnum ;
segment = pci_domain_nr ( pci_bus ) & 0xf ;
2005-07-07 02:29:53 +04:00
/* Do not allow hotplug operations on base I/O cards */
2006-04-04 17:26:46 +04:00
if ( ( ioboard_type = = L1_BRICKTYPE_IX | |
ioboard_type = = L1_BRICKTYPE_IA ) & &
( segment = = 1 & & busnum = = 0 & & device ! = 1 ) )
2005-07-07 02:29:53 +04:00
return - EPERM ;
return 1 ;
}
static int sn_pci_bus_valid ( struct pci_bus * pci_bus )
{
struct pcibus_info * pcibus_info ;
2006-04-04 17:26:46 +04:00
u32 asic_type ;
u16 ioboard_type ;
2005-07-07 02:29:53 +04:00
/* Don't register slots hanging off the TIOCA bus */
2006-04-04 17:26:46 +04:00
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( pci_bus ) ;
2005-07-07 02:29:53 +04:00
asic_type = pcibus_info - > pbi_buscommon . bs_asic_type ;
if ( asic_type = = PCIIO_ASIC_TYPE_TIOCA )
return - EPERM ;
/* Only register slots in I/O Bricks that support hotplug */
2006-04-04 17:26:46 +04:00
ioboard_type = sn_ioboard_to_pci_bus ( pci_bus ) ;
switch ( ioboard_type ) {
2005-08-12 18:13:34 +04:00
case L1_BRICKTYPE_IX :
case L1_BRICKTYPE_PX :
case L1_BRICKTYPE_IA :
case L1_BRICKTYPE_PA :
2006-04-04 17:26:46 +04:00
case L1_BOARDTYPE_PCIX3SLOT :
2005-08-12 18:13:34 +04:00
return 1 ;
break ;
default :
return - EPERM ;
break ;
2005-07-07 02:29:53 +04:00
}
return - EIO ;
}
static int sn_hp_slot_private_alloc ( struct hotplug_slot * bss_hotplug_slot ,
struct pci_bus * pci_bus , int device )
{
struct pcibus_info * pcibus_info ;
struct slot * slot ;
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( pci_bus ) ;
2005-09-22 11:48:11 +04:00
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
2005-08-12 18:13:34 +04:00
if ( ! slot )
2005-07-07 02:29:53 +04:00
return - ENOMEM ;
2005-08-12 18:13:34 +04:00
bss_hotplug_slot - > private = slot ;
2005-07-07 02:29:53 +04:00
2005-08-12 18:13:34 +04:00
bss_hotplug_slot - > name = kmalloc ( SN_SLOT_NAME_SIZE , GFP_KERNEL ) ;
2005-07-07 02:29:53 +04:00
if ( ! bss_hotplug_slot - > name ) {
kfree ( bss_hotplug_slot - > private ) ;
return - ENOMEM ;
}
slot - > device_num = device ;
slot - > pci_bus = pci_bus ;
2005-08-12 18:13:34 +04:00
sprintf ( bss_hotplug_slot - > name , " %04x:%02x:%02x " ,
pci_domain_nr ( pci_bus ) ,
2006-04-04 17:26:46 +04:00
( ( u16 ) pcibus_info - > pbi_buscommon . bs_persist_busnum ) ,
2005-08-12 18:13:34 +04:00
device + 1 ) ;
2006-04-04 17:26:46 +04:00
sn_generate_path ( pci_bus , slot - > physical_path ) ;
2005-07-07 02:29:53 +04:00
slot - > hotplug_slot = bss_hotplug_slot ;
list_add ( & slot - > hp_list , & sn_hp_list ) ;
return 0 ;
}
static struct hotplug_slot * sn_hp_destroy ( void )
{
struct slot * slot ;
struct hotplug_slot * bss_hotplug_slot = NULL ;
2005-08-12 18:13:34 +04:00
list_for_each_entry ( slot , & sn_hp_list , hp_list ) {
2005-07-07 02:29:53 +04:00
bss_hotplug_slot = slot - > hotplug_slot ;
list_del ( & ( ( struct slot * ) bss_hotplug_slot - > private ) - >
hp_list ) ;
2005-08-12 18:13:34 +04:00
sysfs_remove_file ( & bss_hotplug_slot - > kobj ,
& sn_slot_path_attr . attr ) ;
2005-07-07 02:29:53 +04:00
break ;
}
return bss_hotplug_slot ;
}
static void sn_bus_free_data ( struct pci_dev * dev )
{
struct pci_bus * subordinate_bus ;
struct pci_dev * child ;
/* Recursively clean up sn_irq_info structs */
if ( dev - > subordinate ) {
subordinate_bus = dev - > subordinate ;
2005-08-12 18:13:34 +04:00
list_for_each_entry ( child , & subordinate_bus - > devices , bus_list )
2005-07-07 02:29:53 +04:00
sn_bus_free_data ( child ) ;
}
2006-03-08 22:21:34 +03:00
/*
* Some drivers may use dma accesses during the
* driver remove function . We release the sysdata
* areas after the driver remove functions have
* been called .
*/
sn_bus_store_sysdata ( dev ) ;
2005-07-07 02:29:53 +04:00
sn_pci_unfixup_slot ( dev ) ;
}
static int sn_slot_enable ( struct hotplug_slot * bss_hotplug_slot ,
int device_num )
{
2005-08-12 18:13:34 +04:00
struct slot * slot = bss_hotplug_slot - > private ;
2005-07-07 02:29:53 +04:00
struct pcibus_info * pcibus_info ;
struct pcibr_slot_enable_resp resp ;
int rc ;
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( 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 - > pci_bus - > self , " is already active \n " ) ;
2005-08-12 18:13:34 +04:00
return 1 ; /* return 1 to user */
2005-07-07 02:29:53 +04:00
}
if ( rc = = PCI_L1_ERR ) {
dev_dbg ( slot - > pci_bus - > self ,
" L1 failure %d with message: %s " ,
resp . resp_sub_errno , resp . resp_l1_msg ) ;
return - EPERM ;
}
if ( rc ) {
dev_dbg ( slot - > pci_bus - > self ,
" insert failed with error %d sub-error %d \n " ,
rc , resp . resp_sub_errno ) ;
return - EIO ;
}
2005-08-12 18:13:34 +04:00
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( slot - > pci_bus ) ;
pcibus_info - > pbi_enabled_devices | = ( 1 < < device_num ) ;
2005-07-07 02:29:53 +04:00
return 0 ;
}
static int sn_slot_disable ( struct hotplug_slot * bss_hotplug_slot ,
int device_num , int action )
{
2005-08-12 18:13:34 +04:00
struct slot * slot = bss_hotplug_slot - > private ;
2005-07-07 02:29:53 +04:00
struct pcibus_info * pcibus_info ;
struct pcibr_slot_disable_resp resp ;
int rc ;
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( slot - > pci_bus ) ;
rc = sal_pcibr_slot_disable ( pcibus_info , device_num , action , & resp ) ;
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_ELIGIBLE ) & &
( rc = = PCI_SLOT_ALREADY_DOWN ) ) {
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self , " Slot %s already inactive \n " ) ;
2005-08-12 18:13:34 +04:00
return 1 ; /* return 1 to user */
2005-07-07 02:29:53 +04:00
}
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_ELIGIBLE ) & & ( rc = = PCI_EMPTY_33MHZ ) ) {
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self ,
" Cannot remove last 33MHz card \n " ) ;
return - EPERM ;
}
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_ELIGIBLE ) & & ( rc = = PCI_L1_ERR ) ) {
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self ,
" L1 failure %d with message \n %s \n " ,
resp . resp_sub_errno , resp . resp_l1_msg ) ;
return - EPERM ;
}
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_ELIGIBLE ) & & rc ) {
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self ,
" remove failed with error %d sub-error %d \n " ,
rc , resp . resp_sub_errno ) ;
return - EIO ;
}
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_ELIGIBLE ) & & ! rc )
2005-07-07 02:29:53 +04:00
return 0 ;
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_DISABLE ) & & ! rc ) {
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( slot - > pci_bus ) ;
pcibus_info - > pbi_enabled_devices & = ~ ( 1 < < device_num ) ;
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self , " remove successful \n " ) ;
return 0 ;
}
2005-08-12 18:13:34 +04:00
if ( ( action = = PCI_REQ_SLOT_DISABLE ) & & rc ) {
2005-07-07 02:29:53 +04:00
dev_dbg ( slot - > pci_bus - > self , " remove failed rc = %d \n " , rc ) ;
}
return rc ;
}
2006-10-05 01:49:35 +04:00
/*
* Power up and configure the slot via a SAL call to PROM .
* Scan slot ( and any children ) , do any platform specific fixup ,
* and find device driver .
*/
2005-07-07 02:29:53 +04:00
static int enable_slot ( struct hotplug_slot * bss_hotplug_slot )
{
2005-08-12 18:13:34 +04:00
struct slot * slot = bss_hotplug_slot - > private ;
2005-07-07 02:29:53 +04:00
struct pci_bus * new_bus = NULL ;
struct pci_dev * dev ;
int func , num_funcs ;
int new_ppb = 0 ;
int rc ;
2006-10-05 01:49:35 +04:00
void pcibios_fixup_device_resources ( struct pci_dev * ) ;
2005-07-07 02:29:53 +04:00
/* Serialize the Linux PCI infrastructure */
2006-03-26 13:37:14 +04:00
mutex_lock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
/*
* Power - on and initialize the slot in the SN
* PCI infrastructure .
*/
rc = sn_slot_enable ( bss_hotplug_slot , slot - > device_num ) ;
if ( rc ) {
2006-03-26 13:37:14 +04:00
mutex_unlock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
return rc ;
}
2005-08-12 18:13:34 +04:00
num_funcs = pci_scan_slot ( slot - > pci_bus ,
PCI_DEVFN ( slot - > device_num + 1 , 0 ) ) ;
2005-07-07 02:29:53 +04:00
if ( ! num_funcs ) {
dev_dbg ( slot - > pci_bus - > self , " no device in slot \n " ) ;
2006-03-26 13:37:14 +04:00
mutex_unlock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
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 ) {
2006-10-05 01:49:35 +04:00
/* Need to do slot fixup on PPB before fixup of children
* ( PPB ' s pcidev_info needs to be in pcidev_info list
* before child ' s SN_PCIDEV_INFO ( ) call to setup
* pdi_host_pcidev_info ) .
*/
pcibios_fixup_device_resources ( dev ) ;
sn_pci_fixup_slot ( dev ) ;
2005-07-07 02:29:53 +04:00
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 ;
}
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 ) ;
2006-03-26 13:37:14 +04:00
mutex_unlock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
if ( rc = = 0 )
dev_dbg ( slot - > pci_bus - > self ,
" insert operation successful \n " ) ;
else
dev_dbg ( slot - > pci_bus - > self ,
" insert operation failed rc = %d \n " , rc ) ;
return rc ;
}
static int disable_slot ( struct hotplug_slot * bss_hotplug_slot )
{
2005-08-12 18:13:34 +04:00
struct slot * slot = bss_hotplug_slot - > private ;
2005-07-07 02:29:53 +04:00
struct pci_dev * dev ;
int func ;
int rc ;
/* Acquire update access to the bus */
2006-03-26 13:37:14 +04:00
mutex_lock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
/* is it okay to bring this slot down? */
rc = sn_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 ,
2005-08-12 18:13:34 +04:00
PCI_DEVFN ( slot - > device_num + 1 ,
2005-07-07 02:29:53 +04:00
PCI_FUNC ( func ) ) ) ;
if ( dev ) {
sn_bus_free_data ( dev ) ;
pci_remove_bus_device ( dev ) ;
pci_dev_put ( dev ) ;
}
}
/* free the collected sysdata pointers */
sn_bus_free_sysdata ( ) ;
/* Deactivate slot */
rc = sn_slot_disable ( bss_hotplug_slot , slot - > device_num ,
PCI_REQ_SLOT_DISABLE ) ;
leaving :
/* Release the bus lock */
2006-03-26 13:37:14 +04:00
mutex_unlock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
return rc ;
}
2005-08-12 18:13:34 +04:00
static inline int get_power_status ( struct hotplug_slot * bss_hotplug_slot ,
u8 * value )
2005-07-07 02:29:53 +04:00
{
2005-08-12 18:13:34 +04:00
struct slot * slot = bss_hotplug_slot - > private ;
struct pcibus_info * pcibus_info ;
2006-05-06 18:01:59 +04:00
u32 power ;
2005-08-12 18:13:34 +04:00
pcibus_info = SN_PCIBUS_BUSSOFT_INFO ( slot - > pci_bus ) ;
2006-03-26 13:37:14 +04:00
mutex_lock ( & sn_hotplug_mutex ) ;
2006-05-06 18:01:59 +04:00
power = pcibus_info - > pbi_enabled_devices & ( 1 < < slot - > device_num ) ;
* value = power ? 1 : 0 ;
2006-03-26 13:37:14 +04:00
mutex_unlock ( & sn_hotplug_mutex ) ;
2005-07-07 02:29:53 +04:00
return 0 ;
}
static void sn_release_slot ( struct hotplug_slot * bss_hotplug_slot )
{
kfree ( bss_hotplug_slot - > info ) ;
kfree ( bss_hotplug_slot - > name ) ;
kfree ( bss_hotplug_slot - > private ) ;
kfree ( bss_hotplug_slot ) ;
}
static int sn_hotplug_slot_register ( struct pci_bus * pci_bus )
{
int device ;
struct hotplug_slot * bss_hotplug_slot ;
int rc = 0 ;
/*
* Currently only four devices are supported ,
* in the future there maybe more - - up to 32.
*/
for ( device = 0 ; device < SN_MAX_HP_SLOTS ; device + + ) {
if ( sn_pci_slot_valid ( pci_bus , device ) ! = 1 )
continue ;
2005-09-22 11:48:11 +04:00
bss_hotplug_slot = kzalloc ( sizeof ( * bss_hotplug_slot ) ,
2005-07-07 02:29:53 +04:00
GFP_KERNEL ) ;
if ( ! bss_hotplug_slot ) {
rc = - ENOMEM ;
goto alloc_err ;
}
bss_hotplug_slot - > info =
2005-09-22 11:48:11 +04:00
kzalloc ( sizeof ( struct hotplug_slot_info ) ,
2005-07-07 02:29:53 +04:00
GFP_KERNEL ) ;
if ( ! bss_hotplug_slot - > info ) {
rc = - ENOMEM ;
goto alloc_err ;
}
if ( sn_hp_slot_private_alloc ( bss_hotplug_slot ,
pci_bus , device ) ) {
rc = - ENOMEM ;
goto alloc_err ;
}
bss_hotplug_slot - > ops = & sn_hotplug_slot_ops ;
bss_hotplug_slot - > release = & sn_release_slot ;
rc = pci_hp_register ( bss_hotplug_slot ) ;
if ( rc )
goto register_err ;
2005-08-12 18:13:34 +04:00
rc = sysfs_create_file ( & bss_hotplug_slot - > kobj ,
& sn_slot_path_attr . attr ) ;
if ( rc )
goto register_err ;
2005-07-07 02:29:53 +04:00
}
dev_dbg ( pci_bus - > self , " Registered bus with hotplug \n " ) ;
return rc ;
register_err :
dev_dbg ( pci_bus - > self , " bus failed to register with err = %d \n " ,
rc ) ;
alloc_err :
if ( rc = = - ENOMEM )
dev_dbg ( pci_bus - > self , " Memory allocation error \n " ) ;
/* destroy THIS element */
if ( bss_hotplug_slot )
sn_release_slot ( bss_hotplug_slot ) ;
/* destroy anything else on the list */
while ( ( bss_hotplug_slot = sn_hp_destroy ( ) ) )
pci_hp_deregister ( bss_hotplug_slot ) ;
return rc ;
}
static int sn_pci_hotplug_init ( void )
{
struct pci_bus * pci_bus = NULL ;
int rc ;
int registered = 0 ;
2006-04-04 17:26:46 +04:00
if ( ! sn_prom_feature_available ( PRF_HOTPLUG_SUPPORT ) ) {
printk ( KERN_ERR " %s: PROM version does not support hotplug. \n " ,
2005-07-07 02:29:53 +04:00
__FUNCTION__ ) ;
return - EPERM ;
}
2005-08-12 18:13:34 +04:00
INIT_LIST_HEAD ( & sn_hp_list ) ;
2005-07-07 02:29:53 +04:00
while ( ( pci_bus = pci_find_next_bus ( pci_bus ) ) ) {
if ( ! pci_bus - > sysdata )
continue ;
rc = sn_pci_bus_valid ( pci_bus ) ;
if ( rc ! = 1 ) {
dev_dbg ( pci_bus - > self , " not a valid hotplug bus \n " ) ;
continue ;
}
dev_dbg ( pci_bus - > self , " valid hotplug bus \n " ) ;
rc = sn_hotplug_slot_register ( pci_bus ) ;
2005-08-12 18:13:34 +04:00
if ( ! rc ) {
2005-07-07 02:29:53 +04:00
registered = 1 ;
2005-08-12 18:13:34 +04:00
} else {
2005-07-07 02:29:53 +04:00
registered = 0 ;
break ;
}
}
return registered = = 1 ? 0 : - ENODEV ;
}
static void sn_pci_hotplug_exit ( void )
{
struct hotplug_slot * bss_hotplug_slot ;
2005-08-12 18:13:34 +04:00
while ( ( bss_hotplug_slot = sn_hp_destroy ( ) ) )
2005-07-07 02:29:53 +04:00
pci_hp_deregister ( bss_hotplug_slot ) ;
if ( ! list_empty ( & sn_hp_list ) )
printk ( KERN_ERR " %s: internal list is not empty \n " , __FILE__ ) ;
}
module_init ( sn_pci_hotplug_init ) ;
module_exit ( sn_pci_hotplug_exit ) ;