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