xref: /OK3568_Linux_fs/kernel/arch/powerpc/oprofile/op_model_pa6t.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2006-2007 PA Semi, Inc
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Author: Shashi Rao, PA Semi
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Maintained by: Olof Johansson <olof@lixom.net>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Based on arch/powerpc/oprofile/op_model_power4.c
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/oprofile.h>
13*4882a593Smuzhiyun #include <linux/smp.h>
14*4882a593Smuzhiyun #include <linux/percpu.h>
15*4882a593Smuzhiyun #include <asm/processor.h>
16*4882a593Smuzhiyun #include <asm/cputable.h>
17*4882a593Smuzhiyun #include <asm/oprofile_impl.h>
18*4882a593Smuzhiyun #include <asm/reg.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static unsigned char oprofile_running;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */
23*4882a593Smuzhiyun static u64 mmcr0_val;
24*4882a593Smuzhiyun static u64 mmcr1_val;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /* inited in pa6t_reg_setup */
27*4882a593Smuzhiyun static u64 reset_value[OP_MAX_COUNTER];
28*4882a593Smuzhiyun 
ctr_read(unsigned int i)29*4882a593Smuzhiyun static inline u64 ctr_read(unsigned int i)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun 	switch (i) {
32*4882a593Smuzhiyun 	case 0:
33*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC0);
34*4882a593Smuzhiyun 	case 1:
35*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC1);
36*4882a593Smuzhiyun 	case 2:
37*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC2);
38*4882a593Smuzhiyun 	case 3:
39*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC3);
40*4882a593Smuzhiyun 	case 4:
41*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC4);
42*4882a593Smuzhiyun 	case 5:
43*4882a593Smuzhiyun 		return mfspr(SPRN_PA6T_PMC5);
44*4882a593Smuzhiyun 	default:
45*4882a593Smuzhiyun 		printk(KERN_ERR "ctr_read called with bad arg %u\n", i);
46*4882a593Smuzhiyun 		return 0;
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
ctr_write(unsigned int i,u64 val)50*4882a593Smuzhiyun static inline void ctr_write(unsigned int i, u64 val)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	switch (i) {
53*4882a593Smuzhiyun 	case 0:
54*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC0, val);
55*4882a593Smuzhiyun 		break;
56*4882a593Smuzhiyun 	case 1:
57*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC1, val);
58*4882a593Smuzhiyun 		break;
59*4882a593Smuzhiyun 	case 2:
60*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC2, val);
61*4882a593Smuzhiyun 		break;
62*4882a593Smuzhiyun 	case 3:
63*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC3, val);
64*4882a593Smuzhiyun 		break;
65*4882a593Smuzhiyun 	case 4:
66*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC4, val);
67*4882a593Smuzhiyun 		break;
68*4882a593Smuzhiyun 	case 5:
69*4882a593Smuzhiyun 		mtspr(SPRN_PA6T_PMC5, val);
70*4882a593Smuzhiyun 		break;
71*4882a593Smuzhiyun 	default:
72*4882a593Smuzhiyun 		printk(KERN_ERR "ctr_write called with bad arg %u\n", i);
73*4882a593Smuzhiyun 		break;
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /* precompute the values to stuff in the hardware registers */
pa6t_reg_setup(struct op_counter_config * ctr,struct op_system_config * sys,int num_ctrs)79*4882a593Smuzhiyun static int pa6t_reg_setup(struct op_counter_config *ctr,
80*4882a593Smuzhiyun 			   struct op_system_config *sys,
81*4882a593Smuzhiyun 			   int num_ctrs)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	int pmc;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	/*
86*4882a593Smuzhiyun 	 * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the
87*4882a593Smuzhiyun 	 * event_mappings file by turning off the counters that the user doesn't
88*4882a593Smuzhiyun 	 * care about
89*4882a593Smuzhiyun 	 *
90*4882a593Smuzhiyun 	 * setup user and kernel profiling
91*4882a593Smuzhiyun 	 */
92*4882a593Smuzhiyun 	for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++)
93*4882a593Smuzhiyun 		if (!ctr[pmc].enabled) {
94*4882a593Smuzhiyun 			sys->mmcr0 &= ~(0x1UL << pmc);
95*4882a593Smuzhiyun 			sys->mmcr0 &= ~(0x1UL << (pmc+12));
96*4882a593Smuzhiyun 			pr_debug("turned off counter %u\n", pmc);
97*4882a593Smuzhiyun 		}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (sys->enable_kernel)
100*4882a593Smuzhiyun 		sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN;
101*4882a593Smuzhiyun 	else
102*4882a593Smuzhiyun 		sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (sys->enable_user)
105*4882a593Smuzhiyun 		sys->mmcr0 |= PA6T_MMCR0_PREN;
106*4882a593Smuzhiyun 	else
107*4882a593Smuzhiyun 		sys->mmcr0 &= ~PA6T_MMCR0_PREN;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	/*
110*4882a593Smuzhiyun 	 * The performance counter event settings are given in the mmcr0 and
111*4882a593Smuzhiyun 	 * mmcr1 values passed from the user in the op_system_config
112*4882a593Smuzhiyun 	 * structure (sys variable).
113*4882a593Smuzhiyun 	 */
114*4882a593Smuzhiyun 	mmcr0_val = sys->mmcr0;
115*4882a593Smuzhiyun 	mmcr1_val = sys->mmcr1;
116*4882a593Smuzhiyun 	pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0);
117*4882a593Smuzhiyun 	pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) {
120*4882a593Smuzhiyun 		/* counters are 40 bit. Move to cputable at some point? */
121*4882a593Smuzhiyun 		reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count;
122*4882a593Smuzhiyun 		pr_debug("reset_value for pmc%u inited to 0x%llx\n",
123*4882a593Smuzhiyun 				 pmc, reset_value[pmc]);
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	return 0;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun /* configure registers on this cpu */
pa6t_cpu_setup(struct op_counter_config * ctr)130*4882a593Smuzhiyun static int pa6t_cpu_setup(struct op_counter_config *ctr)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	u64 mmcr0 = mmcr0_val;
133*4882a593Smuzhiyun 	u64 mmcr1 = mmcr1_val;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	/* Default is all PMCs off */
136*4882a593Smuzhiyun 	mmcr0 &= ~(0x3FUL);
137*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR0, mmcr0);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	/* program selected programmable events in */
140*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR1, mmcr1);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(),
143*4882a593Smuzhiyun 		mfspr(SPRN_PA6T_MMCR0));
144*4882a593Smuzhiyun 	pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(),
145*4882a593Smuzhiyun 		mfspr(SPRN_PA6T_MMCR1));
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
pa6t_start(struct op_counter_config * ctr)150*4882a593Smuzhiyun static int pa6t_start(struct op_counter_config *ctr)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	int i;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	/* Hold off event counting until rfid */
155*4882a593Smuzhiyun 	u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	for (i = 0; i < cur_cpu_spec->num_pmcs; i++)
158*4882a593Smuzhiyun 		if (ctr[i].enabled)
159*4882a593Smuzhiyun 			ctr_write(i, reset_value[i]);
160*4882a593Smuzhiyun 		else
161*4882a593Smuzhiyun 			ctr_write(i, 0UL);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR0, mmcr0);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	oprofile_running = 1;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	pr_debug("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
pa6t_stop(void)172*4882a593Smuzhiyun static void pa6t_stop(void)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	u64 mmcr0;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	/* freeze counters */
177*4882a593Smuzhiyun 	mmcr0 = mfspr(SPRN_PA6T_MMCR0);
178*4882a593Smuzhiyun 	mmcr0 |= PA6T_MMCR0_FCM0;
179*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR0, mmcr0);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	oprofile_running = 0;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	pr_debug("stop on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun /* handle the perfmon overflow vector */
pa6t_handle_interrupt(struct pt_regs * regs,struct op_counter_config * ctr)187*4882a593Smuzhiyun static void pa6t_handle_interrupt(struct pt_regs *regs,
188*4882a593Smuzhiyun 				  struct op_counter_config *ctr)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	unsigned long pc = mfspr(SPRN_PA6T_SIAR);
191*4882a593Smuzhiyun 	int is_kernel = is_kernel_addr(pc);
192*4882a593Smuzhiyun 	u64 val;
193*4882a593Smuzhiyun 	int i;
194*4882a593Smuzhiyun 	u64 mmcr0;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	/* disable perfmon counting until rfid */
197*4882a593Smuzhiyun 	mmcr0 = mfspr(SPRN_PA6T_MMCR0);
198*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	/* Record samples. We've got one global bit for whether a sample
201*4882a593Smuzhiyun 	 * was taken, so add it for any counter that triggered overflow.
202*4882a593Smuzhiyun 	 */
203*4882a593Smuzhiyun 	for (i = 0; i < cur_cpu_spec->num_pmcs; i++) {
204*4882a593Smuzhiyun 		val = ctr_read(i);
205*4882a593Smuzhiyun 		if (val & (0x1UL << 39)) { /* Overflow bit set */
206*4882a593Smuzhiyun 			if (oprofile_running && ctr[i].enabled) {
207*4882a593Smuzhiyun 				if (mmcr0 & PA6T_MMCR0_SIARLOG)
208*4882a593Smuzhiyun 					oprofile_add_ext_sample(pc, regs, i, is_kernel);
209*4882a593Smuzhiyun 				ctr_write(i, reset_value[i]);
210*4882a593Smuzhiyun 			} else {
211*4882a593Smuzhiyun 				ctr_write(i, 0UL);
212*4882a593Smuzhiyun 			}
213*4882a593Smuzhiyun 		}
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	/* Restore mmcr0 to a good known value since the PMI changes it */
217*4882a593Smuzhiyun 	mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS;
218*4882a593Smuzhiyun 	mtspr(SPRN_PA6T_MMCR0, mmcr0);
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun struct op_powerpc_model op_model_pa6t = {
222*4882a593Smuzhiyun 	.reg_setup		= pa6t_reg_setup,
223*4882a593Smuzhiyun 	.cpu_setup		= pa6t_cpu_setup,
224*4882a593Smuzhiyun 	.start			= pa6t_start,
225*4882a593Smuzhiyun 	.stop			= pa6t_stop,
226*4882a593Smuzhiyun 	.handle_interrupt	= pa6t_handle_interrupt,
227*4882a593Smuzhiyun };
228