162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PS3 Logical Performance Monitor. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Sony Computer Entertainment Inc. 662306a36Sopenharmony_ci * Copyright 2007 Sony Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <asm/smp.h> 1562306a36Sopenharmony_ci#include <asm/time.h> 1662306a36Sopenharmony_ci#include <asm/ps3.h> 1762306a36Sopenharmony_ci#include <asm/lv1call.h> 1862306a36Sopenharmony_ci#include <asm/cell-pmu.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* BOOKMARK tag macros */ 2262306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL 2362306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL 2462306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL 2562306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL 2662306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL 2762306a36Sopenharmony_ci#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* CBE PM CONTROL register macros */ 3062306a36Sopenharmony_ci#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000 3162306a36Sopenharmony_ci#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800 3262306a36Sopenharmony_ci#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000 3362306a36Sopenharmony_ci#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000 3462306a36Sopenharmony_ci#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* CBE PM START STOP register macros */ 3762306a36Sopenharmony_ci#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000 3862306a36Sopenharmony_ci#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000 3962306a36Sopenharmony_ci#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000 4062306a36Sopenharmony_ci#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000 4162306a36Sopenharmony_ci#define PS3_PM_START_STOP_START_MASK 0xFF000000 4262306a36Sopenharmony_ci#define PS3_PM_START_STOP_STOP_MASK 0x00FF0000 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* CBE PM COUNTER register macres */ 4562306a36Sopenharmony_ci#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL 4662306a36Sopenharmony_ci#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* BASE SIGNAL GROUP NUMBER macros */ 4962306a36Sopenharmony_ci#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0 5062306a36Sopenharmony_ci#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6 5162306a36Sopenharmony_ci#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7 5262306a36Sopenharmony_ci#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7 5362306a36Sopenharmony_ci#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15 5462306a36Sopenharmony_ci#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17 5562306a36Sopenharmony_ci#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18 5662306a36Sopenharmony_ci#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18 5762306a36Sopenharmony_ci#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24 5862306a36Sopenharmony_ci#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49 5962306a36Sopenharmony_ci#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52 6062306a36Sopenharmony_ci#define PM_SIG_GROUP_SPU 41 6162306a36Sopenharmony_ci#define PM_SIG_GROUP_SPU_TRIGGER 42 6262306a36Sopenharmony_ci#define PM_SIG_GROUP_SPU_EVENT 43 6362306a36Sopenharmony_ci#define PM_SIG_GROUP_MFC_MAX 60 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * struct ps3_lpm_shadow_regs - Performance monitor shadow registers. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * @pm_control: Shadow of the processor's pm_control register. 6962306a36Sopenharmony_ci * @pm_start_stop: Shadow of the processor's pm_start_stop register. 7062306a36Sopenharmony_ci * @group_control: Shadow of the processor's group_control register. 7162306a36Sopenharmony_ci * @debug_bus_control: Shadow of the processor's debug_bus_control register. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * The logical performance monitor provides a write-only interface to 7462306a36Sopenharmony_ci * these processor registers. These shadow variables cache the processor 7562306a36Sopenharmony_ci * register values for reading. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * The initial value of the shadow registers at lpm creation is 7862306a36Sopenharmony_ci * PS3_LPM_SHADOW_REG_INIT. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct ps3_lpm_shadow_regs { 8262306a36Sopenharmony_ci u64 pm_control; 8362306a36Sopenharmony_ci u64 pm_start_stop; 8462306a36Sopenharmony_ci u64 group_control; 8562306a36Sopenharmony_ci u64 debug_bus_control; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * struct ps3_lpm_priv - Private lpm device data. 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * @open: An atomic variable indicating the lpm driver has been opened. 9462306a36Sopenharmony_ci * @rights: The lpm rigths granted by the system policy module. A logical 9562306a36Sopenharmony_ci * OR of enum ps3_lpm_rights. 9662306a36Sopenharmony_ci * @node_id: The node id of a BE processor whose performance monitor this 9762306a36Sopenharmony_ci * lpar has the right to use. 9862306a36Sopenharmony_ci * @pu_id: The lv1 id of the logical PU. 9962306a36Sopenharmony_ci * @lpm_id: The lv1 id of this lpm instance. 10062306a36Sopenharmony_ci * @outlet_id: The outlet created by lv1 for this lpm instance. 10162306a36Sopenharmony_ci * @tb_count: The number of bytes of data held in the lv1 trace buffer. 10262306a36Sopenharmony_ci * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer. 10362306a36Sopenharmony_ci * Must be 128 byte aligned. 10462306a36Sopenharmony_ci * @tb_cache_size: Size of the kernel @tb_cache buffer. Must be 128 byte 10562306a36Sopenharmony_ci * aligned. 10662306a36Sopenharmony_ci * @tb_cache_internal: An unaligned buffer allocated by this driver to be 10762306a36Sopenharmony_ci * used for the trace buffer cache when ps3_lpm_open() is called with a 10862306a36Sopenharmony_ci * NULL tb_cache argument. Otherwise unused. 10962306a36Sopenharmony_ci * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs. 11062306a36Sopenharmony_ci * @sbd: The struct ps3_system_bus_device attached to this driver. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * The trace buffer is a buffer allocated and used internally to the lv1 11362306a36Sopenharmony_ci * hypervisor to collect trace data. The trace buffer cache is a guest 11462306a36Sopenharmony_ci * buffer that accepts the trace data from the trace buffer. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct ps3_lpm_priv { 11862306a36Sopenharmony_ci atomic_t open; 11962306a36Sopenharmony_ci u64 rights; 12062306a36Sopenharmony_ci u64 node_id; 12162306a36Sopenharmony_ci u64 pu_id; 12262306a36Sopenharmony_ci u64 lpm_id; 12362306a36Sopenharmony_ci u64 outlet_id; 12462306a36Sopenharmony_ci u64 tb_count; 12562306a36Sopenharmony_ci void *tb_cache; 12662306a36Sopenharmony_ci u64 tb_cache_size; 12762306a36Sopenharmony_ci void *tb_cache_internal; 12862306a36Sopenharmony_ci struct ps3_lpm_shadow_regs shadow; 12962306a36Sopenharmony_ci struct ps3_system_bus_device *sbd; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cienum { 13362306a36Sopenharmony_ci PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * lpm_priv - Static instance of the lpm data. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Since the exported routines don't support the notion of a device 14062306a36Sopenharmony_ci * instance we need to hold the instance in this static variable 14162306a36Sopenharmony_ci * and then only allow at most one instance at a time to be created. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct ps3_lpm_priv *lpm_priv; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct device *sbd_core(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci BUG_ON(!lpm_priv || !lpm_priv->sbd); 14962306a36Sopenharmony_ci return &lpm_priv->sbd->core; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/** 15362306a36Sopenharmony_ci * use_start_stop_bookmark - Enable the PPU bookmark trace. 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * And it enables PPU bookmark triggers ONLY if the other triggers are not set. 15662306a36Sopenharmony_ci * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm() 15762306a36Sopenharmony_ci * to start/stop LPM. 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Used to get good quality of the performance counter. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cienum {use_start_stop_bookmark = 1,}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid ps3_set_bookmark(u64 bookmark) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * As per the PPE book IV, to avoid bookmark loss there must 16862306a36Sopenharmony_ci * not be a traced branch within 10 cycles of setting the 16962306a36Sopenharmony_ci * SPRN_BKMK register. The actual text is unclear if 'within' 17062306a36Sopenharmony_ci * includes cycles before the call. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;"); 17462306a36Sopenharmony_ci mtspr(SPRN_BKMK, bookmark); 17562306a36Sopenharmony_ci asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;"); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_set_bookmark); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_civoid ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u64 bookmark; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci bookmark = (get_tb() & 0x00000000FFFFFFFFULL) | 18462306a36Sopenharmony_ci PS3_PM_BOOKMARK_TAG_KERNEL; 18562306a36Sopenharmony_ci bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) | 18662306a36Sopenharmony_ci (incident << 48) | (th_id << 32) | bookmark; 18762306a36Sopenharmony_ci ps3_set_bookmark(bookmark); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_set_pm_bookmark); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * ps3_read_phys_ctr - Read physical counter registers. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Each physical counter can act as one 32 bit counter or as two 16 bit 19562306a36Sopenharmony_ci * counters. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciu32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int result; 20162306a36Sopenharmony_ci u64 counter0415; 20262306a36Sopenharmony_ci u64 counter2637; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (phys_ctr >= NR_PHYS_CTRS) { 20562306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, 20662306a36Sopenharmony_ci __LINE__, phys_ctr); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415, 21162306a36Sopenharmony_ci &counter2637); 21262306a36Sopenharmony_ci if (result) { 21362306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " 21462306a36Sopenharmony_ci "phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr, 21562306a36Sopenharmony_ci ps3_result(result)); 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci switch (phys_ctr) { 22062306a36Sopenharmony_ci case 0: 22162306a36Sopenharmony_ci return counter0415 >> 32; 22262306a36Sopenharmony_ci case 1: 22362306a36Sopenharmony_ci return counter0415 & PS3_PM_COUNTER_MASK_LO; 22462306a36Sopenharmony_ci case 2: 22562306a36Sopenharmony_ci return counter2637 >> 32; 22662306a36Sopenharmony_ci case 3: 22762306a36Sopenharmony_ci return counter2637 & PS3_PM_COUNTER_MASK_LO; 22862306a36Sopenharmony_ci default: 22962306a36Sopenharmony_ci BUG(); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_read_phys_ctr); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * ps3_write_phys_ctr - Write physical counter registers. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Each physical counter can act as one 32 bit counter or as two 16 bit 23962306a36Sopenharmony_ci * counters. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_civoid ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci u64 counter0415; 24562306a36Sopenharmony_ci u64 counter0415_mask; 24662306a36Sopenharmony_ci u64 counter2637; 24762306a36Sopenharmony_ci u64 counter2637_mask; 24862306a36Sopenharmony_ci int result; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (phys_ctr >= NR_PHYS_CTRS) { 25162306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, 25262306a36Sopenharmony_ci __LINE__, phys_ctr); 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci switch (phys_ctr) { 25762306a36Sopenharmony_ci case 0: 25862306a36Sopenharmony_ci counter0415 = (u64)val << 32; 25962306a36Sopenharmony_ci counter0415_mask = PS3_PM_COUNTER_MASK_HI; 26062306a36Sopenharmony_ci counter2637 = 0x0; 26162306a36Sopenharmony_ci counter2637_mask = 0x0; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci case 1: 26462306a36Sopenharmony_ci counter0415 = (u64)val; 26562306a36Sopenharmony_ci counter0415_mask = PS3_PM_COUNTER_MASK_LO; 26662306a36Sopenharmony_ci counter2637 = 0x0; 26762306a36Sopenharmony_ci counter2637_mask = 0x0; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case 2: 27062306a36Sopenharmony_ci counter0415 = 0x0; 27162306a36Sopenharmony_ci counter0415_mask = 0x0; 27262306a36Sopenharmony_ci counter2637 = (u64)val << 32; 27362306a36Sopenharmony_ci counter2637_mask = PS3_PM_COUNTER_MASK_HI; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case 3: 27662306a36Sopenharmony_ci counter0415 = 0x0; 27762306a36Sopenharmony_ci counter0415_mask = 0x0; 27862306a36Sopenharmony_ci counter2637 = (u64)val; 27962306a36Sopenharmony_ci counter2637_mask = PS3_PM_COUNTER_MASK_LO; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci BUG(); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci result = lv1_set_lpm_counter(lpm_priv->lpm_id, 28662306a36Sopenharmony_ci counter0415, counter0415_mask, 28762306a36Sopenharmony_ci counter2637, counter2637_mask, 28862306a36Sopenharmony_ci &counter0415, &counter2637); 28962306a36Sopenharmony_ci if (result) 29062306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " 29162306a36Sopenharmony_ci "phys_ctr %u, val %u, %s\n", __func__, __LINE__, 29262306a36Sopenharmony_ci phys_ctr, val, ps3_result(result)); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_write_phys_ctr); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/** 29762306a36Sopenharmony_ci * ps3_read_ctr - Read counter. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Read 16 or 32 bits depending on the current size of the counter. 30062306a36Sopenharmony_ci * Counters 4, 5, 6 & 7 are always 16 bit. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciu32 ps3_read_ctr(u32 cpu, u32 ctr) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci u32 val; 30662306a36Sopenharmony_ci u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci val = ps3_read_phys_ctr(cpu, phys_ctr); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (ps3_get_ctr_size(cpu, phys_ctr) == 16) 31162306a36Sopenharmony_ci val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return val; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_read_ctr); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/** 31862306a36Sopenharmony_ci * ps3_write_ctr - Write counter. 31962306a36Sopenharmony_ci * 32062306a36Sopenharmony_ci * Write 16 or 32 bits depending on the current size of the counter. 32162306a36Sopenharmony_ci * Counters 4, 5, 6 & 7 are always 16 bit. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_civoid ps3_write_ctr(u32 cpu, u32 ctr, u32 val) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci u32 phys_ctr; 32762306a36Sopenharmony_ci u32 phys_val; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci phys_ctr = ctr & (NR_PHYS_CTRS - 1); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (ps3_get_ctr_size(cpu, phys_ctr) == 16) { 33262306a36Sopenharmony_ci phys_val = ps3_read_phys_ctr(cpu, phys_ctr); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (ctr < NR_PHYS_CTRS) 33562306a36Sopenharmony_ci val = (val << 16) | (phys_val & 0xffff); 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci val = (val & 0xffff) | (phys_val & 0xffff0000); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ps3_write_phys_ctr(cpu, phys_ctr, val); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_write_ctr); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/** 34562306a36Sopenharmony_ci * ps3_read_pm07_control - Read counter control registers. 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * Each logical counter has a corresponding control register. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ciu32 ps3_read_pm07_control(u32 cpu, u32 ctr) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_read_pm07_control); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/** 35762306a36Sopenharmony_ci * ps3_write_pm07_control - Write counter control registers. 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * Each logical counter has a corresponding control register. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci int result; 36562306a36Sopenharmony_ci static const u64 mask = 0xFFFFFFFFFFFFFFFFULL; 36662306a36Sopenharmony_ci u64 old_value; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (ctr >= NR_CTRS) { 36962306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__, 37062306a36Sopenharmony_ci __LINE__, ctr); 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask, 37562306a36Sopenharmony_ci &old_value); 37662306a36Sopenharmony_ci if (result) 37762306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control " 37862306a36Sopenharmony_ci "failed: ctr %u, %s\n", __func__, __LINE__, ctr, 37962306a36Sopenharmony_ci ps3_result(result)); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_write_pm07_control); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/** 38462306a36Sopenharmony_ci * ps3_read_pm - Read Other LPM control registers. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciu32 ps3_read_pm(u32 cpu, enum pm_reg_name reg) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci int result = 0; 39062306a36Sopenharmony_ci u64 val = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci switch (reg) { 39362306a36Sopenharmony_ci case pm_control: 39462306a36Sopenharmony_ci return lpm_priv->shadow.pm_control; 39562306a36Sopenharmony_ci case trace_address: 39662306a36Sopenharmony_ci return CBE_PM_TRACE_BUF_EMPTY; 39762306a36Sopenharmony_ci case pm_start_stop: 39862306a36Sopenharmony_ci return lpm_priv->shadow.pm_start_stop; 39962306a36Sopenharmony_ci case pm_interval: 40062306a36Sopenharmony_ci result = lv1_set_lpm_interval(lpm_priv->lpm_id, 0, 0, &val); 40162306a36Sopenharmony_ci if (result) { 40262306a36Sopenharmony_ci val = 0; 40362306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: lv1 set_interval failed: " 40462306a36Sopenharmony_ci "reg %u, %s\n", __func__, __LINE__, reg, 40562306a36Sopenharmony_ci ps3_result(result)); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci return (u32)val; 40862306a36Sopenharmony_ci case group_control: 40962306a36Sopenharmony_ci return lpm_priv->shadow.group_control; 41062306a36Sopenharmony_ci case debug_bus_control: 41162306a36Sopenharmony_ci return lpm_priv->shadow.debug_bus_control; 41262306a36Sopenharmony_ci case pm_status: 41362306a36Sopenharmony_ci result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id, 41462306a36Sopenharmony_ci &val); 41562306a36Sopenharmony_ci if (result) { 41662306a36Sopenharmony_ci val = 0; 41762306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: " 41862306a36Sopenharmony_ci "reg %u, %s\n", __func__, __LINE__, reg, 41962306a36Sopenharmony_ci ps3_result(result)); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci return (u32)val; 42262306a36Sopenharmony_ci case ext_tr_timer: 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, 42662306a36Sopenharmony_ci __LINE__, reg); 42762306a36Sopenharmony_ci BUG(); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_read_pm); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * ps3_write_pm - Write Other LPM control registers. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_civoid ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int result = 0; 44262306a36Sopenharmony_ci u64 dummy; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci switch (reg) { 44562306a36Sopenharmony_ci case group_control: 44662306a36Sopenharmony_ci if (val != lpm_priv->shadow.group_control) 44762306a36Sopenharmony_ci result = lv1_set_lpm_group_control(lpm_priv->lpm_id, 44862306a36Sopenharmony_ci val, 44962306a36Sopenharmony_ci PS3_WRITE_PM_MASK, 45062306a36Sopenharmony_ci &dummy); 45162306a36Sopenharmony_ci lpm_priv->shadow.group_control = val; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case debug_bus_control: 45462306a36Sopenharmony_ci if (val != lpm_priv->shadow.debug_bus_control) 45562306a36Sopenharmony_ci result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id, 45662306a36Sopenharmony_ci val, 45762306a36Sopenharmony_ci PS3_WRITE_PM_MASK, 45862306a36Sopenharmony_ci &dummy); 45962306a36Sopenharmony_ci lpm_priv->shadow.debug_bus_control = val; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci case pm_control: 46262306a36Sopenharmony_ci if (use_start_stop_bookmark) 46362306a36Sopenharmony_ci val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK | 46462306a36Sopenharmony_ci PS3_PM_CONTROL_PPU_TH1_BOOKMARK); 46562306a36Sopenharmony_ci if (val != lpm_priv->shadow.pm_control) 46662306a36Sopenharmony_ci result = lv1_set_lpm_general_control(lpm_priv->lpm_id, 46762306a36Sopenharmony_ci val, 46862306a36Sopenharmony_ci PS3_WRITE_PM_MASK, 46962306a36Sopenharmony_ci 0, 0, &dummy, 47062306a36Sopenharmony_ci &dummy); 47162306a36Sopenharmony_ci lpm_priv->shadow.pm_control = val; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case pm_interval: 47462306a36Sopenharmony_ci result = lv1_set_lpm_interval(lpm_priv->lpm_id, val, 47562306a36Sopenharmony_ci PS3_WRITE_PM_MASK, &dummy); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case pm_start_stop: 47862306a36Sopenharmony_ci if (val != lpm_priv->shadow.pm_start_stop) 47962306a36Sopenharmony_ci result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, 48062306a36Sopenharmony_ci val, 48162306a36Sopenharmony_ci PS3_WRITE_PM_MASK, 48262306a36Sopenharmony_ci &dummy); 48362306a36Sopenharmony_ci lpm_priv->shadow.pm_start_stop = val; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case trace_address: 48662306a36Sopenharmony_ci case ext_tr_timer: 48762306a36Sopenharmony_ci case pm_status: 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci default: 49062306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, 49162306a36Sopenharmony_ci __LINE__, reg); 49262306a36Sopenharmony_ci BUG(); 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (result) 49762306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1 set_control failed: " 49862306a36Sopenharmony_ci "reg %u, %s\n", __func__, __LINE__, reg, 49962306a36Sopenharmony_ci ps3_result(result)); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_write_pm); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * ps3_get_ctr_size - Get the size of a physical counter. 50562306a36Sopenharmony_ci * 50662306a36Sopenharmony_ci * Returns either 16 or 32. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciu32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci u32 pm_ctrl; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (phys_ctr >= NR_PHYS_CTRS) { 51462306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, 51562306a36Sopenharmony_ci __LINE__, phys_ctr); 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci pm_ctrl = ps3_read_pm(cpu, pm_control); 52062306a36Sopenharmony_ci return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_get_ctr_size); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/** 52562306a36Sopenharmony_ci * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_civoid ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci u32 pm_ctrl; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (phys_ctr >= NR_PHYS_CTRS) { 53362306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, 53462306a36Sopenharmony_ci __LINE__, phys_ctr); 53562306a36Sopenharmony_ci return; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci pm_ctrl = ps3_read_pm(cpu, pm_control); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci switch (ctr_size) { 54162306a36Sopenharmony_ci case 16: 54262306a36Sopenharmony_ci pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); 54362306a36Sopenharmony_ci ps3_write_pm(cpu, pm_control, pm_ctrl); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci case 32: 54762306a36Sopenharmony_ci pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); 54862306a36Sopenharmony_ci ps3_write_pm(cpu, pm_control, pm_ctrl); 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci default: 55162306a36Sopenharmony_ci BUG(); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_set_ctr_size); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island2(u64 subgroup) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (subgroup == 2) 56062306a36Sopenharmony_ci subgroup = 3; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (subgroup <= 6) 56362306a36Sopenharmony_ci return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup; 56462306a36Sopenharmony_ci else if (subgroup == 7) 56562306a36Sopenharmony_ci return PM_ISLAND2_SIGNAL_GROUP_NUMBER1; 56662306a36Sopenharmony_ci else 56762306a36Sopenharmony_ci return PM_ISLAND2_SIGNAL_GROUP_NUMBER2; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island3(u64 subgroup) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci switch (subgroup) { 57462306a36Sopenharmony_ci case 2: 57562306a36Sopenharmony_ci case 3: 57662306a36Sopenharmony_ci case 4: 57762306a36Sopenharmony_ci subgroup += 2; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case 5: 58062306a36Sopenharmony_ci subgroup = 8; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci default: 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island4(u64 subgroup) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island5(u64 subgroup) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (subgroup) { 59762306a36Sopenharmony_ci case 3: 59862306a36Sopenharmony_ci subgroup = 4; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case 4: 60162306a36Sopenharmony_ci subgroup = 6; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island6(u64 subgroup, 61062306a36Sopenharmony_ci u64 subsubgroup) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci switch (subgroup) { 61362306a36Sopenharmony_ci case 3: 61462306a36Sopenharmony_ci case 4: 61562306a36Sopenharmony_ci case 5: 61662306a36Sopenharmony_ci subgroup += 1; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci default: 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci switch (subsubgroup) { 62362306a36Sopenharmony_ci case 4: 62462306a36Sopenharmony_ci case 5: 62562306a36Sopenharmony_ci case 6: 62662306a36Sopenharmony_ci subsubgroup += 2; 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci case 7: 62962306a36Sopenharmony_ci case 8: 63062306a36Sopenharmony_ci case 9: 63162306a36Sopenharmony_ci case 10: 63262306a36Sopenharmony_ci subsubgroup += 4; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case 11: 63562306a36Sopenharmony_ci case 12: 63662306a36Sopenharmony_ci case 13: 63762306a36Sopenharmony_ci subsubgroup += 5; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (subgroup <= 5) 64462306a36Sopenharmony_ci return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup); 64562306a36Sopenharmony_ci else 64662306a36Sopenharmony_ci return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup 64762306a36Sopenharmony_ci + subsubgroup - 1); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island7(u64 subgroup) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic u64 pm_translate_signal_group_number_on_island8(u64 subgroup) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci u64 island; 66362306a36Sopenharmony_ci u64 subgroup; 66462306a36Sopenharmony_ci u64 subsubgroup; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci subgroup = 0; 66762306a36Sopenharmony_ci subsubgroup = 0; 66862306a36Sopenharmony_ci island = 0; 66962306a36Sopenharmony_ci if (group < 1000) { 67062306a36Sopenharmony_ci if (group < 100) { 67162306a36Sopenharmony_ci if (20 <= group && group < 30) { 67262306a36Sopenharmony_ci island = 2; 67362306a36Sopenharmony_ci subgroup = group - 20; 67462306a36Sopenharmony_ci } else if (30 <= group && group < 40) { 67562306a36Sopenharmony_ci island = 3; 67662306a36Sopenharmony_ci subgroup = group - 30; 67762306a36Sopenharmony_ci } else if (40 <= group && group < 50) { 67862306a36Sopenharmony_ci island = 4; 67962306a36Sopenharmony_ci subgroup = group - 40; 68062306a36Sopenharmony_ci } else if (50 <= group && group < 60) { 68162306a36Sopenharmony_ci island = 5; 68262306a36Sopenharmony_ci subgroup = group - 50; 68362306a36Sopenharmony_ci } else if (60 <= group && group < 70) { 68462306a36Sopenharmony_ci island = 6; 68562306a36Sopenharmony_ci subgroup = group - 60; 68662306a36Sopenharmony_ci } else if (70 <= group && group < 80) { 68762306a36Sopenharmony_ci island = 7; 68862306a36Sopenharmony_ci subgroup = group - 70; 68962306a36Sopenharmony_ci } else if (80 <= group && group < 90) { 69062306a36Sopenharmony_ci island = 8; 69162306a36Sopenharmony_ci subgroup = group - 80; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } else if (200 <= group && group < 300) { 69462306a36Sopenharmony_ci island = 2; 69562306a36Sopenharmony_ci subgroup = group - 200; 69662306a36Sopenharmony_ci } else if (600 <= group && group < 700) { 69762306a36Sopenharmony_ci island = 6; 69862306a36Sopenharmony_ci subgroup = 5; 69962306a36Sopenharmony_ci subsubgroup = group - 650; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci } else if (6000 <= group && group < 7000) { 70262306a36Sopenharmony_ci island = 6; 70362306a36Sopenharmony_ci subgroup = 5; 70462306a36Sopenharmony_ci subsubgroup = group - 6500; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci switch (island) { 70862306a36Sopenharmony_ci case 2: 70962306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island2(subgroup); 71062306a36Sopenharmony_ci case 3: 71162306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island3(subgroup); 71262306a36Sopenharmony_ci case 4: 71362306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island4(subgroup); 71462306a36Sopenharmony_ci case 5: 71562306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island5(subgroup); 71662306a36Sopenharmony_ci case 6: 71762306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island6(subgroup, 71862306a36Sopenharmony_ci subsubgroup); 71962306a36Sopenharmony_ci case 7: 72062306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island7(subgroup); 72162306a36Sopenharmony_ci case 8: 72262306a36Sopenharmony_ci return pm_translate_signal_group_number_on_island8(subgroup); 72362306a36Sopenharmony_ci default: 72462306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: island not found: %llu\n", __func__, 72562306a36Sopenharmony_ci __LINE__, group); 72662306a36Sopenharmony_ci BUG(); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci switch (word) { 73662306a36Sopenharmony_ci case 1: 73762306a36Sopenharmony_ci return 0xF000; 73862306a36Sopenharmony_ci case 2: 73962306a36Sopenharmony_ci return 0x0F00; 74062306a36Sopenharmony_ci case 4: 74162306a36Sopenharmony_ci return 0x00F0; 74262306a36Sopenharmony_ci case 8: 74362306a36Sopenharmony_ci default: 74462306a36Sopenharmony_ci return 0x000F; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select, 74962306a36Sopenharmony_ci u64 signal_select, u64 attr1, u64 attr2, u64 attr3) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci int ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select, 75462306a36Sopenharmony_ci signal_select, attr1, attr2, attr3); 75562306a36Sopenharmony_ci if (ret) 75662306a36Sopenharmony_ci dev_err(sbd_core(), 75762306a36Sopenharmony_ci "%s:%u: error:%d 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n", 75862306a36Sopenharmony_ci __func__, __LINE__, ret, lv1_signal_group, bus_select, 75962306a36Sopenharmony_ci signal_select, attr1, attr2, attr3); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ciint ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit, 76562306a36Sopenharmony_ci u8 bus_word) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int ret; 76862306a36Sopenharmony_ci u64 lv1_signal_group; 76962306a36Sopenharmony_ci u64 bus_select; 77062306a36Sopenharmony_ci u64 signal_select; 77162306a36Sopenharmony_ci u64 attr1, attr2, attr3; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (signal_group == 0) 77462306a36Sopenharmony_ci return __ps3_set_signal(0, 0, 0, 0, 0, 0); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci lv1_signal_group = 77762306a36Sopenharmony_ci pm_signal_group_to_ps3_lv1_signal_group(signal_group); 77862306a36Sopenharmony_ci bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci switch (signal_group) { 78162306a36Sopenharmony_ci case PM_SIG_GROUP_SPU_TRIGGER: 78262306a36Sopenharmony_ci signal_select = 1; 78362306a36Sopenharmony_ci signal_select = signal_select << (63 - signal_bit); 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case PM_SIG_GROUP_SPU_EVENT: 78662306a36Sopenharmony_ci signal_select = 1; 78762306a36Sopenharmony_ci signal_select = (signal_select << (63 - signal_bit)) | 0x3; 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci default: 79062306a36Sopenharmony_ci signal_select = 0; 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * 0: physical object. 79662306a36Sopenharmony_ci * 1: logical object. 79762306a36Sopenharmony_ci * This parameter is only used for the PPE and SPE signals. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci attr1 = 1; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* 80262306a36Sopenharmony_ci * This parameter is used to specify the target physical/logical 80362306a36Sopenharmony_ci * PPE/SPE object. 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci if (PM_SIG_GROUP_SPU <= signal_group && 80662306a36Sopenharmony_ci signal_group < PM_SIG_GROUP_MFC_MAX) 80762306a36Sopenharmony_ci attr2 = sub_unit; 80862306a36Sopenharmony_ci else 80962306a36Sopenharmony_ci attr2 = lpm_priv->pu_id; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* 81262306a36Sopenharmony_ci * This parameter is only used for setting the SPE signal. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci attr3 = 0; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select, 81762306a36Sopenharmony_ci attr1, attr2, attr3); 81862306a36Sopenharmony_ci if (ret) 81962306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n", 82062306a36Sopenharmony_ci __func__, __LINE__, ret); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_set_signal); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciu32 ps3_get_hw_thread_id(int cpu) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci return get_hard_smp_processor_id(cpu); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_get_hw_thread_id); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/** 83362306a36Sopenharmony_ci * ps3_enable_pm - Enable the entire performance monitoring unit. 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * When we enable the LPM, all pending writes to counters get committed. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_civoid ps3_enable_pm(u32 cpu) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci int result; 84162306a36Sopenharmony_ci u64 tmp; 84262306a36Sopenharmony_ci int insert_bookmark = 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci lpm_priv->tb_count = 0; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (use_start_stop_bookmark) { 84762306a36Sopenharmony_ci if (!(lpm_priv->shadow.pm_start_stop & 84862306a36Sopenharmony_ci (PS3_PM_START_STOP_START_MASK 84962306a36Sopenharmony_ci | PS3_PM_START_STOP_STOP_MASK))) { 85062306a36Sopenharmony_ci result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, 85162306a36Sopenharmony_ci (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START | 85262306a36Sopenharmony_ci PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START | 85362306a36Sopenharmony_ci PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP | 85462306a36Sopenharmony_ci PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP), 85562306a36Sopenharmony_ci 0xFFFFFFFFFFFFFFFFULL, &tmp); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (result) 85862306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: " 85962306a36Sopenharmony_ci "lv1_set_lpm_trigger_control failed: " 86062306a36Sopenharmony_ci "%s\n", __func__, __LINE__, 86162306a36Sopenharmony_ci ps3_result(result)); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci insert_bookmark = !result; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci result = lv1_start_lpm(lpm_priv->lpm_id); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (result) 87062306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n", 87162306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (use_start_stop_bookmark && !result && insert_bookmark) 87462306a36Sopenharmony_ci ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_enable_pm); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/** 87962306a36Sopenharmony_ci * ps3_disable_pm - Disable the entire performance monitoring unit. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_civoid ps3_disable_pm(u32 cpu) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int result; 88562306a36Sopenharmony_ci u64 tmp; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (result) { 89262306a36Sopenharmony_ci if (result != LV1_WRONG_STATE) 89362306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n", 89462306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 89562306a36Sopenharmony_ci return; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci lpm_priv->tb_count = tmp; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: tb_count %llu (%llxh)\n", __func__, __LINE__, 90162306a36Sopenharmony_ci lpm_priv->tb_count, lpm_priv->tb_count); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_disable_pm); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/** 90662306a36Sopenharmony_ci * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer. 90762306a36Sopenharmony_ci * @offset: Offset in bytes from the start of the trace buffer. 90862306a36Sopenharmony_ci * @buf: Copy destination. 90962306a36Sopenharmony_ci * @count: Maximum count of bytes to copy. 91062306a36Sopenharmony_ci * @bytes_copied: Pointer to a variable that will receive the number of 91162306a36Sopenharmony_ci * bytes copied to @buf. 91262306a36Sopenharmony_ci * 91362306a36Sopenharmony_ci * On error @buf will contain any successfully copied trace buffer data 91462306a36Sopenharmony_ci * and bytes_copied will be set to the number of bytes successfully copied. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ciint ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count, 91862306a36Sopenharmony_ci unsigned long *bytes_copied) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci int result; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci *bytes_copied = 0; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!lpm_priv->tb_cache) 92562306a36Sopenharmony_ci return -EPERM; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (offset >= lpm_priv->tb_count) 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci count = min_t(u64, count, lpm_priv->tb_count - offset); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci while (*bytes_copied < count) { 93362306a36Sopenharmony_ci const unsigned long request = count - *bytes_copied; 93462306a36Sopenharmony_ci u64 tmp; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, 93762306a36Sopenharmony_ci request, &tmp); 93862306a36Sopenharmony_ci if (result) { 93962306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", 94062306a36Sopenharmony_ci __func__, __LINE__, request, offset); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " 94362306a36Sopenharmony_ci "failed: %s\n", __func__, __LINE__, 94462306a36Sopenharmony_ci ps3_result(result)); 94562306a36Sopenharmony_ci return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci memcpy(buf, lpm_priv->tb_cache, tmp); 94962306a36Sopenharmony_ci buf += tmp; 95062306a36Sopenharmony_ci *bytes_copied += tmp; 95162306a36Sopenharmony_ci offset += tmp; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, 95462306a36Sopenharmony_ci *bytes_copied); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_lpm_copy_tb); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci/** 96162306a36Sopenharmony_ci * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer. 96262306a36Sopenharmony_ci * @offset: Offset in bytes from the start of the trace buffer. 96362306a36Sopenharmony_ci * @buf: A __user copy destination. 96462306a36Sopenharmony_ci * @count: Maximum count of bytes to copy. 96562306a36Sopenharmony_ci * @bytes_copied: Pointer to a variable that will receive the number of 96662306a36Sopenharmony_ci * bytes copied to @buf. 96762306a36Sopenharmony_ci * 96862306a36Sopenharmony_ci * On error @buf will contain any successfully copied trace buffer data 96962306a36Sopenharmony_ci * and bytes_copied will be set to the number of bytes successfully copied. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ciint ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf, 97362306a36Sopenharmony_ci unsigned long count, unsigned long *bytes_copied) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci int result; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci *bytes_copied = 0; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (!lpm_priv->tb_cache) 98062306a36Sopenharmony_ci return -EPERM; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (offset >= lpm_priv->tb_count) 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci count = min_t(u64, count, lpm_priv->tb_count - offset); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci while (*bytes_copied < count) { 98862306a36Sopenharmony_ci const unsigned long request = count - *bytes_copied; 98962306a36Sopenharmony_ci u64 tmp; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, 99262306a36Sopenharmony_ci request, &tmp); 99362306a36Sopenharmony_ci if (result) { 99462306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", 99562306a36Sopenharmony_ci __func__, __LINE__, request, offset); 99662306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " 99762306a36Sopenharmony_ci "failed: %s\n", __func__, __LINE__, 99862306a36Sopenharmony_ci ps3_result(result)); 99962306a36Sopenharmony_ci return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci result = copy_to_user(buf, lpm_priv->tb_cache, tmp); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (result) { 100562306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n", 100662306a36Sopenharmony_ci __func__, __LINE__, tmp, buf); 100762306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n", 100862306a36Sopenharmony_ci __func__, __LINE__, result); 100962306a36Sopenharmony_ci return -EFAULT; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci buf += tmp; 101362306a36Sopenharmony_ci *bytes_copied += tmp; 101462306a36Sopenharmony_ci offset += tmp; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, 101762306a36Sopenharmony_ci *bytes_copied); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/** 102462306a36Sopenharmony_ci * ps3_get_and_clear_pm_interrupts - 102562306a36Sopenharmony_ci * 102662306a36Sopenharmony_ci * Clearing interrupts for the entire performance monitoring unit. 102762306a36Sopenharmony_ci * Reading pm_status clears the interrupt bits. 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ciu32 ps3_get_and_clear_pm_interrupts(u32 cpu) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci return ps3_read_pm(cpu, pm_status); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/** 103762306a36Sopenharmony_ci * ps3_enable_pm_interrupts - 103862306a36Sopenharmony_ci * 103962306a36Sopenharmony_ci * Enabling interrupts for the entire performance monitoring unit. 104062306a36Sopenharmony_ci * Enables the interrupt bits in the pm_status register. 104162306a36Sopenharmony_ci */ 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_civoid ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci if (mask) 104662306a36Sopenharmony_ci ps3_write_pm(cpu, pm_status, mask); 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci/** 105162306a36Sopenharmony_ci * ps3_enable_pm_interrupts - 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * Disabling interrupts for the entire performance monitoring unit. 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_civoid ps3_disable_pm_interrupts(u32 cpu) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci ps3_get_and_clear_pm_interrupts(cpu); 105962306a36Sopenharmony_ci ps3_write_pm(cpu, pm_status, 0); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/** 106462306a36Sopenharmony_ci * ps3_lpm_open - Open the logical performance monitor device. 106562306a36Sopenharmony_ci * @tb_type: Specifies the type of trace buffer lv1 should use for this lpm 106662306a36Sopenharmony_ci * instance, specified by one of enum ps3_lpm_tb_type. 106762306a36Sopenharmony_ci * @tb_cache: Optional user supplied buffer to use as the trace buffer cache. 106862306a36Sopenharmony_ci * If NULL, the driver will allocate and manage an internal buffer. 106962306a36Sopenharmony_ci * Unused when @tb_type is PS3_LPM_TB_TYPE_NONE. 107062306a36Sopenharmony_ci * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer. 107162306a36Sopenharmony_ci * Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ciint ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache, 107562306a36Sopenharmony_ci u64 tb_cache_size) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci int result; 107862306a36Sopenharmony_ci u64 tb_size; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci BUG_ON(!lpm_priv); 108162306a36Sopenharmony_ci BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE 108262306a36Sopenharmony_ci && tb_type != PS3_LPM_TB_TYPE_INTERNAL); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache) 108562306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (!atomic_add_unless(&lpm_priv->open, 1, 1)) { 108862306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__); 108962306a36Sopenharmony_ci return -EBUSY; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* Note tb_cache needs 128 byte alignment. */ 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (tb_type == PS3_LPM_TB_TYPE_NONE) { 109562306a36Sopenharmony_ci lpm_priv->tb_cache_size = 0; 109662306a36Sopenharmony_ci lpm_priv->tb_cache_internal = NULL; 109762306a36Sopenharmony_ci lpm_priv->tb_cache = NULL; 109862306a36Sopenharmony_ci } else if (tb_cache) { 109962306a36Sopenharmony_ci if (tb_cache != (void *)ALIGN((unsigned long)tb_cache, 128) 110062306a36Sopenharmony_ci || tb_cache_size != ALIGN(tb_cache_size, 128)) { 110162306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n", 110262306a36Sopenharmony_ci __func__, __LINE__); 110362306a36Sopenharmony_ci result = -EINVAL; 110462306a36Sopenharmony_ci goto fail_align; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci lpm_priv->tb_cache_size = tb_cache_size; 110762306a36Sopenharmony_ci lpm_priv->tb_cache_internal = NULL; 110862306a36Sopenharmony_ci lpm_priv->tb_cache = tb_cache; 110962306a36Sopenharmony_ci } else { 111062306a36Sopenharmony_ci lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE; 111162306a36Sopenharmony_ci lpm_priv->tb_cache_internal = kzalloc( 111262306a36Sopenharmony_ci lpm_priv->tb_cache_size + 127, GFP_KERNEL); 111362306a36Sopenharmony_ci if (!lpm_priv->tb_cache_internal) { 111462306a36Sopenharmony_ci result = -ENOMEM; 111562306a36Sopenharmony_ci goto fail_malloc; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci lpm_priv->tb_cache = (void *)ALIGN( 111862306a36Sopenharmony_ci (unsigned long)lpm_priv->tb_cache_internal, 128); 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0, 112262306a36Sopenharmony_ci ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)), 112362306a36Sopenharmony_ci lpm_priv->tb_cache_size, &lpm_priv->lpm_id, 112462306a36Sopenharmony_ci &lpm_priv->outlet_id, &tb_size); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (result) { 112762306a36Sopenharmony_ci dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n", 112862306a36Sopenharmony_ci __func__, __LINE__, ps3_result(result)); 112962306a36Sopenharmony_ci result = -EINVAL; 113062306a36Sopenharmony_ci goto fail_construct; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT; 113462306a36Sopenharmony_ci lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT; 113562306a36Sopenharmony_ci lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT; 113662306a36Sopenharmony_ci lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%llx, outlet_id 0x%llx, " 113962306a36Sopenharmony_ci "tb_size 0x%llx\n", __func__, __LINE__, lpm_priv->lpm_id, 114062306a36Sopenharmony_ci lpm_priv->outlet_id, tb_size); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cifail_construct: 114562306a36Sopenharmony_ci kfree(lpm_priv->tb_cache_internal); 114662306a36Sopenharmony_ci lpm_priv->tb_cache_internal = NULL; 114762306a36Sopenharmony_cifail_malloc: 114862306a36Sopenharmony_cifail_align: 114962306a36Sopenharmony_ci atomic_dec(&lpm_priv->open); 115062306a36Sopenharmony_ci return result; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_lpm_open); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci/** 115562306a36Sopenharmony_ci * ps3_lpm_close - Close the lpm device. 115662306a36Sopenharmony_ci * 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ciint ps3_lpm_close(void) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci lv1_destruct_lpm(lpm_priv->lpm_id); 116462306a36Sopenharmony_ci lpm_priv->lpm_id = 0; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci kfree(lpm_priv->tb_cache_internal); 116762306a36Sopenharmony_ci lpm_priv->tb_cache_internal = NULL; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci atomic_dec(&lpm_priv->open); 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_lpm_close); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic int ps3_lpm_probe(struct ps3_system_bus_device *dev) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (lpm_priv) { 117962306a36Sopenharmony_ci dev_info(&dev->core, "%s:%u: called twice\n", 118062306a36Sopenharmony_ci __func__, __LINE__); 118162306a36Sopenharmony_ci return -EBUSY; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (!lpm_priv) 118762306a36Sopenharmony_ci return -ENOMEM; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci lpm_priv->sbd = dev; 119062306a36Sopenharmony_ci lpm_priv->node_id = dev->lpm.node_id; 119162306a36Sopenharmony_ci lpm_priv->pu_id = dev->lpm.pu_id; 119262306a36Sopenharmony_ci lpm_priv->rights = dev->lpm.rights; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic void ps3_lpm_remove(struct ps3_system_bus_device *dev) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci ps3_lpm_close(); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci kfree(lpm_priv); 120662306a36Sopenharmony_ci lpm_priv = NULL; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic struct ps3_system_bus_driver ps3_lpm_driver = { 121262306a36Sopenharmony_ci .match_id = PS3_MATCH_ID_LPM, 121362306a36Sopenharmony_ci .core.name = "ps3-lpm", 121462306a36Sopenharmony_ci .core.owner = THIS_MODULE, 121562306a36Sopenharmony_ci .probe = ps3_lpm_probe, 121662306a36Sopenharmony_ci .remove = ps3_lpm_remove, 121762306a36Sopenharmony_ci .shutdown = ps3_lpm_remove, 121862306a36Sopenharmony_ci}; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic int __init ps3_lpm_init(void) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci pr_debug("%s:%d:\n", __func__, __LINE__); 122362306a36Sopenharmony_ci return ps3_system_bus_driver_register(&ps3_lpm_driver); 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic void __exit ps3_lpm_exit(void) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci pr_debug("%s:%d:\n", __func__, __LINE__); 122962306a36Sopenharmony_ci ps3_system_bus_driver_unregister(&ps3_lpm_driver); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cimodule_init(ps3_lpm_init); 123362306a36Sopenharmony_cimodule_exit(ps3_lpm_exit); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 123662306a36Sopenharmony_ciMODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver"); 123762306a36Sopenharmony_ciMODULE_AUTHOR("Sony Corporation"); 123862306a36Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_LPM); 1239