| .. | .. |
|---|
| 439 | 439 | unsigned nres; |
|---|
| 440 | 440 | unsigned type; |
|---|
| 441 | 441 | unsigned otype; |
|---|
| 442 | + bool mknode; |
|---|
| 442 | 443 | unsigned scrpd; |
|---|
| 443 | 444 | int id; |
|---|
| 444 | 445 | } intel_th_subdevices[] = { |
|---|
| .. | .. |
|---|
| 446 | 447 | .nres = 1, |
|---|
| 447 | 448 | .res = { |
|---|
| 448 | 449 | { |
|---|
| 449 | | - /* Handle TSCU from GTH driver */ |
|---|
| 450 | + /* Handle TSCU and CTS from GTH driver */ |
|---|
| 450 | 451 | .start = REG_GTH_OFFSET, |
|---|
| 451 | | - .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1, |
|---|
| 452 | + .end = REG_CTS_OFFSET + REG_CTS_LENGTH - 1, |
|---|
| 452 | 453 | .flags = IORESOURCE_MEM, |
|---|
| 453 | 454 | }, |
|---|
| 454 | 455 | }, |
|---|
| .. | .. |
|---|
| 473 | 474 | .name = "msc", |
|---|
| 474 | 475 | .id = 0, |
|---|
| 475 | 476 | .type = INTEL_TH_OUTPUT, |
|---|
| 477 | + .mknode = true, |
|---|
| 476 | 478 | .otype = GTH_MSU, |
|---|
| 477 | 479 | .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC0_IS_ENABLED, |
|---|
| 478 | 480 | }, |
|---|
| .. | .. |
|---|
| 493 | 495 | .name = "msc", |
|---|
| 494 | 496 | .id = 1, |
|---|
| 495 | 497 | .type = INTEL_TH_OUTPUT, |
|---|
| 498 | + .mknode = true, |
|---|
| 496 | 499 | .otype = GTH_MSU, |
|---|
| 497 | 500 | .scrpd = SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC1_IS_ENABLED, |
|---|
| 498 | 501 | }, |
|---|
| .. | .. |
|---|
| 505 | 508 | .flags = IORESOURCE_MEM, |
|---|
| 506 | 509 | }, |
|---|
| 507 | 510 | { |
|---|
| 508 | | - .start = 1, /* use resource[1] */ |
|---|
| 511 | + .start = TH_MMIO_SW, |
|---|
| 509 | 512 | .end = 0, |
|---|
| 510 | 513 | .flags = IORESOURCE_MEM, |
|---|
| 511 | 514 | }, |
|---|
| 512 | 515 | }, |
|---|
| 513 | 516 | .id = -1, |
|---|
| 514 | 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", |
|---|
| 515 | 536 | .type = INTEL_TH_SOURCE, |
|---|
| 516 | 537 | }, |
|---|
| 517 | 538 | { |
|---|
| .. | .. |
|---|
| 598 | 619 | struct intel_th_device *thdev; |
|---|
| 599 | 620 | struct resource res[3]; |
|---|
| 600 | 621 | unsigned int req = 0; |
|---|
| 601 | | - bool is64bit = false; |
|---|
| 602 | 622 | int r, err; |
|---|
| 603 | 623 | |
|---|
| 604 | 624 | thdev = intel_th_device_alloc(th, subdev->type, subdev->name, |
|---|
| .. | .. |
|---|
| 608 | 628 | |
|---|
| 609 | 629 | thdev->drvdata = th->drvdata; |
|---|
| 610 | 630 | |
|---|
| 611 | | - for (r = 0; r < th->num_resources; r++) |
|---|
| 612 | | - if (th->resource[r].flags & IORESOURCE_MEM_64) { |
|---|
| 613 | | - is64bit = true; |
|---|
| 614 | | - break; |
|---|
| 615 | | - } |
|---|
| 616 | | - |
|---|
| 617 | 631 | memcpy(res, subdev->res, |
|---|
| 618 | 632 | sizeof(struct resource) * subdev->nres); |
|---|
| 619 | 633 | |
|---|
| 620 | 634 | for (r = 0; r < subdev->nres; r++) { |
|---|
| 621 | 635 | struct resource *devres = th->resource; |
|---|
| 622 | | - int bar = 0; /* cut subdevices' MMIO from resource[0] */ |
|---|
| 636 | + int bar = TH_MMIO_CONFIG; |
|---|
| 623 | 637 | |
|---|
| 624 | 638 | /* |
|---|
| 625 | 639 | * Take .end == 0 to mean 'take the whole bar', |
|---|
| .. | .. |
|---|
| 628 | 642 | */ |
|---|
| 629 | 643 | if (!res[r].end && res[r].flags == IORESOURCE_MEM) { |
|---|
| 630 | 644 | bar = res[r].start; |
|---|
| 631 | | - if (is64bit) |
|---|
| 632 | | - bar *= 2; |
|---|
| 645 | + err = -ENODEV; |
|---|
| 646 | + if (bar >= th->num_resources) |
|---|
| 647 | + goto fail_put_device; |
|---|
| 633 | 648 | res[r].start = 0; |
|---|
| 634 | 649 | res[r].end = resource_size(&devres[bar]) - 1; |
|---|
| 635 | 650 | } |
|---|
| .. | .. |
|---|
| 641 | 656 | dev_dbg(th->dev, "%s:%d @ %pR\n", |
|---|
| 642 | 657 | subdev->name, r, &res[r]); |
|---|
| 643 | 658 | } else if (res[r].flags & IORESOURCE_IRQ) { |
|---|
| 644 | | - res[r].start = th->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; |
|---|
| 645 | 665 | } |
|---|
| 646 | 666 | } |
|---|
| 647 | 667 | |
|---|
| .. | .. |
|---|
| 650 | 670 | goto fail_put_device; |
|---|
| 651 | 671 | |
|---|
| 652 | 672 | if (subdev->type == INTEL_TH_OUTPUT) { |
|---|
| 653 | | - thdev->dev.devt = MKDEV(th->major, th->num_thdevs); |
|---|
| 673 | + if (subdev->mknode) |
|---|
| 674 | + thdev->dev.devt = MKDEV(th->major, th->num_thdevs); |
|---|
| 654 | 675 | thdev->output.type = subdev->otype; |
|---|
| 655 | 676 | thdev->output.port = -1; |
|---|
| 656 | 677 | thdev->output.scratchpad = subdev->scrpd; |
|---|
| .. | .. |
|---|
| 767 | 788 | |
|---|
| 768 | 789 | thdev = intel_th_subdevice_alloc(th, subdev); |
|---|
| 769 | 790 | /* note: caller should free subdevices from th::thdev[] */ |
|---|
| 770 | | - if (IS_ERR(thdev)) |
|---|
| 791 | + if (IS_ERR(thdev)) { |
|---|
| 792 | + /* ENODEV for individual subdevices is allowed */ |
|---|
| 793 | + if (PTR_ERR(thdev) == -ENODEV) |
|---|
| 794 | + continue; |
|---|
| 795 | + |
|---|
| 771 | 796 | return PTR_ERR(thdev); |
|---|
| 797 | + } |
|---|
| 772 | 798 | |
|---|
| 773 | 799 | th->thdev[th->num_thdevs++] = thdev; |
|---|
| 774 | 800 | } |
|---|
| 775 | 801 | |
|---|
| 776 | 802 | return 0; |
|---|
| 777 | | -} |
|---|
| 778 | | - |
|---|
| 779 | | -static int match_devt(struct device *dev, void *data) |
|---|
| 780 | | -{ |
|---|
| 781 | | - dev_t devt = (dev_t)(unsigned long)data; |
|---|
| 782 | | - |
|---|
| 783 | | - return dev->devt == devt; |
|---|
| 784 | 803 | } |
|---|
| 785 | 804 | |
|---|
| 786 | 805 | static int intel_th_output_open(struct inode *inode, struct file *file) |
|---|
| .. | .. |
|---|
| 790 | 809 | struct device *dev; |
|---|
| 791 | 810 | int err; |
|---|
| 792 | 811 | |
|---|
| 793 | | - dev = bus_find_device(&intel_th_bus, NULL, |
|---|
| 794 | | - (void *)(unsigned long)inode->i_rdev, |
|---|
| 795 | | - match_devt); |
|---|
| 812 | + dev = bus_find_device_by_devt(&intel_th_bus, inode->i_rdev); |
|---|
| 796 | 813 | if (!dev || !dev->driver) |
|---|
| 797 | 814 | return -ENODEV; |
|---|
| 798 | 815 | |
|---|
| .. | .. |
|---|
| 818 | 835 | .llseek = noop_llseek, |
|---|
| 819 | 836 | }; |
|---|
| 820 | 837 | |
|---|
| 838 | +static 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 | + |
|---|
| 821 | 857 | /** |
|---|
| 822 | 858 | * intel_th_alloc() - allocate a new Intel TH device and its subdevices |
|---|
| 823 | 859 | * @dev: parent device |
|---|
| 824 | | - * @devres: parent's resources |
|---|
| 825 | | - * @ndevres: number of resources |
|---|
| 860 | + * @devres: resources indexed by th_mmio_idx |
|---|
| 826 | 861 | * @irq: irq number |
|---|
| 827 | 862 | */ |
|---|
| 828 | 863 | struct intel_th * |
|---|
| 829 | 864 | intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, |
|---|
| 830 | | - struct resource *devres, unsigned int ndevres, int irq) |
|---|
| 865 | + struct resource *devres, unsigned int ndevres) |
|---|
| 831 | 866 | { |
|---|
| 867 | + int err, r, nr_mmios = 0; |
|---|
| 832 | 868 | struct intel_th *th; |
|---|
| 833 | | - int err, r; |
|---|
| 834 | | - |
|---|
| 835 | | - if (irq == -1) |
|---|
| 836 | | - for (r = 0; r < ndevres; r++) |
|---|
| 837 | | - if (devres[r].flags & IORESOURCE_IRQ) { |
|---|
| 838 | | - irq = devres[r].start; |
|---|
| 839 | | - break; |
|---|
| 840 | | - } |
|---|
| 841 | 869 | |
|---|
| 842 | 870 | th = kzalloc(sizeof(*th), GFP_KERNEL); |
|---|
| 843 | 871 | if (!th) |
|---|
| .. | .. |
|---|
| 855 | 883 | err = th->major; |
|---|
| 856 | 884 | goto err_ida; |
|---|
| 857 | 885 | } |
|---|
| 886 | + th->irq = -1; |
|---|
| 858 | 887 | th->dev = dev; |
|---|
| 859 | 888 | th->drvdata = drvdata; |
|---|
| 860 | 889 | |
|---|
| 861 | | - th->resource = devres; |
|---|
| 862 | | - th->num_resources = ndevres; |
|---|
| 863 | | - th->irq = irq; |
|---|
| 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; |
|---|
| 864 | 913 | |
|---|
| 865 | 914 | dev_set_drvdata(dev, th); |
|---|
| 866 | 915 | |
|---|
| .. | .. |
|---|
| 876 | 925 | } |
|---|
| 877 | 926 | |
|---|
| 878 | 927 | return th; |
|---|
| 928 | + |
|---|
| 929 | +err_chrdev: |
|---|
| 930 | + __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, |
|---|
| 931 | + "intel_th/output"); |
|---|
| 879 | 932 | |
|---|
| 880 | 933 | err_ida: |
|---|
| 881 | 934 | ida_simple_remove(&intel_th_ida, th->id); |
|---|
| .. | .. |
|---|
| 901 | 954 | } |
|---|
| 902 | 955 | |
|---|
| 903 | 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); |
|---|
| 904 | 960 | |
|---|
| 905 | 961 | pm_runtime_get_sync(th->dev); |
|---|
| 906 | 962 | pm_runtime_forbid(th->dev); |
|---|
| .. | .. |
|---|
| 937 | 993 | EXPORT_SYMBOL_GPL(intel_th_trace_enable); |
|---|
| 938 | 994 | |
|---|
| 939 | 995 | /** |
|---|
| 996 | + * intel_th_trace_switch() - execute a switch sequence |
|---|
| 997 | + * @thdev: output device that requests tracing switch |
|---|
| 998 | + */ |
|---|
| 999 | +int 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 | +} |
|---|
| 1014 | +EXPORT_SYMBOL_GPL(intel_th_trace_switch); |
|---|
| 1015 | + |
|---|
| 1016 | +/** |
|---|
| 940 | 1017 | * intel_th_trace_disable() - disable tracing for an output device |
|---|
| 941 | 1018 | * @thdev: output device that requests tracing be disabled |
|---|
| 942 | 1019 | */ |
|---|