18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * platform_device probing code for ARM performance counters. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles 68c2ecf20Sopenharmony_ci * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hw perfevents: " fmt 98c2ecf20Sopenharmony_ci#define dev_fmt pr_fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bug.h> 128c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdesc.h> 178c2ecf20Sopenharmony_ci#include <linux/kconfig.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/percpu.h> 218c2ecf20Sopenharmony_ci#include <linux/perf/arm_pmu.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/printk.h> 248c2ecf20Sopenharmony_ci#include <linux/smp.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int probe_current_pmu(struct arm_pmu *pmu, 278c2ecf20Sopenharmony_ci const struct pmu_probe_info *info) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci int cpu = get_cpu(); 308c2ecf20Sopenharmony_ci unsigned int cpuid = read_cpuid_id(); 318c2ecf20Sopenharmony_ci int ret = -ENODEV; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci pr_info("probing PMU on CPU %d\n", cpu); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (; info->init != NULL; info++) { 368c2ecf20Sopenharmony_ci if ((cpuid & info->mask) != info->cpuid) 378c2ecf20Sopenharmony_ci continue; 388c2ecf20Sopenharmony_ci ret = info->init(pmu); 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci put_cpu(); 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int pmu_parse_percpu_irq(struct arm_pmu *pmu, int irq) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int cpu, ret; 498c2ecf20Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = pmu->hw_events; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus); 528c2ecf20Sopenharmony_ci if (ret) 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for_each_cpu(cpu, &pmu->supported_cpus) 568c2ecf20Sopenharmony_ci per_cpu(hw_events->irq, cpu) = irq; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic bool pmu_has_irq_affinity(struct device_node *node) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return !!of_find_property(node, "interrupt-affinity", NULL); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int pmu_parse_irq_affinity(struct device_node *node, int i) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct device_node *dn; 698c2ecf20Sopenharmony_ci int cpu; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * If we don't have an interrupt-affinity property, we guess irq 738c2ecf20Sopenharmony_ci * affinity matches our logical CPU order, as we used to assume. 748c2ecf20Sopenharmony_ci * This is fragile, so we'll warn in pmu_parse_irqs(). 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci if (!pmu_has_irq_affinity(node)) 778c2ecf20Sopenharmony_ci return i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci dn = of_parse_phandle(node, "interrupt-affinity", i); 808c2ecf20Sopenharmony_ci if (!dn) { 818c2ecf20Sopenharmony_ci pr_warn("failed to parse interrupt-affinity[%d] for %pOFn\n", 828c2ecf20Sopenharmony_ci i, node); 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci cpu = of_cpu_node_to_id(dn); 878c2ecf20Sopenharmony_ci if (cpu < 0) { 888c2ecf20Sopenharmony_ci pr_warn("failed to find logical CPU for %pOFn\n", dn); 898c2ecf20Sopenharmony_ci cpu = nr_cpu_ids; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci of_node_put(dn); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return cpu; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int pmu_parse_irqs(struct arm_pmu *pmu) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int i = 0, num_irqs; 1008c2ecf20Sopenharmony_ci struct platform_device *pdev = pmu->plat_device; 1018c2ecf20Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = pmu->hw_events; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci num_irqs = platform_irq_count(pdev); 1048c2ecf20Sopenharmony_ci if (num_irqs < 0) 1058c2ecf20Sopenharmony_ci return dev_err_probe(&pdev->dev, num_irqs, "unable to count PMU IRQs\n"); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* 1088c2ecf20Sopenharmony_ci * In this case we have no idea which CPUs are covered by the PMU. 1098c2ecf20Sopenharmony_ci * To match our prior behaviour, we assume all CPUs in this case. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci if (num_irqs == 0) { 1128c2ecf20Sopenharmony_ci pr_warn("no irqs for PMU, sampling events not supported\n"); 1138c2ecf20Sopenharmony_ci pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; 1148c2ecf20Sopenharmony_ci cpumask_setall(&pmu->supported_cpus); 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (num_irqs == 1) { 1198c2ecf20Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 1208c2ecf20Sopenharmony_ci if ((irq > 0) && irq_is_percpu_devid(irq)) 1218c2ecf20Sopenharmony_ci return pmu_parse_percpu_irq(pmu, irq); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(pdev->dev.of_node)) { 1258c2ecf20Sopenharmony_ci pr_warn("no interrupt-affinity property for %pOF, guessing.\n", 1268c2ecf20Sopenharmony_ci pdev->dev.of_node); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci for (i = 0; i < num_irqs; i++) { 1308c2ecf20Sopenharmony_ci int cpu, irq; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, i); 1338c2ecf20Sopenharmony_ci if (WARN_ON(irq <= 0)) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (irq_is_percpu_devid(irq)) { 1378c2ecf20Sopenharmony_ci pr_warn("multiple PPIs or mismatched SPI/PPI detected\n"); 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci cpu = pmu_parse_irq_affinity(pdev->dev.of_node, i); 1428c2ecf20Sopenharmony_ci if (cpu < 0) 1438c2ecf20Sopenharmony_ci return cpu; 1448c2ecf20Sopenharmony_ci if (cpu >= nr_cpu_ids) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (per_cpu(hw_events->irq, cpu)) { 1488c2ecf20Sopenharmony_ci pr_warn("multiple PMU IRQs for the same CPU detected\n"); 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci per_cpu(hw_events->irq, cpu) = irq; 1538c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &pmu->supported_cpus); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int armpmu_request_irqs(struct arm_pmu *armpmu) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; 1628c2ecf20Sopenharmony_ci int cpu, err = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci for_each_cpu(cpu, &armpmu->supported_cpus) { 1658c2ecf20Sopenharmony_ci int irq = per_cpu(hw_events->irq, cpu); 1668c2ecf20Sopenharmony_ci if (!irq) 1678c2ecf20Sopenharmony_ci continue; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci err = armpmu_request_irq(irq, cpu); 1708c2ecf20Sopenharmony_ci if (err) 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return err; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void armpmu_free_irqs(struct arm_pmu *armpmu) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int cpu; 1808c2ecf20Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for_each_cpu(cpu, &armpmu->supported_cpus) { 1838c2ecf20Sopenharmony_ci int irq = per_cpu(hw_events->irq, cpu); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci armpmu_free_irq(irq, cpu); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint arm_pmu_device_probe(struct platform_device *pdev, 1908c2ecf20Sopenharmony_ci const struct of_device_id *of_table, 1918c2ecf20Sopenharmony_ci const struct pmu_probe_info *probe_table) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 1948c2ecf20Sopenharmony_ci armpmu_init_fn init_fn; 1958c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 1968c2ecf20Sopenharmony_ci struct arm_pmu *pmu; 1978c2ecf20Sopenharmony_ci int ret = -ENODEV; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci pmu = armpmu_alloc(); 2008c2ecf20Sopenharmony_ci if (!pmu) 2018c2ecf20Sopenharmony_ci return -ENOMEM; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci pmu->plat_device = pdev; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = pmu_parse_irqs(pmu); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto out_free; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (node && (of_id = of_match_node(of_table, pdev->dev.of_node))) { 2108c2ecf20Sopenharmony_ci init_fn = of_id->data; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci pmu->secure_access = of_property_read_bool(pdev->dev.of_node, 2138c2ecf20Sopenharmony_ci "secure-reg-access"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* arm64 systems boot only as non-secure */ 2168c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64) && pmu->secure_access) { 2178c2ecf20Sopenharmony_ci pr_warn("ignoring \"secure-reg-access\" property for arm64\n"); 2188c2ecf20Sopenharmony_ci pmu->secure_access = false; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = init_fn(pmu); 2228c2ecf20Sopenharmony_ci } else if (probe_table) { 2238c2ecf20Sopenharmony_ci cpumask_setall(&pmu->supported_cpus); 2248c2ecf20Sopenharmony_ci ret = probe_current_pmu(pmu, probe_table); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (ret) { 2288c2ecf20Sopenharmony_ci pr_info("%pOF: failed to probe PMU!\n", node); 2298c2ecf20Sopenharmony_ci goto out_free; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = armpmu_request_irqs(pmu); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci goto out_free_irqs; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = armpmu_register(pmu); 2378c2ecf20Sopenharmony_ci if (ret) 2388c2ecf20Sopenharmony_ci goto out_free_irqs; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciout_free_irqs: 2438c2ecf20Sopenharmony_ci armpmu_free_irqs(pmu); 2448c2ecf20Sopenharmony_ciout_free: 2458c2ecf20Sopenharmony_ci pr_info("%pOF: failed to register PMU devices!\n", node); 2468c2ecf20Sopenharmony_ci armpmu_free(pmu); 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci} 249