Commit | Line | Data |
---|---|---|
ad0dfdfd | 1 | // SPDX-License-Identifier: GPL-2.0 |
bc4bf7fe | 2 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. |
941943cf PG |
3 | * |
4 | * Description: CoreSight Trace Memory Controller driver | |
bc4bf7fe PP |
5 | */ |
6 | ||
7 | #include <linux/kernel.h> | |
bc4bf7fe PP |
8 | #include <linux/init.h> |
9 | #include <linux/types.h> | |
10 | #include <linux/device.h> | |
11 | #include <linux/io.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/fs.h> | |
14 | #include <linux/miscdevice.h> | |
15 | #include <linux/uaccess.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/dma-mapping.h> | |
18 | #include <linux/spinlock.h> | |
32398ef6 | 19 | #include <linux/pm_runtime.h> |
bc4bf7fe PP |
20 | #include <linux/of.h> |
21 | #include <linux/coresight.h> | |
22 | #include <linux/amba/bus.h> | |
23 | ||
24 | #include "coresight-priv.h" | |
4c324b5f | 25 | #include "coresight-tmc.h" |
bc4bf7fe | 26 | |
6c6ed1e2 | 27 | void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
28 | { |
29 | /* Ensure formatter, unformatter and hardware fifo are empty */ | |
30 | if (coresight_timeout(drvdata->base, | |
580ff804 | 31 | TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { |
bc4bf7fe | 32 | dev_err(drvdata->dev, |
67337e8d | 33 | "timeout while waiting for TMC to be Ready\n"); |
bc4bf7fe PP |
34 | } |
35 | } | |
36 | ||
6c6ed1e2 | 37 | void tmc_flush_and_stop(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
38 | { |
39 | u32 ffcr; | |
40 | ||
41 | ffcr = readl_relaxed(drvdata->base + TMC_FFCR); | |
42 | ffcr |= TMC_FFCR_STOP_ON_FLUSH; | |
43 | writel_relaxed(ffcr, drvdata->base + TMC_FFCR); | |
a8ab4268 | 44 | ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT); |
bc4bf7fe PP |
45 | writel_relaxed(ffcr, drvdata->base + TMC_FFCR); |
46 | /* Ensure flush completes */ | |
47 | if (coresight_timeout(drvdata->base, | |
48 | TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { | |
49 | dev_err(drvdata->dev, | |
67337e8d | 50 | "timeout while waiting for completion of Manual Flush\n"); |
bc4bf7fe PP |
51 | } |
52 | ||
580ff804 | 53 | tmc_wait_for_tmcready(drvdata); |
bc4bf7fe PP |
54 | } |
55 | ||
6c6ed1e2 | 56 | void tmc_enable_hw(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
57 | { |
58 | writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL); | |
59 | } | |
60 | ||
6c6ed1e2 | 61 | void tmc_disable_hw(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
62 | { |
63 | writel_relaxed(0x0, drvdata->base + TMC_CTL); | |
64 | } | |
65 | ||
bc4bf7fe PP |
66 | static int tmc_read_prepare(struct tmc_drvdata *drvdata) |
67 | { | |
b1789b79 | 68 | int ret = 0; |
bc4bf7fe | 69 | |
b1789b79 MP |
70 | switch (drvdata->config_type) { |
71 | case TMC_CONFIG_TYPE_ETB: | |
b1789b79 | 72 | case TMC_CONFIG_TYPE_ETF: |
4525412a | 73 | ret = tmc_read_prepare_etb(drvdata); |
b1789b79 MP |
74 | break; |
75 | case TMC_CONFIG_TYPE_ETR: | |
4525412a | 76 | ret = tmc_read_prepare_etr(drvdata); |
b1789b79 MP |
77 | break; |
78 | default: | |
79 | ret = -EINVAL; | |
bc4bf7fe | 80 | } |
b1789b79 | 81 | |
4525412a MP |
82 | if (!ret) |
83 | dev_info(drvdata->dev, "TMC read start\n"); | |
84 | ||
bc4bf7fe PP |
85 | return ret; |
86 | } | |
87 | ||
f74debbe | 88 | static int tmc_read_unprepare(struct tmc_drvdata *drvdata) |
bc4bf7fe | 89 | { |
4525412a | 90 | int ret = 0; |
bc4bf7fe | 91 | |
b1789b79 MP |
92 | switch (drvdata->config_type) { |
93 | case TMC_CONFIG_TYPE_ETB: | |
b1789b79 | 94 | case TMC_CONFIG_TYPE_ETF: |
4525412a | 95 | ret = tmc_read_unprepare_etb(drvdata); |
b1789b79 MP |
96 | break; |
97 | case TMC_CONFIG_TYPE_ETR: | |
4525412a | 98 | ret = tmc_read_unprepare_etr(drvdata); |
b1789b79 MP |
99 | break; |
100 | default: | |
4525412a | 101 | ret = -EINVAL; |
bc4bf7fe | 102 | } |
b1789b79 | 103 | |
4525412a MP |
104 | if (!ret) |
105 | dev_info(drvdata->dev, "TMC read end\n"); | |
f74debbe MP |
106 | |
107 | return ret; | |
bc4bf7fe PP |
108 | } |
109 | ||
110 | static int tmc_open(struct inode *inode, struct file *file) | |
111 | { | |
f74debbe | 112 | int ret; |
bc4bf7fe PP |
113 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
114 | struct tmc_drvdata, miscdev); | |
bc4bf7fe PP |
115 | |
116 | ret = tmc_read_prepare(drvdata); | |
117 | if (ret) | |
118 | return ret; | |
f74debbe | 119 | |
bc4bf7fe PP |
120 | nonseekable_open(inode, file); |
121 | ||
122 | dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); | |
123 | return 0; | |
124 | } | |
125 | ||
3495722a SP |
126 | static inline ssize_t tmc_get_sysfs_trace(struct tmc_drvdata *drvdata, |
127 | loff_t pos, size_t len, char **bufpp) | |
128 | { | |
129 | switch (drvdata->config_type) { | |
130 | case TMC_CONFIG_TYPE_ETB: | |
131 | case TMC_CONFIG_TYPE_ETF: | |
132 | return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp); | |
133 | case TMC_CONFIG_TYPE_ETR: | |
134 | return tmc_etr_get_sysfs_trace(drvdata, pos, len, bufpp); | |
135 | } | |
136 | ||
137 | return -EINVAL; | |
138 | } | |
139 | ||
bc4bf7fe PP |
140 | static ssize_t tmc_read(struct file *file, char __user *data, size_t len, |
141 | loff_t *ppos) | |
142 | { | |
3495722a SP |
143 | char *bufp; |
144 | ssize_t actual; | |
bc4bf7fe PP |
145 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
146 | struct tmc_drvdata, miscdev); | |
3495722a SP |
147 | actual = tmc_get_sysfs_trace(drvdata, *ppos, len, &bufp); |
148 | if (actual <= 0) | |
149 | return 0; | |
bc4bf7fe | 150 | |
3495722a | 151 | if (copy_to_user(data, bufp, actual)) { |
bc4bf7fe PP |
152 | dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); |
153 | return -EFAULT; | |
154 | } | |
155 | ||
3495722a SP |
156 | *ppos += actual; |
157 | dev_dbg(drvdata->dev, "%zu bytes copied\n", actual); | |
bc4bf7fe | 158 | |
3495722a | 159 | return actual; |
bc4bf7fe PP |
160 | } |
161 | ||
162 | static int tmc_release(struct inode *inode, struct file *file) | |
163 | { | |
f74debbe | 164 | int ret; |
bc4bf7fe PP |
165 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
166 | struct tmc_drvdata, miscdev); | |
167 | ||
f74debbe MP |
168 | ret = tmc_read_unprepare(drvdata); |
169 | if (ret) | |
170 | return ret; | |
bc4bf7fe | 171 | |
bc4bf7fe PP |
172 | dev_dbg(drvdata->dev, "%s: released\n", __func__); |
173 | return 0; | |
174 | } | |
175 | ||
176 | static const struct file_operations tmc_fops = { | |
177 | .owner = THIS_MODULE, | |
178 | .open = tmc_open, | |
179 | .read = tmc_read, | |
180 | .release = tmc_release, | |
181 | .llseek = no_llseek, | |
182 | }; | |
183 | ||
4f1ff3de MP |
184 | static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) |
185 | { | |
186 | enum tmc_mem_intf_width memwidth; | |
187 | ||
188 | /* | |
189 | * Excerpt from the TRM: | |
190 | * | |
191 | * DEVID::MEMWIDTH[10:8] | |
192 | * 0x2 Memory interface databus is 32 bits wide. | |
193 | * 0x3 Memory interface databus is 64 bits wide. | |
194 | * 0x4 Memory interface databus is 128 bits wide. | |
195 | * 0x5 Memory interface databus is 256 bits wide. | |
196 | */ | |
197 | switch (BMVAL(devid, 8, 10)) { | |
198 | case 0x2: | |
199 | memwidth = TMC_MEM_INTF_WIDTH_32BITS; | |
200 | break; | |
201 | case 0x3: | |
202 | memwidth = TMC_MEM_INTF_WIDTH_64BITS; | |
203 | break; | |
204 | case 0x4: | |
205 | memwidth = TMC_MEM_INTF_WIDTH_128BITS; | |
206 | break; | |
207 | case 0x5: | |
208 | memwidth = TMC_MEM_INTF_WIDTH_256BITS; | |
209 | break; | |
210 | default: | |
211 | memwidth = 0; | |
212 | } | |
213 | ||
214 | return memwidth; | |
215 | } | |
216 | ||
47675f6a SP |
217 | #define coresight_tmc_reg(name, offset) \ |
218 | coresight_simple_reg32(struct tmc_drvdata, name, offset) | |
219 | #define coresight_tmc_reg64(name, lo_off, hi_off) \ | |
220 | coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) | |
221 | ||
222 | coresight_tmc_reg(rsz, TMC_RSZ); | |
223 | coresight_tmc_reg(sts, TMC_STS); | |
224 | coresight_tmc_reg(trg, TMC_TRG); | |
225 | coresight_tmc_reg(ctl, TMC_CTL); | |
226 | coresight_tmc_reg(ffsr, TMC_FFSR); | |
227 | coresight_tmc_reg(ffcr, TMC_FFCR); | |
228 | coresight_tmc_reg(mode, TMC_MODE); | |
229 | coresight_tmc_reg(pscr, TMC_PSCR); | |
2b455339 | 230 | coresight_tmc_reg(axictl, TMC_AXICTL); |
47675f6a SP |
231 | coresight_tmc_reg(devid, CORESIGHT_DEVID); |
232 | coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); | |
233 | coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); | |
2b455339 | 234 | coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); |
7d83d177 MP |
235 | |
236 | static struct attribute *coresight_tmc_mgmt_attrs[] = { | |
237 | &dev_attr_rsz.attr, | |
238 | &dev_attr_sts.attr, | |
239 | &dev_attr_rrp.attr, | |
240 | &dev_attr_rwp.attr, | |
241 | &dev_attr_trg.attr, | |
242 | &dev_attr_ctl.attr, | |
243 | &dev_attr_ffsr.attr, | |
244 | &dev_attr_ffcr.attr, | |
245 | &dev_attr_mode.attr, | |
246 | &dev_attr_pscr.attr, | |
247 | &dev_attr_devid.attr, | |
2b455339 SP |
248 | &dev_attr_dba.attr, |
249 | &dev_attr_axictl.attr, | |
7d83d177 MP |
250 | NULL, |
251 | }; | |
a2d6e184 | 252 | |
0ef7528d BX |
253 | static ssize_t trigger_cntr_show(struct device *dev, |
254 | struct device_attribute *attr, char *buf) | |
bc4bf7fe PP |
255 | { |
256 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
257 | unsigned long val = drvdata->trigger_cntr; | |
258 | ||
259 | return sprintf(buf, "%#lx\n", val); | |
260 | } | |
261 | ||
262 | static ssize_t trigger_cntr_store(struct device *dev, | |
263 | struct device_attribute *attr, | |
264 | const char *buf, size_t size) | |
265 | { | |
266 | int ret; | |
267 | unsigned long val; | |
268 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
269 | ||
270 | ret = kstrtoul(buf, 16, &val); | |
271 | if (ret) | |
272 | return ret; | |
273 | ||
274 | drvdata->trigger_cntr = val; | |
275 | return size; | |
276 | } | |
277 | static DEVICE_ATTR_RW(trigger_cntr); | |
278 | ||
7d83d177 | 279 | static struct attribute *coresight_tmc_attrs[] = { |
bc4bf7fe PP |
280 | &dev_attr_trigger_cntr.attr, |
281 | NULL, | |
282 | }; | |
bc4bf7fe | 283 | |
7d83d177 MP |
284 | static const struct attribute_group coresight_tmc_group = { |
285 | .attrs = coresight_tmc_attrs, | |
bc4bf7fe | 286 | }; |
bc4bf7fe | 287 | |
7d83d177 MP |
288 | static const struct attribute_group coresight_tmc_mgmt_group = { |
289 | .attrs = coresight_tmc_mgmt_attrs, | |
290 | .name = "mgmt", | |
291 | }; | |
292 | ||
293 | const struct attribute_group *coresight_tmc_groups[] = { | |
294 | &coresight_tmc_group, | |
295 | &coresight_tmc_mgmt_group, | |
bc4bf7fe PP |
296 | NULL, |
297 | }; | |
bc4bf7fe | 298 | |
2884132a SP |
299 | /* Detect and initialise the capabilities of a TMC ETR */ |
300 | static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, | |
301 | u32 devid, void *dev_caps) | |
302 | { | |
ff11f5bc SP |
303 | u32 dma_mask = 0; |
304 | ||
2884132a SP |
305 | /* Set the unadvertised capabilities */ |
306 | tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); | |
307 | ||
2e219345 SP |
308 | if (!(devid & TMC_DEVID_NOSCAT)) |
309 | tmc_etr_set_cap(drvdata, TMC_ETR_SG); | |
ff11f5bc SP |
310 | |
311 | /* Check if the AXI address width is available */ | |
312 | if (devid & TMC_DEVID_AXIAW_VALID) | |
313 | dma_mask = ((devid >> TMC_DEVID_AXIAW_SHIFT) & | |
314 | TMC_DEVID_AXIAW_MASK); | |
315 | ||
2884132a | 316 | /* |
ff11f5bc SP |
317 | * Unless specified in the device configuration, ETR uses a 40-bit |
318 | * AXI master in place of the embedded SRAM of ETB/ETF. | |
2884132a | 319 | */ |
ff11f5bc SP |
320 | switch (dma_mask) { |
321 | case 32: | |
322 | case 40: | |
323 | case 44: | |
324 | case 48: | |
325 | case 52: | |
326 | dev_info(drvdata->dev, "Detected dma mask %dbits\n", dma_mask); | |
327 | break; | |
328 | default: | |
329 | dma_mask = 40; | |
330 | } | |
331 | ||
332 | return dma_set_mask_and_coherent(drvdata->dev, DMA_BIT_MASK(dma_mask)); | |
2884132a SP |
333 | } |
334 | ||
bc4bf7fe PP |
335 | static int tmc_probe(struct amba_device *adev, const struct amba_id *id) |
336 | { | |
337 | int ret = 0; | |
338 | u32 devid; | |
339 | void __iomem *base; | |
340 | struct device *dev = &adev->dev; | |
341 | struct coresight_platform_data *pdata = NULL; | |
342 | struct tmc_drvdata *drvdata; | |
343 | struct resource *res = &adev->res; | |
9486295a | 344 | struct coresight_desc desc = { 0 }; |
bc4bf7fe PP |
345 | struct device_node *np = adev->dev.of_node; |
346 | ||
347 | if (np) { | |
348 | pdata = of_get_coresight_platform_data(dev, np); | |
3afd0634 SP |
349 | if (IS_ERR(pdata)) { |
350 | ret = PTR_ERR(pdata); | |
351 | goto out; | |
352 | } | |
bc4bf7fe PP |
353 | adev->dev.platform_data = pdata; |
354 | } | |
355 | ||
3afd0634 | 356 | ret = -ENOMEM; |
bc4bf7fe PP |
357 | drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); |
358 | if (!drvdata) | |
3afd0634 SP |
359 | goto out; |
360 | ||
bc4bf7fe PP |
361 | drvdata->dev = &adev->dev; |
362 | dev_set_drvdata(dev, drvdata); | |
363 | ||
364 | /* Validity for the resource is already checked by the AMBA core */ | |
365 | base = devm_ioremap_resource(dev, res); | |
3afd0634 SP |
366 | if (IS_ERR(base)) { |
367 | ret = PTR_ERR(base); | |
368 | goto out; | |
369 | } | |
bc4bf7fe PP |
370 | |
371 | drvdata->base = base; | |
372 | ||
373 | spin_lock_init(&drvdata->spinlock); | |
374 | ||
bc4bf7fe PP |
375 | devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); |
376 | drvdata->config_type = BMVAL(devid, 6, 7); | |
4f1ff3de | 377 | drvdata->memwidth = tmc_get_memwidth(devid); |
bc4bf7fe PP |
378 | |
379 | if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { | |
380 | if (np) | |
381 | ret = of_property_read_u32(np, | |
382 | "arm,buffer-size", | |
383 | &drvdata->size); | |
384 | if (ret) | |
385 | drvdata->size = SZ_1M; | |
386 | } else { | |
387 | drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; | |
388 | } | |
389 | ||
32398ef6 | 390 | pm_runtime_put(&adev->dev); |
bc4bf7fe | 391 | |
9486295a SP |
392 | desc.pdata = pdata; |
393 | desc.dev = dev; | |
394 | desc.groups = coresight_tmc_groups; | |
bc4bf7fe | 395 | |
99ac6f12 SP |
396 | switch (drvdata->config_type) { |
397 | case TMC_CONFIG_TYPE_ETB: | |
9486295a SP |
398 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
399 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; | |
400 | desc.ops = &tmc_etb_cs_ops; | |
99ac6f12 SP |
401 | break; |
402 | case TMC_CONFIG_TYPE_ETR: | |
9486295a SP |
403 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
404 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; | |
405 | desc.ops = &tmc_etr_cs_ops; | |
2884132a | 406 | ret = tmc_etr_setup_caps(drvdata, devid, id->data); |
a3959c50 RM |
407 | if (ret) |
408 | goto out; | |
99ac6f12 SP |
409 | break; |
410 | case TMC_CONFIG_TYPE_ETF: | |
9486295a SP |
411 | desc.type = CORESIGHT_DEV_TYPE_LINKSINK; |
412 | desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; | |
413 | desc.ops = &tmc_etf_cs_ops; | |
99ac6f12 SP |
414 | break; |
415 | default: | |
416 | pr_err("%s: Unsupported TMC config\n", pdata->name); | |
417 | ret = -EINVAL; | |
418 | goto out; | |
bc4bf7fe PP |
419 | } |
420 | ||
9486295a | 421 | drvdata->csdev = coresight_register(&desc); |
bc4bf7fe PP |
422 | if (IS_ERR(drvdata->csdev)) { |
423 | ret = PTR_ERR(drvdata->csdev); | |
3afd0634 | 424 | goto out; |
bc4bf7fe PP |
425 | } |
426 | ||
427 | drvdata->miscdev.name = pdata->name; | |
428 | drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; | |
429 | drvdata->miscdev.fops = &tmc_fops; | |
430 | ret = misc_register(&drvdata->miscdev); | |
431 | if (ret) | |
3afd0634 SP |
432 | coresight_unregister(drvdata->csdev); |
433 | out: | |
bc4bf7fe PP |
434 | return ret; |
435 | } | |
436 | ||
6f964e7c | 437 | static const struct amba_id tmc_ids[] = { |
bc4bf7fe | 438 | { |
0bbb194c SP |
439 | .id = 0x000bb961, |
440 | .mask = 0x000fffff, | |
bc4bf7fe | 441 | }, |
6495892c SP |
442 | { |
443 | /* Coresight SoC 600 TMC-ETR/ETS */ | |
444 | .id = 0x000bb9e8, | |
445 | .mask = 0x000fffff, | |
446 | .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS, | |
447 | }, | |
448 | { | |
449 | /* Coresight SoC 600 TMC-ETB */ | |
450 | .id = 0x000bb9e9, | |
451 | .mask = 0x000fffff, | |
452 | }, | |
453 | { | |
454 | /* Coresight SoC 600 TMC-ETF */ | |
455 | .id = 0x000bb9ea, | |
456 | .mask = 0x000fffff, | |
457 | }, | |
bc4bf7fe PP |
458 | { 0, 0}, |
459 | }; | |
460 | ||
461 | static struct amba_driver tmc_driver = { | |
462 | .drv = { | |
463 | .name = "coresight-tmc", | |
464 | .owner = THIS_MODULE, | |
b15f0fb6 | 465 | .suppress_bind_attrs = true, |
bc4bf7fe PP |
466 | }, |
467 | .probe = tmc_probe, | |
bc4bf7fe PP |
468 | .id_table = tmc_ids, |
469 | }; | |
941943cf | 470 | builtin_amba_driver(tmc_driver); |