/*
* 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_DEBUG_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