2005-04-17 05:20:36 +07:00
/*
* ipmi_poweroff . c
*
* MontaVista IPMI Poweroff extension to sys_reboot
*
* Author : MontaVista Software , Inc .
* Steven Dake < sdake @ mvista . com >
* Corey Minyard < cminyard @ mvista . com >
* source @ mvista . com
*
* Copyright 2002 , 2004 MontaVista Software Inc .
*
* 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 SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
* TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 .
*/
# include <linux/module.h>
2005-06-24 12:01:42 +07:00
# include <linux/moduleparam.h>
# include <linux/proc_fs.h>
2005-04-17 05:20:36 +07:00
# include <linux/string.h>
2005-06-24 12:01:44 +07:00
# include <linux/completion.h>
2006-03-24 18:16:15 +07:00
# include <linux/pm.h>
2005-06-24 12:01:44 +07:00
# include <linux/kdev_t.h>
2005-04-17 05:20:36 +07:00
# include <linux/ipmi.h>
# include <linux/ipmi_smi.h>
# define PFX "IPMI poweroff: "
2006-12-07 11:41:00 +07:00
static void ipmi_po_smi_gone ( int if_num ) ;
static void ipmi_po_new_smi ( int if_num , struct device * device ) ;
2005-06-24 12:01:42 +07:00
/* Definitions for controlling power off (if the system supports it). It
* conveniently matches the IPMI chassis control values . */
# define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */
# define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */
/* the IPMI data command */
2005-09-07 05:18:46 +07:00
static int poweroff_powercycle ;
2005-06-24 12:01:42 +07:00
2006-12-07 11:41:00 +07:00
/* Which interface to use, -1 means the first we see. */
static int ifnum_to_use = - 1 ;
/* Our local state. */
2006-12-10 17:19:06 +07:00
static int ready ;
2006-12-07 11:41:00 +07:00
static ipmi_user_t ipmi_user ;
static int ipmi_ifnum ;
2006-12-10 17:19:06 +07:00
static void ( * specific_poweroff_func ) ( ipmi_user_t user ) ;
2006-12-07 11:41:00 +07:00
/* Holds the old poweroff function so we can restore it on removal. */
static void ( * old_poweroff_func ) ( void ) ;
static int set_param_ifnum ( const char * val , struct kernel_param * kp )
{
int rv = param_set_int ( val , kp ) ;
if ( rv )
return rv ;
if ( ( ifnum_to_use < 0 ) | | ( ifnum_to_use = = ipmi_ifnum ) )
return 0 ;
ipmi_po_smi_gone ( ipmi_ifnum ) ;
ipmi_po_new_smi ( ifnum_to_use , NULL ) ;
return 0 ;
}
module_param_call ( ifnum_to_use , set_param_ifnum , param_get_int ,
& ifnum_to_use , 0644 ) ;
MODULE_PARM_DESC ( ifnum_to_use , " The interface number to use for the watchdog "
" timer. Setting to -1 defaults to the first registered "
" interface " ) ;
2005-06-24 12:01:42 +07:00
/* parameter definition to allow user to flag power cycle */
2005-09-28 11:45:35 +07:00
module_param ( poweroff_powercycle , int , 0644 ) ;
2005-11-07 15:59:57 +07:00
MODULE_PARM_DESC ( poweroff_powercycle , " Set to non-zero to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down. " ) ;
2005-06-24 12:01:42 +07:00
2005-04-17 05:20:36 +07:00
/* Stuff from the get device id command. */
static unsigned int mfg_id ;
static unsigned int prod_id ;
static unsigned char capabilities ;
2005-09-07 05:18:43 +07:00
static unsigned char ipmi_version ;
2005-04-17 05:20:36 +07:00
/* We use our own messages for this operation, we don't let the system
allocate them , since we may be in a panic situation . The whole
thing is single - threaded , anyway , so multiple messages are not
required . */
2008-04-29 15:01:02 +07:00
static atomic_t dummy_count = ATOMIC_INIT ( 0 ) ;
2005-04-17 05:20:36 +07:00
static void dummy_smi_free ( struct ipmi_smi_msg * msg )
{
2008-04-29 15:01:02 +07:00
atomic_dec ( & dummy_count ) ;
2005-04-17 05:20:36 +07:00
}
static void dummy_recv_free ( struct ipmi_recv_msg * msg )
{
2008-04-29 15:01:02 +07:00
atomic_dec ( & dummy_count ) ;
2005-04-17 05:20:36 +07:00
}
static struct ipmi_smi_msg halt_smi_msg =
{
. done = dummy_smi_free
} ;
static struct ipmi_recv_msg halt_recv_msg =
{
. done = dummy_recv_free
} ;
/*
* Code to send a message and wait for the reponse .
*/
static void receive_handler ( struct ipmi_recv_msg * recv_msg , void * handler_data )
{
2005-06-24 12:01:44 +07:00
struct completion * comp = recv_msg - > user_msg_data ;
2005-04-17 05:20:36 +07:00
2005-06-24 12:01:44 +07:00
if ( comp )
complete ( comp ) ;
2005-04-17 05:20:36 +07:00
}
static struct ipmi_user_hndl ipmi_poweroff_handler =
{
. ipmi_recv_hndl = receive_handler
} ;
static int ipmi_request_wait_for_response ( ipmi_user_t user ,
struct ipmi_addr * addr ,
struct kernel_ipmi_msg * send_msg )
{
2005-06-24 12:01:44 +07:00
int rv ;
struct completion comp ;
2005-04-17 05:20:36 +07:00
2005-06-24 12:01:44 +07:00
init_completion ( & comp ) ;
2005-04-17 05:20:36 +07:00
2005-06-24 12:01:44 +07:00
rv = ipmi_request_supply_msgs ( user , addr , 0 , send_msg , & comp ,
2005-04-17 05:20:36 +07:00
& halt_smi_msg , & halt_recv_msg , 0 ) ;
if ( rv )
return rv ;
2005-06-24 12:01:44 +07:00
wait_for_completion ( & comp ) ;
2005-04-17 05:20:36 +07:00
return halt_recv_msg . msg . data [ 0 ] ;
}
2008-04-29 15:01:02 +07:00
/* Wait for message to complete, spinning. */
2005-04-17 05:20:36 +07:00
static int ipmi_request_in_rc_mode ( ipmi_user_t user ,
struct ipmi_addr * addr ,
struct kernel_ipmi_msg * send_msg )
{
2005-06-24 12:01:44 +07:00
int rv ;
2005-04-17 05:20:36 +07:00
2008-04-29 15:01:02 +07:00
atomic_set ( & dummy_count , 2 ) ;
2005-04-17 05:20:36 +07:00
rv = ipmi_request_supply_msgs ( user , addr , 0 , send_msg , NULL ,
& halt_smi_msg , & halt_recv_msg , 0 ) ;
2008-04-29 15:01:02 +07:00
if ( rv ) {
atomic_set ( & dummy_count , 0 ) ;
2005-04-17 05:20:36 +07:00
return rv ;
2008-04-29 15:01:02 +07:00
}
/*
* Spin until our message is done .
*/
while ( atomic_read ( & dummy_count ) > 0 ) {
ipmi_poll_interface ( user ) ;
cpu_relax ( ) ;
}
2005-04-17 05:20:36 +07:00
return halt_recv_msg . msg . data [ 0 ] ;
}
/*
* ATCA Support
*/
# define IPMI_NETFN_ATCA 0x2c
# define IPMI_ATCA_SET_POWER_CMD 0x11
# define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01
# define IPMI_PICMG_ID 0
2006-12-07 11:41:09 +07:00
# define IPMI_NETFN_OEM 0x2e
# define IPMI_ATCA_PPS_GRACEFUL_RESTART 0x11
# define IPMI_ATCA_PPS_IANA "\x00\x40\x0A"
# define IPMI_MOTOROLA_MANUFACTURER_ID 0x0000A1
# define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID 0x0051
2006-12-10 17:19:06 +07:00
static void ( * atca_oem_poweroff_hook ) ( ipmi_user_t user ) ;
2006-12-07 11:41:09 +07:00
static void pps_poweroff_atca ( ipmi_user_t user )
{
struct ipmi_system_interface_addr smi_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
/*
* Configure IPMI address for local access
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
printk ( KERN_INFO PFX " PPS powerdown hook used " ) ;
send_msg . netfn = IPMI_NETFN_OEM ;
send_msg . cmd = IPMI_ATCA_PPS_GRACEFUL_RESTART ;
send_msg . data = IPMI_ATCA_PPS_IANA ;
send_msg . data_len = 3 ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv & & rv ! = IPMI_UNKNOWN_ERR_COMPLETION_CODE ) {
printk ( KERN_ERR PFX " Unable to send ATCA , "
" IPMI error 0x%x \n " , rv ) ;
}
return ;
}
2005-04-17 05:20:36 +07:00
static int ipmi_atca_detect ( ipmi_user_t user )
{
struct ipmi_system_interface_addr smi_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
unsigned char data [ 1 ] ;
/*
* Configure IPMI address for local access
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
/*
* Use get address info to check and see if we are ATCA
*/
send_msg . netfn = IPMI_NETFN_ATCA ;
send_msg . cmd = IPMI_ATCA_GET_ADDR_INFO_CMD ;
data [ 0 ] = IPMI_PICMG_ID ;
send_msg . data = data ;
send_msg . data_len = sizeof ( data ) ;
rv = ipmi_request_wait_for_response ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
2006-12-07 11:41:09 +07:00
printk ( KERN_INFO PFX " ATCA Detect mfg 0x%X prod 0x%X \n " , mfg_id , prod_id ) ;
if ( ( mfg_id = = IPMI_MOTOROLA_MANUFACTURER_ID )
& & ( prod_id = = IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID ) ) {
printk ( KERN_INFO PFX " Installing Pigeon Point Systems Poweroff Hook \n " ) ;
atca_oem_poweroff_hook = pps_poweroff_atca ;
}
2005-04-17 05:20:36 +07:00
return ! rv ;
}
static void ipmi_poweroff_atca ( ipmi_user_t user )
{
struct ipmi_system_interface_addr smi_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
unsigned char data [ 4 ] ;
/*
* Configure IPMI address for local access
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
printk ( KERN_INFO PFX " Powering down via ATCA power command \n " ) ;
/*
* Power down
*/
send_msg . netfn = IPMI_NETFN_ATCA ;
send_msg . cmd = IPMI_ATCA_SET_POWER_CMD ;
data [ 0 ] = IPMI_PICMG_ID ;
data [ 1 ] = 0 ; /* FRU id */
data [ 2 ] = 0 ; /* Power Level */
data [ 3 ] = 0 ; /* Don't change saved presets */
send_msg . data = data ;
send_msg . data_len = sizeof ( data ) ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
2006-12-07 11:41:09 +07:00
/** At this point, the system may be shutting down, and most
* * serial drivers ( if used ) will have interrupts turned off
* * it may be better to ignore IPMI_UNKNOWN_ERR_COMPLETION_CODE
* * return code
* */
if ( rv & & rv ! = IPMI_UNKNOWN_ERR_COMPLETION_CODE ) {
2005-04-17 05:20:36 +07:00
printk ( KERN_ERR PFX " Unable to send ATCA powerdown message, "
" IPMI error 0x%x \n " , rv ) ;
goto out ;
}
2006-12-07 11:41:09 +07:00
if ( atca_oem_poweroff_hook )
return atca_oem_poweroff_hook ( user ) ;
2005-04-17 05:20:36 +07:00
out :
return ;
}
/*
* CPI1 Support
*/
# define IPMI_NETFN_OEM_1 0xf8
# define OEM_GRP_CMD_SET_RESET_STATE 0x84
# define OEM_GRP_CMD_SET_POWER_STATE 0x82
# define IPMI_NETFN_OEM_8 0xf8
# define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80
# define OEM_GRP_CMD_GET_SLOT_GA 0xa3
# define IPMI_NETFN_SENSOR_EVT 0x10
# define IPMI_CMD_GET_EVENT_RECEIVER 0x01
# define IPMI_CPI1_PRODUCT_ID 0x000157
# define IPMI_CPI1_MANUFACTURER_ID 0x0108
static int ipmi_cpi1_detect ( ipmi_user_t user )
{
return ( ( mfg_id = = IPMI_CPI1_MANUFACTURER_ID )
& & ( prod_id = = IPMI_CPI1_PRODUCT_ID ) ) ;
}
static void ipmi_poweroff_cpi1 ( ipmi_user_t user )
{
struct ipmi_system_interface_addr smi_addr ;
struct ipmi_ipmb_addr ipmb_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
unsigned char data [ 1 ] ;
int slot ;
unsigned char hotswap_ipmb ;
unsigned char aer_addr ;
unsigned char aer_lun ;
/*
* Configure IPMI address for local access
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
printk ( KERN_INFO PFX " Powering down via CPI1 power command \n " ) ;
/*
* Get IPMI ipmb address
*/
send_msg . netfn = IPMI_NETFN_OEM_8 > > 2 ;
send_msg . cmd = OEM_GRP_CMD_GET_SLOT_GA ;
send_msg . data = NULL ;
send_msg . data_len = 0 ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv )
goto out ;
slot = halt_recv_msg . msg . data [ 1 ] ;
hotswap_ipmb = ( slot > 9 ) ? ( 0xb0 + 2 * slot ) : ( 0xae + 2 * slot ) ;
/*
* Get active event receiver
*/
send_msg . netfn = IPMI_NETFN_SENSOR_EVT > > 2 ;
send_msg . cmd = IPMI_CMD_GET_EVENT_RECEIVER ;
send_msg . data = NULL ;
send_msg . data_len = 0 ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv )
goto out ;
aer_addr = halt_recv_msg . msg . data [ 1 ] ;
aer_lun = halt_recv_msg . msg . data [ 2 ] ;
/*
* Setup IPMB address target instead of local target
*/
ipmb_addr . addr_type = IPMI_IPMB_ADDR_TYPE ;
ipmb_addr . channel = 0 ;
ipmb_addr . slave_addr = aer_addr ;
ipmb_addr . lun = aer_lun ;
/*
* Send request hotswap control to remove blade from dpv
*/
send_msg . netfn = IPMI_NETFN_OEM_8 > > 2 ;
send_msg . cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL ;
send_msg . data = & hotswap_ipmb ;
send_msg . data_len = 1 ;
ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & ipmb_addr ,
& send_msg ) ;
/*
* Set reset asserted
*/
send_msg . netfn = IPMI_NETFN_OEM_1 > > 2 ;
send_msg . cmd = OEM_GRP_CMD_SET_RESET_STATE ;
send_msg . data = data ;
data [ 0 ] = 1 ; /* Reset asserted state */
send_msg . data_len = 1 ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv )
goto out ;
/*
* Power down
*/
send_msg . netfn = IPMI_NETFN_OEM_1 > > 2 ;
send_msg . cmd = OEM_GRP_CMD_SET_POWER_STATE ;
send_msg . data = data ;
data [ 0 ] = 1 ; /* Power down state */
send_msg . data_len = 1 ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv )
goto out ;
out :
return ;
}
2005-09-07 05:18:43 +07:00
/*
* ipmi_dell_chassis_detect ( )
* Dell systems with IPMI < 1.5 don ' t set the chassis capability bit
* but they can handle a chassis poweroff or powercycle command .
*/
# define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00}
static int ipmi_dell_chassis_detect ( ipmi_user_t user )
{
const char ipmi_version_major = ipmi_version & 0xF ;
const char ipmi_version_minor = ( ipmi_version > > 4 ) & 0xF ;
2006-03-31 17:30:40 +07:00
const char mfr [ 3 ] = DELL_IANA_MFR_ID ;
2005-09-07 05:18:43 +07:00
if ( ! memcmp ( mfr , & mfg_id , sizeof ( mfr ) ) & &
ipmi_version_major < = 1 & &
ipmi_version_minor < 5 )
return 1 ;
return 0 ;
}
2005-04-17 05:20:36 +07:00
/*
* Standard chassis support
*/
# define IPMI_NETFN_CHASSIS_REQUEST 0
# define IPMI_CHASSIS_CONTROL_CMD 0x02
static int ipmi_chassis_detect ( ipmi_user_t user )
{
/* Chassis support, use it. */
return ( capabilities & 0x80 ) ;
}
static void ipmi_poweroff_chassis ( ipmi_user_t user )
{
struct ipmi_system_interface_addr smi_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
unsigned char data [ 1 ] ;
/*
* Configure IPMI address for local access
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
2005-06-24 12:01:42 +07:00
powercyclefailed :
printk ( KERN_INFO PFX " Powering %s via IPMI chassis control command \n " ,
2005-09-07 05:18:46 +07:00
( poweroff_powercycle ? " cycle " : " down " ) ) ;
2005-04-17 05:20:36 +07:00
/*
* Power down
*/
send_msg . netfn = IPMI_NETFN_CHASSIS_REQUEST ;
send_msg . cmd = IPMI_CHASSIS_CONTROL_CMD ;
2005-09-07 05:18:46 +07:00
if ( poweroff_powercycle )
data [ 0 ] = IPMI_CHASSIS_POWER_CYCLE ;
else
data [ 0 ] = IPMI_CHASSIS_POWER_DOWN ;
2005-04-17 05:20:36 +07:00
send_msg . data = data ;
send_msg . data_len = sizeof ( data ) ;
rv = ipmi_request_in_rc_mode ( user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv ) {
2005-09-07 05:18:46 +07:00
if ( poweroff_powercycle ) {
/* power cycle failed, default to power down */
printk ( KERN_ERR PFX " Unable to send chassis power " \
" cycle message, IPMI error 0x%x \n " , rv ) ;
poweroff_powercycle = 0 ;
goto powercyclefailed ;
2005-06-24 12:01:42 +07:00
}
2005-04-17 05:20:36 +07:00
2005-09-07 05:18:46 +07:00
printk ( KERN_ERR PFX " Unable to send chassis power " \
" down message, IPMI error 0x%x \n " , rv ) ;
}
2005-04-17 05:20:36 +07:00
}
/* Table of possible power off functions. */
struct poweroff_function {
char * platform_type ;
int ( * detect ) ( ipmi_user_t user ) ;
void ( * poweroff_func ) ( ipmi_user_t user ) ;
} ;
static struct poweroff_function poweroff_functions [ ] = {
{ . platform_type = " ATCA " ,
. detect = ipmi_atca_detect ,
. poweroff_func = ipmi_poweroff_atca } ,
{ . platform_type = " CPI1 " ,
. detect = ipmi_cpi1_detect ,
. poweroff_func = ipmi_poweroff_cpi1 } ,
2005-09-07 05:18:43 +07:00
{ . platform_type = " chassis " ,
. detect = ipmi_dell_chassis_detect ,
. poweroff_func = ipmi_poweroff_chassis } ,
2005-04-17 05:20:36 +07:00
/* Chassis should generally be last, other things should override
it . */
{ . platform_type = " chassis " ,
. detect = ipmi_chassis_detect ,
. poweroff_func = ipmi_poweroff_chassis } ,
} ;
# define NUM_PO_FUNCS (sizeof(poweroff_functions) \
/ sizeof ( struct poweroff_function ) )
/* Called on a powerdown request. */
static void ipmi_poweroff_function ( void )
{
if ( ! ready )
return ;
/* Use run-to-completion mode, since interrupts may be off. */
specific_poweroff_func ( ipmi_user ) ;
}
/* Wait for an IPMI interface to be installed, the first one installed
will be grabbed by this code and used to perform the powerdown . */
2006-03-26 16:37:21 +07:00
static void ipmi_po_new_smi ( int if_num , struct device * device )
2005-04-17 05:20:36 +07:00
{
struct ipmi_system_interface_addr smi_addr ;
struct kernel_ipmi_msg send_msg ;
int rv ;
int i ;
if ( ready )
return ;
2006-12-07 11:41:00 +07:00
if ( ( ifnum_to_use > = 0 ) & & ( ifnum_to_use ! = if_num ) )
return ;
2005-06-24 12:01:42 +07:00
rv = ipmi_create_user ( if_num , & ipmi_poweroff_handler , NULL ,
& ipmi_user ) ;
2005-04-17 05:20:36 +07:00
if ( rv ) {
printk ( KERN_ERR PFX " could not create IPMI user, error %d \n " ,
rv ) ;
return ;
}
2006-12-07 11:41:00 +07:00
ipmi_ifnum = if_num ;
2005-04-17 05:20:36 +07:00
/*
* Do a get device ide and store some results , since this is
* used by several functions .
*/
smi_addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
smi_addr . channel = IPMI_BMC_CHANNEL ;
smi_addr . lun = 0 ;
send_msg . netfn = IPMI_NETFN_APP_REQUEST ;
send_msg . cmd = IPMI_GET_DEVICE_ID_CMD ;
send_msg . data = NULL ;
send_msg . data_len = 0 ;
rv = ipmi_request_wait_for_response ( ipmi_user ,
( struct ipmi_addr * ) & smi_addr ,
& send_msg ) ;
if ( rv ) {
printk ( KERN_ERR PFX " Unable to send IPMI get device id info, "
" IPMI error 0x%x \n " , rv ) ;
goto out_err ;
}
if ( halt_recv_msg . msg . data_len < 12 ) {
printk ( KERN_ERR PFX " (chassis) IPMI get device id info too, "
" short, was %d bytes, needed %d bytes \n " ,
halt_recv_msg . msg . data_len , 12 ) ;
goto out_err ;
}
mfg_id = ( halt_recv_msg . msg . data [ 7 ]
| ( halt_recv_msg . msg . data [ 8 ] < < 8 )
| ( halt_recv_msg . msg . data [ 9 ] < < 16 ) ) ;
prod_id = ( halt_recv_msg . msg . data [ 10 ]
| ( halt_recv_msg . msg . data [ 11 ] < < 8 ) ) ;
capabilities = halt_recv_msg . msg . data [ 6 ] ;
2005-09-07 05:18:43 +07:00
ipmi_version = halt_recv_msg . msg . data [ 5 ] ;
2005-04-17 05:20:36 +07:00
/* Scan for a poweroff method */
2005-09-07 05:18:45 +07:00
for ( i = 0 ; i < NUM_PO_FUNCS ; i + + ) {
2005-04-17 05:20:36 +07:00
if ( poweroff_functions [ i ] . detect ( ipmi_user ) )
goto found ;
}
out_err :
printk ( KERN_ERR PFX " Unable to find a poweroff function that "
" will work, giving up \n " ) ;
ipmi_destroy_user ( ipmi_user ) ;
return ;
found :
printk ( KERN_INFO PFX " Found a %s style poweroff function \n " ,
poweroff_functions [ i ] . platform_type ) ;
specific_poweroff_func = poweroff_functions [ i ] . poweroff_func ;
old_poweroff_func = pm_power_off ;
pm_power_off = ipmi_poweroff_function ;
ready = 1 ;
}
static void ipmi_po_smi_gone ( int if_num )
{
2006-12-07 11:41:00 +07:00
if ( ! ready )
return ;
if ( ipmi_ifnum ! = if_num )
return ;
ready = 0 ;
ipmi_destroy_user ( ipmi_user ) ;
pm_power_off = old_poweroff_func ;
2005-04-17 05:20:36 +07:00
}
static struct ipmi_smi_watcher smi_watcher =
{
. owner = THIS_MODULE ,
. new_smi = ipmi_po_new_smi ,
. smi_gone = ipmi_po_smi_gone
} ;
2005-06-24 12:01:42 +07:00
# ifdef CONFIG_PROC_FS
2005-09-07 05:18:46 +07:00
# include <linux/sysctl.h>
static ctl_table ipmi_table [ ] = {
{ . ctl_name = DEV_IPMI_POWEROFF_POWERCYCLE ,
. procname = " poweroff_powercycle " ,
. data = & poweroff_powercycle ,
. maxlen = sizeof ( poweroff_powercycle ) ,
. mode = 0644 ,
. proc_handler = & proc_dointvec } ,
{ }
} ;
2005-06-24 12:01:42 +07:00
2005-09-07 05:18:46 +07:00
static ctl_table ipmi_dir_table [ ] = {
{ . ctl_name = DEV_IPMI ,
. procname = " ipmi " ,
. mode = 0555 ,
. child = ipmi_table } ,
{ }
} ;
2005-06-24 12:01:42 +07:00
2005-09-07 05:18:46 +07:00
static ctl_table ipmi_root_table [ ] = {
{ . ctl_name = CTL_DEV ,
. procname = " dev " ,
. mode = 0555 ,
. child = ipmi_dir_table } ,
{ }
} ;
static struct ctl_table_header * ipmi_table_header ;
2005-06-24 12:01:42 +07:00
# endif /* CONFIG_PROC_FS */
2005-04-17 05:20:36 +07:00
/*
* Startup and shutdown functions .
*/
static int ipmi_poweroff_init ( void )
{
2005-09-07 05:18:46 +07:00
int rv ;
2005-04-17 05:20:36 +07:00
2007-07-16 13:41:56 +07:00
printk ( KERN_INFO " Copyright (C) 2004 MontaVista Software - "
2005-09-07 05:18:42 +07:00
" IPMI Powerdown via sys_reboot. \n " ) ;
2005-04-17 05:20:36 +07:00
2005-09-07 05:18:46 +07:00
if ( poweroff_powercycle )
printk ( KERN_INFO PFX " Power cycle is enabled. \n " ) ;
2005-06-24 12:01:42 +07:00
2005-09-07 05:18:46 +07:00
# ifdef CONFIG_PROC_FS
2007-02-14 15:34:09 +07:00
ipmi_table_header = register_sysctl_table ( ipmi_root_table ) ;
2005-09-07 05:18:46 +07:00
if ( ! ipmi_table_header ) {
printk ( KERN_ERR PFX " Unable to register powercycle sysctl \n " ) ;
rv = - ENOMEM ;
goto out_err ;
2005-06-24 12:01:42 +07:00
}
2005-09-07 05:18:46 +07:00
# endif
2005-06-24 12:01:42 +07:00
2005-04-17 05:20:36 +07:00
rv = ipmi_smi_watcher_register ( & smi_watcher ) ;
2006-01-10 11:51:36 +07:00
# ifdef CONFIG_PROC_FS
2005-06-24 12:01:42 +07:00
if ( rv ) {
2005-09-07 05:18:46 +07:00
unregister_sysctl_table ( ipmi_table_header ) ;
2005-04-17 05:20:36 +07:00
printk ( KERN_ERR PFX " Unable to register SMI watcher: %d \n " , rv ) ;
2005-06-24 12:01:42 +07:00
goto out_err ;
}
out_err :
2006-12-07 11:41:20 +07:00
# endif
2005-04-17 05:20:36 +07:00
return rv ;
}
# ifdef MODULE
static __exit void ipmi_poweroff_cleanup ( void )
{
int rv ;
2005-06-24 12:01:42 +07:00
# ifdef CONFIG_PROC_FS
2005-09-07 05:18:46 +07:00
unregister_sysctl_table ( ipmi_table_header ) ;
2005-06-24 12:01:42 +07:00
# endif
2005-04-17 05:20:36 +07:00
ipmi_smi_watcher_unregister ( & smi_watcher ) ;
if ( ready ) {
rv = ipmi_destroy_user ( ipmi_user ) ;
if ( rv )
printk ( KERN_ERR PFX " could not cleanup the IPMI "
" user: 0x%x \n " , rv ) ;
pm_power_off = old_poweroff_func ;
}
}
module_exit ( ipmi_poweroff_cleanup ) ;
# endif
module_init ( ipmi_poweroff_init ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-09-07 05:18:42 +07:00
MODULE_AUTHOR ( " Corey Minyard <minyard@mvista.com> " ) ;
MODULE_DESCRIPTION ( " IPMI Poweroff extension to sys_reboot " ) ;