2012-11-29 14:35:47 +01:00
/*
* PCI Hot Plug Controller Driver for System z
*
* Copyright 2012 IBM Corp .
*
* Author ( s ) :
* Jan Glauber < jang @ linux . vnet . ibm . com >
*/
# define COMPONENT "zPCI hpc"
# define pr_fmt(fmt) COMPONENT ": " fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <linux/pci_hotplug.h>
# include <linux/init.h>
2013-04-16 14:11:14 +02:00
# include <asm/pci_debug.h>
2012-11-29 14:35:47 +01:00
# include <asm/sclp.h>
# define SLOT_NAME_SIZE 10
static LIST_HEAD ( s390_hotplug_slot_list ) ;
MODULE_AUTHOR ( " Jan Glauber <jang@linux.vnet.ibm.com " ) ;
MODULE_DESCRIPTION ( " Hot Plug PCI Controller for System z " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int zpci_fn_configured ( enum zpci_state state )
{
return state = = ZPCI_FN_STATE_CONFIGURED | |
state = = ZPCI_FN_STATE_ONLINE ;
}
/*
* struct slot - slot information for each * physical * slot
*/
struct slot {
struct list_head slot_list ;
struct hotplug_slot * hotplug_slot ;
struct zpci_dev * zdev ;
} ;
2013-06-05 16:06:42 +02:00
static inline int slot_configure ( struct slot * slot )
{
int ret = sclp_pci_configure ( slot - > zdev - > fid ) ;
zpci_dbg ( 3 , " conf fid:%x, rc:%d \n " , slot - > zdev - > fid , ret ) ;
if ( ! ret )
slot - > zdev - > state = ZPCI_FN_STATE_CONFIGURED ;
return ret ;
}
static inline int slot_deconfigure ( struct slot * slot )
{
int ret = sclp_pci_deconfigure ( slot - > zdev - > fid ) ;
zpci_dbg ( 3 , " deconf fid:%x, rc:%d \n " , slot - > zdev - > fid , ret ) ;
if ( ! ret )
slot - > zdev - > state = ZPCI_FN_STATE_STANDBY ;
return ret ;
}
2012-11-29 14:35:47 +01:00
static int enable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
int rc ;
if ( slot - > zdev - > state ! = ZPCI_FN_STATE_STANDBY )
return - EIO ;
2013-06-05 16:06:42 +02:00
rc = slot_configure ( slot ) ;
if ( rc )
return rc ;
rc = zpci_enable_device ( slot - > zdev ) ;
if ( rc )
goto out_deconfigure ;
pci_scan_slot ( slot - > zdev - > bus , ZPCI_DEVFN ) ;
2014-01-14 12:03:14 -07:00
pci_lock_rescan_remove ( ) ;
2013-06-05 16:06:42 +02:00
pci_bus_add_devices ( slot - > zdev - > bus ) ;
2014-01-14 12:03:14 -07:00
pci_unlock_rescan_remove ( ) ;
2013-06-05 16:06:42 +02:00
return rc ;
out_deconfigure :
slot_deconfigure ( slot ) ;
2012-11-29 14:35:47 +01:00
return rc ;
}
static int disable_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
int rc ;
if ( ! zpci_fn_configured ( slot - > zdev - > state ) )
return - EIO ;
2013-06-05 16:07:28 +02:00
if ( slot - > zdev - > pdev )
2014-01-14 12:03:14 -07:00
pci_stop_and_remove_bus_device_locked ( slot - > zdev - > pdev ) ;
2013-06-05 16:07:28 +02:00
2013-04-16 14:12:17 +02:00
rc = zpci_disable_device ( slot - > zdev ) ;
if ( rc )
return rc ;
2013-06-05 16:07:28 +02:00
2013-06-05 16:06:42 +02:00
return slot_deconfigure ( slot ) ;
2012-11-29 14:35:47 +01:00
}
static int get_power_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = hotplug_slot - > private ;
switch ( slot - > zdev - > state ) {
case ZPCI_FN_STATE_STANDBY :
* value = 0 ;
break ;
default :
* value = 1 ;
break ;
}
return 0 ;
}
static int get_adapter_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
/* if the slot exits it always contains a function */
* value = 1 ;
return 0 ;
}
static void release_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = hotplug_slot - > private ;
kfree ( slot - > hotplug_slot - > info ) ;
kfree ( slot - > hotplug_slot ) ;
kfree ( slot ) ;
}
static struct hotplug_slot_ops s390_hotplug_slot_ops = {
. enable_slot = enable_slot ,
. disable_slot = disable_slot ,
. get_power_status = get_power_status ,
. get_adapter_status = get_adapter_status ,
} ;
2013-08-29 19:33:16 +02:00
int zpci_init_slot ( struct zpci_dev * zdev )
2012-11-29 14:35:47 +01:00
{
struct hotplug_slot * hotplug_slot ;
struct hotplug_slot_info * info ;
char name [ SLOT_NAME_SIZE ] ;
struct slot * slot ;
int rc ;
if ( ! zdev )
return 0 ;
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
if ( ! slot )
goto error ;
hotplug_slot = kzalloc ( sizeof ( * hotplug_slot ) , GFP_KERNEL ) ;
if ( ! hotplug_slot )
goto error_hp ;
hotplug_slot - > private = slot ;
slot - > hotplug_slot = hotplug_slot ;
slot - > zdev = zdev ;
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
goto error_info ;
hotplug_slot - > info = info ;
hotplug_slot - > ops = & s390_hotplug_slot_ops ;
hotplug_slot - > release = & release_slot ;
get_power_status ( hotplug_slot , & info - > power_status ) ;
get_adapter_status ( hotplug_slot , & info - > adapter_status ) ;
snprintf ( name , SLOT_NAME_SIZE , " %08x " , zdev - > fid ) ;
rc = pci_hp_register ( slot - > hotplug_slot , zdev - > bus ,
ZPCI_DEVFN , name ) ;
2013-10-22 15:17:19 +02:00
if ( rc )
2012-11-29 14:35:47 +01:00
goto error_reg ;
2013-10-22 15:17:19 +02:00
2012-11-29 14:35:47 +01:00
list_add ( & slot - > slot_list , & s390_hotplug_slot_list ) ;
return 0 ;
error_reg :
kfree ( info ) ;
error_info :
kfree ( hotplug_slot ) ;
error_hp :
kfree ( slot ) ;
error :
return - ENOMEM ;
}
2013-08-29 19:33:16 +02:00
void zpci_exit_slot ( struct zpci_dev * zdev )
2012-11-29 14:35:47 +01:00
{
struct list_head * tmp , * n ;
struct slot * slot ;
list_for_each_safe ( tmp , n , & s390_hotplug_slot_list ) {
slot = list_entry ( tmp , struct slot , slot_list ) ;
if ( slot - > zdev ! = zdev )
continue ;
list_del ( & slot - > slot_list ) ;
pci_hp_deregister ( slot - > hotplug_slot ) ;
}
}