18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  PS3 System Manager.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2007 Sony Computer Entertainment Inc.
68c2ecf20Sopenharmony_ci *  Copyright 2007 Sony Corp.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
128c2ecf20Sopenharmony_ci#include <linux/reboot.h>
138c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/firmware.h>
168c2ecf20Sopenharmony_ci#include <asm/lv1call.h>
178c2ecf20Sopenharmony_ci#include <asm/ps3.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "vuart.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/**
228c2ecf20Sopenharmony_ci * ps3_sys_manager - PS3 system manager driver.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * The system manager provides an asynchronous system event notification
258c2ecf20Sopenharmony_ci * mechanism for reporting events like thermal alert and button presses to
268c2ecf20Sopenharmony_ci * guests.  It also provides support to control system shutdown and startup.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * The actual system manager is implemented as an application running in the
298c2ecf20Sopenharmony_ci * system policy module in lpar_1.  Guests communicate with the system manager
308c2ecf20Sopenharmony_ci * through port 2 of the vuart using a simple packet message protocol.
318c2ecf20Sopenharmony_ci * Messages are comprised of a fixed field header followed by a message
328c2ecf20Sopenharmony_ci * specific payload.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/**
368c2ecf20Sopenharmony_ci * struct ps3_sys_manager_header - System manager message header.
378c2ecf20Sopenharmony_ci * @version: Header version, currently 1.
388c2ecf20Sopenharmony_ci * @size: Header size in bytes, currently 16.
398c2ecf20Sopenharmony_ci * @payload_size: Message payload size in bytes.
408c2ecf20Sopenharmony_ci * @service_id: Message type, one of enum ps3_sys_manager_service_id.
418c2ecf20Sopenharmony_ci * @request_tag: Unique number to identify reply.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct ps3_sys_manager_header {
458c2ecf20Sopenharmony_ci	/* version 1 */
468c2ecf20Sopenharmony_ci	u8 version;
478c2ecf20Sopenharmony_ci	u8 size;
488c2ecf20Sopenharmony_ci	u16 reserved_1;
498c2ecf20Sopenharmony_ci	u32 payload_size;
508c2ecf20Sopenharmony_ci	u16 service_id;
518c2ecf20Sopenharmony_ci	u16 reserved_2;
528c2ecf20Sopenharmony_ci	u32 request_tag;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
568c2ecf20Sopenharmony_cistatic void __maybe_unused _dump_sm_header(
578c2ecf20Sopenharmony_ci	const struct ps3_sys_manager_header *h, const char *func, int line)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	pr_debug("%s:%d: version:      %xh\n", func, line, h->version);
608c2ecf20Sopenharmony_ci	pr_debug("%s:%d: size:         %xh\n", func, line, h->size);
618c2ecf20Sopenharmony_ci	pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size);
628c2ecf20Sopenharmony_ci	pr_debug("%s:%d: service_id:   %xh\n", func, line, h->service_id);
638c2ecf20Sopenharmony_ci	pr_debug("%s:%d: request_tag:  %xh\n", func, line, h->request_tag);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length.
688c2ecf20Sopenharmony_ci * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length.
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Currently all messages received from the system manager are either
718c2ecf20Sopenharmony_ci * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header
728c2ecf20Sopenharmony_ci * + 16 bytes payload = 32 bytes).  This knowledge is used to simplify
738c2ecf20Sopenharmony_ci * the logic.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cienum {
778c2ecf20Sopenharmony_ci	PS3_SM_RX_MSG_LEN_MIN = 24,
788c2ecf20Sopenharmony_ci	PS3_SM_RX_MSG_LEN_MAX = 32,
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/**
828c2ecf20Sopenharmony_ci * enum ps3_sys_manager_service_id - Message header service_id.
838c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_REQUEST:       guest --> sys_manager.
848c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager.
858c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_COMMAND:       guest <-- sys_manager.
868c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_RESPONSE:      guest --> sys_manager.
878c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_SET_ATTR:      guest --> sys_manager.
888c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_EXTERN_EVENT:  guest <-- sys_manager.
898c2ecf20Sopenharmony_ci * @PS3_SM_SERVICE_ID_SET_NEXT_OP:   guest --> sys_manager.
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a
928c2ecf20Sopenharmony_ci * a PS3_SM_SERVICE_ID_REQUEST message.  It also seems to be returned when
938c2ecf20Sopenharmony_ci * a REQUEST message is sent at the wrong time.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cienum ps3_sys_manager_service_id {
978c2ecf20Sopenharmony_ci	/* version 1 */
988c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_REQUEST = 1,
998c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_RESPONSE = 2,
1008c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_COMMAND = 3,
1018c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
1028c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
1038c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_REQUEST_ERROR = 6,
1048c2ecf20Sopenharmony_ci	PS3_SM_SERVICE_ID_SET_ATTR = 8,
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * enum ps3_sys_manager_attr - Notification attribute (bit position mask).
1098c2ecf20Sopenharmony_ci * @PS3_SM_ATTR_POWER: Power button.
1108c2ecf20Sopenharmony_ci * @PS3_SM_ATTR_RESET: Reset button, not available on retail console.
1118c2ecf20Sopenharmony_ci * @PS3_SM_ATTR_THERMAL: System thermal alert.
1128c2ecf20Sopenharmony_ci * @PS3_SM_ATTR_CONTROLLER: Remote controller event.
1138c2ecf20Sopenharmony_ci * @PS3_SM_ATTR_ALL: Logical OR of all.
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci * The guest tells the system manager which events it is interested in receiving
1168c2ecf20Sopenharmony_ci * notice of by sending the system manager a logical OR of notification
1178c2ecf20Sopenharmony_ci * attributes via the ps3_sys_manager_send_attr() routine.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cienum ps3_sys_manager_attr {
1218c2ecf20Sopenharmony_ci	/* version 1 */
1228c2ecf20Sopenharmony_ci	PS3_SM_ATTR_POWER = 1,
1238c2ecf20Sopenharmony_ci	PS3_SM_ATTR_RESET = 2,
1248c2ecf20Sopenharmony_ci	PS3_SM_ATTR_THERMAL = 4,
1258c2ecf20Sopenharmony_ci	PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
1268c2ecf20Sopenharmony_ci	PS3_SM_ATTR_ALL = 0x0f,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/**
1308c2ecf20Sopenharmony_ci * enum ps3_sys_manager_event - External event type, reported by system manager.
1318c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_POWER_PRESSED: payload.value =
1328c2ecf20Sopenharmony_ci *  enum ps3_sys_manager_button_event.
1338c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec.
1348c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_RESET_PRESSED: payload.value =
1358c2ecf20Sopenharmony_ci *  enum ps3_sys_manager_button_event.
1368c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec.
1378c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id.
1388c2ecf20Sopenharmony_ci * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cienum ps3_sys_manager_event {
1428c2ecf20Sopenharmony_ci	/* version 1 */
1438c2ecf20Sopenharmony_ci	PS3_SM_EVENT_POWER_PRESSED = 3,
1448c2ecf20Sopenharmony_ci	PS3_SM_EVENT_POWER_RELEASED = 4,
1458c2ecf20Sopenharmony_ci	PS3_SM_EVENT_RESET_PRESSED = 5,
1468c2ecf20Sopenharmony_ci	PS3_SM_EVENT_RESET_RELEASED = 6,
1478c2ecf20Sopenharmony_ci	PS3_SM_EVENT_THERMAL_ALERT = 7,
1488c2ecf20Sopenharmony_ci	PS3_SM_EVENT_THERMAL_CLEARED = 8,
1498c2ecf20Sopenharmony_ci	/* no info on controller events */
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/**
1538c2ecf20Sopenharmony_ci * enum ps3_sys_manager_button_event - Button event payload values.
1548c2ecf20Sopenharmony_ci * @PS3_SM_BUTTON_EVENT_HARD: Hardware generated event.
1558c2ecf20Sopenharmony_ci * @PS3_SM_BUTTON_EVENT_SOFT: Software generated event.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cienum ps3_sys_manager_button_event {
1598c2ecf20Sopenharmony_ci	PS3_SM_BUTTON_EVENT_HARD = 0,
1608c2ecf20Sopenharmony_ci	PS3_SM_BUTTON_EVENT_SOFT = 1,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/**
1648c2ecf20Sopenharmony_ci * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed.
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cienum ps3_sys_manager_next_op {
1688c2ecf20Sopenharmony_ci	/* version 3 */
1698c2ecf20Sopenharmony_ci	PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
1708c2ecf20Sopenharmony_ci	PS3_SM_NEXT_OP_SYS_REBOOT = 2,
1718c2ecf20Sopenharmony_ci	PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/**
1758c2ecf20Sopenharmony_ci * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask).
1768c2ecf20Sopenharmony_ci * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button.
1778c2ecf20Sopenharmony_ci * @PS3_SM_WAKE_W_O_L: Ether or wireless LAN.
1788c2ecf20Sopenharmony_ci * @PS3_SM_WAKE_P_O_R: Power on reset.
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN.
1818c2ecf20Sopenharmony_ci * The system will always wake from the PS3_SM_WAKE_DEFAULT sources.
1828c2ecf20Sopenharmony_ci * Sources listed here are the only ones available to guests in the
1838c2ecf20Sopenharmony_ci * other-os lpar.
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cienum ps3_sys_manager_wake_source {
1878c2ecf20Sopenharmony_ci	/* version 3 */
1888c2ecf20Sopenharmony_ci	PS3_SM_WAKE_DEFAULT   = 0,
1898c2ecf20Sopenharmony_ci	PS3_SM_WAKE_W_O_L     = 0x00000400,
1908c2ecf20Sopenharmony_ci	PS3_SM_WAKE_P_O_R     = 0x80000000,
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/**
1948c2ecf20Sopenharmony_ci * user_wake_sources - User specified wakeup sources.
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Logical OR of enum ps3_sys_manager_wake_source types.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * enum ps3_sys_manager_cmd - Command from system manager to guest.
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * The guest completes the actions needed, then acks or naks the command via
2058c2ecf20Sopenharmony_ci * ps3_sys_manager_send_response().  In the case of @PS3_SM_CMD_SHUTDOWN,
2068c2ecf20Sopenharmony_ci * the guest must be fully prepared for a system poweroff prior to acking the
2078c2ecf20Sopenharmony_ci * command.
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cienum ps3_sys_manager_cmd {
2118c2ecf20Sopenharmony_ci	/* version 1 */
2128c2ecf20Sopenharmony_ci	PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * ps3_sm_force_power_off - Poweroff helper.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * A global variable used to force a poweroff when the power button has
2198c2ecf20Sopenharmony_ci * been pressed irrespective of how init handles the ctrl_alt_del signal.
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic unsigned int ps3_sm_force_power_off;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/**
2268c2ecf20Sopenharmony_ci * ps3_sys_manager_write - Helper to write a two part message to the vuart.
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int ps3_sys_manager_write(struct ps3_system_bus_device *dev,
2318c2ecf20Sopenharmony_ci	const struct ps3_sys_manager_header *header, const void *payload)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int result;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	BUG_ON(header->version != 1);
2368c2ecf20Sopenharmony_ci	BUG_ON(header->size != 16);
2378c2ecf20Sopenharmony_ci	BUG_ON(header->payload_size != 8 && header->payload_size != 16);
2388c2ecf20Sopenharmony_ci	BUG_ON(header->service_id > 8);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	result = ps3_vuart_write(dev, header,
2418c2ecf20Sopenharmony_ci		sizeof(struct ps3_sys_manager_header));
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!result)
2448c2ecf20Sopenharmony_ci		result = ps3_vuart_write(dev, payload, header->payload_size);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return result;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/**
2508c2ecf20Sopenharmony_ci * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
2558c2ecf20Sopenharmony_ci	enum ps3_sys_manager_attr attr)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct ps3_sys_manager_header header;
2588c2ecf20Sopenharmony_ci	struct {
2598c2ecf20Sopenharmony_ci		u8 version;
2608c2ecf20Sopenharmony_ci		u8 reserved_1[3];
2618c2ecf20Sopenharmony_ci		u32 attribute;
2628c2ecf20Sopenharmony_ci	} payload;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(payload) != 8);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	memset(&header, 0, sizeof(header));
2698c2ecf20Sopenharmony_ci	header.version = 1;
2708c2ecf20Sopenharmony_ci	header.size = 16;
2718c2ecf20Sopenharmony_ci	header.payload_size = 16;
2728c2ecf20Sopenharmony_ci	header.service_id = PS3_SM_SERVICE_ID_SET_ATTR;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	memset(&payload, 0, sizeof(payload));
2758c2ecf20Sopenharmony_ci	payload.version = 1;
2768c2ecf20Sopenharmony_ci	payload.attribute = attr;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return ps3_sys_manager_write(dev, &header, &payload);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/**
2828c2ecf20Sopenharmony_ci * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager.
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * Tell the system manager what to do after this lpar is destroyed.
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
2888c2ecf20Sopenharmony_ci	enum ps3_sys_manager_next_op op,
2898c2ecf20Sopenharmony_ci	enum ps3_sys_manager_wake_source wake_source)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct ps3_sys_manager_header header;
2928c2ecf20Sopenharmony_ci	struct {
2938c2ecf20Sopenharmony_ci		u8 version;
2948c2ecf20Sopenharmony_ci		u8 type;
2958c2ecf20Sopenharmony_ci		u8 gos_id;
2968c2ecf20Sopenharmony_ci		u8 reserved_1;
2978c2ecf20Sopenharmony_ci		u32 wake_source;
2988c2ecf20Sopenharmony_ci		u8 reserved_2[8];
2998c2ecf20Sopenharmony_ci	} payload;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(payload) != 16);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	memset(&header, 0, sizeof(header));
3068c2ecf20Sopenharmony_ci	header.version = 1;
3078c2ecf20Sopenharmony_ci	header.size = 16;
3088c2ecf20Sopenharmony_ci	header.payload_size = 16;
3098c2ecf20Sopenharmony_ci	header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	memset(&payload, 0, sizeof(payload));
3128c2ecf20Sopenharmony_ci	payload.version = 3;
3138c2ecf20Sopenharmony_ci	payload.type = op;
3148c2ecf20Sopenharmony_ci	payload.gos_id = 3; /* other os */
3158c2ecf20Sopenharmony_ci	payload.wake_source = wake_source;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return ps3_sys_manager_write(dev, &header, &payload);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager.
3228c2ecf20Sopenharmony_ci *
3238c2ecf20Sopenharmony_ci * The guest sends this message to request an operation or action of the system
3248c2ecf20Sopenharmony_ci * manager.  The reply is a command message from the system manager.  In the
3258c2ecf20Sopenharmony_ci * command handler the guest performs the requested operation.  The result of
3268c2ecf20Sopenharmony_ci * the command is then communicated back to the system manager with a response
3278c2ecf20Sopenharmony_ci * message.
3288c2ecf20Sopenharmony_ci *
3298c2ecf20Sopenharmony_ci * Currently, the only supported request is the 'shutdown self' request.
3308c2ecf20Sopenharmony_ci */
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int ps3_sys_manager_send_request_shutdown(
3338c2ecf20Sopenharmony_ci	struct ps3_system_bus_device *dev)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct ps3_sys_manager_header header;
3368c2ecf20Sopenharmony_ci	struct {
3378c2ecf20Sopenharmony_ci		u8 version;
3388c2ecf20Sopenharmony_ci		u8 type;
3398c2ecf20Sopenharmony_ci		u8 gos_id;
3408c2ecf20Sopenharmony_ci		u8 reserved_1[13];
3418c2ecf20Sopenharmony_ci	} payload;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(payload) != 16);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	memset(&header, 0, sizeof(header));
3488c2ecf20Sopenharmony_ci	header.version = 1;
3498c2ecf20Sopenharmony_ci	header.size = 16;
3508c2ecf20Sopenharmony_ci	header.payload_size = 16;
3518c2ecf20Sopenharmony_ci	header.service_id = PS3_SM_SERVICE_ID_REQUEST;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	memset(&payload, 0, sizeof(payload));
3548c2ecf20Sopenharmony_ci	payload.version = 1;
3558c2ecf20Sopenharmony_ci	payload.type = 1; /* shutdown */
3568c2ecf20Sopenharmony_ci	payload.gos_id = 0; /* self */
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return ps3_sys_manager_write(dev, &header, &payload);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/**
3628c2ecf20Sopenharmony_ci * ps3_sys_manager_send_response - Send a 'response' to the system manager.
3638c2ecf20Sopenharmony_ci * @status: zero = success, others fail.
3648c2ecf20Sopenharmony_ci *
3658c2ecf20Sopenharmony_ci * The guest sends this message to the system manager to acnowledge success or
3668c2ecf20Sopenharmony_ci * failure of a command sent by the system manager.
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
3708c2ecf20Sopenharmony_ci	u64 status)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct ps3_sys_manager_header header;
3738c2ecf20Sopenharmony_ci	struct {
3748c2ecf20Sopenharmony_ci		u8 version;
3758c2ecf20Sopenharmony_ci		u8 reserved_1[3];
3768c2ecf20Sopenharmony_ci		u8 status;
3778c2ecf20Sopenharmony_ci		u8 reserved_2[11];
3788c2ecf20Sopenharmony_ci	} payload;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(payload) != 16);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
3838c2ecf20Sopenharmony_ci		(status ? "nak" : "ack"));
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	memset(&header, 0, sizeof(header));
3868c2ecf20Sopenharmony_ci	header.version = 1;
3878c2ecf20Sopenharmony_ci	header.size = 16;
3888c2ecf20Sopenharmony_ci	header.payload_size = 16;
3898c2ecf20Sopenharmony_ci	header.service_id = PS3_SM_SERVICE_ID_RESPONSE;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	memset(&payload, 0, sizeof(payload));
3928c2ecf20Sopenharmony_ci	payload.version = 1;
3938c2ecf20Sopenharmony_ci	payload.status = status;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return ps3_sys_manager_write(dev, &header, &payload);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/**
3998c2ecf20Sopenharmony_ci * ps3_sys_manager_handle_event - Second stage event msg handler.
4008c2ecf20Sopenharmony_ci *
4018c2ecf20Sopenharmony_ci */
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	int result;
4068c2ecf20Sopenharmony_ci	struct {
4078c2ecf20Sopenharmony_ci		u8 version;
4088c2ecf20Sopenharmony_ci		u8 type;
4098c2ecf20Sopenharmony_ci		u8 reserved_1[2];
4108c2ecf20Sopenharmony_ci		u32 value;
4118c2ecf20Sopenharmony_ci		u8 reserved_2[8];
4128c2ecf20Sopenharmony_ci	} event;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(event) != 16);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	result = ps3_vuart_read(dev, &event, sizeof(event));
4178c2ecf20Sopenharmony_ci	BUG_ON(result && "need to retry here");
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (event.version != 1) {
4208c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
4218c2ecf20Sopenharmony_ci			__func__, __LINE__, event.version);
4228c2ecf20Sopenharmony_ci		return -EIO;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	switch (event.type) {
4268c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_POWER_PRESSED:
4278c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n",
4288c2ecf20Sopenharmony_ci			__func__, __LINE__,
4298c2ecf20Sopenharmony_ci			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
4308c2ecf20Sopenharmony_ci			: "hard"));
4318c2ecf20Sopenharmony_ci		ps3_sm_force_power_off = 1;
4328c2ecf20Sopenharmony_ci		/*
4338c2ecf20Sopenharmony_ci		 * A memory barrier is use here to sync memory since
4348c2ecf20Sopenharmony_ci		 * ps3_sys_manager_final_restart() could be called on
4358c2ecf20Sopenharmony_ci		 * another cpu.
4368c2ecf20Sopenharmony_ci		 */
4378c2ecf20Sopenharmony_ci		wmb();
4388c2ecf20Sopenharmony_ci		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_POWER_RELEASED:
4418c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
4428c2ecf20Sopenharmony_ci			__func__, __LINE__, event.value);
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_RESET_PRESSED:
4458c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n",
4468c2ecf20Sopenharmony_ci			__func__, __LINE__,
4478c2ecf20Sopenharmony_ci			(event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
4488c2ecf20Sopenharmony_ci			: "hard"));
4498c2ecf20Sopenharmony_ci		ps3_sm_force_power_off = 0;
4508c2ecf20Sopenharmony_ci		/*
4518c2ecf20Sopenharmony_ci		 * A memory barrier is use here to sync memory since
4528c2ecf20Sopenharmony_ci		 * ps3_sys_manager_final_restart() could be called on
4538c2ecf20Sopenharmony_ci		 * another cpu.
4548c2ecf20Sopenharmony_ci		 */
4558c2ecf20Sopenharmony_ci		wmb();
4568c2ecf20Sopenharmony_ci		kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_RESET_RELEASED:
4598c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
4608c2ecf20Sopenharmony_ci			__func__, __LINE__, event.value);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_THERMAL_ALERT:
4638c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
4648c2ecf20Sopenharmony_ci			__func__, __LINE__, event.value);
4658c2ecf20Sopenharmony_ci		pr_info("PS3 Thermal Alert Zone %u\n", event.value);
4668c2ecf20Sopenharmony_ci		break;
4678c2ecf20Sopenharmony_ci	case PS3_SM_EVENT_THERMAL_CLEARED:
4688c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
4698c2ecf20Sopenharmony_ci			__func__, __LINE__, event.value);
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci	default:
4728c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
4738c2ecf20Sopenharmony_ci			__func__, __LINE__, event.type);
4748c2ecf20Sopenharmony_ci		return -EIO;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci/**
4808c2ecf20Sopenharmony_ci * ps3_sys_manager_handle_cmd - Second stage command msg handler.
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * The system manager sends this in reply to a 'request' message from the guest.
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	int result;
4888c2ecf20Sopenharmony_ci	struct {
4898c2ecf20Sopenharmony_ci		u8 version;
4908c2ecf20Sopenharmony_ci		u8 type;
4918c2ecf20Sopenharmony_ci		u8 reserved_1[14];
4928c2ecf20Sopenharmony_ci	} cmd;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(cmd) != 16);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
4998c2ecf20Sopenharmony_ci	BUG_ON(result && "need to retry here");
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (result)
5028c2ecf20Sopenharmony_ci		return result;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (cmd.version != 1) {
5058c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
5068c2ecf20Sopenharmony_ci			__func__, __LINE__, cmd.version);
5078c2ecf20Sopenharmony_ci		return -EIO;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
5118c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
5128c2ecf20Sopenharmony_ci			__func__, __LINE__, cmd.type);
5138c2ecf20Sopenharmony_ci		return -EIO;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	ps3_sys_manager_send_response(dev, 0);
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/**
5218c2ecf20Sopenharmony_ci * ps3_sys_manager_handle_msg - First stage msg handler.
5228c2ecf20Sopenharmony_ci *
5238c2ecf20Sopenharmony_ci * Can be called directly to manually poll vuart and pump message handler.
5248c2ecf20Sopenharmony_ci */
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	int result;
5298c2ecf20Sopenharmony_ci	struct ps3_sys_manager_header header;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	result = ps3_vuart_read(dev, &header,
5328c2ecf20Sopenharmony_ci		sizeof(struct ps3_sys_manager_header));
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (result)
5358c2ecf20Sopenharmony_ci		return result;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (header.version != 1) {
5388c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
5398c2ecf20Sopenharmony_ci			__func__, __LINE__, header.version);
5408c2ecf20Sopenharmony_ci		dump_sm_header(&header);
5418c2ecf20Sopenharmony_ci		goto fail_header;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(header) != 16);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (header.size != 16 || (header.payload_size != 8
5478c2ecf20Sopenharmony_ci		&& header.payload_size != 16)) {
5488c2ecf20Sopenharmony_ci		dump_sm_header(&header);
5498c2ecf20Sopenharmony_ci		BUG();
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	switch (header.service_id) {
5538c2ecf20Sopenharmony_ci	case PS3_SM_SERVICE_ID_EXTERN_EVENT:
5548c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
5558c2ecf20Sopenharmony_ci		return ps3_sys_manager_handle_event(dev);
5568c2ecf20Sopenharmony_ci	case PS3_SM_SERVICE_ID_COMMAND:
5578c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
5588c2ecf20Sopenharmony_ci		return ps3_sys_manager_handle_cmd(dev);
5598c2ecf20Sopenharmony_ci	case PS3_SM_SERVICE_ID_REQUEST_ERROR:
5608c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
5618c2ecf20Sopenharmony_ci			__LINE__);
5628c2ecf20Sopenharmony_ci		dump_sm_header(&header);
5638c2ecf20Sopenharmony_ci		break;
5648c2ecf20Sopenharmony_ci	default:
5658c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
5668c2ecf20Sopenharmony_ci			__func__, __LINE__, header.service_id);
5678c2ecf20Sopenharmony_ci		break;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci	goto fail_id;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cifail_header:
5728c2ecf20Sopenharmony_ci	ps3_vuart_clear_rx_bytes(dev, 0);
5738c2ecf20Sopenharmony_ci	return -EIO;
5748c2ecf20Sopenharmony_cifail_id:
5758c2ecf20Sopenharmony_ci	ps3_vuart_clear_rx_bytes(dev, header.payload_size);
5768c2ecf20Sopenharmony_ci	return -EIO;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic void ps3_sys_manager_fin(struct ps3_system_bus_device *dev)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	ps3_sys_manager_send_request_shutdown(dev);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	pr_emerg("System Halted, OK to turn off power\n");
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	while (ps3_sys_manager_handle_msg(dev)) {
5868c2ecf20Sopenharmony_ci		/* pause until next DEC interrupt */
5878c2ecf20Sopenharmony_ci		lv1_pause(0);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	while (1) {
5918c2ecf20Sopenharmony_ci		/* pause, ignoring DEC interrupt */
5928c2ecf20Sopenharmony_ci		lv1_pause(1);
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci/**
5978c2ecf20Sopenharmony_ci * ps3_sys_manager_final_power_off - The final platform machine_power_off routine.
5988c2ecf20Sopenharmony_ci *
5998c2ecf20Sopenharmony_ci * This routine never returns.  The routine disables asynchronous vuart reads
6008c2ecf20Sopenharmony_ci * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
6018c2ecf20Sopenharmony_ci * the shutdown command sent from the system manager.  Soon after the
6028c2ecf20Sopenharmony_ci * acknowledgement is sent the lpar is destroyed by the HV.  This routine
6038c2ecf20Sopenharmony_ci * should only be called from ps3_power_off() through
6048c2ecf20Sopenharmony_ci * ps3_sys_manager_ops.power_off.
6058c2ecf20Sopenharmony_ci */
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	BUG_ON(!dev);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	ps3_vuart_cancel_async(dev);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
6168c2ecf20Sopenharmony_ci		user_wake_sources);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	ps3_sys_manager_fin(dev);
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/**
6228c2ecf20Sopenharmony_ci * ps3_sys_manager_final_restart - The final platform machine_restart routine.
6238c2ecf20Sopenharmony_ci *
6248c2ecf20Sopenharmony_ci * This routine never returns.  The routine disables asynchronous vuart reads
6258c2ecf20Sopenharmony_ci * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
6268c2ecf20Sopenharmony_ci * the shutdown command sent from the system manager.  Soon after the
6278c2ecf20Sopenharmony_ci * acknowledgement is sent the lpar is destroyed by the HV.  This routine
6288c2ecf20Sopenharmony_ci * should only be called from ps3_restart() through ps3_sys_manager_ops.restart.
6298c2ecf20Sopenharmony_ci */
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	BUG_ON(!dev);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* Check if we got here via a power button event. */
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (ps3_sm_force_power_off) {
6408c2ecf20Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: forcing poweroff\n",
6418c2ecf20Sopenharmony_ci			__func__, __LINE__);
6428c2ecf20Sopenharmony_ci		ps3_sys_manager_final_power_off(dev);
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	ps3_vuart_cancel_async(dev);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	ps3_sys_manager_send_attr(dev, 0);
6488c2ecf20Sopenharmony_ci	ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
6498c2ecf20Sopenharmony_ci		user_wake_sources);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	ps3_sys_manager_fin(dev);
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/**
6558c2ecf20Sopenharmony_ci * ps3_sys_manager_get_wol - Get wake-on-lan setting.
6568c2ecf20Sopenharmony_ci */
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ciint ps3_sys_manager_get_wol(void)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	pr_debug("%s:%d\n", __func__, __LINE__);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci/**
6678c2ecf20Sopenharmony_ci * ps3_sys_manager_set_wol - Set wake-on-lan setting.
6688c2ecf20Sopenharmony_ci */
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_civoid ps3_sys_manager_set_wol(int state)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	static DEFINE_MUTEX(mutex);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	mutex_lock(&mutex);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	pr_debug("%s:%d: %d\n", __func__, __LINE__, state);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (state)
6798c2ecf20Sopenharmony_ci		user_wake_sources |= PS3_SM_WAKE_W_O_L;
6808c2ecf20Sopenharmony_ci	else
6818c2ecf20Sopenharmony_ci		user_wake_sources &= ~PS3_SM_WAKE_W_O_L;
6828c2ecf20Sopenharmony_ci	mutex_unlock(&mutex);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci/**
6878c2ecf20Sopenharmony_ci * ps3_sys_manager_work - Asynchronous read handler.
6888c2ecf20Sopenharmony_ci *
6898c2ecf20Sopenharmony_ci * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port.
6908c2ecf20Sopenharmony_ci */
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	ps3_sys_manager_handle_msg(dev);
6958c2ecf20Sopenharmony_ci	ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	int result;
7018c2ecf20Sopenharmony_ci	struct ps3_sys_manager_ops ops;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	ops.power_off = ps3_sys_manager_final_power_off;
7068c2ecf20Sopenharmony_ci	ops.restart = ps3_sys_manager_final_restart;
7078c2ecf20Sopenharmony_ci	ops.dev = dev;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/* ps3_sys_manager_register_ops copies ops. */
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	ps3_sys_manager_register_ops(&ops);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
7148c2ecf20Sopenharmony_ci	BUG_ON(result);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
7178c2ecf20Sopenharmony_ci	BUG_ON(result);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return result;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
7258c2ecf20Sopenharmony_ci	return 0;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic struct ps3_vuart_port_driver ps3_sys_manager = {
7348c2ecf20Sopenharmony_ci	.core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
7358c2ecf20Sopenharmony_ci	.core.core.name = "ps3_sys_manager",
7368c2ecf20Sopenharmony_ci	.probe = ps3_sys_manager_probe,
7378c2ecf20Sopenharmony_ci	.remove = ps3_sys_manager_remove,
7388c2ecf20Sopenharmony_ci	.shutdown = ps3_sys_manager_shutdown,
7398c2ecf20Sopenharmony_ci	.work = ps3_sys_manager_work,
7408c2ecf20Sopenharmony_ci};
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int __init ps3_sys_manager_init(void)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
7458c2ecf20Sopenharmony_ci		return -ENODEV;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	return ps3_vuart_port_driver_register(&ps3_sys_manager);
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cimodule_init(ps3_sys_manager_init);
7518c2ecf20Sopenharmony_ci/* Module remove not supported. */
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sony Corporation");
7548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
7558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PS3 System Manager");
7568c2ecf20Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER);
757