op_model_cell.c

来自「linux 内核源代码」· C语言 代码 · 共 1,212 行 · 第 1/3 页

C
1,212
字号
/* * Cell Broadband Engine OProfile Support * * (C) Copyright IBM Corporation 2006 * * Author: David Erb (djerb@us.ibm.com) * Modifications: *	   Carl Love <carll@us.ibm.com> *	   Maynard Johnson <maynardj@us.ibm.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/cpufreq.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/kthread.h>#include <linux/oprofile.h>#include <linux/percpu.h>#include <linux/smp.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <asm/cell-pmu.h>#include <asm/cputable.h>#include <asm/firmware.h>#include <asm/io.h>#include <asm/oprofile_impl.h>#include <asm/processor.h>#include <asm/prom.h>#include <asm/ptrace.h>#include <asm/reg.h>#include <asm/rtas.h>#include <asm/system.h>#include <asm/cell-regs.h>#include "../platforms/cell/interrupt.h"#include "cell/pr_util.h"static void cell_global_stop_spu(void);/* * spu_cycle_reset is the number of cycles between samples. * This variable is used for SPU profiling and should ONLY be set * at the beginning of cell_reg_setup; otherwise, it's read-only. */static unsigned int spu_cycle_reset;#define NUM_SPUS_PER_NODE    8#define SPU_CYCLES_EVENT_NUM 2	/*  event number for SPU_CYCLES */#define PPU_CYCLES_EVENT_NUM 1	/*  event number for CYCLES */#define PPU_CYCLES_GRP_NUM   1	/* special group number for identifying				 * PPU_CYCLES event				 */#define CBE_COUNT_ALL_CYCLES 0x42800000 /* PPU cycle event specifier */#define NUM_THREADS 2         /* number of physical threads in			       * physical processor			       */#define NUM_TRACE_BUS_WORDS 4#define NUM_INPUT_BUS_WORDS 2#define MAX_SPU_COUNT 0xFFFFFF	/* maximum 24 bit LFSR value */struct pmc_cntrl_data {	unsigned long vcntr;	unsigned long evnts;	unsigned long masks;	unsigned long enabled;};/* * ibm,cbe-perftools rtas parameters */struct pm_signal {	u16 cpu;		/* Processor to modify */	u16 sub_unit;		/* hw subunit this applies to (if applicable)*/	short int signal_group; /* Signal Group to Enable/Disable */	u8 bus_word;		/* Enable/Disable on this Trace/Trigger/Event				 * Bus Word(s) (bitmask)				 */	u8 bit;			/* Trigger/Event bit (if applicable) */};/* * rtas call arguments */enum {	SUBFUNC_RESET = 1,	SUBFUNC_ACTIVATE = 2,	SUBFUNC_DEACTIVATE = 3,	PASSTHRU_IGNORE = 0,	PASSTHRU_ENABLE = 1,	PASSTHRU_DISABLE = 2,};struct pm_cntrl {	u16 enable;	u16 stop_at_max;	u16 trace_mode;	u16 freeze;	u16 count_mode;};static struct {	u32 group_control;	u32 debug_bus_control;	struct pm_cntrl pm_cntrl;	u32 pm07_cntrl[NR_PHYS_CTRS];} pm_regs;#define GET_SUB_UNIT(x) ((x & 0x0000f000) >> 12)#define GET_BUS_WORD(x) ((x & 0x000000f0) >> 4)#define GET_BUS_TYPE(x) ((x & 0x00000300) >> 8)#define GET_POLARITY(x) ((x & 0x00000002) >> 1)#define GET_COUNT_CYCLES(x) (x & 0x00000001)#define GET_INPUT_CONTROL(x) ((x & 0x00000004) >> 2)static DEFINE_PER_CPU(unsigned long[NR_PHYS_CTRS], pmc_values);static struct pmc_cntrl_data pmc_cntrl[NUM_THREADS][NR_PHYS_CTRS];/* * The CELL profiling code makes rtas calls to setup the debug bus to * route the performance signals.  Additionally, SPU profiling requires * a second rtas call to setup the hardware to capture the SPU PCs. * The EIO error value is returned if the token lookups or the rtas * call fail.  The EIO error number is the best choice of the existing * error numbers.  The probability of rtas related error is very low.  But * by returning EIO and printing additional information to dmsg the user * will know that OProfile did not start and dmesg will tell them why. * OProfile does not support returning errors on Stop.	Not a huge issue * since failure to reset the debug bus or stop the SPU PC collection is * not a fatel issue.  Chances are if the Stop failed, Start doesn't work * either. *//* * Interpetation of hdw_thread: * 0 - even virtual cpus 0, 2, 4,... * 1 - odd virtual cpus 1, 3, 5, ... * * FIXME: this is strictly wrong, we need to clean this up in a number * of places. It works for now. -arnd */static u32 hdw_thread;static u32 virt_cntr_inter_mask;static struct timer_list timer_virt_cntr;/* * pm_signal needs to be global since it is initialized in * cell_reg_setup at the time when the necessary information * is available. */static struct pm_signal pm_signal[NR_PHYS_CTRS];static int pm_rtas_token;    /* token for debug bus setup call */static int spu_rtas_token;   /* token for SPU cycle profiling */static u32 reset_value[NR_PHYS_CTRS];static int num_counters;static int oprofile_running;static DEFINE_SPINLOCK(virt_cntr_lock);static u32 ctr_enabled;static unsigned char trace_bus[NUM_TRACE_BUS_WORDS];static unsigned char input_bus[NUM_INPUT_BUS_WORDS];/* * Firmware interface functions */static intrtas_ibm_cbe_perftools(int subfunc, int passthru,		       void *address, unsigned long length){	u64 paddr = __pa(address);	return rtas_call(pm_rtas_token, 5, 1, NULL, subfunc,			 passthru, paddr >> 32, paddr & 0xffffffff, length);}static void pm_rtas_reset_signals(u32 node){	int ret;	struct pm_signal pm_signal_local;	/*	 * The debug bus is being set to the passthru disable state.	 * However, the FW still expects atleast one legal signal routing	 * entry or it will return an error on the arguments.	If we don't	 * supply a valid entry, we must ignore all return values.  Ignoring	 * all return values means we might miss an error we should be	 * concerned about.	 */	/*  fw expects physical cpu #. */	pm_signal_local.cpu = node;	pm_signal_local.signal_group = 21;	pm_signal_local.bus_word = 1;	pm_signal_local.sub_unit = 0;	pm_signal_local.bit = 0;	ret = rtas_ibm_cbe_perftools(SUBFUNC_RESET, PASSTHRU_DISABLE,				     &pm_signal_local,				     sizeof(struct pm_signal));	if (unlikely(ret))		/*		 * Not a fatal error. For Oprofile stop, the oprofile		 * functions do not support returning an error for		 * failure to stop OProfile.		 */		printk(KERN_WARNING "%s: rtas returned: %d\n",		       __FUNCTION__, ret);}static int pm_rtas_activate_signals(u32 node, u32 count){	int ret;	int i, j;	struct pm_signal pm_signal_local[NR_PHYS_CTRS];	/*	 * There is no debug setup required for the cycles event.	 * Note that only events in the same group can be used.	 * Otherwise, there will be conflicts in correctly routing	 * the signals on the debug bus.  It is the responsiblity	 * of the OProfile user tool to check the events are in	 * the same group.	 */	i = 0;	for (j = 0; j < count; j++) {		if (pm_signal[j].signal_group != PPU_CYCLES_GRP_NUM) {			/* fw expects physical cpu # */			pm_signal_local[i].cpu = node;			pm_signal_local[i].signal_group				= pm_signal[j].signal_group;			pm_signal_local[i].bus_word = pm_signal[j].bus_word;			pm_signal_local[i].sub_unit = pm_signal[j].sub_unit;			pm_signal_local[i].bit = pm_signal[j].bit;			i++;		}	}	if (i != 0) {		ret = rtas_ibm_cbe_perftools(SUBFUNC_ACTIVATE, PASSTHRU_ENABLE,					     pm_signal_local,					     i * sizeof(struct pm_signal));		if (unlikely(ret)) {			printk(KERN_WARNING "%s: rtas returned: %d\n",			       __FUNCTION__, ret);			return -EIO;		}	}	return 0;}/* * PM Signal functions */static void set_pm_event(u32 ctr, int event, u32 unit_mask){	struct pm_signal *p;	u32 signal_bit;	u32 bus_word, bus_type, count_cycles, polarity, input_control;	int j, i;	if (event == PPU_CYCLES_EVENT_NUM) {		/* Special Event: Count all cpu cycles */		pm_regs.pm07_cntrl[ctr] = CBE_COUNT_ALL_CYCLES;		p = &(pm_signal[ctr]);		p->signal_group = PPU_CYCLES_GRP_NUM;		p->bus_word = 1;		p->sub_unit = 0;		p->bit = 0;		goto out;	} else {		pm_regs.pm07_cntrl[ctr] = 0;	}	bus_word = GET_BUS_WORD(unit_mask);	bus_type = GET_BUS_TYPE(unit_mask);	count_cycles = GET_COUNT_CYCLES(unit_mask);	polarity = GET_POLARITY(unit_mask);	input_control = GET_INPUT_CONTROL(unit_mask);	signal_bit = (event % 100);	p = &(pm_signal[ctr]);	p->signal_group = event / 100;	p->bus_word = bus_word;	p->sub_unit = (unit_mask & 0x0000f000) >> 12;	pm_regs.pm07_cntrl[ctr] = 0;	pm_regs.pm07_cntrl[ctr] |= PM07_CTR_COUNT_CYCLES(count_cycles);	pm_regs.pm07_cntrl[ctr] |= PM07_CTR_POLARITY(polarity);	pm_regs.pm07_cntrl[ctr] |= PM07_CTR_INPUT_CONTROL(input_control);	/*	 * Some of the islands signal selection is based on 64 bit words.	 * The debug bus words are 32 bits, the input words to the performance	 * counters are defined as 32 bits.  Need to convert the 64 bit island	 * specification to the appropriate 32 input bit and bus word for the	 * performance counter event selection.	 See the CELL Performance	 * monitoring signals manual and the Perf cntr hardware descriptions	 * for the details.	 */	if (input_control == 0) {		if (signal_bit > 31) {			signal_bit -= 32;			if (bus_word == 0x3)				bus_word = 0x2;			else if (bus_word == 0xc)				bus_word = 0x8;		}		if ((bus_type == 0) && p->signal_group >= 60)			bus_type = 2;		if ((bus_type == 1) && p->signal_group >= 50)			bus_type = 0;		pm_regs.pm07_cntrl[ctr] |= PM07_CTR_INPUT_MUX(signal_bit);	} else {		pm_regs.pm07_cntrl[ctr] = 0;		p->bit = signal_bit;	}	for (i = 0; i < NUM_TRACE_BUS_WORDS; i++) {		if (bus_word & (1 << i)) {			pm_regs.debug_bus_control |=			    (bus_type << (31 - (2 * i) + 1));			for (j = 0; j < NUM_INPUT_BUS_WORDS; j++) {				if (input_bus[j] == 0xff) {					input_bus[j] = i;					pm_regs.group_control |=					    (i << (31 - i));					break;				}			}		}	}out:	;}static void write_pm_cntrl(int cpu){	/*	 * Oprofile will use 32 bit counters, set bits 7:10 to 0	 * pmregs.pm_cntrl is a global	 */	u32 val = 0;	if (pm_regs.pm_cntrl.enable == 1)		val |= CBE_PM_ENABLE_PERF_MON;	if (pm_regs.pm_cntrl.stop_at_max == 1)		val |= CBE_PM_STOP_AT_MAX;	if (pm_regs.pm_cntrl.trace_mode == 1)		val |= CBE_PM_TRACE_MODE_SET(pm_regs.pm_cntrl.trace_mode);	if (pm_regs.pm_cntrl.freeze == 1)		val |= CBE_PM_FREEZE_ALL_CTRS;	/*	 * Routine set_count_mode must be called previously to set	 * the count mode based on the user selection of user and kernel.	 */	val |= CBE_PM_COUNT_MODE_SET(pm_regs.pm_cntrl.count_mode);	cbe_write_pm(cpu, pm_control, val);}static inline voidset_count_mode(u32 kernel, u32 user){	/*	 * The user must specify user and kernel if they want them. If	 *  neither is specified, OProfile will count in hypervisor mode.	 *  pm_regs.pm_cntrl is a global	 */	if (kernel) {		if (user)			pm_regs.pm_cntrl.count_mode = CBE_COUNT_ALL_MODES;		else			pm_regs.pm_cntrl.count_mode =				CBE_COUNT_SUPERVISOR_MODE;	} else {		if (user)			pm_regs.pm_cntrl.count_mode = CBE_COUNT_PROBLEM_MODE;		else			pm_regs.pm_cntrl.count_mode =				CBE_COUNT_HYPERVISOR_MODE;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?