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