linux/drivers/ps3/ps3-sys-manager.c
<<
>>
Prefs
   1/*
   2 *  PS3 System Manager.
   3 *
   4 *  Copyright (C) 2007 Sony Computer Entertainment Inc.
   5 *  Copyright 2007 Sony Corp.
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License as published by
   9 *  the Free Software Foundation; version 2 of the License.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to the Free Software
  18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/workqueue.h>
  24#include <linux/reboot.h>
  25
  26#include <asm/firmware.h>
  27#include <asm/lv1call.h>
  28#include <asm/ps3.h>
  29
  30#include "vuart.h"
  31
  32/**
  33 * ps3_sys_manager - PS3 system manager driver.
  34 *
  35 * The system manager provides an asynchronous system event notification
  36 * mechanism for reporting events like thermal alert and button presses to
  37 * guests.  It also provides support to control system shutdown and startup.
  38 *
  39 * The actual system manager is implemented as an application running in the
  40 * system policy module in lpar_1.  Guests communicate with the system manager
  41 * through port 2 of the vuart using a simple packet message protocol.
  42 * Messages are comprised of a fixed field header followed by a message
  43 * specific payload.
  44 */
  45
  46/**
  47 * struct ps3_sys_manager_header - System manager message header.
  48 * @version: Header version, currently 1.
  49 * @size: Header size in bytes, currently 16.
  50 * @payload_size: Message payload size in bytes.
  51 * @service_id: Message type, one of enum ps3_sys_manager_service_id.
  52 * @request_tag: Unique number to identify reply.
  53 */
  54
  55struct ps3_sys_manager_header {
  56        /* version 1 */
  57        u8 version;
  58        u8 size;
  59        u16 reserved_1;
  60        u32 payload_size;
  61        u16 service_id;
  62        u16 reserved_2;
  63        u32 request_tag;
  64};
  65
  66#define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
  67static void __maybe_unused _dump_sm_header(
  68        const struct ps3_sys_manager_header *h, const char *func, int line)
  69{
  70        pr_debug("%s:%d: version:      %xh\n", func, line, h->version);
  71        pr_debug("%s:%d: size:         %xh\n", func, line, h->size);
  72        pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size);
  73        pr_debug("%s:%d: service_id:   %xh\n", func, line, h->service_id);
  74        pr_debug("%s:%d: request_tag:  %xh\n", func, line, h->request_tag);
  75}
  76
  77/**
  78 * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length.
  79 * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length.
  80 *
  81 * Currently all messages received from the system manager are either
  82 * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header
  83 * + 16 bytes payload = 32 bytes).  This knowledge is used to simplify
  84 * the logic.
  85 */
  86
  87enum {
  88        PS3_SM_RX_MSG_LEN_MIN = 24,
  89        PS3_SM_RX_MSG_LEN_MAX = 32,
  90};
  91
  92/**
  93 * enum ps3_sys_manager_service_id - Message header service_id.
  94 * @PS3_SM_SERVICE_ID_REQUEST:       guest --> sys_manager.
  95 * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager.
  96 * @PS3_SM_SERVICE_ID_COMMAND:       guest <-- sys_manager.
  97 * @PS3_SM_SERVICE_ID_RESPONSE:      guest --> sys_manager.
  98 * @PS3_SM_SERVICE_ID_SET_ATTR:      guest --> sys_manager.
  99 * @PS3_SM_SERVICE_ID_EXTERN_EVENT:  guest <-- sys_manager.
 100 * @PS3_SM_SERVICE_ID_SET_NEXT_OP:   guest --> sys_manager.
 101 *
 102 * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a
 103 * a PS3_SM_SERVICE_ID_REQUEST message.  It also seems to be returned when
 104 * a REQUEST message is sent at the wrong time.
 105 */
 106
 107enum ps3_sys_manager_service_id {
 108        /* version 1 */
 109        PS3_SM_SERVICE_ID_REQUEST = 1,
 110        PS3_SM_SERVICE_ID_RESPONSE = 2,
 111        PS3_SM_SERVICE_ID_COMMAND = 3,
 112        PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
 113        PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
 114        PS3_SM_SERVICE_ID_REQUEST_ERROR = 6,
 115        PS3_SM_SERVICE_ID_SET_ATTR = 8,
 116};
 117
 118/**
 119 * enum ps3_sys_manager_attr - Notification attribute (bit position mask).
 120 * @PS3_SM_ATTR_POWER: Power button.
 121 * @PS3_SM_ATTR_RESET: Reset button, not available on retail console.
 122 * @PS3_SM_ATTR_THERMAL: System thermal alert.
 123 * @PS3_SM_ATTR_CONTROLLER: Remote controller event.
 124 * @PS3_SM_ATTR_ALL: Logical OR of all.
 125 *
 126 * The guest tells the system manager which events it is interested in receiving
 127 * notice of by sending the system manager a logical OR of notification
 128 * attributes via the ps3_sys_manager_send_attr() routine.
 129 */
 130
 131enum ps3_sys_manager_attr {
 132        /* version 1 */
 133        PS3_SM_ATTR_POWER = 1,
 134        PS3_SM_ATTR_RESET = 2,
 135        PS3_SM_ATTR_THERMAL = 4,
 136        PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
 137        PS3_SM_ATTR_ALL = 0x0f,
 138};
 139
 140/**
 141 * enum ps3_sys_manager_event - External event type, reported by system manager.
 142 * @PS3_SM_EVENT_POWER_PRESSED: payload.value =
 143 *  enum ps3_sys_manager_button_event.
 144 * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec.
 145 * @PS3_SM_EVENT_RESET_PRESSED: payload.value =
 146 *  enum ps3_sys_manager_button_event.
 147 * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec.
 148 * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id.
 149 * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id.
 150 */
 151
 152enum ps3_sys_manager_event {
 153        /* version 1 */
 154        PS3_SM_EVENT_POWER_PRESSED = 3,
 155        PS3_SM_EVENT_POWER_RELEASED = 4,
 156        PS3_SM_EVENT_RESET_PRESSED = 5,
 157        PS3_SM_EVENT_RESET_RELEASED = 6,
 158        PS3_SM_EVENT_THERMAL_ALERT = 7,
 159        PS3_SM_EVENT_THERMAL_CLEARED = 8,
 160        /* no info on controller events */
 161};
 162
 163/**
 164 * enum ps3_sys_manager_button_event - Button event payload values.
 165 * @PS3_SM_BUTTON_EVENT_HARD: Hardware generated event.
 166 * @PS3_SM_BUTTON_EVENT_SOFT: Software generated event.
 167 */
 168
 169enum ps3_sys_manager_button_event {
 170        PS3_SM_BUTTON_EVENT_HARD = 0,
 171        PS3_SM_BUTTON_EVENT_SOFT = 1,
 172};
 173
 174/**
 175 * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed.
 176 */
 177
 178enum ps3_sys_manager_next_op {
 179        /* version 3 */
 180        PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
 181        PS3_SM_NEXT_OP_SYS_REBOOT = 2,
 182        PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
 183};
 184
 185/**
 186 * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask).
 187 * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button.
 188 * @PS3_SM_WAKE_W_O_L: Ether or wireless LAN.
 189 * @PS3_SM_WAKE_P_O_R: Power on reset.
 190 *
 191 * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN.
 192 * The system will always wake from the PS3_SM_WAKE_DEFAULT sources.
 193 * Sources listed here are the only ones available to guests in the
 194 * other-os lpar.
 195 */
 196
 197enum ps3_sys_manager_wake_source {
 198        /* version 3 */
 199        PS3_SM_WAKE_DEFAULT   = 0,
 200        PS3_SM_WAKE_W_O_L     = 0x00000400,
 201        PS3_SM_WAKE_P_O_R     = 0x80000000,
 202};
 203
 204/**
 205 * user_wake_sources - User specified wakeup sources.
 206 *
 207 * Logical OR of enum ps3_sys_manager_wake_source types.
 208 */
 209
 210static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
 211
 212/**
 213 * enum ps3_sys_manager_cmd - Command from system manager to guest.
 214 *
 215 * The guest completes the actions needed, then acks or naks the command via
 216 * ps3_sys_manager_send_response().  In the case of @PS3_SM_CMD_SHUTDOWN,
 217 * the guest must be fully prepared for a system poweroff prior to acking the
 218 * command.
 219 */
 220
 221enum ps3_sys_manager_cmd {
 222        /* version 1 */
 223        PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
 224};
 225
 226/**
 227 * ps3_sm_force_power_off - Poweroff helper.
 228 *
 229 * A global variable used to force a poweroff when the power button has
 230 * been pressed irrespective of how init handles the ctrl_alt_del signal.
 231 *
 232 */
 233
 234static unsigned int ps3_sm_force_power_off;
 235
 236/**
 237 * ps3_sys_manager_write - Helper to write a two part message to the vuart.
 238 *
 239 */
 240
 241static int ps3_sys_manager_write(struct ps3_system_bus_device *dev,
 242        const struct ps3_sys_manager_header *header, const void *payload)
 243{
 244        int result;
 245
 246        BUG_ON(header->version != 1);
 247        BUG_ON(header->size != 16);
 248        BUG_ON(header->payload_size != 8 && header->payload_size != 16);
 249        BUG_ON(header->service_id > 8);
 250
 251        result = ps3_vuart_write(dev, header,
 252                sizeof(struct ps3_sys_manager_header));
 253
 254        if (!result)
 255                result = ps3_vuart_write(dev, payload, header->payload_size);
 256
 257        return result;
 258}
 259
 260/**
 261 * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager.
 262 *
 263 */
 264
 265static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
 266        enum ps3_sys_manager_attr attr)
 267{
 268        struct ps3_sys_manager_header header;
 269        struct {
 270                u8 version;
 271                u8 reserved_1[3];
 272                u32 attribute;
 273        } payload;
 274
 275        BUILD_BUG_ON(sizeof(payload) != 8);
 276
 277        dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
 278
 279        memset(&header, 0, sizeof(header));
 280        header.version = 1;
 281        header.size = 16;
 282        header.payload_size = 16;
 283        header.service_id = PS3_SM_SERVICE_ID_SET_ATTR;
 284
 285        memset(&payload, 0, sizeof(payload));
 286        payload.version = 1;
 287        payload.attribute = attr;
 288
 289        return ps3_sys_manager_write(dev, &header, &payload);
 290}
 291
 292/**
 293 * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager.
 294 *
 295 * Tell the system manager what to do after this lpar is destroyed.
 296 */
 297
 298static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
 299        enum ps3_sys_manager_next_op op,
 300        enum ps3_sys_manager_wake_source wake_source)
 301{
 302        struct ps3_sys_manager_header header;
 303        struct {
 304                u8 version;
 305                u8 type;
 306                u8 gos_id;
 307                u8 reserved_1;
 308                u32 wake_source;
 309                u8 reserved_2[8];
 310        } payload;
 311
 312        BUILD_BUG_ON(sizeof(payload) != 16);
 313
 314        dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
 315
 316        memset(&header, 0, sizeof(header));
 317        header.version = 1;
 318        header.size = 16;
 319        header.payload_size = 16;
 320        header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP;
 321
 322        memset(&payload, 0, sizeof(payload));
 323        payload.version = 3;
 324        payload.type = op;
 325        payload.gos_id = 3; /* other os */
 326        payload.wake_source = wake_source;
 327
 328        return ps3_sys_manager_write(dev, &header, &payload);
 329}
 330
 331/**
 332 * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager.
 333 *
 334 * The guest sends this message to request an operation or action of the system
 335 * manager.  The reply is a command message from the system manager.  In the
 336 * command handler the guest performs the requested operation.  The result of
 337 * the command is then communicated back to the system manager with a response
 338 * message.
 339 *
 340 * Currently, the only supported request is the 'shutdown self' request.
 341 */
 342
 343static int ps3_sys_manager_send_request_shutdown(
 344        struct ps3_system_bus_device *dev)
 345{
 346        struct ps3_sys_manager_header header;
 347        struct {
 348                u8 version;
 349                u8 type;
 350                u8 gos_id;
 351                u8 reserved_1[13];
 352        } payload;
 353
 354        BUILD_BUG_ON(sizeof(payload) != 16);
 355
 356        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 357
 358        memset(&header, 0, sizeof(header));
 359        header.version = 1;
 360        header.size = 16;
 361        header.payload_size = 16;
 362        header.service_id = PS3_SM_SERVICE_ID_REQUEST;
 363
 364        memset(&payload, 0, sizeof(payload));
 365        payload.version = 1;
 366        payload.type = 1; /* shutdown */
 367        payload.gos_id = 0; /* self */
 368
 369        return ps3_sys_manager_write(dev, &header, &payload);
 370}
 371
 372/**
 373 * ps3_sys_manager_send_response - Send a 'response' to the system manager.
 374 * @status: zero = success, others fail.
 375 *
 376 * The guest sends this message to the system manager to acnowledge success or
 377 * failure of a command sent by the system manager.
 378 */
 379
 380static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
 381        u64 status)
 382{
 383        struct ps3_sys_manager_header header;
 384        struct {
 385                u8 version;
 386                u8 reserved_1[3];
 387                u8 status;
 388                u8 reserved_2[11];
 389        } payload;
 390
 391        BUILD_BUG_ON(sizeof(payload) != 16);
 392
 393        dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
 394                (status ? "nak" : "ack"));
 395
 396        memset(&header, 0, sizeof(header));
 397        header.version = 1;
 398        header.size = 16;
 399        header.payload_size = 16;
 400        header.service_id = PS3_SM_SERVICE_ID_RESPONSE;
 401
 402        memset(&payload, 0, sizeof(payload));
 403        payload.version = 1;
 404        payload.status = status;
 405
 406        return ps3_sys_manager_write(dev, &header, &payload);
 407}
 408
 409/**
 410 * ps3_sys_manager_handle_event - Second stage event msg handler.
 411 *
 412 */
 413
 414static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev)
 415{
 416        int result;
 417        struct {
 418                u8 version;
 419                u8 type;
 420                u8 reserved_1[2];
 421                u32 value;
 422                u8 reserved_2[8];
 423        } event;
 424
 425        BUILD_BUG_ON(sizeof(event) != 16);
 426
 427        result = ps3_vuart_read(dev, &event, sizeof(event));
 428        BUG_ON(result && "need to retry here");
 429
 430        if (event.version != 1) {
 431                dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
 432                        __func__, __LINE__, event.version);
 433                return -EIO;
 434        }
 435
 436        switch (event.type) {
 437        case PS3_SM_EVENT_POWER_PRESSED:
 438                dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n",
 439                        __func__, __LINE__,
 440                        (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
 441                        : "hard"));
 442                ps3_sm_force_power_off = 1;
 443                /*
 444                 * A memory barrier is use here to sync memory since
 445                 * ps3_sys_manager_final_restart() could be called on
 446                 * another cpu.
 447                 */
 448                wmb();
 449                kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
 450                break;
 451        case PS3_SM_EVENT_POWER_RELEASED:
 452                dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
 453                        __func__, __LINE__, event.value);
 454                break;
 455        case PS3_SM_EVENT_RESET_PRESSED:
 456                dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n",
 457                        __func__, __LINE__,
 458                        (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
 459                        : "hard"));
 460                ps3_sm_force_power_off = 0;
 461                /*
 462                 * A memory barrier is use here to sync memory since
 463                 * ps3_sys_manager_final_restart() could be called on
 464                 * another cpu.
 465                 */
 466                wmb();
 467                kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */
 468                break;
 469        case PS3_SM_EVENT_RESET_RELEASED:
 470                dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
 471                        __func__, __LINE__, event.value);
 472                break;
 473        case PS3_SM_EVENT_THERMAL_ALERT:
 474                dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
 475                        __func__, __LINE__, event.value);
 476                pr_info("PS3 Thermal Alert Zone %u\n", event.value);
 477                break;
 478        case PS3_SM_EVENT_THERMAL_CLEARED:
 479                dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
 480                        __func__, __LINE__, event.value);
 481                break;
 482        default:
 483                dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
 484                        __func__, __LINE__, event.type);
 485                return -EIO;
 486        }
 487
 488        return 0;
 489}
 490/**
 491 * ps3_sys_manager_handle_cmd - Second stage command msg handler.
 492 *
 493 * The system manager sends this in reply to a 'request' message from the guest.
 494 */
 495
 496static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev)
 497{
 498        int result;
 499        struct {
 500                u8 version;
 501                u8 type;
 502                u8 reserved_1[14];
 503        } cmd;
 504
 505        BUILD_BUG_ON(sizeof(cmd) != 16);
 506
 507        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 508
 509        result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
 510        BUG_ON(result && "need to retry here");
 511
 512        if (result)
 513                return result;
 514
 515        if (cmd.version != 1) {
 516                dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
 517                        __func__, __LINE__, cmd.version);
 518                return -EIO;
 519        }
 520
 521        if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
 522                dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
 523                        __func__, __LINE__, cmd.type);
 524                return -EIO;
 525        }
 526
 527        ps3_sys_manager_send_response(dev, 0);
 528        return 0;
 529}
 530
 531/**
 532 * ps3_sys_manager_handle_msg - First stage msg handler.
 533 *
 534 * Can be called directly to manually poll vuart and pump message handler.
 535 */
 536
 537static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
 538{
 539        int result;
 540        struct ps3_sys_manager_header header;
 541
 542        result = ps3_vuart_read(dev, &header,
 543                sizeof(struct ps3_sys_manager_header));
 544
 545        if (result)
 546                return result;
 547
 548        if (header.version != 1) {
 549                dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
 550                        __func__, __LINE__, header.version);
 551                dump_sm_header(&header);
 552                goto fail_header;
 553        }
 554
 555        BUILD_BUG_ON(sizeof(header) != 16);
 556
 557        if (header.size != 16 || (header.payload_size != 8
 558                && header.payload_size != 16)) {
 559                dump_sm_header(&header);
 560                BUG();
 561        }
 562
 563        switch (header.service_id) {
 564        case PS3_SM_SERVICE_ID_EXTERN_EVENT:
 565                dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
 566                return ps3_sys_manager_handle_event(dev);
 567        case PS3_SM_SERVICE_ID_COMMAND:
 568                dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
 569                return ps3_sys_manager_handle_cmd(dev);
 570        case PS3_SM_SERVICE_ID_REQUEST_ERROR:
 571                dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
 572                        __LINE__);
 573                dump_sm_header(&header);
 574                break;
 575        default:
 576                dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
 577                        __func__, __LINE__, header.service_id);
 578                break;
 579        }
 580        goto fail_id;
 581
 582fail_header:
 583        ps3_vuart_clear_rx_bytes(dev, 0);
 584        return -EIO;
 585fail_id:
 586        ps3_vuart_clear_rx_bytes(dev, header.payload_size);
 587        return -EIO;
 588}
 589
 590static void ps3_sys_manager_fin(struct ps3_system_bus_device *dev)
 591{
 592        ps3_sys_manager_send_request_shutdown(dev);
 593
 594        pr_emerg("System Halted, OK to turn off power\n");
 595
 596        while (ps3_sys_manager_handle_msg(dev)) {
 597                /* pause until next DEC interrupt */
 598                lv1_pause(0);
 599        }
 600
 601        while (1) {
 602                /* pause, ignoring DEC interrupt */
 603                lv1_pause(1);
 604        }
 605}
 606
 607/**
 608 * ps3_sys_manager_final_power_off - The final platform machine_power_off routine.
 609 *
 610 * This routine never returns.  The routine disables asynchronous vuart reads
 611 * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
 612 * the shutdown command sent from the system manager.  Soon after the
 613 * acknowledgement is sent the lpar is destroyed by the HV.  This routine
 614 * should only be called from ps3_power_off() through
 615 * ps3_sys_manager_ops.power_off.
 616 */
 617
 618static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev)
 619{
 620        BUG_ON(!dev);
 621
 622        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 623
 624        ps3_vuart_cancel_async(dev);
 625
 626        ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
 627                user_wake_sources);
 628
 629        ps3_sys_manager_fin(dev);
 630}
 631
 632/**
 633 * ps3_sys_manager_final_restart - The final platform machine_restart routine.
 634 *
 635 * This routine never returns.  The routine disables asynchronous vuart reads
 636 * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
 637 * the shutdown command sent from the system manager.  Soon after the
 638 * acknowledgement is sent the lpar is destroyed by the HV.  This routine
 639 * should only be called from ps3_restart() through ps3_sys_manager_ops.restart.
 640 */
 641
 642static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev)
 643{
 644        BUG_ON(!dev);
 645
 646        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 647
 648        /* Check if we got here via a power button event. */
 649
 650        if (ps3_sm_force_power_off) {
 651                dev_dbg(&dev->core, "%s:%d: forcing poweroff\n",
 652                        __func__, __LINE__);
 653                ps3_sys_manager_final_power_off(dev);
 654        }
 655
 656        ps3_vuart_cancel_async(dev);
 657
 658        ps3_sys_manager_send_attr(dev, 0);
 659        ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
 660                user_wake_sources);
 661
 662        ps3_sys_manager_fin(dev);
 663}
 664
 665/**
 666 * ps3_sys_manager_get_wol - Get wake-on-lan setting.
 667 */
 668
 669int ps3_sys_manager_get_wol(void)
 670{
 671        pr_debug("%s:%d\n", __func__, __LINE__);
 672
 673        return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
 674}
 675EXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol);
 676
 677/**
 678 * ps3_sys_manager_set_wol - Set wake-on-lan setting.
 679 */
 680
 681void ps3_sys_manager_set_wol(int state)
 682{
 683        static DEFINE_MUTEX(mutex);
 684
 685        mutex_lock(&mutex);
 686
 687        pr_debug("%s:%d: %d\n", __func__, __LINE__, state);
 688
 689        if (state)
 690                user_wake_sources |= PS3_SM_WAKE_W_O_L;
 691        else
 692                user_wake_sources &= ~PS3_SM_WAKE_W_O_L;
 693        mutex_unlock(&mutex);
 694}
 695EXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol);
 696
 697/**
 698 * ps3_sys_manager_work - Asynchronous read handler.
 699 *
 700 * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port.
 701 */
 702
 703static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
 704{
 705        ps3_sys_manager_handle_msg(dev);
 706        ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
 707}
 708
 709static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
 710{
 711        int result;
 712        struct ps3_sys_manager_ops ops;
 713
 714        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 715
 716        ops.power_off = ps3_sys_manager_final_power_off;
 717        ops.restart = ps3_sys_manager_final_restart;
 718        ops.dev = dev;
 719
 720        /* ps3_sys_manager_register_ops copies ops. */
 721
 722        ps3_sys_manager_register_ops(&ops);
 723
 724        result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
 725        BUG_ON(result);
 726
 727        result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
 728        BUG_ON(result);
 729
 730        return result;
 731}
 732
 733static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
 734{
 735        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 736        return 0;
 737}
 738
 739static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
 740{
 741        dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 742}
 743
 744static struct ps3_vuart_port_driver ps3_sys_manager = {
 745        .core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
 746        .core.core.name = "ps3_sys_manager",
 747        .probe = ps3_sys_manager_probe,
 748        .remove = ps3_sys_manager_remove,
 749        .shutdown = ps3_sys_manager_shutdown,
 750        .work = ps3_sys_manager_work,
 751};
 752
 753static int __init ps3_sys_manager_init(void)
 754{
 755        if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
 756                return -ENODEV;
 757
 758        return ps3_vuart_port_driver_register(&ps3_sys_manager);
 759}
 760
 761module_init(ps3_sys_manager_init);
 762/* Module remove not supported. */
 763
 764MODULE_AUTHOR("Sony Corporation");
 765MODULE_LICENSE("GPL v2");
 766MODULE_DESCRIPTION("PS3 System Manager");
 767MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER);
 768