2005-04-17 02:20:36 +04:00
/*
* PCI Hot Plug Controller Driver for RPA - compliant PPC64 platform .
* Copyright ( C ) 2003 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/moduleparam.h>
# include <linux/pci.h>
2006-10-14 07:05:19 +04:00
# include <linux/pci_hotplug.h>
2005-04-17 02:20:36 +04:00
# include <linux/smp.h>
# include <linux/init.h>
2010-04-06 19:03:40 +04:00
# include <linux/vmalloc.h>
2005-04-17 02:20:36 +04:00
# include <asm/eeh.h> /* for eeh_add_device() */
# include <asm/rtas.h> /* rtas_call */
# include <asm/pci-bridge.h> /* for pci_controller */
# include "../pci.h" /* for pci_add_new_bus */
/* and pci_do_scan_bus */
# include "rpaphp.h"
2012-01-13 03:02:20 +04:00
bool rpaphp_debug ;
2005-04-17 02:20:36 +04:00
LIST_HEAD ( rpaphp_slot_head ) ;
# define DRIVER_VERSION "0.1"
# define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"
# define DRIVER_DESC "RPA HOT Plug PCI Controller Driver"
# define MAX_LOC_CODE 128
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-10-13 20:59:12 +04:00
module_param_named ( debug , rpaphp_debug , bool , 0644 ) ;
2005-04-17 02:20:36 +04:00
/**
* set_attention_status - set attention LED
2007-11-28 20:04:30 +03:00
* @ hotplug_slot : target & hotplug_slot
* @ value : LED control value
*
2005-04-17 02:20:36 +04:00
* echo 0 > attention - - set LED OFF
* echo 1 > attention - - set LED ON
* echo 2 > attention - - set LED ID ( identify , light is blinking )
*/
static int set_attention_status ( struct hotplug_slot * hotplug_slot , u8 value )
{
2007-04-14 02:34:18 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
switch ( value ) {
case 0 :
case 1 :
case 2 :
2007-04-14 02:34:18 +04:00
break ;
default :
value = 1 ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-04-14 02:34:18 +04:00
rc = rtas_set_indicator ( DR_INDICATOR , slot - > index , value ) ;
if ( ! rc )
hotplug_slot - > info - > attention_status = value ;
return rc ;
2005-04-17 02:20:36 +04:00
}
/**
* get_power_status - get power status of a slot
* @ hotplug_slot : slot to get status
* @ value : pointer to store status
*/
static int get_power_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
2007-04-14 02:34:13 +04:00
int retval , level ;
2005-04-17 02:20:36 +04:00
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
2007-04-14 02:34:13 +04:00
retval = rtas_get_power_level ( slot - > power_domain , & level ) ;
if ( ! retval )
* value = level ;
2005-04-17 02:20:36 +04:00
return retval ;
}
/**
* get_attention_status - get attention LED status
2007-11-28 20:04:30 +03:00
* @ hotplug_slot : slot to get status
* @ value : pointer to store status
2005-04-17 02:20:36 +04:00
*/
static int get_attention_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
2007-04-14 02:34:15 +04:00
* value = slot - > hotplug_slot - > info - > attention_status ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int get_adapter_status ( struct hotplug_slot * hotplug_slot , u8 * value )
{
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
2007-04-14 02:34:14 +04:00
int rc , state ;
2005-04-17 02:20:36 +04:00
2007-04-14 02:34:14 +04:00
rc = rpaphp_get_sensor_state ( slot , & state ) ;
* value = NOT_VALID ;
if ( rc )
return rc ;
if ( state = = EMPTY )
* value = EMPTY ;
else if ( state = = PRESENT )
* value = slot - > state ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-12-13 16:11:32 +03:00
static enum pci_bus_speed get_max_bus_speed ( struct slot * slot )
2005-04-17 02:20:36 +04:00
{
2009-12-13 16:11:32 +03:00
enum pci_bus_speed speed ;
2005-04-17 02:20:36 +04:00
switch ( slot - > type ) {
case 1 :
case 2 :
case 3 :
case 4 :
case 5 :
case 6 :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_33MHz ; /* speed for case 1-6 */
2005-04-17 02:20:36 +04:00
break ;
case 7 :
case 8 :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_66MHz ;
2005-04-17 02:20:36 +04:00
break ;
case 11 :
case 14 :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_66MHz_PCIX ;
2005-04-17 02:20:36 +04:00
break ;
case 12 :
case 15 :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_100MHz_PCIX ;
2005-04-17 02:20:36 +04:00
break ;
case 13 :
case 16 :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_133MHz_PCIX ;
2005-04-17 02:20:36 +04:00
break ;
default :
2009-12-13 16:11:32 +03:00
speed = PCI_SPEED_UNKNOWN ;
2005-04-17 02:20:36 +04:00
break ;
}
2009-12-13 16:11:32 +03:00
return speed ;
2005-04-17 02:20:36 +04:00
}
2006-07-12 09:39:43 +04:00
static int get_children_props ( struct device_node * dn , const int * * drc_indexes ,
const int * * drc_names , const int * * drc_types ,
const int * * drc_power_domains )
2005-04-17 02:20:36 +04:00
{
2006-07-12 09:39:43 +04:00
const int * indexes , * names , * types , * domains ;
2005-04-17 02:20:36 +04:00
2007-05-01 07:54:02 +04:00
indexes = of_get_property ( dn , " ibm,drc-indexes " , NULL ) ;
names = of_get_property ( dn , " ibm,drc-names " , NULL ) ;
types = of_get_property ( dn , " ibm,drc-types " , NULL ) ;
domains = of_get_property ( dn , " ibm,drc-power-domains " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! indexes | | ! names | | ! types | | ! domains ) {
/* Slot does not have dynamically-removable children */
return - EINVAL ;
}
if ( drc_indexes )
* drc_indexes = indexes ;
if ( drc_names )
/* &drc_names[1] contains NULL terminated slot names */
* drc_names = names ;
if ( drc_types )
/* &drc_types[1] contains NULL terminated slot types */
* drc_types = types ;
if ( drc_power_domains )
* drc_power_domains = domains ;
return 0 ;
}
/* To get the DRC props describing the current node, first obtain it's
* my - drc - index property . Next obtain the DRC list from it ' s parent . Use
* the my - drc - index for correlation , and obtain the requested properties .
*/
int rpaphp_get_drc_props ( struct device_node * dn , int * drc_index ,
char * * drc_name , char * * drc_type , int * drc_power_domain )
{
2006-07-12 09:39:43 +04:00
const int * indexes , * names ;
const int * types , * domains ;
const unsigned int * my_index ;
2005-04-17 02:20:36 +04:00
char * name_tmp , * type_tmp ;
int i , rc ;
2007-05-01 07:54:02 +04:00
my_index = of_get_property ( dn , " ibm,my-drc-index " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! my_index ) {
/* Node isn't DLPAR/hotplug capable */
return - EINVAL ;
}
rc = get_children_props ( dn - > parent , & indexes , & names , & types , & domains ) ;
if ( rc < 0 ) {
return - EINVAL ;
}
name_tmp = ( char * ) & names [ 1 ] ;
type_tmp = ( char * ) & types [ 1 ] ;
/* Iterate through parent properties, looking for my-drc-index */
for ( i = 0 ; i < indexes [ 0 ] ; i + + ) {
if ( ( unsigned int ) indexes [ i + 1 ] = = * my_index ) {
if ( drc_name )
* drc_name = name_tmp ;
if ( drc_type )
* drc_type = type_tmp ;
if ( drc_index )
* drc_index = * my_index ;
if ( drc_power_domain )
* drc_power_domain = domains [ i + 1 ] ;
return 0 ;
}
name_tmp + = ( strlen ( name_tmp ) + 1 ) ;
type_tmp + = ( strlen ( type_tmp ) + 1 ) ;
}
return - EINVAL ;
}
static int is_php_type ( char * drc_type )
{
unsigned long value ;
char * endptr ;
/* PCI Hotplug nodes have an integer for drc_type */
value = simple_strtoul ( drc_type , & endptr , 10 ) ;
if ( endptr = = drc_type )
return 0 ;
return 1 ;
}
2007-04-14 02:34:22 +04:00
/**
* is_php_dn ( ) - return 1 if this is a hotpluggable pci slot , else 0
2007-11-28 20:04:30 +03:00
* @ dn : target & device_node
* @ indexes : passed to get_children_props ( )
* @ names : passed to get_children_props ( )
* @ types : returned from get_children_props ( )
* @ power_domains :
2007-04-14 02:34:22 +04:00
*
* This routine will return true only if the device node is
* a hotpluggable slot . This routine will return false
* for built - in pci slots ( even when the built - in slots are
* dlparable . )
*/
2006-07-12 09:39:43 +04:00
static int is_php_dn ( struct device_node * dn , const int * * indexes ,
const int * * names , const int * * types , const int * * power_domains )
2005-04-17 02:20:36 +04:00
{
2006-07-12 09:39:43 +04:00
const int * drc_types ;
2005-04-17 02:20:36 +04:00
int rc ;
rc = get_children_props ( dn , indexes , names , & drc_types , power_domains ) ;
2007-04-14 02:34:22 +04:00
if ( rc < 0 )
return 0 ;
2005-04-17 02:20:36 +04:00
2007-04-14 02:34:22 +04:00
if ( ! is_php_type ( ( char * ) & drc_types [ 1 ] ) )
return 0 ;
* types = drc_types ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
2006-01-13 03:31:01 +03:00
/**
2007-04-14 02:34:22 +04:00
* rpaphp_add_slot - - declare a hotplug slot to the hotplug subsystem .
2007-11-28 20:04:30 +03:00
* @ dn : device node of slot
2007-04-14 02:34:22 +04:00
*
* This subroutine will register a hotplugable slot with the
2011-03-31 05:57:33 +04:00
* PCI hotplug infrastructure . This routine is typically called
2007-04-14 02:34:22 +04:00
* during boot time , if the hotplug slots are present at boot time ,
* or is called later , by the dlpar add code , if the slot is
* being dynamically added during runtime .
*
* If the device node points at an embedded ( built - in ) slot , this
* routine will just return without doing anything , since embedded
* slots cannot be hotplugged .
2006-01-13 03:31:01 +03:00
*
2007-11-28 20:04:30 +03:00
* To remove a slot , it suffices to call rpaphp_deregister_slot ( ) .
2006-01-13 03:31:01 +03:00
*/
2005-04-17 02:20:36 +04:00
int rpaphp_add_slot ( struct device_node * dn )
{
struct slot * slot ;
int retval = 0 ;
2005-07-25 19:17:03 +04:00
int i ;
2006-07-12 09:39:43 +04:00
const int * indexes , * names , * types , * power_domains ;
2005-04-17 02:20:36 +04:00
char * name , * type ;
2007-04-14 02:34:07 +04:00
if ( ! dn - > name | | strcmp ( dn - > name , " pci " ) )
return 0 ;
2007-04-14 02:34:22 +04:00
/* If this is not a hotplug slot, return without doing anything. */
2007-04-14 02:34:07 +04:00
if ( ! is_php_dn ( dn , & indexes , & names , & types , & power_domains ) )
return 0 ;
2008-03-04 06:09:46 +03:00
dbg ( " Entry %s: dn->full_name=%s \n " , __func__ , dn - > full_name ) ;
2005-04-17 02:20:36 +04:00
/* register PCI devices */
2007-04-14 02:34:07 +04:00
name = ( char * ) & names [ 1 ] ;
type = ( char * ) & types [ 1 ] ;
for ( i = 0 ; i < indexes [ 0 ] ; i + + ) {
slot = alloc_slot_struct ( dn , indexes [ i + 1 ] , name , power_domains [ i + 1 ] ) ;
if ( ! slot )
return - ENOMEM ;
slot - > type = simple_strtoul ( type , NULL , 10 ) ;
2005-04-17 02:20:36 +04:00
2007-04-14 02:34:07 +04:00
dbg ( " Found drc-index:0x%x drc-name:%s drc-type:%s \n " ,
indexes [ i + 1 ] , name , type ) ;
2005-04-17 02:20:36 +04:00
2007-04-14 02:34:20 +04:00
retval = rpaphp_enable_slot ( slot ) ;
2007-04-14 02:34:19 +04:00
if ( ! retval )
retval = rpaphp_register_slot ( slot ) ;
2007-04-14 02:34:09 +04:00
if ( retval )
dealloc_slot_struct ( slot ) ;
2007-04-14 02:34:07 +04:00
name + = strlen ( name ) + 1 ;
type + = strlen ( type ) + 1 ;
2005-04-17 02:20:36 +04:00
}
2008-03-04 06:09:46 +03:00
dbg ( " %s - Exit: rc[%d] \n " , __func__ , retval ) ;
2007-04-14 02:34:09 +04:00
/* XXX FIXME: reports a failure only if last entry in loop failed */
2005-04-17 02:20:36 +04:00
return retval ;
}
static void __exit cleanup_slots ( void )
{
struct list_head * tmp , * n ;
struct slot * slot ;
/*
* Unregister all of our slots with the pci_hotplug subsystem ,
* and free up all memory that we had allocated .
* memory will be freed in release_slot callback .
*/
list_for_each_safe ( tmp , n , & rpaphp_slot_head ) {
slot = list_entry ( tmp , struct slot , rpaphp_slot_list ) ;
list_del ( & slot - > rpaphp_slot_list ) ;
pci_hp_deregister ( slot - > hotplug_slot ) ;
}
return ;
}
static int __init rpaphp_init ( void )
{
2005-07-25 19:16:42 +04:00
struct device_node * dn = NULL ;
2005-04-17 02:20:36 +04:00
info ( DRIVER_DESC " version: " DRIVER_VERSION " \n " ) ;
2005-07-25 19:16:42 +04:00
2006-11-14 02:12:52 +03:00
while ( ( dn = of_find_node_by_name ( dn , " pci " ) ) )
2005-07-25 19:16:42 +04:00
rpaphp_add_slot ( dn ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void __exit rpaphp_exit ( void )
{
cleanup_slots ( ) ;
}
2007-04-14 02:34:25 +04:00
static int enable_slot ( struct hotplug_slot * hotplug_slot )
2005-04-17 02:20:36 +04:00
{
2007-04-14 02:34:25 +04:00
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
2006-01-13 03:28:22 +03:00
int state ;
int retval ;
2005-04-17 02:20:36 +04:00
2006-01-13 03:28:22 +03:00
if ( slot - > state = = CONFIGURED )
return 0 ;
retval = rpaphp_get_sensor_state ( slot , & state ) ;
if ( retval )
return retval ;
if ( state = = PRESENT ) {
pcibios_add_pci_devices ( slot - > bus ) ;
slot - > state = CONFIGURED ;
} else if ( state = = EMPTY ) {
slot - > state = EMPTY ;
} else {
2008-03-04 06:09:46 +03:00
err ( " %s: slot[%s] is in invalid state \n " , __func__ , slot - > name ) ;
2006-01-13 03:28:22 +03:00
slot - > state = NOT_VALID ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-12-13 16:11:32 +03:00
slot - > bus - > max_bus_speed = get_max_bus_speed ( slot ) ;
2006-01-13 03:28:22 +03:00
return 0 ;
}
2007-04-14 02:34:25 +04:00
static int disable_slot ( struct hotplug_slot * hotplug_slot )
2006-01-13 03:28:22 +03:00
{
struct slot * slot = ( struct slot * ) hotplug_slot - > private ;
2006-01-13 03:26:27 +03:00
if ( slot - > state = = NOT_CONFIGURED )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-04-14 02:34:23 +04:00
pcibios_remove_pci_devices ( slot - > bus ) ;
2010-04-06 19:03:40 +04:00
vm_unmap_aliases ( ) ;
2006-01-13 03:26:27 +03:00
slot - > state = NOT_CONFIGURED ;
return 0 ;
}
2006-01-13 03:32:58 +03:00
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
. enable_slot = enable_slot ,
. disable_slot = disable_slot ,
. set_attention_status = set_attention_status ,
. get_power_status = get_power_status ,
. get_attention_status = get_attention_status ,
. get_adapter_status = get_adapter_status ,
} ;
2005-04-17 02:20:36 +04:00
module_init ( rpaphp_init ) ;
module_exit ( rpaphp_exit ) ;
EXPORT_SYMBOL_GPL ( rpaphp_add_slot ) ;
EXPORT_SYMBOL_GPL ( rpaphp_slot_head ) ;
EXPORT_SYMBOL_GPL ( rpaphp_get_drc_props ) ;