18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file arch/alpha/oprofile/op_model_ev4.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 */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
118c2ecf20Sopenharmony_ci#include <linux/smp.h>
128c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "op_impl.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Compute all of the registers in preparation for enabling profiling.  */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void
208c2ecf20Sopenharmony_ciev4_reg_setup(struct op_register_config *reg,
218c2ecf20Sopenharmony_ci	      struct op_counter_config *ctr,
228c2ecf20Sopenharmony_ci	      struct op_system_config *sys)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	unsigned long ctl = 0, count, hilo;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* Select desired events.  We've mapped the event numbers
278c2ecf20Sopenharmony_ci	   such that they fit directly into the event selection fields.
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	   Note that there is no "off" setting.  In both cases we select
308c2ecf20Sopenharmony_ci	   the EXTERNAL event source, hoping that it'll be the lowest
318c2ecf20Sopenharmony_ci	   frequency, and set the frequency counter to LOW.  The interrupts
328c2ecf20Sopenharmony_ci	   for these "disabled" counter overflows are ignored by the
338c2ecf20Sopenharmony_ci	   interrupt handler.
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	   This is most irritating, because the hardware *can* enable and
368c2ecf20Sopenharmony_ci	   disable the interrupts for these counters independently, but the
378c2ecf20Sopenharmony_ci	   wrperfmon interface doesn't allow it.  */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ctl |= (ctr[0].enabled ? ctr[0].event << 8 : 14 << 8);
408c2ecf20Sopenharmony_ci	ctl |= (ctr[1].enabled ? (ctr[1].event - 16) << 32 : 7ul << 32);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* EV4 can not read or write its counter registers.  The only
438c2ecf20Sopenharmony_ci	   thing one can do at all is see if you overflow and get an
448c2ecf20Sopenharmony_ci	   interrupt.  We can set the width of the counters, to some
458c2ecf20Sopenharmony_ci	   extent.  Take the interrupt count selected by the user,
468c2ecf20Sopenharmony_ci	   map it onto one of the possible values, and write it back.  */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	count = ctr[0].count;
498c2ecf20Sopenharmony_ci	if (count <= 4096)
508c2ecf20Sopenharmony_ci		count = 4096, hilo = 1;
518c2ecf20Sopenharmony_ci	else
528c2ecf20Sopenharmony_ci		count = 65536, hilo = 0;
538c2ecf20Sopenharmony_ci	ctr[0].count = count;
548c2ecf20Sopenharmony_ci	ctl |= (ctr[0].enabled && hilo) << 3;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	count = ctr[1].count;
578c2ecf20Sopenharmony_ci	if (count <= 256)
588c2ecf20Sopenharmony_ci		count = 256, hilo = 1;
598c2ecf20Sopenharmony_ci	else
608c2ecf20Sopenharmony_ci		count = 4096, hilo = 0;
618c2ecf20Sopenharmony_ci	ctr[1].count = count;
628c2ecf20Sopenharmony_ci	ctl |= (ctr[1].enabled && hilo);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	reg->mux_select = ctl;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Select performance monitoring options.  */
678c2ecf20Sopenharmony_ci	/* ??? Need to come up with some mechanism to trace only
688c2ecf20Sopenharmony_ci	   selected processes.  EV4 does not have a mechanism to
698c2ecf20Sopenharmony_ci	   select kernel or user mode only.  For now, enable always.  */
708c2ecf20Sopenharmony_ci	reg->proc_mode = 0;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Frequency is folded into mux_select for EV4.  */
738c2ecf20Sopenharmony_ci	reg->freq = 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* See above regarding no writes.  */
768c2ecf20Sopenharmony_ci	reg->reset_values = 0;
778c2ecf20Sopenharmony_ci	reg->need_reset = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* Program all of the registers in preparation for enabling profiling.  */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void
848c2ecf20Sopenharmony_ciev4_cpu_setup(void *x)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct op_register_config *reg = x;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	wrperfmon(2, reg->mux_select);
898c2ecf20Sopenharmony_ci	wrperfmon(3, reg->proc_mode);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void
938c2ecf20Sopenharmony_ciev4_handle_interrupt(unsigned long which, struct pt_regs *regs,
948c2ecf20Sopenharmony_ci		     struct op_counter_config *ctr)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	/* EV4 can't properly disable counters individually.
978c2ecf20Sopenharmony_ci	   Discard "disabled" events now.  */
988c2ecf20Sopenharmony_ci	if (!ctr[which].enabled)
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Record the sample.  */
1028c2ecf20Sopenharmony_ci	oprofile_add_sample(regs, which);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistruct op_axp_model op_model_ev4 = {
1078c2ecf20Sopenharmony_ci	.reg_setup		= ev4_reg_setup,
1088c2ecf20Sopenharmony_ci	.cpu_setup		= ev4_cpu_setup,
1098c2ecf20Sopenharmony_ci	.reset_ctr		= NULL,
1108c2ecf20Sopenharmony_ci	.handle_interrupt	= ev4_handle_interrupt,
1118c2ecf20Sopenharmony_ci	.cpu_type		= "alpha/ev4",
1128c2ecf20Sopenharmony_ci	.num_counters		= 2,
1138c2ecf20Sopenharmony_ci	.can_set_proc_mode	= 0,
1148c2ecf20Sopenharmony_ci};
115