.. | .. |
---|
63 | 63 | #define ETB_FFSR_BIT 1 |
---|
64 | 64 | #define ETB_FRAME_SIZE_WORDS 4 |
---|
65 | 65 | |
---|
| 66 | +DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); |
---|
| 67 | + |
---|
66 | 68 | /** |
---|
67 | 69 | * struct etb_drvdata - specifics associated to an ETB component |
---|
68 | 70 | * @base: memory mapped base address for this component. |
---|
69 | | - * @dev: the device entity associated to this component. |
---|
70 | 71 | * @atclk: optional clock for the core parts of the ETB. |
---|
71 | 72 | * @csdev: component vitals needed by the framework. |
---|
72 | 73 | * @miscdev: specifics to handle "/dev/xyz.etb" entry. |
---|
73 | 74 | * @spinlock: only one at a time pls. |
---|
74 | 75 | * @reading: synchronise user space access to etb buffer. |
---|
| 76 | + * @pid: Process ID of the process being monitored by the session |
---|
| 77 | + * that is using this component. |
---|
75 | 78 | * @buf: area of memory where ETB buffer content gets sent. |
---|
76 | 79 | * @mode: this ETB is being used. |
---|
77 | 80 | * @buffer_depth: size of @buf. |
---|
.. | .. |
---|
79 | 82 | */ |
---|
80 | 83 | struct etb_drvdata { |
---|
81 | 84 | void __iomem *base; |
---|
82 | | - struct device *dev; |
---|
83 | 85 | struct clk *atclk; |
---|
84 | 86 | struct coresight_device *csdev; |
---|
85 | 87 | struct miscdevice miscdev; |
---|
86 | 88 | spinlock_t spinlock; |
---|
87 | 89 | local_t reading; |
---|
| 90 | + pid_t pid; |
---|
88 | 91 | u8 *buf; |
---|
89 | 92 | u32 mode; |
---|
90 | 93 | u32 buffer_depth; |
---|
.. | .. |
---|
94 | 97 | static int etb_set_buffer(struct coresight_device *csdev, |
---|
95 | 98 | struct perf_output_handle *handle); |
---|
96 | 99 | |
---|
97 | | -static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) |
---|
| 100 | +static inline unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) |
---|
98 | 101 | { |
---|
99 | | - u32 depth = 0; |
---|
100 | | - |
---|
101 | | - pm_runtime_get_sync(drvdata->dev); |
---|
102 | | - |
---|
103 | | - /* RO registers don't need locking */ |
---|
104 | | - depth = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); |
---|
105 | | - |
---|
106 | | - pm_runtime_put(drvdata->dev); |
---|
107 | | - return depth; |
---|
| 102 | + return readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); |
---|
108 | 103 | } |
---|
109 | 104 | |
---|
110 | 105 | static void __etb_enable_hw(struct etb_drvdata *drvdata) |
---|
.. | .. |
---|
137 | 132 | |
---|
138 | 133 | static int etb_enable_hw(struct etb_drvdata *drvdata) |
---|
139 | 134 | { |
---|
140 | | - int rc = coresight_claim_device(drvdata->base); |
---|
| 135 | + int rc = coresight_claim_device(drvdata->csdev); |
---|
141 | 136 | |
---|
142 | 137 | if (rc) |
---|
143 | 138 | return rc; |
---|
.. | .. |
---|
177 | 172 | static int etb_enable_perf(struct coresight_device *csdev, void *data) |
---|
178 | 173 | { |
---|
179 | 174 | int ret = 0; |
---|
| 175 | + pid_t pid; |
---|
180 | 176 | unsigned long flags; |
---|
181 | 177 | struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
| 178 | + struct perf_output_handle *handle = data; |
---|
| 179 | + struct cs_buffers *buf = etm_perf_sink_config(handle); |
---|
182 | 180 | |
---|
183 | 181 | spin_lock_irqsave(&drvdata->spinlock, flags); |
---|
184 | 182 | |
---|
185 | | - /* No need to continue if the component is already in use. */ |
---|
186 | | - if (drvdata->mode != CS_MODE_DISABLED) { |
---|
| 183 | + /* No need to continue if the component is already in used by sysFS. */ |
---|
| 184 | + if (drvdata->mode == CS_MODE_SYSFS) { |
---|
187 | 185 | ret = -EBUSY; |
---|
| 186 | + goto out; |
---|
| 187 | + } |
---|
| 188 | + |
---|
| 189 | + /* Get a handle on the pid of the process to monitor */ |
---|
| 190 | + pid = buf->pid; |
---|
| 191 | + |
---|
| 192 | + if (drvdata->pid != -1 && drvdata->pid != pid) { |
---|
| 193 | + ret = -EBUSY; |
---|
| 194 | + goto out; |
---|
| 195 | + } |
---|
| 196 | + |
---|
| 197 | + /* |
---|
| 198 | + * No HW configuration is needed if the sink is already in |
---|
| 199 | + * use for this session. |
---|
| 200 | + */ |
---|
| 201 | + if (drvdata->pid == pid) { |
---|
| 202 | + atomic_inc(csdev->refcnt); |
---|
188 | 203 | goto out; |
---|
189 | 204 | } |
---|
190 | 205 | |
---|
.. | .. |
---|
193 | 208 | * the perf buffer. So we can perform the step before we turn the |
---|
194 | 209 | * ETB on and leave without cleaning up. |
---|
195 | 210 | */ |
---|
196 | | - ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); |
---|
| 211 | + ret = etb_set_buffer(csdev, handle); |
---|
197 | 212 | if (ret) |
---|
198 | 213 | goto out; |
---|
199 | 214 | |
---|
200 | 215 | ret = etb_enable_hw(drvdata); |
---|
201 | 216 | if (!ret) { |
---|
| 217 | + /* Associate with monitored process. */ |
---|
| 218 | + drvdata->pid = pid; |
---|
202 | 219 | drvdata->mode = CS_MODE_PERF; |
---|
203 | 220 | atomic_inc(csdev->refcnt); |
---|
204 | 221 | } |
---|
.. | .. |
---|
211 | 228 | static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) |
---|
212 | 229 | { |
---|
213 | 230 | int ret; |
---|
214 | | - struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
215 | 231 | |
---|
216 | 232 | switch (mode) { |
---|
217 | 233 | case CS_MODE_SYSFS: |
---|
.. | .. |
---|
228 | 244 | if (ret) |
---|
229 | 245 | return ret; |
---|
230 | 246 | |
---|
231 | | - dev_dbg(drvdata->dev, "ETB enabled\n"); |
---|
| 247 | + dev_dbg(&csdev->dev, "ETB enabled\n"); |
---|
232 | 248 | return 0; |
---|
233 | 249 | } |
---|
234 | 250 | |
---|
235 | 251 | static void __etb_disable_hw(struct etb_drvdata *drvdata) |
---|
236 | 252 | { |
---|
237 | 253 | u32 ffcr; |
---|
| 254 | + struct device *dev = &drvdata->csdev->dev; |
---|
| 255 | + struct csdev_access *csa = &drvdata->csdev->access; |
---|
238 | 256 | |
---|
239 | 257 | CS_UNLOCK(drvdata->base); |
---|
240 | 258 | |
---|
.. | .. |
---|
246 | 264 | ffcr |= ETB_FFCR_FON_MAN; |
---|
247 | 265 | writel_relaxed(ffcr, drvdata->base + ETB_FFCR); |
---|
248 | 266 | |
---|
249 | | - if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) { |
---|
250 | | - dev_err(drvdata->dev, |
---|
| 267 | + if (coresight_timeout(csa, ETB_FFCR, ETB_FFCR_BIT, 0)) { |
---|
| 268 | + dev_err(dev, |
---|
251 | 269 | "timeout while waiting for completion of Manual Flush\n"); |
---|
252 | 270 | } |
---|
253 | 271 | |
---|
254 | 272 | /* disable trace capture */ |
---|
255 | 273 | writel_relaxed(0x0, drvdata->base + ETB_CTL_REG); |
---|
256 | 274 | |
---|
257 | | - if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) { |
---|
258 | | - dev_err(drvdata->dev, |
---|
| 275 | + if (coresight_timeout(csa, ETB_FFSR, ETB_FFSR_BIT, 1)) { |
---|
| 276 | + dev_err(dev, |
---|
259 | 277 | "timeout while waiting for Formatter to Stop\n"); |
---|
260 | 278 | } |
---|
261 | 279 | |
---|
.. | .. |
---|
270 | 288 | u32 read_data, depth; |
---|
271 | 289 | u32 read_ptr, write_ptr; |
---|
272 | 290 | u32 frame_off, frame_endoff; |
---|
| 291 | + struct device *dev = &drvdata->csdev->dev; |
---|
273 | 292 | |
---|
274 | 293 | CS_UNLOCK(drvdata->base); |
---|
275 | 294 | |
---|
.. | .. |
---|
279 | 298 | frame_off = write_ptr % ETB_FRAME_SIZE_WORDS; |
---|
280 | 299 | frame_endoff = ETB_FRAME_SIZE_WORDS - frame_off; |
---|
281 | 300 | if (frame_off) { |
---|
282 | | - dev_err(drvdata->dev, |
---|
| 301 | + dev_err(dev, |
---|
283 | 302 | "write_ptr: %lu not aligned to formatter frame size\n", |
---|
284 | 303 | (unsigned long)write_ptr); |
---|
285 | | - dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n", |
---|
| 304 | + dev_err(dev, "frameoff: %lu, frame_endoff: %lu\n", |
---|
286 | 305 | (unsigned long)frame_off, (unsigned long)frame_endoff); |
---|
287 | 306 | write_ptr += frame_endoff; |
---|
288 | 307 | } |
---|
.. | .. |
---|
326 | 345 | { |
---|
327 | 346 | __etb_disable_hw(drvdata); |
---|
328 | 347 | etb_dump_hw(drvdata); |
---|
329 | | - coresight_disclaim_device(drvdata->base); |
---|
| 348 | + coresight_disclaim_device(drvdata->csdev); |
---|
330 | 349 | } |
---|
331 | 350 | |
---|
332 | 351 | static int etb_disable(struct coresight_device *csdev) |
---|
.. | .. |
---|
344 | 363 | /* Complain if we (somehow) got out of sync */ |
---|
345 | 364 | WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); |
---|
346 | 365 | etb_disable_hw(drvdata); |
---|
| 366 | + /* Dissociate from monitored process. */ |
---|
| 367 | + drvdata->pid = -1; |
---|
347 | 368 | drvdata->mode = CS_MODE_DISABLED; |
---|
348 | 369 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
349 | 370 | |
---|
350 | | - dev_dbg(drvdata->dev, "ETB disabled\n"); |
---|
| 371 | + dev_dbg(&csdev->dev, "ETB disabled\n"); |
---|
351 | 372 | return 0; |
---|
352 | 373 | } |
---|
353 | 374 | |
---|
.. | .. |
---|
355 | 376 | struct perf_event *event, void **pages, |
---|
356 | 377 | int nr_pages, bool overwrite) |
---|
357 | 378 | { |
---|
358 | | - int node, cpu = event->cpu; |
---|
| 379 | + int node; |
---|
359 | 380 | struct cs_buffers *buf; |
---|
360 | 381 | |
---|
361 | | - node = (cpu == -1) ? NUMA_NO_NODE : cpu_to_node(cpu); |
---|
| 382 | + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); |
---|
362 | 383 | |
---|
363 | 384 | buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); |
---|
364 | 385 | if (!buf) |
---|
365 | 386 | return NULL; |
---|
366 | 387 | |
---|
| 388 | + buf->pid = task_pid_nr(event->owner); |
---|
367 | 389 | buf->snapshot = overwrite; |
---|
368 | 390 | buf->nr_pages = nr_pages; |
---|
369 | 391 | buf->data_pages = pages; |
---|
.. | .. |
---|
412 | 434 | const u32 *barrier; |
---|
413 | 435 | u32 read_ptr, write_ptr, capacity; |
---|
414 | 436 | u32 status, read_data; |
---|
415 | | - unsigned long offset, to_read, flags; |
---|
| 437 | + unsigned long offset, to_read = 0, flags; |
---|
416 | 438 | struct cs_buffers *buf = sink_config; |
---|
417 | 439 | struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
418 | 440 | |
---|
.. | .. |
---|
422 | 444 | capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; |
---|
423 | 445 | |
---|
424 | 446 | spin_lock_irqsave(&drvdata->spinlock, flags); |
---|
| 447 | + |
---|
| 448 | + /* Don't do anything if another tracer is using this sink */ |
---|
| 449 | + if (atomic_read(csdev->refcnt) != 1) |
---|
| 450 | + goto out; |
---|
| 451 | + |
---|
425 | 452 | __etb_disable_hw(drvdata); |
---|
426 | 453 | CS_UNLOCK(drvdata->base); |
---|
427 | 454 | |
---|
.. | .. |
---|
435 | 462 | * chance to fix things. |
---|
436 | 463 | */ |
---|
437 | 464 | if (write_ptr % ETB_FRAME_SIZE_WORDS) { |
---|
438 | | - dev_err(drvdata->dev, |
---|
| 465 | + dev_err(&csdev->dev, |
---|
439 | 466 | "write_ptr: %lu not aligned to formatter frame size\n", |
---|
440 | 467 | (unsigned long)write_ptr); |
---|
441 | 468 | |
---|
.. | .. |
---|
501 | 528 | |
---|
502 | 529 | cur = buf->cur; |
---|
503 | 530 | offset = buf->offset; |
---|
504 | | - barrier = barrier_pkt; |
---|
| 531 | + barrier = coresight_barrier_pkt; |
---|
505 | 532 | |
---|
506 | 533 | for (i = 0; i < to_read; i += 4) { |
---|
507 | 534 | buf_ptr = buf->data_pages[cur] + offset; |
---|
.. | .. |
---|
529 | 556 | writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); |
---|
530 | 557 | |
---|
531 | 558 | /* |
---|
532 | | - * In snapshot mode we have to update the handle->head to point |
---|
533 | | - * to the new location. |
---|
| 559 | + * In snapshot mode we simply increment the head by the number of byte |
---|
| 560 | + * that were written. User space function cs_etm_find_snapshot() will |
---|
| 561 | + * figure out how many bytes to get from the AUX buffer based on the |
---|
| 562 | + * position of the head. |
---|
534 | 563 | */ |
---|
535 | | - if (buf->snapshot) { |
---|
536 | | - handle->head = (cur * PAGE_SIZE) + offset; |
---|
537 | | - to_read = buf->nr_pages << PAGE_SHIFT; |
---|
538 | | - } |
---|
| 564 | + if (buf->snapshot) |
---|
| 565 | + handle->head += to_read; |
---|
| 566 | + |
---|
539 | 567 | __etb_enable_hw(drvdata); |
---|
540 | 568 | CS_LOCK(drvdata->base); |
---|
| 569 | +out: |
---|
541 | 570 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
542 | 571 | |
---|
543 | 572 | return to_read; |
---|
.. | .. |
---|
567 | 596 | } |
---|
568 | 597 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
569 | 598 | |
---|
570 | | - dev_dbg(drvdata->dev, "ETB dumped\n"); |
---|
| 599 | + dev_dbg(&drvdata->csdev->dev, "ETB dumped\n"); |
---|
571 | 600 | } |
---|
572 | 601 | |
---|
573 | 602 | static int etb_open(struct inode *inode, struct file *file) |
---|
.. | .. |
---|
578 | 607 | if (local_cmpxchg(&drvdata->reading, 0, 1)) |
---|
579 | 608 | return -EBUSY; |
---|
580 | 609 | |
---|
581 | | - dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); |
---|
| 610 | + dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); |
---|
582 | 611 | return 0; |
---|
583 | 612 | } |
---|
584 | 613 | |
---|
.. | .. |
---|
588 | 617 | u32 depth; |
---|
589 | 618 | struct etb_drvdata *drvdata = container_of(file->private_data, |
---|
590 | 619 | struct etb_drvdata, miscdev); |
---|
| 620 | + struct device *dev = &drvdata->csdev->dev; |
---|
591 | 621 | |
---|
592 | 622 | etb_dump(drvdata); |
---|
593 | 623 | |
---|
.. | .. |
---|
596 | 626 | len = depth * 4 - *ppos; |
---|
597 | 627 | |
---|
598 | 628 | if (copy_to_user(data, drvdata->buf + *ppos, len)) { |
---|
599 | | - dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); |
---|
| 629 | + dev_dbg(dev, |
---|
| 630 | + "%s: copy_to_user failed\n", __func__); |
---|
600 | 631 | return -EFAULT; |
---|
601 | 632 | } |
---|
602 | 633 | |
---|
603 | 634 | *ppos += len; |
---|
604 | 635 | |
---|
605 | | - dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", |
---|
| 636 | + dev_dbg(dev, "%s: %zu bytes copied, %d bytes left\n", |
---|
606 | 637 | __func__, len, (int)(depth * 4 - *ppos)); |
---|
607 | 638 | return len; |
---|
608 | 639 | } |
---|
.. | .. |
---|
613 | 644 | struct etb_drvdata, miscdev); |
---|
614 | 645 | local_set(&drvdata->reading, 0); |
---|
615 | 646 | |
---|
616 | | - dev_dbg(drvdata->dev, "%s: released\n", __func__); |
---|
| 647 | + dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); |
---|
617 | 648 | return 0; |
---|
618 | 649 | } |
---|
619 | 650 | |
---|
.. | .. |
---|
689 | 720 | .name = "mgmt", |
---|
690 | 721 | }; |
---|
691 | 722 | |
---|
692 | | -const struct attribute_group *coresight_etb_groups[] = { |
---|
| 723 | +static const struct attribute_group *coresight_etb_groups[] = { |
---|
693 | 724 | &coresight_etb_group, |
---|
694 | 725 | &coresight_etb_mgmt_group, |
---|
695 | 726 | NULL, |
---|
.. | .. |
---|
704 | 735 | struct etb_drvdata *drvdata; |
---|
705 | 736 | struct resource *res = &adev->res; |
---|
706 | 737 | struct coresight_desc desc = { 0 }; |
---|
707 | | - struct device_node *np = adev->dev.of_node; |
---|
708 | 738 | |
---|
709 | | - if (np) { |
---|
710 | | - pdata = of_get_coresight_platform_data(dev, np); |
---|
711 | | - if (IS_ERR(pdata)) |
---|
712 | | - return PTR_ERR(pdata); |
---|
713 | | - adev->dev.platform_data = pdata; |
---|
714 | | - } |
---|
| 739 | + desc.name = coresight_alloc_device_name(&etb_devs, dev); |
---|
| 740 | + if (!desc.name) |
---|
| 741 | + return -ENOMEM; |
---|
715 | 742 | |
---|
716 | 743 | drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); |
---|
717 | 744 | if (!drvdata) |
---|
718 | 745 | return -ENOMEM; |
---|
719 | 746 | |
---|
720 | | - drvdata->dev = &adev->dev; |
---|
721 | 747 | drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ |
---|
722 | 748 | if (!IS_ERR(drvdata->atclk)) { |
---|
723 | 749 | ret = clk_prepare_enable(drvdata->atclk); |
---|
.. | .. |
---|
732 | 758 | return PTR_ERR(base); |
---|
733 | 759 | |
---|
734 | 760 | drvdata->base = base; |
---|
| 761 | + desc.access = CSDEV_ACCESS_IOMEM(base); |
---|
735 | 762 | |
---|
736 | 763 | spin_lock_init(&drvdata->spinlock); |
---|
737 | 764 | |
---|
738 | 765 | drvdata->buffer_depth = etb_get_buffer_depth(drvdata); |
---|
739 | | - pm_runtime_put(&adev->dev); |
---|
740 | 766 | |
---|
741 | 767 | if (drvdata->buffer_depth & 0x80000000) |
---|
742 | 768 | return -EINVAL; |
---|
.. | .. |
---|
745 | 771 | drvdata->buffer_depth, 4, GFP_KERNEL); |
---|
746 | 772 | if (!drvdata->buf) |
---|
747 | 773 | return -ENOMEM; |
---|
| 774 | + |
---|
| 775 | + /* This device is not associated with a session */ |
---|
| 776 | + drvdata->pid = -1; |
---|
| 777 | + |
---|
| 778 | + pdata = coresight_get_platform_data(dev); |
---|
| 779 | + if (IS_ERR(pdata)) |
---|
| 780 | + return PTR_ERR(pdata); |
---|
| 781 | + adev->dev.platform_data = pdata; |
---|
748 | 782 | |
---|
749 | 783 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
---|
750 | 784 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; |
---|
.. | .. |
---|
756 | 790 | if (IS_ERR(drvdata->csdev)) |
---|
757 | 791 | return PTR_ERR(drvdata->csdev); |
---|
758 | 792 | |
---|
759 | | - drvdata->miscdev.name = pdata->name; |
---|
| 793 | + drvdata->miscdev.name = desc.name; |
---|
760 | 794 | drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; |
---|
761 | 795 | drvdata->miscdev.fops = &etb_fops; |
---|
762 | 796 | ret = misc_register(&drvdata->miscdev); |
---|
763 | 797 | if (ret) |
---|
764 | 798 | goto err_misc_register; |
---|
765 | 799 | |
---|
| 800 | + pm_runtime_put(&adev->dev); |
---|
766 | 801 | return 0; |
---|
767 | 802 | |
---|
768 | 803 | err_misc_register: |
---|
769 | 804 | coresight_unregister(drvdata->csdev); |
---|
770 | 805 | return ret; |
---|
| 806 | +} |
---|
| 807 | + |
---|
| 808 | +static void etb_remove(struct amba_device *adev) |
---|
| 809 | +{ |
---|
| 810 | + struct etb_drvdata *drvdata = dev_get_drvdata(&adev->dev); |
---|
| 811 | + |
---|
| 812 | + /* |
---|
| 813 | + * Since misc_open() holds a refcount on the f_ops, which is |
---|
| 814 | + * etb fops in this case, device is there until last file |
---|
| 815 | + * handler to this device is closed. |
---|
| 816 | + */ |
---|
| 817 | + misc_deregister(&drvdata->miscdev); |
---|
| 818 | + coresight_unregister(drvdata->csdev); |
---|
771 | 819 | } |
---|
772 | 820 | |
---|
773 | 821 | #ifdef CONFIG_PM |
---|
.. | .. |
---|
804 | 852 | { 0, 0}, |
---|
805 | 853 | }; |
---|
806 | 854 | |
---|
| 855 | +MODULE_DEVICE_TABLE(amba, etb_ids); |
---|
| 856 | + |
---|
807 | 857 | static struct amba_driver etb_driver = { |
---|
808 | 858 | .drv = { |
---|
809 | 859 | .name = "coresight-etb10", |
---|
.. | .. |
---|
813 | 863 | |
---|
814 | 864 | }, |
---|
815 | 865 | .probe = etb_probe, |
---|
| 866 | + .remove = etb_remove, |
---|
816 | 867 | .id_table = etb_ids, |
---|
817 | 868 | }; |
---|
818 | | -builtin_amba_driver(etb_driver); |
---|
| 869 | + |
---|
| 870 | +module_amba_driver(etb_driver); |
---|
| 871 | + |
---|
| 872 | +MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>"); |
---|
| 873 | +MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); |
---|
| 874 | +MODULE_DESCRIPTION("Arm CoreSight Embedded Trace Buffer driver"); |
---|
| 875 | +MODULE_LICENSE("GPL v2"); |
---|