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