Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alexander Shishkin and committed by
Greg Kroah-Hartman
39f40346 e3e5a3d3

+1148
+13
Documentation/ABI/testing/sysfs-bus-intel_th-output-devices
··· 1 + What: /sys/bus/intel_th/devices/<intel_th_id>-<device><id>/active 2 + Date: June 2015 3 + KernelVersion: 4.3 4 + Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com> 5 + Description: (RW) Writes of 1 or 0 enable or disable trace output to this 6 + output device. Reads return current status. 7 + 8 + What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/port 9 + Date: June 2015 10 + KernelVersion: 4.3 11 + Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com> 12 + Description: (RO) Port number, corresponding to this output device on the 13 + switch (GTH).
+99
Documentation/trace/intel_th.txt
··· 1 + Intel(R) Trace Hub (TH) 2 + ======================= 3 + 4 + Overview 5 + -------- 6 + 7 + Intel(R) Trace Hub (TH) is a set of hardware blocks that produce, 8 + switch and output trace data from multiple hardware and software 9 + sources over several types of trace output ports encoded in System 10 + Trace Protocol (MIPI STPv2) and is intended to perform full system 11 + debugging. For more information on the hardware, see Intel(R) Trace 12 + Hub developer's manual [1]. 13 + 14 + It consists of trace sources, trace destinations (outputs) and a 15 + switch (Global Trace Hub, GTH). These devices are placed on a bus of 16 + their own ("intel_th"), where they can be discovered and configured 17 + via sysfs attributes. 18 + 19 + Currently, 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 + 29 + Common attributes for output devices are described in 30 + Documentation/ABI/testing/sysfs-bus-intel_th-output-devices, the most 31 + notable of them is "active", which enables or disables trace output 32 + into that particular output device. 33 + 34 + GTH allows directing different STP masters into different output ports 35 + via its "masters" attribute group. More detailed GTH interface 36 + description is at Documentation/ABI/testing/sysfs-bus-intel_th-devices-gth. 37 + 38 + STH registers an stm class device, through which it provides interface 39 + to userspace and kernelspace software trace sources. See 40 + Documentation/tracing/stm.txt for more information on that. 41 + 42 + MSU can be configured to collect trace data into a system memory 43 + buffer, which can later on be read from its device nodes via read() or 44 + mmap() interface. 45 + 46 + On the whole, Intel(R) Trace Hub does not require any special 47 + userspace software to function; everything can be configured, started 48 + and 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 + 52 + Bus and Subdevices 53 + ------------------ 54 + 55 + For each Intel TH device in the system a bus of its own is 56 + created and assigned an id number that reflects the order in which TH 57 + devices were emumerated. All TH subdevices (devices on intel_th bus) 58 + begin with this id: 0-gth, 0-msc0, 0-msc1, 0-pti, 0-sth, which is 59 + followed by device's name and an optional index. 60 + 61 + Output devices also get a device node in /dev/intel_thN, where N is 62 + the Intel TH device id. For example, MSU's memory buffers, when 63 + allocated, are accessible via /dev/intel_th0/msc{0,1}. 64 + 65 + Quick 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 71 + 0 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
+2
drivers/Kconfig
··· 190 190 191 191 source "drivers/hwtracing/stm/Kconfig" 192 192 193 + source "drivers/hwtracing/intel_th/Kconfig" 194 + 193 195 endmenu
+1
drivers/Makefile
··· 165 165 obj-$(CONFIG_RAS) += ras/ 166 166 obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ 167 167 obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ 168 + obj-y += hwtracing/intel_th/ 168 169 obj-$(CONFIG_STM) += hwtracing/stm/ 169 170 obj-$(CONFIG_ANDROID) += android/ 170 171 obj-$(CONFIG_NVMEM) += nvmem/
+24
drivers/hwtracing/intel_th/Kconfig
··· 1 + config 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 + 16 + if INTEL_TH 17 + 18 + config 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 + 24 + endif
+3
drivers/hwtracing/intel_th/Makefile
··· 1 + obj-$(CONFIG_INTEL_TH) += intel_th.o 2 + intel_th-y := core.o 3 + intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
+692
drivers/hwtracing/intel_th/core.c
··· 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 + 31 + static DEFINE_IDA(intel_th_ida); 32 + 33 + static 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 + 45 + static int intel_th_child_remove(struct device *dev, void *data) 46 + { 47 + device_release_driver(dev); 48 + 49 + return 0; 50 + } 51 + 52 + static 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 + 81 + static 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 + 107 + static 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 + 115 + static void intel_th_device_free(struct intel_th_device *thdev); 116 + 117 + static void intel_th_device_release(struct device *dev) 118 + { 119 + intel_th_device_free(to_intel_th_device(dev)); 120 + } 121 + 122 + static struct device_type intel_th_source_device_type = { 123 + .name = "intel_th_source_device", 124 + .release = intel_th_device_release, 125 + }; 126 + 127 + static 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 + 142 + static 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 + 153 + static DEVICE_ATTR_RO(port); 154 + 155 + static 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 + 167 + static 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 + 177 + static 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 + 185 + static 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 + 206 + static DEVICE_ATTR_RW(active); 207 + 208 + static struct attribute *intel_th_output_attrs[] = { 209 + &dev_attr_port.attr, 210 + &dev_attr_active.attr, 211 + NULL, 212 + }; 213 + 214 + ATTRIBUTE_GROUPS(intel_th_output); 215 + 216 + static 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 + 223 + static struct device_type intel_th_switch_device_type = { 224 + .name = "intel_th_switch_device", 225 + .release = intel_th_device_release, 226 + }; 227 + 228 + static 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 + 234 + int 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 + } 243 + EXPORT_SYMBOL_GPL(intel_th_driver_register); 244 + 245 + void intel_th_driver_unregister(struct intel_th_driver *thdrv) 246 + { 247 + driver_unregister(&thdrv->driver); 248 + } 249 + EXPORT_SYMBOL_GPL(intel_th_driver_unregister); 250 + 251 + static struct intel_th_device * 252 + intel_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 + 286 + static 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 + 301 + static void intel_th_device_remove(struct intel_th_device *thdev) 302 + { 303 + device_del(&thdev->dev); 304 + put_device(&thdev->dev); 305 + } 306 + 307 + static 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 + */ 316 + static 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 + 422 + static 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 + 501 + kill_subdevs: 502 + for (i-- ; i >= 0; i--) 503 + intel_th_device_remove(th->thdev[i]); 504 + 505 + return err; 506 + } 507 + 508 + static 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 + 515 + static 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 + 545 + static 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 + */ 557 + struct intel_th * 558 + intel_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 + 588 + err_chrdev: 589 + __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, 590 + "intel_th/output"); 591 + 592 + err_ida: 593 + ida_simple_remove(&intel_th_ida, th->id); 594 + 595 + err_alloc: 596 + kfree(th); 597 + 598 + return ERR_PTR(err); 599 + } 600 + EXPORT_SYMBOL_GPL(intel_th_alloc); 601 + 602 + void 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 + } 619 + EXPORT_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 + */ 625 + int 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 + } 640 + EXPORT_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 + */ 646 + int 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 + } 659 + EXPORT_SYMBOL_GPL(intel_th_trace_disable); 660 + 661 + int 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 + } 672 + EXPORT_SYMBOL_GPL(intel_th_set_output); 673 + 674 + static int __init intel_th_init(void) 675 + { 676 + intel_th_debug_init(); 677 + 678 + return bus_register(&intel_th_bus); 679 + } 680 + subsys_initcall(intel_th_init); 681 + 682 + static void __exit intel_th_exit(void) 683 + { 684 + intel_th_debug_done(); 685 + 686 + bus_unregister(&intel_th_bus); 687 + } 688 + module_exit(intel_th_exit); 689 + 690 + MODULE_LICENSE("GPL v2"); 691 + MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver"); 692 + MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
+36
drivers/hwtracing/intel_th/debug.c
··· 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 + 23 + struct dentry *intel_th_dbg; 24 + 25 + void 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 + 32 + void intel_th_debug_done(void) 33 + { 34 + debugfs_remove(intel_th_dbg); 35 + intel_th_dbg = NULL; 36 + }
+34
drivers/hwtracing/intel_th/debug.h
··· 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 20 + extern struct dentry *intel_th_dbg; 21 + 22 + void intel_th_debug_init(void); 23 + void intel_th_debug_done(void); 24 + #else 25 + static inline void intel_th_debug_init(void) 26 + { 27 + } 28 + 29 + static inline void intel_th_debug_done(void) 30 + { 31 + } 32 + #endif 33 + 34 + #endif /* __INTEL_TH_DEBUG_H__ */
+244
drivers/hwtracing/intel_th/intel_th.h
··· 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 */ 20 + enum { 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 + */ 41 + struct 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 + */ 58 + struct 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 + */ 80 + static inline struct resource * 81 + intel_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 + */ 99 + static inline bool 100 + intel_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 + */ 121 + struct 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 + 149 + static inline struct intel_th_device * 150 + to_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 + 160 + struct intel_th * 161 + intel_th_alloc(struct device *dev, struct resource *devres, 162 + unsigned int ndevres, int irq); 163 + void intel_th_free(struct intel_th *th); 164 + 165 + int intel_th_driver_register(struct intel_th_driver *thdrv); 166 + void intel_th_driver_unregister(struct intel_th_driver *thdrv); 167 + 168 + int intel_th_trace_enable(struct intel_th_device *thdev); 169 + int intel_th_trace_disable(struct intel_th_device *thdev); 170 + int intel_th_set_output(struct intel_th_device *thdev, 171 + unsigned int master); 172 + 173 + enum { 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 + */ 192 + struct 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 + */ 208 + enum { 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 + */ 237 + enum { 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