]>
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> | |
c5ff7344 | 11 | #include <linux/idr.h> |
bc4bf7fe PP |
12 | #include <linux/io.h> |
13 | #include <linux/err.h> | |
14 | #include <linux/fs.h> | |
15 | #include <linux/miscdevice.h> | |
c5ff7344 | 16 | #include <linux/mutex.h> |
ed2cfb2b | 17 | #include <linux/property.h> |
bc4bf7fe PP |
18 | #include <linux/uaccess.h> |
19 | #include <linux/slab.h> | |
20 | #include <linux/dma-mapping.h> | |
21 | #include <linux/spinlock.h> | |
32398ef6 | 22 | #include <linux/pm_runtime.h> |
bc4bf7fe PP |
23 | #include <linux/of.h> |
24 | #include <linux/coresight.h> | |
25 | #include <linux/amba/bus.h> | |
26 | ||
27 | #include "coresight-priv.h" | |
4c324b5f | 28 | #include "coresight-tmc.h" |
bc4bf7fe | 29 | |
0f5f9b6b SP |
30 | DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); |
31 | DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); | |
32 | DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr"); | |
33 | ||
6c6ed1e2 | 34 | void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
35 | { |
36 | /* Ensure formatter, unformatter and hardware fifo are empty */ | |
37 | if (coresight_timeout(drvdata->base, | |
580ff804 | 38 | TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { |
9dd0a920 | 39 | dev_err(&drvdata->csdev->dev, |
67337e8d | 40 | "timeout while waiting for TMC to be Ready\n"); |
bc4bf7fe PP |
41 | } |
42 | } | |
43 | ||
6c6ed1e2 | 44 | void tmc_flush_and_stop(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
45 | { |
46 | u32 ffcr; | |
47 | ||
48 | ffcr = readl_relaxed(drvdata->base + TMC_FFCR); | |
49 | ffcr |= TMC_FFCR_STOP_ON_FLUSH; | |
50 | writel_relaxed(ffcr, drvdata->base + TMC_FFCR); | |
a8ab4268 | 51 | ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT); |
bc4bf7fe PP |
52 | writel_relaxed(ffcr, drvdata->base + TMC_FFCR); |
53 | /* Ensure flush completes */ | |
54 | if (coresight_timeout(drvdata->base, | |
55 | TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { | |
9dd0a920 | 56 | dev_err(&drvdata->csdev->dev, |
67337e8d | 57 | "timeout while waiting for completion of Manual Flush\n"); |
bc4bf7fe PP |
58 | } |
59 | ||
580ff804 | 60 | tmc_wait_for_tmcready(drvdata); |
bc4bf7fe PP |
61 | } |
62 | ||
6c6ed1e2 | 63 | void tmc_enable_hw(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
64 | { |
65 | writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL); | |
66 | } | |
67 | ||
6c6ed1e2 | 68 | void tmc_disable_hw(struct tmc_drvdata *drvdata) |
bc4bf7fe PP |
69 | { |
70 | writel_relaxed(0x0, drvdata->base + TMC_CTL); | |
71 | } | |
72 | ||
00bb485c MP |
73 | u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata) |
74 | { | |
75 | u32 mask = 0; | |
76 | ||
77 | /* | |
78 | * When moving RRP or an offset address forward, the new values must | |
79 | * be byte-address aligned to the width of the trace memory databus | |
80 | * _and_ to a frame boundary (16 byte), whichever is the biggest. For | |
81 | * example, for 32-bit, 64-bit and 128-bit wide trace memory, the four | |
82 | * LSBs must be 0s. For 256-bit wide trace memory, the five LSBs must | |
83 | * be 0s. | |
84 | */ | |
85 | switch (drvdata->memwidth) { | |
86 | case TMC_MEM_INTF_WIDTH_32BITS: | |
87 | /* fallthrough */ | |
88 | case TMC_MEM_INTF_WIDTH_64BITS: | |
89 | /* fallthrough */ | |
90 | case TMC_MEM_INTF_WIDTH_128BITS: | |
91 | mask = GENMASK(31, 4); | |
92 | break; | |
93 | case TMC_MEM_INTF_WIDTH_256BITS: | |
94 | mask = GENMASK(31, 5); | |
95 | break; | |
96 | } | |
97 | ||
98 | return mask; | |
99 | } | |
100 | ||
bc4bf7fe PP |
101 | static int tmc_read_prepare(struct tmc_drvdata *drvdata) |
102 | { | |
b1789b79 | 103 | int ret = 0; |
bc4bf7fe | 104 | |
b1789b79 MP |
105 | switch (drvdata->config_type) { |
106 | case TMC_CONFIG_TYPE_ETB: | |
b1789b79 | 107 | case TMC_CONFIG_TYPE_ETF: |
4525412a | 108 | ret = tmc_read_prepare_etb(drvdata); |
b1789b79 MP |
109 | break; |
110 | case TMC_CONFIG_TYPE_ETR: | |
4525412a | 111 | ret = tmc_read_prepare_etr(drvdata); |
b1789b79 MP |
112 | break; |
113 | default: | |
114 | ret = -EINVAL; | |
bc4bf7fe | 115 | } |
b1789b79 | 116 | |
4525412a | 117 | if (!ret) |
9dd0a920 | 118 | dev_dbg(&drvdata->csdev->dev, "TMC read start\n"); |
4525412a | 119 | |
bc4bf7fe PP |
120 | return ret; |
121 | } | |
122 | ||
f74debbe | 123 | static int tmc_read_unprepare(struct tmc_drvdata *drvdata) |
bc4bf7fe | 124 | { |
4525412a | 125 | int ret = 0; |
bc4bf7fe | 126 | |
b1789b79 MP |
127 | switch (drvdata->config_type) { |
128 | case TMC_CONFIG_TYPE_ETB: | |
b1789b79 | 129 | case TMC_CONFIG_TYPE_ETF: |
4525412a | 130 | ret = tmc_read_unprepare_etb(drvdata); |
b1789b79 MP |
131 | break; |
132 | case TMC_CONFIG_TYPE_ETR: | |
4525412a | 133 | ret = tmc_read_unprepare_etr(drvdata); |
b1789b79 MP |
134 | break; |
135 | default: | |
4525412a | 136 | ret = -EINVAL; |
bc4bf7fe | 137 | } |
b1789b79 | 138 | |
4525412a | 139 | if (!ret) |
9dd0a920 | 140 | dev_dbg(&drvdata->csdev->dev, "TMC read end\n"); |
f74debbe MP |
141 | |
142 | return ret; | |
bc4bf7fe PP |
143 | } |
144 | ||
145 | static int tmc_open(struct inode *inode, struct file *file) | |
146 | { | |
f74debbe | 147 | int ret; |
bc4bf7fe PP |
148 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
149 | struct tmc_drvdata, miscdev); | |
bc4bf7fe PP |
150 | |
151 | ret = tmc_read_prepare(drvdata); | |
152 | if (ret) | |
153 | return ret; | |
f74debbe | 154 | |
bc4bf7fe PP |
155 | nonseekable_open(inode, file); |
156 | ||
9dd0a920 | 157 | dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); |
bc4bf7fe PP |
158 | return 0; |
159 | } | |
160 | ||
3495722a SP |
161 | static inline ssize_t tmc_get_sysfs_trace(struct tmc_drvdata *drvdata, |
162 | loff_t pos, size_t len, char **bufpp) | |
163 | { | |
164 | switch (drvdata->config_type) { | |
165 | case TMC_CONFIG_TYPE_ETB: | |
166 | case TMC_CONFIG_TYPE_ETF: | |
167 | return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp); | |
168 | case TMC_CONFIG_TYPE_ETR: | |
169 | return tmc_etr_get_sysfs_trace(drvdata, pos, len, bufpp); | |
170 | } | |
171 | ||
172 | return -EINVAL; | |
173 | } | |
174 | ||
bc4bf7fe PP |
175 | static ssize_t tmc_read(struct file *file, char __user *data, size_t len, |
176 | loff_t *ppos) | |
177 | { | |
3495722a SP |
178 | char *bufp; |
179 | ssize_t actual; | |
bc4bf7fe PP |
180 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
181 | struct tmc_drvdata, miscdev); | |
3495722a SP |
182 | actual = tmc_get_sysfs_trace(drvdata, *ppos, len, &bufp); |
183 | if (actual <= 0) | |
184 | return 0; | |
bc4bf7fe | 185 | |
3495722a | 186 | if (copy_to_user(data, bufp, actual)) { |
9dd0a920 SP |
187 | dev_dbg(&drvdata->csdev->dev, |
188 | "%s: copy_to_user failed\n", __func__); | |
bc4bf7fe PP |
189 | return -EFAULT; |
190 | } | |
191 | ||
3495722a | 192 | *ppos += actual; |
9dd0a920 | 193 | dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual); |
bc4bf7fe | 194 | |
3495722a | 195 | return actual; |
bc4bf7fe PP |
196 | } |
197 | ||
198 | static int tmc_release(struct inode *inode, struct file *file) | |
199 | { | |
f74debbe | 200 | int ret; |
bc4bf7fe PP |
201 | struct tmc_drvdata *drvdata = container_of(file->private_data, |
202 | struct tmc_drvdata, miscdev); | |
203 | ||
f74debbe MP |
204 | ret = tmc_read_unprepare(drvdata); |
205 | if (ret) | |
206 | return ret; | |
bc4bf7fe | 207 | |
9dd0a920 | 208 | dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); |
bc4bf7fe PP |
209 | return 0; |
210 | } | |
211 | ||
212 | static const struct file_operations tmc_fops = { | |
213 | .owner = THIS_MODULE, | |
214 | .open = tmc_open, | |
215 | .read = tmc_read, | |
216 | .release = tmc_release, | |
217 | .llseek = no_llseek, | |
218 | }; | |
219 | ||
4f1ff3de MP |
220 | static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) |
221 | { | |
222 | enum tmc_mem_intf_width memwidth; | |
223 | ||
224 | /* | |
225 | * Excerpt from the TRM: | |
226 | * | |
227 | * DEVID::MEMWIDTH[10:8] | |
228 | * 0x2 Memory interface databus is 32 bits wide. | |
229 | * 0x3 Memory interface databus is 64 bits wide. | |
230 | * 0x4 Memory interface databus is 128 bits wide. | |
231 | * 0x5 Memory interface databus is 256 bits wide. | |
232 | */ | |
233 | switch (BMVAL(devid, 8, 10)) { | |
234 | case 0x2: | |
235 | memwidth = TMC_MEM_INTF_WIDTH_32BITS; | |
236 | break; | |
237 | case 0x3: | |
238 | memwidth = TMC_MEM_INTF_WIDTH_64BITS; | |
239 | break; | |
240 | case 0x4: | |
241 | memwidth = TMC_MEM_INTF_WIDTH_128BITS; | |
242 | break; | |
243 | case 0x5: | |
244 | memwidth = TMC_MEM_INTF_WIDTH_256BITS; | |
245 | break; | |
246 | default: | |
247 | memwidth = 0; | |
248 | } | |
249 | ||
250 | return memwidth; | |
251 | } | |
252 | ||
47675f6a SP |
253 | #define coresight_tmc_reg(name, offset) \ |
254 | coresight_simple_reg32(struct tmc_drvdata, name, offset) | |
255 | #define coresight_tmc_reg64(name, lo_off, hi_off) \ | |
256 | coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) | |
257 | ||
258 | coresight_tmc_reg(rsz, TMC_RSZ); | |
259 | coresight_tmc_reg(sts, TMC_STS); | |
260 | coresight_tmc_reg(trg, TMC_TRG); | |
261 | coresight_tmc_reg(ctl, TMC_CTL); | |
262 | coresight_tmc_reg(ffsr, TMC_FFSR); | |
263 | coresight_tmc_reg(ffcr, TMC_FFCR); | |
264 | coresight_tmc_reg(mode, TMC_MODE); | |
265 | coresight_tmc_reg(pscr, TMC_PSCR); | |
2b455339 | 266 | coresight_tmc_reg(axictl, TMC_AXICTL); |
8a4bc4f1 | 267 | coresight_tmc_reg(authstatus, TMC_AUTHSTATUS); |
47675f6a SP |
268 | coresight_tmc_reg(devid, CORESIGHT_DEVID); |
269 | coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); | |
270 | coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); | |
2b455339 | 271 | coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); |
7d83d177 MP |
272 | |
273 | static struct attribute *coresight_tmc_mgmt_attrs[] = { | |
274 | &dev_attr_rsz.attr, | |
275 | &dev_attr_sts.attr, | |
276 | &dev_attr_rrp.attr, | |
277 | &dev_attr_rwp.attr, | |
278 | &dev_attr_trg.attr, | |
279 | &dev_attr_ctl.attr, | |
280 | &dev_attr_ffsr.attr, | |
281 | &dev_attr_ffcr.attr, | |
282 | &dev_attr_mode.attr, | |
283 | &dev_attr_pscr.attr, | |
284 | &dev_attr_devid.attr, | |
2b455339 SP |
285 | &dev_attr_dba.attr, |
286 | &dev_attr_axictl.attr, | |
8a4bc4f1 | 287 | &dev_attr_authstatus.attr, |
7d83d177 MP |
288 | NULL, |
289 | }; | |
a2d6e184 | 290 | |
0ef7528d BX |
291 | static ssize_t trigger_cntr_show(struct device *dev, |
292 | struct device_attribute *attr, char *buf) | |
bc4bf7fe PP |
293 | { |
294 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
295 | unsigned long val = drvdata->trigger_cntr; | |
296 | ||
297 | return sprintf(buf, "%#lx\n", val); | |
298 | } | |
299 | ||
300 | static ssize_t trigger_cntr_store(struct device *dev, | |
301 | struct device_attribute *attr, | |
302 | const char *buf, size_t size) | |
303 | { | |
304 | int ret; | |
305 | unsigned long val; | |
306 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
307 | ||
308 | ret = kstrtoul(buf, 16, &val); | |
309 | if (ret) | |
310 | return ret; | |
311 | ||
312 | drvdata->trigger_cntr = val; | |
313 | return size; | |
314 | } | |
315 | static DEVICE_ATTR_RW(trigger_cntr); | |
316 | ||
c34cc23f SP |
317 | static ssize_t buffer_size_show(struct device *dev, |
318 | struct device_attribute *attr, char *buf) | |
319 | { | |
320 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
321 | ||
322 | return sprintf(buf, "%#x\n", drvdata->size); | |
323 | } | |
324 | ||
325 | static ssize_t buffer_size_store(struct device *dev, | |
326 | struct device_attribute *attr, | |
327 | const char *buf, size_t size) | |
328 | { | |
329 | int ret; | |
330 | unsigned long val; | |
331 | struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
332 | ||
333 | /* Only permitted for TMC-ETRs */ | |
334 | if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) | |
335 | return -EPERM; | |
336 | ||
337 | ret = kstrtoul(buf, 0, &val); | |
338 | if (ret) | |
339 | return ret; | |
340 | /* The buffer size should be page aligned */ | |
341 | if (val & (PAGE_SIZE - 1)) | |
342 | return -EINVAL; | |
343 | drvdata->size = val; | |
344 | return size; | |
345 | } | |
346 | ||
347 | static DEVICE_ATTR_RW(buffer_size); | |
348 | ||
7d83d177 | 349 | static struct attribute *coresight_tmc_attrs[] = { |
bc4bf7fe | 350 | &dev_attr_trigger_cntr.attr, |
c34cc23f | 351 | &dev_attr_buffer_size.attr, |
bc4bf7fe PP |
352 | NULL, |
353 | }; | |
bc4bf7fe | 354 | |
7d83d177 MP |
355 | static const struct attribute_group coresight_tmc_group = { |
356 | .attrs = coresight_tmc_attrs, | |
bc4bf7fe | 357 | }; |
bc4bf7fe | 358 | |
7d83d177 MP |
359 | static const struct attribute_group coresight_tmc_mgmt_group = { |
360 | .attrs = coresight_tmc_mgmt_attrs, | |
361 | .name = "mgmt", | |
362 | }; | |
363 | ||
e54d9c77 | 364 | static const struct attribute_group *coresight_tmc_groups[] = { |
7d83d177 MP |
365 | &coresight_tmc_group, |
366 | &coresight_tmc_mgmt_group, | |
bc4bf7fe PP |
367 | NULL, |
368 | }; | |
bc4bf7fe | 369 | |
9dd0a920 | 370 | static inline bool tmc_etr_can_use_sg(struct device *dev) |
ed2cfb2b | 371 | { |
9dd0a920 | 372 | return fwnode_property_present(dev->fwnode, "arm,scatter-gather"); |
ed2cfb2b SP |
373 | } |
374 | ||
8a4bc4f1 SP |
375 | static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata) |
376 | { | |
377 | u32 auth = readl_relaxed(drvdata->base + TMC_AUTHSTATUS); | |
378 | ||
379 | return (auth & TMC_AUTH_NSID_MASK) == 0x3; | |
380 | } | |
381 | ||
2884132a | 382 | /* Detect and initialise the capabilities of a TMC ETR */ |
9dd0a920 | 383 | static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) |
2884132a | 384 | { |
08be8747 | 385 | int rc; |
ff11f5bc | 386 | u32 dma_mask = 0; |
9dd0a920 | 387 | struct tmc_drvdata *drvdata = dev_get_drvdata(parent); |
ff11f5bc | 388 | |
8a4bc4f1 SP |
389 | if (!tmc_etr_has_non_secure_access(drvdata)) |
390 | return -EACCES; | |
391 | ||
2884132a SP |
392 | /* Set the unadvertised capabilities */ |
393 | tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); | |
394 | ||
9dd0a920 | 395 | if (!(devid & TMC_DEVID_NOSCAT) && tmc_etr_can_use_sg(parent)) |
2e219345 | 396 | tmc_etr_set_cap(drvdata, TMC_ETR_SG); |
ff11f5bc SP |
397 | |
398 | /* Check if the AXI address width is available */ | |
399 | if (devid & TMC_DEVID_AXIAW_VALID) | |
400 | dma_mask = ((devid >> TMC_DEVID_AXIAW_SHIFT) & | |
401 | TMC_DEVID_AXIAW_MASK); | |
402 | ||
2884132a | 403 | /* |
ff11f5bc SP |
404 | * Unless specified in the device configuration, ETR uses a 40-bit |
405 | * AXI master in place of the embedded SRAM of ETB/ETF. | |
2884132a | 406 | */ |
ff11f5bc SP |
407 | switch (dma_mask) { |
408 | case 32: | |
409 | case 40: | |
410 | case 44: | |
411 | case 48: | |
412 | case 52: | |
9dd0a920 | 413 | dev_info(parent, "Detected dma mask %dbits\n", dma_mask); |
ff11f5bc SP |
414 | break; |
415 | default: | |
416 | dma_mask = 40; | |
417 | } | |
418 | ||
9dd0a920 | 419 | rc = dma_set_mask_and_coherent(parent, DMA_BIT_MASK(dma_mask)); |
08be8747 | 420 | if (rc) |
9dd0a920 | 421 | dev_err(parent, "Failed to setup DMA mask: %d\n", rc); |
08be8747 | 422 | return rc; |
2884132a SP |
423 | } |
424 | ||
538eca25 SP |
425 | static u32 tmc_etr_get_default_buffer_size(struct device *dev) |
426 | { | |
427 | u32 size; | |
428 | ||
429 | if (fwnode_property_read_u32(dev->fwnode, "arm,buffer-size", &size)) | |
430 | size = SZ_1M; | |
431 | return size; | |
432 | } | |
433 | ||
bc4bf7fe PP |
434 | static int tmc_probe(struct amba_device *adev, const struct amba_id *id) |
435 | { | |
436 | int ret = 0; | |
437 | u32 devid; | |
438 | void __iomem *base; | |
439 | struct device *dev = &adev->dev; | |
440 | struct coresight_platform_data *pdata = NULL; | |
441 | struct tmc_drvdata *drvdata; | |
442 | struct resource *res = &adev->res; | |
9486295a | 443 | struct coresight_desc desc = { 0 }; |
0f5f9b6b | 444 | struct coresight_dev_list *dev_list = NULL; |
f03631da | 445 | |
3afd0634 | 446 | ret = -ENOMEM; |
bc4bf7fe PP |
447 | drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); |
448 | if (!drvdata) | |
3afd0634 SP |
449 | goto out; |
450 | ||
bc4bf7fe PP |
451 | dev_set_drvdata(dev, drvdata); |
452 | ||
453 | /* Validity for the resource is already checked by the AMBA core */ | |
454 | base = devm_ioremap_resource(dev, res); | |
3afd0634 SP |
455 | if (IS_ERR(base)) { |
456 | ret = PTR_ERR(base); | |
457 | goto out; | |
458 | } | |
bc4bf7fe PP |
459 | |
460 | drvdata->base = base; | |
461 | ||
462 | spin_lock_init(&drvdata->spinlock); | |
463 | ||
bc4bf7fe PP |
464 | devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); |
465 | drvdata->config_type = BMVAL(devid, 6, 7); | |
4f1ff3de | 466 | drvdata->memwidth = tmc_get_memwidth(devid); |
8d03cfd1 MP |
467 | /* This device is not associated with a session */ |
468 | drvdata->pid = -1; | |
bc4bf7fe | 469 | |
538eca25 SP |
470 | if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) |
471 | drvdata->size = tmc_etr_get_default_buffer_size(dev); | |
472 | else | |
bc4bf7fe | 473 | drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; |
bc4bf7fe | 474 | |
9486295a SP |
475 | desc.dev = dev; |
476 | desc.groups = coresight_tmc_groups; | |
bc4bf7fe | 477 | |
99ac6f12 SP |
478 | switch (drvdata->config_type) { |
479 | case TMC_CONFIG_TYPE_ETB: | |
9486295a SP |
480 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
481 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; | |
482 | desc.ops = &tmc_etb_cs_ops; | |
0f5f9b6b | 483 | dev_list = &etb_devs; |
99ac6f12 SP |
484 | break; |
485 | case TMC_CONFIG_TYPE_ETR: | |
9486295a | 486 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
24497d38 | 487 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM; |
9486295a | 488 | desc.ops = &tmc_etr_cs_ops; |
9dd0a920 | 489 | ret = tmc_etr_setup_caps(dev, devid, |
e85fa28e | 490 | coresight_get_uci_data(id)); |
a3959c50 RM |
491 | if (ret) |
492 | goto out; | |
c5ff7344 MP |
493 | idr_init(&drvdata->idr); |
494 | mutex_init(&drvdata->idr_mutex); | |
0f5f9b6b | 495 | dev_list = &etr_devs; |
99ac6f12 SP |
496 | break; |
497 | case TMC_CONFIG_TYPE_ETF: | |
9486295a | 498 | desc.type = CORESIGHT_DEV_TYPE_LINKSINK; |
24497d38 | 499 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; |
9486295a SP |
500 | desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; |
501 | desc.ops = &tmc_etf_cs_ops; | |
0f5f9b6b | 502 | dev_list = &etf_devs; |
99ac6f12 SP |
503 | break; |
504 | default: | |
2ede79a6 | 505 | pr_err("%s: Unsupported TMC config\n", desc.name); |
99ac6f12 SP |
506 | ret = -EINVAL; |
507 | goto out; | |
bc4bf7fe PP |
508 | } |
509 | ||
0f5f9b6b SP |
510 | desc.name = coresight_alloc_device_name(dev_list, dev); |
511 | if (!desc.name) { | |
512 | ret = -ENOMEM; | |
513 | goto out; | |
514 | } | |
515 | ||
af7cfd0f SP |
516 | pdata = coresight_get_platform_data(dev); |
517 | if (IS_ERR(pdata)) { | |
518 | ret = PTR_ERR(pdata); | |
519 | goto out; | |
520 | } | |
521 | adev->dev.platform_data = pdata; | |
522 | desc.pdata = pdata; | |
523 | ||
9486295a | 524 | drvdata->csdev = coresight_register(&desc); |
bc4bf7fe PP |
525 | if (IS_ERR(drvdata->csdev)) { |
526 | ret = PTR_ERR(drvdata->csdev); | |
3afd0634 | 527 | goto out; |
bc4bf7fe PP |
528 | } |
529 | ||
2ede79a6 | 530 | drvdata->miscdev.name = desc.name; |
bc4bf7fe PP |
531 | drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; |
532 | drvdata->miscdev.fops = &tmc_fops; | |
533 | ret = misc_register(&drvdata->miscdev); | |
534 | if (ret) | |
3afd0634 | 535 | coresight_unregister(drvdata->csdev); |
26355695 SP |
536 | else |
537 | pm_runtime_put(&adev->dev); | |
3afd0634 | 538 | out: |
bc4bf7fe PP |
539 | return ret; |
540 | } | |
541 | ||
865d3079 SPR |
542 | static void tmc_shutdown(struct amba_device *adev) |
543 | { | |
544 | unsigned long flags; | |
545 | struct tmc_drvdata *drvdata = amba_get_drvdata(adev); | |
546 | ||
547 | spin_lock_irqsave(&drvdata->spinlock, flags); | |
548 | ||
549 | if (drvdata->mode == CS_MODE_DISABLED) | |
550 | goto out; | |
551 | ||
552 | if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) | |
553 | tmc_etr_disable_hw(drvdata); | |
554 | ||
555 | /* | |
556 | * We do not care about coresight unregister here unlike remove | |
557 | * callback which is required for making coresight modular since | |
558 | * the system is going down after this. | |
559 | */ | |
560 | out: | |
561 | spin_unlock_irqrestore(&drvdata->spinlock, flags); | |
562 | } | |
563 | ||
6f964e7c | 564 | static const struct amba_id tmc_ids[] = { |
e85fa28e ML |
565 | CS_AMBA_ID(0x000bb961), |
566 | /* Coresight SoC 600 TMC-ETR/ETS */ | |
567 | CS_AMBA_ID_DATA(0x000bb9e8, (unsigned long)CORESIGHT_SOC_600_ETR_CAPS), | |
568 | /* Coresight SoC 600 TMC-ETB */ | |
569 | CS_AMBA_ID(0x000bb9e9), | |
570 | /* Coresight SoC 600 TMC-ETF */ | |
571 | CS_AMBA_ID(0x000bb9ea), | |
bc4bf7fe PP |
572 | { 0, 0}, |
573 | }; | |
574 | ||
575 | static struct amba_driver tmc_driver = { | |
576 | .drv = { | |
577 | .name = "coresight-tmc", | |
578 | .owner = THIS_MODULE, | |
b15f0fb6 | 579 | .suppress_bind_attrs = true, |
bc4bf7fe PP |
580 | }, |
581 | .probe = tmc_probe, | |
865d3079 | 582 | .shutdown = tmc_shutdown, |
bc4bf7fe PP |
583 | .id_table = tmc_ids, |
584 | }; | |
941943cf | 585 | builtin_amba_driver(tmc_driver); |