]>
Commit | Line | Data |
---|---|---|
2e1cdfe1 PP |
1 | /* Copyright (c) 2014, The Linux Foundation. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/moduleparam.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/types.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/err.h> | |
20 | #include <linux/fs.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/smp.h> | |
24 | #include <linux/sysfs.h> | |
25 | #include <linux/stat.h> | |
26 | #include <linux/clk.h> | |
27 | #include <linux/cpu.h> | |
28 | #include <linux/coresight.h> | |
fc208abe | 29 | #include <linux/coresight-pmu.h> |
2e1cdfe1 PP |
30 | #include <linux/pm_wakeup.h> |
31 | #include <linux/amba/bus.h> | |
32 | #include <linux/seq_file.h> | |
33 | #include <linux/uaccess.h> | |
37fbbdbd | 34 | #include <linux/perf_event.h> |
2e1cdfe1 PP |
35 | #include <linux/pm_runtime.h> |
36 | #include <asm/sections.h> | |
c38a9ec2 | 37 | #include <asm/local.h> |
2e1cdfe1 PP |
38 | |
39 | #include "coresight-etm4x.h" | |
37fbbdbd | 40 | #include "coresight-etm-perf.h" |
2e1cdfe1 PP |
41 | |
42 | static int boot_enable; | |
43 | module_param_named(boot_enable, boot_enable, int, S_IRUGO); | |
44 | ||
45 | /* The number of ETMv4 currently registered */ | |
46 | static int etm4_count; | |
47 | static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; | |
37fbbdbd | 48 | static void etm4_set_default(struct etmv4_config *config); |
2e1cdfe1 | 49 | |
58eb457b SAS |
50 | static enum cpuhp_state hp_online; |
51 | ||
66bbbb77 | 52 | static void etm4_os_unlock(struct etmv4_drvdata *drvdata) |
2e1cdfe1 | 53 | { |
2e1cdfe1 PP |
54 | /* Writing any value to ETMOSLAR unlocks the trace registers */ |
55 | writel_relaxed(0x0, drvdata->base + TRCOSLAR); | |
66bbbb77 | 56 | drvdata->os_unlock = true; |
2e1cdfe1 PP |
57 | isb(); |
58 | } | |
59 | ||
60 | static bool etm4_arch_supported(u8 arch) | |
61 | { | |
62 | switch (arch) { | |
63 | case ETM_ARCH_V4: | |
64 | break; | |
65 | default: | |
66 | return false; | |
67 | } | |
68 | return true; | |
69 | } | |
70 | ||
52210c87 MP |
71 | static int etm4_cpu_id(struct coresight_device *csdev) |
72 | { | |
73 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
74 | ||
75 | return drvdata->cpu; | |
76 | } | |
77 | ||
2e1cdfe1 PP |
78 | static int etm4_trace_id(struct coresight_device *csdev) |
79 | { | |
80 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
2e1cdfe1 | 81 | |
b1149ad9 | 82 | return drvdata->trcid; |
2e1cdfe1 PP |
83 | } |
84 | ||
85 | static void etm4_enable_hw(void *info) | |
86 | { | |
87 | int i; | |
88 | struct etmv4_drvdata *drvdata = info; | |
54ff892b | 89 | struct etmv4_config *config = &drvdata->config; |
2e1cdfe1 PP |
90 | |
91 | CS_UNLOCK(drvdata->base); | |
92 | ||
93 | etm4_os_unlock(drvdata); | |
94 | ||
95 | /* Disable the trace unit before programming trace registers */ | |
96 | writel_relaxed(0, drvdata->base + TRCPRGCTLR); | |
97 | ||
98 | /* wait for TRCSTATR.IDLE to go up */ | |
99 | if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) | |
100 | dev_err(drvdata->dev, | |
67337e8d | 101 | "timeout while waiting for Idle Trace Status\n"); |
2e1cdfe1 | 102 | |
54ff892b MP |
103 | writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); |
104 | writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR); | |
2e1cdfe1 PP |
105 | /* nothing specific implemented */ |
106 | writel_relaxed(0x0, drvdata->base + TRCAUXCTLR); | |
54ff892b MP |
107 | writel_relaxed(config->eventctrl0, drvdata->base + TRCEVENTCTL0R); |
108 | writel_relaxed(config->eventctrl1, drvdata->base + TRCEVENTCTL1R); | |
109 | writel_relaxed(config->stall_ctrl, drvdata->base + TRCSTALLCTLR); | |
110 | writel_relaxed(config->ts_ctrl, drvdata->base + TRCTSCTLR); | |
111 | writel_relaxed(config->syncfreq, drvdata->base + TRCSYNCPR); | |
112 | writel_relaxed(config->ccctlr, drvdata->base + TRCCCCTLR); | |
113 | writel_relaxed(config->bb_ctrl, drvdata->base + TRCBBCTLR); | |
2e1cdfe1 | 114 | writel_relaxed(drvdata->trcid, drvdata->base + TRCTRACEIDR); |
54ff892b MP |
115 | writel_relaxed(config->vinst_ctrl, drvdata->base + TRCVICTLR); |
116 | writel_relaxed(config->viiectlr, drvdata->base + TRCVIIECTLR); | |
117 | writel_relaxed(config->vissctlr, | |
2e1cdfe1 | 118 | drvdata->base + TRCVISSCTLR); |
54ff892b | 119 | writel_relaxed(config->vipcssctlr, |
2e1cdfe1 PP |
120 | drvdata->base + TRCVIPCSSCTLR); |
121 | for (i = 0; i < drvdata->nrseqstate - 1; i++) | |
54ff892b | 122 | writel_relaxed(config->seq_ctrl[i], |
2e1cdfe1 | 123 | drvdata->base + TRCSEQEVRn(i)); |
54ff892b MP |
124 | writel_relaxed(config->seq_rst, drvdata->base + TRCSEQRSTEVR); |
125 | writel_relaxed(config->seq_state, drvdata->base + TRCSEQSTR); | |
126 | writel_relaxed(config->ext_inp, drvdata->base + TRCEXTINSELR); | |
2e1cdfe1 | 127 | for (i = 0; i < drvdata->nr_cntr; i++) { |
54ff892b | 128 | writel_relaxed(config->cntrldvr[i], |
2e1cdfe1 | 129 | drvdata->base + TRCCNTRLDVRn(i)); |
54ff892b | 130 | writel_relaxed(config->cntr_ctrl[i], |
2e1cdfe1 | 131 | drvdata->base + TRCCNTCTLRn(i)); |
54ff892b | 132 | writel_relaxed(config->cntr_val[i], |
2e1cdfe1 PP |
133 | drvdata->base + TRCCNTVRn(i)); |
134 | } | |
497b5956 CZ |
135 | |
136 | /* Resource selector pair 0 is always implemented and reserved */ | |
54ff892b MP |
137 | for (i = 0; i < drvdata->nr_resource * 2; i++) |
138 | writel_relaxed(config->res_ctrl[i], | |
2e1cdfe1 PP |
139 | drvdata->base + TRCRSCTLRn(i)); |
140 | ||
141 | for (i = 0; i < drvdata->nr_ss_cmp; i++) { | |
54ff892b | 142 | writel_relaxed(config->ss_ctrl[i], |
2e1cdfe1 | 143 | drvdata->base + TRCSSCCRn(i)); |
54ff892b | 144 | writel_relaxed(config->ss_status[i], |
2e1cdfe1 | 145 | drvdata->base + TRCSSCSRn(i)); |
54ff892b | 146 | writel_relaxed(config->ss_pe_cmp[i], |
2e1cdfe1 PP |
147 | drvdata->base + TRCSSPCICRn(i)); |
148 | } | |
149 | for (i = 0; i < drvdata->nr_addr_cmp; i++) { | |
54ff892b | 150 | writeq_relaxed(config->addr_val[i], |
2e1cdfe1 | 151 | drvdata->base + TRCACVRn(i)); |
54ff892b | 152 | writeq_relaxed(config->addr_acc[i], |
2e1cdfe1 PP |
153 | drvdata->base + TRCACATRn(i)); |
154 | } | |
155 | for (i = 0; i < drvdata->numcidc; i++) | |
54ff892b | 156 | writeq_relaxed(config->ctxid_pid[i], |
2e1cdfe1 | 157 | drvdata->base + TRCCIDCVRn(i)); |
54ff892b MP |
158 | writel_relaxed(config->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); |
159 | writel_relaxed(config->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); | |
2e1cdfe1 PP |
160 | |
161 | for (i = 0; i < drvdata->numvmidc; i++) | |
54ff892b | 162 | writeq_relaxed(config->vmid_val[i], |
2e1cdfe1 | 163 | drvdata->base + TRCVMIDCVRn(i)); |
54ff892b MP |
164 | writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); |
165 | writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); | |
2e1cdfe1 | 166 | |
46a3d5cd SH |
167 | /* |
168 | * Request to keep the trace unit powered and also | |
169 | * emulation of powerdown | |
170 | */ | |
171 | writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU, | |
172 | drvdata->base + TRCPDCR); | |
173 | ||
2e1cdfe1 PP |
174 | /* Enable the trace unit */ |
175 | writel_relaxed(1, drvdata->base + TRCPRGCTLR); | |
176 | ||
177 | /* wait for TRCSTATR.IDLE to go back down to '0' */ | |
178 | if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0)) | |
179 | dev_err(drvdata->dev, | |
67337e8d | 180 | "timeout while waiting for Idle Trace Status\n"); |
2e1cdfe1 PP |
181 | |
182 | CS_LOCK(drvdata->base); | |
183 | ||
184 | dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); | |
185 | } | |
186 | ||
37fbbdbd MP |
187 | static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, |
188 | struct perf_event_attr *attr) | |
189 | { | |
190 | struct etmv4_config *config = &drvdata->config; | |
191 | ||
192 | if (!attr) | |
193 | return -EINVAL; | |
194 | ||
195 | /* Clear configuration from previous run */ | |
196 | memset(config, 0, sizeof(struct etmv4_config)); | |
197 | ||
198 | if (attr->exclude_kernel) | |
199 | config->mode = ETM_MODE_EXCL_KERN; | |
200 | ||
201 | if (attr->exclude_user) | |
202 | config->mode = ETM_MODE_EXCL_USER; | |
203 | ||
204 | /* Always start from the default config */ | |
205 | etm4_set_default(config); | |
206 | ||
207 | /* | |
208 | * By default the tracers are configured to trace the whole address | |
209 | * range. Narrow the field only if requested by user space. | |
210 | */ | |
211 | if (config->mode) | |
212 | etm4_config_trace_mode(config); | |
213 | ||
214 | /* Go from generic option to ETMv4 specifics */ | |
215 | if (attr->config & BIT(ETM_OPT_CYCACC)) | |
216 | config->cfg |= ETMv4_MODE_CYCACC; | |
217 | if (attr->config & BIT(ETM_OPT_TS)) | |
218 | config->cfg |= ETMv4_MODE_TIMESTAMP; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static int etm4_enable_perf(struct coresight_device *csdev, | |
224 | struct perf_event_attr *attr) | |
225 | { | |
226 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
227 | ||
228 | if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) | |
229 | return -EINVAL; | |
230 | ||
231 | /* Configure the tracer based on the session's specifics */ | |
232 | etm4_parse_event_config(drvdata, attr); | |
233 | /* And enable it */ | |
234 | etm4_enable_hw(drvdata); | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
c38a9ec2 | 239 | static int etm4_enable_sysfs(struct coresight_device *csdev) |
2e1cdfe1 PP |
240 | { |
241 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
242 | int ret; | |
243 | ||
2e1cdfe1 PP |
244 | spin_lock(&drvdata->spinlock); |
245 | ||
246 | /* | |
247 | * Executing etm4_enable_hw on the cpu whose ETM is being enabled | |
248 | * ensures that register writes occur when cpu is powered. | |
249 | */ | |
250 | ret = smp_call_function_single(drvdata->cpu, | |
251 | etm4_enable_hw, drvdata, 1); | |
252 | if (ret) | |
253 | goto err; | |
2e1cdfe1 | 254 | |
c38a9ec2 | 255 | drvdata->sticky_enable = true; |
2e1cdfe1 PP |
256 | spin_unlock(&drvdata->spinlock); |
257 | ||
258 | dev_info(drvdata->dev, "ETM tracing enabled\n"); | |
259 | return 0; | |
c38a9ec2 | 260 | |
2e1cdfe1 PP |
261 | err: |
262 | spin_unlock(&drvdata->spinlock); | |
2e1cdfe1 PP |
263 | return ret; |
264 | } | |
265 | ||
c38a9ec2 MP |
266 | static int etm4_enable(struct coresight_device *csdev, |
267 | struct perf_event_attr *attr, u32 mode) | |
268 | { | |
269 | int ret; | |
270 | u32 val; | |
271 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
272 | ||
273 | val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); | |
274 | ||
275 | /* Someone is already using the tracer */ | |
276 | if (val) | |
277 | return -EBUSY; | |
278 | ||
279 | switch (mode) { | |
280 | case CS_MODE_SYSFS: | |
281 | ret = etm4_enable_sysfs(csdev); | |
282 | break; | |
37fbbdbd MP |
283 | case CS_MODE_PERF: |
284 | ret = etm4_enable_perf(csdev, attr); | |
285 | break; | |
c38a9ec2 MP |
286 | default: |
287 | ret = -EINVAL; | |
288 | } | |
289 | ||
290 | /* The tracer didn't start */ | |
291 | if (ret) | |
292 | local_set(&drvdata->mode, CS_MODE_DISABLED); | |
293 | ||
294 | return ret; | |
295 | } | |
296 | ||
2e1cdfe1 PP |
297 | static void etm4_disable_hw(void *info) |
298 | { | |
299 | u32 control; | |
300 | struct etmv4_drvdata *drvdata = info; | |
301 | ||
302 | CS_UNLOCK(drvdata->base); | |
303 | ||
46a3d5cd SH |
304 | /* power can be removed from the trace unit now */ |
305 | control = readl_relaxed(drvdata->base + TRCPDCR); | |
306 | control &= ~TRCPDCR_PU; | |
307 | writel_relaxed(control, drvdata->base + TRCPDCR); | |
308 | ||
2e1cdfe1 PP |
309 | control = readl_relaxed(drvdata->base + TRCPRGCTLR); |
310 | ||
311 | /* EN, bit[0] Trace unit enable bit */ | |
312 | control &= ~0x1; | |
313 | ||
314 | /* make sure everything completes before disabling */ | |
315 | mb(); | |
316 | isb(); | |
317 | writel_relaxed(control, drvdata->base + TRCPRGCTLR); | |
318 | ||
319 | CS_LOCK(drvdata->base); | |
320 | ||
321 | dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); | |
322 | } | |
323 | ||
37fbbdbd MP |
324 | static int etm4_disable_perf(struct coresight_device *csdev) |
325 | { | |
326 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
327 | ||
328 | if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) | |
329 | return -EINVAL; | |
330 | ||
331 | etm4_disable_hw(drvdata); | |
332 | return 0; | |
333 | } | |
334 | ||
c38a9ec2 | 335 | static void etm4_disable_sysfs(struct coresight_device *csdev) |
2e1cdfe1 PP |
336 | { |
337 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
338 | ||
339 | /* | |
340 | * Taking hotplug lock here protects from clocks getting disabled | |
341 | * with tracing being left on (crash scenario) if user disable occurs | |
342 | * after cpu online mask indicates the cpu is offline but before the | |
343 | * DYING hotplug callback is serviced by the ETM driver. | |
344 | */ | |
345 | get_online_cpus(); | |
346 | spin_lock(&drvdata->spinlock); | |
347 | ||
348 | /* | |
349 | * Executing etm4_disable_hw on the cpu whose ETM is being disabled | |
350 | * ensures that register writes occur when cpu is powered. | |
351 | */ | |
352 | smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); | |
2e1cdfe1 PP |
353 | |
354 | spin_unlock(&drvdata->spinlock); | |
355 | put_online_cpus(); | |
356 | ||
2e1cdfe1 PP |
357 | dev_info(drvdata->dev, "ETM tracing disabled\n"); |
358 | } | |
359 | ||
c38a9ec2 MP |
360 | static void etm4_disable(struct coresight_device *csdev) |
361 | { | |
362 | u32 mode; | |
363 | struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
364 | ||
365 | /* | |
366 | * For as long as the tracer isn't disabled another entity can't | |
367 | * change its status. As such we can read the status here without | |
368 | * fearing it will change under us. | |
369 | */ | |
370 | mode = local_read(&drvdata->mode); | |
371 | ||
372 | switch (mode) { | |
373 | case CS_MODE_DISABLED: | |
374 | break; | |
375 | case CS_MODE_SYSFS: | |
376 | etm4_disable_sysfs(csdev); | |
377 | break; | |
37fbbdbd MP |
378 | case CS_MODE_PERF: |
379 | etm4_disable_perf(csdev); | |
380 | break; | |
c38a9ec2 MP |
381 | } |
382 | ||
383 | if (mode) | |
384 | local_set(&drvdata->mode, CS_MODE_DISABLED); | |
385 | } | |
386 | ||
2e1cdfe1 | 387 | static const struct coresight_ops_source etm4_source_ops = { |
52210c87 | 388 | .cpu_id = etm4_cpu_id, |
2e1cdfe1 PP |
389 | .trace_id = etm4_trace_id, |
390 | .enable = etm4_enable, | |
391 | .disable = etm4_disable, | |
392 | }; | |
393 | ||
394 | static const struct coresight_ops etm4_cs_ops = { | |
395 | .source_ops = &etm4_source_ops, | |
396 | }; | |
397 | ||
2e1cdfe1 PP |
398 | static void etm4_init_arch_data(void *info) |
399 | { | |
400 | u32 etmidr0; | |
401 | u32 etmidr1; | |
402 | u32 etmidr2; | |
403 | u32 etmidr3; | |
404 | u32 etmidr4; | |
405 | u32 etmidr5; | |
406 | struct etmv4_drvdata *drvdata = info; | |
407 | ||
66bbbb77 MP |
408 | /* Make sure all registers are accessible */ |
409 | etm4_os_unlock(drvdata); | |
410 | ||
2e1cdfe1 PP |
411 | CS_UNLOCK(drvdata->base); |
412 | ||
413 | /* find all capabilities of the tracing unit */ | |
414 | etmidr0 = readl_relaxed(drvdata->base + TRCIDR0); | |
415 | ||
416 | /* INSTP0, bits[2:1] P0 tracing support field */ | |
417 | if (BMVAL(etmidr0, 1, 1) && BMVAL(etmidr0, 2, 2)) | |
418 | drvdata->instrp0 = true; | |
419 | else | |
420 | drvdata->instrp0 = false; | |
421 | ||
422 | /* TRCBB, bit[5] Branch broadcast tracing support bit */ | |
423 | if (BMVAL(etmidr0, 5, 5)) | |
424 | drvdata->trcbb = true; | |
425 | else | |
426 | drvdata->trcbb = false; | |
427 | ||
428 | /* TRCCOND, bit[6] Conditional instruction tracing support bit */ | |
429 | if (BMVAL(etmidr0, 6, 6)) | |
430 | drvdata->trccond = true; | |
431 | else | |
432 | drvdata->trccond = false; | |
433 | ||
434 | /* TRCCCI, bit[7] Cycle counting instruction bit */ | |
435 | if (BMVAL(etmidr0, 7, 7)) | |
436 | drvdata->trccci = true; | |
437 | else | |
438 | drvdata->trccci = false; | |
439 | ||
440 | /* RETSTACK, bit[9] Return stack bit */ | |
441 | if (BMVAL(etmidr0, 9, 9)) | |
442 | drvdata->retstack = true; | |
443 | else | |
444 | drvdata->retstack = false; | |
445 | ||
446 | /* NUMEVENT, bits[11:10] Number of events field */ | |
447 | drvdata->nr_event = BMVAL(etmidr0, 10, 11); | |
448 | /* QSUPP, bits[16:15] Q element support field */ | |
449 | drvdata->q_support = BMVAL(etmidr0, 15, 16); | |
450 | /* TSSIZE, bits[28:24] Global timestamp size field */ | |
451 | drvdata->ts_size = BMVAL(etmidr0, 24, 28); | |
452 | ||
453 | /* base architecture of trace unit */ | |
454 | etmidr1 = readl_relaxed(drvdata->base + TRCIDR1); | |
455 | /* | |
456 | * TRCARCHMIN, bits[7:4] architecture the minor version number | |
457 | * TRCARCHMAJ, bits[11:8] architecture major versin number | |
458 | */ | |
459 | drvdata->arch = BMVAL(etmidr1, 4, 11); | |
460 | ||
461 | /* maximum size of resources */ | |
462 | etmidr2 = readl_relaxed(drvdata->base + TRCIDR2); | |
463 | /* CIDSIZE, bits[9:5] Indicates the Context ID size */ | |
464 | drvdata->ctxid_size = BMVAL(etmidr2, 5, 9); | |
465 | /* VMIDSIZE, bits[14:10] Indicates the VMID size */ | |
466 | drvdata->vmid_size = BMVAL(etmidr2, 10, 14); | |
467 | /* CCSIZE, bits[28:25] size of the cycle counter in bits minus 12 */ | |
468 | drvdata->ccsize = BMVAL(etmidr2, 25, 28); | |
469 | ||
470 | etmidr3 = readl_relaxed(drvdata->base + TRCIDR3); | |
471 | /* CCITMIN, bits[11:0] minimum threshold value that can be programmed */ | |
472 | drvdata->ccitmin = BMVAL(etmidr3, 0, 11); | |
473 | /* EXLEVEL_S, bits[19:16] Secure state instruction tracing */ | |
474 | drvdata->s_ex_level = BMVAL(etmidr3, 16, 19); | |
475 | /* EXLEVEL_NS, bits[23:20] Non-secure state instruction tracing */ | |
476 | drvdata->ns_ex_level = BMVAL(etmidr3, 20, 23); | |
477 | ||
478 | /* | |
479 | * TRCERR, bit[24] whether a trace unit can trace a | |
480 | * system error exception. | |
481 | */ | |
482 | if (BMVAL(etmidr3, 24, 24)) | |
483 | drvdata->trc_error = true; | |
484 | else | |
485 | drvdata->trc_error = false; | |
486 | ||
487 | /* SYNCPR, bit[25] implementation has a fixed synchronization period? */ | |
488 | if (BMVAL(etmidr3, 25, 25)) | |
489 | drvdata->syncpr = true; | |
490 | else | |
491 | drvdata->syncpr = false; | |
492 | ||
493 | /* STALLCTL, bit[26] is stall control implemented? */ | |
494 | if (BMVAL(etmidr3, 26, 26)) | |
495 | drvdata->stallctl = true; | |
496 | else | |
497 | drvdata->stallctl = false; | |
498 | ||
499 | /* SYSSTALL, bit[27] implementation can support stall control? */ | |
500 | if (BMVAL(etmidr3, 27, 27)) | |
501 | drvdata->sysstall = true; | |
502 | else | |
503 | drvdata->sysstall = false; | |
504 | ||
505 | /* NUMPROC, bits[30:28] the number of PEs available for tracing */ | |
506 | drvdata->nr_pe = BMVAL(etmidr3, 28, 30); | |
507 | ||
508 | /* NOOVERFLOW, bit[31] is trace overflow prevention supported */ | |
509 | if (BMVAL(etmidr3, 31, 31)) | |
510 | drvdata->nooverflow = true; | |
511 | else | |
512 | drvdata->nooverflow = false; | |
513 | ||
514 | /* number of resources trace unit supports */ | |
515 | etmidr4 = readl_relaxed(drvdata->base + TRCIDR4); | |
516 | /* NUMACPAIRS, bits[0:3] number of addr comparator pairs for tracing */ | |
517 | drvdata->nr_addr_cmp = BMVAL(etmidr4, 0, 3); | |
518 | /* NUMPC, bits[15:12] number of PE comparator inputs for tracing */ | |
519 | drvdata->nr_pe_cmp = BMVAL(etmidr4, 12, 15); | |
497b5956 CZ |
520 | /* |
521 | * NUMRSPAIR, bits[19:16] | |
522 | * The number of resource pairs conveyed by the HW starts at 0, i.e a | |
523 | * value of 0x0 indicate 1 resource pair, 0x1 indicate two and so on. | |
524 | * As such add 1 to the value of NUMRSPAIR for a better representation. | |
525 | */ | |
526 | drvdata->nr_resource = BMVAL(etmidr4, 16, 19) + 1; | |
2e1cdfe1 PP |
527 | /* |
528 | * NUMSSCC, bits[23:20] the number of single-shot | |
529 | * comparator control for tracing | |
530 | */ | |
531 | drvdata->nr_ss_cmp = BMVAL(etmidr4, 20, 23); | |
532 | /* NUMCIDC, bits[27:24] number of Context ID comparators for tracing */ | |
533 | drvdata->numcidc = BMVAL(etmidr4, 24, 27); | |
534 | /* NUMVMIDC, bits[31:28] number of VMID comparators for tracing */ | |
535 | drvdata->numvmidc = BMVAL(etmidr4, 28, 31); | |
536 | ||
537 | etmidr5 = readl_relaxed(drvdata->base + TRCIDR5); | |
538 | /* NUMEXTIN, bits[8:0] number of external inputs implemented */ | |
539 | drvdata->nr_ext_inp = BMVAL(etmidr5, 0, 8); | |
540 | /* TRACEIDSIZE, bits[21:16] indicates the trace ID width */ | |
541 | drvdata->trcid_size = BMVAL(etmidr5, 16, 21); | |
542 | /* ATBTRIG, bit[22] implementation can support ATB triggers? */ | |
543 | if (BMVAL(etmidr5, 22, 22)) | |
544 | drvdata->atbtrig = true; | |
545 | else | |
546 | drvdata->atbtrig = false; | |
547 | /* | |
548 | * LPOVERRIDE, bit[23] implementation supports | |
549 | * low-power state override | |
550 | */ | |
551 | if (BMVAL(etmidr5, 23, 23)) | |
552 | drvdata->lpoverride = true; | |
553 | else | |
554 | drvdata->lpoverride = false; | |
555 | /* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */ | |
556 | drvdata->nrseqstate = BMVAL(etmidr5, 25, 27); | |
557 | /* NUMCNTR, bits[30:28] number of counters available for tracing */ | |
558 | drvdata->nr_cntr = BMVAL(etmidr5, 28, 30); | |
559 | CS_LOCK(drvdata->base); | |
560 | } | |
561 | ||
fc208abe | 562 | static void etm4_set_default(struct etmv4_config *config) |
2e1cdfe1 | 563 | { |
fc208abe MP |
564 | if (WARN_ON_ONCE(!config)) |
565 | return; | |
2e1cdfe1 | 566 | |
fc208abe MP |
567 | /* |
568 | * Make default initialisation trace everything | |
569 | * | |
570 | * Select the "always true" resource selector on the | |
571 | * "Enablign Event" line and configure address range comparator | |
572 | * '0' to trace all the possible address range. From there | |
573 | * configure the "include/exclude" engine to include address | |
574 | * range comparator '0'. | |
575 | */ | |
2e1cdfe1 PP |
576 | |
577 | /* disable all events tracing */ | |
54ff892b MP |
578 | config->eventctrl0 = 0x0; |
579 | config->eventctrl1 = 0x0; | |
2e1cdfe1 PP |
580 | |
581 | /* disable stalling */ | |
54ff892b | 582 | config->stall_ctrl = 0x0; |
2e1cdfe1 | 583 | |
fc208abe MP |
584 | /* enable trace synchronization every 4096 bytes, if available */ |
585 | config->syncfreq = 0xC; | |
586 | ||
2e1cdfe1 | 587 | /* disable timestamp event */ |
54ff892b | 588 | config->ts_ctrl = 0x0; |
2e1cdfe1 | 589 | |
fc208abe MP |
590 | /* TRCVICTLR::EVENT = 0x01, select the always on logic */ |
591 | config->vinst_ctrl |= BIT(0); | |
2e1cdfe1 PP |
592 | |
593 | /* | |
fc208abe MP |
594 | * TRCVICTLR::SSSTATUS == 1, the start-stop logic is |
595 | * in the started state | |
2e1cdfe1 | 596 | */ |
fc208abe | 597 | config->vinst_ctrl |= BIT(9); |
2e1cdfe1 | 598 | |
fc208abe MP |
599 | /* |
600 | * Configure address range comparator '0' to encompass all | |
601 | * possible addresses. | |
602 | */ | |
f67b467a | 603 | |
fc208abe MP |
604 | /* First half of default address comparator: start at address 0 */ |
605 | config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0; | |
606 | /* trace instruction addresses */ | |
607 | config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1)); | |
608 | /* EXLEVEL_NS, bits[12:15], only trace application and kernel space */ | |
609 | config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP; | |
610 | /* EXLEVEL_S, bits[11:8], don't trace anything in secure state */ | |
611 | config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP | | |
612 | ETM_EXLEVEL_S_OS | | |
613 | ETM_EXLEVEL_S_HYP); | |
614 | config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; | |
2e1cdfe1 | 615 | |
fc208abe MP |
616 | /* |
617 | * Second half of default address comparator: go all | |
618 | * the way to the top. | |
619 | */ | |
620 | config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0; | |
621 | /* trace instruction addresses */ | |
622 | config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1)); | |
623 | /* Address comparator type must be equal for both halves */ | |
624 | config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = | |
625 | config->addr_acc[ETM_DEFAULT_ADDR_COMP]; | |
626 | config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE; | |
2e1cdfe1 PP |
627 | |
628 | /* | |
fc208abe MP |
629 | * Configure the ViewInst function to filter on address range |
630 | * comparator '0'. | |
2e1cdfe1 | 631 | */ |
fc208abe MP |
632 | config->viiectlr = BIT(0); |
633 | ||
634 | /* no start-stop filtering for ViewInst */ | |
635 | config->vissctlr = 0x0; | |
2e1cdfe1 PP |
636 | } |
637 | ||
4f6fce54 MP |
638 | void etm4_config_trace_mode(struct etmv4_config *config) |
639 | { | |
640 | u32 addr_acc, mode; | |
641 | ||
642 | mode = config->mode; | |
643 | mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); | |
644 | ||
645 | /* excluding kernel AND user space doesn't make sense */ | |
646 | WARN_ON_ONCE(mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)); | |
647 | ||
648 | /* nothing to do if neither flags are set */ | |
649 | if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) | |
650 | return; | |
651 | ||
652 | addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP]; | |
653 | /* clear default config */ | |
654 | addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS); | |
655 | ||
656 | /* | |
657 | * EXLEVEL_NS, bits[15:12] | |
658 | * The Exception levels are: | |
659 | * Bit[12] Exception level 0 - Application | |
660 | * Bit[13] Exception level 1 - OS | |
661 | * Bit[14] Exception level 2 - Hypervisor | |
662 | * Bit[15] Never implemented | |
663 | */ | |
664 | if (mode & ETM_MODE_EXCL_KERN) | |
665 | addr_acc |= ETM_EXLEVEL_NS_OS; | |
666 | else | |
667 | addr_acc |= ETM_EXLEVEL_NS_APP; | |
668 | ||
669 | config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc; | |
670 | config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc; | |
671 | } | |
672 | ||
58eb457b | 673 | static int etm4_online_cpu(unsigned int cpu) |
2e1cdfe1 | 674 | { |
2e1cdfe1 | 675 | if (!etmdrvdata[cpu]) |
58eb457b | 676 | return 0; |
2e1cdfe1 | 677 | |
58eb457b SAS |
678 | if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable) |
679 | coresight_enable(etmdrvdata[cpu]->csdev); | |
680 | return 0; | |
681 | } | |
2e1cdfe1 | 682 | |
58eb457b SAS |
683 | static int etm4_starting_cpu(unsigned int cpu) |
684 | { | |
685 | if (!etmdrvdata[cpu]) | |
686 | return 0; | |
687 | ||
688 | spin_lock(&etmdrvdata[cpu]->spinlock); | |
689 | if (!etmdrvdata[cpu]->os_unlock) { | |
690 | etm4_os_unlock(etmdrvdata[cpu]); | |
691 | etmdrvdata[cpu]->os_unlock = true; | |
2e1cdfe1 | 692 | } |
58eb457b SAS |
693 | |
694 | if (local_read(&etmdrvdata[cpu]->mode)) | |
695 | etm4_enable_hw(etmdrvdata[cpu]); | |
696 | spin_unlock(&etmdrvdata[cpu]->spinlock); | |
697 | return 0; | |
2e1cdfe1 PP |
698 | } |
699 | ||
58eb457b SAS |
700 | static int etm4_dying_cpu(unsigned int cpu) |
701 | { | |
702 | if (!etmdrvdata[cpu]) | |
703 | return 0; | |
704 | ||
705 | spin_lock(&etmdrvdata[cpu]->spinlock); | |
706 | if (local_read(&etmdrvdata[cpu]->mode)) | |
707 | etm4_disable_hw(etmdrvdata[cpu]); | |
708 | spin_unlock(&etmdrvdata[cpu]->spinlock); | |
709 | return 0; | |
710 | } | |
2e1cdfe1 | 711 | |
fc208abe MP |
712 | static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) |
713 | { | |
714 | drvdata->trcid = coresight_get_trace_id(drvdata->cpu); | |
715 | } | |
716 | ||
2e1cdfe1 PP |
717 | static int etm4_probe(struct amba_device *adev, const struct amba_id *id) |
718 | { | |
719 | int ret; | |
720 | void __iomem *base; | |
721 | struct device *dev = &adev->dev; | |
722 | struct coresight_platform_data *pdata = NULL; | |
723 | struct etmv4_drvdata *drvdata; | |
724 | struct resource *res = &adev->res; | |
9486295a | 725 | struct coresight_desc desc = { 0 }; |
2e1cdfe1 PP |
726 | struct device_node *np = adev->dev.of_node; |
727 | ||
2e1cdfe1 PP |
728 | drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); |
729 | if (!drvdata) | |
730 | return -ENOMEM; | |
731 | ||
732 | if (np) { | |
733 | pdata = of_get_coresight_platform_data(dev, np); | |
734 | if (IS_ERR(pdata)) | |
735 | return PTR_ERR(pdata); | |
736 | adev->dev.platform_data = pdata; | |
737 | } | |
738 | ||
739 | drvdata->dev = &adev->dev; | |
740 | dev_set_drvdata(dev, drvdata); | |
741 | ||
742 | /* Validity for the resource is already checked by the AMBA core */ | |
743 | base = devm_ioremap_resource(dev, res); | |
744 | if (IS_ERR(base)) | |
745 | return PTR_ERR(base); | |
746 | ||
747 | drvdata->base = base; | |
748 | ||
749 | spin_lock_init(&drvdata->spinlock); | |
750 | ||
751 | drvdata->cpu = pdata ? pdata->cpu : 0; | |
752 | ||
753 | get_online_cpus(); | |
754 | etmdrvdata[drvdata->cpu] = drvdata; | |
755 | ||
2e1cdfe1 PP |
756 | if (smp_call_function_single(drvdata->cpu, |
757 | etm4_init_arch_data, drvdata, 1)) | |
758 | dev_err(dev, "ETM arch init failed\n"); | |
759 | ||
58eb457b SAS |
760 | if (!etm4_count++) { |
761 | cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT4_STARTING, | |
762 | "AP_ARM_CORESIGHT4_STARTING", | |
763 | etm4_starting_cpu, etm4_dying_cpu); | |
764 | ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, | |
765 | "AP_ARM_CORESIGHT4_ONLINE", | |
766 | etm4_online_cpu, NULL); | |
767 | if (ret < 0) | |
768 | goto err_arch_supported; | |
769 | hp_online = ret; | |
770 | } | |
2e1cdfe1 PP |
771 | |
772 | put_online_cpus(); | |
773 | ||
774 | if (etm4_arch_supported(drvdata->arch) == false) { | |
775 | ret = -EINVAL; | |
776 | goto err_arch_supported; | |
777 | } | |
fc208abe MP |
778 | |
779 | etm4_init_trace_id(drvdata); | |
780 | etm4_set_default(&drvdata->config); | |
2e1cdfe1 | 781 | |
9486295a SP |
782 | desc.type = CORESIGHT_DEV_TYPE_SOURCE; |
783 | desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; | |
784 | desc.ops = &etm4_cs_ops; | |
785 | desc.pdata = pdata; | |
786 | desc.dev = dev; | |
787 | desc.groups = coresight_etmv4_groups; | |
788 | drvdata->csdev = coresight_register(&desc); | |
2e1cdfe1 PP |
789 | if (IS_ERR(drvdata->csdev)) { |
790 | ret = PTR_ERR(drvdata->csdev); | |
37fbbdbd | 791 | goto err_arch_supported; |
2e1cdfe1 PP |
792 | } |
793 | ||
37fbbdbd MP |
794 | ret = etm_perf_symlink(drvdata->csdev, true); |
795 | if (ret) { | |
796 | coresight_unregister(drvdata->csdev); | |
797 | goto err_arch_supported; | |
798 | } | |
799 | ||
800 | pm_runtime_put(&adev->dev); | |
2e1cdfe1 PP |
801 | dev_info(dev, "%s initialized\n", (char *)id->data); |
802 | ||
803 | if (boot_enable) { | |
804 | coresight_enable(drvdata->csdev); | |
805 | drvdata->boot_enable = true; | |
806 | } | |
807 | ||
808 | return 0; | |
809 | ||
810 | err_arch_supported: | |
58eb457b SAS |
811 | if (--etm4_count == 0) { |
812 | cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT4_STARTING); | |
813 | if (hp_online) | |
814 | cpuhp_remove_state_nocalls(hp_online); | |
815 | } | |
2e1cdfe1 PP |
816 | return ret; |
817 | } | |
818 | ||
2e1cdfe1 | 819 | static struct amba_id etm4_ids[] = { |
78247e25 SP |
820 | { /* ETM 4.0 - Cortex-A53 */ |
821 | .id = 0x000bb95d, | |
822 | .mask = 0x000fffff, | |
2e1cdfe1 PP |
823 | .data = "ETM 4.0", |
824 | }, | |
78247e25 | 825 | { /* ETM 4.0 - Cortex-A57 */ |
2e1cdfe1 PP |
826 | .id = 0x000bb95e, |
827 | .mask = 0x000fffff, | |
828 | .data = "ETM 4.0", | |
829 | }, | |
960e3095 LP |
830 | { /* ETM 4.0 - A72, Maia, HiSilicon */ |
831 | .id = 0x000bb95a, | |
832 | .mask = 0x000fffff, | |
833 | .data = "ETM 4.0", | |
834 | }, | |
2e1cdfe1 PP |
835 | { 0, 0}, |
836 | }; | |
837 | ||
838 | static struct amba_driver etm4x_driver = { | |
839 | .drv = { | |
840 | .name = "coresight-etm4x", | |
b15f0fb6 | 841 | .suppress_bind_attrs = true, |
2e1cdfe1 PP |
842 | }, |
843 | .probe = etm4_probe, | |
2e1cdfe1 PP |
844 | .id_table = etm4_ids, |
845 | }; | |
941943cf | 846 | builtin_amba_driver(etm4x_driver); |