2005-04-17 02:20:36 +04:00
/*
* RPA Virtual I / O device functions
* Copyright ( C ) 2004 Linda Xie < lxie @ us . ibm . com >
*
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Send feedback to < lxie @ us . ibm . com >
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/kobject.h>
# include <linux/sysfs.h>
# include <linux/pci.h>
2005-10-31 02:03:48 +03:00
# include <linux/string.h>
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <asm/rtas.h>
# include "rpaphp.h"
static ssize_t location_read_file ( struct hotplug_slot * php_slot , char * buf )
{
char * value ;
int retval = - ENOENT ;
struct slot * slot = ( struct slot * ) php_slot - > private ;
if ( ! slot )
return retval ;
value = slot - > location ;
retval = sprintf ( buf , " %s \n " , value ) ;
return retval ;
}
static struct hotplug_slot_attribute hotplug_slot_attr_location = {
. attr = { . name = " phy_location " , . mode = S_IFREG | S_IRUGO } ,
. show = location_read_file ,
} ;
static void rpaphp_sysfs_add_attr_location ( struct hotplug_slot * slot )
{
sysfs_create_file ( & slot - > kobj , & hotplug_slot_attr_location . attr ) ;
}
static void rpaphp_sysfs_remove_attr_location ( struct hotplug_slot * slot )
{
sysfs_remove_file ( & slot - > kobj , & hotplug_slot_attr_location . attr ) ;
}
/* free up the memory used by a slot */
static void rpaphp_release_slot ( struct hotplug_slot * hotplug_slot )
{
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
dealloc_slot_struct ( slot ) ;
}
void dealloc_slot_struct ( struct slot * slot )
{
kfree ( slot - > hotplug_slot - > info ) ;
kfree ( slot - > hotplug_slot - > name ) ;
kfree ( slot - > hotplug_slot ) ;
kfree ( slot ) ;
return ;
}
struct slot * alloc_slot_struct ( struct device_node * dn , int drc_index , char * drc_name ,
int power_domain )
{
struct slot * slot ;
slot = kmalloc ( sizeof ( struct slot ) , GFP_KERNEL ) ;
if ( ! slot )
goto error_nomem ;
memset ( slot , 0 , sizeof ( struct slot ) ) ;
slot - > hotplug_slot = kmalloc ( sizeof ( struct hotplug_slot ) , GFP_KERNEL ) ;
if ( ! slot - > hotplug_slot )
goto error_slot ;
memset ( slot - > hotplug_slot , 0 , sizeof ( struct hotplug_slot ) ) ;
slot - > hotplug_slot - > info = kmalloc ( sizeof ( struct hotplug_slot_info ) ,
GFP_KERNEL ) ;
if ( ! slot - > hotplug_slot - > info )
goto error_hpslot ;
memset ( slot - > hotplug_slot - > info , 0 , sizeof ( struct hotplug_slot_info ) ) ;
slot - > hotplug_slot - > name = kmalloc ( BUS_ID_SIZE + 1 , GFP_KERNEL ) ;
if ( ! slot - > hotplug_slot - > name )
goto error_info ;
slot - > location = kmalloc ( strlen ( drc_name ) + 1 , GFP_KERNEL ) ;
if ( ! slot - > location )
goto error_name ;
slot - > name = slot - > hotplug_slot - > name ;
slot - > dn = dn ;
slot - > index = drc_index ;
strcpy ( slot - > location , drc_name ) ;
slot - > power_domain = power_domain ;
slot - > hotplug_slot - > private = slot ;
slot - > hotplug_slot - > ops = & rpaphp_hotplug_slot_ops ;
slot - > hotplug_slot - > release = & rpaphp_release_slot ;
return ( slot ) ;
error_name :
kfree ( slot - > hotplug_slot - > name ) ;
error_info :
kfree ( slot - > hotplug_slot - > info ) ;
error_hpslot :
kfree ( slot - > hotplug_slot ) ;
error_slot :
kfree ( slot ) ;
error_nomem :
return NULL ;
}
static int is_registered ( struct slot * slot )
{
struct slot * tmp_slot ;
list_for_each_entry ( tmp_slot , & rpaphp_slot_head , rpaphp_slot_list ) {
if ( ! strcmp ( tmp_slot - > name , slot - > name ) )
return 1 ;
}
return 0 ;
}
int deregister_slot ( struct slot * slot )
{
int retval = 0 ;
struct hotplug_slot * php_slot = slot - > hotplug_slot ;
dbg ( " %s - Entry: deregistering slot=%s \n " ,
__FUNCTION__ , slot - > name ) ;
list_del ( & slot - > rpaphp_slot_list ) ;
/* remove "phy_location" file */
rpaphp_sysfs_remove_attr_location ( php_slot ) ;
retval = pci_hp_deregister ( php_slot ) ;
if ( retval )
err ( " Problem unregistering a slot %s \n " , slot - > name ) ;
else
num_slots - - ;
dbg ( " %s - Exit: rc[%d] \n " , __FUNCTION__ , retval ) ;
return retval ;
}
int register_slot ( struct slot * slot )
{
int retval ;
dbg ( " %s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d] \n " ,
__FUNCTION__ , slot - > dn - > full_name , slot - > index , slot - > name ,
slot - > power_domain , slot - > type ) ;
/* should not try to register the same slot twice */
if ( is_registered ( slot ) ) { /* should't be here */
err ( " register_slot: slot[%s] is already registered \n " , slot - > name ) ;
rpaphp_release_slot ( slot - > hotplug_slot ) ;
return - EAGAIN ;
}
retval = pci_hp_register ( slot - > hotplug_slot ) ;
if ( retval ) {
err ( " pci_hp_register failed with error %d \n " , retval ) ;
rpaphp_release_slot ( slot - > hotplug_slot ) ;
return retval ;
}
/* create "phy_locatoin" file */
rpaphp_sysfs_add_attr_location ( slot - > hotplug_slot ) ;
/* add slot to our internal list */
dbg ( " %s adding slot[%s] to rpaphp_slot_list \n " ,
__FUNCTION__ , slot - > name ) ;
list_add ( & slot - > rpaphp_slot_list , & rpaphp_slot_head ) ;
2005-07-25 19:16:42 +04:00
info ( " Slot [%s](PCI location=%s) registered \n " , slot - > name ,
slot - > location ) ;
2005-04-17 02:20:36 +04:00
num_slots + + ;
return 0 ;
}
int rpaphp_get_power_status ( struct slot * slot , u8 * value )
{
int rc = 0 , level ;
2005-07-25 19:17:03 +04:00
rc = rtas_get_power_level ( slot - > power_domain , & level ) ;
if ( rc < 0 ) {
err ( " failed to get power-level for slot(%s), rc=0x%x \n " ,
slot - > location , rc ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
2005-07-25 19:17:03 +04:00
dbg ( " %s the power level of slot %s(pwd-domain:0x%x) is %d \n " ,
__FUNCTION__ , slot - > name , slot - > power_domain , level ) ;
* value = level ;
2005-04-17 02:20:36 +04:00
return rc ;
}
int rpaphp_set_attention_status ( struct slot * slot , u8 status )
{
int rc ;
/* status: LED_OFF or LED_ON */
rc = rtas_set_indicator ( DR_INDICATOR , slot - > index , status ) ;
if ( rc < 0 )
err ( " slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x \n " ,
slot - > name , slot - > location , slot - > index , status , rc ) ;
return rc ;
}