brintos

brintos / linux-shallow public Read only

0
0
Text · 30.5 KiB · 200ad87 Raw
1239 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 Logical Performance Monitor.4 *5 *  Copyright (C) 2007 Sony Computer Entertainment Inc.6 *  Copyright 2007 Sony Corp.7 */8 9#include <linux/slab.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/interrupt.h>13#include <linux/uaccess.h>14#include <asm/smp.h>15#include <asm/time.h>16#include <asm/ps3.h>17#include <asm/lv1call.h>18#include <asm/cell-pmu.h>19 20 21/* BOOKMARK tag macros */22#define PS3_PM_BOOKMARK_START                    0x8000000000000000ULL23#define PS3_PM_BOOKMARK_STOP                     0x4000000000000000ULL24#define PS3_PM_BOOKMARK_TAG_KERNEL               0x1000000000000000ULL25#define PS3_PM_BOOKMARK_TAG_USER                 0x3000000000000000ULL26#define PS3_PM_BOOKMARK_TAG_MASK_HI              0xF000000000000000ULL27#define PS3_PM_BOOKMARK_TAG_MASK_LO              0x0F00000000000000ULL28 29/* CBE PM CONTROL register macros */30#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK          0x0000100031#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK          0x0000080032#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK       0x000C000033#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM    0x0008000034#define PS3_WRITE_PM_MASK                        0xFFFFFFFFFFFFFFFFULL35 36/* CBE PM START STOP register macros */37#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x0200000038#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x0100000039#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP  0x0002000040#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP  0x0001000041#define PS3_PM_START_STOP_START_MASK             0xFF00000042#define PS3_PM_START_STOP_STOP_MASK              0x00FF000043 44/* CBE PM COUNTER register macres */45#define PS3_PM_COUNTER_MASK_HI                   0xFFFFFFFF00000000ULL46#define PS3_PM_COUNTER_MASK_LO                   0x00000000FFFFFFFFULL47 48/* BASE SIGNAL GROUP NUMBER macros */49#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER  050#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1      651#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2      752#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER  753#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER  1554#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER   1755#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER     1856#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER  1857#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER  2458#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER  4959#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER  5260#define PM_SIG_GROUP_SPU                     4161#define PM_SIG_GROUP_SPU_TRIGGER             4262#define PM_SIG_GROUP_SPU_EVENT               4363#define PM_SIG_GROUP_MFC_MAX                 6064 65/**66 * struct ps3_lpm_shadow_regs - Performance monitor shadow registers.67 *68 * @pm_control: Shadow of the processor's pm_control register.69 * @pm_start_stop: Shadow of the processor's pm_start_stop register.70 * @group_control: Shadow of the processor's group_control register.71 * @debug_bus_control: Shadow of the processor's debug_bus_control register.72 *73 * The logical performance monitor provides a write-only interface to74 * these processor registers.  These shadow variables cache the processor75 * register values for reading.76 *77 * The initial value of the shadow registers at lpm creation is78 * PS3_LPM_SHADOW_REG_INIT.79 */80 81struct ps3_lpm_shadow_regs {82	u64 pm_control;83	u64 pm_start_stop;84	u64 group_control;85	u64 debug_bus_control;86};87 88#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL89 90/**91 * struct ps3_lpm_priv - Private lpm device data.92 *93 * @open: An atomic variable indicating the lpm driver has been opened.94 * @rights: The lpm rigths granted by the system policy module.  A logical95 *  OR of enum ps3_lpm_rights.96 * @node_id: The node id of a BE processor whose performance monitor this97 *  lpar has the right to use.98 * @pu_id: The lv1 id of the logical PU.99 * @lpm_id: The lv1 id of this lpm instance.100 * @outlet_id: The outlet created by lv1 for this lpm instance.101 * @tb_count: The number of bytes of data held in the lv1 trace buffer.102 * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer.103 *  Must be 128 byte aligned.104 * @tb_cache_size: Size of the kernel @tb_cache buffer.  Must be 128 byte105 *  aligned.106 * @tb_cache_internal: An unaligned buffer allocated by this driver to be107 *  used for the trace buffer cache when ps3_lpm_open() is called with a108 *  NULL tb_cache argument.  Otherwise unused.109 * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs.110 * @sbd: The struct ps3_system_bus_device attached to this driver.111 *112 * The trace buffer is a buffer allocated and used internally to the lv1113 * hypervisor to collect trace data.  The trace buffer cache is a guest114 * buffer that accepts the trace data from the trace buffer.115 */116 117struct ps3_lpm_priv {118	atomic_t open;119	u64 rights;120	u64 node_id;121	u64 pu_id;122	u64 lpm_id;123	u64 outlet_id;124	u64 tb_count;125	void *tb_cache;126	u64 tb_cache_size;127	void *tb_cache_internal;128	struct ps3_lpm_shadow_regs shadow;129	struct ps3_system_bus_device *sbd;130};131 132enum {133	PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000,134};135 136/**137 * lpm_priv - Static instance of the lpm data.138 *139 * Since the exported routines don't support the notion of a device140 * instance we need to hold the instance in this static variable141 * and then only allow at most one instance at a time to be created.142 */143 144static struct ps3_lpm_priv *lpm_priv;145 146static struct device *sbd_core(void)147{148	BUG_ON(!lpm_priv || !lpm_priv->sbd);149	return &lpm_priv->sbd->core;150}151 152/**153 * use_start_stop_bookmark - Enable the PPU bookmark trace.154 *155 * And it enables PPU bookmark triggers ONLY if the other triggers are not set.156 * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm()157 * to start/stop LPM.158 *159 * Used to get good quality of the performance counter.160 */161 162enum {use_start_stop_bookmark = 1,};163 164void ps3_set_bookmark(u64 bookmark)165{166	/*167	 * As per the PPE book IV, to avoid bookmark loss there must168	 * not be a traced branch within 10 cycles of setting the169	 * SPRN_BKMK register.  The actual text is unclear if 'within'170	 * includes cycles before the call.171	 */172 173	asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");174	mtspr(SPRN_BKMK, bookmark);175	asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;");176}177EXPORT_SYMBOL_GPL(ps3_set_bookmark);178 179void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id)180{181	u64 bookmark;182 183	bookmark = (get_tb() & 0x00000000FFFFFFFFULL) |184		PS3_PM_BOOKMARK_TAG_KERNEL;185	bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) |186		(incident << 48) | (th_id << 32) | bookmark;187	ps3_set_bookmark(bookmark);188}189EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark);190 191/**192 * ps3_read_phys_ctr - Read physical counter registers.193 *194 * Each physical counter can act as one 32 bit counter or as two 16 bit195 * counters.196 */197 198u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr)199{200	int result;201	u64 counter0415;202	u64 counter2637;203 204	if (phys_ctr >= NR_PHYS_CTRS) {205		dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,206			__LINE__, phys_ctr);207		return 0;208	}209 210	result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415,211				     &counter2637);212	if (result) {213		dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "214			"phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr,215			ps3_result(result));216		return 0;217	}218 219	switch (phys_ctr) {220	case 0:221		return counter0415 >> 32;222	case 1:223		return counter0415 & PS3_PM_COUNTER_MASK_LO;224	case 2:225		return counter2637 >> 32;226	case 3:227		return counter2637 & PS3_PM_COUNTER_MASK_LO;228	default:229		BUG();230	}231	return 0;232}233EXPORT_SYMBOL_GPL(ps3_read_phys_ctr);234 235/**236 * ps3_write_phys_ctr - Write physical counter registers.237 *238 * Each physical counter can act as one 32 bit counter or as two 16 bit239 * counters.240 */241 242void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val)243{244	u64 counter0415;245	u64 counter0415_mask;246	u64 counter2637;247	u64 counter2637_mask;248	int result;249 250	if (phys_ctr >= NR_PHYS_CTRS) {251		dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,252			__LINE__, phys_ctr);253		return;254	}255 256	switch (phys_ctr) {257	case 0:258		counter0415 = (u64)val << 32;259		counter0415_mask = PS3_PM_COUNTER_MASK_HI;260		counter2637 = 0x0;261		counter2637_mask = 0x0;262		break;263	case 1:264		counter0415 = (u64)val;265		counter0415_mask = PS3_PM_COUNTER_MASK_LO;266		counter2637 = 0x0;267		counter2637_mask = 0x0;268		break;269	case 2:270		counter0415 = 0x0;271		counter0415_mask = 0x0;272		counter2637 = (u64)val << 32;273		counter2637_mask = PS3_PM_COUNTER_MASK_HI;274		break;275	case 3:276		counter0415 = 0x0;277		counter0415_mask = 0x0;278		counter2637 = (u64)val;279		counter2637_mask = PS3_PM_COUNTER_MASK_LO;280		break;281	default:282		BUG();283	}284 285	result = lv1_set_lpm_counter(lpm_priv->lpm_id,286				     counter0415, counter0415_mask,287				     counter2637, counter2637_mask,288				     &counter0415, &counter2637);289	if (result)290		dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: "291			"phys_ctr %u, val %u, %s\n", __func__, __LINE__,292			phys_ctr, val, ps3_result(result));293}294EXPORT_SYMBOL_GPL(ps3_write_phys_ctr);295 296/**297 * ps3_read_ctr - Read counter.298 *299 * Read 16 or 32 bits depending on the current size of the counter.300 * Counters 4, 5, 6 & 7 are always 16 bit.301 */302 303u32 ps3_read_ctr(u32 cpu, u32 ctr)304{305	u32 val;306	u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1);307 308	val = ps3_read_phys_ctr(cpu, phys_ctr);309 310	if (ps3_get_ctr_size(cpu, phys_ctr) == 16)311		val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff);312 313	return val;314}315EXPORT_SYMBOL_GPL(ps3_read_ctr);316 317/**318 * ps3_write_ctr - Write counter.319 *320 * Write 16 or 32 bits depending on the current size of the counter.321 * Counters 4, 5, 6 & 7 are always 16 bit.322 */323 324void ps3_write_ctr(u32 cpu, u32 ctr, u32 val)325{326	u32 phys_ctr;327	u32 phys_val;328 329	phys_ctr = ctr & (NR_PHYS_CTRS - 1);330 331	if (ps3_get_ctr_size(cpu, phys_ctr) == 16) {332		phys_val = ps3_read_phys_ctr(cpu, phys_ctr);333 334		if (ctr < NR_PHYS_CTRS)335			val = (val << 16) | (phys_val & 0xffff);336		else337			val = (val & 0xffff) | (phys_val & 0xffff0000);338	}339 340	ps3_write_phys_ctr(cpu, phys_ctr, val);341}342EXPORT_SYMBOL_GPL(ps3_write_ctr);343 344/**345 * ps3_read_pm07_control - Read counter control registers.346 *347 * Each logical counter has a corresponding control register.348 */349 350u32 ps3_read_pm07_control(u32 cpu, u32 ctr)351{352	return 0;353}354EXPORT_SYMBOL_GPL(ps3_read_pm07_control);355 356/**357 * ps3_write_pm07_control - Write counter control registers.358 *359 * Each logical counter has a corresponding control register.360 */361 362void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val)363{364	int result;365	static const u64 mask = 0xFFFFFFFFFFFFFFFFULL;366	u64 old_value;367 368	if (ctr >= NR_CTRS) {369		dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__,370			__LINE__, ctr);371		return;372	}373 374	result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask,375					     &old_value);376	if (result)377		dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control "378			"failed: ctr %u, %s\n", __func__, __LINE__, ctr,379			ps3_result(result));380}381EXPORT_SYMBOL_GPL(ps3_write_pm07_control);382 383/**384 * ps3_read_pm - Read Other LPM control registers.385 */386 387u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg)388{389	int result = 0;390	u64 val = 0;391 392	switch (reg) {393	case pm_control:394		return lpm_priv->shadow.pm_control;395	case trace_address:396		return CBE_PM_TRACE_BUF_EMPTY;397	case pm_start_stop:398		return lpm_priv->shadow.pm_start_stop;399	case pm_interval:400		result = lv1_set_lpm_interval(lpm_priv->lpm_id, 0, 0, &val);401		if (result) {402			val = 0;403			dev_dbg(sbd_core(), "%s:%u: lv1 set_interval failed: "404				"reg %u, %s\n", __func__, __LINE__, reg,405				ps3_result(result));406		}407		return (u32)val;408	case group_control:409		return lpm_priv->shadow.group_control;410	case debug_bus_control:411		return lpm_priv->shadow.debug_bus_control;412	case pm_status:413		result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id,414						      &val);415		if (result) {416			val = 0;417			dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: "418				"reg %u, %s\n", __func__, __LINE__, reg,419				ps3_result(result));420		}421		return (u32)val;422	case ext_tr_timer:423		return 0;424	default:425		dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,426			__LINE__, reg);427		BUG();428		break;429	}430 431	return 0;432}433EXPORT_SYMBOL_GPL(ps3_read_pm);434 435/**436 * ps3_write_pm - Write Other LPM control registers.437 */438 439void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val)440{441	int result = 0;442	u64 dummy;443 444	switch (reg) {445	case group_control:446		if (val != lpm_priv->shadow.group_control)447			result = lv1_set_lpm_group_control(lpm_priv->lpm_id,448							   val,449							   PS3_WRITE_PM_MASK,450							   &dummy);451		lpm_priv->shadow.group_control = val;452		break;453	case debug_bus_control:454		if (val != lpm_priv->shadow.debug_bus_control)455			result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id,456							      val,457							      PS3_WRITE_PM_MASK,458							      &dummy);459		lpm_priv->shadow.debug_bus_control = val;460		break;461	case pm_control:462		if (use_start_stop_bookmark)463			val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK |464				PS3_PM_CONTROL_PPU_TH1_BOOKMARK);465		if (val != lpm_priv->shadow.pm_control)466			result = lv1_set_lpm_general_control(lpm_priv->lpm_id,467							     val,468							     PS3_WRITE_PM_MASK,469							     0, 0, &dummy,470							     &dummy);471		lpm_priv->shadow.pm_control = val;472		break;473	case pm_interval:474		result = lv1_set_lpm_interval(lpm_priv->lpm_id, val,475					      PS3_WRITE_PM_MASK, &dummy);476		break;477	case pm_start_stop:478		if (val != lpm_priv->shadow.pm_start_stop)479			result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,480							     val,481							     PS3_WRITE_PM_MASK,482							     &dummy);483		lpm_priv->shadow.pm_start_stop = val;484		break;485	case trace_address:486	case ext_tr_timer:487	case pm_status:488		break;489	default:490		dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__,491			__LINE__, reg);492		BUG();493		break;494	}495 496	if (result)497		dev_err(sbd_core(), "%s:%u: lv1 set_control failed: "498			"reg %u, %s\n", __func__, __LINE__, reg,499			ps3_result(result));500}501EXPORT_SYMBOL_GPL(ps3_write_pm);502 503/**504 * ps3_get_ctr_size - Get the size of a physical counter.505 *506 * Returns either 16 or 32.507 */508 509u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr)510{511	u32 pm_ctrl;512 513	if (phys_ctr >= NR_PHYS_CTRS) {514		dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,515			__LINE__, phys_ctr);516		return 0;517	}518 519	pm_ctrl = ps3_read_pm(cpu, pm_control);520	return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32;521}522EXPORT_SYMBOL_GPL(ps3_get_ctr_size);523 524/**525 * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits.526 */527 528void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size)529{530	u32 pm_ctrl;531 532	if (phys_ctr >= NR_PHYS_CTRS) {533		dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__,534			__LINE__, phys_ctr);535		return;536	}537 538	pm_ctrl = ps3_read_pm(cpu, pm_control);539 540	switch (ctr_size) {541	case 16:542		pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr);543		ps3_write_pm(cpu, pm_control, pm_ctrl);544		break;545 546	case 32:547		pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr);548		ps3_write_pm(cpu, pm_control, pm_ctrl);549		break;550	default:551		BUG();552	}553}554EXPORT_SYMBOL_GPL(ps3_set_ctr_size);555 556static u64 pm_translate_signal_group_number_on_island2(u64 subgroup)557{558 559	if (subgroup == 2)560		subgroup = 3;561 562	if (subgroup <= 6)563		return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup;564	else if (subgroup == 7)565		return PM_ISLAND2_SIGNAL_GROUP_NUMBER1;566	else567		return PM_ISLAND2_SIGNAL_GROUP_NUMBER2;568}569 570static u64 pm_translate_signal_group_number_on_island3(u64 subgroup)571{572 573	switch (subgroup) {574	case 2:575	case 3:576	case 4:577		subgroup += 2;578		break;579	case 5:580		subgroup = 8;581		break;582	default:583		break;584	}585	return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup;586}587 588static u64 pm_translate_signal_group_number_on_island4(u64 subgroup)589{590	return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup;591}592 593static u64 pm_translate_signal_group_number_on_island5(u64 subgroup)594{595 596	switch (subgroup) {597	case 3:598		subgroup = 4;599		break;600	case 4:601		subgroup = 6;602		break;603	default:604		break;605	}606	return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup;607}608 609static u64 pm_translate_signal_group_number_on_island6(u64 subgroup,610						       u64 subsubgroup)611{612	switch (subgroup) {613	case 3:614	case 4:615	case 5:616		subgroup += 1;617		break;618	default:619		break;620	}621 622	switch (subsubgroup) {623	case 4:624	case 5:625	case 6:626		subsubgroup += 2;627		break;628	case 7:629	case 8:630	case 9:631	case 10:632		subsubgroup += 4;633		break;634	case 11:635	case 12:636	case 13:637		subsubgroup += 5;638		break;639	default:640		break;641	}642 643	if (subgroup <= 5)644		return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup);645	else646		return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup647			+ subsubgroup - 1);648}649 650static u64 pm_translate_signal_group_number_on_island7(u64 subgroup)651{652	return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup;653}654 655static u64 pm_translate_signal_group_number_on_island8(u64 subgroup)656{657	return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup;658}659 660static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group)661{662	u64 island;663	u64 subgroup;664	u64 subsubgroup;665 666	subgroup = 0;667	subsubgroup = 0;668	island = 0;669	if (group < 1000) {670		if (group < 100) {671			if (20 <= group && group < 30) {672				island = 2;673				subgroup = group - 20;674			} else if (30 <= group && group < 40) {675				island = 3;676				subgroup = group - 30;677			} else if (40 <= group && group < 50) {678				island = 4;679				subgroup = group - 40;680			} else if (50 <= group && group < 60) {681				island = 5;682				subgroup = group - 50;683			} else if (60 <= group && group < 70) {684				island = 6;685				subgroup = group - 60;686			} else if (70 <= group && group < 80) {687				island = 7;688				subgroup = group - 70;689			} else if (80 <= group && group < 90) {690				island = 8;691				subgroup = group - 80;692			}693		} else if (200 <= group && group < 300) {694			island = 2;695			subgroup = group - 200;696		} else if (600 <= group && group < 700) {697			island = 6;698			subgroup = 5;699			subsubgroup = group - 650;700		}701	} else if (6000 <= group && group < 7000) {702		island = 6;703		subgroup = 5;704		subsubgroup = group - 6500;705	}706 707	switch (island) {708	case 2:709		return pm_translate_signal_group_number_on_island2(subgroup);710	case 3:711		return pm_translate_signal_group_number_on_island3(subgroup);712	case 4:713		return pm_translate_signal_group_number_on_island4(subgroup);714	case 5:715		return pm_translate_signal_group_number_on_island5(subgroup);716	case 6:717		return pm_translate_signal_group_number_on_island6(subgroup,718								   subsubgroup);719	case 7:720		return pm_translate_signal_group_number_on_island7(subgroup);721	case 8:722		return pm_translate_signal_group_number_on_island8(subgroup);723	default:724		dev_dbg(sbd_core(), "%s:%u: island not found: %llu\n", __func__,725			__LINE__, group);726		BUG();727		break;728	}729	return 0;730}731 732static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word)733{734 735	switch (word) {736	case 1:737		return 0xF000;738	case 2:739		return 0x0F00;740	case 4:741		return 0x00F0;742	case 8:743	default:744		return 0x000F;745	}746}747 748static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select,749			    u64 signal_select, u64 attr1, u64 attr2, u64 attr3)750{751	int ret;752 753	ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select,754				 signal_select, attr1, attr2, attr3);755	if (ret)756		dev_err(sbd_core(),757			"%s:%u: error:%d 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n",758			__func__, __LINE__, ret, lv1_signal_group, bus_select,759			signal_select, attr1, attr2, attr3);760 761	return ret;762}763 764int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit,765		   u8 bus_word)766{767	int ret;768	u64 lv1_signal_group;769	u64 bus_select;770	u64 signal_select;771	u64 attr1, attr2, attr3;772 773	if (signal_group == 0)774		return __ps3_set_signal(0, 0, 0, 0, 0, 0);775 776	lv1_signal_group =777		pm_signal_group_to_ps3_lv1_signal_group(signal_group);778	bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word);779 780	switch (signal_group) {781	case PM_SIG_GROUP_SPU_TRIGGER:782		signal_select = 1;783		signal_select = signal_select << (63 - signal_bit);784		break;785	case PM_SIG_GROUP_SPU_EVENT:786		signal_select = 1;787		signal_select = (signal_select << (63 - signal_bit)) | 0x3;788		break;789	default:790		signal_select = 0;791		break;792	}793 794	/*795	 * 0: physical object.796	 * 1: logical object.797	 * This parameter is only used for the PPE and SPE signals.798	 */799	attr1 = 1;800 801	/*802	 * This parameter is used to specify the target physical/logical803	 * PPE/SPE object.804	 */805	if (PM_SIG_GROUP_SPU <= signal_group &&806		signal_group < PM_SIG_GROUP_MFC_MAX)807		attr2 = sub_unit;808	else809		attr2 = lpm_priv->pu_id;810 811	/*812	 * This parameter is only used for setting the SPE signal.813	 */814	attr3 = 0;815 816	ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select,817			       attr1, attr2, attr3);818	if (ret)819		dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n",820			__func__, __LINE__, ret);821 822	return ret;823}824EXPORT_SYMBOL_GPL(ps3_set_signal);825 826u32 ps3_get_hw_thread_id(int cpu)827{828	return get_hard_smp_processor_id(cpu);829}830EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id);831 832/**833 * ps3_enable_pm - Enable the entire performance monitoring unit.834 *835 * When we enable the LPM, all pending writes to counters get committed.836 */837 838void ps3_enable_pm(u32 cpu)839{840	int result;841	u64 tmp;842	int insert_bookmark = 0;843 844	lpm_priv->tb_count = 0;845 846	if (use_start_stop_bookmark) {847		if (!(lpm_priv->shadow.pm_start_stop &848			(PS3_PM_START_STOP_START_MASK849			| PS3_PM_START_STOP_STOP_MASK))) {850			result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id,851				(PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START |852				PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START |853				PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP |854				PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP),855				0xFFFFFFFFFFFFFFFFULL, &tmp);856 857			if (result)858				dev_err(sbd_core(), "%s:%u: "859					"lv1_set_lpm_trigger_control failed: "860					"%s\n", __func__, __LINE__,861					ps3_result(result));862 863			insert_bookmark = !result;864		}865	}866 867	result = lv1_start_lpm(lpm_priv->lpm_id);868 869	if (result)870		dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n",871			__func__, __LINE__, ps3_result(result));872 873	if (use_start_stop_bookmark && !result && insert_bookmark)874		ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START);875}876EXPORT_SYMBOL_GPL(ps3_enable_pm);877 878/**879 * ps3_disable_pm - Disable the entire performance monitoring unit.880 */881 882void ps3_disable_pm(u32 cpu)883{884	int result;885	u64 tmp;886 887	ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP);888 889	result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp);890 891	if (result) {892		if (result != LV1_WRONG_STATE)893			dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n",894				__func__, __LINE__, ps3_result(result));895		return;896	}897 898	lpm_priv->tb_count = tmp;899 900	dev_dbg(sbd_core(), "%s:%u: tb_count %llu (%llxh)\n", __func__, __LINE__,901		lpm_priv->tb_count, lpm_priv->tb_count);902}903EXPORT_SYMBOL_GPL(ps3_disable_pm);904 905/**906 * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer.907 * @offset: Offset in bytes from the start of the trace buffer.908 * @buf: Copy destination.909 * @count: Maximum count of bytes to copy.910 * @bytes_copied: Pointer to a variable that will receive the number of911 *  bytes copied to @buf.912 *913 * On error @buf will contain any successfully copied trace buffer data914 * and bytes_copied will be set to the number of bytes successfully copied.915 */916 917int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count,918		    unsigned long *bytes_copied)919{920	int result;921 922	*bytes_copied = 0;923 924	if (!lpm_priv->tb_cache)925		return -EPERM;926 927	if (offset >= lpm_priv->tb_count)928		return 0;929 930	count = min_t(u64, count, lpm_priv->tb_count - offset);931 932	while (*bytes_copied < count) {933		const unsigned long request = count - *bytes_copied;934		u64 tmp;935 936		result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,937						   request, &tmp);938		if (result) {939			dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",940				__func__, __LINE__, request, offset);941 942			dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "943				"failed: %s\n", __func__, __LINE__,944				ps3_result(result));945			return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;946		}947 948		memcpy(buf, lpm_priv->tb_cache, tmp);949		buf += tmp;950		*bytes_copied += tmp;951		offset += tmp;952	}953	dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,954		*bytes_copied);955 956	return 0;957}958EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb);959 960/**961 * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer.962 * @offset: Offset in bytes from the start of the trace buffer.963 * @buf: A __user copy destination.964 * @count: Maximum count of bytes to copy.965 * @bytes_copied: Pointer to a variable that will receive the number of966 *  bytes copied to @buf.967 *968 * On error @buf will contain any successfully copied trace buffer data969 * and bytes_copied will be set to the number of bytes successfully copied.970 */971 972int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,973			    unsigned long count, unsigned long *bytes_copied)974{975	int result;976 977	*bytes_copied = 0;978 979	if (!lpm_priv->tb_cache)980		return -EPERM;981 982	if (offset >= lpm_priv->tb_count)983		return 0;984 985	count = min_t(u64, count, lpm_priv->tb_count - offset);986 987	while (*bytes_copied < count) {988		const unsigned long request = count - *bytes_copied;989		u64 tmp;990 991		result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset,992						   request, &tmp);993		if (result) {994			dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n",995				__func__, __LINE__, request, offset);996			dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer "997				"failed: %s\n", __func__, __LINE__,998				ps3_result(result));999			return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;1000		}1001 1002		result = copy_to_user(buf, lpm_priv->tb_cache, tmp);1003 1004		if (result) {1005			dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n",1006				__func__, __LINE__, tmp, buf);1007			dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n",1008				__func__, __LINE__, result);1009			return -EFAULT;1010		}1011 1012		buf += tmp;1013		*bytes_copied += tmp;1014		offset += tmp;1015	}1016	dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__,1017		*bytes_copied);1018 1019	return 0;1020}1021EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user);1022 1023/**1024 * ps3_get_and_clear_pm_interrupts -1025 *1026 * Clearing interrupts for the entire performance monitoring unit.1027 * Reading pm_status clears the interrupt bits.1028 */1029 1030u32 ps3_get_and_clear_pm_interrupts(u32 cpu)1031{1032	return ps3_read_pm(cpu, pm_status);1033}1034EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts);1035 1036/**1037 * ps3_enable_pm_interrupts -1038 *1039 * Enabling interrupts for the entire performance monitoring unit.1040 * Enables the interrupt bits in the pm_status register.1041 */1042 1043void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask)1044{1045	if (mask)1046		ps3_write_pm(cpu, pm_status, mask);1047}1048EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts);1049 1050/**1051 * ps3_enable_pm_interrupts -1052 *1053 * Disabling interrupts for the entire performance monitoring unit.1054 */1055 1056void ps3_disable_pm_interrupts(u32 cpu)1057{1058	ps3_get_and_clear_pm_interrupts(cpu);1059	ps3_write_pm(cpu, pm_status, 0);1060}1061EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts);1062 1063/**1064 * ps3_lpm_open - Open the logical performance monitor device.1065 * @tb_type: Specifies the type of trace buffer lv1 should use for this lpm1066 *  instance, specified by one of enum ps3_lpm_tb_type.1067 * @tb_cache: Optional user supplied buffer to use as the trace buffer cache.1068 *  If NULL, the driver will allocate and manage an internal buffer.1069 *  Unused when @tb_type is PS3_LPM_TB_TYPE_NONE.1070 * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer.1071 *  Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE.1072 */1073 1074int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache,1075	u64 tb_cache_size)1076{1077	int result;1078	u64 tb_size;1079 1080	BUG_ON(!lpm_priv);1081	BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE1082		&& tb_type != PS3_LPM_TB_TYPE_INTERNAL);1083 1084	if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache)1085		dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__);1086 1087	if (!atomic_add_unless(&lpm_priv->open, 1, 1)) {1088		dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__);1089		return -EBUSY;1090	}1091 1092	/* Note tb_cache needs 128 byte alignment. */1093 1094	if (tb_type == PS3_LPM_TB_TYPE_NONE) {1095		lpm_priv->tb_cache_size = 0;1096		lpm_priv->tb_cache_internal = NULL;1097		lpm_priv->tb_cache = NULL;1098	} else if (tb_cache) {1099		if (tb_cache != (void *)ALIGN((unsigned long)tb_cache, 128)1100			|| tb_cache_size != ALIGN(tb_cache_size, 128)) {1101			dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n",1102				__func__, __LINE__);1103			result = -EINVAL;1104			goto fail_align;1105		}1106		lpm_priv->tb_cache_size = tb_cache_size;1107		lpm_priv->tb_cache_internal = NULL;1108		lpm_priv->tb_cache = tb_cache;1109	} else {1110		lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE;1111		lpm_priv->tb_cache_internal = kzalloc(1112			lpm_priv->tb_cache_size + 127, GFP_KERNEL);1113		if (!lpm_priv->tb_cache_internal) {1114			result = -ENOMEM;1115			goto fail_malloc;1116		}1117		lpm_priv->tb_cache = (void *)ALIGN(1118			(unsigned long)lpm_priv->tb_cache_internal, 128);1119	}1120 1121	result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0,1122				ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)),1123				lpm_priv->tb_cache_size, &lpm_priv->lpm_id,1124				&lpm_priv->outlet_id, &tb_size);1125 1126	if (result) {1127		dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n",1128			__func__, __LINE__, ps3_result(result));1129		result = -EINVAL;1130		goto fail_construct;1131	}1132 1133	lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT;1134	lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT;1135	lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT;1136	lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT;1137 1138	dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%llx, outlet_id 0x%llx, "1139		"tb_size 0x%llx\n", __func__, __LINE__, lpm_priv->lpm_id,1140		lpm_priv->outlet_id, tb_size);1141 1142	return 0;1143 1144fail_construct:1145	kfree(lpm_priv->tb_cache_internal);1146	lpm_priv->tb_cache_internal = NULL;1147fail_malloc:1148fail_align:1149	atomic_dec(&lpm_priv->open);1150	return result;1151}1152EXPORT_SYMBOL_GPL(ps3_lpm_open);1153 1154/**1155 * ps3_lpm_close - Close the lpm device.1156 *1157 */1158 1159int ps3_lpm_close(void)1160{1161	dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__);1162 1163	lv1_destruct_lpm(lpm_priv->lpm_id);1164	lpm_priv->lpm_id = 0;1165 1166	kfree(lpm_priv->tb_cache_internal);1167	lpm_priv->tb_cache_internal = NULL;1168 1169	atomic_dec(&lpm_priv->open);1170	return 0;1171}1172EXPORT_SYMBOL_GPL(ps3_lpm_close);1173 1174static int ps3_lpm_probe(struct ps3_system_bus_device *dev)1175{1176	dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__);1177 1178	if (lpm_priv) {1179		dev_info(&dev->core, "%s:%u: called twice\n",1180			__func__, __LINE__);1181		return -EBUSY;1182	}1183 1184	lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL);1185 1186	if (!lpm_priv)1187		return -ENOMEM;1188 1189	lpm_priv->sbd = dev;1190	lpm_priv->node_id = dev->lpm.node_id;1191	lpm_priv->pu_id = dev->lpm.pu_id;1192	lpm_priv->rights = dev->lpm.rights;1193 1194	dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);1195 1196	return 0;1197}1198 1199static void ps3_lpm_remove(struct ps3_system_bus_device *dev)1200{1201	dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__);1202 1203	ps3_lpm_close();1204 1205	kfree(lpm_priv);1206	lpm_priv = NULL;1207 1208	dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__);1209}1210 1211static struct ps3_system_bus_driver ps3_lpm_driver = {1212	.match_id = PS3_MATCH_ID_LPM,1213	.core.name	= "ps3-lpm",1214	.core.owner	= THIS_MODULE,1215	.probe		= ps3_lpm_probe,1216	.remove		= ps3_lpm_remove,1217	.shutdown	= ps3_lpm_remove,1218};1219 1220static int __init ps3_lpm_init(void)1221{1222	pr_debug("%s:%d:\n", __func__, __LINE__);1223	return ps3_system_bus_driver_register(&ps3_lpm_driver);1224}1225 1226static void __exit ps3_lpm_exit(void)1227{1228	pr_debug("%s:%d:\n", __func__, __LINE__);1229	ps3_system_bus_driver_unregister(&ps3_lpm_driver);1230}1231 1232module_init(ps3_lpm_init);1233module_exit(ps3_lpm_exit);1234 1235MODULE_LICENSE("GPL v2");1236MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver");1237MODULE_AUTHOR("Sony Corporation");1238MODULE_ALIAS(PS3_MODULE_ALIAS_LPM);1239