2007-02-07 23:20:01 +03:00
/*
* PS3 System Manager .
*
* Copyright ( C ) 2007 Sony Computer Entertainment Inc .
* Copyright 2007 Sony Corp .
*
* 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 ; version 2 of the License .
*
* 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 . 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/workqueue.h>
# include <linux/reboot.h>
2007-03-10 02:05:38 +03:00
# include <asm/firmware.h>
2008-03-27 03:38:31 +03:00
# include <asm/lv1call.h>
2007-02-07 23:20:01 +03:00
# include <asm/ps3.h>
2007-03-10 02:05:38 +03:00
2007-02-07 23:20:01 +03:00
# include "vuart.h"
/**
* ps3_sys_manager - PS3 system manager driver .
*
2007-06-16 02:03:54 +04:00
* The system manager provides an asynchronous system event notification
2007-02-07 23:20:01 +03:00
* mechanism for reporting events like thermal alert and button presses to
* guests . It also provides support to control system shutdown and startup .
*
* The actual system manager is implemented as an application running in the
* system policy module in lpar_1 . Guests communicate with the system manager
* through port 2 of the vuart using a simple packet message protocol .
* Messages are comprised of a fixed field header followed by a message
* specific payload .
*/
/**
* struct ps3_sys_manager_header - System manager message header .
* @ version : Header version , currently 1.
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 18:09:05 +03:00
* @ size : Header size in bytes , currently 16.
2007-02-07 23:20:01 +03:00
* @ payload_size : Message payload size in bytes .
* @ service_id : Message type , one of enum ps3_sys_manager_service_id .
2007-06-16 02:03:54 +04:00
* @ request_tag : Unique number to identify reply .
2007-02-07 23:20:01 +03:00
*/
struct ps3_sys_manager_header {
/* version 1 */
u8 version ;
u8 size ;
u16 reserved_1 ;
u32 payload_size ;
u16 service_id ;
2007-06-16 02:03:54 +04:00
u16 reserved_2 ;
u32 request_tag ;
2007-02-07 23:20:01 +03:00
} ;
2007-06-16 02:03:54 +04:00
# define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
static void __maybe_unused _dump_sm_header (
const struct ps3_sys_manager_header * h , const char * func , int line )
{
pr_debug ( " %s:%d: version: %xh \n " , func , line , h - > version ) ;
pr_debug ( " %s:%d: size: %xh \n " , func , line , h - > size ) ;
pr_debug ( " %s:%d: payload_size: %xh \n " , func , line , h - > payload_size ) ;
pr_debug ( " %s:%d: service_id: %xh \n " , func , line , h - > service_id ) ;
pr_debug ( " %s:%d: request_tag: %xh \n " , func , line , h - > request_tag ) ;
}
2007-02-07 23:20:01 +03:00
/**
2007-06-16 02:03:54 +04:00
* @ PS3_SM_RX_MSG_LEN_MIN - Shortest received message length .
* @ PS3_SM_RX_MSG_LEN_MAX - Longest received message length .
2007-02-07 23:20:01 +03:00
*
2007-06-16 02:03:54 +04:00
* Currently all messages received from the system manager are either
* ( 16 bytes header + 8 bytes payload = 24 bytes ) or ( 16 bytes header
2011-03-31 05:57:33 +04:00
* + 16 bytes payload = 32 bytes ) . This knowledge is used to simplify
2007-06-16 02:03:54 +04:00
* the logic .
2007-02-07 23:20:01 +03:00
*/
enum {
2007-06-16 02:03:54 +04:00
PS3_SM_RX_MSG_LEN_MIN = 24 ,
PS3_SM_RX_MSG_LEN_MAX = 32 ,
2007-02-07 23:20:01 +03:00
} ;
/**
* enum ps3_sys_manager_service_id - Message header service_id .
2007-06-16 02:03:54 +04:00
* @ PS3_SM_SERVICE_ID_REQUEST : guest - - > sys_manager .
* @ PS3_SM_SERVICE_ID_REQUEST_ERROR : guest < - - sys_manager .
* @ PS3_SM_SERVICE_ID_COMMAND : guest < - - sys_manager .
* @ PS3_SM_SERVICE_ID_RESPONSE : guest - - > sys_manager .
* @ PS3_SM_SERVICE_ID_SET_ATTR : guest - - > sys_manager .
* @ PS3_SM_SERVICE_ID_EXTERN_EVENT : guest < - - sys_manager .
* @ PS3_SM_SERVICE_ID_SET_NEXT_OP : guest - - > sys_manager .
*
* PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a
* a PS3_SM_SERVICE_ID_REQUEST message . It also seems to be returned when
* a REQUEST message is sent at the wrong time .
2007-02-07 23:20:01 +03:00
*/
enum ps3_sys_manager_service_id {
/* version 1 */
PS3_SM_SERVICE_ID_REQUEST = 1 ,
PS3_SM_SERVICE_ID_RESPONSE = 2 ,
PS3_SM_SERVICE_ID_COMMAND = 3 ,
PS3_SM_SERVICE_ID_EXTERN_EVENT = 4 ,
PS3_SM_SERVICE_ID_SET_NEXT_OP = 5 ,
2007-06-16 02:03:54 +04:00
PS3_SM_SERVICE_ID_REQUEST_ERROR = 6 ,
2007-02-07 23:20:01 +03:00
PS3_SM_SERVICE_ID_SET_ATTR = 8 ,
} ;
/**
* enum ps3_sys_manager_attr - Notification attribute ( bit position mask ) .
* @ PS3_SM_ATTR_POWER : Power button .
* @ PS3_SM_ATTR_RESET : Reset button , not available on retail console .
2010-03-16 13:47:56 +03:00
* @ PS3_SM_ATTR_THERMAL : System thermal alert .
2007-02-07 23:20:01 +03:00
* @ PS3_SM_ATTR_CONTROLLER : Remote controller event .
* @ PS3_SM_ATTR_ALL : Logical OR of all .
*
* The guest tells the system manager which events it is interested in receiving
* notice of by sending the system manager a logical OR of notification
* attributes via the ps3_sys_manager_send_attr ( ) routine .
*/
enum ps3_sys_manager_attr {
/* version 1 */
PS3_SM_ATTR_POWER = 1 ,
PS3_SM_ATTR_RESET = 2 ,
PS3_SM_ATTR_THERMAL = 4 ,
PS3_SM_ATTR_CONTROLLER = 8 , /* bogus? */
PS3_SM_ATTR_ALL = 0x0f ,
} ;
/**
* enum ps3_sys_manager_event - External event type , reported by system manager .
2008-02-09 01:53:07 +03:00
* @ PS3_SM_EVENT_POWER_PRESSED : payload . value =
* enum ps3_sys_manager_button_event .
2007-02-07 23:20:01 +03:00
* @ PS3_SM_EVENT_POWER_RELEASED : payload . value = time pressed in millisec .
2008-02-09 01:53:07 +03:00
* @ PS3_SM_EVENT_RESET_PRESSED : payload . value =
* enum ps3_sys_manager_button_event .
2007-02-07 23:20:01 +03:00
* @ PS3_SM_EVENT_RESET_RELEASED : payload . value = time pressed in millisec .
* @ PS3_SM_EVENT_THERMAL_ALERT : payload . value = thermal zone id .
* @ PS3_SM_EVENT_THERMAL_CLEARED : payload . value = thermal zone id .
*/
enum ps3_sys_manager_event {
/* version 1 */
PS3_SM_EVENT_POWER_PRESSED = 3 ,
PS3_SM_EVENT_POWER_RELEASED = 4 ,
PS3_SM_EVENT_RESET_PRESSED = 5 ,
PS3_SM_EVENT_RESET_RELEASED = 6 ,
PS3_SM_EVENT_THERMAL_ALERT = 7 ,
PS3_SM_EVENT_THERMAL_CLEARED = 8 ,
/* no info on controller events */
} ;
2008-02-09 01:53:07 +03:00
/**
* enum ps3_sys_manager_button_event - Button event payload values .
* @ PS3_SM_BUTTON_EVENT_HARD : Hardware generated event .
* @ PS3_SM_BUTTON_EVENT_SOFT : Software generated event .
*/
enum ps3_sys_manager_button_event {
PS3_SM_BUTTON_EVENT_HARD = 0 ,
PS3_SM_BUTTON_EVENT_SOFT = 1 ,
} ;
2007-02-07 23:20:01 +03:00
/**
* enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed .
*/
enum ps3_sys_manager_next_op {
/* version 3 */
PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1 ,
PS3_SM_NEXT_OP_SYS_REBOOT = 2 ,
PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82 ,
} ;
/**
* enum ps3_sys_manager_wake_source - Next - op wakeup source ( bit position mask ) .
2008-05-01 02:25:30 +04:00
* @ PS3_SM_WAKE_DEFAULT : Disk insert , power button , eject button .
2008-03-27 03:39:04 +03:00
* @ PS3_SM_WAKE_W_O_L : Ether or wireless LAN .
2007-02-07 23:20:01 +03:00
* @ PS3_SM_WAKE_P_O_R : Power on reset .
*
* Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN .
2008-02-09 01:53:01 +03:00
* The system will always wake from the PS3_SM_WAKE_DEFAULT sources .
* Sources listed here are the only ones available to guests in the
* other - os lpar .
2007-02-07 23:20:01 +03:00
*/
enum ps3_sys_manager_wake_source {
/* version 3 */
PS3_SM_WAKE_DEFAULT = 0 ,
2008-03-27 03:39:04 +03:00
PS3_SM_WAKE_W_O_L = 0x00000400 ,
2008-02-09 01:53:01 +03:00
PS3_SM_WAKE_P_O_R = 0x80000000 ,
2007-02-07 23:20:01 +03:00
} ;
2008-03-27 03:39:04 +03:00
/**
* user_wake_sources - User specified wakeup sources .
*
* Logical OR of enum ps3_sys_manager_wake_source types .
*/
static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT ;
2007-02-07 23:20:01 +03:00
/**
* enum ps3_sys_manager_cmd - Command from system manager to guest .
*
* The guest completes the actions needed , then acks or naks the command via
* ps3_sys_manager_send_response ( ) . In the case of @ PS3_SM_CMD_SHUTDOWN ,
* the guest must be fully prepared for a system poweroff prior to acking the
* command .
*/
enum ps3_sys_manager_cmd {
/* version 1 */
PS3_SM_CMD_SHUTDOWN = 1 , /* shutdown guest OS */
} ;
2007-06-16 02:03:54 +04:00
/**
* ps3_sm_force_power_off - Poweroff helper .
*
* A global variable used to force a poweroff when the power button has
* been pressed irrespective of how init handles the ctrl_alt_del signal .
*
*/
static unsigned int ps3_sm_force_power_off ;
2007-02-07 23:20:01 +03:00
/**
* ps3_sys_manager_write - Helper to write a two part message to the vuart .
*
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_write ( struct ps3_system_bus_device * dev ,
2007-02-07 23:20:01 +03:00
const struct ps3_sys_manager_header * header , const void * payload )
{
int result ;
BUG_ON ( header - > version ! = 1 ) ;
BUG_ON ( header - > size ! = 16 ) ;
BUG_ON ( header - > payload_size ! = 8 & & header - > payload_size ! = 16 ) ;
BUG_ON ( header - > service_id > 8 ) ;
result = ps3_vuart_write ( dev , header ,
sizeof ( struct ps3_sys_manager_header ) ) ;
if ( ! result )
result = ps3_vuart_write ( dev , payload , header - > payload_size ) ;
return result ;
}
/**
* ps3_sys_manager_send_attr - Send a ' set attribute ' to the system manager .
*
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_send_attr ( struct ps3_system_bus_device * dev ,
2007-02-07 23:20:01 +03:00
enum ps3_sys_manager_attr attr )
{
2007-06-16 02:03:54 +04:00
struct ps3_sys_manager_header header ;
2007-02-07 23:20:01 +03:00
struct {
u8 version ;
u8 reserved_1 [ 3 ] ;
u32 attribute ;
} payload ;
BUILD_BUG_ON ( sizeof ( payload ) ! = 8 ) ;
dev_dbg ( & dev - > core , " %s:%d: %xh \n " , __func__ , __LINE__ , attr ) ;
2007-06-16 02:03:54 +04:00
memset ( & header , 0 , sizeof ( header ) ) ;
header . version = 1 ;
header . size = 16 ;
header . payload_size = 16 ;
header . service_id = PS3_SM_SERVICE_ID_SET_ATTR ;
2007-02-07 23:20:01 +03:00
memset ( & payload , 0 , sizeof ( payload ) ) ;
payload . version = 1 ;
payload . attribute = attr ;
return ps3_sys_manager_write ( dev , & header , & payload ) ;
}
/**
* ps3_sys_manager_send_next_op - Send a ' set next op ' to the system manager .
*
* Tell the system manager what to do after this lpar is destroyed .
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_send_next_op ( struct ps3_system_bus_device * dev ,
2007-02-07 23:20:01 +03:00
enum ps3_sys_manager_next_op op ,
enum ps3_sys_manager_wake_source wake_source )
{
2007-06-16 02:03:54 +04:00
struct ps3_sys_manager_header header ;
2007-02-07 23:20:01 +03:00
struct {
u8 version ;
u8 type ;
u8 gos_id ;
u8 reserved_1 ;
u32 wake_source ;
u8 reserved_2 [ 8 ] ;
} payload ;
BUILD_BUG_ON ( sizeof ( payload ) ! = 16 ) ;
dev_dbg ( & dev - > core , " %s:%d: (%xh) \n " , __func__ , __LINE__ , op ) ;
2007-06-16 02:03:54 +04:00
memset ( & header , 0 , sizeof ( header ) ) ;
header . version = 1 ;
header . size = 16 ;
header . payload_size = 16 ;
header . service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP ;
2007-02-07 23:20:01 +03:00
memset ( & payload , 0 , sizeof ( payload ) ) ;
payload . version = 3 ;
payload . type = op ;
payload . gos_id = 3 ; /* other os */
payload . wake_source = wake_source ;
return ps3_sys_manager_write ( dev , & header , & payload ) ;
}
/**
* ps3_sys_manager_send_request_shutdown - Send ' request ' to the system manager .
*
* The guest sends this message to request an operation or action of the system
* manager . The reply is a command message from the system manager . In the
* command handler the guest performs the requested operation . The result of
* the command is then communicated back to the system manager with a response
* message .
*
2007-06-16 02:03:54 +04:00
* Currently , the only supported request is the ' shutdown self ' request .
2007-02-07 23:20:01 +03:00
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_send_request_shutdown (
struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
2007-06-16 02:03:54 +04:00
struct ps3_sys_manager_header header ;
2007-02-07 23:20:01 +03:00
struct {
u8 version ;
u8 type ;
u8 gos_id ;
u8 reserved_1 [ 13 ] ;
2007-06-16 02:03:54 +04:00
} payload ;
2007-02-07 23:20:01 +03:00
BUILD_BUG_ON ( sizeof ( payload ) ! = 16 ) ;
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:03:54 +04:00
memset ( & header , 0 , sizeof ( header ) ) ;
header . version = 1 ;
header . size = 16 ;
header . payload_size = 16 ;
header . service_id = PS3_SM_SERVICE_ID_REQUEST ;
memset ( & payload , 0 , sizeof ( payload ) ) ;
payload . version = 1 ;
payload . type = 1 ; /* shutdown */
payload . gos_id = 0 ; /* self */
2007-02-07 23:20:01 +03:00
return ps3_sys_manager_write ( dev , & header , & payload ) ;
}
/**
* ps3_sys_manager_send_response - Send a ' response ' to the system manager .
* @ status : zero = success , others fail .
*
* The guest sends this message to the system manager to acnowledge success or
* failure of a command sent by the system manager .
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_send_response ( struct ps3_system_bus_device * dev ,
2007-02-07 23:20:01 +03:00
u64 status )
{
2007-06-16 02:03:54 +04:00
struct ps3_sys_manager_header header ;
2007-02-07 23:20:01 +03:00
struct {
u8 version ;
u8 reserved_1 [ 3 ] ;
u8 status ;
u8 reserved_2 [ 11 ] ;
} payload ;
BUILD_BUG_ON ( sizeof ( payload ) ! = 16 ) ;
dev_dbg ( & dev - > core , " %s:%d: (%s) \n " , __func__ , __LINE__ ,
( status ? " nak " : " ack " ) ) ;
2007-06-16 02:03:54 +04:00
memset ( & header , 0 , sizeof ( header ) ) ;
header . version = 1 ;
header . size = 16 ;
header . payload_size = 16 ;
header . service_id = PS3_SM_SERVICE_ID_RESPONSE ;
2007-02-07 23:20:01 +03:00
memset ( & payload , 0 , sizeof ( payload ) ) ;
payload . version = 1 ;
payload . status = status ;
return ps3_sys_manager_write ( dev , & header , & payload ) ;
}
/**
* ps3_sys_manager_handle_event - Second stage event msg handler .
*
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_handle_event ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
int result ;
struct {
u8 version ;
u8 type ;
u8 reserved_1 [ 2 ] ;
u32 value ;
u8 reserved_2 [ 8 ] ;
} event ;
BUILD_BUG_ON ( sizeof ( event ) ! = 16 ) ;
result = ps3_vuart_read ( dev , & event , sizeof ( event ) ) ;
2007-06-16 02:03:54 +04:00
BUG_ON ( result & & " need to retry here " ) ;
2007-02-07 23:20:01 +03:00
if ( event . version ! = 1 ) {
dev_dbg ( & dev - > core , " %s:%d: unsupported event version (%u) \n " ,
__func__ , __LINE__ , event . version ) ;
return - EIO ;
}
switch ( event . type ) {
case PS3_SM_EVENT_POWER_PRESSED :
2008-02-09 01:53:07 +03:00
dev_dbg ( & dev - > core , " %s:%d: POWER_PRESSED (%s) \n " ,
__func__ , __LINE__ ,
( event . value = = PS3_SM_BUTTON_EVENT_SOFT ? " soft "
: " hard " ) ) ;
2007-06-16 02:03:54 +04:00
ps3_sm_force_power_off = 1 ;
/*
* A memory barrier is use here to sync memory since
* ps3_sys_manager_final_restart ( ) could be called on
* another cpu .
*/
wmb ( ) ;
kill_cad_pid ( SIGINT , 1 ) ; /* ctrl_alt_del */
2007-02-07 23:20:01 +03:00
break ;
case PS3_SM_EVENT_POWER_RELEASED :
dev_dbg ( & dev - > core , " %s:%d: POWER_RELEASED (%u ms) \n " ,
__func__ , __LINE__ , event . value ) ;
2007-06-16 02:03:54 +04:00
break ;
case PS3_SM_EVENT_RESET_PRESSED :
2008-02-09 01:53:07 +03:00
dev_dbg ( & dev - > core , " %s:%d: RESET_PRESSED (%s) \n " ,
__func__ , __LINE__ ,
( event . value = = PS3_SM_BUTTON_EVENT_SOFT ? " soft "
: " hard " ) ) ;
2007-06-16 02:03:54 +04:00
ps3_sm_force_power_off = 0 ;
/*
* A memory barrier is use here to sync memory since
* ps3_sys_manager_final_restart ( ) could be called on
* another cpu .
*/
wmb ( ) ;
kill_cad_pid ( SIGINT , 1 ) ; /* ctrl_alt_del */
break ;
case PS3_SM_EVENT_RESET_RELEASED :
dev_dbg ( & dev - > core , " %s:%d: RESET_RELEASED (%u ms) \n " ,
__func__ , __LINE__ , event . value ) ;
2007-02-07 23:20:01 +03:00
break ;
case PS3_SM_EVENT_THERMAL_ALERT :
dev_dbg ( & dev - > core , " %s:%d: THERMAL_ALERT (zone %u) \n " ,
__func__ , __LINE__ , event . value ) ;
2008-01-18 23:32:14 +03:00
pr_info ( " PS3 Thermal Alert Zone %u \n " , event . value ) ;
2007-02-07 23:20:01 +03:00
break ;
case PS3_SM_EVENT_THERMAL_CLEARED :
dev_dbg ( & dev - > core , " %s:%d: THERMAL_CLEARED (zone %u) \n " ,
__func__ , __LINE__ , event . value ) ;
break ;
default :
dev_dbg ( & dev - > core , " %s:%d: unknown event (%u) \n " ,
__func__ , __LINE__ , event . type ) ;
return - EIO ;
}
return 0 ;
}
/**
* ps3_sys_manager_handle_cmd - Second stage command msg handler .
*
* The system manager sends this in reply to a ' request ' message from the guest .
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_handle_cmd ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
int result ;
struct {
u8 version ;
u8 type ;
u8 reserved_1 [ 14 ] ;
} cmd ;
BUILD_BUG_ON ( sizeof ( cmd ) ! = 16 ) ;
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
result = ps3_vuart_read ( dev , & cmd , sizeof ( cmd ) ) ;
2007-06-16 02:03:54 +04:00
BUG_ON ( result & & " need to retry here " ) ;
2007-02-07 23:20:01 +03:00
2008-01-18 23:32:14 +03:00
if ( result )
2007-02-07 23:20:01 +03:00
return result ;
if ( cmd . version ! = 1 ) {
dev_dbg ( & dev - > core , " %s:%d: unsupported cmd version (%u) \n " ,
__func__ , __LINE__ , cmd . version ) ;
return - EIO ;
}
if ( cmd . type ! = PS3_SM_CMD_SHUTDOWN ) {
dev_dbg ( & dev - > core , " %s:%d: unknown cmd (%u) \n " ,
__func__ , __LINE__ , cmd . type ) ;
return - EIO ;
}
ps3_sys_manager_send_response ( dev , 0 ) ;
return 0 ;
}
/**
* ps3_sys_manager_handle_msg - First stage msg handler .
*
2007-06-16 02:03:54 +04:00
* Can be called directly to manually poll vuart and pump message handler .
2007-02-07 23:20:01 +03:00
*/
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_handle_msg ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
int result ;
struct ps3_sys_manager_header header ;
result = ps3_vuart_read ( dev , & header ,
sizeof ( struct ps3_sys_manager_header ) ) ;
2008-01-18 23:32:14 +03:00
if ( result )
2007-02-07 23:20:01 +03:00
return result ;
if ( header . version ! = 1 ) {
dev_dbg ( & dev - > core , " %s:%d: unsupported header version (%u) \n " ,
__func__ , __LINE__ , header . version ) ;
2007-06-16 02:03:54 +04:00
dump_sm_header ( & header ) ;
2007-02-07 23:20:01 +03:00
goto fail_header ;
}
BUILD_BUG_ON ( sizeof ( header ) ! = 16 ) ;
2007-06-16 02:03:54 +04:00
if ( header . size ! = 16 | | ( header . payload_size ! = 8
& & header . payload_size ! = 16 ) ) {
dump_sm_header ( & header ) ;
BUG ( ) ;
}
2007-02-07 23:20:01 +03:00
switch ( header . service_id ) {
case PS3_SM_SERVICE_ID_EXTERN_EVENT :
dev_dbg ( & dev - > core , " %s:%d: EVENT \n " , __func__ , __LINE__ ) ;
return ps3_sys_manager_handle_event ( dev ) ;
case PS3_SM_SERVICE_ID_COMMAND :
dev_dbg ( & dev - > core , " %s:%d: COMMAND \n " , __func__ , __LINE__ ) ;
return ps3_sys_manager_handle_cmd ( dev ) ;
2007-06-16 02:03:54 +04:00
case PS3_SM_SERVICE_ID_REQUEST_ERROR :
dev_dbg ( & dev - > core , " %s:%d: REQUEST_ERROR \n " , __func__ ,
__LINE__ ) ;
dump_sm_header ( & header ) ;
break ;
2007-02-07 23:20:01 +03:00
default :
dev_dbg ( & dev - > core , " %s:%d: unknown service_id (%u) \n " ,
__func__ , __LINE__ , header . service_id ) ;
break ;
}
goto fail_id ;
fail_header :
ps3_vuart_clear_rx_bytes ( dev , 0 ) ;
return - EIO ;
fail_id :
ps3_vuart_clear_rx_bytes ( dev , header . payload_size ) ;
return - EIO ;
}
2008-03-27 03:38:31 +03:00
static void ps3_sys_manager_fin ( struct ps3_system_bus_device * dev )
{
ps3_sys_manager_send_request_shutdown ( dev ) ;
pr_emerg ( " System Halted, OK to turn off power \n " ) ;
while ( ps3_sys_manager_handle_msg ( dev ) ) {
/* pause until next DEC interrupt */
lv1_pause ( 0 ) ;
}
while ( 1 ) {
/* pause, ignoring DEC interrupt */
lv1_pause ( 1 ) ;
}
}
2007-02-07 23:20:01 +03:00
/**
2007-06-16 02:03:54 +04:00
* ps3_sys_manager_final_power_off - The final platform machine_power_off routine .
2007-02-07 23:20:01 +03:00
*
2007-06-16 02:03:54 +04:00
* This routine never returns . The routine disables asynchronous vuart reads
2007-02-07 23:20:01 +03:00
* then spins calling ps3_sys_manager_handle_msg ( ) to receive and acknowledge
* the shutdown command sent from the system manager . Soon after the
* acknowledgement is sent the lpar is destroyed by the HV . This routine
2007-06-16 02:03:54 +04:00
* should only be called from ps3_power_off ( ) through
* ps3_sys_manager_ops . power_off .
2007-02-07 23:20:01 +03:00
*/
2007-06-16 02:03:54 +04:00
static void ps3_sys_manager_final_power_off ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
2007-06-16 02:03:54 +04:00
BUG_ON ( ! dev ) ;
2007-02-07 23:20:01 +03:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
ps3_vuart_cancel_async ( dev ) ;
2007-06-16 02:03:54 +04:00
ps3_sys_manager_send_next_op ( dev , PS3_SM_NEXT_OP_SYS_SHUTDOWN ,
2008-03-27 03:39:04 +03:00
user_wake_sources ) ;
2007-02-07 23:20:01 +03:00
2008-03-27 03:38:31 +03:00
ps3_sys_manager_fin ( dev ) ;
2007-02-07 23:20:01 +03:00
}
/**
2007-06-16 02:03:54 +04:00
* ps3_sys_manager_final_restart - The final platform machine_restart routine .
2007-02-07 23:20:01 +03:00
*
2007-06-16 02:03:54 +04:00
* This routine never returns . The routine disables asynchronous vuart reads
2007-02-07 23:20:01 +03:00
* then spins calling ps3_sys_manager_handle_msg ( ) to receive and acknowledge
* the shutdown command sent from the system manager . Soon after the
* acknowledgement is sent the lpar is destroyed by the HV . This routine
2007-06-16 02:03:54 +04:00
* should only be called from ps3_restart ( ) through ps3_sys_manager_ops . restart .
2007-02-07 23:20:01 +03:00
*/
2007-06-16 02:03:54 +04:00
static void ps3_sys_manager_final_restart ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
2007-06-16 02:03:54 +04:00
BUG_ON ( ! dev ) ;
2007-02-07 23:20:01 +03:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:03:54 +04:00
/* Check if we got here via a power button event. */
if ( ps3_sm_force_power_off ) {
dev_dbg ( & dev - > core , " %s:%d: forcing poweroff \n " ,
__func__ , __LINE__ ) ;
ps3_sys_manager_final_power_off ( dev ) ;
}
2007-02-07 23:20:01 +03:00
ps3_vuart_cancel_async ( dev ) ;
2007-06-16 02:03:54 +04:00
ps3_sys_manager_send_attr ( dev , 0 ) ;
2008-02-09 01:52:55 +03:00
ps3_sys_manager_send_next_op ( dev , PS3_SM_NEXT_OP_SYS_REBOOT ,
2008-03-27 03:39:04 +03:00
user_wake_sources ) ;
2007-02-07 23:20:01 +03:00
2008-03-27 03:38:31 +03:00
ps3_sys_manager_fin ( dev ) ;
2007-02-07 23:20:01 +03:00
}
2008-03-27 03:39:04 +03:00
/**
* ps3_sys_manager_get_wol - Get wake - on - lan setting .
*/
int ps3_sys_manager_get_wol ( void )
{
pr_debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
return ( user_wake_sources & PS3_SM_WAKE_W_O_L ) ! = 0 ;
}
EXPORT_SYMBOL_GPL ( ps3_sys_manager_get_wol ) ;
/**
* ps3_sys_manager_set_wol - Set wake - on - lan setting .
*/
void ps3_sys_manager_set_wol ( int state )
{
static DEFINE_MUTEX ( mutex ) ;
mutex_lock ( & mutex ) ;
pr_debug ( " %s:%d: %d \n " , __func__ , __LINE__ , state ) ;
if ( state )
user_wake_sources | = PS3_SM_WAKE_W_O_L ;
else
user_wake_sources & = ~ PS3_SM_WAKE_W_O_L ;
mutex_unlock ( & mutex ) ;
}
EXPORT_SYMBOL_GPL ( ps3_sys_manager_set_wol ) ;
2007-06-16 02:03:54 +04:00
/**
* ps3_sys_manager_work - Asynchronous read handler .
*
* Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port .
*/
static void ps3_sys_manager_work ( struct ps3_system_bus_device * dev )
{
ps3_sys_manager_handle_msg ( dev ) ;
ps3_vuart_read_async ( dev , PS3_SM_RX_MSG_LEN_MIN ) ;
}
2012-12-22 03:14:44 +04:00
static int ps3_sys_manager_probe ( struct ps3_system_bus_device * dev )
2007-02-07 23:20:01 +03:00
{
int result ;
2007-06-16 02:03:54 +04:00
struct ps3_sys_manager_ops ops ;
2007-02-07 23:20:01 +03:00
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
2007-06-16 02:03:54 +04:00
ops . power_off = ps3_sys_manager_final_power_off ;
ops . restart = ps3_sys_manager_final_restart ;
ops . dev = dev ;
/* ps3_sys_manager_register_ops copies ops. */
ps3_sys_manager_register_ops ( & ops ) ;
2007-02-07 23:20:01 +03:00
result = ps3_sys_manager_send_attr ( dev , PS3_SM_ATTR_ALL ) ;
BUG_ON ( result ) ;
2007-06-16 02:03:54 +04:00
result = ps3_vuart_read_async ( dev , PS3_SM_RX_MSG_LEN_MIN ) ;
2007-02-07 23:20:01 +03:00
BUG_ON ( result ) ;
return result ;
}
2007-06-16 02:03:54 +04:00
static int ps3_sys_manager_remove ( struct ps3_system_bus_device * dev )
{
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
return 0 ;
}
static void ps3_sys_manager_shutdown ( struct ps3_system_bus_device * dev )
{
dev_dbg ( & dev - > core , " %s:%d \n " , __func__ , __LINE__ ) ;
}
2007-02-07 23:20:01 +03:00
static struct ps3_vuart_port_driver ps3_sys_manager = {
2007-06-16 02:03:54 +04:00
. core . match_id = PS3_MATCH_ID_SYSTEM_MANAGER ,
. core . core . name = " ps3_sys_manager " ,
2007-02-07 23:20:01 +03:00
. probe = ps3_sys_manager_probe ,
2007-06-16 02:03:54 +04:00
. remove = ps3_sys_manager_remove ,
. shutdown = ps3_sys_manager_shutdown ,
. work = ps3_sys_manager_work ,
2007-02-07 23:20:01 +03:00
} ;
static int __init ps3_sys_manager_init ( void )
{
2007-03-10 02:05:38 +03:00
if ( ! firmware_has_feature ( FW_FEATURE_PS3_LV1 ) )
return - ENODEV ;
2007-02-07 23:20:01 +03:00
return ps3_vuart_port_driver_register ( & ps3_sys_manager ) ;
}
module_init ( ps3_sys_manager_init ) ;
2007-06-16 02:03:54 +04:00
/* Module remove not supported. */
2008-02-09 01:53:01 +03:00
MODULE_AUTHOR ( " Sony Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " PS3 System Manager " ) ;
2007-06-16 02:03:54 +04:00
MODULE_ALIAS ( PS3_MODULE_ALIAS_SYSTEM_MANAGER ) ;