]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/hwtracing/coresight/coresight-tmc-etf.c
coresight: tmc: make sysFS and Perf mode mutually exclusive
[mirror_ubuntu-artful-kernel.git] / drivers / hwtracing / coresight / coresight-tmc-etf.c
CommitLineData
6c6ed1e2
MP
1/*
2 * Copyright(C) 2016 Linaro Limited. All rights reserved.
3 * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/coresight.h>
de546197 19#include <linux/slab.h>
6c6ed1e2
MP
20#include "coresight-priv.h"
21#include "coresight-tmc.h"
22
23void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
24{
6c6ed1e2
MP
25 CS_UNLOCK(drvdata->base);
26
27 /* Wait for TMCSReady bit to be set */
28 tmc_wait_for_tmcready(drvdata);
29
30 writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
31 writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
32 TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
33 TMC_FFCR_TRIGON_TRIGIN,
34 drvdata->base + TMC_FFCR);
35
36 writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
37 tmc_enable_hw(drvdata);
38
39 CS_LOCK(drvdata->base);
40}
41
42static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
43{
44 enum tmc_mem_intf_width memwidth;
45 u8 memwords;
46 char *bufp;
47 u32 read_data;
48 int i;
49
50 memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
51 if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
52 memwords = 1;
53 else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
54 memwords = 2;
55 else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
56 memwords = 4;
57 else
58 memwords = 8;
59
60 bufp = drvdata->buf;
61 while (1) {
62 for (i = 0; i < memwords; i++) {
63 read_data = readl_relaxed(drvdata->base + TMC_RRD);
64 if (read_data == 0xFFFFFFFF)
65 return;
66 memcpy(bufp, &read_data, 4);
67 bufp += 4;
68 }
69 }
70}
71
4525412a 72static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
6c6ed1e2
MP
73{
74 CS_UNLOCK(drvdata->base);
75
76 tmc_flush_and_stop(drvdata);
a40318fb
MP
77 /*
78 * When operating in sysFS mode the content of the buffer needs to be
79 * read before the TMC is disabled.
80 */
81 if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
82 tmc_etb_dump_hw(drvdata);
6c6ed1e2
MP
83 tmc_disable_hw(drvdata);
84
85 CS_LOCK(drvdata->base);
86}
87
88static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
89{
90 CS_UNLOCK(drvdata->base);
91
92 /* Wait for TMCSReady bit to be set */
93 tmc_wait_for_tmcready(drvdata);
94
95 writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
96 writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
97 drvdata->base + TMC_FFCR);
98 writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
99 tmc_enable_hw(drvdata);
100
101 CS_LOCK(drvdata->base);
102}
103
104static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
105{
106 CS_UNLOCK(drvdata->base);
107
108 tmc_flush_and_stop(drvdata);
109 tmc_disable_hw(drvdata);
110
111 CS_LOCK(drvdata->base);
112}
113
b217601e 114static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
6c6ed1e2 115{
de546197
MP
116 int ret = 0;
117 bool used = false;
118 char *buf = NULL;
f2facc33 119 long val;
6c6ed1e2
MP
120 unsigned long flags;
121 struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
122
de546197
MP
123 /* This shouldn't be happening */
124 if (WARN_ON(mode != CS_MODE_SYSFS))
125 return -EINVAL;
126
127 /*
128 * If we don't have a buffer release the lock and allocate memory.
129 * Otherwise keep the lock and move along.
130 */
6c6ed1e2 131 spin_lock_irqsave(&drvdata->spinlock, flags);
de546197 132 if (!drvdata->buf) {
6c6ed1e2 133 spin_unlock_irqrestore(&drvdata->spinlock, flags);
de546197
MP
134
135 /* Allocating the memory here while outside of the spinlock */
136 buf = kzalloc(drvdata->size, GFP_KERNEL);
137 if (!buf)
138 return -ENOMEM;
139
140 /* Let's try again */
141 spin_lock_irqsave(&drvdata->spinlock, flags);
142 }
143
144 if (drvdata->reading) {
145 ret = -EBUSY;
146 goto out;
147 }
148
f2facc33
MP
149 val = local_xchg(&drvdata->mode, mode);
150 /*
151 * In sysFS mode we can have multiple writers per sink. Since this
152 * sink is already enabled no memory is needed and the HW need not be
153 * touched.
154 */
155 if (val == CS_MODE_SYSFS)
156 goto out;
157
de546197
MP
158 /*
159 * If drvdata::buf isn't NULL, memory was allocated for a previous
160 * trace run but wasn't read. If so simply zero-out the memory.
161 * Otherwise use the memory allocated above.
162 *
163 * The memory is freed when users read the buffer using the
164 * /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for
165 * details.
166 */
167 if (drvdata->buf) {
168 memset(drvdata->buf, 0, drvdata->size);
169 } else {
170 used = true;
171 drvdata->buf = buf;
6c6ed1e2
MP
172 }
173
174 tmc_etb_enable_hw(drvdata);
de546197 175out:
6c6ed1e2
MP
176 spin_unlock_irqrestore(&drvdata->spinlock, flags);
177
de546197
MP
178 /* Free memory outside the spinlock if need be */
179 if (!used && buf)
180 kfree(buf);
181
182 if (!ret)
183 dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
184
185 return ret;
6c6ed1e2
MP
186}
187
b217601e
MP
188static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode)
189{
190 int ret = 0;
191 long val;
192 unsigned long flags;
193 struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
194
195 /* This shouldn't be happening */
196 if (WARN_ON(mode != CS_MODE_PERF))
197 return -EINVAL;
198
199 spin_lock_irqsave(&drvdata->spinlock, flags);
200 if (drvdata->reading) {
201 ret = -EINVAL;
202 goto out;
203 }
204
205 val = local_xchg(&drvdata->mode, mode);
206 /*
207 * In Perf mode there can be only one writer per sink. There
208 * is also no need to continue if the ETB/ETR is already operated
209 * from sysFS.
210 */
211 if (val != CS_MODE_DISABLED) {
212 ret = -EINVAL;
213 goto out;
214 }
215
216 tmc_etb_enable_hw(drvdata);
217out:
218 spin_unlock_irqrestore(&drvdata->spinlock, flags);
219
220 return ret;
221}
222
223static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
224{
225 switch (mode) {
226 case CS_MODE_SYSFS:
227 return tmc_enable_etf_sink_sysfs(csdev, mode);
228 case CS_MODE_PERF:
229 return tmc_enable_etf_sink_perf(csdev, mode);
230 }
231
232 /* We shouldn't be here */
233 return -EINVAL;
234}
235
6c6ed1e2
MP
236static void tmc_disable_etf_sink(struct coresight_device *csdev)
237{
f2facc33 238 long val;
6c6ed1e2
MP
239 unsigned long flags;
240 struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
241
242 spin_lock_irqsave(&drvdata->spinlock, flags);
243 if (drvdata->reading) {
244 spin_unlock_irqrestore(&drvdata->spinlock, flags);
245 return;
246 }
247
f2facc33
MP
248 val = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
249 /* Disable the TMC only if it needs to */
250 if (val != CS_MODE_DISABLED)
251 tmc_etb_disable_hw(drvdata);
252
6c6ed1e2
MP
253 spin_unlock_irqrestore(&drvdata->spinlock, flags);
254
255 dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
256}
257
258static int tmc_enable_etf_link(struct coresight_device *csdev,
259 int inport, int outport)
260{
261 unsigned long flags;
262 struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
263
264 spin_lock_irqsave(&drvdata->spinlock, flags);
265 if (drvdata->reading) {
266 spin_unlock_irqrestore(&drvdata->spinlock, flags);
267 return -EBUSY;
268 }
269
270 tmc_etf_enable_hw(drvdata);
f2facc33 271 local_set(&drvdata->mode, CS_MODE_SYSFS);
6c6ed1e2
MP
272 spin_unlock_irqrestore(&drvdata->spinlock, flags);
273
274 dev_info(drvdata->dev, "TMC-ETF enabled\n");
275 return 0;
276}
277
278static void tmc_disable_etf_link(struct coresight_device *csdev,
279 int inport, int outport)
280{
281 unsigned long flags;
282 struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
283
284 spin_lock_irqsave(&drvdata->spinlock, flags);
285 if (drvdata->reading) {
286 spin_unlock_irqrestore(&drvdata->spinlock, flags);
287 return;
288 }
289
290 tmc_etf_disable_hw(drvdata);
f2facc33 291 local_set(&drvdata->mode, CS_MODE_DISABLED);
6c6ed1e2
MP
292 spin_unlock_irqrestore(&drvdata->spinlock, flags);
293
294 dev_info(drvdata->dev, "TMC disabled\n");
295}
296
297static const struct coresight_ops_sink tmc_etf_sink_ops = {
298 .enable = tmc_enable_etf_sink,
299 .disable = tmc_disable_etf_sink,
300};
301
302static const struct coresight_ops_link tmc_etf_link_ops = {
303 .enable = tmc_enable_etf_link,
304 .disable = tmc_disable_etf_link,
305};
306
307const struct coresight_ops tmc_etb_cs_ops = {
308 .sink_ops = &tmc_etf_sink_ops,
309};
310
311const struct coresight_ops tmc_etf_cs_ops = {
312 .sink_ops = &tmc_etf_sink_ops,
313 .link_ops = &tmc_etf_link_ops,
314};
4525412a
MP
315
316int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
317{
b217601e 318 long val;
4525412a
MP
319 enum tmc_mode mode;
320 int ret = 0;
321 unsigned long flags;
322
323 /* config types are set a boot time and never change */
324 if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
325 drvdata->config_type != TMC_CONFIG_TYPE_ETF))
326 return -EINVAL;
327
328 spin_lock_irqsave(&drvdata->spinlock, flags);
329
f74debbe
MP
330 if (drvdata->reading) {
331 ret = -EBUSY;
332 goto out;
333 }
334
4525412a
MP
335 /* There is no point in reading a TMC in HW FIFO mode */
336 mode = readl_relaxed(drvdata->base + TMC_MODE);
337 if (mode != TMC_MODE_CIRCULAR_BUFFER) {
338 ret = -EINVAL;
339 goto out;
340 }
341
b217601e
MP
342 val = local_read(&drvdata->mode);
343 /* Don't interfere if operated from Perf */
344 if (val == CS_MODE_PERF) {
345 ret = -EINVAL;
346 goto out;
347 }
348
de546197
MP
349 /* If drvdata::buf is NULL the trace data has been read already */
350 if (drvdata->buf == NULL) {
351 ret = -EINVAL;
352 goto out;
353 }
354
4525412a 355 /* Disable the TMC if need be */
b217601e 356 if (val == CS_MODE_SYSFS)
4525412a
MP
357 tmc_etb_disable_hw(drvdata);
358
359 drvdata->reading = true;
360out:
361 spin_unlock_irqrestore(&drvdata->spinlock, flags);
362
363 return ret;
364}
365
366int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
367{
de546197 368 char *buf = NULL;
4525412a
MP
369 enum tmc_mode mode;
370 unsigned long flags;
371
372 /* config types are set a boot time and never change */
373 if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
374 drvdata->config_type != TMC_CONFIG_TYPE_ETF))
375 return -EINVAL;
376
377 spin_lock_irqsave(&drvdata->spinlock, flags);
378
379 /* There is no point in reading a TMC in HW FIFO mode */
380 mode = readl_relaxed(drvdata->base + TMC_MODE);
381 if (mode != TMC_MODE_CIRCULAR_BUFFER) {
382 spin_unlock_irqrestore(&drvdata->spinlock, flags);
383 return -EINVAL;
384 }
385
386 /* Re-enable the TMC if need be */
f2facc33 387 if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
de546197
MP
388 /*
389 * The trace run will continue with the same allocated trace
390 * buffer. As such zero-out the buffer so that we don't end
391 * up with stale data.
392 *
393 * Since the tracer is still enabled drvdata::buf
394 * can't be NULL.
395 */
396 memset(drvdata->buf, 0, drvdata->size);
4525412a 397 tmc_etb_enable_hw(drvdata);
de546197
MP
398 } else {
399 /*
400 * The ETB/ETF is not tracing and the buffer was just read.
401 * As such prepare to free the trace buffer.
402 */
403 buf = drvdata->buf;
404 drvdata->buf = NULL;
405 }
4525412a
MP
406
407 drvdata->reading = false;
408 spin_unlock_irqrestore(&drvdata->spinlock, flags);
409
de546197
MP
410 /*
411 * Free allocated memory outside of the spinlock. There is no need
412 * to assert the validity of 'buf' since calling kfree(NULL) is safe.
413 */
414 kfree(buf);
415
4525412a
MP
416 return 0;
417}