xref: /linux/drivers/ps3/ps3-sys-manager.c (revision 0fe763c570ad2701c830b9e4e53c65ad89c11c32)
124828550SGeert Uytterhoeven /*
224828550SGeert Uytterhoeven  *  PS3 System Manager.
324828550SGeert Uytterhoeven  *
424828550SGeert Uytterhoeven  *  Copyright (C) 2007 Sony Computer Entertainment Inc.
524828550SGeert Uytterhoeven  *  Copyright 2007 Sony Corp.
624828550SGeert Uytterhoeven  *
724828550SGeert Uytterhoeven  *  This program is free software; you can redistribute it and/or modify
824828550SGeert Uytterhoeven  *  it under the terms of the GNU General Public License as published by
924828550SGeert Uytterhoeven  *  the Free Software Foundation; version 2 of the License.
1024828550SGeert Uytterhoeven  *
1124828550SGeert Uytterhoeven  *  This program is distributed in the hope that it will be useful,
1224828550SGeert Uytterhoeven  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1324828550SGeert Uytterhoeven  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1424828550SGeert Uytterhoeven  *  GNU General Public License for more details.
1524828550SGeert Uytterhoeven  *
1624828550SGeert Uytterhoeven  *  You should have received a copy of the GNU General Public License
1724828550SGeert Uytterhoeven  *  along with this program; if not, write to the Free Software
1824828550SGeert Uytterhoeven  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1924828550SGeert Uytterhoeven  */
2024828550SGeert Uytterhoeven 
2124828550SGeert Uytterhoeven #include <linux/kernel.h>
2224828550SGeert Uytterhoeven #include <linux/module.h>
2324828550SGeert Uytterhoeven #include <linux/workqueue.h>
2424828550SGeert Uytterhoeven #include <linux/reboot.h>
2524828550SGeert Uytterhoeven 
2624828550SGeert Uytterhoeven #include <asm/firmware.h>
27ca052f79SGeert Uytterhoeven #include <asm/lv1call.h>
2824828550SGeert Uytterhoeven #include <asm/ps3.h>
2924828550SGeert Uytterhoeven 
3024828550SGeert Uytterhoeven #include "vuart.h"
3124828550SGeert Uytterhoeven 
3224828550SGeert Uytterhoeven /**
3324828550SGeert Uytterhoeven  * ps3_sys_manager - PS3 system manager driver.
3424828550SGeert Uytterhoeven  *
3524828550SGeert Uytterhoeven  * The system manager provides an asynchronous system event notification
3624828550SGeert Uytterhoeven  * mechanism for reporting events like thermal alert and button presses to
3724828550SGeert Uytterhoeven  * guests.  It also provides support to control system shutdown and startup.
3824828550SGeert Uytterhoeven  *
3924828550SGeert Uytterhoeven  * The actual system manager is implemented as an application running in the
4024828550SGeert Uytterhoeven  * system policy module in lpar_1.  Guests communicate with the system manager
4124828550SGeert Uytterhoeven  * through port 2 of the vuart using a simple packet message protocol.
4224828550SGeert Uytterhoeven  * Messages are comprised of a fixed field header followed by a message
4324828550SGeert Uytterhoeven  * specific payload.
4424828550SGeert Uytterhoeven  */
4524828550SGeert Uytterhoeven 
4624828550SGeert Uytterhoeven /**
4724828550SGeert Uytterhoeven  * struct ps3_sys_manager_header - System manager message header.
4824828550SGeert Uytterhoeven  * @version: Header version, currently 1.
49af901ca1SAndré Goddard Rosa  * @size: Header size in bytes, currently 16.
5024828550SGeert Uytterhoeven  * @payload_size: Message payload size in bytes.
5124828550SGeert Uytterhoeven  * @service_id: Message type, one of enum ps3_sys_manager_service_id.
5224828550SGeert Uytterhoeven  * @request_tag: Unique number to identify reply.
5324828550SGeert Uytterhoeven  */
5424828550SGeert Uytterhoeven 
5524828550SGeert Uytterhoeven struct ps3_sys_manager_header {
5624828550SGeert Uytterhoeven 	/* version 1 */
5724828550SGeert Uytterhoeven 	u8 version;
5824828550SGeert Uytterhoeven 	u8 size;
5924828550SGeert Uytterhoeven 	u16 reserved_1;
6024828550SGeert Uytterhoeven 	u32 payload_size;
6124828550SGeert Uytterhoeven 	u16 service_id;
6224828550SGeert Uytterhoeven 	u16 reserved_2;
6324828550SGeert Uytterhoeven 	u32 request_tag;
6424828550SGeert Uytterhoeven };
6524828550SGeert Uytterhoeven 
6624828550SGeert Uytterhoeven #define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
6724828550SGeert Uytterhoeven static void __maybe_unused _dump_sm_header(
6824828550SGeert Uytterhoeven 	const struct ps3_sys_manager_header *h, const char *func, int line)
6924828550SGeert Uytterhoeven {
7024828550SGeert Uytterhoeven 	pr_debug("%s:%d: version:      %xh\n", func, line, h->version);
7124828550SGeert Uytterhoeven 	pr_debug("%s:%d: size:         %xh\n", func, line, h->size);
7224828550SGeert Uytterhoeven 	pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size);
7324828550SGeert Uytterhoeven 	pr_debug("%s:%d: service_id:   %xh\n", func, line, h->service_id);
7424828550SGeert Uytterhoeven 	pr_debug("%s:%d: request_tag:  %xh\n", func, line, h->request_tag);
7524828550SGeert Uytterhoeven }
7624828550SGeert Uytterhoeven 
7724828550SGeert Uytterhoeven /**
7824828550SGeert Uytterhoeven  * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length.
7924828550SGeert Uytterhoeven  * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length.
8024828550SGeert Uytterhoeven  *
8124828550SGeert Uytterhoeven  * Currently all messages received from the system manager are either
8224828550SGeert Uytterhoeven  * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header
8325985edcSLucas De Marchi  * + 16 bytes payload = 32 bytes).  This knowledge is used to simplify
8424828550SGeert Uytterhoeven  * the logic.
8524828550SGeert Uytterhoeven  */
8624828550SGeert Uytterhoeven 
8724828550SGeert Uytterhoeven enum {
8824828550SGeert Uytterhoeven 	PS3_SM_RX_MSG_LEN_MIN = 24,
8924828550SGeert Uytterhoeven 	PS3_SM_RX_MSG_LEN_MAX = 32,
9024828550SGeert Uytterhoeven };
9124828550SGeert Uytterhoeven 
9224828550SGeert Uytterhoeven /**
9324828550SGeert Uytterhoeven  * enum ps3_sys_manager_service_id - Message header service_id.
9424828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_REQUEST:       guest --> sys_manager.
9524828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager.
9624828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_COMMAND:       guest <-- sys_manager.
9724828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_RESPONSE:      guest --> sys_manager.
9824828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_SET_ATTR:      guest --> sys_manager.
9924828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_EXTERN_EVENT:  guest <-- sys_manager.
10024828550SGeert Uytterhoeven  * @PS3_SM_SERVICE_ID_SET_NEXT_OP:   guest --> sys_manager.
10124828550SGeert Uytterhoeven  *
10224828550SGeert Uytterhoeven  * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a
10324828550SGeert Uytterhoeven  * a PS3_SM_SERVICE_ID_REQUEST message.  It also seems to be returned when
10424828550SGeert Uytterhoeven  * a REQUEST message is sent at the wrong time.
10524828550SGeert Uytterhoeven  */
10624828550SGeert Uytterhoeven 
10724828550SGeert Uytterhoeven enum ps3_sys_manager_service_id {
10824828550SGeert Uytterhoeven 	/* version 1 */
10924828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_REQUEST = 1,
11024828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_RESPONSE = 2,
11124828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_COMMAND = 3,
11224828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
11324828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
11424828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_REQUEST_ERROR = 6,
11524828550SGeert Uytterhoeven 	PS3_SM_SERVICE_ID_SET_ATTR = 8,
11624828550SGeert Uytterhoeven };
11724828550SGeert Uytterhoeven 
11824828550SGeert Uytterhoeven /**
11924828550SGeert Uytterhoeven  * enum ps3_sys_manager_attr - Notification attribute (bit position mask).
12024828550SGeert Uytterhoeven  * @PS3_SM_ATTR_POWER: Power button.
12124828550SGeert Uytterhoeven  * @PS3_SM_ATTR_RESET: Reset button, not available on retail console.
12288393161SThomas Weber  * @PS3_SM_ATTR_THERMAL: System thermal alert.
12324828550SGeert Uytterhoeven  * @PS3_SM_ATTR_CONTROLLER: Remote controller event.
12424828550SGeert Uytterhoeven  * @PS3_SM_ATTR_ALL: Logical OR of all.
12524828550SGeert Uytterhoeven  *
12624828550SGeert Uytterhoeven  * The guest tells the system manager which events it is interested in receiving
12724828550SGeert Uytterhoeven  * notice of by sending the system manager a logical OR of notification
12824828550SGeert Uytterhoeven  * attributes via the ps3_sys_manager_send_attr() routine.
12924828550SGeert Uytterhoeven  */
13024828550SGeert Uytterhoeven 
13124828550SGeert Uytterhoeven enum ps3_sys_manager_attr {
13224828550SGeert Uytterhoeven 	/* version 1 */
13324828550SGeert Uytterhoeven 	PS3_SM_ATTR_POWER = 1,
13424828550SGeert Uytterhoeven 	PS3_SM_ATTR_RESET = 2,
13524828550SGeert Uytterhoeven 	PS3_SM_ATTR_THERMAL = 4,
13624828550SGeert Uytterhoeven 	PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
13724828550SGeert Uytterhoeven 	PS3_SM_ATTR_ALL = 0x0f,
13824828550SGeert Uytterhoeven };
13924828550SGeert Uytterhoeven 
14024828550SGeert Uytterhoeven /**
14124828550SGeert Uytterhoeven  * enum ps3_sys_manager_event - External event type, reported by system manager.
142ea24608fSGeoff Levand  * @PS3_SM_EVENT_POWER_PRESSED: payload.value =
143ea24608fSGeoff Levand  *  enum ps3_sys_manager_button_event.
14424828550SGeert Uytterhoeven  * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec.
145ea24608fSGeoff Levand  * @PS3_SM_EVENT_RESET_PRESSED: payload.value =
146ea24608fSGeoff Levand  *  enum ps3_sys_manager_button_event.
14724828550SGeert Uytterhoeven  * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec.
14824828550SGeert Uytterhoeven  * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id.
14924828550SGeert Uytterhoeven  * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id.
15024828550SGeert Uytterhoeven  */
15124828550SGeert Uytterhoeven 
15224828550SGeert Uytterhoeven enum ps3_sys_manager_event {
15324828550SGeert Uytterhoeven 	/* version 1 */
15424828550SGeert Uytterhoeven 	PS3_SM_EVENT_POWER_PRESSED = 3,
15524828550SGeert Uytterhoeven 	PS3_SM_EVENT_POWER_RELEASED = 4,
15624828550SGeert Uytterhoeven 	PS3_SM_EVENT_RESET_PRESSED = 5,
15724828550SGeert Uytterhoeven 	PS3_SM_EVENT_RESET_RELEASED = 6,
15824828550SGeert Uytterhoeven 	PS3_SM_EVENT_THERMAL_ALERT = 7,
15924828550SGeert Uytterhoeven 	PS3_SM_EVENT_THERMAL_CLEARED = 8,
16024828550SGeert Uytterhoeven 	/* no info on controller events */
16124828550SGeert Uytterhoeven };
16224828550SGeert Uytterhoeven 
16324828550SGeert Uytterhoeven /**
164ea24608fSGeoff Levand  * enum ps3_sys_manager_button_event - Button event payload values.
165ea24608fSGeoff Levand  * @PS3_SM_BUTTON_EVENT_HARD: Hardware generated event.
166ea24608fSGeoff Levand  * @PS3_SM_BUTTON_EVENT_SOFT: Software generated event.
167ea24608fSGeoff Levand  */
168ea24608fSGeoff Levand 
169ea24608fSGeoff Levand enum ps3_sys_manager_button_event {
170ea24608fSGeoff Levand 	PS3_SM_BUTTON_EVENT_HARD = 0,
171ea24608fSGeoff Levand 	PS3_SM_BUTTON_EVENT_SOFT = 1,
172ea24608fSGeoff Levand };
173ea24608fSGeoff Levand 
174ea24608fSGeoff Levand /**
17524828550SGeert Uytterhoeven  * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed.
17624828550SGeert Uytterhoeven  */
17724828550SGeert Uytterhoeven 
17824828550SGeert Uytterhoeven enum ps3_sys_manager_next_op {
17924828550SGeert Uytterhoeven 	/* version 3 */
18024828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
18124828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_SYS_REBOOT = 2,
18224828550SGeert Uytterhoeven 	PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
18324828550SGeert Uytterhoeven };
18424828550SGeert Uytterhoeven 
18524828550SGeert Uytterhoeven /**
18624828550SGeert Uytterhoeven  * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask).
1875442381cSGeoff Levand  * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button.
1881c43d265SGeoff Levand  * @PS3_SM_WAKE_W_O_L: Ether or wireless LAN.
18924828550SGeert Uytterhoeven  * @PS3_SM_WAKE_P_O_R: Power on reset.
19024828550SGeert Uytterhoeven  *
19124828550SGeert Uytterhoeven  * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN.
19250dad902SGeoff Levand  * The system will always wake from the PS3_SM_WAKE_DEFAULT sources.
19350dad902SGeoff Levand  * Sources listed here are the only ones available to guests in the
19450dad902SGeoff Levand  * other-os lpar.
19524828550SGeert Uytterhoeven  */
19624828550SGeert Uytterhoeven 
19724828550SGeert Uytterhoeven enum ps3_sys_manager_wake_source {
19824828550SGeert Uytterhoeven 	/* version 3 */
19924828550SGeert Uytterhoeven 	PS3_SM_WAKE_DEFAULT   = 0,
2001c43d265SGeoff Levand 	PS3_SM_WAKE_W_O_L     = 0x00000400,
20150dad902SGeoff Levand 	PS3_SM_WAKE_P_O_R     = 0x80000000,
20224828550SGeert Uytterhoeven };
20324828550SGeert Uytterhoeven 
20424828550SGeert Uytterhoeven /**
2051c43d265SGeoff Levand  * user_wake_sources - User specified wakeup sources.
2061c43d265SGeoff Levand  *
2071c43d265SGeoff Levand  * Logical OR of enum ps3_sys_manager_wake_source types.
2081c43d265SGeoff Levand  */
2091c43d265SGeoff Levand 
2101c43d265SGeoff Levand static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
2111c43d265SGeoff Levand 
2121c43d265SGeoff Levand /**
21324828550SGeert Uytterhoeven  * enum ps3_sys_manager_cmd - Command from system manager to guest.
21424828550SGeert Uytterhoeven  *
21524828550SGeert Uytterhoeven  * The guest completes the actions needed, then acks or naks the command via
21624828550SGeert Uytterhoeven  * ps3_sys_manager_send_response().  In the case of @PS3_SM_CMD_SHUTDOWN,
21724828550SGeert Uytterhoeven  * the guest must be fully prepared for a system poweroff prior to acking the
21824828550SGeert Uytterhoeven  * command.
21924828550SGeert Uytterhoeven  */
22024828550SGeert Uytterhoeven 
22124828550SGeert Uytterhoeven enum ps3_sys_manager_cmd {
22224828550SGeert Uytterhoeven 	/* version 1 */
22324828550SGeert Uytterhoeven 	PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
22424828550SGeert Uytterhoeven };
22524828550SGeert Uytterhoeven 
22624828550SGeert Uytterhoeven /**
22724828550SGeert Uytterhoeven  * ps3_sm_force_power_off - Poweroff helper.
22824828550SGeert Uytterhoeven  *
22924828550SGeert Uytterhoeven  * A global variable used to force a poweroff when the power button has
23024828550SGeert Uytterhoeven  * been pressed irrespective of how init handles the ctrl_alt_del signal.
23124828550SGeert Uytterhoeven  *
23224828550SGeert Uytterhoeven  */
23324828550SGeert Uytterhoeven 
23424828550SGeert Uytterhoeven static unsigned int ps3_sm_force_power_off;
23524828550SGeert Uytterhoeven 
23624828550SGeert Uytterhoeven /**
23724828550SGeert Uytterhoeven  * ps3_sys_manager_write - Helper to write a two part message to the vuart.
23824828550SGeert Uytterhoeven  *
23924828550SGeert Uytterhoeven  */
24024828550SGeert Uytterhoeven 
24124828550SGeert Uytterhoeven static int ps3_sys_manager_write(struct ps3_system_bus_device *dev,
24224828550SGeert Uytterhoeven 	const struct ps3_sys_manager_header *header, const void *payload)
24324828550SGeert Uytterhoeven {
24424828550SGeert Uytterhoeven 	int result;
24524828550SGeert Uytterhoeven 
24624828550SGeert Uytterhoeven 	BUG_ON(header->version != 1);
24724828550SGeert Uytterhoeven 	BUG_ON(header->size != 16);
24824828550SGeert Uytterhoeven 	BUG_ON(header->payload_size != 8 && header->payload_size != 16);
24924828550SGeert Uytterhoeven 	BUG_ON(header->service_id > 8);
25024828550SGeert Uytterhoeven 
25124828550SGeert Uytterhoeven 	result = ps3_vuart_write(dev, header,
25224828550SGeert Uytterhoeven 		sizeof(struct ps3_sys_manager_header));
25324828550SGeert Uytterhoeven 
25424828550SGeert Uytterhoeven 	if (!result)
25524828550SGeert Uytterhoeven 		result = ps3_vuart_write(dev, payload, header->payload_size);
25624828550SGeert Uytterhoeven 
25724828550SGeert Uytterhoeven 	return result;
25824828550SGeert Uytterhoeven }
25924828550SGeert Uytterhoeven 
26024828550SGeert Uytterhoeven /**
26124828550SGeert Uytterhoeven  * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager.
26224828550SGeert Uytterhoeven  *
26324828550SGeert Uytterhoeven  */
26424828550SGeert Uytterhoeven 
26524828550SGeert Uytterhoeven static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
26624828550SGeert Uytterhoeven 	enum ps3_sys_manager_attr attr)
26724828550SGeert Uytterhoeven {
26824828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
26924828550SGeert Uytterhoeven 	struct {
27024828550SGeert Uytterhoeven 		u8 version;
27124828550SGeert Uytterhoeven 		u8 reserved_1[3];
27224828550SGeert Uytterhoeven 		u32 attribute;
27324828550SGeert Uytterhoeven 	} payload;
27424828550SGeert Uytterhoeven 
27524828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 8);
27624828550SGeert Uytterhoeven 
27724828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
27824828550SGeert Uytterhoeven 
27924828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
28024828550SGeert Uytterhoeven 	header.version = 1;
28124828550SGeert Uytterhoeven 	header.size = 16;
28224828550SGeert Uytterhoeven 	header.payload_size = 16;
28324828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_SET_ATTR;
28424828550SGeert Uytterhoeven 
28524828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
28624828550SGeert Uytterhoeven 	payload.version = 1;
28724828550SGeert Uytterhoeven 	payload.attribute = attr;
28824828550SGeert Uytterhoeven 
28924828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
29024828550SGeert Uytterhoeven }
29124828550SGeert Uytterhoeven 
29224828550SGeert Uytterhoeven /**
29324828550SGeert Uytterhoeven  * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager.
29424828550SGeert Uytterhoeven  *
29524828550SGeert Uytterhoeven  * Tell the system manager what to do after this lpar is destroyed.
29624828550SGeert Uytterhoeven  */
29724828550SGeert Uytterhoeven 
29824828550SGeert Uytterhoeven static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
29924828550SGeert Uytterhoeven 	enum ps3_sys_manager_next_op op,
30024828550SGeert Uytterhoeven 	enum ps3_sys_manager_wake_source wake_source)
30124828550SGeert Uytterhoeven {
30224828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
30324828550SGeert Uytterhoeven 	struct {
30424828550SGeert Uytterhoeven 		u8 version;
30524828550SGeert Uytterhoeven 		u8 type;
30624828550SGeert Uytterhoeven 		u8 gos_id;
30724828550SGeert Uytterhoeven 		u8 reserved_1;
30824828550SGeert Uytterhoeven 		u32 wake_source;
30924828550SGeert Uytterhoeven 		u8 reserved_2[8];
31024828550SGeert Uytterhoeven 	} payload;
31124828550SGeert Uytterhoeven 
31224828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
31324828550SGeert Uytterhoeven 
31424828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
31524828550SGeert Uytterhoeven 
31624828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
31724828550SGeert Uytterhoeven 	header.version = 1;
31824828550SGeert Uytterhoeven 	header.size = 16;
31924828550SGeert Uytterhoeven 	header.payload_size = 16;
32024828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP;
32124828550SGeert Uytterhoeven 
32224828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
32324828550SGeert Uytterhoeven 	payload.version = 3;
32424828550SGeert Uytterhoeven 	payload.type = op;
32524828550SGeert Uytterhoeven 	payload.gos_id = 3; /* other os */
32624828550SGeert Uytterhoeven 	payload.wake_source = wake_source;
32724828550SGeert Uytterhoeven 
32824828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
32924828550SGeert Uytterhoeven }
33024828550SGeert Uytterhoeven 
33124828550SGeert Uytterhoeven /**
33224828550SGeert Uytterhoeven  * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager.
33324828550SGeert Uytterhoeven  *
33424828550SGeert Uytterhoeven  * The guest sends this message to request an operation or action of the system
33524828550SGeert Uytterhoeven  * manager.  The reply is a command message from the system manager.  In the
33624828550SGeert Uytterhoeven  * command handler the guest performs the requested operation.  The result of
33724828550SGeert Uytterhoeven  * the command is then communicated back to the system manager with a response
33824828550SGeert Uytterhoeven  * message.
33924828550SGeert Uytterhoeven  *
34024828550SGeert Uytterhoeven  * Currently, the only supported request is the 'shutdown self' request.
34124828550SGeert Uytterhoeven  */
34224828550SGeert Uytterhoeven 
34324828550SGeert Uytterhoeven static int ps3_sys_manager_send_request_shutdown(
34424828550SGeert Uytterhoeven 	struct ps3_system_bus_device *dev)
34524828550SGeert Uytterhoeven {
34624828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
34724828550SGeert Uytterhoeven 	struct {
34824828550SGeert Uytterhoeven 		u8 version;
34924828550SGeert Uytterhoeven 		u8 type;
35024828550SGeert Uytterhoeven 		u8 gos_id;
35124828550SGeert Uytterhoeven 		u8 reserved_1[13];
35224828550SGeert Uytterhoeven 	} payload;
35324828550SGeert Uytterhoeven 
35424828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
35524828550SGeert Uytterhoeven 
35624828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
35724828550SGeert Uytterhoeven 
35824828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
35924828550SGeert Uytterhoeven 	header.version = 1;
36024828550SGeert Uytterhoeven 	header.size = 16;
36124828550SGeert Uytterhoeven 	header.payload_size = 16;
36224828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_REQUEST;
36324828550SGeert Uytterhoeven 
36424828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
36524828550SGeert Uytterhoeven 	payload.version = 1;
36624828550SGeert Uytterhoeven 	payload.type = 1; /* shutdown */
36724828550SGeert Uytterhoeven 	payload.gos_id = 0; /* self */
36824828550SGeert Uytterhoeven 
36924828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
37024828550SGeert Uytterhoeven }
37124828550SGeert Uytterhoeven 
37224828550SGeert Uytterhoeven /**
37324828550SGeert Uytterhoeven  * ps3_sys_manager_send_response - Send a 'response' to the system manager.
37424828550SGeert Uytterhoeven  * @status: zero = success, others fail.
37524828550SGeert Uytterhoeven  *
37624828550SGeert Uytterhoeven  * The guest sends this message to the system manager to acnowledge success or
37724828550SGeert Uytterhoeven  * failure of a command sent by the system manager.
37824828550SGeert Uytterhoeven  */
37924828550SGeert Uytterhoeven 
38024828550SGeert Uytterhoeven static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
38124828550SGeert Uytterhoeven 	u64 status)
38224828550SGeert Uytterhoeven {
38324828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
38424828550SGeert Uytterhoeven 	struct {
38524828550SGeert Uytterhoeven 		u8 version;
38624828550SGeert Uytterhoeven 		u8 reserved_1[3];
38724828550SGeert Uytterhoeven 		u8 status;
38824828550SGeert Uytterhoeven 		u8 reserved_2[11];
38924828550SGeert Uytterhoeven 	} payload;
39024828550SGeert Uytterhoeven 
39124828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(payload) != 16);
39224828550SGeert Uytterhoeven 
39324828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
39424828550SGeert Uytterhoeven 		(status ? "nak" : "ack"));
39524828550SGeert Uytterhoeven 
39624828550SGeert Uytterhoeven 	memset(&header, 0, sizeof(header));
39724828550SGeert Uytterhoeven 	header.version = 1;
39824828550SGeert Uytterhoeven 	header.size = 16;
39924828550SGeert Uytterhoeven 	header.payload_size = 16;
40024828550SGeert Uytterhoeven 	header.service_id = PS3_SM_SERVICE_ID_RESPONSE;
40124828550SGeert Uytterhoeven 
40224828550SGeert Uytterhoeven 	memset(&payload, 0, sizeof(payload));
40324828550SGeert Uytterhoeven 	payload.version = 1;
40424828550SGeert Uytterhoeven 	payload.status = status;
40524828550SGeert Uytterhoeven 
40624828550SGeert Uytterhoeven 	return ps3_sys_manager_write(dev, &header, &payload);
40724828550SGeert Uytterhoeven }
40824828550SGeert Uytterhoeven 
40924828550SGeert Uytterhoeven /**
41024828550SGeert Uytterhoeven  * ps3_sys_manager_handle_event - Second stage event msg handler.
41124828550SGeert Uytterhoeven  *
41224828550SGeert Uytterhoeven  */
41324828550SGeert Uytterhoeven 
41424828550SGeert Uytterhoeven static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev)
41524828550SGeert Uytterhoeven {
41624828550SGeert Uytterhoeven 	int result;
41724828550SGeert Uytterhoeven 	struct {
41824828550SGeert Uytterhoeven 		u8 version;
41924828550SGeert Uytterhoeven 		u8 type;
42024828550SGeert Uytterhoeven 		u8 reserved_1[2];
42124828550SGeert Uytterhoeven 		u32 value;
42224828550SGeert Uytterhoeven 		u8 reserved_2[8];
42324828550SGeert Uytterhoeven 	} event;
42424828550SGeert Uytterhoeven 
42524828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(event) != 16);
42624828550SGeert Uytterhoeven 
42724828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &event, sizeof(event));
42824828550SGeert Uytterhoeven 	BUG_ON(result && "need to retry here");
42924828550SGeert Uytterhoeven 
43024828550SGeert Uytterhoeven 	if (event.version != 1) {
43124828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
43224828550SGeert Uytterhoeven 			__func__, __LINE__, event.version);
43324828550SGeert Uytterhoeven 		return -EIO;
43424828550SGeert Uytterhoeven 	}
43524828550SGeert Uytterhoeven 
43624828550SGeert Uytterhoeven 	switch (event.type) {
43724828550SGeert Uytterhoeven 	case PS3_SM_EVENT_POWER_PRESSED:
438ea24608fSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n",
439ea24608fSGeoff Levand 			__func__, __LINE__,
440ea24608fSGeoff Levand 			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
441ea24608fSGeoff Levand 			: "hard"));
44224828550SGeert Uytterhoeven 		ps3_sm_force_power_off = 1;
44324828550SGeert Uytterhoeven 		/*
44424828550SGeert Uytterhoeven 		 * A memory barrier is use here to sync memory since
44524828550SGeert Uytterhoeven 		 * ps3_sys_manager_final_restart() could be called on
44624828550SGeert Uytterhoeven 		 * another cpu.
44724828550SGeert Uytterhoeven 		 */
44824828550SGeert Uytterhoeven 		wmb();
44924828550SGeert Uytterhoeven 		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
45024828550SGeert Uytterhoeven 		break;
45124828550SGeert Uytterhoeven 	case PS3_SM_EVENT_POWER_RELEASED:
45224828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
45324828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
45424828550SGeert Uytterhoeven 		break;
45524828550SGeert Uytterhoeven 	case PS3_SM_EVENT_RESET_PRESSED:
456ea24608fSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n",
457ea24608fSGeoff Levand 			__func__, __LINE__,
458ea24608fSGeoff Levand 			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
459ea24608fSGeoff Levand 			: "hard"));
46024828550SGeert Uytterhoeven 		ps3_sm_force_power_off = 0;
46124828550SGeert Uytterhoeven 		/*
46224828550SGeert Uytterhoeven 		 * A memory barrier is use here to sync memory since
46324828550SGeert Uytterhoeven 		 * ps3_sys_manager_final_restart() could be called on
46424828550SGeert Uytterhoeven 		 * another cpu.
46524828550SGeert Uytterhoeven 		 */
46624828550SGeert Uytterhoeven 		wmb();
46724828550SGeert Uytterhoeven 		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
46824828550SGeert Uytterhoeven 		break;
46924828550SGeert Uytterhoeven 	case PS3_SM_EVENT_RESET_RELEASED:
47024828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
47124828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
47224828550SGeert Uytterhoeven 		break;
47324828550SGeert Uytterhoeven 	case PS3_SM_EVENT_THERMAL_ALERT:
47424828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
47524828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
476eff56c92SGeert Uytterhoeven 		pr_info("PS3 Thermal Alert Zone %u\n", event.value);
47724828550SGeert Uytterhoeven 		break;
47824828550SGeert Uytterhoeven 	case PS3_SM_EVENT_THERMAL_CLEARED:
47924828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
48024828550SGeert Uytterhoeven 			__func__, __LINE__, event.value);
48124828550SGeert Uytterhoeven 		break;
48224828550SGeert Uytterhoeven 	default:
48324828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
48424828550SGeert Uytterhoeven 			__func__, __LINE__, event.type);
48524828550SGeert Uytterhoeven 		return -EIO;
48624828550SGeert Uytterhoeven 	}
48724828550SGeert Uytterhoeven 
48824828550SGeert Uytterhoeven 	return 0;
48924828550SGeert Uytterhoeven }
49024828550SGeert Uytterhoeven /**
49124828550SGeert Uytterhoeven  * ps3_sys_manager_handle_cmd - Second stage command msg handler.
49224828550SGeert Uytterhoeven  *
49324828550SGeert Uytterhoeven  * The system manager sends this in reply to a 'request' message from the guest.
49424828550SGeert Uytterhoeven  */
49524828550SGeert Uytterhoeven 
49624828550SGeert Uytterhoeven static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev)
49724828550SGeert Uytterhoeven {
49824828550SGeert Uytterhoeven 	int result;
49924828550SGeert Uytterhoeven 	struct {
50024828550SGeert Uytterhoeven 		u8 version;
50124828550SGeert Uytterhoeven 		u8 type;
50224828550SGeert Uytterhoeven 		u8 reserved_1[14];
50324828550SGeert Uytterhoeven 	} cmd;
50424828550SGeert Uytterhoeven 
50524828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(cmd) != 16);
50624828550SGeert Uytterhoeven 
50724828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
50824828550SGeert Uytterhoeven 
50924828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
51024828550SGeert Uytterhoeven 	BUG_ON(result && "need to retry here");
51124828550SGeert Uytterhoeven 
51224828550SGeert Uytterhoeven 	if (result)
51324828550SGeert Uytterhoeven 		return result;
51424828550SGeert Uytterhoeven 
51524828550SGeert Uytterhoeven 	if (cmd.version != 1) {
51624828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
51724828550SGeert Uytterhoeven 			__func__, __LINE__, cmd.version);
51824828550SGeert Uytterhoeven 		return -EIO;
51924828550SGeert Uytterhoeven 	}
52024828550SGeert Uytterhoeven 
52124828550SGeert Uytterhoeven 	if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
52224828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
52324828550SGeert Uytterhoeven 			__func__, __LINE__, cmd.type);
52424828550SGeert Uytterhoeven 		return -EIO;
52524828550SGeert Uytterhoeven 	}
52624828550SGeert Uytterhoeven 
52724828550SGeert Uytterhoeven 	ps3_sys_manager_send_response(dev, 0);
52824828550SGeert Uytterhoeven 	return 0;
52924828550SGeert Uytterhoeven }
53024828550SGeert Uytterhoeven 
53124828550SGeert Uytterhoeven /**
53224828550SGeert Uytterhoeven  * ps3_sys_manager_handle_msg - First stage msg handler.
53324828550SGeert Uytterhoeven  *
53424828550SGeert Uytterhoeven  * Can be called directly to manually poll vuart and pump message handler.
53524828550SGeert Uytterhoeven  */
53624828550SGeert Uytterhoeven 
53724828550SGeert Uytterhoeven static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
53824828550SGeert Uytterhoeven {
53924828550SGeert Uytterhoeven 	int result;
54024828550SGeert Uytterhoeven 	struct ps3_sys_manager_header header;
54124828550SGeert Uytterhoeven 
54224828550SGeert Uytterhoeven 	result = ps3_vuart_read(dev, &header,
54324828550SGeert Uytterhoeven 		sizeof(struct ps3_sys_manager_header));
54424828550SGeert Uytterhoeven 
54524828550SGeert Uytterhoeven 	if (result)
54624828550SGeert Uytterhoeven 		return result;
54724828550SGeert Uytterhoeven 
54824828550SGeert Uytterhoeven 	if (header.version != 1) {
54924828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
55024828550SGeert Uytterhoeven 			__func__, __LINE__, header.version);
55124828550SGeert Uytterhoeven 		dump_sm_header(&header);
55224828550SGeert Uytterhoeven 		goto fail_header;
55324828550SGeert Uytterhoeven 	}
55424828550SGeert Uytterhoeven 
55524828550SGeert Uytterhoeven 	BUILD_BUG_ON(sizeof(header) != 16);
55624828550SGeert Uytterhoeven 
55724828550SGeert Uytterhoeven 	if (header.size != 16 || (header.payload_size != 8
55824828550SGeert Uytterhoeven 		&& header.payload_size != 16)) {
55924828550SGeert Uytterhoeven 		dump_sm_header(&header);
56024828550SGeert Uytterhoeven 		BUG();
56124828550SGeert Uytterhoeven 	}
56224828550SGeert Uytterhoeven 
56324828550SGeert Uytterhoeven 	switch (header.service_id) {
56424828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_EXTERN_EVENT:
56524828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
56624828550SGeert Uytterhoeven 		return ps3_sys_manager_handle_event(dev);
56724828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_COMMAND:
56824828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
56924828550SGeert Uytterhoeven 		return ps3_sys_manager_handle_cmd(dev);
57024828550SGeert Uytterhoeven 	case PS3_SM_SERVICE_ID_REQUEST_ERROR:
57124828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
57224828550SGeert Uytterhoeven 			__LINE__);
57324828550SGeert Uytterhoeven 		dump_sm_header(&header);
57424828550SGeert Uytterhoeven 		break;
57524828550SGeert Uytterhoeven 	default:
57624828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
57724828550SGeert Uytterhoeven 			__func__, __LINE__, header.service_id);
57824828550SGeert Uytterhoeven 		break;
57924828550SGeert Uytterhoeven 	}
58024828550SGeert Uytterhoeven 	goto fail_id;
58124828550SGeert Uytterhoeven 
58224828550SGeert Uytterhoeven fail_header:
58324828550SGeert Uytterhoeven 	ps3_vuart_clear_rx_bytes(dev, 0);
58424828550SGeert Uytterhoeven 	return -EIO;
58524828550SGeert Uytterhoeven fail_id:
58624828550SGeert Uytterhoeven 	ps3_vuart_clear_rx_bytes(dev, header.payload_size);
58724828550SGeert Uytterhoeven 	return -EIO;
58824828550SGeert Uytterhoeven }
58924828550SGeert Uytterhoeven 
590ca052f79SGeert Uytterhoeven static void ps3_sys_manager_fin(struct ps3_system_bus_device *dev)
591ca052f79SGeert Uytterhoeven {
592ca052f79SGeert Uytterhoeven 	ps3_sys_manager_send_request_shutdown(dev);
593ca052f79SGeert Uytterhoeven 
594ca052f79SGeert Uytterhoeven 	pr_emerg("System Halted, OK to turn off power\n");
595ca052f79SGeert Uytterhoeven 
596ca052f79SGeert Uytterhoeven 	while (ps3_sys_manager_handle_msg(dev)) {
597ca052f79SGeert Uytterhoeven 		/* pause until next DEC interrupt */
598ca052f79SGeert Uytterhoeven 		lv1_pause(0);
599ca052f79SGeert Uytterhoeven 	}
600ca052f79SGeert Uytterhoeven 
601ca052f79SGeert Uytterhoeven 	while (1) {
602ca052f79SGeert Uytterhoeven 		/* pause, ignoring DEC interrupt */
603ca052f79SGeert Uytterhoeven 		lv1_pause(1);
604ca052f79SGeert Uytterhoeven 	}
605ca052f79SGeert Uytterhoeven }
606ca052f79SGeert Uytterhoeven 
60724828550SGeert Uytterhoeven /**
60824828550SGeert Uytterhoeven  * ps3_sys_manager_final_power_off - The final platform machine_power_off routine.
60924828550SGeert Uytterhoeven  *
61024828550SGeert Uytterhoeven  * This routine never returns.  The routine disables asynchronous vuart reads
61124828550SGeert Uytterhoeven  * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
61224828550SGeert Uytterhoeven  * the shutdown command sent from the system manager.  Soon after the
61324828550SGeert Uytterhoeven  * acknowledgement is sent the lpar is destroyed by the HV.  This routine
61424828550SGeert Uytterhoeven  * should only be called from ps3_power_off() through
61524828550SGeert Uytterhoeven  * ps3_sys_manager_ops.power_off.
61624828550SGeert Uytterhoeven  */
61724828550SGeert Uytterhoeven 
61824828550SGeert Uytterhoeven static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev)
61924828550SGeert Uytterhoeven {
62024828550SGeert Uytterhoeven 	BUG_ON(!dev);
62124828550SGeert Uytterhoeven 
62224828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
62324828550SGeert Uytterhoeven 
62424828550SGeert Uytterhoeven 	ps3_vuart_cancel_async(dev);
62524828550SGeert Uytterhoeven 
62624828550SGeert Uytterhoeven 	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
6271c43d265SGeoff Levand 		user_wake_sources);
62824828550SGeert Uytterhoeven 
629ca052f79SGeert Uytterhoeven 	ps3_sys_manager_fin(dev);
63024828550SGeert Uytterhoeven }
63124828550SGeert Uytterhoeven 
63224828550SGeert Uytterhoeven /**
63324828550SGeert Uytterhoeven  * ps3_sys_manager_final_restart - The final platform machine_restart routine.
63424828550SGeert Uytterhoeven  *
63524828550SGeert Uytterhoeven  * This routine never returns.  The routine disables asynchronous vuart reads
63624828550SGeert Uytterhoeven  * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
63724828550SGeert Uytterhoeven  * the shutdown command sent from the system manager.  Soon after the
63824828550SGeert Uytterhoeven  * acknowledgement is sent the lpar is destroyed by the HV.  This routine
63924828550SGeert Uytterhoeven  * should only be called from ps3_restart() through ps3_sys_manager_ops.restart.
64024828550SGeert Uytterhoeven  */
64124828550SGeert Uytterhoeven 
64224828550SGeert Uytterhoeven static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev)
64324828550SGeert Uytterhoeven {
64424828550SGeert Uytterhoeven 	BUG_ON(!dev);
64524828550SGeert Uytterhoeven 
64624828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
64724828550SGeert Uytterhoeven 
64824828550SGeert Uytterhoeven 	/* Check if we got here via a power button event. */
64924828550SGeert Uytterhoeven 
65024828550SGeert Uytterhoeven 	if (ps3_sm_force_power_off) {
65124828550SGeert Uytterhoeven 		dev_dbg(&dev->core, "%s:%d: forcing poweroff\n",
65224828550SGeert Uytterhoeven 			__func__, __LINE__);
65324828550SGeert Uytterhoeven 		ps3_sys_manager_final_power_off(dev);
65424828550SGeert Uytterhoeven 	}
65524828550SGeert Uytterhoeven 
65624828550SGeert Uytterhoeven 	ps3_vuart_cancel_async(dev);
65724828550SGeert Uytterhoeven 
65824828550SGeert Uytterhoeven 	ps3_sys_manager_send_attr(dev, 0);
65975ffe88dSGeoff Levand 	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
6601c43d265SGeoff Levand 		user_wake_sources);
66124828550SGeert Uytterhoeven 
662ca052f79SGeert Uytterhoeven 	ps3_sys_manager_fin(dev);
66324828550SGeert Uytterhoeven }
66424828550SGeert Uytterhoeven 
66524828550SGeert Uytterhoeven /**
6661c43d265SGeoff Levand  * ps3_sys_manager_get_wol - Get wake-on-lan setting.
6671c43d265SGeoff Levand  */
6681c43d265SGeoff Levand 
6691c43d265SGeoff Levand int ps3_sys_manager_get_wol(void)
6701c43d265SGeoff Levand {
6711c43d265SGeoff Levand 	pr_debug("%s:%d\n", __func__, __LINE__);
6721c43d265SGeoff Levand 
6731c43d265SGeoff Levand 	return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
6741c43d265SGeoff Levand }
6751c43d265SGeoff Levand EXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol);
6761c43d265SGeoff Levand 
6771c43d265SGeoff Levand /**
6781c43d265SGeoff Levand  * ps3_sys_manager_set_wol - Set wake-on-lan setting.
6791c43d265SGeoff Levand  */
6801c43d265SGeoff Levand 
6811c43d265SGeoff Levand void ps3_sys_manager_set_wol(int state)
6821c43d265SGeoff Levand {
6831c43d265SGeoff Levand 	static DEFINE_MUTEX(mutex);
6841c43d265SGeoff Levand 
6851c43d265SGeoff Levand 	mutex_lock(&mutex);
6861c43d265SGeoff Levand 
6871c43d265SGeoff Levand 	pr_debug("%s:%d: %d\n", __func__, __LINE__, state);
6881c43d265SGeoff Levand 
6891c43d265SGeoff Levand 	if (state)
6901c43d265SGeoff Levand 		user_wake_sources |= PS3_SM_WAKE_W_O_L;
6911c43d265SGeoff Levand 	else
6921c43d265SGeoff Levand 		user_wake_sources &= ~PS3_SM_WAKE_W_O_L;
6931c43d265SGeoff Levand 	mutex_unlock(&mutex);
6941c43d265SGeoff Levand }
6951c43d265SGeoff Levand EXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol);
6961c43d265SGeoff Levand 
6971c43d265SGeoff Levand /**
69824828550SGeert Uytterhoeven  * ps3_sys_manager_work - Asynchronous read handler.
69924828550SGeert Uytterhoeven  *
70024828550SGeert Uytterhoeven  * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port.
70124828550SGeert Uytterhoeven  */
70224828550SGeert Uytterhoeven 
70324828550SGeert Uytterhoeven static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
70424828550SGeert Uytterhoeven {
70524828550SGeert Uytterhoeven 	ps3_sys_manager_handle_msg(dev);
70624828550SGeert Uytterhoeven 	ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
70724828550SGeert Uytterhoeven }
70824828550SGeert Uytterhoeven 
709*0fe763c5SGreg Kroah-Hartman static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
71024828550SGeert Uytterhoeven {
71124828550SGeert Uytterhoeven 	int result;
71224828550SGeert Uytterhoeven 	struct ps3_sys_manager_ops ops;
71324828550SGeert Uytterhoeven 
71424828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
71524828550SGeert Uytterhoeven 
71624828550SGeert Uytterhoeven 	ops.power_off = ps3_sys_manager_final_power_off;
71724828550SGeert Uytterhoeven 	ops.restart = ps3_sys_manager_final_restart;
71824828550SGeert Uytterhoeven 	ops.dev = dev;
71924828550SGeert Uytterhoeven 
72024828550SGeert Uytterhoeven 	/* ps3_sys_manager_register_ops copies ops. */
72124828550SGeert Uytterhoeven 
72224828550SGeert Uytterhoeven 	ps3_sys_manager_register_ops(&ops);
72324828550SGeert Uytterhoeven 
72424828550SGeert Uytterhoeven 	result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
72524828550SGeert Uytterhoeven 	BUG_ON(result);
72624828550SGeert Uytterhoeven 
72724828550SGeert Uytterhoeven 	result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
72824828550SGeert Uytterhoeven 	BUG_ON(result);
72924828550SGeert Uytterhoeven 
73024828550SGeert Uytterhoeven 	return result;
73124828550SGeert Uytterhoeven }
73224828550SGeert Uytterhoeven 
73324828550SGeert Uytterhoeven static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
73424828550SGeert Uytterhoeven {
73524828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
73624828550SGeert Uytterhoeven 	return 0;
73724828550SGeert Uytterhoeven }
73824828550SGeert Uytterhoeven 
73924828550SGeert Uytterhoeven static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
74024828550SGeert Uytterhoeven {
74124828550SGeert Uytterhoeven 	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
74224828550SGeert Uytterhoeven }
74324828550SGeert Uytterhoeven 
74424828550SGeert Uytterhoeven static struct ps3_vuart_port_driver ps3_sys_manager = {
74524828550SGeert Uytterhoeven 	.core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
74624828550SGeert Uytterhoeven 	.core.core.name = "ps3_sys_manager",
74724828550SGeert Uytterhoeven 	.probe = ps3_sys_manager_probe,
74824828550SGeert Uytterhoeven 	.remove = ps3_sys_manager_remove,
74924828550SGeert Uytterhoeven 	.shutdown = ps3_sys_manager_shutdown,
75024828550SGeert Uytterhoeven 	.work = ps3_sys_manager_work,
75124828550SGeert Uytterhoeven };
75224828550SGeert Uytterhoeven 
75324828550SGeert Uytterhoeven static int __init ps3_sys_manager_init(void)
75424828550SGeert Uytterhoeven {
75524828550SGeert Uytterhoeven 	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
75624828550SGeert Uytterhoeven 		return -ENODEV;
75724828550SGeert Uytterhoeven 
75824828550SGeert Uytterhoeven 	return ps3_vuart_port_driver_register(&ps3_sys_manager);
75924828550SGeert Uytterhoeven }
76024828550SGeert Uytterhoeven 
76124828550SGeert Uytterhoeven module_init(ps3_sys_manager_init);
76224828550SGeert Uytterhoeven /* Module remove not supported. */
76324828550SGeert Uytterhoeven 
76450dad902SGeoff Levand MODULE_AUTHOR("Sony Corporation");
76550dad902SGeoff Levand MODULE_LICENSE("GPL v2");
76650dad902SGeoff Levand MODULE_DESCRIPTION("PS3 System Manager");
76724828550SGeert Uytterhoeven MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER);
768