core.c (24146B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel(R) Trace Hub driver core 4 * 5 * Copyright (C) 2014-2015 Intel Corporation. 6 */ 7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10#include <linux/types.h> 11#include <linux/module.h> 12#include <linux/device.h> 13#include <linux/sysfs.h> 14#include <linux/kdev_t.h> 15#include <linux/debugfs.h> 16#include <linux/idr.h> 17#include <linux/pci.h> 18#include <linux/pm_runtime.h> 19#include <linux/dma-mapping.h> 20 21#include "intel_th.h" 22#include "debug.h" 23 24static bool host_mode __read_mostly; 25module_param(host_mode, bool, 0444); 26 27static DEFINE_IDA(intel_th_ida); 28 29static int intel_th_match(struct device *dev, struct device_driver *driver) 30{ 31 struct intel_th_driver *thdrv = to_intel_th_driver(driver); 32 struct intel_th_device *thdev = to_intel_th_device(dev); 33 34 if (thdev->type == INTEL_TH_SWITCH && 35 (!thdrv->enable || !thdrv->disable)) 36 return 0; 37 38 return !strcmp(thdev->name, driver->name); 39} 40 41static int intel_th_child_remove(struct device *dev, void *data) 42{ 43 device_release_driver(dev); 44 45 return 0; 46} 47 48static int intel_th_probe(struct device *dev) 49{ 50 struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); 51 struct intel_th_device *thdev = to_intel_th_device(dev); 52 struct intel_th_driver *hubdrv; 53 struct intel_th_device *hub = NULL; 54 int ret; 55 56 if (thdev->type == INTEL_TH_SWITCH) 57 hub = thdev; 58 else if (dev->parent) 59 hub = to_intel_th_device(dev->parent); 60 61 if (!hub || !hub->dev.driver) 62 return -EPROBE_DEFER; 63 64 hubdrv = to_intel_th_driver(hub->dev.driver); 65 66 pm_runtime_set_active(dev); 67 pm_runtime_no_callbacks(dev); 68 pm_runtime_enable(dev); 69 70 ret = thdrv->probe(to_intel_th_device(dev)); 71 if (ret) 72 goto out_pm; 73 74 if (thdrv->attr_group) { 75 ret = sysfs_create_group(&thdev->dev.kobj, thdrv->attr_group); 76 if (ret) 77 goto out; 78 } 79 80 if (thdev->type == INTEL_TH_OUTPUT && 81 !intel_th_output_assigned(thdev)) 82 /* does not talk to hardware */ 83 ret = hubdrv->assign(hub, thdev); 84 85out: 86 if (ret) 87 thdrv->remove(thdev); 88 89out_pm: 90 if (ret) 91 pm_runtime_disable(dev); 92 93 return ret; 94} 95 96static void intel_th_device_remove(struct intel_th_device *thdev); 97 98static void intel_th_remove(struct device *dev) 99{ 100 struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); 101 struct intel_th_device *thdev = to_intel_th_device(dev); 102 struct intel_th_device *hub = to_intel_th_hub(thdev); 103 104 if (thdev->type == INTEL_TH_SWITCH) { 105 struct intel_th *th = to_intel_th(hub); 106 int i, lowest; 107 108 /* 109 * disconnect outputs 110 * 111 * intel_th_child_remove returns 0 unconditionally, so there is 112 * no need to check the return value of device_for_each_child. 113 */ 114 device_for_each_child(dev, thdev, intel_th_child_remove); 115 116 /* 117 * Remove outputs, that is, hub's children: they are created 118 * at hub's probe time by having the hub call 119 * intel_th_output_enable() for each of them. 120 */ 121 for (i = 0, lowest = -1; i < th->num_thdevs; i++) { 122 /* 123 * Move the non-output devices from higher up the 124 * th->thdev[] array to lower positions to maintain 125 * a contiguous array. 126 */ 127 if (th->thdev[i]->type != INTEL_TH_OUTPUT) { 128 if (lowest >= 0) { 129 th->thdev[lowest] = th->thdev[i]; 130 th->thdev[i] = NULL; 131 ++lowest; 132 } 133 134 continue; 135 } 136 137 if (lowest == -1) 138 lowest = i; 139 140 intel_th_device_remove(th->thdev[i]); 141 th->thdev[i] = NULL; 142 } 143 144 if (lowest >= 0) 145 th->num_thdevs = lowest; 146 } 147 148 if (thdrv->attr_group) 149 sysfs_remove_group(&thdev->dev.kobj, thdrv->attr_group); 150 151 pm_runtime_get_sync(dev); 152 153 thdrv->remove(thdev); 154 155 if (intel_th_output_assigned(thdev)) { 156 struct intel_th_driver *hubdrv = 157 to_intel_th_driver(dev->parent->driver); 158 159 if (hub->dev.driver) 160 /* does not talk to hardware */ 161 hubdrv->unassign(hub, thdev); 162 } 163 164 pm_runtime_disable(dev); 165 pm_runtime_set_active(dev); 166 pm_runtime_enable(dev); 167} 168 169static struct bus_type intel_th_bus = { 170 .name = "intel_th", 171 .match = intel_th_match, 172 .probe = intel_th_probe, 173 .remove = intel_th_remove, 174}; 175 176static void intel_th_device_free(struct intel_th_device *thdev); 177 178static void intel_th_device_release(struct device *dev) 179{ 180 intel_th_device_free(to_intel_th_device(dev)); 181} 182 183static struct device_type intel_th_source_device_type = { 184 .name = "intel_th_source_device", 185 .release = intel_th_device_release, 186}; 187 188static char *intel_th_output_devnode(struct device *dev, umode_t *mode, 189 kuid_t *uid, kgid_t *gid) 190{ 191 struct intel_th_device *thdev = to_intel_th_device(dev); 192 struct intel_th *th = to_intel_th(thdev); 193 char *node; 194 195 if (thdev->id >= 0) 196 node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", th->id, 197 thdev->name, thdev->id); 198 else 199 node = kasprintf(GFP_KERNEL, "intel_th%d/%s", th->id, 200 thdev->name); 201 202 return node; 203} 204 205static ssize_t port_show(struct device *dev, struct device_attribute *attr, 206 char *buf) 207{ 208 struct intel_th_device *thdev = to_intel_th_device(dev); 209 210 if (thdev->output.port >= 0) 211 return scnprintf(buf, PAGE_SIZE, "%u\n", thdev->output.port); 212 213 return scnprintf(buf, PAGE_SIZE, "unassigned\n"); 214} 215 216static DEVICE_ATTR_RO(port); 217 218static void intel_th_trace_prepare(struct intel_th_device *thdev) 219{ 220 struct intel_th_device *hub = to_intel_th_hub(thdev); 221 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); 222 223 if (hub->type != INTEL_TH_SWITCH) 224 return; 225 226 if (thdev->type != INTEL_TH_OUTPUT) 227 return; 228 229 pm_runtime_get_sync(&thdev->dev); 230 hubdrv->prepare(hub, &thdev->output); 231 pm_runtime_put(&thdev->dev); 232} 233 234static int intel_th_output_activate(struct intel_th_device *thdev) 235{ 236 struct intel_th_driver *thdrv = 237 to_intel_th_driver_or_null(thdev->dev.driver); 238 struct intel_th *th = to_intel_th(thdev); 239 int ret = 0; 240 241 if (!thdrv) 242 return -ENODEV; 243 244 if (!try_module_get(thdrv->driver.owner)) 245 return -ENODEV; 246 247 pm_runtime_get_sync(&thdev->dev); 248 249 if (th->activate) 250 ret = th->activate(th); 251 if (ret) 252 goto fail_put; 253 254 intel_th_trace_prepare(thdev); 255 if (thdrv->activate) 256 ret = thdrv->activate(thdev); 257 else 258 intel_th_trace_enable(thdev); 259 260 if (ret) 261 goto fail_deactivate; 262 263 return 0; 264 265fail_deactivate: 266 if (th->deactivate) 267 th->deactivate(th); 268 269fail_put: 270 pm_runtime_put(&thdev->dev); 271 module_put(thdrv->driver.owner); 272 273 return ret; 274} 275 276static void intel_th_output_deactivate(struct intel_th_device *thdev) 277{ 278 struct intel_th_driver *thdrv = 279 to_intel_th_driver_or_null(thdev->dev.driver); 280 struct intel_th *th = to_intel_th(thdev); 281 282 if (!thdrv) 283 return; 284 285 if (thdrv->deactivate) 286 thdrv->deactivate(thdev); 287 else 288 intel_th_trace_disable(thdev); 289 290 if (th->deactivate) 291 th->deactivate(th); 292 293 pm_runtime_put(&thdev->dev); 294 module_put(thdrv->driver.owner); 295} 296 297static ssize_t active_show(struct device *dev, struct device_attribute *attr, 298 char *buf) 299{ 300 struct intel_th_device *thdev = to_intel_th_device(dev); 301 302 return scnprintf(buf, PAGE_SIZE, "%d\n", thdev->output.active); 303} 304 305static ssize_t active_store(struct device *dev, struct device_attribute *attr, 306 const char *buf, size_t size) 307{ 308 struct intel_th_device *thdev = to_intel_th_device(dev); 309 unsigned long val; 310 int ret; 311 312 ret = kstrtoul(buf, 10, &val); 313 if (ret) 314 return ret; 315 316 if (!!val != thdev->output.active) { 317 if (val) 318 ret = intel_th_output_activate(thdev); 319 else 320 intel_th_output_deactivate(thdev); 321 } 322 323 return ret ? ret : size; 324} 325 326static DEVICE_ATTR_RW(active); 327 328static struct attribute *intel_th_output_attrs[] = { 329 &dev_attr_port.attr, 330 &dev_attr_active.attr, 331 NULL, 332}; 333 334ATTRIBUTE_GROUPS(intel_th_output); 335 336static struct device_type intel_th_output_device_type = { 337 .name = "intel_th_output_device", 338 .groups = intel_th_output_groups, 339 .release = intel_th_device_release, 340 .devnode = intel_th_output_devnode, 341}; 342 343static struct device_type intel_th_switch_device_type = { 344 .name = "intel_th_switch_device", 345 .release = intel_th_device_release, 346}; 347 348static struct device_type *intel_th_device_type[] = { 349 [INTEL_TH_SOURCE] = &intel_th_source_device_type, 350 [INTEL_TH_OUTPUT] = &intel_th_output_device_type, 351 [INTEL_TH_SWITCH] = &intel_th_switch_device_type, 352}; 353 354int intel_th_driver_register(struct intel_th_driver *thdrv) 355{ 356 if (!thdrv->probe || !thdrv->remove) 357 return -EINVAL; 358 359 thdrv->driver.bus = &intel_th_bus; 360 361 return driver_register(&thdrv->driver); 362} 363EXPORT_SYMBOL_GPL(intel_th_driver_register); 364 365void intel_th_driver_unregister(struct intel_th_driver *thdrv) 366{ 367 driver_unregister(&thdrv->driver); 368} 369EXPORT_SYMBOL_GPL(intel_th_driver_unregister); 370 371static struct intel_th_device * 372intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, 373 int id) 374{ 375 struct device *parent; 376 struct intel_th_device *thdev; 377 378 if (type == INTEL_TH_OUTPUT) 379 parent = &th->hub->dev; 380 else 381 parent = th->dev; 382 383 thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); 384 if (!thdev) 385 return NULL; 386 387 thdev->id = id; 388 thdev->type = type; 389 390 strcpy(thdev->name, name); 391 device_initialize(&thdev->dev); 392 thdev->dev.bus = &intel_th_bus; 393 thdev->dev.type = intel_th_device_type[type]; 394 thdev->dev.parent = parent; 395 thdev->dev.dma_mask = parent->dma_mask; 396 thdev->dev.dma_parms = parent->dma_parms; 397 dma_set_coherent_mask(&thdev->dev, parent->coherent_dma_mask); 398 if (id >= 0) 399 dev_set_name(&thdev->dev, "%d-%s%d", th->id, name, id); 400 else 401 dev_set_name(&thdev->dev, "%d-%s", th->id, name); 402 403 return thdev; 404} 405 406static int intel_th_device_add_resources(struct intel_th_device *thdev, 407 struct resource *res, int nres) 408{ 409 struct resource *r; 410 411 r = kmemdup(res, sizeof(*res) * nres, GFP_KERNEL); 412 if (!r) 413 return -ENOMEM; 414 415 thdev->resource = r; 416 thdev->num_resources = nres; 417 418 return 0; 419} 420 421static void intel_th_device_remove(struct intel_th_device *thdev) 422{ 423 device_del(&thdev->dev); 424 put_device(&thdev->dev); 425} 426 427static void intel_th_device_free(struct intel_th_device *thdev) 428{ 429 kfree(thdev->resource); 430 kfree(thdev); 431} 432 433/* 434 * Intel(R) Trace Hub subdevices 435 */ 436static const struct intel_th_subdevice { 437 const char *name; 438 struct resource res[3]; 439 unsigned nres; 440 unsigned type; 441 unsigned otype; 442 bool mknode; 443 unsigned scrpd; 444 int id; 445} intel_th_subdevices[] = { 446 { 447 .nres = 1, 448 .res = { 449 { 450 /* Handle TSCU and CTS from GTH driver */ 451 .start = REG_GTH_OFFSET, 452 .end = REG_CTS_OFFSET + REG_CTS_LENGTH - 1, 453 .flags = IORESOURCE_MEM, 454 }, 455 }, 456 .name = "gth", 457 .type = INTEL_TH_SWITCH, 458 .id = -1, 459 }, 460 { 461 .nres = 2, 462 .res = { 463 { 464 .start = REG_MSU_OFFSET, 465 .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1, 466 .flags = IORESOURCE_MEM, 467 }, 468 { 469 .start = BUF_MSU_OFFSET, 470 .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1, 471 .flags = IORESOURCE_MEM, 472 }, 473 }, 474 .name = "msc", 475 .id = 0, 476 .type = INTEL_TH_OUTPUT, 477 .mknode = true, 478 .otype = GTH_MSU, 479 .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC0_IS_ENABLED, 480 }, 481 { 482 .nres = 2, 483 .res = { 484 { 485 .start = REG_MSU_OFFSET, 486 .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1, 487 .flags = IORESOURCE_MEM, 488 }, 489 { 490 .start = BUF_MSU_OFFSET, 491 .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1, 492 .flags = IORESOURCE_MEM, 493 }, 494 }, 495 .name = "msc", 496 .id = 1, 497 .type = INTEL_TH_OUTPUT, 498 .mknode = true, 499 .otype = GTH_MSU, 500 .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC1_IS_ENABLED, 501 }, 502 { 503 .nres = 2, 504 .res = { 505 { 506 .start = REG_STH_OFFSET, 507 .end = REG_STH_OFFSET + REG_STH_LENGTH - 1, 508 .flags = IORESOURCE_MEM, 509 }, 510 { 511 .start = TH_MMIO_SW, 512 .end = 0, 513 .flags = IORESOURCE_MEM, 514 }, 515 }, 516 .id = -1, 517 .name = "sth", 518 .type = INTEL_TH_SOURCE, 519 }, 520 { 521 .nres = 2, 522 .res = { 523 { 524 .start = REG_STH_OFFSET, 525 .end = REG_STH_OFFSET + REG_STH_LENGTH - 1, 526 .flags = IORESOURCE_MEM, 527 }, 528 { 529 .start = TH_MMIO_RTIT, 530 .end = 0, 531 .flags = IORESOURCE_MEM, 532 }, 533 }, 534 .id = -1, 535 .name = "rtit", 536 .type = INTEL_TH_SOURCE, 537 }, 538 { 539 .nres = 1, 540 .res = { 541 { 542 .start = REG_PTI_OFFSET, 543 .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, 544 .flags = IORESOURCE_MEM, 545 }, 546 }, 547 .id = -1, 548 .name = "pti", 549 .type = INTEL_TH_OUTPUT, 550 .otype = GTH_PTI, 551 .scrpd = SCRPD_PTI_IS_PRIM_DEST, 552 }, 553 { 554 .nres = 1, 555 .res = { 556 { 557 .start = REG_PTI_OFFSET, 558 .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, 559 .flags = IORESOURCE_MEM, 560 }, 561 }, 562 .id = -1, 563 .name = "lpp", 564 .type = INTEL_TH_OUTPUT, 565 .otype = GTH_LPP, 566 .scrpd = SCRPD_PTI_IS_PRIM_DEST, 567 }, 568 { 569 .nres = 1, 570 .res = { 571 { 572 .start = REG_DCIH_OFFSET, 573 .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1, 574 .flags = IORESOURCE_MEM, 575 }, 576 }, 577 .id = -1, 578 .name = "dcih", 579 .type = INTEL_TH_OUTPUT, 580 }, 581}; 582 583#ifdef CONFIG_MODULES 584static void __intel_th_request_hub_module(struct work_struct *work) 585{ 586 struct intel_th *th = container_of(work, struct intel_th, 587 request_module_work); 588 589 request_module("intel_th_%s", th->hub->name); 590} 591 592static int intel_th_request_hub_module(struct intel_th *th) 593{ 594 INIT_WORK(&th->request_module_work, __intel_th_request_hub_module); 595 schedule_work(&th->request_module_work); 596 597 return 0; 598} 599 600static void intel_th_request_hub_module_flush(struct intel_th *th) 601{ 602 flush_work(&th->request_module_work); 603} 604#else 605static inline int intel_th_request_hub_module(struct intel_th *th) 606{ 607 return -EINVAL; 608} 609 610static inline void intel_th_request_hub_module_flush(struct intel_th *th) 611{ 612} 613#endif /* CONFIG_MODULES */ 614 615static struct intel_th_device * 616intel_th_subdevice_alloc(struct intel_th *th, 617 const struct intel_th_subdevice *subdev) 618{ 619 struct intel_th_device *thdev; 620 struct resource res[3]; 621 unsigned int req = 0; 622 int r, err; 623 624 thdev = intel_th_device_alloc(th, subdev->type, subdev->name, 625 subdev->id); 626 if (!thdev) 627 return ERR_PTR(-ENOMEM); 628 629 thdev->drvdata = th->drvdata; 630 631 memcpy(res, subdev->res, 632 sizeof(struct resource) * subdev->nres); 633 634 for (r = 0; r < subdev->nres; r++) { 635 struct resource *devres = th->resource; 636 int bar = TH_MMIO_CONFIG; 637 638 /* 639 * Take .end == 0 to mean 'take the whole bar', 640 * .start then tells us which bar it is. Default to 641 * TH_MMIO_CONFIG. 642 */ 643 if (!res[r].end && res[r].flags == IORESOURCE_MEM) { 644 bar = res[r].start; 645 err = -ENODEV; 646 if (bar >= th->num_resources) 647 goto fail_put_device; 648 res[r].start = 0; 649 res[r].end = resource_size(&devres[bar]) - 1; 650 } 651 652 if (res[r].flags & IORESOURCE_MEM) { 653 res[r].start += devres[bar].start; 654 res[r].end += devres[bar].start; 655 656 dev_dbg(th->dev, "%s:%d @ %pR\n", 657 subdev->name, r, &res[r]); 658 } else if (res[r].flags & IORESOURCE_IRQ) { 659 /* 660 * Only pass on the IRQ if we have useful interrupts: 661 * the ones that can be configured via MINTCTL. 662 */ 663 if (INTEL_TH_CAP(th, has_mintctl) && th->irq != -1) 664 res[r].start = th->irq; 665 } 666 } 667 668 err = intel_th_device_add_resources(thdev, res, subdev->nres); 669 if (err) 670 goto fail_put_device; 671 672 if (subdev->type == INTEL_TH_OUTPUT) { 673 if (subdev->mknode) 674 thdev->dev.devt = MKDEV(th->major, th->num_thdevs); 675 thdev->output.type = subdev->otype; 676 thdev->output.port = -1; 677 thdev->output.scratchpad = subdev->scrpd; 678 } else if (subdev->type == INTEL_TH_SWITCH) { 679 thdev->host_mode = 680 INTEL_TH_CAP(th, host_mode_only) ? true : host_mode; 681 th->hub = thdev; 682 } 683 684 err = device_add(&thdev->dev); 685 if (err) 686 goto fail_free_res; 687 688 /* need switch driver to be loaded to enumerate the rest */ 689 if (subdev->type == INTEL_TH_SWITCH && !req) { 690 err = intel_th_request_hub_module(th); 691 if (!err) 692 req++; 693 } 694 695 return thdev; 696 697fail_free_res: 698 kfree(thdev->resource); 699 700fail_put_device: 701 put_device(&thdev->dev); 702 703 return ERR_PTR(err); 704} 705 706/** 707 * intel_th_output_enable() - find and enable a device for a given output type 708 * @th: Intel TH instance 709 * @otype: output type 710 * 711 * Go through the unallocated output devices, find the first one whos type 712 * matches @otype and instantiate it. These devices are removed when the hub 713 * device is removed, see intel_th_remove(). 714 */ 715int intel_th_output_enable(struct intel_th *th, unsigned int otype) 716{ 717 struct intel_th_device *thdev; 718 int src = 0, dst = 0; 719 720 for (src = 0, dst = 0; dst <= th->num_thdevs; src++, dst++) { 721 for (; src < ARRAY_SIZE(intel_th_subdevices); src++) { 722 if (intel_th_subdevices[src].type != INTEL_TH_OUTPUT) 723 continue; 724 725 if (intel_th_subdevices[src].otype != otype) 726 continue; 727 728 break; 729 } 730 731 /* no unallocated matching subdevices */ 732 if (src == ARRAY_SIZE(intel_th_subdevices)) 733 return -ENODEV; 734 735 for (; dst < th->num_thdevs; dst++) { 736 if (th->thdev[dst]->type != INTEL_TH_OUTPUT) 737 continue; 738 739 if (th->thdev[dst]->output.type != otype) 740 continue; 741 742 break; 743 } 744 745 /* 746 * intel_th_subdevices[src] matches our requirements and is 747 * not matched in th::thdev[] 748 */ 749 if (dst == th->num_thdevs) 750 goto found; 751 } 752 753 return -ENODEV; 754 755found: 756 thdev = intel_th_subdevice_alloc(th, &intel_th_subdevices[src]); 757 if (IS_ERR(thdev)) 758 return PTR_ERR(thdev); 759 760 th->thdev[th->num_thdevs++] = thdev; 761 762 return 0; 763} 764EXPORT_SYMBOL_GPL(intel_th_output_enable); 765 766static int intel_th_populate(struct intel_th *th) 767{ 768 int src; 769 770 /* create devices for each intel_th_subdevice */ 771 for (src = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { 772 const struct intel_th_subdevice *subdev = 773 &intel_th_subdevices[src]; 774 struct intel_th_device *thdev; 775 776 /* only allow SOURCE and SWITCH devices in host mode */ 777 if ((INTEL_TH_CAP(th, host_mode_only) || host_mode) && 778 subdev->type == INTEL_TH_OUTPUT) 779 continue; 780 781 /* 782 * don't enable port OUTPUTs in this path; SWITCH enables them 783 * via intel_th_output_enable() 784 */ 785 if (subdev->type == INTEL_TH_OUTPUT && 786 subdev->otype != GTH_NONE) 787 continue; 788 789 thdev = intel_th_subdevice_alloc(th, subdev); 790 /* note: caller should free subdevices from th::thdev[] */ 791 if (IS_ERR(thdev)) { 792 /* ENODEV for individual subdevices is allowed */ 793 if (PTR_ERR(thdev) == -ENODEV) 794 continue; 795 796 return PTR_ERR(thdev); 797 } 798 799 th->thdev[th->num_thdevs++] = thdev; 800 } 801 802 return 0; 803} 804 805static int intel_th_output_open(struct inode *inode, struct file *file) 806{ 807 const struct file_operations *fops; 808 struct intel_th_driver *thdrv; 809 struct device *dev; 810 int err; 811 812 dev = bus_find_device_by_devt(&intel_th_bus, inode->i_rdev); 813 if (!dev || !dev->driver) 814 return -ENODEV; 815 816 thdrv = to_intel_th_driver(dev->driver); 817 fops = fops_get(thdrv->fops); 818 if (!fops) 819 return -ENODEV; 820 821 replace_fops(file, fops); 822 823 file->private_data = to_intel_th_device(dev); 824 825 if (file->f_op->open) { 826 err = file->f_op->open(inode, file); 827 return err; 828 } 829 830 return 0; 831} 832 833static const struct file_operations intel_th_output_fops = { 834 .open = intel_th_output_open, 835 .llseek = noop_llseek, 836}; 837 838static irqreturn_t intel_th_irq(int irq, void *data) 839{ 840 struct intel_th *th = data; 841 irqreturn_t ret = IRQ_NONE; 842 struct intel_th_driver *d; 843 int i; 844 845 for (i = 0; i < th->num_thdevs; i++) { 846 if (th->thdev[i]->type != INTEL_TH_OUTPUT) 847 continue; 848 849 d = to_intel_th_driver(th->thdev[i]->dev.driver); 850 if (d && d->irq) 851 ret |= d->irq(th->thdev[i]); 852 } 853 854 return ret; 855} 856 857/** 858 * intel_th_alloc() - allocate a new Intel TH device and its subdevices 859 * @dev: parent device 860 * @devres: resources indexed by th_mmio_idx 861 * @irq: irq number 862 */ 863struct intel_th * 864intel_th_alloc(struct device *dev, const struct intel_th_drvdata *drvdata, 865 struct resource *devres, unsigned int ndevres) 866{ 867 int err, r, nr_mmios = 0; 868 struct intel_th *th; 869 870 th = kzalloc(sizeof(*th), GFP_KERNEL); 871 if (!th) 872 return ERR_PTR(-ENOMEM); 873 874 th->id = ida_simple_get(&intel_th_ida, 0, 0, GFP_KERNEL); 875 if (th->id < 0) { 876 err = th->id; 877 goto err_alloc; 878 } 879 880 th->major = __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS, 881 "intel_th/output", &intel_th_output_fops); 882 if (th->major < 0) { 883 err = th->major; 884 goto err_ida; 885 } 886 th->irq = -1; 887 th->dev = dev; 888 th->drvdata = drvdata; 889 890 for (r = 0; r < ndevres; r++) 891 switch (devres[r].flags & IORESOURCE_TYPE_BITS) { 892 case IORESOURCE_MEM: 893 th->resource[nr_mmios++] = devres[r]; 894 break; 895 case IORESOURCE_IRQ: 896 err = devm_request_irq(dev, devres[r].start, 897 intel_th_irq, IRQF_SHARED, 898 dev_name(dev), th); 899 if (err) 900 goto err_chrdev; 901 902 if (th->irq == -1) 903 th->irq = devres[r].start; 904 th->num_irqs++; 905 break; 906 default: 907 dev_warn(dev, "Unknown resource type %lx\n", 908 devres[r].flags); 909 break; 910 } 911 912 th->num_resources = nr_mmios; 913 914 dev_set_drvdata(dev, th); 915 916 pm_runtime_no_callbacks(dev); 917 pm_runtime_put(dev); 918 pm_runtime_allow(dev); 919 920 err = intel_th_populate(th); 921 if (err) { 922 /* free the subdevices and undo everything */ 923 intel_th_free(th); 924 return ERR_PTR(err); 925 } 926 927 return th; 928 929err_chrdev: 930 __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, 931 "intel_th/output"); 932 933err_ida: 934 ida_simple_remove(&intel_th_ida, th->id); 935 936err_alloc: 937 kfree(th); 938 939 return ERR_PTR(err); 940} 941EXPORT_SYMBOL_GPL(intel_th_alloc); 942 943void intel_th_free(struct intel_th *th) 944{ 945 int i; 946 947 intel_th_request_hub_module_flush(th); 948 949 intel_th_device_remove(th->hub); 950 for (i = 0; i < th->num_thdevs; i++) { 951 if (th->thdev[i] != th->hub) 952 intel_th_device_remove(th->thdev[i]); 953 th->thdev[i] = NULL; 954 } 955 956 th->num_thdevs = 0; 957 958 for (i = 0; i < th->num_irqs; i++) 959 devm_free_irq(th->dev, th->irq + i, th); 960 961 pm_runtime_get_sync(th->dev); 962 pm_runtime_forbid(th->dev); 963 964 __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, 965 "intel_th/output"); 966 967 ida_simple_remove(&intel_th_ida, th->id); 968 969 kfree(th); 970} 971EXPORT_SYMBOL_GPL(intel_th_free); 972 973/** 974 * intel_th_trace_enable() - enable tracing for an output device 975 * @thdev: output device that requests tracing be enabled 976 */ 977int intel_th_trace_enable(struct intel_th_device *thdev) 978{ 979 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); 980 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); 981 982 if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) 983 return -EINVAL; 984 985 if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) 986 return -EINVAL; 987 988 pm_runtime_get_sync(&thdev->dev); 989 hubdrv->enable(hub, &thdev->output); 990 991 return 0; 992} 993EXPORT_SYMBOL_GPL(intel_th_trace_enable); 994 995/** 996 * intel_th_trace_switch() - execute a switch sequence 997 * @thdev: output device that requests tracing switch 998 */ 999int intel_th_trace_switch(struct intel_th_device *thdev) 1000{ 1001 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); 1002 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); 1003 1004 if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) 1005 return -EINVAL; 1006 1007 if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) 1008 return -EINVAL; 1009 1010 hubdrv->trig_switch(hub, &thdev->output); 1011 1012 return 0; 1013} 1014EXPORT_SYMBOL_GPL(intel_th_trace_switch); 1015 1016/** 1017 * intel_th_trace_disable() - disable tracing for an output device 1018 * @thdev: output device that requests tracing be disabled 1019 */ 1020int intel_th_trace_disable(struct intel_th_device *thdev) 1021{ 1022 struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); 1023 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); 1024 1025 WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH); 1026 if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) 1027 return -EINVAL; 1028 1029 hubdrv->disable(hub, &thdev->output); 1030 pm_runtime_put(&thdev->dev); 1031 1032 return 0; 1033} 1034EXPORT_SYMBOL_GPL(intel_th_trace_disable); 1035 1036int intel_th_set_output(struct intel_th_device *thdev, 1037 unsigned int master) 1038{ 1039 struct intel_th_device *hub = to_intel_th_hub(thdev); 1040 struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); 1041 int ret; 1042 1043 /* In host mode, this is up to the external debugger, do nothing. */ 1044 if (hub->host_mode) 1045 return 0; 1046 1047 /* 1048 * hub is instantiated together with the source device that 1049 * calls here, so guaranteed to be present. 1050 */ 1051 hubdrv = to_intel_th_driver(hub->dev.driver); 1052 if (!hubdrv || !try_module_get(hubdrv->driver.owner)) 1053 return -EINVAL; 1054 1055 if (!hubdrv->set_output) { 1056 ret = -ENOTSUPP; 1057 goto out; 1058 } 1059 1060 ret = hubdrv->set_output(hub, master); 1061 1062out: 1063 module_put(hubdrv->driver.owner); 1064 return ret; 1065} 1066EXPORT_SYMBOL_GPL(intel_th_set_output); 1067 1068static int __init intel_th_init(void) 1069{ 1070 intel_th_debug_init(); 1071 1072 return bus_register(&intel_th_bus); 1073} 1074subsys_initcall(intel_th_init); 1075 1076static void __exit intel_th_exit(void) 1077{ 1078 intel_th_debug_done(); 1079 1080 bus_unregister(&intel_th_bus); 1081} 1082module_exit(intel_th_exit); 1083 1084MODULE_LICENSE("GPL v2"); 1085MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver"); 1086MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");