Commit 781749a46b34dc5c9e4000df7b9d37d675c17463

Authored by Takashi Yamamoto
Committed by Paul Mackerras
1 parent ed7570022a

[POWERPC] PS3: Add logical performance monitor driver support

Add PS3 logical performance monitor (lpm) device driver.

The PS3's LV1 hypervisor provides a Logical Performance Monitor that
abstracts the Cell processor's performance monitor features for use
by guest operating systems.

Signed-off-by: Takashi Yamamoto <TakashiA.Yamamoto@jp.sony.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>

Showing 4 changed files with 1324 additions and 0 deletions Side-by-side Diff

arch/powerpc/platforms/ps3/Kconfig
... ... @@ -138,5 +138,18 @@
138 138 be disabled on the kernel command line using "ps3flash=off", to
139 139 not allocate this fixed buffer.
140 140  
  141 +config PS3_LPM
  142 + tristate "PS3 Logical Performance Monitor support"
  143 + depends on PPC_PS3
  144 + help
  145 + Include support for the PS3 Logical Performance Monitor.
  146 +
  147 + This support is required to use the logical performance monitor
  148 + of the PS3's LV1 hypervisor.
  149 +
  150 + If you intend to use the advanced performance monitoring and
  151 + profiling support of the Cell processor with programs like
  152 + oprofile and perfmon2, then say Y or M, otherwise say N.
  153 +
141 154 endmenu
drivers/ps3/Makefile
... ... @@ -4,4 +4,5 @@
4 4 obj-$(CONFIG_PPC_PS3) += sys-manager-core.o
5 5 obj-$(CONFIG_PS3_SYS_MANAGER) += ps3-sys-manager.o
6 6 obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o
  7 +obj-$(CONFIG_PS3_LPM) += ps3-lpm.o
drivers/ps3/ps3-lpm.c
Changes suppressed. Click to show
  1 +/*
  2 + * PS3 Logical Performance Monitor.
  3 + *
  4 + * Copyright (C) 2007 Sony Computer Entertainment Inc.
  5 + * Copyright 2007 Sony Corp.
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; version 2 of the License.
  10 + *
  11 + * This program is distributed in the hope that it will be useful,
  12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 + * GNU General Public License for more details.
  15 + *
  16 + * You should have received a copy of the GNU General Public License
  17 + * along with this program; if not, write to the Free Software
  18 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  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/ps3.h>
  26 +#include <asm/lv1call.h>
  27 +#include <asm/cell-pmu.h>
  28 +
  29 +
  30 +/* BOOKMARK tag macros */
  31 +#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL
  32 +#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL
  33 +#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL
  34 +#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL
  35 +#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL
  36 +#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL
  37 +
  38 +/* CBE PM CONTROL register macros */
  39 +#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000
  40 +#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800
  41 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000
  42 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000
  43 +#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL
  44 +
  45 +/* CBE PM START STOP register macros */
  46 +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000
  47 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000
  48 +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000
  49 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000
  50 +#define PS3_PM_START_STOP_START_MASK 0xFF000000
  51 +#define PS3_PM_START_STOP_STOP_MASK 0x00FF0000
  52 +
  53 +/* CBE PM COUNTER register macres */
  54 +#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL
  55 +#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL
  56 +
  57 +/* BASE SIGNAL GROUP NUMBER macros */
  58 +#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0
  59 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6
  60 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7
  61 +#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7
  62 +#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15
  63 +#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17
  64 +#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18
  65 +#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18
  66 +#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24
  67 +#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49
  68 +#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52
  69 +#define PM_SIG_GROUP_SPU 41
  70 +#define PM_SIG_GROUP_SPU_TRIGGER 42
  71 +#define PM_SIG_GROUP_SPU_EVENT 43
  72 +#define PM_SIG_GROUP_MFC_MAX 60
  73 +
  74 +/**
  75 + * struct ps3_lpm_shadow_regs - Performance monitor shadow registers.
  76 + *
  77 + * @pm_control: Shadow of the processor's pm_control register.
  78 + * @pm_start_stop: Shadow of the processor's pm_start_stop register.
  79 + * @pm_interval: Shadow of the processor's pm_interval register.
  80 + * @group_control: Shadow of the processor's group_control register.
  81 + * @debug_bus_control: Shadow of the processor's debug_bus_control register.
  82 + *
  83 + * The logical performance monitor provides a write-only interface to
  84 + * these processor registers. These shadow variables cache the processor
  85 + * register values for reading.
  86 + *
  87 + * The initial value of the shadow registers at lpm creation is
  88 + * PS3_LPM_SHADOW_REG_INIT.
  89 + */
  90 +
  91 +struct ps3_lpm_shadow_regs {
  92 + u64 pm_control;
  93 + u64 pm_start_stop;
  94 + u64 pm_interval;
  95 + u64 group_control;
  96 + u64 debug_bus_control;
  97 +};
  98 +
  99 +#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL
  100 +
  101 +/**
  102 + * struct ps3_lpm_priv - Private lpm device data.
  103 + *
  104 + * @open: An atomic variable indicating the lpm driver has been opened.
  105 + * @rights: The lpm rigths granted by the system policy module. A logical
  106 + * OR of enum ps3_lpm_rights.
  107 + * @node_id: The node id of a BE prosessor whose performance monitor this
  108 + * lpar has the right to use.
  109 + * @pu_id: The lv1 id of the logical PU.
  110 + * @lpm_id: The lv1 id of this lpm instance.
  111 + * @outlet_id: The outlet created by lv1 for this lpm instance.
  112 + * @tb_count: The number of bytes of data held in the lv1 trace buffer.
  113 + * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer.
  114 + * Must be 128 byte aligned.
  115 + * @tb_cache_size: Size of the kernel @tb_cache buffer. Must be 128 byte
  116 + * aligned.
  117 + * @tb_cache_internal: An unaligned buffer allocated by this driver to be
  118 + * used for the trace buffer cache when ps3_lpm_open() is called with a
  119 + * NULL tb_cache argument. Otherwise unused.
  120 + * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs.
  121 + * @sbd: The struct ps3_system_bus_device attached to this driver.
  122 + *
  123 + * The trace buffer is a buffer allocated and used internally to the lv1
  124 + * hypervisor to collect trace data. The trace buffer cache is a guest
  125 + * buffer that accepts the trace data from the trace buffer.
  126 + */
  127 +
  128 +struct 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 +
  143 +enum {
  144 + PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000,
  145 +};
  146 +
  147 +/**
  148 + * lpm_priv - Static instance of the lpm data.
  149 + *
  150 + * Since the exported routines don't support the notion of a device
  151 + * instance we need to hold the instance in this static variable
  152 + * and then only allow at most one instance at a time to be created.
  153 + */
  154 +
  155 +static struct ps3_lpm_priv *lpm_priv;
  156 +
  157 +static struct device *sbd_core(void)
  158 +{
  159 + BUG_ON(!lpm_priv || !lpm_priv->sbd);
  160 + return &lpm_priv->sbd->core;
  161 +}
  162 +
  163 +/**
  164 + * use_start_stop_bookmark - Enable the PPU bookmark trace.
  165 + *
  166 + * And it enables PPU bookmark triggers ONLY if the other triggers are not set.
  167 + * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm()
  168 + * to start/stop LPM.
  169 + *
  170 + * Used to get good quality of the performance counter.
  171 + */
  172 +
  173 +enum {use_start_stop_bookmark = 1,};
  174 +
  175 +void ps3_set_bookmark(u64 bookmark)
  176 +{
  177 + /*
  178 + * As per the PPE book IV, to avoid bookmark loss there must
  179 + * not be a traced branch within 10 cycles of setting the
  180 + * SPRN_BKMK register. The actual text is unclear if 'within'
  181 + * includes cycles before the call.
  182 + */
  183 +
  184 + asm volatile("or 29, 29, 29;"); /* db10cyc */
  185 + mtspr(SPRN_BKMK, bookmark);
  186 + asm volatile("or 29, 29, 29;"); /* db10cyc */
  187 +}
  188 +EXPORT_SYMBOL_GPL(ps3_set_bookmark);
  189 +
  190 +void 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 +}
  200 +EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark);
  201 +
  202 +/**
  203 + * ps3_read_phys_ctr - Read physical counter registers.
  204 + *
  205 + * Each physical counter can act as one 32 bit counter or as two 16 bit
  206 + * counters.
  207 + */
  208 +
  209 +u32 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 +}
  244 +EXPORT_SYMBOL_GPL(ps3_read_phys_ctr);
  245 +
  246 +/**
  247 + * ps3_write_phys_ctr - Write physical counter registers.
  248 + *
  249 + * Each physical counter can act as one 32 bit counter or as two 16 bit
  250 + * counters.
  251 + */
  252 +
  253 +void 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 +}
  305 +EXPORT_SYMBOL_GPL(ps3_write_phys_ctr);
  306 +
  307 +/**
  308 + * ps3_read_ctr - Read counter.
  309 + *
  310 + * Read 16 or 32 bits depending on the current size of the counter.
  311 + * Counters 4, 5, 6 & 7 are always 16 bit.
  312 + */
  313 +
  314 +u32 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 +}
  326 +EXPORT_SYMBOL_GPL(ps3_read_ctr);
  327 +
  328 +/**
  329 + * ps3_write_ctr - Write counter.
  330 + *
  331 + * Write 16 or 32 bits depending on the current size of the counter.
  332 + * Counters 4, 5, 6 & 7 are always 16 bit.
  333 + */
  334 +
  335 +void 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 +}
  353 +EXPORT_SYMBOL_GPL(ps3_write_ctr);
  354 +
  355 +/**
  356 + * ps3_read_pm07_control - Read counter control registers.
  357 + *
  358 + * Each logical counter has a corresponding control register.
  359 + */
  360 +
  361 +u32 ps3_read_pm07_control(u32 cpu, u32 ctr)
  362 +{
  363 + return 0;
  364 +}
  365 +EXPORT_SYMBOL_GPL(ps3_read_pm07_control);
  366 +
  367 +/**
  368 + * ps3_write_pm07_control - Write counter control registers.
  369 + *
  370 + * Each logical counter has a corresponding control register.
  371 + */
  372 +
  373 +void 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 +}
  392 +EXPORT_SYMBOL_GPL(ps3_write_pm07_control);
  393 +
  394 +/**
  395 + * ps3_read_pm - Read Other LPM control registers.
  396 + */
  397 +
  398 +u32 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 + return lpm_priv->shadow.pm_interval;
  412 + case group_control:
  413 + return lpm_priv->shadow.group_control;
  414 + case debug_bus_control:
  415 + return lpm_priv->shadow.debug_bus_control;
  416 + case pm_status:
  417 + result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id,
  418 + &val);
  419 + if (result) {
  420 + val = 0;
  421 + dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: "
  422 + "reg %u, %s\n", __func__, __LINE__, reg,
  423 + ps3_result(result));
  424 + }
  425 + return (u32)val;
  426 + case ext_tr_timer:
  427 + return 0;
  428 + default:
  429 + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
  430 + __LINE__, reg);
  431 + BUG();
  432 + break;
  433 + }
  434 +
  435 + return 0;
  436 +}
  437 +EXPORT_SYMBOL_GPL(ps3_read_pm);
  438 +
  439 +/**
  440 + * ps3_write_pm - Write Other LPM control registers.
  441 + */
  442 +
  443 +void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)
  444 +{
  445 + int result = 0;
  446 + u64 dummy;
  447 +
  448 + switch (reg) {
  449 + case group_control:
  450 + if (val != lpm_priv->shadow.group_control)
  451 + result = lv1_set_lpm_group_control(lpm_priv->lpm_id,
  452 + val,
  453 + PS3_WRITE_PM_MASK,
  454 + &dummy);
  455 + lpm_priv->shadow.group_control = val;
  456 + break;
  457 + case debug_bus_control:
  458 + if (val != lpm_priv->shadow.debug_bus_control)
  459 + result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id,
  460 + val,
  461 + PS3_WRITE_PM_MASK,
  462 + &dummy);
  463 + lpm_priv->shadow.debug_bus_control = val;
  464 + break;
  465 + case pm_control:
  466 + if (use_start_stop_bookmark)
  467 + val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK |
  468 + PS3_PM_CONTROL_PPU_TH1_BOOKMARK);
  469 + if (val != lpm_priv->shadow.pm_control)
  470 + result = lv1_set_lpm_general_control(lpm_priv->lpm_id,
  471 + val,
  472 + PS3_WRITE_PM_MASK,
  473 + 0, 0, &dummy,
  474 + &dummy);
  475 + lpm_priv->shadow.pm_control = val;
  476 + break;
  477 + case pm_interval:
  478 + if (val != lpm_priv->shadow.pm_interval)
  479 + result = lv1_set_lpm_interval(lpm_priv->lpm_id, val,
  480 + PS3_WRITE_PM_MASK, &dummy);
  481 + lpm_priv->shadow.pm_interval = val;
  482 + break;
  483 + case pm_start_stop:
  484 + if (val != lpm_priv->shadow.pm_start_stop)
  485 + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
  486 + val,
  487 + PS3_WRITE_PM_MASK,
  488 + &dummy);
  489 + lpm_priv->shadow.pm_start_stop = val;
  490 + break;
  491 + case trace_address:
  492 + case ext_tr_timer:
  493 + case pm_status:
  494 + break;
  495 + default:
  496 + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,
  497 + __LINE__, reg);
  498 + BUG();
  499 + break;
  500 + }
  501 +
  502 + if (result)
  503 + dev_err(sbd_core(), "%s:%u: lv1 set_control failed: "
  504 + "reg %u, %s\n", __func__, __LINE__, reg,
  505 + ps3_result(result));
  506 +}
  507 +EXPORT_SYMBOL_GPL(ps3_write_pm);
  508 +
  509 +/**
  510 + * ps3_get_ctr_size - Get the size of a physical counter.
  511 + *
  512 + * Returns either 16 or 32.
  513 + */
  514 +
  515 +u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr)
  516 +{
  517 + u32 pm_ctrl;
  518 +
  519 + if (phys_ctr >= NR_PHYS_CTRS) {
  520 + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
  521 + __LINE__, phys_ctr);
  522 + return 0;
  523 + }
  524 +
  525 + pm_ctrl = ps3_read_pm(cpu, pm_control);
  526 + return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32;
  527 +}
  528 +EXPORT_SYMBOL_GPL(ps3_get_ctr_size);
  529 +
  530 +/**
  531 + * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits.
  532 + */
  533 +
  534 +void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)
  535 +{
  536 + u32 pm_ctrl;
  537 +
  538 + if (phys_ctr >= NR_PHYS_CTRS) {
  539 + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,
  540 + __LINE__, phys_ctr);
  541 + return;
  542 + }
  543 +
  544 + pm_ctrl = ps3_read_pm(cpu, pm_control);
  545 +
  546 + switch (ctr_size) {
  547 + case 16:
  548 + pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr);
  549 + ps3_write_pm(cpu, pm_control, pm_ctrl);
  550 + break;
  551 +
  552 + case 32:
  553 + pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr);
  554 + ps3_write_pm(cpu, pm_control, pm_ctrl);
  555 + break;
  556 + default:
  557 + BUG();
  558 + }
  559 +}
  560 +EXPORT_SYMBOL_GPL(ps3_set_ctr_size);
  561 +
  562 +static u64 pm_translate_signal_group_number_on_island2(u64 subgroup)
  563 +{
  564 +
  565 + if (subgroup == 2)
  566 + subgroup = 3;
  567 +
  568 + if (subgroup <= 6)
  569 + return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  570 + else if (subgroup == 7)
  571 + return PM_ISLAND2_SIGNAL_GROUP_NUMBER1;
  572 + else
  573 + return PM_ISLAND2_SIGNAL_GROUP_NUMBER2;
  574 +}
  575 +
  576 +static u64 pm_translate_signal_group_number_on_island3(u64 subgroup)
  577 +{
  578 +
  579 + switch (subgroup) {
  580 + case 2:
  581 + case 3:
  582 + case 4:
  583 + subgroup += 2;
  584 + break;
  585 + case 5:
  586 + subgroup = 8;
  587 + break;
  588 + default:
  589 + break;
  590 + }
  591 + return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  592 +}
  593 +
  594 +static u64 pm_translate_signal_group_number_on_island4(u64 subgroup)
  595 +{
  596 + return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  597 +}
  598 +
  599 +static u64 pm_translate_signal_group_number_on_island5(u64 subgroup)
  600 +{
  601 +
  602 + switch (subgroup) {
  603 + case 3:
  604 + subgroup = 4;
  605 + break;
  606 + case 4:
  607 + subgroup = 6;
  608 + break;
  609 + default:
  610 + break;
  611 + }
  612 + return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  613 +}
  614 +
  615 +static u64 pm_translate_signal_group_number_on_island6(u64 subgroup,
  616 + u64 subsubgroup)
  617 +{
  618 + switch (subgroup) {
  619 + case 3:
  620 + case 4:
  621 + case 5:
  622 + subgroup += 1;
  623 + break;
  624 + default:
  625 + break;
  626 + }
  627 +
  628 + switch (subsubgroup) {
  629 + case 4:
  630 + case 5:
  631 + case 6:
  632 + subsubgroup += 2;
  633 + break;
  634 + case 7:
  635 + case 8:
  636 + case 9:
  637 + case 10:
  638 + subsubgroup += 4;
  639 + break;
  640 + case 11:
  641 + case 12:
  642 + case 13:
  643 + subsubgroup += 5;
  644 + break;
  645 + default:
  646 + break;
  647 + }
  648 +
  649 + if (subgroup <= 5)
  650 + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup);
  651 + else
  652 + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup
  653 + + subsubgroup - 1);
  654 +}
  655 +
  656 +static u64 pm_translate_signal_group_number_on_island7(u64 subgroup)
  657 +{
  658 + return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  659 +}
  660 +
  661 +static u64 pm_translate_signal_group_number_on_island8(u64 subgroup)
  662 +{
  663 + return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup;
  664 +}
  665 +
  666 +static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group)
  667 +{
  668 + u64 island;
  669 + u64 subgroup;
  670 + u64 subsubgroup;
  671 +
  672 + subgroup = 0;
  673 + subsubgroup = 0;
  674 + island = 0;
  675 + if (group < 1000) {
  676 + if (group < 100) {
  677 + if (20 <= group && group < 30) {
  678 + island = 2;
  679 + subgroup = group - 20;
  680 + } else if (30 <= group && group < 40) {
  681 + island = 3;
  682 + subgroup = group - 30;
  683 + } else if (40 <= group && group < 50) {
  684 + island = 4;
  685 + subgroup = group - 40;
  686 + } else if (50 <= group && group < 60) {
  687 + island = 5;
  688 + subgroup = group - 50;
  689 + } else if (60 <= group && group < 70) {
  690 + island = 6;
  691 + subgroup = group - 60;
  692 + } else if (70 <= group && group < 80) {
  693 + island = 7;
  694 + subgroup = group - 70;
  695 + } else if (80 <= group && group < 90) {
  696 + island = 8;
  697 + subgroup = group - 80;
  698 + }
  699 + } else if (200 <= group && group < 300) {
  700 + island = 2;
  701 + subgroup = group - 200;
  702 + } else if (600 <= group && group < 700) {
  703 + island = 6;
  704 + subgroup = 5;
  705 + subsubgroup = group - 650;
  706 + }
  707 + } else if (6000 <= group && group < 7000) {
  708 + island = 6;
  709 + subgroup = 5;
  710 + subsubgroup = group - 6500;
  711 + }
  712 +
  713 + switch (island) {
  714 + case 2:
  715 + return pm_translate_signal_group_number_on_island2(subgroup);
  716 + case 3:
  717 + return pm_translate_signal_group_number_on_island3(subgroup);
  718 + case 4:
  719 + return pm_translate_signal_group_number_on_island4(subgroup);
  720 + case 5:
  721 + return pm_translate_signal_group_number_on_island5(subgroup);
  722 + case 6:
  723 + return pm_translate_signal_group_number_on_island6(subgroup,
  724 + subsubgroup);
  725 + case 7:
  726 + return pm_translate_signal_group_number_on_island7(subgroup);
  727 + case 8:
  728 + return pm_translate_signal_group_number_on_island8(subgroup);
  729 + default:
  730 + dev_dbg(sbd_core(), "%s:%u: island not found: %lu\n", __func__,
  731 + __LINE__, group);
  732 + BUG();
  733 + break;
  734 + }
  735 + return 0;
  736 +}
  737 +
  738 +static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word)
  739 +{
  740 +
  741 + switch (word) {
  742 + case 1:
  743 + return 0xF000;
  744 + case 2:
  745 + return 0x0F00;
  746 + case 4:
  747 + return 0x00F0;
  748 + case 8:
  749 + default:
  750 + return 0x000F;
  751 + }
  752 +}
  753 +
  754 +static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select,
  755 + u64 signal_select, u64 attr1, u64 attr2, u64 attr3)
  756 +{
  757 + int ret;
  758 +
  759 + ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select,
  760 + signal_select, attr1, attr2, attr3);
  761 + if (ret)
  762 + dev_err(sbd_core(),
  763 + "%s:%u: error:%d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
  764 + __func__, __LINE__, ret, lv1_signal_group, bus_select,
  765 + signal_select, attr1, attr2, attr3);
  766 +
  767 + return ret;
  768 +}
  769 +
  770 +int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit,
  771 + u8 bus_word)
  772 +{
  773 + int ret;
  774 + u64 lv1_signal_group;
  775 + u64 bus_select;
  776 + u64 signal_select;
  777 + u64 attr1, attr2, attr3;
  778 +
  779 + if (signal_group == 0)
  780 + return __ps3_set_signal(0, 0, 0, 0, 0, 0);
  781 +
  782 + lv1_signal_group =
  783 + pm_signal_group_to_ps3_lv1_signal_group(signal_group);
  784 + bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word);
  785 +
  786 + switch (signal_group) {
  787 + case PM_SIG_GROUP_SPU_TRIGGER:
  788 + signal_select = 1;
  789 + signal_select = signal_select << (63 - signal_bit);
  790 + break;
  791 + case PM_SIG_GROUP_SPU_EVENT:
  792 + signal_select = 1;
  793 + signal_select = (signal_select << (63 - signal_bit)) | 0x3;
  794 + break;
  795 + default:
  796 + signal_select = 0;
  797 + break;
  798 + }
  799 +
  800 + /*
  801 + * 0: physical object.
  802 + * 1: logical object.
  803 + * This parameter is only used for the PPE and SPE signals.
  804 + */
  805 + attr1 = 1;
  806 +
  807 + /*
  808 + * This parameter is used to specify the target physical/logical
  809 + * PPE/SPE object.
  810 + */
  811 + if (PM_SIG_GROUP_SPU <= signal_group &&
  812 + signal_group < PM_SIG_GROUP_MFC_MAX)
  813 + attr2 = sub_unit;
  814 + else
  815 + attr2 = lpm_priv->pu_id;
  816 +
  817 + /*
  818 + * This parameter is only used for setting the SPE signal.
  819 + */
  820 + attr3 = 0;
  821 +
  822 + ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select,
  823 + attr1, attr2, attr3);
  824 + if (ret)
  825 + dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n",
  826 + __func__, __LINE__, ret);
  827 +
  828 + return ret;
  829 +}
  830 +EXPORT_SYMBOL_GPL(ps3_set_signal);
  831 +
  832 +u32 ps3_get_hw_thread_id(int cpu)
  833 +{
  834 + return get_hard_smp_processor_id(cpu);
  835 +}
  836 +EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id);
  837 +
  838 +/**
  839 + * ps3_enable_pm - Enable the entire performance monitoring unit.
  840 + *
  841 + * When we enable the LPM, all pending writes to counters get committed.
  842 + */
  843 +
  844 +void ps3_enable_pm(u32 cpu)
  845 +{
  846 + int result;
  847 + u64 tmp;
  848 + int insert_bookmark = 0;
  849 +
  850 + lpm_priv->tb_count = 0;
  851 +
  852 + if (use_start_stop_bookmark) {
  853 + if (!(lpm_priv->shadow.pm_start_stop &
  854 + (PS3_PM_START_STOP_START_MASK
  855 + | PS3_PM_START_STOP_STOP_MASK))) {
  856 + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,
  857 + (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START |
  858 + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START |
  859 + PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP |
  860 + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP),
  861 + 0xFFFFFFFFFFFFFFFFULL, &tmp);
  862 +
  863 + if (result)
  864 + dev_err(sbd_core(), "%s:%u: "
  865 + "lv1_set_lpm_trigger_control failed: "
  866 + "%s\n", __func__, __LINE__,
  867 + ps3_result(result));
  868 +
  869 + insert_bookmark = !result;
  870 + }
  871 + }
  872 +
  873 + result = lv1_start_lpm(lpm_priv->lpm_id);
  874 +
  875 + if (result)
  876 + dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n",
  877 + __func__, __LINE__, ps3_result(result));
  878 +
  879 + if (use_start_stop_bookmark && !result && insert_bookmark)
  880 + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START);
  881 +}
  882 +EXPORT_SYMBOL_GPL(ps3_enable_pm);
  883 +
  884 +/**
  885 + * ps3_disable_pm - Disable the entire performance monitoring unit.
  886 + */
  887 +
  888 +void ps3_disable_pm(u32 cpu)
  889 +{
  890 + int result;
  891 + u64 tmp;
  892 +
  893 + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP);
  894 +
  895 + result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp);
  896 +
  897 + if (result) {
  898 + if(result != LV1_WRONG_STATE)
  899 + dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n",
  900 + __func__, __LINE__, ps3_result(result));
  901 + return;
  902 + }
  903 +
  904 + lpm_priv->tb_count = tmp;
  905 +
  906 + dev_dbg(sbd_core(), "%s:%u: tb_count %lu (%lxh)\n", __func__, __LINE__,
  907 + lpm_priv->tb_count, lpm_priv->tb_count);
  908 +}
  909 +EXPORT_SYMBOL_GPL(ps3_disable_pm);
  910 +
  911 +/**
  912 + * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer.
  913 + * @offset: Offset in bytes from the start of the trace buffer.
  914 + * @buf: Copy destination.
  915 + * @count: Maximum count of bytes to copy.
  916 + * @bytes_copied: Pointer to a variable that will recieve the number of
  917 + * bytes copied to @buf.
  918 + *
  919 + * On error @buf will contain any successfully copied trace buffer data
  920 + * and bytes_copied will be set to the number of bytes successfully copied.
  921 + */
  922 +
  923 +int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count,
  924 + unsigned long *bytes_copied)
  925 +{
  926 + int result;
  927 +
  928 + *bytes_copied = 0;
  929 +
  930 + if (!lpm_priv->tb_cache)
  931 + return -EPERM;
  932 +
  933 + if (offset >= lpm_priv->tb_count)
  934 + return 0;
  935 +
  936 + count = min(count, lpm_priv->tb_count - offset);
  937 +
  938 + while (*bytes_copied < count) {
  939 + const unsigned long request = count - *bytes_copied;
  940 + u64 tmp;
  941 +
  942 + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
  943 + request, &tmp);
  944 + if (result) {
  945 + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
  946 + __func__, __LINE__, request, offset);
  947 +
  948 + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
  949 + "failed: %s\n", __func__, __LINE__,
  950 + ps3_result(result));
  951 + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
  952 + }
  953 +
  954 + memcpy(buf, lpm_priv->tb_cache, tmp);
  955 + buf += tmp;
  956 + *bytes_copied += tmp;
  957 + offset += tmp;
  958 + }
  959 + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,
  960 + *bytes_copied);
  961 +
  962 + return 0;
  963 +}
  964 +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb);
  965 +
  966 +/**
  967 + * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer.
  968 + * @offset: Offset in bytes from the start of the trace buffer.
  969 + * @buf: A __user copy destination.
  970 + * @count: Maximum count of bytes to copy.
  971 + * @bytes_copied: Pointer to a variable that will recieve the number of
  972 + * bytes copied to @buf.
  973 + *
  974 + * On error @buf will contain any successfully copied trace buffer data
  975 + * and bytes_copied will be set to the number of bytes successfully copied.
  976 + */
  977 +
  978 +int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,
  979 + unsigned long count, unsigned long *bytes_copied)
  980 +{
  981 + int result;
  982 +
  983 + *bytes_copied = 0;
  984 +
  985 + if (!lpm_priv->tb_cache)
  986 + return -EPERM;
  987 +
  988 + if (offset >= lpm_priv->tb_count)
  989 + return 0;
  990 +
  991 + count = min(count, lpm_priv->tb_count - offset);
  992 +
  993 + while (*bytes_copied < count) {
  994 + const unsigned long request = count - *bytes_copied;
  995 + u64 tmp;
  996 +
  997 + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,
  998 + request, &tmp);
  999 + if (result) {
  1000 + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",
  1001 + __func__, __LINE__, request, offset);
  1002 + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "
  1003 + "failed: %s\n", __func__, __LINE__,
  1004 + ps3_result(result));
  1005 + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
  1006 + }
  1007 +
  1008 + result = copy_to_user(buf, lpm_priv->tb_cache, tmp);
  1009 +
  1010 + if (result) {
  1011 + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%p\n",
  1012 + __func__, __LINE__, tmp, buf);
  1013 + dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n",
  1014 + __func__, __LINE__, result);
  1015 + return -EFAULT;
  1016 + }
  1017 +
  1018 + buf += tmp;
  1019 + *bytes_copied += tmp;
  1020 + offset += tmp;
  1021 + }
  1022 + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,
  1023 + *bytes_copied);
  1024 +
  1025 + return 0;
  1026 +}
  1027 +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user);
  1028 +
  1029 +/**
  1030 + * ps3_get_and_clear_pm_interrupts -
  1031 + *
  1032 + * Clearing interrupts for the entire performance monitoring unit.
  1033 + * Reading pm_status clears the interrupt bits.
  1034 + */
  1035 +
  1036 +u32 ps3_get_and_clear_pm_interrupts(u32 cpu)
  1037 +{
  1038 + return ps3_read_pm(cpu, pm_status);
  1039 +}
  1040 +EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts);
  1041 +
  1042 +/**
  1043 + * ps3_enable_pm_interrupts -
  1044 + *
  1045 + * Enabling interrupts for the entire performance monitoring unit.
  1046 + * Enables the interrupt bits in the pm_status register.
  1047 + */
  1048 +
  1049 +void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)
  1050 +{
  1051 + if (mask)
  1052 + ps3_write_pm(cpu, pm_status, mask);
  1053 +}
  1054 +EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts);
  1055 +
  1056 +/**
  1057 + * ps3_enable_pm_interrupts -
  1058 + *
  1059 + * Disabling interrupts for the entire performance monitoring unit.
  1060 + */
  1061 +
  1062 +void ps3_disable_pm_interrupts(u32 cpu)
  1063 +{
  1064 + ps3_get_and_clear_pm_interrupts(cpu);
  1065 + ps3_write_pm(cpu, pm_status, 0);
  1066 +}
  1067 +EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts);
  1068 +
  1069 +/**
  1070 + * ps3_lpm_open - Open the logical performance monitor device.
  1071 + * @tb_type: Specifies the type of trace buffer lv1 sould use for this lpm
  1072 + * instance, specified by one of enum ps3_lpm_tb_type.
  1073 + * @tb_cache: Optional user supplied buffer to use as the trace buffer cache.
  1074 + * If NULL, the driver will allocate and manage an internal buffer.
  1075 + * Unused when when @tb_type is PS3_LPM_TB_TYPE_NONE.
  1076 + * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer.
  1077 + * Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE.
  1078 + */
  1079 +
  1080 +int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache,
  1081 + u64 tb_cache_size)
  1082 +{
  1083 + int result;
  1084 + u64 tb_size;
  1085 +
  1086 + BUG_ON(!lpm_priv);
  1087 + BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE
  1088 + && tb_type != PS3_LPM_TB_TYPE_INTERNAL);
  1089 +
  1090 + if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache)
  1091 + dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__);
  1092 +
  1093 + if (!atomic_add_unless(&lpm_priv->open, 1, 1)) {
  1094 + dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__);
  1095 + return -EBUSY;
  1096 + }
  1097 +
  1098 + /* Note tb_cache needs 128 byte alignment. */
  1099 +
  1100 + if (tb_type == PS3_LPM_TB_TYPE_NONE) {
  1101 + lpm_priv->tb_cache_size = 0;
  1102 + lpm_priv->tb_cache_internal = NULL;
  1103 + lpm_priv->tb_cache = NULL;
  1104 + } else if (tb_cache) {
  1105 + if (tb_cache != (void *)_ALIGN_UP((unsigned long)tb_cache, 128)
  1106 + || tb_cache_size != _ALIGN_UP(tb_cache_size, 128)) {
  1107 + dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n",
  1108 + __func__, __LINE__);
  1109 + result = -EINVAL;
  1110 + goto fail_align;
  1111 + }
  1112 + lpm_priv->tb_cache_size = tb_cache_size;
  1113 + lpm_priv->tb_cache_internal = NULL;
  1114 + lpm_priv->tb_cache = tb_cache;
  1115 + } else {
  1116 + lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE;
  1117 + lpm_priv->tb_cache_internal = kzalloc(
  1118 + lpm_priv->tb_cache_size + 127, GFP_KERNEL);
  1119 + if (!lpm_priv->tb_cache_internal) {
  1120 + dev_err(sbd_core(), "%s:%u: alloc internal tb_cache "
  1121 + "failed\n", __func__, __LINE__);
  1122 + result = -ENOMEM;
  1123 + goto fail_malloc;
  1124 + }
  1125 + lpm_priv->tb_cache = (void *)_ALIGN_UP(
  1126 + (unsigned long)lpm_priv->tb_cache_internal, 128);
  1127 + }
  1128 +
  1129 + result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0,
  1130 + ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)),
  1131 + lpm_priv->tb_cache_size, &lpm_priv->lpm_id,
  1132 + &lpm_priv->outlet_id, &tb_size);
  1133 +
  1134 + if (result) {
  1135 + dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n",
  1136 + __func__, __LINE__, ps3_result(result));
  1137 + result = -EINVAL;
  1138 + goto fail_construct;
  1139 + }
  1140 +
  1141 + lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT;
  1142 + lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT;
  1143 + lpm_priv->shadow.pm_interval = PS3_LPM_SHADOW_REG_INIT;
  1144 + lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT;
  1145 + lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT;
  1146 +
  1147 + dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%lx, outlet_id 0x%lx, "
  1148 + "tb_size 0x%lx\n", __func__, __LINE__, lpm_priv->lpm_id,
  1149 + lpm_priv->outlet_id, tb_size);
  1150 +
  1151 + return 0;
  1152 +
  1153 +fail_construct:
  1154 + kfree(lpm_priv->tb_cache_internal);
  1155 + lpm_priv->tb_cache_internal = NULL;
  1156 +fail_malloc:
  1157 +fail_align:
  1158 + atomic_dec(&lpm_priv->open);
  1159 + return result;
  1160 +}
  1161 +EXPORT_SYMBOL_GPL(ps3_lpm_open);
  1162 +
  1163 +/**
  1164 + * ps3_lpm_close - Close the lpm device.
  1165 + *
  1166 + */
  1167 +
  1168 +int ps3_lpm_close(void)
  1169 +{
  1170 + dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__);
  1171 +
  1172 + lv1_destruct_lpm(lpm_priv->lpm_id);
  1173 + lpm_priv->lpm_id = 0;
  1174 +
  1175 + kfree(lpm_priv->tb_cache_internal);
  1176 + lpm_priv->tb_cache_internal = NULL;
  1177 +
  1178 + atomic_dec(&lpm_priv->open);
  1179 + return 0;
  1180 +}
  1181 +EXPORT_SYMBOL_GPL(ps3_lpm_close);
  1182 +
  1183 +static int __devinit ps3_lpm_probe(struct ps3_system_bus_device *dev)
  1184 +{
  1185 + dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__);
  1186 +
  1187 + if (lpm_priv) {
  1188 + dev_info(&dev->core, "%s:%u: called twice\n",
  1189 + __func__, __LINE__);
  1190 + return -EBUSY;
  1191 + }
  1192 +
  1193 + lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL);
  1194 +
  1195 + if (!lpm_priv)
  1196 + return -ENOMEM;
  1197 +
  1198 + lpm_priv->sbd = dev;
  1199 + lpm_priv->node_id = dev->lpm.node_id;
  1200 + lpm_priv->pu_id = dev->lpm.pu_id;
  1201 + lpm_priv->rights = dev->lpm.rights;
  1202 +
  1203 + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
  1204 +
  1205 + return 0;
  1206 +}
  1207 +
  1208 +static int ps3_lpm_remove(struct ps3_system_bus_device *dev)
  1209 +{
  1210 + dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__);
  1211 +
  1212 + ps3_lpm_close();
  1213 +
  1214 + kfree(lpm_priv);
  1215 + lpm_priv = NULL;
  1216 +
  1217 + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);
  1218 + return 0;
  1219 +}
  1220 +
  1221 +static struct ps3_system_bus_driver ps3_lpm_driver = {
  1222 + .match_id = PS3_MATCH_ID_LPM,
  1223 + .core.name = "ps3-lpm",
  1224 + .core.owner = THIS_MODULE,
  1225 + .probe = ps3_lpm_probe,
  1226 + .remove = ps3_lpm_remove,
  1227 + .shutdown = ps3_lpm_remove,
  1228 +};
  1229 +
  1230 +static int __init ps3_lpm_init(void)
  1231 +{
  1232 + pr_debug("%s:%d:\n", __func__, __LINE__);
  1233 + return ps3_system_bus_driver_register(&ps3_lpm_driver);
  1234 +}
  1235 +
  1236 +static void __exit ps3_lpm_exit(void)
  1237 +{
  1238 + pr_debug("%s:%d:\n", __func__, __LINE__);
  1239 + ps3_system_bus_driver_unregister(&ps3_lpm_driver);
  1240 +}
  1241 +
  1242 +module_init(ps3_lpm_init);
  1243 +module_exit(ps3_lpm_exit);
  1244 +
  1245 +MODULE_LICENSE("GPL v2");
  1246 +MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver");
  1247 +MODULE_AUTHOR("Sony Corporation");
  1248 +MODULE_ALIAS(PS3_MODULE_ALIAS_LPM);
include/asm-powerpc/ps3.h
... ... @@ -24,6 +24,7 @@
24 24 #include <linux/init.h>
25 25 #include <linux/types.h>
26 26 #include <linux/device.h>
  27 +#include "cell-pmu.h"
27 28  
28 29 union ps3_firmware_version {
29 30 u64 raw;
... ... @@ -446,6 +447,67 @@
446 447 extern struct ps3_prealloc ps3fb_videomemory;
447 448 extern struct ps3_prealloc ps3flash_bounce_buffer;
448 449  
  450 +/* logical performance monitor */
  451 +
  452 +/**
  453 + * enum ps3_lpm_rights - Rigths granted by the system policy module.
  454 + *
  455 + * @PS3_LPM_RIGHTS_USE_LPM: The right to use the lpm.
  456 + * @PS3_LPM_RIGHTS_USE_TB: The right to use the internal trace buffer.
  457 + */
  458 +
  459 +enum ps3_lpm_rights {
  460 + PS3_LPM_RIGHTS_USE_LPM = 0x001,
  461 + PS3_LPM_RIGHTS_USE_TB = 0x100,
  462 +};
  463 +
  464 +/**
  465 + * enum ps3_lpm_tb_type - Type of trace buffer lv1 should use.
  466 + *
  467 + * @PS3_LPM_TB_TYPE_NONE: Do not use a trace buffer.
  468 + * @PS3_LPM_RIGHTS_USE_TB: Use the lv1 internal trace buffer. Must have
  469 + * rights @PS3_LPM_RIGHTS_USE_TB.
  470 + */
  471 +
  472 +enum ps3_lpm_tb_type {
  473 + PS3_LPM_TB_TYPE_NONE = 0,
  474 + PS3_LPM_TB_TYPE_INTERNAL = 1,
  475 +};
  476 +
  477 +int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache,
  478 + u64 tb_cache_size);
  479 +int ps3_lpm_close(void);
  480 +int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count,
  481 + unsigned long *bytes_copied);
  482 +int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,
  483 + unsigned long count, unsigned long *bytes_copied);
  484 +void ps3_set_bookmark(u64 bookmark);
  485 +void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id);
  486 +int ps3_set_signal(u64 rtas_signal_group, u8 signal_bit, u16 sub_unit,
  487 + u8 bus_word);
  488 +
  489 +u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr);
  490 +void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val);
  491 +u32 ps3_read_ctr(u32 cpu, u32 ctr);
  492 +void ps3_write_ctr(u32 cpu, u32 ctr, u32 val);
  493 +
  494 +u32 ps3_read_pm07_control(u32 cpu, u32 ctr);
  495 +void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val);
  496 +u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg);
  497 +void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val);
  498 +
  499 +u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr);
  500 +void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size);
  501 +
  502 +void ps3_enable_pm(u32 cpu);
  503 +void ps3_disable_pm(u32 cpu);
  504 +void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask);
  505 +void ps3_disable_pm_interrupts(u32 cpu);
  506 +
  507 +u32 ps3_get_and_clear_pm_interrupts(u32 cpu);
  508 +void ps3_sync_irq(int node);
  509 +u32 ps3_get_hw_thread_id(int cpu);
  510 +u64 ps3_get_spe_id(void *arg);
449 511  
450 512 #endif