1
2
3
4
5
6
7
8
9#include <linux/slab.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/interrupt.h>
13#include <linux/uaccess.h>
14#include <asm/smp.h>
15#include <asm/time.h>
16#include <asm/ps3.h>
17#include <asm/lv1call.h>
18#include <asm/cell-pmu.h>
19
20
21
22#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL
23#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL
24#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL
25#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL
26#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL
27#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL
28
29
30#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000
31#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800
32#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000
33#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000
34#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL
35
36
37#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000
38#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000
39#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000
40#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000
41#define PS3_PM_START_STOP_START_MASK 0xFF000000
42#define PS3_PM_START_STOP_STOP_MASK 0x00FF0000
43
44
45#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL
46#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL
47
48
49#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0
50#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6
51#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7
52#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7
53#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15
54#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17
55#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18
56#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18
57#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24
58#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49
59#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52
60#define PM_SIG_GROUP_SPU 41
61#define PM_SIG_GROUP_SPU_TRIGGER 42
62#define PM_SIG_GROUP_SPU_EVENT 43
63#define PM_SIG_GROUP_MFC_MAX 60
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81struct ps3_lpm_shadow_regs {
82 u64 pm_control;
83 u64 pm_start_stop;
84 u64 group_control;
85 u64 debug_bus_control;
86};
87
88#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117struct ps3_lpm_priv {
118 atomic_t open;
119 u64 rights;
120 u64 node_id;
121 u64 pu_id;
122 u64 lpm_id;
123 u64 outlet_id;
124 u64 tb_count;
125 void *tb_cache;
126 u64 tb_cache_size;
127 void *tb_cache_internal;
128 struct ps3_lpm_shadow_regs shadow;
129 struct ps3_system_bus_device *sbd;
130};
131
132enum {
133 PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000,
134};
135
136
137
138
139
140
141
142
143
144static struct ps3_lpm_priv *lpm_priv;
145
146static struct device *sbd_core(void)
147{
148 BUG_ON(!lpm_priv || !lpm_priv->sbd);
149 return &lpm_priv->sbd->core;
150}
151
152
153
154
155
156
157
158
159
160
161
162enum {use_start_stop_bookmark = 1,};
163
164void ps3_set_bookmark(u64 bookmark)
165{
166
167
168
169
170
171
172
173 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");
174 mtspr(SPRN_BKMK, bookmark);
175 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");
176}
177EXPORT_SYMBOL_GPL(ps3_set_bookmark);
178
179void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id)
180{
181 u64 bookmark;
182
183 bookmark = (get_tb() & 0x00000000FFFFFFFFULL) |
184 PS3_PM_BOOKMARK_TAG_KERNEL;
185 bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) |
186 (incident << 48) | (th_id << 32) | bookmark;
187 ps3_set_bookmark(bookmark);
188}
189EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark);
190
191
192
193
194
195
196
197
198u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr)
199{
200 int result;
201 u64 counter0415;
202 u64 counter2637;
203
204 if (phys_ctr >= NR_PHYS_CTRS) {
205 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
206 __LINE__, phys_ctr);
207 return 0;
208 }
209
210 result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415,
211 &counter2637);
212 if (result) {
213 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "
214 "phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr,
215 ps3_result(result));
216 return 0;
217 }
218
219 switch (phys_ctr) {
220 case 0:
221 return counter0415 >> 32;
222 case 1:
223 return counter0415 & PS3_PM_COUNTER_MASK_LO;
224 case 2:
225 return counter2637 >> 32;
226 case 3:
227 return counter2637 & PS3_PM_COUNTER_MASK_LO;
228 default:
229 BUG();
230 }
231 return 0;
232}
233EXPORT_SYMBOL_GPL(ps3_read_phys_ctr);
234
235
236
237
238
239
240
241
242void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val)
243{
244 u64 counter0415;
245 u64 counter0415_mask;
246 u64 counter2637;
247 u64 counter2637_mask;
248 int result;
249
250 if (phys_ctr >= NR_PHYS_CTRS) {
251 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
252 __LINE__, phys_ctr);
253 return;
254 }
255
256 switch (phys_ctr) {
257 case 0:
258 counter0415 = (u64)val << 32;
259 counter0415_mask = PS3_PM_COUNTER_MASK_HI;
260 counter2637 = 0x0;
261 counter2637_mask = 0x0;
262 break;
263 case 1:
264 counter0415 = (u64)val;
265 counter0415_mask = PS3_PM_COUNTER_MASK_LO;
266 counter2637 = 0x0;
267 counter2637_mask = 0x0;
268 break;
269 case 2:
270 counter0415 = 0x0;
271 counter0415_mask = 0x0;
272 counter2637 = (u64)val << 32;
273 counter2637_mask = PS3_PM_COUNTER_MASK_HI;
274 break;
275 case 3:
276 counter0415 = 0x0;
277 counter0415_mask = 0x0;
278 counter2637 = (u64)val;
279 counter2637_mask = PS3_PM_COUNTER_MASK_LO;
280 break;
281 default:
282 BUG();
283 }
284
285 result = lv1_set_lpm_counter(lpm_priv->lpm_id,
286 counter0415, counter0415_mask,
287 counter2637, counter2637_mask,
288 &counter0415, &counter2637);
289 if (result)
290 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "
291 "phys_ctr %u, val %u, %s\n", __func__, __LINE__,
292 phys_ctr, val, ps3_result(result));
293}
294EXPORT_SYMBOL_GPL(ps3_write_phys_ctr);
295
296
297
298
299
300
301
302
303u32 ps3_read_ctr(u32 cpu, u32 ctr)
304{
305 u32 val;
306 u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1);
307
308 val = ps3_read_phys_ctr(cpu, phys_ctr);
309
310 if (ps3_get_ctr_size(cpu, phys_ctr) == 16)
311 val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff);
312
313 return val;
314}
315EXPORT_SYMBOL_GPL(ps3_read_ctr);
316
317
318
319
320
321
322
323
324void ps3_write_ctr(u32 cpu, u32 ctr, u32 val)
325{
326 u32 phys_ctr;
327 u32 phys_val;
328
329 phys_ctr = ctr & (NR_PHYS_CTRS - 1);
330
331 if (ps3_get_ctr_size(cpu, phys_ctr) == 16) {
332 phys_val = ps3_read_phys_ctr(cpu, phys_ctr);
333
334 if (ctr < NR_PHYS_CTRS)
335 val = (val << 16) | (phys_val & 0xffff);
336 else
337 val = (val & 0xffff) | (phys_val & 0xffff0000);
338 }
339
340 ps3_write_phys_ctr(cpu, phys_ctr, val);
341}
342EXPORT_SYMBOL_GPL(ps3_write_ctr);
343
344
345
346
347
348
349
350u32 ps3_read_pm07_control(u32 cpu, u32 ctr)
351{
352 return 0;
353}
354EXPORT_SYMBOL_GPL(ps3_read_pm07_control);
355
356
357
358
359
360
361
362void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val)
363{
364 int result;
365 static const u64 mask = 0xFFFFFFFFFFFFFFFFULL;
366 u64 old_value;
367
368 if (ctr >= NR_CTRS) {
369 dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__,
370 __LINE__, ctr);
371 return;
372 }
373
374 result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask,
375 &old_value);
376 if (result)
377 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control "
378 "failed: ctr %u, %s\n", __func__, __LINE__, ctr,
379 ps3_result(result));
380}
381EXPORT_SYMBOL_GPL(ps3_write_pm07_control);
382
383
384
385
386
387u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg)
388{
389 int result = 0;
390 u64 val = 0;
391
392 switch (reg) {
393 case pm_control:
394 return lpm_priv->shadow.pm_control;
395 case trace_address:
396 return CBE_PM_TRACE_BUF_EMPTY;
397 case pm_start_stop:
398 return lpm_priv->shadow.pm_start_stop;
399 case pm_interval:
400 result = lv1_set_lpm_interval(lpm_priv->lpm_id, 0, 0, &val);
401 if (result) {
402 val = 0;
403 dev_dbg(sbd_core(), "%s:%u: lv1 set_interval failed: "
404 "reg %u, %s\n", __func__, __LINE__, reg,
405 ps3_result(result));
406 }
407 return (u32)val;
408 case group_control:
409 return lpm_priv->shadow.group_control;
410 case debug_bus_control:
411 return lpm_priv->shadow.debug_bus_control;
412 case pm_status:
413 result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id,
414 &val);
415 if (result) {
416 val = 0;
417 dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: "
418 "reg %u, %s\n", __func__, __LINE__, reg,
419 ps3_result(result));
420 }
421 return (u32)val;
422 case ext_tr_timer:
423 return 0;
424 default:
425 dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
426 __LINE__, reg);
427 BUG();
428 break;
429 }
430
431 return 0;
432}
433EXPORT_SYMBOL_GPL(ps3_read_pm);
434
435
436
437
438
439void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)
440{
441 int result = 0;
442 u64 dummy;
443
444 switch (reg) {
445 case group_control:
446 if (val != lpm_priv->shadow.group_control)
447 result = lv1_set_lpm_group_control(lpm_priv->lpm_id,
448 val,
449 PS3_WRITE_PM_MASK,
450 &dummy);
451 lpm_priv->shadow.group_control = val;
452 break;
453 case debug_bus_control:
454 if (val != lpm_priv->shadow.debug_bus_control)
455 result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id,
456 val,
457 PS3_WRITE_PM_MASK,
458 &dummy);
459 lpm_priv->shadow.debug_bus_control = val;
460 break;
461 case pm_control:
462 if (use_start_stop_bookmark)
463 val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK |
464 PS3_PM_CONTROL_PPU_TH1_BOOKMARK);
465 if (val != lpm_priv->shadow.pm_control)
466 result = lv1_set_lpm_general_control(lpm_priv->lpm_id,
467 val,
468 PS3_WRITE_PM_MASK,
469 0, 0, &dummy,
470 &dummy);
471 lpm_priv->shadow.pm_control = val;
472 break;
473 case pm_interval:
474 result = lv1_set_lpm_interval(lpm_priv->lpm_id, val,
475 PS3_WRITE_PM_MASK, &dummy);
476 break;
477 case pm_start_stop:
478 if (val != lpm_priv->shadow.pm_start_stop)
479 result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
480 val,
481 PS3_WRITE_PM_MASK,
482 &dummy);
483 lpm_priv->shadow.pm_start_stop = val;
484 break;
485 case trace_address:
486 case ext_tr_timer:
487 case pm_status:
488 break;
489 default:
490 dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
491 __LINE__, reg);
492 BUG();
493 break;
494 }
495
496 if (result)
497 dev_err(sbd_core(), "%s:%u: lv1 set_control failed: "
498 "reg %u, %s\n", __func__, __LINE__, reg,
499 ps3_result(result));
500}
501EXPORT_SYMBOL_GPL(ps3_write_pm);
502
503
504
505
506
507
508
509u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr)
510{
511 u32 pm_ctrl;
512
513 if (phys_ctr >= NR_PHYS_CTRS) {
514 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
515 __LINE__, phys_ctr);
516 return 0;
517 }
518
519 pm_ctrl = ps3_read_pm(cpu, pm_control);
520 return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32;
521}
522EXPORT_SYMBOL_GPL(ps3_get_ctr_size);
523
524
525
526
527
528void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)
529{
530 u32 pm_ctrl;
531
532 if (phys_ctr >= NR_PHYS_CTRS) {
533 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
534 __LINE__, phys_ctr);
535 return;
536 }
537
538 pm_ctrl = ps3_read_pm(cpu, pm_control);
539
540 switch (ctr_size) {
541 case 16:
542 pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr);
543 ps3_write_pm(cpu, pm_control, pm_ctrl);
544 break;
545
546 case 32:
547 pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr);
548 ps3_write_pm(cpu, pm_control, pm_ctrl);
549 break;
550 default:
551 BUG();
552 }
553}
554EXPORT_SYMBOL_GPL(ps3_set_ctr_size);
555
556static u64 pm_translate_signal_group_number_on_island2(u64 subgroup)
557{
558
559 if (subgroup == 2)
560 subgroup = 3;
561
562 if (subgroup <= 6)
563 return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup;
564 else if (subgroup == 7)
565 return PM_ISLAND2_SIGNAL_GROUP_NUMBER1;
566 else
567 return PM_ISLAND2_SIGNAL_GROUP_NUMBER2;
568}
569
570static u64 pm_translate_signal_group_number_on_island3(u64 subgroup)
571{
572
573 switch (subgroup) {
574 case 2:
575 case 3:
576 case 4:
577 subgroup += 2;
578 break;
579 case 5:
580 subgroup = 8;
581 break;
582 default:
583 break;
584 }
585 return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup;
586}
587
588static u64 pm_translate_signal_group_number_on_island4(u64 subgroup)
589{
590 return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup;
591}
592
593static u64 pm_translate_signal_group_number_on_island5(u64 subgroup)
594{
595
596 switch (subgroup) {
597 case 3:
598 subgroup = 4;
599 break;
600 case 4:
601 subgroup = 6;
602 break;
603 default:
604 break;
605 }
606 return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup;
607}
608
609static u64 pm_translate_signal_group_number_on_island6(u64 subgroup,
610 u64 subsubgroup)
611{
612 switch (subgroup) {
613 case 3:
614 case 4:
615 case 5:
616 subgroup += 1;
617 break;
618 default:
619 break;
620 }
621
622 switch (subsubgroup) {
623 case 4:
624 case 5:
625 case 6:
626 subsubgroup += 2;
627 break;
628 case 7:
629 case 8:
630 case 9:
631 case 10:
632 subsubgroup += 4;
633 break;
634 case 11:
635 case 12:
636 case 13:
637 subsubgroup += 5;
638 break;
639 default:
640 break;
641 }
642
643 if (subgroup <= 5)
644 return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup);
645 else
646 return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup
647 + subsubgroup - 1);
648}
649
650static u64 pm_translate_signal_group_number_on_island7(u64 subgroup)
651{
652 return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup;
653}
654
655static u64 pm_translate_signal_group_number_on_island8(u64 subgroup)
656{
657 return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup;
658}
659
660static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group)
661{
662 u64 island;
663 u64 subgroup;
664 u64 subsubgroup;
665
666 subgroup = 0;
667 subsubgroup = 0;
668 island = 0;
669 if (group < 1000) {
670 if (group < 100) {
671 if (20 <= group && group < 30) {
672 island = 2;
673 subgroup = group - 20;
674 } else if (30 <= group && group < 40) {
675 island = 3;
676 subgroup = group - 30;
677 } else if (40 <= group && group < 50) {
678 island = 4;
679 subgroup = group - 40;
680 } else if (50 <= group && group < 60) {
681 island = 5;
682 subgroup = group - 50;
683 } else if (60 <= group && group < 70) {
684 island = 6;
685 subgroup = group - 60;
686 } else if (70 <= group && group < 80) {
687 island = 7;
688 subgroup = group - 70;
689 } else if (80 <= group && group < 90) {
690 island = 8;
691 subgroup = group - 80;
692 }
693 } else if (200 <= group && group < 300) {
694 island = 2;
695 subgroup = group - 200;
696 } else if (600 <= group && group < 700) {
697 island = 6;
698 subgroup = 5;
699 subsubgroup = group - 650;
700 }
701 } else if (6000 <= group && group < 7000) {
702 island = 6;
703 subgroup = 5;
704 subsubgroup = group - 6500;
705 }
706
707 switch (island) {
708 case 2:
709 return pm_translate_signal_group_number_on_island2(subgroup);
710 case 3:
711 return pm_translate_signal_group_number_on_island3(subgroup);
712 case 4:
713 return pm_translate_signal_group_number_on_island4(subgroup);
714 case 5:
715 return pm_translate_signal_group_number_on_island5(subgroup);
716 case 6:
717 return pm_translate_signal_group_number_on_island6(subgroup,
718 subsubgroup);
719 case 7:
720 return pm_translate_signal_group_number_on_island7(subgroup);
721 case 8:
722 return pm_translate_signal_group_number_on_island8(subgroup);
723 default:
724 dev_dbg(sbd_core(), "%s:%u: island not found: %llu\n", __func__,
725 __LINE__, group);
726 BUG();
727 break;
728 }
729 return 0;
730}
731
732static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word)
733{
734
735 switch (word) {
736 case 1:
737 return 0xF000;
738 case 2:
739 return 0x0F00;
740 case 4:
741 return 0x00F0;
742 case 8:
743 default:
744 return 0x000F;
745 }
746}
747
748static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select,
749 u64 signal_select, u64 attr1, u64 attr2, u64 attr3)
750{
751 int ret;
752
753 ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select,
754 signal_select, attr1, attr2, attr3);
755 if (ret)
756 dev_err(sbd_core(),
757 "%s:%u: error:%d 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n",
758 __func__, __LINE__, ret, lv1_signal_group, bus_select,
759 signal_select, attr1, attr2, attr3);
760
761 return ret;
762}
763
764int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit,
765 u8 bus_word)
766{
767 int ret;
768 u64 lv1_signal_group;
769 u64 bus_select;
770 u64 signal_select;
771 u64 attr1, attr2, attr3;
772
773 if (signal_group == 0)
774 return __ps3_set_signal(0, 0, 0, 0, 0, 0);
775
776 lv1_signal_group =
777 pm_signal_group_to_ps3_lv1_signal_group(signal_group);
778 bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word);
779
780 switch (signal_group) {
781 case PM_SIG_GROUP_SPU_TRIGGER:
782 signal_select = 1;
783 signal_select = signal_select << (63 - signal_bit);
784 break;
785 case PM_SIG_GROUP_SPU_EVENT:
786 signal_select = 1;
787 signal_select = (signal_select << (63 - signal_bit)) | 0x3;
788 break;
789 default:
790 signal_select = 0;
791 break;
792 }
793
794
795
796
797
798
799 attr1 = 1;
800
801
802
803
804
805 if (PM_SIG_GROUP_SPU <= signal_group &&
806 signal_group < PM_SIG_GROUP_MFC_MAX)
807 attr2 = sub_unit;
808 else
809 attr2 = lpm_priv->pu_id;
810
811
812
813
814 attr3 = 0;
815
816 ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select,
817 attr1, attr2, attr3);
818 if (ret)
819 dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n",
820 __func__, __LINE__, ret);
821
822 return ret;
823}
824EXPORT_SYMBOL_GPL(ps3_set_signal);
825
826u32 ps3_get_hw_thread_id(int cpu)
827{
828 return get_hard_smp_processor_id(cpu);
829}
830EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id);
831
832
833
834
835
836
837
838void ps3_enable_pm(u32 cpu)
839{
840 int result;
841 u64 tmp;
842 int insert_bookmark = 0;
843
844 lpm_priv->tb_count = 0;
845
846 if (use_start_stop_bookmark) {
847 if (!(lpm_priv->shadow.pm_start_stop &
848 (PS3_PM_START_STOP_START_MASK
849 | PS3_PM_START_STOP_STOP_MASK))) {
850 result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
851 (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START |
852 PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START |
853 PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP |
854 PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP),
855 0xFFFFFFFFFFFFFFFFULL, &tmp);
856
857 if (result)
858 dev_err(sbd_core(), "%s:%u: "
859 "lv1_set_lpm_trigger_control failed: "
860 "%s\n", __func__, __LINE__,
861 ps3_result(result));
862
863 insert_bookmark = !result;
864 }
865 }
866
867 result = lv1_start_lpm(lpm_priv->lpm_id);
868
869 if (result)
870 dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n",
871 __func__, __LINE__, ps3_result(result));
872
873 if (use_start_stop_bookmark && !result && insert_bookmark)
874 ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START);
875}
876EXPORT_SYMBOL_GPL(ps3_enable_pm);
877
878
879
880
881
882void ps3_disable_pm(u32 cpu)
883{
884 int result;
885 u64 tmp;
886
887 ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP);
888
889 result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp);
890
891 if (result) {
892 if (result != LV1_WRONG_STATE)
893 dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n",
894 __func__, __LINE__, ps3_result(result));
895 return;
896 }
897
898 lpm_priv->tb_count = tmp;
899
900 dev_dbg(sbd_core(), "%s:%u: tb_count %llu (%llxh)\n", __func__, __LINE__,
901 lpm_priv->tb_count, lpm_priv->tb_count);
902}
903EXPORT_SYMBOL_GPL(ps3_disable_pm);
904
905
906
907
908
909
910
911
912
913
914
915
916
917int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count,
918 unsigned long *bytes_copied)
919{
920 int result;
921
922 *bytes_copied = 0;
923
924 if (!lpm_priv->tb_cache)
925 return -EPERM;
926
927 if (offset >= lpm_priv->tb_count)
928 return 0;
929
930 count = min_t(u64, count, lpm_priv->tb_count - offset);
931
932 while (*bytes_copied < count) {
933 const unsigned long request = count - *bytes_copied;
934 u64 tmp;
935
936 result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
937 request, &tmp);
938 if (result) {
939 dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
940 __func__, __LINE__, request, offset);
941
942 dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
943 "failed: %s\n", __func__, __LINE__,
944 ps3_result(result));
945 return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
946 }
947
948 memcpy(buf, lpm_priv->tb_cache, tmp);
949 buf += tmp;
950 *bytes_copied += tmp;
951 offset += tmp;
952 }
953 dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,
954 *bytes_copied);
955
956 return 0;
957}
958EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb);
959
960
961
962
963
964
965
966
967
968
969
970
971
972int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,
973 unsigned long count, unsigned long *bytes_copied)
974{
975 int result;
976
977 *bytes_copied = 0;
978
979 if (!lpm_priv->tb_cache)
980 return -EPERM;
981
982 if (offset >= lpm_priv->tb_count)
983 return 0;
984
985 count = min_t(u64, count, lpm_priv->tb_count - offset);
986
987 while (*bytes_copied < count) {
988 const unsigned long request = count - *bytes_copied;
989 u64 tmp;
990
991 result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
992 request, &tmp);
993 if (result) {
994 dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
995 __func__, __LINE__, request, offset);
996 dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
997 "failed: %s\n", __func__, __LINE__,
998 ps3_result(result));
999 return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
1000 }
1001
1002 result = copy_to_user(buf, lpm_priv->tb_cache, tmp);
1003
1004 if (result) {
1005 dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n",
1006 __func__, __LINE__, tmp, buf);
1007 dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n",
1008 __func__, __LINE__, result);
1009 return -EFAULT;
1010 }
1011
1012 buf += tmp;
1013 *bytes_copied += tmp;
1014 offset += tmp;
1015 }
1016 dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,
1017 *bytes_copied);
1018
1019 return 0;
1020}
1021EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user);
1022
1023
1024
1025
1026
1027
1028
1029
1030u32 ps3_get_and_clear_pm_interrupts(u32 cpu)
1031{
1032 return ps3_read_pm(cpu, pm_status);
1033}
1034EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts);
1035
1036
1037
1038
1039
1040
1041
1042
1043void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)
1044{
1045 if (mask)
1046 ps3_write_pm(cpu, pm_status, mask);
1047}
1048EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts);
1049
1050
1051
1052
1053
1054
1055
1056void ps3_disable_pm_interrupts(u32 cpu)
1057{
1058 ps3_get_and_clear_pm_interrupts(cpu);
1059 ps3_write_pm(cpu, pm_status, 0);
1060}
1061EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts);
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache,
1075 u64 tb_cache_size)
1076{
1077 int result;
1078 u64 tb_size;
1079
1080 BUG_ON(!lpm_priv);
1081 BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE
1082 && tb_type != PS3_LPM_TB_TYPE_INTERNAL);
1083
1084 if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache)
1085 dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__);
1086
1087 if (!atomic_add_unless(&lpm_priv->open, 1, 1)) {
1088 dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__);
1089 return -EBUSY;
1090 }
1091
1092
1093
1094 if (tb_type == PS3_LPM_TB_TYPE_NONE) {
1095 lpm_priv->tb_cache_size = 0;
1096 lpm_priv->tb_cache_internal = NULL;
1097 lpm_priv->tb_cache = NULL;
1098 } else if (tb_cache) {
1099 if (tb_cache != (void *)_ALIGN_UP((unsigned long)tb_cache, 128)
1100 || tb_cache_size != _ALIGN_UP(tb_cache_size, 128)) {
1101 dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n",
1102 __func__, __LINE__);
1103 result = -EINVAL;
1104 goto fail_align;
1105 }
1106 lpm_priv->tb_cache_size = tb_cache_size;
1107 lpm_priv->tb_cache_internal = NULL;
1108 lpm_priv->tb_cache = tb_cache;
1109 } else {
1110 lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE;
1111 lpm_priv->tb_cache_internal = kzalloc(
1112 lpm_priv->tb_cache_size + 127, GFP_KERNEL);
1113 if (!lpm_priv->tb_cache_internal) {
1114 dev_err(sbd_core(), "%s:%u: alloc internal tb_cache "
1115 "failed\n", __func__, __LINE__);
1116 result = -ENOMEM;
1117 goto fail_malloc;
1118 }
1119 lpm_priv->tb_cache = (void *)_ALIGN_UP(
1120 (unsigned long)lpm_priv->tb_cache_internal, 128);
1121 }
1122
1123 result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0,
1124 ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)),
1125 lpm_priv->tb_cache_size, &lpm_priv->lpm_id,
1126 &lpm_priv->outlet_id, &tb_size);
1127
1128 if (result) {
1129 dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n",
1130 __func__, __LINE__, ps3_result(result));
1131 result = -EINVAL;
1132 goto fail_construct;
1133 }
1134
1135 lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT;
1136 lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT;
1137 lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT;
1138 lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT;
1139
1140 dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%llx, outlet_id 0x%llx, "
1141 "tb_size 0x%llx\n", __func__, __LINE__, lpm_priv->lpm_id,
1142 lpm_priv->outlet_id, tb_size);
1143
1144 return 0;
1145
1146fail_construct:
1147 kfree(lpm_priv->tb_cache_internal);
1148 lpm_priv->tb_cache_internal = NULL;
1149fail_malloc:
1150fail_align:
1151 atomic_dec(&lpm_priv->open);
1152 return result;
1153}
1154EXPORT_SYMBOL_GPL(ps3_lpm_open);
1155
1156
1157
1158
1159
1160
1161int ps3_lpm_close(void)
1162{
1163 dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__);
1164
1165 lv1_destruct_lpm(lpm_priv->lpm_id);
1166 lpm_priv->lpm_id = 0;
1167
1168 kfree(lpm_priv->tb_cache_internal);
1169 lpm_priv->tb_cache_internal = NULL;
1170
1171 atomic_dec(&lpm_priv->open);
1172 return 0;
1173}
1174EXPORT_SYMBOL_GPL(ps3_lpm_close);
1175
1176static int ps3_lpm_probe(struct ps3_system_bus_device *dev)
1177{
1178 dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__);
1179
1180 if (lpm_priv) {
1181 dev_info(&dev->core, "%s:%u: called twice\n",
1182 __func__, __LINE__);
1183 return -EBUSY;
1184 }
1185
1186 lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL);
1187
1188 if (!lpm_priv)
1189 return -ENOMEM;
1190
1191 lpm_priv->sbd = dev;
1192 lpm_priv->node_id = dev->lpm.node_id;
1193 lpm_priv->pu_id = dev->lpm.pu_id;
1194 lpm_priv->rights = dev->lpm.rights;
1195
1196 dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
1197
1198 return 0;
1199}
1200
1201static int ps3_lpm_remove(struct ps3_system_bus_device *dev)
1202{
1203 dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__);
1204
1205 ps3_lpm_close();
1206
1207 kfree(lpm_priv);
1208 lpm_priv = NULL;
1209
1210 dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
1211 return 0;
1212}
1213
1214static struct ps3_system_bus_driver ps3_lpm_driver = {
1215 .match_id = PS3_MATCH_ID_LPM,
1216 .core.name = "ps3-lpm",
1217 .core.owner = THIS_MODULE,
1218 .probe = ps3_lpm_probe,
1219 .remove = ps3_lpm_remove,
1220 .shutdown = ps3_lpm_remove,
1221};
1222
1223static int __init ps3_lpm_init(void)
1224{
1225 pr_debug("%s:%d:\n", __func__, __LINE__);
1226 return ps3_system_bus_driver_register(&ps3_lpm_driver);
1227}
1228
1229static void __exit ps3_lpm_exit(void)
1230{
1231 pr_debug("%s:%d:\n", __func__, __LINE__);
1232 ps3_system_bus_driver_unregister(&ps3_lpm_driver);
1233}
1234
1235module_init(ps3_lpm_init);
1236module_exit(ps3_lpm_exit);
1237
1238MODULE_LICENSE("GPL v2");
1239MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver");
1240MODULE_AUTHOR("Sony Corporation");
1241MODULE_ALIAS(PS3_MODULE_ALIAS_LPM);
1242