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