1239 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 Logical Performance Monitor.4 *5 * Copyright (C) 2007 Sony Computer Entertainment Inc.6 * Copyright 2007 Sony Corp.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/* BOOKMARK tag macros */22#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL23#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL24#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL25#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL26#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL27#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL28 29/* CBE PM CONTROL register macros */30#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x0000100031#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x0000080032#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C000033#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x0008000034#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL35 36/* CBE PM START STOP register macros */37#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x0200000038#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x0100000039#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x0002000040#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x0001000041#define PS3_PM_START_STOP_START_MASK 0xFF00000042#define PS3_PM_START_STOP_STOP_MASK 0x00FF000043 44/* CBE PM COUNTER register macres */45#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL46#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL47 48/* BASE SIGNAL GROUP NUMBER macros */49#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 050#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 651#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 752#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 753#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 1554#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 1755#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 1856#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 1857#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 2458#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 4959#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 5260#define PM_SIG_GROUP_SPU 4161#define PM_SIG_GROUP_SPU_TRIGGER 4262#define PM_SIG_GROUP_SPU_EVENT 4363#define PM_SIG_GROUP_MFC_MAX 6064 65/**66 * struct ps3_lpm_shadow_regs - Performance monitor shadow registers.67 *68 * @pm_control: Shadow of the processor's pm_control register.69 * @pm_start_stop: Shadow of the processor's pm_start_stop register.70 * @group_control: Shadow of the processor's group_control register.71 * @debug_bus_control: Shadow of the processor's debug_bus_control register.72 *73 * The logical performance monitor provides a write-only interface to74 * these processor registers. These shadow variables cache the processor75 * register values for reading.76 *77 * The initial value of the shadow registers at lpm creation is78 * PS3_LPM_SHADOW_REG_INIT.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 0xFFFFFFFF00000000ULL89 90/**91 * struct ps3_lpm_priv - Private lpm device data.92 *93 * @open: An atomic variable indicating the lpm driver has been opened.94 * @rights: The lpm rigths granted by the system policy module. A logical95 * OR of enum ps3_lpm_rights.96 * @node_id: The node id of a BE processor whose performance monitor this97 * lpar has the right to use.98 * @pu_id: The lv1 id of the logical PU.99 * @lpm_id: The lv1 id of this lpm instance.100 * @outlet_id: The outlet created by lv1 for this lpm instance.101 * @tb_count: The number of bytes of data held in the lv1 trace buffer.102 * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer.103 * Must be 128 byte aligned.104 * @tb_cache_size: Size of the kernel @tb_cache buffer. Must be 128 byte105 * aligned.106 * @tb_cache_internal: An unaligned buffer allocated by this driver to be107 * used for the trace buffer cache when ps3_lpm_open() is called with a108 * NULL tb_cache argument. Otherwise unused.109 * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs.110 * @sbd: The struct ps3_system_bus_device attached to this driver.111 *112 * The trace buffer is a buffer allocated and used internally to the lv1113 * hypervisor to collect trace data. The trace buffer cache is a guest114 * buffer that accepts the trace data from the trace buffer.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 * lpm_priv - Static instance of the lpm data.138 *139 * Since the exported routines don't support the notion of a device140 * instance we need to hold the instance in this static variable141 * and then only allow at most one instance at a time to be created.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 * use_start_stop_bookmark - Enable the PPU bookmark trace.154 *155 * And it enables PPU bookmark triggers ONLY if the other triggers are not set.156 * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm()157 * to start/stop LPM.158 *159 * Used to get good quality of the performance counter.160 */161 162enum {use_start_stop_bookmark = 1,};163 164void ps3_set_bookmark(u64 bookmark)165{166 /*167 * As per the PPE book IV, to avoid bookmark loss there must168 * not be a traced branch within 10 cycles of setting the169 * SPRN_BKMK register. The actual text is unclear if 'within'170 * includes cycles before the call.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 * ps3_read_phys_ctr - Read physical counter registers.193 *194 * Each physical counter can act as one 32 bit counter or as two 16 bit195 * counters.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 * ps3_write_phys_ctr - Write physical counter registers.237 *238 * Each physical counter can act as one 32 bit counter or as two 16 bit239 * counters.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 * ps3_read_ctr - Read counter.298 *299 * Read 16 or 32 bits depending on the current size of the counter.300 * Counters 4, 5, 6 & 7 are always 16 bit.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 * ps3_write_ctr - Write counter.319 *320 * Write 16 or 32 bits depending on the current size of the counter.321 * Counters 4, 5, 6 & 7 are always 16 bit.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 else337 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 * ps3_read_pm07_control - Read counter control registers.346 *347 * Each logical counter has a corresponding control register.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 * ps3_write_pm07_control - Write counter control registers.358 *359 * Each logical counter has a corresponding control register.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 * ps3_read_pm - Read Other LPM control registers.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 * ps3_write_pm - Write Other LPM control registers.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 * ps3_get_ctr_size - Get the size of a physical counter.505 *506 * Returns either 16 or 32.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 * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits.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 else567 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 else646 return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup647 + 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 * 0: physical object.796 * 1: logical object.797 * This parameter is only used for the PPE and SPE signals.798 */799 attr1 = 1;800 801 /*802 * This parameter is used to specify the target physical/logical803 * PPE/SPE object.804 */805 if (PM_SIG_GROUP_SPU <= signal_group &&806 signal_group < PM_SIG_GROUP_MFC_MAX)807 attr2 = sub_unit;808 else809 attr2 = lpm_priv->pu_id;810 811 /*812 * This parameter is only used for setting the SPE signal.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 * ps3_enable_pm - Enable the entire performance monitoring unit.834 *835 * When we enable the LPM, all pending writes to counters get committed.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_MASK849 | 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 * ps3_disable_pm - Disable the entire performance monitoring unit.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 * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer.907 * @offset: Offset in bytes from the start of the trace buffer.908 * @buf: Copy destination.909 * @count: Maximum count of bytes to copy.910 * @bytes_copied: Pointer to a variable that will receive the number of911 * bytes copied to @buf.912 *913 * On error @buf will contain any successfully copied trace buffer data914 * and bytes_copied will be set to the number of bytes successfully copied.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 * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer.962 * @offset: Offset in bytes from the start of the trace buffer.963 * @buf: A __user copy destination.964 * @count: Maximum count of bytes to copy.965 * @bytes_copied: Pointer to a variable that will receive the number of966 * bytes copied to @buf.967 *968 * On error @buf will contain any successfully copied trace buffer data969 * and bytes_copied will be set to the number of bytes successfully copied.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 * ps3_get_and_clear_pm_interrupts -1025 *1026 * Clearing interrupts for the entire performance monitoring unit.1027 * Reading pm_status clears the interrupt bits.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 * ps3_enable_pm_interrupts -1038 *1039 * Enabling interrupts for the entire performance monitoring unit.1040 * Enables the interrupt bits in the pm_status register.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 * ps3_enable_pm_interrupts -1052 *1053 * Disabling interrupts for the entire performance monitoring unit.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 * ps3_lpm_open - Open the logical performance monitor device.1065 * @tb_type: Specifies the type of trace buffer lv1 should use for this lpm1066 * instance, specified by one of enum ps3_lpm_tb_type.1067 * @tb_cache: Optional user supplied buffer to use as the trace buffer cache.1068 * If NULL, the driver will allocate and manage an internal buffer.1069 * Unused when @tb_type is PS3_LPM_TB_TYPE_NONE.1070 * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer.1071 * Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE.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_NONE1082 && 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 /* Note tb_cache needs 128 byte alignment. */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 1144fail_construct:1145 kfree(lpm_priv->tb_cache_internal);1146 lpm_priv->tb_cache_internal = NULL;1147fail_malloc:1148fail_align:1149 atomic_dec(&lpm_priv->open);1150 return result;1151}1152EXPORT_SYMBOL_GPL(ps3_lpm_open);1153 1154/**1155 * ps3_lpm_close - Close the lpm device.1156 *1157 */1158 1159int 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}1172EXPORT_SYMBOL_GPL(ps3_lpm_close);1173 1174static 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 1199static 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 1211static 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 1220static 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 1226static 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 1232module_init(ps3_lpm_init);1233module_exit(ps3_lpm_exit);1234 1235MODULE_LICENSE("GPL v2");1236MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver");1237MODULE_AUTHOR("Sony Corporation");1238MODULE_ALIAS(PS3_MODULE_ALIAS_LPM);1239