| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ARM DynamIQ Shared Unit (DSU) PMU driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) ARM Limited, 2017. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Based on ARM CCI-PMU, ARMv8 PMU-v3 drivers. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or |
|---|
| 9 | | - * modify it under the terms of the GNU General Public License |
|---|
| 10 | | - * version 2 as published by the Free Software Foundation. |
|---|
| 11 | 8 | */ |
|---|
| 12 | 9 | |
|---|
| 13 | 10 | #define PMUNAME "arm_dsu" |
|---|
| 14 | 11 | #define DRVNAME PMUNAME "_pmu" |
|---|
| 15 | 12 | #define pr_fmt(fmt) DRVNAME ": " fmt |
|---|
| 16 | 13 | |
|---|
| 14 | +#include <linux/acpi.h> |
|---|
| 17 | 15 | #include <linux/bitmap.h> |
|---|
| 18 | 16 | #include <linux/bitops.h> |
|---|
| 19 | 17 | #include <linux/bug.h> |
|---|
| .. | .. |
|---|
| 562 | 560 | return -EINVAL; |
|---|
| 563 | 561 | } |
|---|
| 564 | 562 | |
|---|
| 565 | | - if (has_branch_stack(event) || |
|---|
| 566 | | - event->attr.exclude_user || |
|---|
| 567 | | - event->attr.exclude_kernel || |
|---|
| 568 | | - event->attr.exclude_hv || |
|---|
| 569 | | - event->attr.exclude_idle || |
|---|
| 570 | | - event->attr.exclude_host || |
|---|
| 571 | | - event->attr.exclude_guest) { |
|---|
| 563 | + if (has_branch_stack(event)) { |
|---|
| 572 | 564 | dev_dbg(dsu_pmu->pmu.dev, "Can't support filtering\n"); |
|---|
| 573 | 565 | return -EINVAL; |
|---|
| 574 | 566 | } |
|---|
| .. | .. |
|---|
| 612 | 604 | } |
|---|
| 613 | 605 | |
|---|
| 614 | 606 | /** |
|---|
| 615 | | - * dsu_pmu_dt_get_cpus: Get the list of CPUs in the cluster. |
|---|
| 607 | + * dsu_pmu_dt_get_cpus: Get the list of CPUs in the cluster |
|---|
| 608 | + * from device tree. |
|---|
| 616 | 609 | */ |
|---|
| 617 | | -static int dsu_pmu_dt_get_cpus(struct device_node *dev, cpumask_t *mask) |
|---|
| 610 | +static int dsu_pmu_dt_get_cpus(struct device *dev, cpumask_t *mask) |
|---|
| 618 | 611 | { |
|---|
| 619 | 612 | int i = 0, n, cpu; |
|---|
| 620 | 613 | struct device_node *cpu_node; |
|---|
| 621 | 614 | |
|---|
| 622 | | - n = of_count_phandle_with_args(dev, "cpus", NULL); |
|---|
| 615 | + n = of_count_phandle_with_args(dev->of_node, "cpus", NULL); |
|---|
| 623 | 616 | if (n <= 0) |
|---|
| 624 | 617 | return -ENODEV; |
|---|
| 625 | 618 | for (; i < n; i++) { |
|---|
| 626 | | - cpu_node = of_parse_phandle(dev, "cpus", i); |
|---|
| 619 | + cpu_node = of_parse_phandle(dev->of_node, "cpus", i); |
|---|
| 627 | 620 | if (!cpu_node) |
|---|
| 628 | 621 | break; |
|---|
| 629 | 622 | cpu = of_cpu_node_to_id(cpu_node); |
|---|
| .. | .. |
|---|
| 637 | 630 | continue; |
|---|
| 638 | 631 | cpumask_set_cpu(cpu, mask); |
|---|
| 639 | 632 | } |
|---|
| 633 | + return 0; |
|---|
| 634 | +} |
|---|
| 635 | + |
|---|
| 636 | +/** |
|---|
| 637 | + * dsu_pmu_acpi_get_cpus: Get the list of CPUs in the cluster |
|---|
| 638 | + * from ACPI. |
|---|
| 639 | + */ |
|---|
| 640 | +static int dsu_pmu_acpi_get_cpus(struct device *dev, cpumask_t *mask) |
|---|
| 641 | +{ |
|---|
| 642 | +#ifdef CONFIG_ACPI |
|---|
| 643 | + int cpu; |
|---|
| 644 | + |
|---|
| 645 | + /* |
|---|
| 646 | + * A dsu pmu node is inside a cluster parent node along with cpu nodes. |
|---|
| 647 | + * We need to find out all cpus that have the same parent with this pmu. |
|---|
| 648 | + */ |
|---|
| 649 | + for_each_possible_cpu(cpu) { |
|---|
| 650 | + struct acpi_device *acpi_dev; |
|---|
| 651 | + struct device *cpu_dev = get_cpu_device(cpu); |
|---|
| 652 | + |
|---|
| 653 | + if (!cpu_dev) |
|---|
| 654 | + continue; |
|---|
| 655 | + |
|---|
| 656 | + acpi_dev = ACPI_COMPANION(cpu_dev); |
|---|
| 657 | + if (acpi_dev && |
|---|
| 658 | + acpi_dev->parent == ACPI_COMPANION(dev)->parent) |
|---|
| 659 | + cpumask_set_cpu(cpu, mask); |
|---|
| 660 | + } |
|---|
| 661 | +#endif |
|---|
| 662 | + |
|---|
| 640 | 663 | return 0; |
|---|
| 641 | 664 | } |
|---|
| 642 | 665 | |
|---|
| .. | .. |
|---|
| 685 | 708 | { |
|---|
| 686 | 709 | int irq, rc; |
|---|
| 687 | 710 | struct dsu_pmu *dsu_pmu; |
|---|
| 711 | + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); |
|---|
| 688 | 712 | char *name; |
|---|
| 689 | 713 | static atomic_t pmu_idx = ATOMIC_INIT(-1); |
|---|
| 690 | 714 | |
|---|
| .. | .. |
|---|
| 692 | 716 | if (IS_ERR(dsu_pmu)) |
|---|
| 693 | 717 | return PTR_ERR(dsu_pmu); |
|---|
| 694 | 718 | |
|---|
| 695 | | - rc = dsu_pmu_dt_get_cpus(pdev->dev.of_node, &dsu_pmu->associated_cpus); |
|---|
| 719 | + if (IS_ERR_OR_NULL(fwnode)) |
|---|
| 720 | + return -ENOENT; |
|---|
| 721 | + |
|---|
| 722 | + if (is_of_node(fwnode)) |
|---|
| 723 | + rc = dsu_pmu_dt_get_cpus(&pdev->dev, &dsu_pmu->associated_cpus); |
|---|
| 724 | + else if (is_acpi_device_node(fwnode)) |
|---|
| 725 | + rc = dsu_pmu_acpi_get_cpus(&pdev->dev, &dsu_pmu->associated_cpus); |
|---|
| 726 | + else |
|---|
| 727 | + return -ENOENT; |
|---|
| 728 | + |
|---|
| 696 | 729 | if (rc) { |
|---|
| 697 | 730 | dev_warn(&pdev->dev, "Failed to parse the CPUs\n"); |
|---|
| 698 | 731 | return rc; |
|---|
| 699 | 732 | } |
|---|
| 700 | 733 | |
|---|
| 701 | 734 | irq = platform_get_irq(pdev, 0); |
|---|
| 702 | | - if (irq < 0) { |
|---|
| 703 | | - dev_warn(&pdev->dev, "Failed to find IRQ\n"); |
|---|
| 735 | + if (irq < 0) |
|---|
| 704 | 736 | return -EINVAL; |
|---|
| 705 | | - } |
|---|
| 706 | 737 | |
|---|
| 707 | 738 | name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_%d", |
|---|
| 708 | 739 | PMUNAME, atomic_inc_return(&pmu_idx)); |
|---|
| .. | .. |
|---|
| 735 | 766 | .read = dsu_pmu_read, |
|---|
| 736 | 767 | |
|---|
| 737 | 768 | .attr_groups = dsu_pmu_attr_groups, |
|---|
| 769 | + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, |
|---|
| 738 | 770 | }; |
|---|
| 739 | 771 | |
|---|
| 740 | 772 | rc = perf_pmu_register(&dsu_pmu->pmu, name, -1); |
|---|
| .. | .. |
|---|
| 762 | 794 | { .compatible = "arm,dsu-pmu", }, |
|---|
| 763 | 795 | {}, |
|---|
| 764 | 796 | }; |
|---|
| 797 | +MODULE_DEVICE_TABLE(of, dsu_pmu_of_match); |
|---|
| 798 | + |
|---|
| 799 | +#ifdef CONFIG_ACPI |
|---|
| 800 | +static const struct acpi_device_id dsu_pmu_acpi_match[] = { |
|---|
| 801 | + { "ARMHD500", 0}, |
|---|
| 802 | + {}, |
|---|
| 803 | +}; |
|---|
| 804 | +MODULE_DEVICE_TABLE(acpi, dsu_pmu_acpi_match); |
|---|
| 805 | +#endif |
|---|
| 765 | 806 | |
|---|
| 766 | 807 | static struct platform_driver dsu_pmu_driver = { |
|---|
| 767 | 808 | .driver = { |
|---|
| 768 | 809 | .name = DRVNAME, |
|---|
| 769 | 810 | .of_match_table = of_match_ptr(dsu_pmu_of_match), |
|---|
| 811 | + .acpi_match_table = ACPI_PTR(dsu_pmu_acpi_match), |
|---|
| 812 | + .suppress_bind_attrs = true, |
|---|
| 770 | 813 | }, |
|---|
| 771 | 814 | .probe = dsu_pmu_device_probe, |
|---|
| 772 | 815 | .remove = dsu_pmu_device_remove, |
|---|
| .. | .. |
|---|
| 823 | 866 | if (ret < 0) |
|---|
| 824 | 867 | return ret; |
|---|
| 825 | 868 | dsu_pmu_cpuhp_state = ret; |
|---|
| 826 | | - return platform_driver_register(&dsu_pmu_driver); |
|---|
| 869 | + ret = platform_driver_register(&dsu_pmu_driver); |
|---|
| 870 | + if (ret) |
|---|
| 871 | + cpuhp_remove_multi_state(dsu_pmu_cpuhp_state); |
|---|
| 872 | + |
|---|
| 873 | + return ret; |
|---|
| 827 | 874 | } |
|---|
| 828 | 875 | |
|---|
| 829 | 876 | static void __exit dsu_pmu_exit(void) |
|---|
| .. | .. |
|---|
| 835 | 882 | module_init(dsu_pmu_init); |
|---|
| 836 | 883 | module_exit(dsu_pmu_exit); |
|---|
| 837 | 884 | |
|---|
| 838 | | -MODULE_DEVICE_TABLE(of, dsu_pmu_of_match); |
|---|
| 839 | 885 | MODULE_DESCRIPTION("Perf driver for ARM DynamIQ Shared Unit"); |
|---|
| 840 | 886 | MODULE_AUTHOR("Suzuki K Poulose <suzuki.poulose@arm.com>"); |
|---|
| 841 | 887 | MODULE_LICENSE("GPL v2"); |
|---|