0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/slab.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/uaccess.h>
0014 #include <asm/smp.h>
0015 #include <asm/time.h>
0016 #include <asm/ps3.h>
0017 #include <asm/lv1call.h>
0018 #include <asm/cell-pmu.h>
0019
0020
0021
0022 #define PS3_PM_BOOKMARK_START 0x8000000000000000ULL
0023 #define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL
0024 #define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL
0025 #define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL
0026 #define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL
0027 #define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL
0028
0029
0030 #define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000
0031 #define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800
0032 #define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000
0033 #define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000
0034 #define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL
0035
0036
0037 #define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000
0038 #define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000
0039 #define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000
0040 #define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000
0041 #define PS3_PM_START_STOP_START_MASK 0xFF000000
0042 #define PS3_PM_START_STOP_STOP_MASK 0x00FF0000
0043
0044
0045 #define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL
0046 #define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL
0047
0048
0049 #define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0
0050 #define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6
0051 #define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7
0052 #define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7
0053 #define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15
0054 #define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17
0055 #define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18
0056 #define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18
0057 #define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24
0058 #define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49
0059 #define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52
0060 #define PM_SIG_GROUP_SPU 41
0061 #define PM_SIG_GROUP_SPU_TRIGGER 42
0062 #define PM_SIG_GROUP_SPU_EVENT 43
0063 #define PM_SIG_GROUP_MFC_MAX 60
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 struct ps3_lpm_shadow_regs {
0082 u64 pm_control;
0083 u64 pm_start_stop;
0084 u64 group_control;
0085 u64 debug_bus_control;
0086 };
0087
0088 #define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117 struct ps3_lpm_priv {
0118 atomic_t open;
0119 u64 rights;
0120 u64 node_id;
0121 u64 pu_id;
0122 u64 lpm_id;
0123 u64 outlet_id;
0124 u64 tb_count;
0125 void *tb_cache;
0126 u64 tb_cache_size;
0127 void *tb_cache_internal;
0128 struct ps3_lpm_shadow_regs shadow;
0129 struct ps3_system_bus_device *sbd;
0130 };
0131
0132 enum {
0133 PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000,
0134 };
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144 static struct ps3_lpm_priv *lpm_priv;
0145
0146 static struct device *sbd_core(void)
0147 {
0148 BUG_ON(!lpm_priv || !lpm_priv->sbd);
0149 return &lpm_priv->sbd->core;
0150 }
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162 enum {use_start_stop_bookmark = 1,};
0163
0164 void ps3_set_bookmark(u64 bookmark)
0165 {
0166
0167
0168
0169
0170
0171
0172
0173 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");
0174 mtspr(SPRN_BKMK, bookmark);
0175 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");
0176 }
0177 EXPORT_SYMBOL_GPL(ps3_set_bookmark);
0178
0179 void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id)
0180 {
0181 u64 bookmark;
0182
0183 bookmark = (get_tb() & 0x00000000FFFFFFFFULL) |
0184 PS3_PM_BOOKMARK_TAG_KERNEL;
0185 bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) |
0186 (incident << 48) | (th_id << 32) | bookmark;
0187 ps3_set_bookmark(bookmark);
0188 }
0189 EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark);
0190
0191
0192
0193
0194
0195
0196
0197
0198 u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr)
0199 {
0200 int result;
0201 u64 counter0415;
0202 u64 counter2637;
0203
0204 if (phys_ctr >= NR_PHYS_CTRS) {
0205 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
0206 __LINE__, phys_ctr);
0207 return 0;
0208 }
0209
0210 result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415,
0211 &counter2637);
0212 if (result) {
0213 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "
0214 "phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr,
0215 ps3_result(result));
0216 return 0;
0217 }
0218
0219 switch (phys_ctr) {
0220 case 0:
0221 return counter0415 >> 32;
0222 case 1:
0223 return counter0415 & PS3_PM_COUNTER_MASK_LO;
0224 case 2:
0225 return counter2637 >> 32;
0226 case 3:
0227 return counter2637 & PS3_PM_COUNTER_MASK_LO;
0228 default:
0229 BUG();
0230 }
0231 return 0;
0232 }
0233 EXPORT_SYMBOL_GPL(ps3_read_phys_ctr);
0234
0235
0236
0237
0238
0239
0240
0241
0242 void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val)
0243 {
0244 u64 counter0415;
0245 u64 counter0415_mask;
0246 u64 counter2637;
0247 u64 counter2637_mask;
0248 int result;
0249
0250 if (phys_ctr >= NR_PHYS_CTRS) {
0251 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
0252 __LINE__, phys_ctr);
0253 return;
0254 }
0255
0256 switch (phys_ctr) {
0257 case 0:
0258 counter0415 = (u64)val << 32;
0259 counter0415_mask = PS3_PM_COUNTER_MASK_HI;
0260 counter2637 = 0x0;
0261 counter2637_mask = 0x0;
0262 break;
0263 case 1:
0264 counter0415 = (u64)val;
0265 counter0415_mask = PS3_PM_COUNTER_MASK_LO;
0266 counter2637 = 0x0;
0267 counter2637_mask = 0x0;
0268 break;
0269 case 2:
0270 counter0415 = 0x0;
0271 counter0415_mask = 0x0;
0272 counter2637 = (u64)val << 32;
0273 counter2637_mask = PS3_PM_COUNTER_MASK_HI;
0274 break;
0275 case 3:
0276 counter0415 = 0x0;
0277 counter0415_mask = 0x0;
0278 counter2637 = (u64)val;
0279 counter2637_mask = PS3_PM_COUNTER_MASK_LO;
0280 break;
0281 default:
0282 BUG();
0283 }
0284
0285 result = lv1_set_lpm_counter(lpm_priv->lpm_id,
0286 counter0415, counter0415_mask,
0287 counter2637, counter2637_mask,
0288 &counter0415, &counter2637);
0289 if (result)
0290 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "
0291 "phys_ctr %u, val %u, %s\n", __func__, __LINE__,
0292 phys_ctr, val, ps3_result(result));
0293 }
0294 EXPORT_SYMBOL_GPL(ps3_write_phys_ctr);
0295
0296
0297
0298
0299
0300
0301
0302
0303 u32 ps3_read_ctr(u32 cpu, u32 ctr)
0304 {
0305 u32 val;
0306 u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1);
0307
0308 val = ps3_read_phys_ctr(cpu, phys_ctr);
0309
0310 if (ps3_get_ctr_size(cpu, phys_ctr) == 16)
0311 val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff);
0312
0313 return val;
0314 }
0315 EXPORT_SYMBOL_GPL(ps3_read_ctr);
0316
0317
0318
0319
0320
0321
0322
0323
0324 void ps3_write_ctr(u32 cpu, u32 ctr, u32 val)
0325 {
0326 u32 phys_ctr;
0327 u32 phys_val;
0328
0329 phys_ctr = ctr & (NR_PHYS_CTRS - 1);
0330
0331 if (ps3_get_ctr_size(cpu, phys_ctr) == 16) {
0332 phys_val = ps3_read_phys_ctr(cpu, phys_ctr);
0333
0334 if (ctr < NR_PHYS_CTRS)
0335 val = (val << 16) | (phys_val & 0xffff);
0336 else
0337 val = (val & 0xffff) | (phys_val & 0xffff0000);
0338 }
0339
0340 ps3_write_phys_ctr(cpu, phys_ctr, val);
0341 }
0342 EXPORT_SYMBOL_GPL(ps3_write_ctr);
0343
0344
0345
0346
0347
0348
0349
0350 u32 ps3_read_pm07_control(u32 cpu, u32 ctr)
0351 {
0352 return 0;
0353 }
0354 EXPORT_SYMBOL_GPL(ps3_read_pm07_control);
0355
0356
0357
0358
0359
0360
0361
0362 void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val)
0363 {
0364 int result;
0365 static const u64 mask = 0xFFFFFFFFFFFFFFFFULL;
0366 u64 old_value;
0367
0368 if (ctr >= NR_CTRS) {
0369 dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__,
0370 __LINE__, ctr);
0371 return;
0372 }
0373
0374 result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask,
0375 &old_value);
0376 if (result)
0377 dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control "
0378 "failed: ctr %u, %s\n", __func__, __LINE__, ctr,
0379 ps3_result(result));
0380 }
0381 EXPORT_SYMBOL_GPL(ps3_write_pm07_control);
0382
0383
0384
0385
0386
0387 u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg)
0388 {
0389 int result = 0;
0390 u64 val = 0;
0391
0392 switch (reg) {
0393 case pm_control:
0394 return lpm_priv->shadow.pm_control;
0395 case trace_address:
0396 return CBE_PM_TRACE_BUF_EMPTY;
0397 case pm_start_stop:
0398 return lpm_priv->shadow.pm_start_stop;
0399 case pm_interval:
0400 result = lv1_set_lpm_interval(lpm_priv->lpm_id, 0, 0, &val);
0401 if (result) {
0402 val = 0;
0403 dev_dbg(sbd_core(), "%s:%u: lv1 set_interval failed: "
0404 "reg %u, %s\n", __func__, __LINE__, reg,
0405 ps3_result(result));
0406 }
0407 return (u32)val;
0408 case group_control:
0409 return lpm_priv->shadow.group_control;
0410 case debug_bus_control:
0411 return lpm_priv->shadow.debug_bus_control;
0412 case pm_status:
0413 result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id,
0414 &val);
0415 if (result) {
0416 val = 0;
0417 dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: "
0418 "reg %u, %s\n", __func__, __LINE__, reg,
0419 ps3_result(result));
0420 }
0421 return (u32)val;
0422 case ext_tr_timer:
0423 return 0;
0424 default:
0425 dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
0426 __LINE__, reg);
0427 BUG();
0428 break;
0429 }
0430
0431 return 0;
0432 }
0433 EXPORT_SYMBOL_GPL(ps3_read_pm);
0434
0435
0436
0437
0438
0439 void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)
0440 {
0441 int result = 0;
0442 u64 dummy;
0443
0444 switch (reg) {
0445 case group_control:
0446 if (val != lpm_priv->shadow.group_control)
0447 result = lv1_set_lpm_group_control(lpm_priv->lpm_id,
0448 val,
0449 PS3_WRITE_PM_MASK,
0450 &dummy);
0451 lpm_priv->shadow.group_control = val;
0452 break;
0453 case debug_bus_control:
0454 if (val != lpm_priv->shadow.debug_bus_control)
0455 result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id,
0456 val,
0457 PS3_WRITE_PM_MASK,
0458 &dummy);
0459 lpm_priv->shadow.debug_bus_control = val;
0460 break;
0461 case pm_control:
0462 if (use_start_stop_bookmark)
0463 val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK |
0464 PS3_PM_CONTROL_PPU_TH1_BOOKMARK);
0465 if (val != lpm_priv->shadow.pm_control)
0466 result = lv1_set_lpm_general_control(lpm_priv->lpm_id,
0467 val,
0468 PS3_WRITE_PM_MASK,
0469 0, 0, &dummy,
0470 &dummy);
0471 lpm_priv->shadow.pm_control = val;
0472 break;
0473 case pm_interval:
0474 result = lv1_set_lpm_interval(lpm_priv->lpm_id, val,
0475 PS3_WRITE_PM_MASK, &dummy);
0476 break;
0477 case pm_start_stop:
0478 if (val != lpm_priv->shadow.pm_start_stop)
0479 result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
0480 val,
0481 PS3_WRITE_PM_MASK,
0482 &dummy);
0483 lpm_priv->shadow.pm_start_stop = val;
0484 break;
0485 case trace_address:
0486 case ext_tr_timer:
0487 case pm_status:
0488 break;
0489 default:
0490 dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
0491 __LINE__, reg);
0492 BUG();
0493 break;
0494 }
0495
0496 if (result)
0497 dev_err(sbd_core(), "%s:%u: lv1 set_control failed: "
0498 "reg %u, %s\n", __func__, __LINE__, reg,
0499 ps3_result(result));
0500 }
0501 EXPORT_SYMBOL_GPL(ps3_write_pm);
0502
0503
0504
0505
0506
0507
0508
0509 u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr)
0510 {
0511 u32 pm_ctrl;
0512
0513 if (phys_ctr >= NR_PHYS_CTRS) {
0514 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
0515 __LINE__, phys_ctr);
0516 return 0;
0517 }
0518
0519 pm_ctrl = ps3_read_pm(cpu, pm_control);
0520 return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32;
0521 }
0522 EXPORT_SYMBOL_GPL(ps3_get_ctr_size);
0523
0524
0525
0526
0527
0528 void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)
0529 {
0530 u32 pm_ctrl;
0531
0532 if (phys_ctr >= NR_PHYS_CTRS) {
0533 dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
0534 __LINE__, phys_ctr);
0535 return;
0536 }
0537
0538 pm_ctrl = ps3_read_pm(cpu, pm_control);
0539
0540 switch (ctr_size) {
0541 case 16:
0542 pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr);
0543 ps3_write_pm(cpu, pm_control, pm_ctrl);
0544 break;
0545
0546 case 32:
0547 pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr);
0548 ps3_write_pm(cpu, pm_control, pm_ctrl);
0549 break;
0550 default:
0551 BUG();
0552 }
0553 }
0554 EXPORT_SYMBOL_GPL(ps3_set_ctr_size);
0555
0556 static u64 pm_translate_signal_group_number_on_island2(u64 subgroup)
0557 {
0558
0559 if (subgroup == 2)
0560 subgroup = 3;
0561
0562 if (subgroup <= 6)
0563 return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0564 else if (subgroup == 7)
0565 return PM_ISLAND2_SIGNAL_GROUP_NUMBER1;
0566 else
0567 return PM_ISLAND2_SIGNAL_GROUP_NUMBER2;
0568 }
0569
0570 static u64 pm_translate_signal_group_number_on_island3(u64 subgroup)
0571 {
0572
0573 switch (subgroup) {
0574 case 2:
0575 case 3:
0576 case 4:
0577 subgroup += 2;
0578 break;
0579 case 5:
0580 subgroup = 8;
0581 break;
0582 default:
0583 break;
0584 }
0585 return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0586 }
0587
0588 static u64 pm_translate_signal_group_number_on_island4(u64 subgroup)
0589 {
0590 return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0591 }
0592
0593 static u64 pm_translate_signal_group_number_on_island5(u64 subgroup)
0594 {
0595
0596 switch (subgroup) {
0597 case 3:
0598 subgroup = 4;
0599 break;
0600 case 4:
0601 subgroup = 6;
0602 break;
0603 default:
0604 break;
0605 }
0606 return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0607 }
0608
0609 static u64 pm_translate_signal_group_number_on_island6(u64 subgroup,
0610 u64 subsubgroup)
0611 {
0612 switch (subgroup) {
0613 case 3:
0614 case 4:
0615 case 5:
0616 subgroup += 1;
0617 break;
0618 default:
0619 break;
0620 }
0621
0622 switch (subsubgroup) {
0623 case 4:
0624 case 5:
0625 case 6:
0626 subsubgroup += 2;
0627 break;
0628 case 7:
0629 case 8:
0630 case 9:
0631 case 10:
0632 subsubgroup += 4;
0633 break;
0634 case 11:
0635 case 12:
0636 case 13:
0637 subsubgroup += 5;
0638 break;
0639 default:
0640 break;
0641 }
0642
0643 if (subgroup <= 5)
0644 return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup);
0645 else
0646 return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup
0647 + subsubgroup - 1);
0648 }
0649
0650 static u64 pm_translate_signal_group_number_on_island7(u64 subgroup)
0651 {
0652 return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0653 }
0654
0655 static u64 pm_translate_signal_group_number_on_island8(u64 subgroup)
0656 {
0657 return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup;
0658 }
0659
0660 static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group)
0661 {
0662 u64 island;
0663 u64 subgroup;
0664 u64 subsubgroup;
0665
0666 subgroup = 0;
0667 subsubgroup = 0;
0668 island = 0;
0669 if (group < 1000) {
0670 if (group < 100) {
0671 if (20 <= group && group < 30) {
0672 island = 2;
0673 subgroup = group - 20;
0674 } else if (30 <= group && group < 40) {
0675 island = 3;
0676 subgroup = group - 30;
0677 } else if (40 <= group && group < 50) {
0678 island = 4;
0679 subgroup = group - 40;
0680 } else if (50 <= group && group < 60) {
0681 island = 5;
0682 subgroup = group - 50;
0683 } else if (60 <= group && group < 70) {
0684 island = 6;
0685 subgroup = group - 60;
0686 } else if (70 <= group && group < 80) {
0687 island = 7;
0688 subgroup = group - 70;
0689 } else if (80 <= group && group < 90) {
0690 island = 8;
0691 subgroup = group - 80;
0692 }
0693 } else if (200 <= group && group < 300) {
0694 island = 2;
0695 subgroup = group - 200;
0696 } else if (600 <= group && group < 700) {
0697 island = 6;
0698 subgroup = 5;
0699 subsubgroup = group - 650;
0700 }
0701 } else if (6000 <= group && group < 7000) {
0702 island = 6;
0703 subgroup = 5;
0704 subsubgroup = group - 6500;
0705 }
0706
0707 switch (island) {
0708 case 2:
0709 return pm_translate_signal_group_number_on_island2(subgroup);
0710 case 3:
0711 return pm_translate_signal_group_number_on_island3(subgroup);
0712 case 4:
0713 return pm_translate_signal_group_number_on_island4(subgroup);
0714 case 5:
0715 return pm_translate_signal_group_number_on_island5(subgroup);
0716 case 6:
0717 return pm_translate_signal_group_number_on_island6(subgroup,
0718 subsubgroup);
0719 case 7:
0720 return pm_translate_signal_group_number_on_island7(subgroup);
0721 case 8:
0722 return pm_translate_signal_group_number_on_island8(subgroup);
0723 default:
0724 dev_dbg(sbd_core(), "%s:%u: island not found: %llu\n", __func__,
0725 __LINE__, group);
0726 BUG();
0727 break;
0728 }
0729 return 0;
0730 }
0731
0732 static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word)
0733 {
0734
0735 switch (word) {
0736 case 1:
0737 return 0xF000;
0738 case 2:
0739 return 0x0F00;
0740 case 4:
0741 return 0x00F0;
0742 case 8:
0743 default:
0744 return 0x000F;
0745 }
0746 }
0747
0748 static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select,
0749 u64 signal_select, u64 attr1, u64 attr2, u64 attr3)
0750 {
0751 int ret;
0752
0753 ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select,
0754 signal_select, attr1, attr2, attr3);
0755 if (ret)
0756 dev_err(sbd_core(),
0757 "%s:%u: error:%d 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n",
0758 __func__, __LINE__, ret, lv1_signal_group, bus_select,
0759 signal_select, attr1, attr2, attr3);
0760
0761 return ret;
0762 }
0763
0764 int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit,
0765 u8 bus_word)
0766 {
0767 int ret;
0768 u64 lv1_signal_group;
0769 u64 bus_select;
0770 u64 signal_select;
0771 u64 attr1, attr2, attr3;
0772
0773 if (signal_group == 0)
0774 return __ps3_set_signal(0, 0, 0, 0, 0, 0);
0775
0776 lv1_signal_group =
0777 pm_signal_group_to_ps3_lv1_signal_group(signal_group);
0778 bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word);
0779
0780 switch (signal_group) {
0781 case PM_SIG_GROUP_SPU_TRIGGER:
0782 signal_select = 1;
0783 signal_select = signal_select << (63 - signal_bit);
0784 break;
0785 case PM_SIG_GROUP_SPU_EVENT:
0786 signal_select = 1;
0787 signal_select = (signal_select << (63 - signal_bit)) | 0x3;
0788 break;
0789 default:
0790 signal_select = 0;
0791 break;
0792 }
0793
0794
0795
0796
0797
0798
0799 attr1 = 1;
0800
0801
0802
0803
0804
0805 if (PM_SIG_GROUP_SPU <= signal_group &&
0806 signal_group < PM_SIG_GROUP_MFC_MAX)
0807 attr2 = sub_unit;
0808 else
0809 attr2 = lpm_priv->pu_id;
0810
0811
0812
0813
0814 attr3 = 0;
0815
0816 ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select,
0817 attr1, attr2, attr3);
0818 if (ret)
0819 dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n",
0820 __func__, __LINE__, ret);
0821
0822 return ret;
0823 }
0824 EXPORT_SYMBOL_GPL(ps3_set_signal);
0825
0826 u32 ps3_get_hw_thread_id(int cpu)
0827 {
0828 return get_hard_smp_processor_id(cpu);
0829 }
0830 EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id);
0831
0832
0833
0834
0835
0836
0837
0838 void ps3_enable_pm(u32 cpu)
0839 {
0840 int result;
0841 u64 tmp;
0842 int insert_bookmark = 0;
0843
0844 lpm_priv->tb_count = 0;
0845
0846 if (use_start_stop_bookmark) {
0847 if (!(lpm_priv->shadow.pm_start_stop &
0848 (PS3_PM_START_STOP_START_MASK
0849 | PS3_PM_START_STOP_STOP_MASK))) {
0850 result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
0851 (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START |
0852 PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START |
0853 PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP |
0854 PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP),
0855 0xFFFFFFFFFFFFFFFFULL, &tmp);
0856
0857 if (result)
0858 dev_err(sbd_core(), "%s:%u: "
0859 "lv1_set_lpm_trigger_control failed: "
0860 "%s\n", __func__, __LINE__,
0861 ps3_result(result));
0862
0863 insert_bookmark = !result;
0864 }
0865 }
0866
0867 result = lv1_start_lpm(lpm_priv->lpm_id);
0868
0869 if (result)
0870 dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n",
0871 __func__, __LINE__, ps3_result(result));
0872
0873 if (use_start_stop_bookmark && !result && insert_bookmark)
0874 ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START);
0875 }
0876 EXPORT_SYMBOL_GPL(ps3_enable_pm);
0877
0878
0879
0880
0881
0882 void ps3_disable_pm(u32 cpu)
0883 {
0884 int result;
0885 u64 tmp;
0886
0887 ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP);
0888
0889 result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp);
0890
0891 if (result) {
0892 if (result != LV1_WRONG_STATE)
0893 dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n",
0894 __func__, __LINE__, ps3_result(result));
0895 return;
0896 }
0897
0898 lpm_priv->tb_count = tmp;
0899
0900 dev_dbg(sbd_core(), "%s:%u: tb_count %llu (%llxh)\n", __func__, __LINE__,
0901 lpm_priv->tb_count, lpm_priv->tb_count);
0902 }
0903 EXPORT_SYMBOL_GPL(ps3_disable_pm);
0904
0905
0906
0907
0908
0909
0910
0911
0912
0913
0914
0915
0916
0917 int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count,
0918 unsigned long *bytes_copied)
0919 {
0920 int result;
0921
0922 *bytes_copied = 0;
0923
0924 if (!lpm_priv->tb_cache)
0925 return -EPERM;
0926
0927 if (offset >= lpm_priv->tb_count)
0928 return 0;
0929
0930 count = min_t(u64, count, lpm_priv->tb_count - offset);
0931
0932 while (*bytes_copied < count) {
0933 const unsigned long request = count - *bytes_copied;
0934 u64 tmp;
0935
0936 result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
0937 request, &tmp);
0938 if (result) {
0939 dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
0940 __func__, __LINE__, request, offset);
0941
0942 dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
0943 "failed: %s\n", __func__, __LINE__,
0944 ps3_result(result));
0945 return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
0946 }
0947
0948 memcpy(buf, lpm_priv->tb_cache, tmp);
0949 buf += tmp;
0950 *bytes_copied += tmp;
0951 offset += tmp;
0952 }
0953 dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,
0954 *bytes_copied);
0955
0956 return 0;
0957 }
0958 EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb);
0959
0960
0961
0962
0963
0964
0965
0966
0967
0968
0969
0970
0971
0972 int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,
0973 unsigned long count, unsigned long *bytes_copied)
0974 {
0975 int result;
0976
0977 *bytes_copied = 0;
0978
0979 if (!lpm_priv->tb_cache)
0980 return -EPERM;
0981
0982 if (offset >= lpm_priv->tb_count)
0983 return 0;
0984
0985 count = min_t(u64, count, lpm_priv->tb_count - offset);
0986
0987 while (*bytes_copied < count) {
0988 const unsigned long request = count - *bytes_copied;
0989 u64 tmp;
0990
0991 result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
0992 request, &tmp);
0993 if (result) {
0994 dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
0995 __func__, __LINE__, request, offset);
0996 dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
0997 "failed: %s\n", __func__, __LINE__,
0998 ps3_result(result));
0999 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 }
1021 EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user);
1022
1023
1024
1025
1026
1027
1028
1029
1030 u32 ps3_get_and_clear_pm_interrupts(u32 cpu)
1031 {
1032 return ps3_read_pm(cpu, pm_status);
1033 }
1034 EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts);
1035
1036
1037
1038
1039
1040
1041
1042
1043 void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)
1044 {
1045 if (mask)
1046 ps3_write_pm(cpu, pm_status, mask);
1047 }
1048 EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts);
1049
1050
1051
1052
1053
1054
1055
1056 void ps3_disable_pm_interrupts(u32 cpu)
1057 {
1058 ps3_get_and_clear_pm_interrupts(cpu);
1059 ps3_write_pm(cpu, pm_status, 0);
1060 }
1061 EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts);
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074 int 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((unsigned long)tb_cache, 128)
1100 || tb_cache_size != ALIGN(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 result = -ENOMEM;
1115 goto fail_malloc;
1116 }
1117 lpm_priv->tb_cache = (void *)ALIGN(
1118 (unsigned long)lpm_priv->tb_cache_internal, 128);
1119 }
1120
1121 result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0,
1122 ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)),
1123 lpm_priv->tb_cache_size, &lpm_priv->lpm_id,
1124 &lpm_priv->outlet_id, &tb_size);
1125
1126 if (result) {
1127 dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n",
1128 __func__, __LINE__, ps3_result(result));
1129 result = -EINVAL;
1130 goto fail_construct;
1131 }
1132
1133 lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT;
1134 lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT;
1135 lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT;
1136 lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT;
1137
1138 dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%llx, outlet_id 0x%llx, "
1139 "tb_size 0x%llx\n", __func__, __LINE__, lpm_priv->lpm_id,
1140 lpm_priv->outlet_id, tb_size);
1141
1142 return 0;
1143
1144 fail_construct:
1145 kfree(lpm_priv->tb_cache_internal);
1146 lpm_priv->tb_cache_internal = NULL;
1147 fail_malloc:
1148 fail_align:
1149 atomic_dec(&lpm_priv->open);
1150 return result;
1151 }
1152 EXPORT_SYMBOL_GPL(ps3_lpm_open);
1153
1154
1155
1156
1157
1158
1159 int ps3_lpm_close(void)
1160 {
1161 dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__);
1162
1163 lv1_destruct_lpm(lpm_priv->lpm_id);
1164 lpm_priv->lpm_id = 0;
1165
1166 kfree(lpm_priv->tb_cache_internal);
1167 lpm_priv->tb_cache_internal = NULL;
1168
1169 atomic_dec(&lpm_priv->open);
1170 return 0;
1171 }
1172 EXPORT_SYMBOL_GPL(ps3_lpm_close);
1173
1174 static int ps3_lpm_probe(struct ps3_system_bus_device *dev)
1175 {
1176 dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__);
1177
1178 if (lpm_priv) {
1179 dev_info(&dev->core, "%s:%u: called twice\n",
1180 __func__, __LINE__);
1181 return -EBUSY;
1182 }
1183
1184 lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL);
1185
1186 if (!lpm_priv)
1187 return -ENOMEM;
1188
1189 lpm_priv->sbd = dev;
1190 lpm_priv->node_id = dev->lpm.node_id;
1191 lpm_priv->pu_id = dev->lpm.pu_id;
1192 lpm_priv->rights = dev->lpm.rights;
1193
1194 dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
1195
1196 return 0;
1197 }
1198
1199 static void ps3_lpm_remove(struct ps3_system_bus_device *dev)
1200 {
1201 dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__);
1202
1203 ps3_lpm_close();
1204
1205 kfree(lpm_priv);
1206 lpm_priv = NULL;
1207
1208 dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
1209 }
1210
1211 static struct ps3_system_bus_driver ps3_lpm_driver = {
1212 .match_id = PS3_MATCH_ID_LPM,
1213 .core.name = "ps3-lpm",
1214 .core.owner = THIS_MODULE,
1215 .probe = ps3_lpm_probe,
1216 .remove = ps3_lpm_remove,
1217 .shutdown = ps3_lpm_remove,
1218 };
1219
1220 static int __init ps3_lpm_init(void)
1221 {
1222 pr_debug("%s:%d:\n", __func__, __LINE__);
1223 return ps3_system_bus_driver_register(&ps3_lpm_driver);
1224 }
1225
1226 static void __exit ps3_lpm_exit(void)
1227 {
1228 pr_debug("%s:%d:\n", __func__, __LINE__);
1229 ps3_system_bus_driver_unregister(&ps3_lpm_driver);
1230 }
1231
1232 module_init(ps3_lpm_init);
1233 module_exit(ps3_lpm_exit);
1234
1235 MODULE_LICENSE("GPL v2");
1236 MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver");
1237 MODULE_AUTHOR("Sony Corporation");
1238 MODULE_ALIAS(PS3_MODULE_ALIAS_LPM);