xref: /openbmc/linux/drivers/ps3/ps3-sys-manager.c (revision 873e65bc)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
224828550SGeert Uytterhoeven /*
324828550SGeert Uytterhoeven  *  PS3 System Manager.
424828550SGeert Uytterhoeven  *
524828550SGeert Uytterhoeven  *  Copyright (C) 2007 Sony Computer Entertainment Inc.
624828550SGeert Uytterhoeven  *  Copyright 2007 Sony Corp.
724828550SGeert Uytterhoeven  */
824828550SGeert Uytterhoeven 
924828550SGeert Uytterhoeven #include <linux/kernel.h>
1024828550SGeert Uytterhoeven #include <linux/module.h>
1124828550SGeert Uytterhoeven #include <linux/workqueue.h>
1224828550SGeert Uytterhoeven #include <linux/reboot.h>
133f07c014SIngo Molnar #include <linux/sched/signal.h>
1424828550SGeert Uytterhoeven 
1524828550SGeert Uytterhoeven #include <asm/firmware.h>
16ca052f79SGeert Uytterhoeven #include <asm/lv1call.h>
1724828550SGeert Uytterhoeven #include <asm/ps3.h>
1824828550SGeert Uytterhoeven 
1924828550SGeert Uytterhoeven #include "vuart.h"
2024828550SGeert Uytterhoeven 
2124828550SGeert Uytterhoeven /**
2224828550SGeert Uytterhoeven  * ps3_sys_manager - PS3 system manager driver.
2324828550SGeert Uytterhoeven  *
2424828550SGeert Uytterhoeven  * The system manager provides an asynchronous system event notification
2524828550SGeert Uytterhoeven  * mechanism for reporting events like thermal alert and button presses to
2624828550SGeert Uytterhoeven  * guests.  It also provides support to control system shutdown and startup.
2724828550SGeert Uytterhoeven  *
2824828550SGeert Uytterhoeven  * The actual system manager is implemented as an application running in the
2924828550SGeert Uytterhoeven  * system policy module in lpar_1.  Guests communicate with the system manager
3024828550SGeert Uytterhoeven  * through port 2 of the vuart using a simple packet message protocol.
3124828550SGeert Uytterhoeven  * Messages are comprised of a fixed field header followed by a message
3224828550SGeert Uytterhoeven  * specific payload.
3324828550SGeert Uytterhoeven  */
3424828550SGeert Uytterhoeven 
3524828550SGeert Uytterhoeven /**
3624828550SGeert Uytterhoeven  * struct ps3_sys_manager_header - System manager message header.
3724828550SGeert Uytterhoeven  * @version: Header version, currently 1.
38af901ca1SAndré Goddard Rosa  * @size: Header size in bytes, currently 16.
3924828550SGeert Uytterhoeven  * @payload_size: Message payload size in bytes.
4024828550SGeert Uytterhoeven  * @service_id: Message type, one of enum ps3_sys_manager_service_id.
4124828550SGeert Uytterhoeven  * @request_tag: Unique number to identify reply.
4224828550SGeert Uytterhoeven  */
4324828550SGeert Uytterhoeven 
4424828550SGeert Uytterhoeven struct ps3_sys_manager_header {
4524828550SGeert Uytterhoeven 	/* version 1 */
4624828550SGeert Uytterhoeven 	u8 version;
4724828550SGeert Uytterhoeven 	u8 size;
4824828550SGeert Uytterhoeven 	u16 reserved_1;
4924828550SGeert Uytterhoeven 	u32 payload_size;
5024828550SGeert Uytterhoeven 	u16 service_id;
5124828550SGeert Uytterhoeven 	u16 reserved_2;
5224828550SGeert Uytterhoeven 	u32 request_tag;
5324828550SGeert Uytterhoeven };
5424828550SGeert Uytterhoeven 
5524828550SGeert Uytterhoeven #define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
_dump_sm_header(const struct ps3_sys_manager_header * h,const char * func,int line)5624828550SGeert Uytterhoeven static void __maybe_unused _dump_sm_header(
5724828550SGeert Uytterhoeven 	const struct ps3_sys_manager_header *h, const char *func, int line)
5824828550SGeert Uytterhoeven {
5924828550SGeert Uytterhoeven 	pr_debug("%s:%d: version:      %xh\n", func, line, h->version);
6024828550SGeert Uytterhoeven 	pr_debug("%s:%d: size:         %xh\n", func, line, h->size);
6124828550SGeert Uytterhoeven 	pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size);
6224828550SGeert Uytterhoeven 	pr_debug("%s:%d: service_id:   %xh\n", func, line, h->service_id);
6324828550SGeert Uytterhoeven 	pr_debug("%s:%d: request_tag:  %xh\n", func, line, h->request_tag);
6424828550SGeert Uytterhoeven }
6524828550SGeert Uytterhoeven 
6624828550SGeert Uytterhoeven /**
6724828550SGeert Uytterhoeven  * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length.
6824828550SGeert Uytterhoeven  * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length.
6924828550SGeert Uytterhoeven  *
7024828550SGeert Uytterhoeven  * Currently all messages received from the system manager are either
7124828550SGeert Uytterhoeven  * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header
7225985edcSLucas De Marchi  * + 16 bytes payload = 32 bytes).  This knowledge is used to simplify
7324828550SGeert Uytterhoeven  * the logic.
7424828550SGeert Uytterhoeven  */
7524828550SGeert Uytterhoeven 
7624828550SGeert Uytterhoeven enum {
7724828550SGeert Uytterhoeven 	PS3_SM_RX_MSG_LEN_MIN = 24,
7824828550SGeert Uytterhoeven 	PS3_SM_RX_MSG_LEN_MAX = 32,
7924828550SGeert Uytterhoeven };
8024828550SGeert Uytterhoeven 
8124828550SGeert Uytterhoeven /**
8224828550SGeert Uytterhoeven  * enum ps3_sys_manager_service_id - Message header service_id.
8324828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_REQUEST:       guest --> sys_manager.
8424828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager.
8524828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_COMMAND:       guest <-- sys_manager.
8624828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_RESPONSE:      guest --> sys_manager.
8724828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_SET_ATTR:      guest --> sys_manager.
8824828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_EXTERN_EVENT:  guest <-- sys_manager.
8924828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_SET_NEXT_OP:   guest --> sys_manager.
9024828550SGeert Uytterhoeven  *
9124828550SGeert Uytterhoeven  * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a
9224828550SGeert Uytterhoeven  * a PS3_SM_SERVICE_ID_REQUEST message.  It also seems to be returned when
9324828550SGeert Uytterhoeven  * a REQUEST message is sent at the wrong time.
9424828550SGeert Uytterhoeven  */
9524828550SGeert Uytterhoeven 
9624828550SGeert Uytterhoeven enum ps3_sys_manager_service_id {
9724828550SGeert Uytterhoeven 	/* version 1 */
9824828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_REQUEST = 1,
9924828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_RESPONSE = 2,
10024828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_COMMAND = 3,
10124828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
10224828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
10324828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_REQUEST_ERROR = 6,
10424828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_SET_ATTR = 8,
10524828550SGeert Uytterhoeven };
10624828550SGeert Uytterhoeven 
10724828550SGeert Uytterhoeven /**
10824828550SGeert Uytterhoeven  * enum ps3_sys_manager_attr - Notification attribute (bit position mask).
10924828550SGeert Uytterhoeven  * @PS3_SM_ATTR_POWER: Power button.
11024828550SGeert Uytterhoeven  * @PS3_SM_ATTR_RESET: Reset button, not available on retail console.
11188393161SThomas Weber  * @PS3_SM_ATTR_THERMAL: System thermal alert.
11224828550SGeert Uytterhoeven  * @PS3_SM_ATTR_CONTROLLER: Remote controller event.
11324828550SGeert Uytterhoeven  * @PS3_SM_ATTR_ALL: Logical OR of all.
11424828550SGeert Uytterhoeven  *
11524828550SGeert Uytterhoeven  * The guest tells the system manager which events it is interested in receiving
11624828550SGeert Uytterhoeven  * notice of by sending the system manager a logical OR of notification
11724828550SGeert Uytterhoeven  * attributes via the ps3_sys_manager_send_attr() routine.
11824828550SGeert Uytterhoeven  */
11924828550SGeert Uytterhoeven 
12024828550SGeert Uytterhoeven enum ps3_sys_manager_attr {
12124828550SGeert Uytterhoeven 	/* version 1 */
12224828550SGeert Uytterhoeven 	PS3_SM_ATTR_POWER = 1,
12324828550SGeert Uytterhoeven 	PS3_SM_ATTR_RESET = 2,
12424828550SGeert Uytterhoeven 	PS3_SM_ATTR_THERMAL = 4,
12524828550SGeert Uytterhoeven 	PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
12624828550SGeert Uytterhoeven 	PS3_SM_ATTR_ALL = 0x0f,
12724828550SGeert Uytterhoeven };
12824828550SGeert Uytterhoeven 
12924828550SGeert Uytterhoeven /**
13024828550SGeert Uytterhoeven  * enum ps3_sys_manager_event - External event type, reported by system manager.
131ea24608fSGeoff Levand  * @PS3_SM_EVENT_POWER_PRESSED: payload.value =
132ea24608fSGeoff Levand  *  enum ps3_sys_manager_button_event.
13324828550SGeert Uytterhoeven  * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec.
134ea24608fSGeoff Levand  * @PS3_SM_EVENT_RESET_PRESSED: payload.value =
135ea24608fSGeoff Levand  *  enum ps3_sys_manager_button_event.
13624828550SGeert Uytterhoeven  * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec.
13724828550SGeert Uytterhoeven  * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id.
13824828550SGeert Uytterhoeven  * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id.
13924828550SGeert Uytterhoeven  */
14024828550SGeert Uytterhoeven 
14124828550SGeert Uytterhoeven enum ps3_sys_manager_event {
14224828550SGeert Uytterhoeven 	/* version 1 */
14324828550SGeert Uytterhoeven 	PS3_SM_EVENT_POWER_PRESSED = 3,
14424828550SGeert Uytterhoeven 	PS3_SM_EVENT_POWER_RELEASED = 4,
14524828550SGeert Uytterhoeven 	PS3_SM_EVENT_RESET_PRESSED = 5,
14624828550SGeert Uytterhoeven 	PS3_SM_EVENT_RESET_RELEASED = 6,
14724828550SGeert Uytterhoeven 	PS3_SM_EVENT_THERMAL_ALERT = 7,
14824828550SGeert Uytterhoeven 	PS3_SM_EVENT_THERMAL_CLEARED = 8,
14924828550SGeert Uytterhoeven 	/* no info on controller events */
15024828550SGeert Uytterhoeven };
15124828550SGeert Uytterhoeven 
15224828550SGeert Uytterhoeven /**
153ea24608fSGeoff Levand  * enum ps3_sys_manager_button_event - Button event payload values.
154ea24608fSGeoff Levand  * @PS3_SM_BUTTON_EVENT_HARD: Hardware generated event.
155ea24608fSGeoff Levand  * @PS3_SM_BUTTON_EVENT_SOFT: Software generated event.
156ea24608fSGeoff Levand  */
157ea24608fSGeoff Levand 
158ea24608fSGeoff Levand enum ps3_sys_manager_button_event {
159ea24608fSGeoff Levand 	PS3_SM_BUTTON_EVENT_HARD = 0,
160ea24608fSGeoff Levand 	PS3_SM_BUTTON_EVENT_SOFT = 1,
161ea24608fSGeoff Levand };
162ea24608fSGeoff Levand 
163ea24608fSGeoff Levand /**
16424828550SGeert Uytterhoeven  * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed.
16524828550SGeert Uytterhoeven  */
16624828550SGeert Uytterhoeven 
16724828550SGeert Uytterhoeven enum ps3_sys_manager_next_op {
16824828550SGeert Uytterhoeven 	/* version 3 */
16924828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
17024828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_SYS_REBOOT = 2,
17124828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
17224828550SGeert Uytterhoeven };
17324828550SGeert Uytterhoeven 
17424828550SGeert Uytterhoeven /**
17524828550SGeert Uytterhoeven  * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask).
1765442381cSGeoff Levand  * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button.
1771c43d265SGeoff Levand  * @PS3_SM_WAKE_W_O_L: Ether or wireless LAN.
17824828550SGeert Uytterhoeven  * @PS3_SM_WAKE_P_O_R: Power on reset.
17924828550SGeert Uytterhoeven  *
18024828550SGeert Uytterhoeven  * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN.
18150dad902SGeoff Levand  * The system will always wake from the PS3_SM_WAKE_DEFAULT sources.
18250dad902SGeoff Levand  * Sources listed here are the only ones available to guests in the
18350dad902SGeoff Levand  * other-os lpar.
18424828550SGeert Uytterhoeven  */
18524828550SGeert Uytterhoeven 
18624828550SGeert Uytterhoeven enum ps3_sys_manager_wake_source {
18724828550SGeert Uytterhoeven 	/* version 3 */
18824828550SGeert Uytterhoeven 	PS3_SM_WAKE_DEFAULT   = 0,
1891c43d265SGeoff Levand 	PS3_SM_WAKE_W_O_L     = 0x00000400,
19050dad902SGeoff Levand 	PS3_SM_WAKE_P_O_R     = 0x80000000,
19124828550SGeert Uytterhoeven };
19224828550SGeert Uytterhoeven 
19324828550SGeert Uytterhoeven /**
1941c43d265SGeoff Levand  * user_wake_sources - User specified wakeup sources.
1951c43d265SGeoff Levand  *
1961c43d265SGeoff Levand  * Logical OR of enum ps3_sys_manager_wake_source types.
1971c43d265SGeoff Levand  */
1981c43d265SGeoff Levand 
1991c43d265SGeoff Levand static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
2001c43d265SGeoff Levand 
2011c43d265SGeoff Levand /**
20224828550SGeert Uytterhoeven  * enum ps3_sys_manager_cmd - Command from system manager to guest.
20324828550SGeert Uytterhoeven  *
20424828550SGeert Uytterhoeven  * The guest completes the actions needed, then acks or naks the command via
20524828550SGeert Uytterhoeven  * ps3_sys_manager_send_response().  In the case of @PS3_SM_CMD_SHUTDOWN,
20624828550SGeert Uytterhoeven  * the guest must be fully prepared for a system poweroff prior to acking the
20724828550SGeert Uytterhoeven  * command.
20824828550SGeert Uytterhoeven  */
20924828550SGeert Uytterhoeven 
21024828550SGeert Uytterhoeven enum ps3_sys_manager_cmd {
21124828550SGeert Uytterhoeven 	/* version 1 */
21224828550SGeert Uytterhoeven 	PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
21324828550SGeert Uytterhoeven };
21424828550SGeert Uytterhoeven 
21524828550SGeert Uytterhoeven /**
21624828550SGeert Uytterhoeven  * ps3_sm_force_power_off - Poweroff helper.
21724828550SGeert Uytterhoeven  *
21824828550SGeert Uytterhoeven  * A global variable used to force a poweroff when the power button has
21924828550SGeert Uytterhoeven  * been pressed irrespective of how init handles the ctrl_alt_del signal.
22024828550SGeert Uytterhoeven  *
22124828550SGeert Uytterhoeven  */
22224828550SGeert Uytterhoeven 
22324828550SGeert Uytterhoeven static unsigned int ps3_sm_force_power_off;
22424828550SGeert Uytterhoeven 
22524828550SGeert Uytterhoeven /**
22624828550SGeert Uytterhoeven  * ps3_sys_manager_write - Helper to write a two part message to the vuart.
22724828550SGeert Uytterhoeven  *
22824828550SGeert Uytterhoeven  */
22924828550SGeert Uytterhoeven 
ps3_sys_manager_write(struct ps3_system_bus_device * dev,const struct ps3_sys_manager_header * header,const void * payload)23024828550SGeert Uytterhoeven static int ps3_sys_manager_write(struct ps3_system_bus_device *dev,
23124828550SGeert Uytterhoeven 	const struct ps3_sys_manager_header *header, const void *payload)
23224828550SGeert Uytterhoeven {
23324828550SGeert Uytterhoeven 	int result;
23424828550SGeert Uytterhoeven 
23524828550SGeert Uytterhoeven 	BUG_ON(header->version != 1);
23624828550SGeert Uytterhoeven 	BUG_ON(header->size != 16);
23724828550SGeert Uytterhoeven 	BUG_ON(header->payload_size != 8 && header->payload_size != 16);
23824828550SGeert Uytterhoeven 	BUG_ON(header->service_id > 8);
23924828550SGeert Uytterhoeven 
24024828550SGeert Uytterhoeven 	result = ps3_vuart_write(dev, header,
24124828550SGeert Uytterhoeven 		sizeof(struct ps3_sys_manager_header));
24224828550SGeert Uytterhoeven 
24324828550SGeert Uytterhoeven 	if (!result)
24424828550SGeert Uytterhoeven 		result = ps3_vuart_write(dev, payload, header->payload_size);
24524828550SGeert Uytterhoeven 
24624828550SGeert Uytterhoeven 	return result;
24724828550SGeert Uytterhoeven }
24824828550SGeert Uytterhoeven 
24924828550SGeert Uytterhoeven /**
25024828550SGeert Uytterhoeven  * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager.
25124828550SGeert Uytterhoeven  *
25224828550SGeert Uytterhoeven  */
25324828550SGeert Uytterhoeven 
ps3_sys_manager_send_attr(struct ps3_system_bus_device * dev,enum ps3_sys_manager_attr attr)25424828550SGeert Uytterhoeven static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
25524828550SGeert Uytterhoeven 	enum ps3_sys_manager_attr attr)
25624828550SGeert Uytterhoeven {
25724828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
25824828550SGeert Uytterhoeven 	struct {
25924828550SGeert Uytterhoeven 		u8 version;
26024828550SGeert Uytterhoeven 		u8 reserved_1[3];
26124828550SGeert Uytterhoeven 		u32 attribute;
26224828550SGeert Uytterhoeven 	} payload;
26324828550SGeert Uytterhoeven 
26424828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 8);
26524828550SGeert Uytterhoeven 
26624828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
26724828550SGeert Uytterhoeven 
26824828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
26924828550SGeert Uytterhoeven 	header.version = 1;
27024828550SGeert Uytterhoeven 	header.size = 16;
27124828550SGeert Uytterhoeven 	header.payload_size = 16;
27224828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_SET_ATTR;
27324828550SGeert Uytterhoeven 
27424828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
27524828550SGeert Uytterhoeven 	payload.version = 1;
27624828550SGeert Uytterhoeven 	payload.attribute = attr;
27724828550SGeert Uytterhoeven 
27824828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
27924828550SGeert Uytterhoeven }
28024828550SGeert Uytterhoeven 
28124828550SGeert Uytterhoeven /**
28224828550SGeert Uytterhoeven  * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager.
28324828550SGeert Uytterhoeven  *
28424828550SGeert Uytterhoeven  * Tell the system manager what to do after this lpar is destroyed.
28524828550SGeert Uytterhoeven  */
28624828550SGeert Uytterhoeven 
ps3_sys_manager_send_next_op(struct ps3_system_bus_device * dev,enum ps3_sys_manager_next_op op,enum ps3_sys_manager_wake_source wake_source)28724828550SGeert Uytterhoeven static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
28824828550SGeert Uytterhoeven 	enum ps3_sys_manager_next_op op,
28924828550SGeert Uytterhoeven 	enum ps3_sys_manager_wake_source wake_source)
29024828550SGeert Uytterhoeven {
29124828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
29224828550SGeert Uytterhoeven 	struct {
29324828550SGeert Uytterhoeven 		u8 version;
29424828550SGeert Uytterhoeven 		u8 type;
29524828550SGeert Uytterhoeven 		u8 gos_id;
29624828550SGeert Uytterhoeven 		u8 reserved_1;
29724828550SGeert Uytterhoeven 		u32 wake_source;
29824828550SGeert Uytterhoeven 		u8 reserved_2[8];
29924828550SGeert Uytterhoeven 	} payload;
30024828550SGeert Uytterhoeven 
30124828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
30224828550SGeert Uytterhoeven 
30324828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
30424828550SGeert Uytterhoeven 
30524828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
30624828550SGeert Uytterhoeven 	header.version = 1;
30724828550SGeert Uytterhoeven 	header.size = 16;
30824828550SGeert Uytterhoeven 	header.payload_size = 16;
30924828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP;
31024828550SGeert Uytterhoeven 
31124828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
31224828550SGeert Uytterhoeven 	payload.version = 3;
31324828550SGeert Uytterhoeven 	payload.type = op;
31424828550SGeert Uytterhoeven 	payload.gos_id = 3; /* other os */
31524828550SGeert Uytterhoeven 	payload.wake_source = wake_source;
31624828550SGeert Uytterhoeven 
31724828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
31824828550SGeert Uytterhoeven }
31924828550SGeert Uytterhoeven 
32024828550SGeert Uytterhoeven /**
32124828550SGeert Uytterhoeven  * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager.
32224828550SGeert Uytterhoeven  *
32324828550SGeert Uytterhoeven  * The guest sends this message to request an operation or action of the system
32424828550SGeert Uytterhoeven  * manager.  The reply is a command message from the system manager.  In the
32524828550SGeert Uytterhoeven  * command handler the guest performs the requested operation.  The result of
32624828550SGeert Uytterhoeven  * the command is then communicated back to the system manager with a response
32724828550SGeert Uytterhoeven  * message.
32824828550SGeert Uytterhoeven  *
32924828550SGeert Uytterhoeven  * Currently, the only supported request is the 'shutdown self' request.
33024828550SGeert Uytterhoeven  */
33124828550SGeert Uytterhoeven 
ps3_sys_manager_send_request_shutdown(struct ps3_system_bus_device * dev)33224828550SGeert Uytterhoeven static int ps3_sys_manager_send_request_shutdown(
33324828550SGeert Uytterhoeven 	struct ps3_system_bus_device *dev)
33424828550SGeert Uytterhoeven {
33524828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
33624828550SGeert Uytterhoeven 	struct {
33724828550SGeert Uytterhoeven 		u8 version;
33824828550SGeert Uytterhoeven 		u8 type;
33924828550SGeert Uytterhoeven 		u8 gos_id;
34024828550SGeert Uytterhoeven 		u8 reserved_1[13];
34124828550SGeert Uytterhoeven 	} payload;
34224828550SGeert Uytterhoeven 
34324828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
34424828550SGeert Uytterhoeven 
34524828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
34624828550SGeert Uytterhoeven 
34724828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
34824828550SGeert Uytterhoeven 	header.version = 1;
34924828550SGeert Uytterhoeven 	header.size = 16;
35024828550SGeert Uytterhoeven 	header.payload_size = 16;
35124828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_REQUEST;
35224828550SGeert Uytterhoeven 
35324828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
35424828550SGeert Uytterhoeven 	payload.version = 1;
35524828550SGeert Uytterhoeven 	payload.type = 1; /* shutdown */
35624828550SGeert Uytterhoeven 	payload.gos_id = 0; /* self */
35724828550SGeert Uytterhoeven 
35824828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
35924828550SGeert Uytterhoeven }
36024828550SGeert Uytterhoeven 
36124828550SGeert Uytterhoeven /**
36224828550SGeert Uytterhoeven  * ps3_sys_manager_send_response - Send a 'response' to the system manager.
36324828550SGeert Uytterhoeven  * @status: zero = success, others fail.
36424828550SGeert Uytterhoeven  *
36524828550SGeert Uytterhoeven  * The guest sends this message to the system manager to acnowledge success or
36624828550SGeert Uytterhoeven  * failure of a command sent by the system manager.
36724828550SGeert Uytterhoeven  */
36824828550SGeert Uytterhoeven 
ps3_sys_manager_send_response(struct ps3_system_bus_device * dev,u64 status)36924828550SGeert Uytterhoeven static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
37024828550SGeert Uytterhoeven 	u64 status)
37124828550SGeert Uytterhoeven {
37224828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
37324828550SGeert Uytterhoeven 	struct {
37424828550SGeert Uytterhoeven 		u8 version;
37524828550SGeert Uytterhoeven 		u8 reserved_1[3];
37624828550SGeert Uytterhoeven 		u8 status;
37724828550SGeert Uytterhoeven 		u8 reserved_2[11];
37824828550SGeert Uytterhoeven 	} payload;
37924828550SGeert Uytterhoeven 
38024828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
38124828550SGeert Uytterhoeven 
38224828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
38324828550SGeert Uytterhoeven 		(status ? "nak" : "ack"));
38424828550SGeert Uytterhoeven 
38524828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
38624828550SGeert Uytterhoeven 	header.version = 1;
38724828550SGeert Uytterhoeven 	header.size = 16;
38824828550SGeert Uytterhoeven 	header.payload_size = 16;
38924828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_RESPONSE;
39024828550SGeert Uytterhoeven 
39124828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
39224828550SGeert Uytterhoeven 	payload.version = 1;
39324828550SGeert Uytterhoeven 	payload.status = status;
39424828550SGeert Uytterhoeven 
39524828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
39624828550SGeert Uytterhoeven }
39724828550SGeert Uytterhoeven 
39824828550SGeert Uytterhoeven /**
39924828550SGeert Uytterhoeven  * ps3_sys_manager_handle_event - Second stage event msg handler.
40024828550SGeert Uytterhoeven  *
40124828550SGeert Uytterhoeven  */
40224828550SGeert Uytterhoeven 
ps3_sys_manager_handle_event(struct ps3_system_bus_device * dev)40324828550SGeert Uytterhoeven static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev)
40424828550SGeert Uytterhoeven {
40524828550SGeert Uytterhoeven 	int result;
40624828550SGeert Uytterhoeven 	struct {
40724828550SGeert Uytterhoeven 		u8 version;
40824828550SGeert Uytterhoeven 		u8 type;
40924828550SGeert Uytterhoeven 		u8 reserved_1[2];
41024828550SGeert Uytterhoeven 		u32 value;
41124828550SGeert Uytterhoeven 		u8 reserved_2[8];
41224828550SGeert Uytterhoeven 	} event;
41324828550SGeert Uytterhoeven 
41424828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(event) != 16);
41524828550SGeert Uytterhoeven 
41624828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &event, sizeof(event));
41724828550SGeert Uytterhoeven 	BUG_ON(result && "need to retry here");
41824828550SGeert Uytterhoeven 
41924828550SGeert Uytterhoeven 	if (event.version != 1) {
42024828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
42124828550SGeert Uytterhoeven 			__func__, __LINE__, event.version);
42224828550SGeert Uytterhoeven 		return -EIO;
42324828550SGeert Uytterhoeven 	}
42424828550SGeert Uytterhoeven 
42524828550SGeert Uytterhoeven 	switch (event.type) {
42624828550SGeert Uytterhoeven 	case PS3_SM_EVENT_POWER_PRESSED:
427ea24608fSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n",
428ea24608fSGeoff Levand 			__func__, __LINE__,
429ea24608fSGeoff Levand 			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
430ea24608fSGeoff Levand 			: "hard"));
43124828550SGeert Uytterhoeven 		ps3_sm_force_power_off = 1;
43224828550SGeert Uytterhoeven 		/*
43324828550SGeert Uytterhoeven 		 * A memory barrier is use here to sync memory since
43424828550SGeert Uytterhoeven 		 * ps3_sys_manager_final_restart() could be called on
43524828550SGeert Uytterhoeven 		 * another cpu.
43624828550SGeert Uytterhoeven 		 */
43724828550SGeert Uytterhoeven 		wmb();
43824828550SGeert Uytterhoeven 		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
43924828550SGeert Uytterhoeven 		break;
44024828550SGeert Uytterhoeven 	case PS3_SM_EVENT_POWER_RELEASED:
44124828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
44224828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
44324828550SGeert Uytterhoeven 		break;
44424828550SGeert Uytterhoeven 	case PS3_SM_EVENT_RESET_PRESSED:
445ea24608fSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n",
446ea24608fSGeoff Levand 			__func__, __LINE__,
447ea24608fSGeoff Levand 			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
448ea24608fSGeoff Levand 			: "hard"));
44924828550SGeert Uytterhoeven 		ps3_sm_force_power_off = 0;
45024828550SGeert Uytterhoeven 		/*
45124828550SGeert Uytterhoeven 		 * A memory barrier is use here to sync memory since
45224828550SGeert Uytterhoeven 		 * ps3_sys_manager_final_restart() could be called on
45324828550SGeert Uytterhoeven 		 * another cpu.
45424828550SGeert Uytterhoeven 		 */
45524828550SGeert Uytterhoeven 		wmb();
45624828550SGeert Uytterhoeven 		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
45724828550SGeert Uytterhoeven 		break;
45824828550SGeert Uytterhoeven 	case PS3_SM_EVENT_RESET_RELEASED:
45924828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
46024828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
46124828550SGeert Uytterhoeven 		break;
46224828550SGeert Uytterhoeven 	case PS3_SM_EVENT_THERMAL_ALERT:
46324828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
46424828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
465eff56c92SGeert Uytterhoeven 		pr_info("PS3 Thermal Alert Zone %u\n", event.value);
46624828550SGeert Uytterhoeven 		break;
46724828550SGeert Uytterhoeven 	case PS3_SM_EVENT_THERMAL_CLEARED:
46824828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
46924828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
47024828550SGeert Uytterhoeven 		break;
47124828550SGeert Uytterhoeven 	default:
47224828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
47324828550SGeert Uytterhoeven 			__func__, __LINE__, event.type);
47424828550SGeert Uytterhoeven 		return -EIO;
47524828550SGeert Uytterhoeven 	}
47624828550SGeert Uytterhoeven 
47724828550SGeert Uytterhoeven 	return 0;
47824828550SGeert Uytterhoeven }
47924828550SGeert Uytterhoeven /**
48024828550SGeert Uytterhoeven  * ps3_sys_manager_handle_cmd - Second stage command msg handler.
48124828550SGeert Uytterhoeven  *
48224828550SGeert Uytterhoeven  * The system manager sends this in reply to a 'request' message from the guest.
48324828550SGeert Uytterhoeven  */
48424828550SGeert Uytterhoeven 
ps3_sys_manager_handle_cmd(struct ps3_system_bus_device * dev)48524828550SGeert Uytterhoeven static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev)
48624828550SGeert Uytterhoeven {
48724828550SGeert Uytterhoeven 	int result;
48824828550SGeert Uytterhoeven 	struct {
48924828550SGeert Uytterhoeven 		u8 version;
49024828550SGeert Uytterhoeven 		u8 type;
49124828550SGeert Uytterhoeven 		u8 reserved_1[14];
49224828550SGeert Uytterhoeven 	} cmd;
49324828550SGeert Uytterhoeven 
49424828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(cmd) != 16);
49524828550SGeert Uytterhoeven 
49624828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
49724828550SGeert Uytterhoeven 
49824828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
49924828550SGeert Uytterhoeven 	BUG_ON(result && "need to retry here");
50024828550SGeert Uytterhoeven 
50124828550SGeert Uytterhoeven 	if (result)
50224828550SGeert Uytterhoeven 		return result;
50324828550SGeert Uytterhoeven 
50424828550SGeert Uytterhoeven 	if (cmd.version != 1) {
50524828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
50624828550SGeert Uytterhoeven 			__func__, __LINE__, cmd.version);
50724828550SGeert Uytterhoeven 		return -EIO;
50824828550SGeert Uytterhoeven 	}
50924828550SGeert Uytterhoeven 
51024828550SGeert Uytterhoeven 	if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
51124828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
51224828550SGeert Uytterhoeven 			__func__, __LINE__, cmd.type);
51324828550SGeert Uytterhoeven 		return -EIO;
51424828550SGeert Uytterhoeven 	}
51524828550SGeert Uytterhoeven 
51624828550SGeert Uytterhoeven 	ps3_sys_manager_send_response(dev, 0);
51724828550SGeert Uytterhoeven 	return 0;
51824828550SGeert Uytterhoeven }
51924828550SGeert Uytterhoeven 
52024828550SGeert Uytterhoeven /**
52124828550SGeert Uytterhoeven  * ps3_sys_manager_handle_msg - First stage msg handler.
52224828550SGeert Uytterhoeven  *
52324828550SGeert Uytterhoeven  * Can be called directly to manually poll vuart and pump message handler.
52424828550SGeert Uytterhoeven  */
52524828550SGeert Uytterhoeven 
ps3_sys_manager_handle_msg(struct ps3_system_bus_device * dev)52624828550SGeert Uytterhoeven static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
52724828550SGeert Uytterhoeven {
52824828550SGeert Uytterhoeven 	int result;
52924828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
53024828550SGeert Uytterhoeven 
53124828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &header,
53224828550SGeert Uytterhoeven 		sizeof(struct ps3_sys_manager_header));
53324828550SGeert Uytterhoeven 
53424828550SGeert Uytterhoeven 	if (result)
53524828550SGeert Uytterhoeven 		return result;
53624828550SGeert Uytterhoeven 
53724828550SGeert Uytterhoeven 	if (header.version != 1) {
53824828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
53924828550SGeert Uytterhoeven 			__func__, __LINE__, header.version);
54024828550SGeert Uytterhoeven 		dump_sm_header(&header);
54124828550SGeert Uytterhoeven 		goto fail_header;
54224828550SGeert Uytterhoeven 	}
54324828550SGeert Uytterhoeven 
54424828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(header) != 16);
54524828550SGeert Uytterhoeven 
54624828550SGeert Uytterhoeven 	if (header.size != 16 || (header.payload_size != 8
54724828550SGeert Uytterhoeven 		&& header.payload_size != 16)) {
54824828550SGeert Uytterhoeven 		dump_sm_header(&header);
54924828550SGeert Uytterhoeven 		BUG();
55024828550SGeert Uytterhoeven 	}
55124828550SGeert Uytterhoeven 
55224828550SGeert Uytterhoeven 	switch (header.service_id) {
55324828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_EXTERN_EVENT:
55424828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
55524828550SGeert Uytterhoeven 		return ps3_sys_manager_handle_event(dev);
55624828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_COMMAND:
55724828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
55824828550SGeert Uytterhoeven 		return ps3_sys_manager_handle_cmd(dev);
55924828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_REQUEST_ERROR:
56024828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
56124828550SGeert Uytterhoeven 			__LINE__);
56224828550SGeert Uytterhoeven 		dump_sm_header(&header);
56324828550SGeert Uytterhoeven 		break;
56424828550SGeert Uytterhoeven 	default:
56524828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
56624828550SGeert Uytterhoeven 			__func__, __LINE__, header.service_id);
56724828550SGeert Uytterhoeven 		break;
56824828550SGeert Uytterhoeven 	}
56924828550SGeert Uytterhoeven 	goto fail_id;
57024828550SGeert Uytterhoeven 
57124828550SGeert Uytterhoeven fail_header:
57224828550SGeert Uytterhoeven 	ps3_vuart_clear_rx_bytes(dev, 0);
57324828550SGeert Uytterhoeven 	return -EIO;
57424828550SGeert Uytterhoeven fail_id:
57524828550SGeert Uytterhoeven 	ps3_vuart_clear_rx_bytes(dev, header.payload_size);
57624828550SGeert Uytterhoeven 	return -EIO;
57724828550SGeert Uytterhoeven }
57824828550SGeert Uytterhoeven 
ps3_sys_manager_fin(struct ps3_system_bus_device * dev)579ca052f79SGeert Uytterhoeven static void ps3_sys_manager_fin(struct ps3_system_bus_device *dev)
580ca052f79SGeert Uytterhoeven {
581ca052f79SGeert Uytterhoeven 	ps3_sys_manager_send_request_shutdown(dev);
582ca052f79SGeert Uytterhoeven 
583ca052f79SGeert Uytterhoeven 	pr_emerg("System Halted, OK to turn off power\n");
584ca052f79SGeert Uytterhoeven 
585ca052f79SGeert Uytterhoeven 	while (ps3_sys_manager_handle_msg(dev)) {
586ca052f79SGeert Uytterhoeven 		/* pause until next DEC interrupt */
587ca052f79SGeert Uytterhoeven 		lv1_pause(0);
588ca052f79SGeert Uytterhoeven 	}
589ca052f79SGeert Uytterhoeven 
590ca052f79SGeert Uytterhoeven 	while (1) {
591ca052f79SGeert Uytterhoeven 		/* pause, ignoring DEC interrupt */
592ca052f79SGeert Uytterhoeven 		lv1_pause(1);
593ca052f79SGeert Uytterhoeven 	}
594ca052f79SGeert Uytterhoeven }
595ca052f79SGeert Uytterhoeven 
59624828550SGeert Uytterhoeven /**
59724828550SGeert Uytterhoeven  * ps3_sys_manager_final_power_off - The final platform machine_power_off routine.
59824828550SGeert Uytterhoeven  *
59924828550SGeert Uytterhoeven  * This routine never returns.  The routine disables asynchronous vuart reads
60024828550SGeert Uytterhoeven  * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
60124828550SGeert Uytterhoeven  * the shutdown command sent from the system manager.  Soon after the
60224828550SGeert Uytterhoeven  * acknowledgement is sent the lpar is destroyed by the HV.  This routine
60324828550SGeert Uytterhoeven  * should only be called from ps3_power_off() through
60424828550SGeert Uytterhoeven  * ps3_sys_manager_ops.power_off.
60524828550SGeert Uytterhoeven  */
60624828550SGeert Uytterhoeven 
ps3_sys_manager_final_power_off(struct ps3_system_bus_device * dev)60724828550SGeert Uytterhoeven static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev)
60824828550SGeert Uytterhoeven {
60924828550SGeert Uytterhoeven 	BUG_ON(!dev);
61024828550SGeert Uytterhoeven 
61124828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
61224828550SGeert Uytterhoeven 
61324828550SGeert Uytterhoeven 	ps3_vuart_cancel_async(dev);
61424828550SGeert Uytterhoeven 
61524828550SGeert Uytterhoeven 	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
6161c43d265SGeoff Levand 		user_wake_sources);
61724828550SGeert Uytterhoeven 
618ca052f79SGeert Uytterhoeven 	ps3_sys_manager_fin(dev);
61924828550SGeert Uytterhoeven }
62024828550SGeert Uytterhoeven 
62124828550SGeert Uytterhoeven /**
62224828550SGeert Uytterhoeven  * ps3_sys_manager_final_restart - The final platform machine_restart routine.
62324828550SGeert Uytterhoeven  *
62424828550SGeert Uytterhoeven  * This routine never returns.  The routine disables asynchronous vuart reads
62524828550SGeert Uytterhoeven  * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
62624828550SGeert Uytterhoeven  * the shutdown command sent from the system manager.  Soon after the
62724828550SGeert Uytterhoeven  * acknowledgement is sent the lpar is destroyed by the HV.  This routine
62824828550SGeert Uytterhoeven  * should only be called from ps3_restart() through ps3_sys_manager_ops.restart.
62924828550SGeert Uytterhoeven  */
63024828550SGeert Uytterhoeven 
ps3_sys_manager_final_restart(struct ps3_system_bus_device * dev)63124828550SGeert Uytterhoeven static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev)
63224828550SGeert Uytterhoeven {
63324828550SGeert Uytterhoeven 	BUG_ON(!dev);
63424828550SGeert Uytterhoeven 
63524828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
63624828550SGeert Uytterhoeven 
63724828550SGeert Uytterhoeven 	/* Check if we got here via a power button event. */
63824828550SGeert Uytterhoeven 
63924828550SGeert Uytterhoeven 	if (ps3_sm_force_power_off) {
64024828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: forcing poweroff\n",
64124828550SGeert Uytterhoeven 			__func__, __LINE__);
64224828550SGeert Uytterhoeven 		ps3_sys_manager_final_power_off(dev);
64324828550SGeert Uytterhoeven 	}
64424828550SGeert Uytterhoeven 
64524828550SGeert Uytterhoeven 	ps3_vuart_cancel_async(dev);
64624828550SGeert Uytterhoeven 
64724828550SGeert Uytterhoeven 	ps3_sys_manager_send_attr(dev, 0);
64875ffe88dSGeoff Levand 	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
6491c43d265SGeoff Levand 		user_wake_sources);
65024828550SGeert Uytterhoeven 
651ca052f79SGeert Uytterhoeven 	ps3_sys_manager_fin(dev);
65224828550SGeert Uytterhoeven }
65324828550SGeert Uytterhoeven 
65424828550SGeert Uytterhoeven /**
6551c43d265SGeoff Levand  * ps3_sys_manager_get_wol - Get wake-on-lan setting.
6561c43d265SGeoff Levand  */
6571c43d265SGeoff Levand 
ps3_sys_manager_get_wol(void)6581c43d265SGeoff Levand int ps3_sys_manager_get_wol(void)
6591c43d265SGeoff Levand {
6601c43d265SGeoff Levand 	pr_debug("%s:%d\n", __func__, __LINE__);
6611c43d265SGeoff Levand 
6621c43d265SGeoff Levand 	return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
6631c43d265SGeoff Levand }
6641c43d265SGeoff Levand EXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol);
6651c43d265SGeoff Levand 
6661c43d265SGeoff Levand /**
6671c43d265SGeoff Levand  * ps3_sys_manager_set_wol - Set wake-on-lan setting.
6681c43d265SGeoff Levand  */
6691c43d265SGeoff Levand 
ps3_sys_manager_set_wol(int state)6701c43d265SGeoff Levand void ps3_sys_manager_set_wol(int state)
6711c43d265SGeoff Levand {
6721c43d265SGeoff Levand 	static DEFINE_MUTEX(mutex);
6731c43d265SGeoff Levand 
6741c43d265SGeoff Levand 	mutex_lock(&mutex);
6751c43d265SGeoff Levand 
6761c43d265SGeoff Levand 	pr_debug("%s:%d: %d\n", __func__, __LINE__, state);
6771c43d265SGeoff Levand 
6781c43d265SGeoff Levand 	if (state)
6791c43d265SGeoff Levand 		user_wake_sources |= PS3_SM_WAKE_W_O_L;
6801c43d265SGeoff Levand 	else
6811c43d265SGeoff Levand 		user_wake_sources &= ~PS3_SM_WAKE_W_O_L;
6821c43d265SGeoff Levand 	mutex_unlock(&mutex);
6831c43d265SGeoff Levand }
6841c43d265SGeoff Levand EXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol);
6851c43d265SGeoff Levand 
6861c43d265SGeoff Levand /**
68724828550SGeert Uytterhoeven  * ps3_sys_manager_work - Asynchronous read handler.
68824828550SGeert Uytterhoeven  *
68924828550SGeert Uytterhoeven  * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port.
69024828550SGeert Uytterhoeven  */
69124828550SGeert Uytterhoeven 
ps3_sys_manager_work(struct ps3_system_bus_device * dev)69224828550SGeert Uytterhoeven static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
69324828550SGeert Uytterhoeven {
69424828550SGeert Uytterhoeven 	ps3_sys_manager_handle_msg(dev);
69524828550SGeert Uytterhoeven 	ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
69624828550SGeert Uytterhoeven }
69724828550SGeert Uytterhoeven 
ps3_sys_manager_probe(struct ps3_system_bus_device * dev)6980fe763c5SGreg Kroah-Hartman static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
69924828550SGeert Uytterhoeven {
70024828550SGeert Uytterhoeven 	int result;
70124828550SGeert Uytterhoeven 	struct ps3_sys_manager_ops ops;
70224828550SGeert Uytterhoeven 
70324828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
70424828550SGeert Uytterhoeven 
70524828550SGeert Uytterhoeven 	ops.power_off = ps3_sys_manager_final_power_off;
70624828550SGeert Uytterhoeven 	ops.restart = ps3_sys_manager_final_restart;
70724828550SGeert Uytterhoeven 	ops.dev = dev;
70824828550SGeert Uytterhoeven 
70924828550SGeert Uytterhoeven 	/* ps3_sys_manager_register_ops copies ops. */
71024828550SGeert Uytterhoeven 
71124828550SGeert Uytterhoeven 	ps3_sys_manager_register_ops(&ops);
71224828550SGeert Uytterhoeven 
71324828550SGeert Uytterhoeven 	result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
71424828550SGeert Uytterhoeven 	BUG_ON(result);
71524828550SGeert Uytterhoeven 
71624828550SGeert Uytterhoeven 	result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
71724828550SGeert Uytterhoeven 	BUG_ON(result);
71824828550SGeert Uytterhoeven 
71924828550SGeert Uytterhoeven 	return result;
72024828550SGeert Uytterhoeven }
72124828550SGeert Uytterhoeven 
ps3_sys_manager_remove(struct ps3_system_bus_device * dev)72224828550SGeert Uytterhoeven static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
72324828550SGeert Uytterhoeven {
72424828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
72524828550SGeert Uytterhoeven 	return 0;
72624828550SGeert Uytterhoeven }
72724828550SGeert Uytterhoeven 
ps3_sys_manager_shutdown(struct ps3_system_bus_device * dev)72824828550SGeert Uytterhoeven static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
72924828550SGeert Uytterhoeven {
73024828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
73124828550SGeert Uytterhoeven }
73224828550SGeert Uytterhoeven 
73324828550SGeert Uytterhoeven static struct ps3_vuart_port_driver ps3_sys_manager = {
73424828550SGeert Uytterhoeven 	.core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
73524828550SGeert Uytterhoeven 	.core.core.name = "ps3_sys_manager",
73624828550SGeert Uytterhoeven 	.probe = ps3_sys_manager_probe,
73724828550SGeert Uytterhoeven 	.remove = ps3_sys_manager_remove,
73824828550SGeert Uytterhoeven 	.shutdown = ps3_sys_manager_shutdown,
73924828550SGeert Uytterhoeven 	.work = ps3_sys_manager_work,
74024828550SGeert Uytterhoeven };
74124828550SGeert Uytterhoeven 
ps3_sys_manager_init(void)74224828550SGeert Uytterhoeven static int __init ps3_sys_manager_init(void)
74324828550SGeert Uytterhoeven {
74424828550SGeert Uytterhoeven 	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
74524828550SGeert Uytterhoeven 		return -ENODEV;
74624828550SGeert Uytterhoeven 
74724828550SGeert Uytterhoeven 	return ps3_vuart_port_driver_register(&ps3_sys_manager);
74824828550SGeert Uytterhoeven }
74924828550SGeert Uytterhoeven 
75024828550SGeert Uytterhoeven module_init(ps3_sys_manager_init);
75124828550SGeert Uytterhoeven /* Module remove not supported. */
75224828550SGeert Uytterhoeven 
75350dad902SGeoff Levand MODULE_AUTHOR("Sony Corporation");
75450dad902SGeoff Levand MODULE_LICENSE("GPL v2");
75550dad902SGeoff Levand MODULE_DESCRIPTION("PS3 System Manager");
75624828550SGeert Uytterhoeven MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER);
757