2 * Intel(R) Trace Hub driver core
4 * Copyright (C) 2014-2015 Intel Corporation.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18 #include <linux/types.h>
19 #include <linux/module.h>
20 #include <linux/device.h>
21 #include <linux/sysfs.h>
22 #include <linux/kdev_t.h>
23 #include <linux/debugfs.h>
24 #include <linux/idr.h>
25 #include <linux/pci.h>
26 #include <linux/dma-mapping.h>
31 static DEFINE_IDA(intel_th_ida
);
33 static int intel_th_match(struct device
*dev
, struct device_driver
*driver
)
35 struct intel_th_driver
*thdrv
= to_intel_th_driver(driver
);
36 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
38 if (thdev
->type
== INTEL_TH_SWITCH
&&
39 (!thdrv
->enable
|| !thdrv
->disable
))
42 return !strcmp(thdev
->name
, driver
->name
);
45 static int intel_th_child_remove(struct device
*dev
, void *data
)
47 device_release_driver(dev
);
52 static int intel_th_probe(struct device
*dev
)
54 struct intel_th_driver
*thdrv
= to_intel_th_driver(dev
->driver
);
55 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
56 struct intel_th_driver
*hubdrv
;
57 struct intel_th_device
*hub
= NULL
;
60 if (thdev
->type
== INTEL_TH_SWITCH
)
63 hub
= to_intel_th_device(dev
->parent
);
65 if (!hub
|| !hub
->dev
.driver
)
68 hubdrv
= to_intel_th_driver(hub
->dev
.driver
);
70 ret
= thdrv
->probe(to_intel_th_device(dev
));
74 if (thdev
->type
== INTEL_TH_OUTPUT
&&
75 !intel_th_output_assigned(thdev
))
76 ret
= hubdrv
->assign(hub
, thdev
);
81 static int intel_th_remove(struct device
*dev
)
83 struct intel_th_driver
*thdrv
= to_intel_th_driver(dev
->driver
);
84 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
85 struct intel_th_device
*hub
= to_intel_th_device(dev
->parent
);
88 if (thdev
->type
== INTEL_TH_SWITCH
) {
89 err
= device_for_each_child(dev
, thdev
, intel_th_child_remove
);
96 if (intel_th_output_assigned(thdev
)) {
97 struct intel_th_driver
*hubdrv
=
98 to_intel_th_driver(dev
->parent
->driver
);
101 hubdrv
->unassign(hub
, thdev
);
107 static struct bus_type intel_th_bus
= {
110 .match
= intel_th_match
,
111 .probe
= intel_th_probe
,
112 .remove
= intel_th_remove
,
115 static void intel_th_device_free(struct intel_th_device
*thdev
);
117 static void intel_th_device_release(struct device
*dev
)
119 intel_th_device_free(to_intel_th_device(dev
));
122 static struct device_type intel_th_source_device_type
= {
123 .name
= "intel_th_source_device",
124 .release
= intel_th_device_release
,
127 static struct intel_th
*to_intel_th(struct intel_th_device
*thdev
)
130 * subdevice tree is flat: if this one is not a switch, its
133 if (thdev
->type
!= INTEL_TH_SWITCH
)
134 thdev
= to_intel_th_hub(thdev
);
136 if (WARN_ON_ONCE(!thdev
|| thdev
->type
!= INTEL_TH_SWITCH
))
139 return dev_get_drvdata(thdev
->dev
.parent
);
142 static char *intel_th_output_devnode(struct device
*dev
, umode_t
*mode
,
143 kuid_t
*uid
, kgid_t
*gid
)
145 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
146 struct intel_th
*th
= to_intel_th(thdev
);
150 node
= kasprintf(GFP_KERNEL
, "intel_th%d/%s%d", th
->id
,
151 thdev
->name
, thdev
->id
);
153 node
= kasprintf(GFP_KERNEL
, "intel_th%d/%s", th
->id
,
159 static ssize_t
port_show(struct device
*dev
, struct device_attribute
*attr
,
162 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
164 if (thdev
->output
.port
>= 0)
165 return scnprintf(buf
, PAGE_SIZE
, "%u\n", thdev
->output
.port
);
167 return scnprintf(buf
, PAGE_SIZE
, "unassigned\n");
170 static DEVICE_ATTR_RO(port
);
172 static int intel_th_output_activate(struct intel_th_device
*thdev
)
174 struct intel_th_driver
*thdrv
= to_intel_th_driver(thdev
->dev
.driver
);
177 return thdrv
->activate(thdev
);
179 intel_th_trace_enable(thdev
);
184 static void intel_th_output_deactivate(struct intel_th_device
*thdev
)
186 struct intel_th_driver
*thdrv
= to_intel_th_driver(thdev
->dev
.driver
);
188 if (thdrv
->deactivate
)
189 thdrv
->deactivate(thdev
);
191 intel_th_trace_disable(thdev
);
194 static ssize_t
active_show(struct device
*dev
, struct device_attribute
*attr
,
197 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
199 return scnprintf(buf
, PAGE_SIZE
, "%d\n", thdev
->output
.active
);
202 static ssize_t
active_store(struct device
*dev
, struct device_attribute
*attr
,
203 const char *buf
, size_t size
)
205 struct intel_th_device
*thdev
= to_intel_th_device(dev
);
209 ret
= kstrtoul(buf
, 10, &val
);
213 if (!!val
!= thdev
->output
.active
) {
215 ret
= intel_th_output_activate(thdev
);
217 intel_th_output_deactivate(thdev
);
220 return ret
? ret
: size
;
223 static DEVICE_ATTR_RW(active
);
225 static struct attribute
*intel_th_output_attrs
[] = {
227 &dev_attr_active
.attr
,
231 ATTRIBUTE_GROUPS(intel_th_output
);
233 static struct device_type intel_th_output_device_type
= {
234 .name
= "intel_th_output_device",
235 .groups
= intel_th_output_groups
,
236 .release
= intel_th_device_release
,
237 .devnode
= intel_th_output_devnode
,
240 static struct device_type intel_th_switch_device_type
= {
241 .name
= "intel_th_switch_device",
242 .release
= intel_th_device_release
,
245 static struct device_type
*intel_th_device_type
[] = {
246 [INTEL_TH_SOURCE
] = &intel_th_source_device_type
,
247 [INTEL_TH_OUTPUT
] = &intel_th_output_device_type
,
248 [INTEL_TH_SWITCH
] = &intel_th_switch_device_type
,
251 int intel_th_driver_register(struct intel_th_driver
*thdrv
)
253 if (!thdrv
->probe
|| !thdrv
->remove
)
256 thdrv
->driver
.bus
= &intel_th_bus
;
258 return driver_register(&thdrv
->driver
);
260 EXPORT_SYMBOL_GPL(intel_th_driver_register
);
262 void intel_th_driver_unregister(struct intel_th_driver
*thdrv
)
264 driver_unregister(&thdrv
->driver
);
266 EXPORT_SYMBOL_GPL(intel_th_driver_unregister
);
268 static struct intel_th_device
*
269 intel_th_device_alloc(struct intel_th
*th
, unsigned int type
, const char *name
,
272 struct device
*parent
;
273 struct intel_th_device
*thdev
;
275 if (type
== INTEL_TH_SWITCH
)
278 parent
= &th
->hub
->dev
;
280 thdev
= kzalloc(sizeof(*thdev
) + strlen(name
) + 1, GFP_KERNEL
);
287 strcpy(thdev
->name
, name
);
288 device_initialize(&thdev
->dev
);
289 thdev
->dev
.bus
= &intel_th_bus
;
290 thdev
->dev
.type
= intel_th_device_type
[type
];
291 thdev
->dev
.parent
= parent
;
292 thdev
->dev
.dma_mask
= parent
->dma_mask
;
293 thdev
->dev
.dma_parms
= parent
->dma_parms
;
294 dma_set_coherent_mask(&thdev
->dev
, parent
->coherent_dma_mask
);
296 dev_set_name(&thdev
->dev
, "%d-%s%d", th
->id
, name
, id
);
298 dev_set_name(&thdev
->dev
, "%d-%s", th
->id
, name
);
303 static int intel_th_device_add_resources(struct intel_th_device
*thdev
,
304 struct resource
*res
, int nres
)
308 r
= kmemdup(res
, sizeof(*res
) * nres
, GFP_KERNEL
);
313 thdev
->num_resources
= nres
;
318 static void intel_th_device_remove(struct intel_th_device
*thdev
)
320 device_del(&thdev
->dev
);
321 put_device(&thdev
->dev
);
324 static void intel_th_device_free(struct intel_th_device
*thdev
)
326 kfree(thdev
->resource
);
331 * Intel(R) Trace Hub subdevices
333 static struct intel_th_subdevice
{
335 struct resource res
[3];
341 } intel_th_subdevices
[TH_SUBDEVICE_MAX
] = {
346 .start
= REG_GTH_OFFSET
,
347 .end
= REG_GTH_OFFSET
+ REG_GTH_LENGTH
- 1,
348 .flags
= IORESOURCE_MEM
,
352 .type
= INTEL_TH_SWITCH
,
359 .start
= REG_MSU_OFFSET
,
360 .end
= REG_MSU_OFFSET
+ REG_MSU_LENGTH
- 1,
361 .flags
= IORESOURCE_MEM
,
364 .start
= BUF_MSU_OFFSET
,
365 .end
= BUF_MSU_OFFSET
+ BUF_MSU_LENGTH
- 1,
366 .flags
= IORESOURCE_MEM
,
371 .type
= INTEL_TH_OUTPUT
,
373 .scrpd
= SCRPD_MEM_IS_PRIM_DEST
| SCRPD_MSC0_IS_ENABLED
,
379 .start
= REG_MSU_OFFSET
,
380 .end
= REG_MSU_OFFSET
+ REG_MSU_LENGTH
- 1,
381 .flags
= IORESOURCE_MEM
,
384 .start
= BUF_MSU_OFFSET
,
385 .end
= BUF_MSU_OFFSET
+ BUF_MSU_LENGTH
- 1,
386 .flags
= IORESOURCE_MEM
,
391 .type
= INTEL_TH_OUTPUT
,
393 .scrpd
= SCRPD_MEM_IS_PRIM_DEST
| SCRPD_MSC1_IS_ENABLED
,
399 .start
= REG_STH_OFFSET
,
400 .end
= REG_STH_OFFSET
+ REG_STH_LENGTH
- 1,
401 .flags
= IORESOURCE_MEM
,
406 .flags
= IORESOURCE_MEM
,
411 .type
= INTEL_TH_SOURCE
,
417 .start
= REG_PTI_OFFSET
,
418 .end
= REG_PTI_OFFSET
+ REG_PTI_LENGTH
- 1,
419 .flags
= IORESOURCE_MEM
,
424 .type
= INTEL_TH_OUTPUT
,
426 .scrpd
= SCRPD_PTI_IS_PRIM_DEST
,
432 .start
= REG_DCIH_OFFSET
,
433 .end
= REG_DCIH_OFFSET
+ REG_DCIH_LENGTH
- 1,
434 .flags
= IORESOURCE_MEM
,
439 .type
= INTEL_TH_OUTPUT
,
443 static int intel_th_populate(struct intel_th
*th
, struct resource
*devres
,
444 unsigned int ndevres
, int irq
)
446 struct resource res
[3];
447 unsigned int req
= 0;
450 /* create devices for each intel_th_subdevice */
451 for (i
= 0; i
< ARRAY_SIZE(intel_th_subdevices
); i
++) {
452 struct intel_th_subdevice
*subdev
= &intel_th_subdevices
[i
];
453 struct intel_th_device
*thdev
;
456 thdev
= intel_th_device_alloc(th
, subdev
->type
, subdev
->name
,
463 memcpy(res
, subdev
->res
,
464 sizeof(struct resource
) * subdev
->nres
);
466 for (r
= 0; r
< subdev
->nres
; r
++) {
467 int bar
= TH_MMIO_CONFIG
;
470 * Take .end == 0 to mean 'take the whole bar',
471 * .start then tells us which bar it is. Default to
474 if (!res
[r
].end
&& res
[r
].flags
== IORESOURCE_MEM
) {
477 res
[r
].end
= resource_size(&devres
[bar
]) - 1;
480 if (res
[r
].flags
& IORESOURCE_MEM
) {
481 res
[r
].start
+= devres
[bar
].start
;
482 res
[r
].end
+= devres
[bar
].start
;
484 dev_dbg(th
->dev
, "%s:%d @ %pR\n",
485 subdev
->name
, r
, &res
[r
]);
486 } else if (res
[r
].flags
& IORESOURCE_IRQ
) {
491 err
= intel_th_device_add_resources(thdev
, res
, subdev
->nres
);
493 put_device(&thdev
->dev
);
497 if (subdev
->type
== INTEL_TH_OUTPUT
) {
498 thdev
->dev
.devt
= MKDEV(th
->major
, i
);
499 thdev
->output
.type
= subdev
->otype
;
500 thdev
->output
.port
= -1;
501 thdev
->output
.scratchpad
= subdev
->scrpd
;
504 err
= device_add(&thdev
->dev
);
506 put_device(&thdev
->dev
);
510 /* need switch driver to be loaded to enumerate the rest */
511 if (subdev
->type
== INTEL_TH_SWITCH
&& !req
) {
513 err
= request_module("intel_th_%s", subdev
->name
);
518 th
->thdev
[i
] = thdev
;
524 for (i
-- ; i
>= 0; i
--)
525 intel_th_device_remove(th
->thdev
[i
]);
530 static int match_devt(struct device
*dev
, void *data
)
532 dev_t devt
= (dev_t
)(unsigned long)data
;
534 return dev
->devt
== devt
;
537 static int intel_th_output_open(struct inode
*inode
, struct file
*file
)
539 const struct file_operations
*fops
;
540 struct intel_th_driver
*thdrv
;
544 dev
= bus_find_device(&intel_th_bus
, NULL
,
545 (void *)(unsigned long)inode
->i_rdev
,
547 if (!dev
|| !dev
->driver
)
550 thdrv
= to_intel_th_driver(dev
->driver
);
551 fops
= fops_get(thdrv
->fops
);
555 replace_fops(file
, fops
);
557 file
->private_data
= to_intel_th_device(dev
);
559 if (file
->f_op
->open
) {
560 err
= file
->f_op
->open(inode
, file
);
567 static const struct file_operations intel_th_output_fops
= {
568 .open
= intel_th_output_open
,
569 .llseek
= noop_llseek
,
573 * intel_th_alloc() - allocate a new Intel TH device and its subdevices
574 * @dev: parent device
575 * @devres: parent's resources
576 * @ndevres: number of resources
580 intel_th_alloc(struct device
*dev
, struct resource
*devres
,
581 unsigned int ndevres
, int irq
)
586 th
= kzalloc(sizeof(*th
), GFP_KERNEL
);
588 return ERR_PTR(-ENOMEM
);
590 th
->id
= ida_simple_get(&intel_th_ida
, 0, 0, GFP_KERNEL
);
596 th
->major
= __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS
,
597 "intel_th/output", &intel_th_output_fops
);
604 dev_set_drvdata(dev
, th
);
606 err
= intel_th_populate(th
, devres
, ndevres
, irq
);
613 __unregister_chrdev(th
->major
, 0, TH_POSSIBLE_OUTPUTS
,
617 ida_simple_remove(&intel_th_ida
, th
->id
);
624 EXPORT_SYMBOL_GPL(intel_th_alloc
);
626 void intel_th_free(struct intel_th
*th
)
630 for (i
= 0; i
< TH_SUBDEVICE_MAX
; i
++)
631 if (th
->thdev
[i
] != th
->hub
)
632 intel_th_device_remove(th
->thdev
[i
]);
634 intel_th_device_remove(th
->hub
);
636 __unregister_chrdev(th
->major
, 0, TH_POSSIBLE_OUTPUTS
,
639 ida_simple_remove(&intel_th_ida
, th
->id
);
643 EXPORT_SYMBOL_GPL(intel_th_free
);
646 * intel_th_trace_enable() - enable tracing for an output device
647 * @thdev: output device that requests tracing be enabled
649 int intel_th_trace_enable(struct intel_th_device
*thdev
)
651 struct intel_th_device
*hub
= to_intel_th_device(thdev
->dev
.parent
);
652 struct intel_th_driver
*hubdrv
= to_intel_th_driver(hub
->dev
.driver
);
654 if (WARN_ON_ONCE(hub
->type
!= INTEL_TH_SWITCH
))
657 if (WARN_ON_ONCE(thdev
->type
!= INTEL_TH_OUTPUT
))
660 hubdrv
->enable(hub
, &thdev
->output
);
664 EXPORT_SYMBOL_GPL(intel_th_trace_enable
);
667 * intel_th_trace_disable() - disable tracing for an output device
668 * @thdev: output device that requests tracing be disabled
670 int intel_th_trace_disable(struct intel_th_device
*thdev
)
672 struct intel_th_device
*hub
= to_intel_th_device(thdev
->dev
.parent
);
673 struct intel_th_driver
*hubdrv
= to_intel_th_driver(hub
->dev
.driver
);
675 WARN_ON_ONCE(hub
->type
!= INTEL_TH_SWITCH
);
676 if (WARN_ON_ONCE(thdev
->type
!= INTEL_TH_OUTPUT
))
679 hubdrv
->disable(hub
, &thdev
->output
);
683 EXPORT_SYMBOL_GPL(intel_th_trace_disable
);
685 int intel_th_set_output(struct intel_th_device
*thdev
,
688 struct intel_th_device
*hub
= to_intel_th_device(thdev
->dev
.parent
);
689 struct intel_th_driver
*hubdrv
= to_intel_th_driver(hub
->dev
.driver
);
691 if (!hubdrv
->set_output
)
694 return hubdrv
->set_output(hub
, master
);
696 EXPORT_SYMBOL_GPL(intel_th_set_output
);
698 static int __init
intel_th_init(void)
700 intel_th_debug_init();
702 return bus_register(&intel_th_bus
);
704 subsys_initcall(intel_th_init
);
706 static void __exit
intel_th_exit(void)
708 intel_th_debug_done();
710 bus_unregister(&intel_th_bus
);
712 module_exit(intel_th_exit
);
714 MODULE_LICENSE("GPL v2");
715 MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver");
716 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");