Commit | Line | Data |
---|---|---|
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/dma-mapping.h> |
6c6ed1e2 MP |
20 | #include "coresight-priv.h" |
21 | #include "coresight-tmc.h" | |
22 | ||
0ef7528d | 23 | static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) |
6c6ed1e2 | 24 | { |
f2e931a2 | 25 | u32 axictl, sts; |
6c6ed1e2 MP |
26 | |
27 | /* Zero out the memory to help with debug */ | |
28 | memset(drvdata->vaddr, 0, drvdata->size); | |
29 | ||
30 | CS_UNLOCK(drvdata->base); | |
31 | ||
32 | /* Wait for TMCSReady bit to be set */ | |
33 | tmc_wait_for_tmcready(drvdata); | |
34 | ||
35 | writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); | |
36 | writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); | |
37 | ||
38 | axictl = readl_relaxed(drvdata->base + TMC_AXICTL); | |
cd407abd SP |
39 | axictl &= ~TMC_AXICTL_CLEAR_MASK; |
40 | axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); | |
41 | axictl |= TMC_AXICTL_AXCACHE_OS; | |
ebab6a7d SP |
42 | |
43 | if (tmc_etr_has_cap(drvdata, TMC_ETR_AXI_ARCACHE)) { | |
44 | axictl &= ~TMC_AXICTL_ARCACHE_MASK; | |
45 | axictl |= TMC_AXICTL_ARCACHE_OS; | |
46 | } | |
47 | ||
6c6ed1e2 | 48 | writel_relaxed(axictl, drvdata->base + TMC_AXICTL); |
6f6ab4fc | 49 | tmc_write_dba(drvdata, drvdata->paddr); |
f2e931a2 SP |
50 | /* |
51 | * If the TMC pointers must be programmed before the session, | |
52 | * we have to set it properly (i.e, RRP/RWP to base address and | |
53 | * STS to "not full"). | |
54 | */ | |
55 | if (tmc_etr_has_cap(drvdata, TMC_ETR_SAVE_RESTORE)) { | |
56 | tmc_write_rrp(drvdata, drvdata->paddr); | |
57 | tmc_write_rwp(drvdata, drvdata->paddr); | |
58 | sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; | |
59 | writel_relaxed(sts, drvdata->base + TMC_STS); | |
60 | } | |
6c6ed1e2 | 61 | |
6c6ed1e2 MP |
62 | writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | |
63 | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | | |
64 | TMC_FFCR_TRIGON_TRIGIN, | |
65 | drvdata->base + TMC_FFCR); | |
66 | writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); | |
67 | tmc_enable_hw(drvdata); | |
68 | ||
69 | CS_LOCK(drvdata->base); | |
70 | } | |
71 | ||
72 | static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) | |
73 | { | |
0c3fc4d5 | 74 | const u32 *barrier; |
6f6ab4fc | 75 | u32 val; |
0c3fc4d5 | 76 | u32 *temp; |
6f6ab4fc | 77 | u64 rwp; |
6c6ed1e2 | 78 | |
6f6ab4fc | 79 | rwp = tmc_read_rwp(drvdata); |
6c6ed1e2 MP |
80 | val = readl_relaxed(drvdata->base + TMC_STS); |
81 | ||
8505feae SP |
82 | /* |
83 | * Adjust the buffer to point to the beginning of the trace data | |
84 | * and update the available trace data. | |
85 | */ | |
1c9cbe11 | 86 | if (val & TMC_STS_FULL) { |
6c6ed1e2 | 87 | drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; |
8505feae | 88 | drvdata->len = drvdata->size; |
0c3fc4d5 MP |
89 | |
90 | barrier = barrier_pkt; | |
91 | temp = (u32 *)drvdata->buf; | |
92 | ||
93 | while (*barrier) { | |
94 | *temp = *barrier; | |
95 | temp++; | |
96 | barrier++; | |
97 | } | |
98 | ||
8505feae | 99 | } else { |
6c6ed1e2 | 100 | drvdata->buf = drvdata->vaddr; |
8505feae SP |
101 | drvdata->len = rwp - drvdata->paddr; |
102 | } | |
6c6ed1e2 MP |
103 | } |
104 | ||
4525412a | 105 | static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) |
6c6ed1e2 MP |
106 | { |
107 | CS_UNLOCK(drvdata->base); | |
108 | ||
109 | tmc_flush_and_stop(drvdata); | |
a40318fb MP |
110 | /* |
111 | * When operating in sysFS mode the content of the buffer needs to be | |
112 | * read before the TMC is disabled. | |
113 | */ | |
297ab90f | 114 | if (drvdata->mode == CS_MODE_SYSFS) |
a40318fb | 115 | tmc_etr_dump_hw(drvdata); |
6c6ed1e2 MP |
116 | tmc_disable_hw(drvdata); |
117 | ||
118 | CS_LOCK(drvdata->base); | |
119 | } | |
120 | ||
c38e505e | 121 | static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) |
6c6ed1e2 | 122 | { |
de546197 MP |
123 | int ret = 0; |
124 | bool used = false; | |
6c6ed1e2 | 125 | unsigned long flags; |
de546197 MP |
126 | void __iomem *vaddr = NULL; |
127 | dma_addr_t paddr; | |
6c6ed1e2 MP |
128 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
129 | ||
de546197 MP |
130 | |
131 | /* | |
132 | * If we don't have a buffer release the lock and allocate memory. | |
133 | * Otherwise keep the lock and move along. | |
134 | */ | |
6c6ed1e2 | 135 | spin_lock_irqsave(&drvdata->spinlock, flags); |
de546197 | 136 | if (!drvdata->vaddr) { |
6c6ed1e2 | 137 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
de546197 MP |
138 | |
139 | /* | |
140 | * Contiguous memory can't be allocated while a spinlock is | |
141 | * held. As such allocate memory here and free it if a buffer | |
142 | * has already been allocated (from a previous session). | |
143 | */ | |
144 | vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size, | |
145 | &paddr, GFP_KERNEL); | |
146 | if (!vaddr) | |
147 | return -ENOMEM; | |
148 | ||
149 | /* Let's try again */ | |
150 | spin_lock_irqsave(&drvdata->spinlock, flags); | |
151 | } | |
152 | ||
153 | if (drvdata->reading) { | |
154 | ret = -EBUSY; | |
155 | goto out; | |
156 | } | |
157 | ||
f2facc33 MP |
158 | /* |
159 | * In sysFS mode we can have multiple writers per sink. Since this | |
160 | * sink is already enabled no memory is needed and the HW need not be | |
161 | * touched. | |
162 | */ | |
297ab90f | 163 | if (drvdata->mode == CS_MODE_SYSFS) |
f2facc33 MP |
164 | goto out; |
165 | ||
de546197 MP |
166 | /* |
167 | * If drvdata::buf == NULL, use the memory allocated above. | |
168 | * Otherwise a buffer still exists from a previous session, so | |
169 | * simply use that. | |
170 | */ | |
171 | if (drvdata->buf == NULL) { | |
172 | used = true; | |
173 | drvdata->vaddr = vaddr; | |
174 | drvdata->paddr = paddr; | |
175 | drvdata->buf = drvdata->vaddr; | |
6c6ed1e2 MP |
176 | } |
177 | ||
297ab90f | 178 | drvdata->mode = CS_MODE_SYSFS; |
6c6ed1e2 | 179 | tmc_etr_enable_hw(drvdata); |
de546197 | 180 | out: |
6c6ed1e2 MP |
181 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
182 | ||
de546197 MP |
183 | /* Free memory outside the spinlock if need be */ |
184 | if (!used && vaddr) | |
185 | dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); | |
186 | ||
187 | if (!ret) | |
188 | dev_info(drvdata->dev, "TMC-ETR enabled\n"); | |
189 | ||
190 | return ret; | |
6c6ed1e2 MP |
191 | } |
192 | ||
c38e505e | 193 | static int tmc_enable_etr_sink_perf(struct coresight_device *csdev) |
b217601e MP |
194 | { |
195 | int ret = 0; | |
b217601e MP |
196 | unsigned long flags; |
197 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
198 | ||
b217601e MP |
199 | spin_lock_irqsave(&drvdata->spinlock, flags); |
200 | if (drvdata->reading) { | |
201 | ret = -EINVAL; | |
202 | goto out; | |
203 | } | |
204 | ||
b217601e MP |
205 | /* |
206 | * In Perf mode there can be only one writer per sink. There | |
207 | * is also no need to continue if the ETR is already operated | |
208 | * from sysFS. | |
209 | */ | |
297ab90f | 210 | if (drvdata->mode != CS_MODE_DISABLED) { |
b217601e MP |
211 | ret = -EINVAL; |
212 | goto out; | |
213 | } | |
214 | ||
297ab90f | 215 | drvdata->mode = CS_MODE_PERF; |
b217601e MP |
216 | tmc_etr_enable_hw(drvdata); |
217 | out: | |
218 | spin_unlock_irqrestore(&drvdata->spinlock, flags); | |
219 | ||
220 | return ret; | |
221 | } | |
222 | ||
223 | static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) | |
224 | { | |
225 | switch (mode) { | |
226 | case CS_MODE_SYSFS: | |
c38e505e | 227 | return tmc_enable_etr_sink_sysfs(csdev); |
b217601e | 228 | case CS_MODE_PERF: |
c38e505e | 229 | return tmc_enable_etr_sink_perf(csdev); |
b217601e MP |
230 | } |
231 | ||
232 | /* We shouldn't be here */ | |
233 | return -EINVAL; | |
234 | } | |
235 | ||
6c6ed1e2 MP |
236 | static void tmc_disable_etr_sink(struct coresight_device *csdev) |
237 | { | |
238 | unsigned long flags; | |
239 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
240 | ||
241 | spin_lock_irqsave(&drvdata->spinlock, flags); | |
242 | if (drvdata->reading) { | |
243 | spin_unlock_irqrestore(&drvdata->spinlock, flags); | |
244 | return; | |
245 | } | |
246 | ||
f2facc33 | 247 | /* Disable the TMC only if it needs to */ |
297ab90f | 248 | if (drvdata->mode != CS_MODE_DISABLED) { |
f2facc33 | 249 | tmc_etr_disable_hw(drvdata); |
297ab90f SP |
250 | drvdata->mode = CS_MODE_DISABLED; |
251 | } | |
f2facc33 | 252 | |
6c6ed1e2 MP |
253 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
254 | ||
255 | dev_info(drvdata->dev, "TMC-ETR disabled\n"); | |
256 | } | |
257 | ||
258 | static const struct coresight_ops_sink tmc_etr_sink_ops = { | |
259 | .enable = tmc_enable_etr_sink, | |
260 | .disable = tmc_disable_etr_sink, | |
261 | }; | |
262 | ||
263 | const struct coresight_ops tmc_etr_cs_ops = { | |
264 | .sink_ops = &tmc_etr_sink_ops, | |
265 | }; | |
4525412a MP |
266 | |
267 | int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) | |
268 | { | |
de546197 | 269 | int ret = 0; |
4525412a MP |
270 | unsigned long flags; |
271 | ||
272 | /* config types are set a boot time and never change */ | |
273 | if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) | |
274 | return -EINVAL; | |
275 | ||
276 | spin_lock_irqsave(&drvdata->spinlock, flags); | |
f74debbe MP |
277 | if (drvdata->reading) { |
278 | ret = -EBUSY; | |
279 | goto out; | |
280 | } | |
4525412a | 281 | |
b217601e | 282 | /* Don't interfere if operated from Perf */ |
297ab90f | 283 | if (drvdata->mode == CS_MODE_PERF) { |
b217601e MP |
284 | ret = -EINVAL; |
285 | goto out; | |
286 | } | |
287 | ||
de546197 MP |
288 | /* If drvdata::buf is NULL the trace data has been read already */ |
289 | if (drvdata->buf == NULL) { | |
290 | ret = -EINVAL; | |
291 | goto out; | |
292 | } | |
293 | ||
4525412a | 294 | /* Disable the TMC if need be */ |
297ab90f | 295 | if (drvdata->mode == CS_MODE_SYSFS) |
4525412a MP |
296 | tmc_etr_disable_hw(drvdata); |
297 | ||
298 | drvdata->reading = true; | |
de546197 | 299 | out: |
4525412a MP |
300 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
301 | ||
b217601e | 302 | return ret; |
4525412a MP |
303 | } |
304 | ||
305 | int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) | |
306 | { | |
307 | unsigned long flags; | |
de546197 MP |
308 | dma_addr_t paddr; |
309 | void __iomem *vaddr = NULL; | |
4525412a MP |
310 | |
311 | /* config types are set a boot time and never change */ | |
312 | if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) | |
313 | return -EINVAL; | |
314 | ||
315 | spin_lock_irqsave(&drvdata->spinlock, flags); | |
316 | ||
317 | /* RE-enable the TMC if need be */ | |
297ab90f | 318 | if (drvdata->mode == CS_MODE_SYSFS) { |
de546197 MP |
319 | /* |
320 | * The trace run will continue with the same allocated trace | |
f3b8172f SP |
321 | * buffer. The trace buffer is cleared in tmc_etr_enable_hw(), |
322 | * so we don't have to explicitly clear it. Also, since the | |
323 | * tracer is still enabled drvdata::buf can't be NULL. | |
de546197 | 324 | */ |
4525412a | 325 | tmc_etr_enable_hw(drvdata); |
de546197 MP |
326 | } else { |
327 | /* | |
328 | * The ETR is not tracing and the buffer was just read. | |
329 | * As such prepare to free the trace buffer. | |
330 | */ | |
331 | vaddr = drvdata->vaddr; | |
332 | paddr = drvdata->paddr; | |
8e215298 | 333 | drvdata->buf = drvdata->vaddr = NULL; |
de546197 | 334 | } |
4525412a MP |
335 | |
336 | drvdata->reading = false; | |
337 | spin_unlock_irqrestore(&drvdata->spinlock, flags); | |
338 | ||
de546197 MP |
339 | /* Free allocated memory out side of the spinlock */ |
340 | if (vaddr) | |
341 | dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); | |
342 | ||
4525412a MP |
343 | return 0; |
344 | } |