Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ps3-sys-manager.c
Go to the documentation of this file.
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 
56  /* version 1 */
64 };
65 
66 #define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
67 static 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 
87 enum {
90 };
91 
108  /* version 1 */
116 };
117 
132  /* version 1 */
136  PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
138 };
139 
153  /* version 1 */
160  /* no info on controller events */
161 };
162 
172 };
173 
179  /* version 3 */
183 };
184 
198  /* version 3 */
200  PS3_SM_WAKE_W_O_L = 0x00000400,
201  PS3_SM_WAKE_P_O_R = 0x80000000,
202 };
203 
210 static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
211 
222  /* version 1 */
223  PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
224 };
225 
234 static unsigned int ps3_sm_force_power_off;
235 
241 static 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 
265 static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
267 {
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 
298 static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
300  enum ps3_sys_manager_wake_source wake_source)
301 {
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;
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 
343 static int ps3_sys_manager_send_request_shutdown(
344  struct ps3_system_bus_device *dev)
345 {
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 
380 static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
381  u64 status)
382 {
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 
414 static 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) {
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;
452  dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
453  __func__, __LINE__, event.value);
454  break;
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;
470  dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
471  __func__, __LINE__, event.value);
472  break;
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;
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 }
496 static 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 
537 static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
538 {
539  int result;
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);
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)) {
560  BUG();
561  }
562 
563  switch (header.service_id) {
565  dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
566  return ps3_sys_manager_handle_event(dev);
568  dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
569  return ps3_sys_manager_handle_cmd(dev);
571  dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
572  __LINE__);
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 
582 fail_header:
583  ps3_vuart_clear_rx_bytes(dev, 0);
584  return -EIO;
585 fail_id:
586  ps3_vuart_clear_rx_bytes(dev, header.payload_size);
587  return -EIO;
588 }
589 
590 static 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 
618 static 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 
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 
642 static 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 
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 
670 {
671  pr_debug("%s:%d\n", __func__, __LINE__);
672 
673  return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
674 }
676 
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;
694 }
696 
703 static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
704 {
705  ps3_sys_manager_handle_msg(dev);
707 }
708 
709 static int __devinit 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 
723 
724  result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
725  BUG_ON(result);
726 
728  BUG_ON(result);
729 
730  return result;
731 }
732 
733 static 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 
739 static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
740 {
741  dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
742 }
743 
744 static 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 
753 static 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 
761 module_init(ps3_sys_manager_init);
762 /* Module remove not supported. */
763 
764 MODULE_AUTHOR("Sony Corporation");
765 MODULE_LICENSE("GPL v2");
766 MODULE_DESCRIPTION("PS3 System Manager");