1
2
3
4
5
6
7
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/workqueue.h>
12#include <linux/reboot.h>
13#include <linux/sched/signal.h>
14
15#include <asm/firmware.h>
16#include <asm/lv1call.h>
17#include <asm/ps3.h>
18
19#include "vuart.h"
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44struct ps3_sys_manager_header {
45
46 u8 version;
47 u8 size;
48 u16 reserved_1;
49 u32 payload_size;
50 u16 service_id;
51 u16 reserved_2;
52 u32 request_tag;
53};
54
55#define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__)
56static void __maybe_unused _dump_sm_header(
57 const struct ps3_sys_manager_header *h, const char *func, int line)
58{
59 pr_debug("%s:%d: version: %xh\n", func, line, h->version);
60 pr_debug("%s:%d: size: %xh\n", func, line, h->size);
61 pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size);
62 pr_debug("%s:%d: service_id: %xh\n", func, line, h->service_id);
63 pr_debug("%s:%d: request_tag: %xh\n", func, line, h->request_tag);
64}
65
66
67
68
69
70
71
72
73
74
75
76enum {
77 PS3_SM_RX_MSG_LEN_MIN = 24,
78 PS3_SM_RX_MSG_LEN_MAX = 32,
79};
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96enum ps3_sys_manager_service_id {
97
98 PS3_SM_SERVICE_ID_REQUEST = 1,
99 PS3_SM_SERVICE_ID_RESPONSE = 2,
100 PS3_SM_SERVICE_ID_COMMAND = 3,
101 PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
102 PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
103 PS3_SM_SERVICE_ID_REQUEST_ERROR = 6,
104 PS3_SM_SERVICE_ID_SET_ATTR = 8,
105};
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120enum ps3_sys_manager_attr {
121
122 PS3_SM_ATTR_POWER = 1,
123 PS3_SM_ATTR_RESET = 2,
124 PS3_SM_ATTR_THERMAL = 4,
125 PS3_SM_ATTR_CONTROLLER = 8,
126 PS3_SM_ATTR_ALL = 0x0f,
127};
128
129
130
131
132
133
134
135
136
137
138
139
140
141enum ps3_sys_manager_event {
142
143 PS3_SM_EVENT_POWER_PRESSED = 3,
144 PS3_SM_EVENT_POWER_RELEASED = 4,
145 PS3_SM_EVENT_RESET_PRESSED = 5,
146 PS3_SM_EVENT_RESET_RELEASED = 6,
147 PS3_SM_EVENT_THERMAL_ALERT = 7,
148 PS3_SM_EVENT_THERMAL_CLEARED = 8,
149
150};
151
152
153
154
155
156
157
158enum ps3_sys_manager_button_event {
159 PS3_SM_BUTTON_EVENT_HARD = 0,
160 PS3_SM_BUTTON_EVENT_SOFT = 1,
161};
162
163
164
165
166
167enum ps3_sys_manager_next_op {
168
169 PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
170 PS3_SM_NEXT_OP_SYS_REBOOT = 2,
171 PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
172};
173
174
175
176
177
178
179
180
181
182
183
184
185
186enum ps3_sys_manager_wake_source {
187
188 PS3_SM_WAKE_DEFAULT = 0,
189 PS3_SM_WAKE_W_O_L = 0x00000400,
190 PS3_SM_WAKE_P_O_R = 0x80000000,
191};
192
193
194
195
196
197
198
199static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT;
200
201
202
203
204
205
206
207
208
209
210enum ps3_sys_manager_cmd {
211
212 PS3_SM_CMD_SHUTDOWN = 1,
213};
214
215
216
217
218
219
220
221
222
223static unsigned int ps3_sm_force_power_off;
224
225
226
227
228
229
230static int ps3_sys_manager_write(struct ps3_system_bus_device *dev,
231 const struct ps3_sys_manager_header *header, const void *payload)
232{
233 int result;
234
235 BUG_ON(header->version != 1);
236 BUG_ON(header->size != 16);
237 BUG_ON(header->payload_size != 8 && header->payload_size != 16);
238 BUG_ON(header->service_id > 8);
239
240 result = ps3_vuart_write(dev, header,
241 sizeof(struct ps3_sys_manager_header));
242
243 if (!result)
244 result = ps3_vuart_write(dev, payload, header->payload_size);
245
246 return result;
247}
248
249
250
251
252
253
254static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev,
255 enum ps3_sys_manager_attr attr)
256{
257 struct ps3_sys_manager_header header;
258 struct {
259 u8 version;
260 u8 reserved_1[3];
261 u32 attribute;
262 } payload;
263
264 BUILD_BUG_ON(sizeof(payload) != 8);
265
266 dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
267
268 memset(&header, 0, sizeof(header));
269 header.version = 1;
270 header.size = 16;
271 header.payload_size = 16;
272 header.service_id = PS3_SM_SERVICE_ID_SET_ATTR;
273
274 memset(&payload, 0, sizeof(payload));
275 payload.version = 1;
276 payload.attribute = attr;
277
278 return ps3_sys_manager_write(dev, &header, &payload);
279}
280
281
282
283
284
285
286
287static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev,
288 enum ps3_sys_manager_next_op op,
289 enum ps3_sys_manager_wake_source wake_source)
290{
291 struct ps3_sys_manager_header header;
292 struct {
293 u8 version;
294 u8 type;
295 u8 gos_id;
296 u8 reserved_1;
297 u32 wake_source;
298 u8 reserved_2[8];
299 } payload;
300
301 BUILD_BUG_ON(sizeof(payload) != 16);
302
303 dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
304
305 memset(&header, 0, sizeof(header));
306 header.version = 1;
307 header.size = 16;
308 header.payload_size = 16;
309 header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP;
310
311 memset(&payload, 0, sizeof(payload));
312 payload.version = 3;
313 payload.type = op;
314 payload.gos_id = 3;
315 payload.wake_source = wake_source;
316
317 return ps3_sys_manager_write(dev, &header, &payload);
318}
319
320
321
322
323
324
325
326
327
328
329
330
331
332static int ps3_sys_manager_send_request_shutdown(
333 struct ps3_system_bus_device *dev)
334{
335 struct ps3_sys_manager_header header;
336 struct {
337 u8 version;
338 u8 type;
339 u8 gos_id;
340 u8 reserved_1[13];
341 } payload;
342
343 BUILD_BUG_ON(sizeof(payload) != 16);
344
345 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
346
347 memset(&header, 0, sizeof(header));
348 header.version = 1;
349 header.size = 16;
350 header.payload_size = 16;
351 header.service_id = PS3_SM_SERVICE_ID_REQUEST;
352
353 memset(&payload, 0, sizeof(payload));
354 payload.version = 1;
355 payload.type = 1;
356 payload.gos_id = 0;
357
358 return ps3_sys_manager_write(dev, &header, &payload);
359}
360
361
362
363
364
365
366
367
368
369static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev,
370 u64 status)
371{
372 struct ps3_sys_manager_header header;
373 struct {
374 u8 version;
375 u8 reserved_1[3];
376 u8 status;
377 u8 reserved_2[11];
378 } payload;
379
380 BUILD_BUG_ON(sizeof(payload) != 16);
381
382 dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
383 (status ? "nak" : "ack"));
384
385 memset(&header, 0, sizeof(header));
386 header.version = 1;
387 header.size = 16;
388 header.payload_size = 16;
389 header.service_id = PS3_SM_SERVICE_ID_RESPONSE;
390
391 memset(&payload, 0, sizeof(payload));
392 payload.version = 1;
393 payload.status = status;
394
395 return ps3_sys_manager_write(dev, &header, &payload);
396}
397
398
399
400
401
402
403static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev)
404{
405 int result;
406 struct {
407 u8 version;
408 u8 type;
409 u8 reserved_1[2];
410 u32 value;
411 u8 reserved_2[8];
412 } event;
413
414 BUILD_BUG_ON(sizeof(event) != 16);
415
416 result = ps3_vuart_read(dev, &event, sizeof(event));
417 BUG_ON(result && "need to retry here");
418
419 if (event.version != 1) {
420 dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
421 __func__, __LINE__, event.version);
422 return -EIO;
423 }
424
425 switch (event.type) {
426 case PS3_SM_EVENT_POWER_PRESSED:
427 dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n",
428 __func__, __LINE__,
429 (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
430 : "hard"));
431 ps3_sm_force_power_off = 1;
432
433
434
435
436
437 wmb();
438 kill_cad_pid(SIGINT, 1);
439 break;
440 case PS3_SM_EVENT_POWER_RELEASED:
441 dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
442 __func__, __LINE__, event.value);
443 break;
444 case PS3_SM_EVENT_RESET_PRESSED:
445 dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n",
446 __func__, __LINE__,
447 (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft"
448 : "hard"));
449 ps3_sm_force_power_off = 0;
450
451
452
453
454
455 wmb();
456 kill_cad_pid(SIGINT, 1);
457 break;
458 case PS3_SM_EVENT_RESET_RELEASED:
459 dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n",
460 __func__, __LINE__, event.value);
461 break;
462 case PS3_SM_EVENT_THERMAL_ALERT:
463 dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
464 __func__, __LINE__, event.value);
465 pr_info("PS3 Thermal Alert Zone %u\n", event.value);
466 break;
467 case PS3_SM_EVENT_THERMAL_CLEARED:
468 dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
469 __func__, __LINE__, event.value);
470 break;
471 default:
472 dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
473 __func__, __LINE__, event.type);
474 return -EIO;
475 }
476
477 return 0;
478}
479
480
481
482
483
484
485static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev)
486{
487 int result;
488 struct {
489 u8 version;
490 u8 type;
491 u8 reserved_1[14];
492 } cmd;
493
494 BUILD_BUG_ON(sizeof(cmd) != 16);
495
496 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
497
498 result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
499 BUG_ON(result && "need to retry here");
500
501 if (result)
502 return result;
503
504 if (cmd.version != 1) {
505 dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
506 __func__, __LINE__, cmd.version);
507 return -EIO;
508 }
509
510 if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
511 dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
512 __func__, __LINE__, cmd.type);
513 return -EIO;
514 }
515
516 ps3_sys_manager_send_response(dev, 0);
517 return 0;
518}
519
520
521
522
523
524
525
526static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev)
527{
528 int result;
529 struct ps3_sys_manager_header header;
530
531 result = ps3_vuart_read(dev, &header,
532 sizeof(struct ps3_sys_manager_header));
533
534 if (result)
535 return result;
536
537 if (header.version != 1) {
538 dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
539 __func__, __LINE__, header.version);
540 dump_sm_header(&header);
541 goto fail_header;
542 }
543
544 BUILD_BUG_ON(sizeof(header) != 16);
545
546 if (header.size != 16 || (header.payload_size != 8
547 && header.payload_size != 16)) {
548 dump_sm_header(&header);
549 BUG();
550 }
551
552 switch (header.service_id) {
553 case PS3_SM_SERVICE_ID_EXTERN_EVENT:
554 dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
555 return ps3_sys_manager_handle_event(dev);
556 case PS3_SM_SERVICE_ID_COMMAND:
557 dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
558 return ps3_sys_manager_handle_cmd(dev);
559 case PS3_SM_SERVICE_ID_REQUEST_ERROR:
560 dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__,
561 __LINE__);
562 dump_sm_header(&header);
563 break;
564 default:
565 dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
566 __func__, __LINE__, header.service_id);
567 break;
568 }
569 goto fail_id;
570
571fail_header:
572 ps3_vuart_clear_rx_bytes(dev, 0);
573 return -EIO;
574fail_id:
575 ps3_vuart_clear_rx_bytes(dev, header.payload_size);
576 return -EIO;
577}
578
579static void ps3_sys_manager_fin(struct ps3_system_bus_device *dev)
580{
581 ps3_sys_manager_send_request_shutdown(dev);
582
583 pr_emerg("System Halted, OK to turn off power\n");
584
585 while (ps3_sys_manager_handle_msg(dev)) {
586
587 lv1_pause(0);
588 }
589
590 while (1) {
591
592 lv1_pause(1);
593 }
594}
595
596
597
598
599
600
601
602
603
604
605
606
607static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev)
608{
609 BUG_ON(!dev);
610
611 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
612
613 ps3_vuart_cancel_async(dev);
614
615 ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
616 user_wake_sources);
617
618 ps3_sys_manager_fin(dev);
619}
620
621
622
623
624
625
626
627
628
629
630
631static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev)
632{
633 BUG_ON(!dev);
634
635 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
636
637
638
639 if (ps3_sm_force_power_off) {
640 dev_dbg(&dev->core, "%s:%d: forcing poweroff\n",
641 __func__, __LINE__);
642 ps3_sys_manager_final_power_off(dev);
643 }
644
645 ps3_vuart_cancel_async(dev);
646
647 ps3_sys_manager_send_attr(dev, 0);
648 ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT,
649 user_wake_sources);
650
651 ps3_sys_manager_fin(dev);
652}
653
654
655
656
657
658int ps3_sys_manager_get_wol(void)
659{
660 pr_debug("%s:%d\n", __func__, __LINE__);
661
662 return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0;
663}
664EXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol);
665
666
667
668
669
670void ps3_sys_manager_set_wol(int state)
671{
672 static DEFINE_MUTEX(mutex);
673
674 mutex_lock(&mutex);
675
676 pr_debug("%s:%d: %d\n", __func__, __LINE__, state);
677
678 if (state)
679 user_wake_sources |= PS3_SM_WAKE_W_O_L;
680 else
681 user_wake_sources &= ~PS3_SM_WAKE_W_O_L;
682 mutex_unlock(&mutex);
683}
684EXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol);
685
686
687
688
689
690
691
692static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
693{
694 ps3_sys_manager_handle_msg(dev);
695 ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
696}
697
698static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
699{
700 int result;
701 struct ps3_sys_manager_ops ops;
702
703 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
704
705 ops.power_off = ps3_sys_manager_final_power_off;
706 ops.restart = ps3_sys_manager_final_restart;
707 ops.dev = dev;
708
709
710
711 ps3_sys_manager_register_ops(&ops);
712
713 result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
714 BUG_ON(result);
715
716 result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
717 BUG_ON(result);
718
719 return result;
720}
721
722static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev)
723{
724 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
725 return 0;
726}
727
728static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev)
729{
730 dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
731}
732
733static struct ps3_vuart_port_driver ps3_sys_manager = {
734 .core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
735 .core.core.name = "ps3_sys_manager",
736 .probe = ps3_sys_manager_probe,
737 .remove = ps3_sys_manager_remove,
738 .shutdown = ps3_sys_manager_shutdown,
739 .work = ps3_sys_manager_work,
740};
741
742static int __init ps3_sys_manager_init(void)
743{
744 if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
745 return -ENODEV;
746
747 return ps3_vuart_port_driver_register(&ps3_sys_manager);
748}
749
750module_init(ps3_sys_manager_init);
751
752
753MODULE_AUTHOR("Sony Corporation");
754MODULE_LICENSE("GPL v2");
755MODULE_DESCRIPTION("PS3 System Manager");
756MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER);
757