1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PPC 64 oprofile support:
4 * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
5 * PPC 32 oprofile support: (based on PPC 64 support)
6 * Copyright (C) Freescale Semiconductor, Inc 2004
7 * Author: Andy Fleming
8 *
9 * Based on alpha version.
10 */
11
12 #include <linux/oprofile.h>
13 #include <linux/init.h>
14 #include <linux/smp.h>
15 #include <linux/errno.h>
16 #include <asm/ptrace.h>
17 #include <asm/pmc.h>
18 #include <asm/cputable.h>
19 #include <asm/oprofile_impl.h>
20 #include <asm/firmware.h>
21
22 static struct op_powerpc_model *model;
23
24 static struct op_counter_config ctr[OP_MAX_COUNTER];
25 static struct op_system_config sys;
26
27 static int op_per_cpu_rc;
28
op_handle_interrupt(struct pt_regs * regs)29 static void op_handle_interrupt(struct pt_regs *regs)
30 {
31 model->handle_interrupt(regs, ctr);
32 }
33
op_powerpc_cpu_setup(void * dummy)34 static void op_powerpc_cpu_setup(void *dummy)
35 {
36 int ret;
37
38 ret = model->cpu_setup(ctr);
39
40 if (ret != 0)
41 op_per_cpu_rc = ret;
42 }
43
op_powerpc_setup(void)44 static int op_powerpc_setup(void)
45 {
46 int err;
47
48 op_per_cpu_rc = 0;
49
50 /* Grab the hardware */
51 err = reserve_pmc_hardware(op_handle_interrupt);
52 if (err)
53 return err;
54
55 /* Pre-compute the values to stuff in the hardware registers. */
56 op_per_cpu_rc = model->reg_setup(ctr, &sys, model->num_counters);
57
58 if (op_per_cpu_rc)
59 goto out;
60
61 /* Configure the registers on all cpus. If an error occurs on one
62 * of the cpus, op_per_cpu_rc will be set to the error */
63 on_each_cpu(op_powerpc_cpu_setup, NULL, 1);
64
65 out: if (op_per_cpu_rc) {
66 /* error on setup release the performance counter hardware */
67 release_pmc_hardware();
68 }
69
70 return op_per_cpu_rc;
71 }
72
op_powerpc_shutdown(void)73 static void op_powerpc_shutdown(void)
74 {
75 release_pmc_hardware();
76 }
77
op_powerpc_cpu_start(void * dummy)78 static void op_powerpc_cpu_start(void *dummy)
79 {
80 /* If any of the cpus have return an error, set the
81 * global flag to the error so it can be returned
82 * to the generic OProfile caller.
83 */
84 int ret;
85
86 ret = model->start(ctr);
87 if (ret != 0)
88 op_per_cpu_rc = ret;
89 }
90
op_powerpc_start(void)91 static int op_powerpc_start(void)
92 {
93 op_per_cpu_rc = 0;
94
95 if (model->global_start)
96 return model->global_start(ctr);
97 if (model->start) {
98 on_each_cpu(op_powerpc_cpu_start, NULL, 1);
99 return op_per_cpu_rc;
100 }
101 return -EIO; /* No start function is defined for this
102 power architecture */
103 }
104
op_powerpc_cpu_stop(void * dummy)105 static inline void op_powerpc_cpu_stop(void *dummy)
106 {
107 model->stop();
108 }
109
op_powerpc_stop(void)110 static void op_powerpc_stop(void)
111 {
112 if (model->stop)
113 on_each_cpu(op_powerpc_cpu_stop, NULL, 1);
114 if (model->global_stop)
115 model->global_stop();
116 }
117
op_powerpc_create_files(struct dentry * root)118 static int op_powerpc_create_files(struct dentry *root)
119 {
120 int i;
121
122 #ifdef CONFIG_PPC64
123 /*
124 * There is one mmcr0, mmcr1 and mmcra for setting the events for
125 * all of the counters.
126 */
127 oprofilefs_create_ulong(root, "mmcr0", &sys.mmcr0);
128 oprofilefs_create_ulong(root, "mmcr1", &sys.mmcr1);
129 oprofilefs_create_ulong(root, "mmcra", &sys.mmcra);
130 #ifdef CONFIG_OPROFILE_CELL
131 /* create a file the user tool can check to see what level of profiling
132 * support exits with this kernel. Initialize bit mask to indicate
133 * what support the kernel has:
134 * bit 0 - Supports SPU event profiling in addition to PPU
135 * event and cycles; and SPU cycle profiling
136 * bits 1-31 - Currently unused.
137 *
138 * If the file does not exist, then the kernel only supports SPU
139 * cycle profiling, PPU event and cycle profiling.
140 */
141 oprofilefs_create_ulong(root, "cell_support", &sys.cell_support);
142 sys.cell_support = 0x1; /* Note, the user OProfile tool must check
143 * that this bit is set before attempting to
144 * user SPU event profiling. Older kernels
145 * will not have this file, hence the user
146 * tool is not allowed to do SPU event
147 * profiling on older kernels. Older kernels
148 * will accept SPU events but collected data
149 * is garbage.
150 */
151 #endif
152 #endif
153
154 for (i = 0; i < model->num_counters; ++i) {
155 struct dentry *dir;
156 char buf[4];
157
158 snprintf(buf, sizeof buf, "%d", i);
159 dir = oprofilefs_mkdir(root, buf);
160
161 oprofilefs_create_ulong(dir, "enabled", &ctr[i].enabled);
162 oprofilefs_create_ulong(dir, "event", &ctr[i].event);
163 oprofilefs_create_ulong(dir, "count", &ctr[i].count);
164
165 /*
166 * Classic PowerPC doesn't support per-counter
167 * control like this, but the options are
168 * expected, so they remain. For Freescale
169 * Book-E style performance monitors, we do
170 * support them.
171 */
172 oprofilefs_create_ulong(dir, "kernel", &ctr[i].kernel);
173 oprofilefs_create_ulong(dir, "user", &ctr[i].user);
174
175 oprofilefs_create_ulong(dir, "unit_mask", &ctr[i].unit_mask);
176 }
177
178 oprofilefs_create_ulong(root, "enable_kernel", &sys.enable_kernel);
179 oprofilefs_create_ulong(root, "enable_user", &sys.enable_user);
180
181 /* Default to tracing both kernel and user */
182 sys.enable_kernel = 1;
183 sys.enable_user = 1;
184
185 return 0;
186 }
187
oprofile_arch_init(struct oprofile_operations * ops)188 int __init oprofile_arch_init(struct oprofile_operations *ops)
189 {
190 if (!cur_cpu_spec->oprofile_cpu_type)
191 return -ENODEV;
192
193 switch (cur_cpu_spec->oprofile_type) {
194 #ifdef CONFIG_PPC_BOOK3S_64
195 #ifdef CONFIG_OPROFILE_CELL
196 case PPC_OPROFILE_CELL:
197 if (firmware_has_feature(FW_FEATURE_LPAR))
198 return -ENODEV;
199 model = &op_model_cell;
200 ops->sync_start = model->sync_start;
201 ops->sync_stop = model->sync_stop;
202 break;
203 #endif
204 case PPC_OPROFILE_POWER4:
205 model = &op_model_power4;
206 break;
207 case PPC_OPROFILE_PA6T:
208 model = &op_model_pa6t;
209 break;
210 #endif
211 #ifdef CONFIG_PPC_BOOK3S_32
212 case PPC_OPROFILE_G4:
213 model = &op_model_7450;
214 break;
215 #endif
216 #if defined(CONFIG_FSL_EMB_PERFMON)
217 case PPC_OPROFILE_FSL_EMB:
218 model = &op_model_fsl_emb;
219 break;
220 #endif
221 default:
222 return -ENODEV;
223 }
224
225 model->num_counters = cur_cpu_spec->num_pmcs;
226
227 ops->cpu_type = cur_cpu_spec->oprofile_cpu_type;
228 ops->create_files = op_powerpc_create_files;
229 ops->setup = op_powerpc_setup;
230 ops->shutdown = op_powerpc_shutdown;
231 ops->start = op_powerpc_start;
232 ops->stop = op_powerpc_stop;
233 ops->backtrace = op_powerpc_backtrace;
234
235 printk(KERN_DEBUG "oprofile: using %s performance monitoring.\n",
236 ops->cpu_type);
237
238 return 0;
239 }
240
oprofile_arch_exit(void)241 void oprofile_arch_exit(void)
242 {
243 }
244