aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>2015-09-22 08:47:14 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-04 15:28:58 -0400
commit39f4034693b7c7bd1fe4cb58c93259d600f55561 (patch)
treedebe2c6087fd2d996dbdc6accd799522f2de25c3
parente3e5a3d3da3e68ac5a7ab0278dffec8353ca8174 (diff)
intel_th: Add driver infrastructure for Intel(R) Trace Hub devices
Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that produce, switch and output trace data from multiple hardware and software sources over several types of trace output ports encoded in System Trace Protocol (MIPI STPv2) and is intended to perform full system debugging. For these subdevices, we create a bus, where they can be discovered and configured by userspace software. This patch creates this bus infrastructure, three types of devices (source, output, switch), resource allocation, some callback mechanisms to facilitate communication between the subdevices' drivers and some common sysfs attributes. Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/ABI/testing/sysfs-bus-intel_th-output-devices13
-rw-r--r--Documentation/trace/intel_th.txt99
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/hwtracing/intel_th/Kconfig24
-rw-r--r--drivers/hwtracing/intel_th/Makefile3
-rw-r--r--drivers/hwtracing/intel_th/core.c692
-rw-r--r--drivers/hwtracing/intel_th/debug.c36
-rw-r--r--drivers/hwtracing/intel_th/debug.h34
-rw-r--r--drivers/hwtracing/intel_th/intel_th.h244
10 files changed, 1148 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-intel_th-output-devices b/Documentation/ABI/testing/sysfs-bus-intel_th-output-devices
new file mode 100644
index 000000000000..4d48a9451866
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-intel_th-output-devices
@@ -0,0 +1,13 @@
1What: /sys/bus/intel_th/devices/<intel_th_id>-<device><id>/active
2Date: June 2015
3KernelVersion: 4.3
4Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
5Description: (RW) Writes of 1 or 0 enable or disable trace output to this
6 output device. Reads return current status.
7
8What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/port
9Date: June 2015
10KernelVersion: 4.3
11Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
12Description: (RO) Port number, corresponding to this output device on the
13 switch (GTH).
diff --git a/Documentation/trace/intel_th.txt b/Documentation/trace/intel_th.txt
new file mode 100644
index 000000000000..f7fc5ba5df8d
--- /dev/null
+++ b/Documentation/trace/intel_th.txt
@@ -0,0 +1,99 @@
1Intel(R) Trace Hub (TH)
2=======================
3
4Overview
5--------
6
7Intel(R) Trace Hub (TH) is a set of hardware blocks that produce,
8switch and output trace data from multiple hardware and software
9sources over several types of trace output ports encoded in System
10Trace Protocol (MIPI STPv2) and is intended to perform full system
11debugging. For more information on the hardware, see Intel(R) Trace
12Hub developer's manual [1].
13
14It consists of trace sources, trace destinations (outputs) and a
15switch (Global Trace Hub, GTH). These devices are placed on a bus of
16their own ("intel_th"), where they can be discovered and configured
17via sysfs attributes.
18
19Currently, the following Intel TH subdevices (blocks) are supported:
20 - Software Trace Hub (STH), trace source, which is a System Trace
21 Module (STM) device,
22 - Memory Storage Unit (MSU), trace output, which allows storing
23 trace hub output in system memory,
24 - Parallel Trace Interface output (PTI), trace output to an external
25 debug host via a PTI port,
26 - Global Trace Hub (GTH), which is a switch and a central component
27 of Intel(R) Trace Hub architecture.
28
29Common attributes for output devices are described in
30Documentation/ABI/testing/sysfs-bus-intel_th-output-devices, the most
31notable of them is "active", which enables or disables trace output
32into that particular output device.
33
34GTH allows directing different STP masters into different output ports
35via its "masters" attribute group. More detailed GTH interface
36description is at Documentation/ABI/testing/sysfs-bus-intel_th-devices-gth.
37
38STH registers an stm class device, through which it provides interface
39to userspace and kernelspace software trace sources. See
40Documentation/tracing/stm.txt for more information on that.
41
42MSU can be configured to collect trace data into a system memory
43buffer, which can later on be read from its device nodes via read() or
44mmap() interface.
45
46On the whole, Intel(R) Trace Hub does not require any special
47userspace software to function; everything can be configured, started
48and collected via sysfs attributes, and device nodes.
49
50[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
51
52Bus and Subdevices
53------------------
54
55For each Intel TH device in the system a bus of its own is
56created and assigned an id number that reflects the order in which TH
57devices were emumerated. All TH subdevices (devices on intel_th bus)
58begin with this id: 0-gth, 0-msc0, 0-msc1, 0-pti, 0-sth, which is
59followed by device's name and an optional index.
60
61Output devices also get a device node in /dev/intel_thN, where N is
62the Intel TH device id. For example, MSU's memory buffers, when
63allocated, are accessible via /dev/intel_th0/msc{0,1}.
64
65Quick example
66-------------
67
68# figure out which GTH port is the first memory controller:
69
70$ cat /sys/bus/intel_th/devices/0-msc0/port
710
72
73# looks like it's port 0, configure master 33 to send data to port 0:
74
75$ echo 0 > /sys/bus/intel_th/devices/0-gth/masters/33
76
77# allocate a 2-windowed multiblock buffer on the first memory
78# controller, each with 64 pages:
79
80$ echo multi > /sys/bus/intel_th/devices/0-msc0/mode
81$ echo 64,64 > /sys/bus/intel_th/devices/0-msc0/nr_pages
82
83# enable wrapping for this controller, too:
84
85$ echo 1 > /sys/bus/intel_th/devices/0-msc0/wrap
86
87# and enable tracing into this port:
88
89$ echo 1 > /sys/bus/intel_th/devices/0-msc0/active
90
91# .. send data to master 33, see stm.txt for more details ..
92# .. wait for traces to pile up ..
93# .. and stop the trace:
94
95$ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
96
97# and now you can collect the trace from the device node:
98
99$ cat /dev/intel_th0/msc0 > my_stp_trace
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b6e1cea63f81..709488ae882e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -190,4 +190,6 @@ source "drivers/nvmem/Kconfig"
190 190
191source "drivers/hwtracing/stm/Kconfig" 191source "drivers/hwtracing/stm/Kconfig"
192 192
193source "drivers/hwtracing/intel_th/Kconfig"
194
193endmenu 195endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index e71b5e2a2c71..e63542dd7010 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,6 +165,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf/
165obj-$(CONFIG_RAS) += ras/ 165obj-$(CONFIG_RAS) += ras/
166obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ 166obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
167obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ 167obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
168obj-y += hwtracing/intel_th/
168obj-$(CONFIG_STM) += hwtracing/stm/ 169obj-$(CONFIG_STM) += hwtracing/stm/
169obj-$(CONFIG_ANDROID) += android/ 170obj-$(CONFIG_ANDROID) += android/
170obj-$(CONFIG_NVMEM) += nvmem/ 171obj-$(CONFIG_NVMEM) += nvmem/
diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig
new file mode 100644
index 000000000000..0cb01ee9090e
--- /dev/null
+++ b/drivers/hwtracing/intel_th/Kconfig
@@ -0,0 +1,24 @@
1config INTEL_TH
2 tristate "Intel(R) Trace Hub controller"
3 help
4 Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that
5 produce, switch and output trace data from multiple hardware and
6 software sources over several types of trace output ports encoded
7 in System Trace Protocol (MIPI STPv2) and is intended to perform
8 full system debugging.
9
10 This option enables intel_th bus and common code used by TH
11 subdevices to interact with each other and hardware and for
12 platform glue layers to drive Intel TH devices.
13
14 Say Y here to enable Intel(R) Trace Hub controller support.
15
16if INTEL_TH
17
18config INTEL_TH_DEBUG
19 bool "Intel(R) Trace Hub debugging"
20 depends on DEBUG_FS
21 help
22 Say Y here to enable debugging.
23
24endif
diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile
new file mode 100644
index 000000000000..dfd7906462da
--- /dev/null
+++ b/drivers/hwtracing/intel_th/Makefile
@@ -0,0 +1,3 @@
1obj-$(CONFIG_INTEL_TH) += intel_th.o
2intel_th-y := core.o
3intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
new file mode 100644
index 000000000000..165d3001c301
--- /dev/null
+++ b/drivers/hwtracing/intel_th/core.c
@@ -0,0 +1,692 @@
1/*
2 * Intel(R) Trace Hub driver core
3 *
4 * Copyright (C) 2014-2015 Intel Corporation.
5 *
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.
9 *
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
13 * more details.
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
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>
27
28#include "intel_th.h"
29#include "debug.h"
30
31static DEFINE_IDA(intel_th_ida);
32
33static int intel_th_match(struct device *dev, struct device_driver *driver)
34{
35 struct intel_th_driver *thdrv = to_intel_th_driver(driver);
36 struct intel_th_device *thdev = to_intel_th_device(dev);
37
38 if (thdev->type == INTEL_TH_SWITCH &&
39 (!thdrv->enable || !thdrv->disable))
40 return 0;
41
42 return !strcmp(thdev->name, driver->name);
43}
44
45static int intel_th_child_remove(struct device *dev, void *data)
46{
47 device_release_driver(dev);
48
49 return 0;
50}
51
52static int intel_th_probe(struct device *dev)
53{
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;
58 int ret;
59
60 if (thdev->type == INTEL_TH_SWITCH)
61 hub = thdev;
62 else if (dev->parent)
63 hub = to_intel_th_device(dev->parent);
64
65 if (!hub || !hub->dev.driver)
66 return -EPROBE_DEFER;
67
68 hubdrv = to_intel_th_driver(hub->dev.driver);
69
70 ret = thdrv->probe(to_intel_th_device(dev));
71 if (ret)
72 return ret;
73
74 if (thdev->type == INTEL_TH_OUTPUT &&
75 !intel_th_output_assigned(thdev))
76 ret = hubdrv->assign(hub, thdev);
77
78 return ret;
79}
80
81static int intel_th_remove(struct device *dev)
82{
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);
86 int err;
87
88 if (thdev->type == INTEL_TH_SWITCH) {
89 err = device_for_each_child(dev, thdev, intel_th_child_remove);
90 if (err)
91 return err;
92 }
93
94 thdrv->remove(thdev);
95
96 if (intel_th_output_assigned(thdev)) {
97 struct intel_th_driver *hubdrv =
98 to_intel_th_driver(dev->parent->driver);
99
100 if (hub->dev.driver)
101 hubdrv->unassign(hub, thdev);
102 }
103
104 return 0;
105}
106
107static struct bus_type intel_th_bus = {
108 .name = "intel_th",
109 .dev_attrs = NULL,
110 .match = intel_th_match,
111 .probe = intel_th_probe,
112 .remove = intel_th_remove,
113};
114
115static void intel_th_device_free(struct intel_th_device *thdev);
116
117static void intel_th_device_release(struct device *dev)
118{
119 intel_th_device_free(to_intel_th_device(dev));
120}
121
122static struct device_type intel_th_source_device_type = {
123 .name = "intel_th_source_device",
124 .release = intel_th_device_release,
125};
126
127static char *intel_th_output_devnode(struct device *dev, umode_t *mode,
128 kuid_t *uid, kgid_t *gid)
129{
130 struct intel_th_device *thdev = to_intel_th_device(dev);
131 char *node;
132
133 if (thdev->id >= 0)
134 node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", 0, thdev->name,
135 thdev->id);
136 else
137 node = kasprintf(GFP_KERNEL, "intel_th%d/%s", 0, thdev->name);
138
139 return node;
140}
141
142static ssize_t port_show(struct device *dev, struct device_attribute *attr,
143 char *buf)
144{
145 struct intel_th_device *thdev = to_intel_th_device(dev);
146
147 if (thdev->output.port >= 0)
148 return scnprintf(buf, PAGE_SIZE, "%u\n", thdev->output.port);
149
150 return scnprintf(buf, PAGE_SIZE, "unassigned\n");
151}
152
153static DEVICE_ATTR_RO(port);
154
155static int intel_th_output_activate(struct intel_th_device *thdev)
156{
157 struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
158
159 if (thdrv->activate)
160 return thdrv->activate(thdev);
161
162 intel_th_trace_enable(thdev);
163
164 return 0;
165}
166
167static void intel_th_output_deactivate(struct intel_th_device *thdev)
168{
169 struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
170
171 if (thdrv->deactivate)
172 thdrv->deactivate(thdev);
173 else
174 intel_th_trace_disable(thdev);
175}
176
177static ssize_t active_show(struct device *dev, struct device_attribute *attr,
178 char *buf)
179{
180 struct intel_th_device *thdev = to_intel_th_device(dev);
181
182 return scnprintf(buf, PAGE_SIZE, "%d\n", thdev->output.active);
183}
184
185static ssize_t active_store(struct device *dev, struct device_attribute *attr,
186 const char *buf, size_t size)
187{
188 struct intel_th_device *thdev = to_intel_th_device(dev);
189 unsigned long val;
190 int ret;
191
192 ret = kstrtoul(buf, 10, &val);
193 if (ret)
194 return ret;
195
196 if (!!val != thdev->output.active) {
197 if (val)
198 ret = intel_th_output_activate(thdev);
199 else
200 intel_th_output_deactivate(thdev);
201 }
202
203 return ret ? ret : size;
204}
205
206static DEVICE_ATTR_RW(active);
207
208static struct attribute *intel_th_output_attrs[] = {
209 &dev_attr_port.attr,
210 &dev_attr_active.attr,
211 NULL,
212};
213
214ATTRIBUTE_GROUPS(intel_th_output);
215
216static struct device_type intel_th_output_device_type = {
217 .name = "intel_th_output_device",
218 .groups = intel_th_output_groups,
219 .release = intel_th_device_release,
220 .devnode = intel_th_output_devnode,
221};
222
223static struct device_type intel_th_switch_device_type = {
224 .name = "intel_th_switch_device",
225 .release = intel_th_device_release,
226};
227
228static struct device_type *intel_th_device_type[] = {
229 [INTEL_TH_SOURCE] = &intel_th_source_device_type,
230 [INTEL_TH_OUTPUT] = &intel_th_output_device_type,
231 [INTEL_TH_SWITCH] = &intel_th_switch_device_type,
232};
233
234int intel_th_driver_register(struct intel_th_driver *thdrv)
235{
236 if (!thdrv->probe || !thdrv->remove)
237 return -EINVAL;
238
239 thdrv->driver.bus = &intel_th_bus;
240
241 return driver_register(&thdrv->driver);
242}
243EXPORT_SYMBOL_GPL(intel_th_driver_register);
244
245void intel_th_driver_unregister(struct intel_th_driver *thdrv)
246{
247 driver_unregister(&thdrv->driver);
248}
249EXPORT_SYMBOL_GPL(intel_th_driver_unregister);
250
251static struct intel_th_device *
252intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name,
253 int id)
254{
255 struct device *parent;
256 struct intel_th_device *thdev;
257
258 if (type == INTEL_TH_SWITCH)
259 parent = th->dev;
260 else
261 parent = &th->hub->dev;
262
263 thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL);
264 if (!thdev)
265 return NULL;
266
267 thdev->id = id;
268 thdev->type = type;
269
270 strcpy(thdev->name, name);
271 device_initialize(&thdev->dev);
272 thdev->dev.bus = &intel_th_bus;
273 thdev->dev.type = intel_th_device_type[type];
274 thdev->dev.parent = parent;
275 thdev->dev.dma_mask = parent->dma_mask;
276 thdev->dev.dma_parms = parent->dma_parms;
277 dma_set_coherent_mask(&thdev->dev, parent->coherent_dma_mask);
278 if (id >= 0)
279 dev_set_name(&thdev->dev, "%d-%s%d", th->id, name, id);
280 else
281 dev_set_name(&thdev->dev, "%d-%s", th->id, name);
282
283 return thdev;
284}
285
286static int intel_th_device_add_resources(struct intel_th_device *thdev,
287 struct resource *res, int nres)
288{
289 struct resource *r;
290
291 r = kmemdup(res, sizeof(*res) * nres, GFP_KERNEL);
292 if (!r)
293 return -ENOMEM;
294
295 thdev->resource = r;
296 thdev->num_resources = nres;
297
298 return 0;
299}
300
301static void intel_th_device_remove(struct intel_th_device *thdev)
302{
303 device_del(&thdev->dev);
304 put_device(&thdev->dev);
305}
306
307static void intel_th_device_free(struct intel_th_device *thdev)
308{
309 kfree(thdev->resource);
310 kfree(thdev);
311}
312
313/*
314 * Intel(R) Trace Hub subdevices
315 */
316static struct intel_th_subdevice {
317 const char *name;
318 struct resource res[3];
319 unsigned nres;
320 unsigned type;
321 unsigned otype;
322 int id;
323} intel_th_subdevices[TH_SUBDEVICE_MAX] = {
324 {
325 .nres = 1,
326 .res = {
327 {
328 .start = REG_GTH_OFFSET,
329 .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1,
330 .flags = IORESOURCE_MEM,
331 },
332 },
333 .name = "gth",
334 .type = INTEL_TH_SWITCH,
335 .id = -1,
336 },
337 {
338 .nres = 2,
339 .res = {
340 {
341 .start = REG_MSU_OFFSET,
342 .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
343 .flags = IORESOURCE_MEM,
344 },
345 {
346 .start = BUF_MSU_OFFSET,
347 .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
348 .flags = IORESOURCE_MEM,
349 },
350 },
351 .name = "msc",
352 .id = 0,
353 .type = INTEL_TH_OUTPUT,
354 .otype = GTH_MSU,
355 },
356 {
357 .nres = 2,
358 .res = {
359 {
360 .start = REG_MSU_OFFSET,
361 .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
362 .flags = IORESOURCE_MEM,
363 },
364 {
365 .start = BUF_MSU_OFFSET,
366 .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
367 .flags = IORESOURCE_MEM,
368 },
369 },
370 .name = "msc",
371 .id = 1,
372 .type = INTEL_TH_OUTPUT,
373 .otype = GTH_MSU,
374 },
375 {
376 .nres = 2,
377 .res = {
378 {
379 .start = REG_STH_OFFSET,
380 .end = REG_STH_OFFSET + REG_STH_LENGTH - 1,
381 .flags = IORESOURCE_MEM,
382 },
383 {
384 .start = TH_MMIO_SW,
385 .end = 0,
386 .flags = IORESOURCE_MEM,
387 },
388 },
389 .id = -1,
390 .name = "sth",
391 .type = INTEL_TH_SOURCE,
392 },
393 {
394 .nres = 1,
395 .res = {
396 {
397 .start = REG_PTI_OFFSET,
398 .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1,
399 .flags = IORESOURCE_MEM,
400 },
401 },
402 .id = -1,
403 .name = "pti",
404 .type = INTEL_TH_OUTPUT,
405 .otype = GTH_PTI,
406 },
407 {
408 .nres = 1,
409 .res = {
410 {
411 .start = REG_DCIH_OFFSET,
412 .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1,
413 .flags = IORESOURCE_MEM,
414 },
415 },
416 .id = -1,
417 .name = "dcih",
418 .type = INTEL_TH_OUTPUT,
419 },
420};
421
422static int intel_th_populate(struct intel_th *th, struct resource *devres,
423 unsigned int ndevres, int irq)
424{
425 struct resource res[3];
426 unsigned int req = 0;
427 int i, err;
428
429 /* create devices for each intel_th_subdevice */
430 for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) {
431 struct intel_th_subdevice *subdev = &intel_th_subdevices[i];
432 struct intel_th_device *thdev;
433 int r;
434
435 thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
436 subdev->id);
437 if (!thdev) {
438 err = -ENOMEM;
439 goto kill_subdevs;
440 }
441
442 memcpy(res, subdev->res,
443 sizeof(struct resource) * subdev->nres);
444
445 for (r = 0; r < subdev->nres; r++) {
446 int bar = TH_MMIO_CONFIG;
447
448 /*
449 * Take .end == 0 to mean 'take the whole bar',
450 * .start then tells us which bar it is. Default to
451 * TH_MMIO_CONFIG.
452 */
453 if (!res[r].end && res[r].flags == IORESOURCE_MEM) {
454 bar = res[r].start;
455 res[r].start = 0;
456 res[r].end = resource_size(&devres[bar]) - 1;
457 }
458
459 if (res[r].flags & IORESOURCE_MEM) {
460 res[r].start += devres[bar].start;
461 res[r].end += devres[bar].start;
462
463 dev_dbg(th->dev, "%s:%d @ %pR\n",
464 subdev->name, r, &res[r]);
465 } else if (res[r].flags & IORESOURCE_IRQ) {
466 res[r].start = irq;
467 }
468 }
469
470 err = intel_th_device_add_resources(thdev, res, subdev->nres);
471 if (err) {
472 put_device(&thdev->dev);
473 goto kill_subdevs;
474 }
475
476 if (subdev->type == INTEL_TH_OUTPUT) {
477 thdev->dev.devt = MKDEV(th->major, i);
478 thdev->output.type = subdev->otype;
479 thdev->output.port = -1;
480 }
481
482 err = device_add(&thdev->dev);
483 if (err) {
484 put_device(&thdev->dev);
485 goto kill_subdevs;
486 }
487
488 /* need switch driver to be loaded to enumerate the rest */
489 if (subdev->type == INTEL_TH_SWITCH && !req) {
490 th->hub = thdev;
491 err = request_module("intel_th_%s", subdev->name);
492 if (!err)
493 req++;
494 }
495
496 th->thdev[i] = thdev;
497 }
498
499 return 0;
500
501kill_subdevs:
502 for (i-- ; i >= 0; i--)
503 intel_th_device_remove(th->thdev[i]);
504
505 return err;
506}
507
508static int match_devt(struct device *dev, void *data)
509{
510 dev_t devt = (dev_t)(unsigned long)data;
511
512 return dev->devt == devt;
513}
514
515static int intel_th_output_open(struct inode *inode, struct file *file)
516{
517 const struct file_operations *fops;
518 struct intel_th_driver *thdrv;
519 struct device *dev;
520 int err;
521
522 dev = bus_find_device(&intel_th_bus, NULL,
523 (void *)(unsigned long)inode->i_rdev,
524 match_devt);
525 if (!dev || !dev->driver)
526 return -ENODEV;
527
528 thdrv = to_intel_th_driver(dev->driver);
529 fops = fops_get(thdrv->fops);
530 if (!fops)
531 return -ENODEV;
532
533 replace_fops(file, fops);
534
535 file->private_data = to_intel_th_device(dev);
536
537 if (file->f_op->open) {
538 err = file->f_op->open(inode, file);
539 return err;
540 }
541
542 return 0;
543}
544
545static const struct file_operations intel_th_output_fops = {
546 .open = intel_th_output_open,
547 .llseek = noop_llseek,
548};
549
550/**
551 * intel_th_alloc() - allocate a new Intel TH device and its subdevices
552 * @dev: parent device
553 * @devres: parent's resources
554 * @ndevres: number of resources
555 * @irq: irq number
556 */
557struct intel_th *
558intel_th_alloc(struct device *dev, struct resource *devres,
559 unsigned int ndevres, int irq)
560{
561 struct intel_th *th;
562 int err;
563
564 th = kzalloc(sizeof(*th), GFP_KERNEL);
565 if (!th)
566 return ERR_PTR(-ENOMEM);
567
568 th->id = ida_simple_get(&intel_th_ida, 0, 0, GFP_KERNEL);
569 if (th->id < 0) {
570 err = th->id;
571 goto err_alloc;
572 }
573
574 th->major = __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS,
575 "intel_th/output", &intel_th_output_fops);
576 if (th->major < 0) {
577 err = th->major;
578 goto err_ida;
579 }
580 th->dev = dev;
581
582 err = intel_th_populate(th, devres, ndevres, irq);
583 if (err)
584 goto err_chrdev;
585
586 return th;
587
588err_chrdev:
589 __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
590 "intel_th/output");
591
592err_ida:
593 ida_simple_remove(&intel_th_ida, th->id);
594
595err_alloc:
596 kfree(th);
597
598 return ERR_PTR(err);
599}
600EXPORT_SYMBOL_GPL(intel_th_alloc);
601
602void intel_th_free(struct intel_th *th)
603{
604 int i;
605
606 for (i = 0; i < TH_SUBDEVICE_MAX; i++)
607 if (th->thdev[i] != th->hub)
608 intel_th_device_remove(th->thdev[i]);
609
610 intel_th_device_remove(th->hub);
611
612 __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
613 "intel_th/output");
614
615 ida_simple_remove(&intel_th_ida, th->id);
616
617 kfree(th);
618}
619EXPORT_SYMBOL_GPL(intel_th_free);
620
621/**
622 * intel_th_trace_enable() - enable tracing for an output device
623 * @thdev: output device that requests tracing be enabled
624 */
625int intel_th_trace_enable(struct intel_th_device *thdev)
626{
627 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
628 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
629
630 if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH))
631 return -EINVAL;
632
633 if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
634 return -EINVAL;
635
636 hubdrv->enable(hub, &thdev->output);
637
638 return 0;
639}
640EXPORT_SYMBOL_GPL(intel_th_trace_enable);
641
642/**
643 * intel_th_trace_disable() - disable tracing for an output device
644 * @thdev: output device that requests tracing be disabled
645 */
646int intel_th_trace_disable(struct intel_th_device *thdev)
647{
648 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
649 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
650
651 WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH);
652 if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
653 return -EINVAL;
654
655 hubdrv->disable(hub, &thdev->output);
656
657 return 0;
658}
659EXPORT_SYMBOL_GPL(intel_th_trace_disable);
660
661int intel_th_set_output(struct intel_th_device *thdev,
662 unsigned int master)
663{
664 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
665 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
666
667 if (!hubdrv->set_output)
668 return -ENOTSUPP;
669
670 return hubdrv->set_output(hub, master);
671}
672EXPORT_SYMBOL_GPL(intel_th_set_output);
673
674static int __init intel_th_init(void)
675{
676 intel_th_debug_init();
677
678 return bus_register(&intel_th_bus);
679}
680subsys_initcall(intel_th_init);
681
682static void __exit intel_th_exit(void)
683{
684 intel_th_debug_done();
685
686 bus_unregister(&intel_th_bus);
687}
688module_exit(intel_th_exit);
689
690MODULE_LICENSE("GPL v2");
691MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver");
692MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
diff --git a/drivers/hwtracing/intel_th/debug.c b/drivers/hwtracing/intel_th/debug.c
new file mode 100644
index 000000000000..788a1f0a97ad
--- /dev/null
+++ b/drivers/hwtracing/intel_th/debug.c
@@ -0,0 +1,36 @@
1/*
2 * Intel(R) Trace Hub driver debugging
3 *
4 * Copyright (C) 2014-2015 Intel Corporation.
5 *
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.
9 *
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
13 * more details.
14 */
15
16#include <linux/types.h>
17#include <linux/device.h>
18#include <linux/debugfs.h>
19
20#include "intel_th.h"
21#include "debug.h"
22
23struct dentry *intel_th_dbg;
24
25void intel_th_debug_init(void)
26{
27 intel_th_dbg = debugfs_create_dir("intel_th", NULL);
28 if (IS_ERR(intel_th_dbg))
29 intel_th_dbg = NULL;
30}
31
32void intel_th_debug_done(void)
33{
34 debugfs_remove(intel_th_dbg);
35 intel_th_dbg = NULL;
36}
diff --git a/drivers/hwtracing/intel_th/debug.h b/drivers/hwtracing/intel_th/debug.h
new file mode 100644
index 000000000000..88311bad3ba4
--- /dev/null
+++ b/drivers/hwtracing/intel_th/debug.h
@@ -0,0 +1,34 @@
1/*
2 * Intel(R) Trace Hub driver debugging
3 *
4 * Copyright (C) 2014-2015 Intel Corporation.
5 *
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.
9 *
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
13 * more details.
14 */
15
16#ifndef __INTEL_TH_DEBUG_H__
17#define __INTEL_TH_DEBUG_H__
18
19#ifdef CONFIG_INTEL_TH_DEBUG
20extern struct dentry *intel_th_dbg;
21
22void intel_th_debug_init(void);
23void intel_th_debug_done(void);
24#else
25static inline void intel_th_debug_init(void)
26{
27}
28
29static inline void intel_th_debug_done(void)
30{
31}
32#endif
33
34#endif /* __INTEL_TH_DEBUG_H__ */
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
new file mode 100644
index 000000000000..57fd72b20fae
--- /dev/null
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -0,0 +1,244 @@
1/*
2 * Intel(R) Trace Hub data structures
3 *
4 * Copyright (C) 2014-2015 Intel Corporation.
5 *
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.
9 *
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
13 * more details.
14 */
15
16#ifndef __INTEL_TH_H__
17#define __INTEL_TH_H__
18
19/* intel_th_device device types */
20enum {
21 /* Devices that generate trace data */
22 INTEL_TH_SOURCE = 0,
23 /* Output ports (MSC, PTI) */
24 INTEL_TH_OUTPUT,
25 /* Switch, the Global Trace Hub (GTH) */
26 INTEL_TH_SWITCH,
27};
28
29/**
30 * struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices
31 * @port: output port number, assigned by the switch
32 * @type: GTH_{MSU,CTP,PTI}
33 * @multiblock: true for multiblock output configuration
34 * @active: true when this output is enabled
35 *
36 * Output port descriptor, used by switch driver to tell which output
37 * port this output device corresponds to. Filled in at output device's
38 * probe time by switch::assign(). Passed from output device driver to
39 * switch related code to enable/disable its port.
40 */
41struct intel_th_output {
42 int port;
43 unsigned int type;
44 bool multiblock;
45 bool active;
46};
47
48/**
49 * struct intel_th_device - device on the intel_th bus
50 * @dev: device
51 * @resource: array of resources available to this device
52 * @num_resources: number of resources in @resource array
53 * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH}
54 * @id: device instance or -1
55 * @output: output descriptor for INTEL_TH_OUTPUT devices
56 * @name: device name to match the driver
57 */
58struct intel_th_device {
59 struct device dev;
60 struct resource *resource;
61 unsigned int num_resources;
62 unsigned int type;
63 int id;
64
65 /* INTEL_TH_OUTPUT specific */
66 struct intel_th_output output;
67
68 char name[];
69};
70
71#define to_intel_th_device(_d) \
72 container_of((_d), struct intel_th_device, dev)
73
74/**
75 * intel_th_device_get_resource() - obtain @num'th resource of type @type
76 * @thdev: the device to search the resource for
77 * @type: resource type
78 * @num: number of the resource
79 */
80static inline struct resource *
81intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type,
82 unsigned int num)
83{
84 int i;
85
86 for (i = 0; i < thdev->num_resources; i++)
87 if (resource_type(&thdev->resource[i]) == type && !num--)
88 return &thdev->resource[i];
89
90 return NULL;
91}
92
93/**
94 * intel_th_output_assigned() - if an output device is assigned to a switch port
95 * @thdev: the output device
96 *
97 * Return: true if the device is INTEL_TH_OUTPUT *and* is assigned a port
98 */
99static inline bool
100intel_th_output_assigned(struct intel_th_device *thdev)
101{
102 return thdev->type == INTEL_TH_OUTPUT &&
103 thdev->output.port >= 0;
104}
105
106/**
107 * struct intel_th_driver - driver for an intel_th_device device
108 * @driver: generic driver
109 * @probe: probe method
110 * @remove: remove method
111 * @assign: match a given output type device against available outputs
112 * @unassign: deassociate an output type device from an output port
113 * @enable: enable tracing for a given output device
114 * @disable: disable tracing for a given output device
115 * @fops: file operations for device nodes
116 *
117 * Callbacks @probe and @remove are required for all device types.
118 * Switch device driver needs to fill in @assign, @enable and @disable
119 * callbacks.
120 */
121struct intel_th_driver {
122 struct device_driver driver;
123 int (*probe)(struct intel_th_device *thdev);
124 void (*remove)(struct intel_th_device *thdev);
125 /* switch (GTH) ops */
126 int (*assign)(struct intel_th_device *thdev,
127 struct intel_th_device *othdev);
128 void (*unassign)(struct intel_th_device *thdev,
129 struct intel_th_device *othdev);
130 void (*enable)(struct intel_th_device *thdev,
131 struct intel_th_output *output);
132 void (*disable)(struct intel_th_device *thdev,
133 struct intel_th_output *output);
134 /* output ops */
135 void (*irq)(struct intel_th_device *thdev);
136 int (*activate)(struct intel_th_device *thdev);
137 void (*deactivate)(struct intel_th_device *thdev);
138 /* file_operations for those who want a device node */
139 const struct file_operations *fops;
140
141 /* source ops */
142 int (*set_output)(struct intel_th_device *thdev,
143 unsigned int master);
144};
145
146#define to_intel_th_driver(_d) \
147 container_of((_d), struct intel_th_driver, driver)
148
149static inline struct intel_th_device *
150to_intel_th_hub(struct intel_th_device *thdev)
151{
152 struct device *parent = thdev->dev.parent;
153
154 if (!parent)
155 return NULL;
156
157 return to_intel_th_device(parent);
158}
159
160struct intel_th *
161intel_th_alloc(struct device *dev, struct resource *devres,
162 unsigned int ndevres, int irq);
163void intel_th_free(struct intel_th *th);
164
165int intel_th_driver_register(struct intel_th_driver *thdrv);
166void intel_th_driver_unregister(struct intel_th_driver *thdrv);
167
168int intel_th_trace_enable(struct intel_th_device *thdev);
169int intel_th_trace_disable(struct intel_th_device *thdev);
170int intel_th_set_output(struct intel_th_device *thdev,
171 unsigned int master);
172
173enum {
174 TH_MMIO_CONFIG = 0,
175 TH_MMIO_SW = 2,
176 TH_MMIO_END,
177};
178
179#define TH_SUBDEVICE_MAX 6
180#define TH_POSSIBLE_OUTPUTS 8
181#define TH_CONFIGURABLE_MASTERS 256
182#define TH_MSC_MAX 2
183
184/**
185 * struct intel_th - Intel TH controller
186 * @dev: driver core's device
187 * @thdev: subdevices
188 * @hub: "switch" subdevice (GTH)
189 * @id: this Intel TH controller's device ID in the system
190 * @major: device node major for output devices
191 */
192struct intel_th {
193 struct device *dev;
194
195 struct intel_th_device *thdev[TH_SUBDEVICE_MAX];
196 struct intel_th_device *hub;
197
198 int id;
199 int major;
200#ifdef CONFIG_INTEL_TH_DEBUG
201 struct dentry *dbg;
202#endif
203};
204
205/*
206 * Register windows
207 */
208enum {
209 /* Global Trace Hub (GTH) */
210 REG_GTH_OFFSET = 0x0000,
211 REG_GTH_LENGTH = 0x2000,
212
213 /* Software Trace Hub (STH) [0x4000..0x4fff] */
214 REG_STH_OFFSET = 0x4000,
215 REG_STH_LENGTH = 0x2000,
216
217 /* Memory Storage Unit (MSU) [0xa0000..0xa1fff] */
218 REG_MSU_OFFSET = 0xa0000,
219 REG_MSU_LENGTH = 0x02000,
220
221 /* Internal MSU trace buffer [0x80000..0x9ffff] */
222 BUF_MSU_OFFSET = 0x80000,
223 BUF_MSU_LENGTH = 0x20000,
224
225 /* PTI output == same window as GTH */
226 REG_PTI_OFFSET = REG_GTH_OFFSET,
227 REG_PTI_LENGTH = REG_GTH_LENGTH,
228
229 /* DCI Handler (DCIH) == some window as MSU */
230 REG_DCIH_OFFSET = REG_MSU_OFFSET,
231 REG_DCIH_LENGTH = REG_MSU_LENGTH,
232};
233
234/*
235 * GTH, output ports configuration
236 */
237enum {
238 GTH_NONE = 0,
239 GTH_MSU, /* memory/usb */
240 GTH_CTP, /* Common Trace Port */
241 GTH_PTI = 4, /* MIPI-PTI */
242};
243
244#endif