18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file arch/alpha/oprofile/op_model_ev67.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors
58c2ecf20Sopenharmony_ci * @remark Read the file COPYING
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @author Richard Henderson <rth@twiddle.net>
88c2ecf20Sopenharmony_ci * @author Falk Hueffner <falk@debian.org>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
128c2ecf20Sopenharmony_ci#include <linux/smp.h>
138c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "op_impl.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Compute all of the registers in preparation for enabling profiling.  */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic void
218c2ecf20Sopenharmony_ciev67_reg_setup(struct op_register_config *reg,
228c2ecf20Sopenharmony_ci	       struct op_counter_config *ctr,
238c2ecf20Sopenharmony_ci	       struct op_system_config *sys)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	unsigned long ctl, reset, need_reset, i;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	/* Select desired events.  */
288c2ecf20Sopenharmony_ci	ctl = 1UL << 4;		/* Enable ProfileMe mode. */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/* The event numbers are chosen so we can use them directly if
318c2ecf20Sopenharmony_ci	   PCTR1 is enabled.  */
328c2ecf20Sopenharmony_ci	if (ctr[1].enabled) {
338c2ecf20Sopenharmony_ci		ctl |= (ctr[1].event & 3) << 2;
348c2ecf20Sopenharmony_ci	} else {
358c2ecf20Sopenharmony_ci		if (ctr[0].event == 0) /* cycles */
368c2ecf20Sopenharmony_ci			ctl |= 1UL << 2;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci	reg->mux_select = ctl;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* Select logging options.  */
418c2ecf20Sopenharmony_ci	/* ??? Need to come up with some mechanism to trace only
428c2ecf20Sopenharmony_ci	   selected processes.  EV67 does not have a mechanism to
438c2ecf20Sopenharmony_ci	   select kernel or user mode only.  For now, enable always.  */
448c2ecf20Sopenharmony_ci	reg->proc_mode = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* EV67 cannot change the width of the counters as with the
478c2ecf20Sopenharmony_ci	   other implementations.  But fortunately, we can write to
488c2ecf20Sopenharmony_ci	   the counters and set the value such that it will overflow
498c2ecf20Sopenharmony_ci	   at the right time.  */
508c2ecf20Sopenharmony_ci	reset = need_reset = 0;
518c2ecf20Sopenharmony_ci	for (i = 0; i < 2; ++i) {
528c2ecf20Sopenharmony_ci		unsigned long count = ctr[i].count;
538c2ecf20Sopenharmony_ci		if (!ctr[i].enabled)
548c2ecf20Sopenharmony_ci			continue;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		if (count > 0x100000)
578c2ecf20Sopenharmony_ci			count = 0x100000;
588c2ecf20Sopenharmony_ci		ctr[i].count = count;
598c2ecf20Sopenharmony_ci		reset |= (0x100000 - count) << (i ? 6 : 28);
608c2ecf20Sopenharmony_ci		if (count != 0x100000)
618c2ecf20Sopenharmony_ci			need_reset |= 1 << i;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	reg->reset_values = reset;
648c2ecf20Sopenharmony_ci	reg->need_reset = need_reset;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* Program all of the registers in preparation for enabling profiling.  */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void
708c2ecf20Sopenharmony_ciev67_cpu_setup (void *x)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct op_register_config *reg = x;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	wrperfmon(2, reg->mux_select);
758c2ecf20Sopenharmony_ci	wrperfmon(3, reg->proc_mode);
768c2ecf20Sopenharmony_ci	wrperfmon(6, reg->reset_values | 3);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* CTR is a counter for which the user has requested an interrupt count
808c2ecf20Sopenharmony_ci   in between one of the widths selectable in hardware.  Reset the count
818c2ecf20Sopenharmony_ci   for CTR to the value stored in REG->RESET_VALUES.  */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void
848c2ecf20Sopenharmony_ciev67_reset_ctr(struct op_register_config *reg, unsigned long ctr)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	wrperfmon(6, reg->reset_values | (1 << ctr));
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* ProfileMe conditions which will show up as counters. We can also
908c2ecf20Sopenharmony_ci   detect the following, but it seems unlikely that anybody is
918c2ecf20Sopenharmony_ci   interested in counting them:
928c2ecf20Sopenharmony_ci    * Reset
938c2ecf20Sopenharmony_ci    * MT_FPCR (write to floating point control register)
948c2ecf20Sopenharmony_ci    * Arithmetic trap
958c2ecf20Sopenharmony_ci    * Dstream Fault
968c2ecf20Sopenharmony_ci    * Machine Check (ECC fault, etc.)
978c2ecf20Sopenharmony_ci    * OPCDEC (illegal opcode)
988c2ecf20Sopenharmony_ci    * Floating point disabled
998c2ecf20Sopenharmony_ci    * Differentiate between DTB single/double misses and 3 or 4 level
1008c2ecf20Sopenharmony_ci      page tables
1018c2ecf20Sopenharmony_ci    * Istream access violation
1028c2ecf20Sopenharmony_ci    * Interrupt
1038c2ecf20Sopenharmony_ci    * Icache Parity Error.
1048c2ecf20Sopenharmony_ci    * Instruction killed (nop, trapb)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci   Unfortunately, there seems to be no way to detect Dcache and Bcache
1078c2ecf20Sopenharmony_ci   misses; the latter could be approximated by making the counter
1088c2ecf20Sopenharmony_ci   count Bcache misses, but that is not precise.
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci   We model this as 20 counters:
1118c2ecf20Sopenharmony_ci    * PCTR0
1128c2ecf20Sopenharmony_ci    * PCTR1
1138c2ecf20Sopenharmony_ci    * 9 ProfileMe events, induced by PCTR0
1148c2ecf20Sopenharmony_ci    * 9 ProfileMe events, induced by PCTR1
1158c2ecf20Sopenharmony_ci*/
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cienum profileme_counters {
1188c2ecf20Sopenharmony_ci	PM_STALLED,		/* Stalled for at least one cycle
1198c2ecf20Sopenharmony_ci				   between the fetch and map stages  */
1208c2ecf20Sopenharmony_ci	PM_TAKEN,		/* Conditional branch taken */
1218c2ecf20Sopenharmony_ci	PM_MISPREDICT,		/* Branch caused mispredict trap */
1228c2ecf20Sopenharmony_ci	PM_ITB_MISS,		/* ITB miss */
1238c2ecf20Sopenharmony_ci	PM_DTB_MISS,		/* DTB miss */
1248c2ecf20Sopenharmony_ci	PM_REPLAY,		/* Replay trap */
1258c2ecf20Sopenharmony_ci	PM_LOAD_STORE,		/* Load-store order trap */
1268c2ecf20Sopenharmony_ci	PM_ICACHE_MISS,		/* Icache miss */
1278c2ecf20Sopenharmony_ci	PM_UNALIGNED,		/* Unaligned Load/Store */
1288c2ecf20Sopenharmony_ci	PM_NUM_COUNTERS
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline void
1328c2ecf20Sopenharmony_ciop_add_pm(unsigned long pc, int kern, unsigned long counter,
1338c2ecf20Sopenharmony_ci	  struct op_counter_config *ctr, unsigned long event)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	unsigned long fake_counter = 2 + event;
1368c2ecf20Sopenharmony_ci	if (counter == 1)
1378c2ecf20Sopenharmony_ci		fake_counter += PM_NUM_COUNTERS;
1388c2ecf20Sopenharmony_ci	if (ctr[fake_counter].enabled)
1398c2ecf20Sopenharmony_ci		oprofile_add_pc(pc, kern, fake_counter);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void
1438c2ecf20Sopenharmony_ciev67_handle_interrupt(unsigned long which, struct pt_regs *regs,
1448c2ecf20Sopenharmony_ci		      struct op_counter_config *ctr)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	unsigned long pmpc, pctr_ctl;
1478c2ecf20Sopenharmony_ci	int kern = !user_mode(regs);
1488c2ecf20Sopenharmony_ci	int mispredict = 0;
1498c2ecf20Sopenharmony_ci	union {
1508c2ecf20Sopenharmony_ci		unsigned long v;
1518c2ecf20Sopenharmony_ci		struct {
1528c2ecf20Sopenharmony_ci			unsigned reserved:	30; /*  0-29 */
1538c2ecf20Sopenharmony_ci			unsigned overcount:	 3; /* 30-32 */
1548c2ecf20Sopenharmony_ci			unsigned icache_miss:	 1; /*    33 */
1558c2ecf20Sopenharmony_ci			unsigned trap_type:	 4; /* 34-37 */
1568c2ecf20Sopenharmony_ci			unsigned load_store:	 1; /*    38 */
1578c2ecf20Sopenharmony_ci			unsigned trap:		 1; /*    39 */
1588c2ecf20Sopenharmony_ci			unsigned mispredict:	 1; /*    40 */
1598c2ecf20Sopenharmony_ci		} fields;
1608c2ecf20Sopenharmony_ci	} i_stat;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	enum trap_types {
1638c2ecf20Sopenharmony_ci		TRAP_REPLAY,
1648c2ecf20Sopenharmony_ci		TRAP_INVALID0,
1658c2ecf20Sopenharmony_ci		TRAP_DTB_DOUBLE_MISS_3,
1668c2ecf20Sopenharmony_ci		TRAP_DTB_DOUBLE_MISS_4,
1678c2ecf20Sopenharmony_ci		TRAP_FP_DISABLED,
1688c2ecf20Sopenharmony_ci		TRAP_UNALIGNED,
1698c2ecf20Sopenharmony_ci		TRAP_DTB_SINGLE_MISS,
1708c2ecf20Sopenharmony_ci		TRAP_DSTREAM_FAULT,
1718c2ecf20Sopenharmony_ci		TRAP_OPCDEC,
1728c2ecf20Sopenharmony_ci		TRAP_INVALID1,
1738c2ecf20Sopenharmony_ci		TRAP_MACHINE_CHECK,
1748c2ecf20Sopenharmony_ci		TRAP_INVALID2,
1758c2ecf20Sopenharmony_ci		TRAP_ARITHMETIC,
1768c2ecf20Sopenharmony_ci		TRAP_INVALID3,
1778c2ecf20Sopenharmony_ci		TRAP_MT_FPCR,
1788c2ecf20Sopenharmony_ci		TRAP_RESET
1798c2ecf20Sopenharmony_ci	};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	pmpc = wrperfmon(9, 0);
1828c2ecf20Sopenharmony_ci	/* ??? Don't know how to handle physical-mode PALcode address.  */
1838c2ecf20Sopenharmony_ci	if (pmpc & 1)
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci	pmpc &= ~2;		/* clear reserved bit */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	i_stat.v = wrperfmon(8, 0);
1888c2ecf20Sopenharmony_ci	if (i_stat.fields.trap) {
1898c2ecf20Sopenharmony_ci		switch (i_stat.fields.trap_type) {
1908c2ecf20Sopenharmony_ci		case TRAP_INVALID1:
1918c2ecf20Sopenharmony_ci		case TRAP_INVALID2:
1928c2ecf20Sopenharmony_ci		case TRAP_INVALID3:
1938c2ecf20Sopenharmony_ci			/* Pipeline redirection occurred. PMPC points
1948c2ecf20Sopenharmony_ci			   to PALcode. Recognize ITB miss by PALcode
1958c2ecf20Sopenharmony_ci			   offset address, and get actual PC from
1968c2ecf20Sopenharmony_ci			   EXC_ADDR.  */
1978c2ecf20Sopenharmony_ci			oprofile_add_pc(regs->pc, kern, which);
1988c2ecf20Sopenharmony_ci			if ((pmpc & ((1 << 15) - 1)) ==  581)
1998c2ecf20Sopenharmony_ci				op_add_pm(regs->pc, kern, which,
2008c2ecf20Sopenharmony_ci					  ctr, PM_ITB_MISS);
2018c2ecf20Sopenharmony_ci			/* Most other bit and counter values will be
2028c2ecf20Sopenharmony_ci			   those for the first instruction in the
2038c2ecf20Sopenharmony_ci			   fault handler, so we're done.  */
2048c2ecf20Sopenharmony_ci			return;
2058c2ecf20Sopenharmony_ci		case TRAP_REPLAY:
2068c2ecf20Sopenharmony_ci			op_add_pm(pmpc, kern, which, ctr,
2078c2ecf20Sopenharmony_ci				  (i_stat.fields.load_store
2088c2ecf20Sopenharmony_ci				   ? PM_LOAD_STORE : PM_REPLAY));
2098c2ecf20Sopenharmony_ci			break;
2108c2ecf20Sopenharmony_ci		case TRAP_DTB_DOUBLE_MISS_3:
2118c2ecf20Sopenharmony_ci		case TRAP_DTB_DOUBLE_MISS_4:
2128c2ecf20Sopenharmony_ci		case TRAP_DTB_SINGLE_MISS:
2138c2ecf20Sopenharmony_ci			op_add_pm(pmpc, kern, which, ctr, PM_DTB_MISS);
2148c2ecf20Sopenharmony_ci			break;
2158c2ecf20Sopenharmony_ci		case TRAP_UNALIGNED:
2168c2ecf20Sopenharmony_ci			op_add_pm(pmpc, kern, which, ctr, PM_UNALIGNED);
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci		case TRAP_INVALID0:
2198c2ecf20Sopenharmony_ci		case TRAP_FP_DISABLED:
2208c2ecf20Sopenharmony_ci		case TRAP_DSTREAM_FAULT:
2218c2ecf20Sopenharmony_ci		case TRAP_OPCDEC:
2228c2ecf20Sopenharmony_ci		case TRAP_MACHINE_CHECK:
2238c2ecf20Sopenharmony_ci		case TRAP_ARITHMETIC:
2248c2ecf20Sopenharmony_ci		case TRAP_MT_FPCR:
2258c2ecf20Sopenharmony_ci		case TRAP_RESET:
2268c2ecf20Sopenharmony_ci			break;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		/* ??? JSR/JMP/RET/COR or HW_JSR/HW_JMP/HW_RET/HW_COR
2308c2ecf20Sopenharmony_ci		   mispredicts do not set this bit but can be
2318c2ecf20Sopenharmony_ci		   recognized by the presence of one of these
2328c2ecf20Sopenharmony_ci		   instructions at the PMPC location with bit 39
2338c2ecf20Sopenharmony_ci		   set.  */
2348c2ecf20Sopenharmony_ci		if (i_stat.fields.mispredict) {
2358c2ecf20Sopenharmony_ci			mispredict = 1;
2368c2ecf20Sopenharmony_ci			op_add_pm(pmpc, kern, which, ctr, PM_MISPREDICT);
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	oprofile_add_pc(pmpc, kern, which);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	pctr_ctl = wrperfmon(5, 0);
2438c2ecf20Sopenharmony_ci	if (pctr_ctl & (1UL << 27))
2448c2ecf20Sopenharmony_ci		op_add_pm(pmpc, kern, which, ctr, PM_STALLED);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* Unfortunately, TAK is undefined on mispredicted branches.
2478c2ecf20Sopenharmony_ci	   ??? It is also undefined for non-cbranch insns, should
2488c2ecf20Sopenharmony_ci	   check that.  */
2498c2ecf20Sopenharmony_ci	if (!mispredict && pctr_ctl & (1UL << 0))
2508c2ecf20Sopenharmony_ci		op_add_pm(pmpc, kern, which, ctr, PM_TAKEN);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct op_axp_model op_model_ev67 = {
2548c2ecf20Sopenharmony_ci	.reg_setup		= ev67_reg_setup,
2558c2ecf20Sopenharmony_ci	.cpu_setup		= ev67_cpu_setup,
2568c2ecf20Sopenharmony_ci	.reset_ctr		= ev67_reset_ctr,
2578c2ecf20Sopenharmony_ci	.handle_interrupt	= ev67_handle_interrupt,
2588c2ecf20Sopenharmony_ci	.cpu_type		= "alpha/ev67",
2598c2ecf20Sopenharmony_ci	.num_counters		= 20,
2608c2ecf20Sopenharmony_ci	.can_set_proc_mode	= 0,
2618c2ecf20Sopenharmony_ci};
262