162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * platform_device probing code for ARM performance counters. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles 662306a36Sopenharmony_ci * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#define pr_fmt(fmt) "hw perfevents: " fmt 962306a36Sopenharmony_ci#define dev_fmt pr_fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bug.h> 1262306a36Sopenharmony_ci#include <linux/cpumask.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqdesc.h> 1762306a36Sopenharmony_ci#include <linux/kconfig.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/percpu.h> 2062306a36Sopenharmony_ci#include <linux/perf/arm_pmu.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/printk.h> 2362306a36Sopenharmony_ci#include <linux/smp.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int probe_current_pmu(struct arm_pmu *pmu, 2662306a36Sopenharmony_ci const struct pmu_probe_info *info) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int cpu = get_cpu(); 2962306a36Sopenharmony_ci unsigned int cpuid = read_cpuid_id(); 3062306a36Sopenharmony_ci int ret = -ENODEV; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci pr_info("probing PMU on CPU %d\n", cpu); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (; info->init != NULL; info++) { 3562306a36Sopenharmony_ci if ((cpuid & info->mask) != info->cpuid) 3662306a36Sopenharmony_ci continue; 3762306a36Sopenharmony_ci ret = info->init(pmu); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci put_cpu(); 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int pmu_parse_percpu_irq(struct arm_pmu *pmu, int irq) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int cpu, ret; 4862306a36Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = pmu->hw_events; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus); 5162306a36Sopenharmony_ci if (ret) 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for_each_cpu(cpu, &pmu->supported_cpus) 5562306a36Sopenharmony_ci per_cpu(hw_events->irq, cpu) = irq; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic bool pmu_has_irq_affinity(struct device_node *node) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return !!of_find_property(node, "interrupt-affinity", NULL); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int pmu_parse_irq_affinity(struct device *dev, int i) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct device_node *dn; 6862306a36Sopenharmony_ci int cpu; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * If we don't have an interrupt-affinity property, we guess irq 7262306a36Sopenharmony_ci * affinity matches our logical CPU order, as we used to assume. 7362306a36Sopenharmony_ci * This is fragile, so we'll warn in pmu_parse_irqs(). 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci if (!pmu_has_irq_affinity(dev->of_node)) 7662306a36Sopenharmony_ci return i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci dn = of_parse_phandle(dev->of_node, "interrupt-affinity", i); 7962306a36Sopenharmony_ci if (!dn) { 8062306a36Sopenharmony_ci dev_warn(dev, "failed to parse interrupt-affinity[%d]\n", i); 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci cpu = of_cpu_node_to_id(dn); 8562306a36Sopenharmony_ci if (cpu < 0) { 8662306a36Sopenharmony_ci dev_warn(dev, "failed to find logical CPU for %pOFn\n", dn); 8762306a36Sopenharmony_ci cpu = nr_cpu_ids; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci of_node_put(dn); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return cpu; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int pmu_parse_irqs(struct arm_pmu *pmu) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int i = 0, num_irqs; 9862306a36Sopenharmony_ci struct platform_device *pdev = pmu->plat_device; 9962306a36Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = pmu->hw_events; 10062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci num_irqs = platform_irq_count(pdev); 10362306a36Sopenharmony_ci if (num_irqs < 0) 10462306a36Sopenharmony_ci return dev_err_probe(dev, num_irqs, "unable to count PMU IRQs\n"); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * In this case we have no idea which CPUs are covered by the PMU. 10862306a36Sopenharmony_ci * To match our prior behaviour, we assume all CPUs in this case. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (num_irqs == 0) { 11162306a36Sopenharmony_ci dev_warn(dev, "no irqs for PMU, sampling events not supported\n"); 11262306a36Sopenharmony_ci pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; 11362306a36Sopenharmony_ci cpumask_setall(&pmu->supported_cpus); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (num_irqs == 1) { 11862306a36Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 11962306a36Sopenharmony_ci if ((irq > 0) && irq_is_percpu_devid(irq)) 12062306a36Sopenharmony_ci return pmu_parse_percpu_irq(pmu, irq); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(dev->of_node)) 12462306a36Sopenharmony_ci dev_warn(dev, "no interrupt-affinity property, guessing.\n"); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < num_irqs; i++) { 12762306a36Sopenharmony_ci int cpu, irq; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci irq = platform_get_irq(pdev, i); 13062306a36Sopenharmony_ci if (WARN_ON(irq <= 0)) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (irq_is_percpu_devid(irq)) { 13462306a36Sopenharmony_ci dev_warn(dev, "multiple PPIs or mismatched SPI/PPI detected\n"); 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci cpu = pmu_parse_irq_affinity(dev, i); 13962306a36Sopenharmony_ci if (cpu < 0) 14062306a36Sopenharmony_ci return cpu; 14162306a36Sopenharmony_ci if (cpu >= nr_cpu_ids) 14262306a36Sopenharmony_ci continue; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (per_cpu(hw_events->irq, cpu)) { 14562306a36Sopenharmony_ci dev_warn(dev, "multiple PMU IRQs for the same CPU detected\n"); 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci per_cpu(hw_events->irq, cpu) = irq; 15062306a36Sopenharmony_ci cpumask_set_cpu(cpu, &pmu->supported_cpus); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int armpmu_request_irqs(struct arm_pmu *armpmu) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; 15962306a36Sopenharmony_ci int cpu, err = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for_each_cpu(cpu, &armpmu->supported_cpus) { 16262306a36Sopenharmony_ci int irq = per_cpu(hw_events->irq, cpu); 16362306a36Sopenharmony_ci if (!irq) 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = armpmu_request_irq(irq, cpu); 16762306a36Sopenharmony_ci if (err) 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return err; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void armpmu_free_irqs(struct arm_pmu *armpmu) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci int cpu; 17762306a36Sopenharmony_ci struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for_each_cpu(cpu, &armpmu->supported_cpus) { 18062306a36Sopenharmony_ci int irq = per_cpu(hw_events->irq, cpu); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci armpmu_free_irq(irq, cpu); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint arm_pmu_device_probe(struct platform_device *pdev, 18762306a36Sopenharmony_ci const struct of_device_id *of_table, 18862306a36Sopenharmony_ci const struct pmu_probe_info *probe_table) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci armpmu_init_fn init_fn; 19162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 19262306a36Sopenharmony_ci struct arm_pmu *pmu; 19362306a36Sopenharmony_ci int ret = -ENODEV; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pmu = armpmu_alloc(); 19662306a36Sopenharmony_ci if (!pmu) 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci pmu->plat_device = pdev; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = pmu_parse_irqs(pmu); 20262306a36Sopenharmony_ci if (ret) 20362306a36Sopenharmony_ci goto out_free; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci init_fn = of_device_get_match_data(dev); 20662306a36Sopenharmony_ci if (init_fn) { 20762306a36Sopenharmony_ci pmu->secure_access = of_property_read_bool(dev->of_node, 20862306a36Sopenharmony_ci "secure-reg-access"); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* arm64 systems boot only as non-secure */ 21162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64) && pmu->secure_access) { 21262306a36Sopenharmony_ci dev_warn(dev, "ignoring \"secure-reg-access\" property for arm64\n"); 21362306a36Sopenharmony_ci pmu->secure_access = false; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = init_fn(pmu); 21762306a36Sopenharmony_ci } else if (probe_table) { 21862306a36Sopenharmony_ci cpumask_setall(&pmu->supported_cpus); 21962306a36Sopenharmony_ci ret = probe_current_pmu(pmu, probe_table); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (ret) { 22362306a36Sopenharmony_ci dev_err(dev, "failed to probe PMU!\n"); 22462306a36Sopenharmony_ci goto out_free; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = armpmu_request_irqs(pmu); 22862306a36Sopenharmony_ci if (ret) 22962306a36Sopenharmony_ci goto out_free_irqs; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = armpmu_register(pmu); 23262306a36Sopenharmony_ci if (ret) { 23362306a36Sopenharmony_ci dev_err(dev, "failed to register PMU devices!\n"); 23462306a36Sopenharmony_ci goto out_free_irqs; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciout_free_irqs: 24062306a36Sopenharmony_ci armpmu_free_irqs(pmu); 24162306a36Sopenharmony_ciout_free: 24262306a36Sopenharmony_ci armpmu_free(pmu); 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci} 245