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