linux/drivers/perf/arm_pmu_platform.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * platform_device probing code for ARM performance counters.
   4 *
   5 * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
   6 * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
   7 */
   8#define pr_fmt(fmt) "hw perfevents: " fmt
   9#define dev_fmt pr_fmt
  10
  11#include <linux/bug.h>
  12#include <linux/cpumask.h>
  13#include <linux/device.h>
  14#include <linux/errno.h>
  15#include <linux/irq.h>
  16#include <linux/irqdesc.h>
  17#include <linux/kconfig.h>
  18#include <linux/of.h>
  19#include <linux/of_device.h>
  20#include <linux/percpu.h>
  21#include <linux/perf/arm_pmu.h>
  22#include <linux/platform_device.h>
  23#include <linux/printk.h>
  24#include <linux/smp.h>
  25
  26static int probe_current_pmu(struct arm_pmu *pmu,
  27                             const struct pmu_probe_info *info)
  28{
  29        int cpu = get_cpu();
  30        unsigned int cpuid = read_cpuid_id();
  31        int ret = -ENODEV;
  32
  33        pr_info("probing PMU on CPU %d\n", cpu);
  34
  35        for (; info->init != NULL; info++) {
  36                if ((cpuid & info->mask) != info->cpuid)
  37                        continue;
  38                ret = info->init(pmu);
  39                break;
  40        }
  41
  42        put_cpu();
  43        return ret;
  44}
  45
  46static int pmu_parse_percpu_irq(struct arm_pmu *pmu, int irq)
  47{
  48        int cpu, ret;
  49        struct pmu_hw_events __percpu *hw_events = pmu->hw_events;
  50
  51        ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
  52        if (ret)
  53                return ret;
  54
  55        for_each_cpu(cpu, &pmu->supported_cpus)
  56                per_cpu(hw_events->irq, cpu) = irq;
  57
  58        return 0;
  59}
  60
  61static bool pmu_has_irq_affinity(struct device_node *node)
  62{
  63        return !!of_find_property(node, "interrupt-affinity", NULL);
  64}
  65
  66static int pmu_parse_irq_affinity(struct device *dev, int i)
  67{
  68        struct device_node *dn;
  69        int cpu;
  70
  71        /*
  72         * If we don't have an interrupt-affinity property, we guess irq
  73         * affinity matches our logical CPU order, as we used to assume.
  74         * This is fragile, so we'll warn in pmu_parse_irqs().
  75         */
  76        if (!pmu_has_irq_affinity(dev->of_node))
  77                return i;
  78
  79        dn = of_parse_phandle(dev->of_node, "interrupt-affinity", i);
  80        if (!dn) {
  81                dev_warn(dev, "failed to parse interrupt-affinity[%d]\n", i);
  82                return -EINVAL;
  83        }
  84
  85        cpu = of_cpu_node_to_id(dn);
  86        if (cpu < 0) {
  87                dev_warn(dev, "failed to find logical CPU for %pOFn\n", dn);
  88                cpu = nr_cpu_ids;
  89        }
  90
  91        of_node_put(dn);
  92
  93        return cpu;
  94}
  95
  96static int pmu_parse_irqs(struct arm_pmu *pmu)
  97{
  98        int i = 0, num_irqs;
  99        struct platform_device *pdev = pmu->plat_device;
 100        struct pmu_hw_events __percpu *hw_events = pmu->hw_events;
 101        struct device *dev = &pdev->dev;
 102
 103        num_irqs = platform_irq_count(pdev);
 104        if (num_irqs < 0)
 105                return dev_err_probe(dev, num_irqs, "unable to count PMU IRQs\n");
 106
 107        /*
 108         * In this case we have no idea which CPUs are covered by the PMU.
 109         * To match our prior behaviour, we assume all CPUs in this case.
 110         */
 111        if (num_irqs == 0) {
 112                dev_warn(dev, "no irqs for PMU, sampling events not supported\n");
 113                pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
 114                cpumask_setall(&pmu->supported_cpus);
 115                return 0;
 116        }
 117
 118        if (num_irqs == 1) {
 119                int irq = platform_get_irq(pdev, 0);
 120                if (irq && irq_is_percpu_devid(irq))
 121                        return pmu_parse_percpu_irq(pmu, irq);
 122        }
 123
 124        if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(dev->of_node))
 125                dev_warn(dev, "no interrupt-affinity property, guessing.\n");
 126
 127        for (i = 0; i < num_irqs; i++) {
 128                int cpu, irq;
 129
 130                irq = platform_get_irq(pdev, i);
 131                if (WARN_ON(irq <= 0))
 132                        continue;
 133
 134                if (irq_is_percpu_devid(irq)) {
 135                        dev_warn(dev, "multiple PPIs or mismatched SPI/PPI detected\n");
 136                        return -EINVAL;
 137                }
 138
 139                cpu = pmu_parse_irq_affinity(dev, i);
 140                if (cpu < 0)
 141                        return cpu;
 142                if (cpu >= nr_cpu_ids)
 143                        continue;
 144
 145                if (per_cpu(hw_events->irq, cpu)) {
 146                        dev_warn(dev, "multiple PMU IRQs for the same CPU detected\n");
 147                        return -EINVAL;
 148                }
 149
 150                per_cpu(hw_events->irq, cpu) = irq;
 151                cpumask_set_cpu(cpu, &pmu->supported_cpus);
 152        }
 153
 154        return 0;
 155}
 156
 157static int armpmu_request_irqs(struct arm_pmu *armpmu)
 158{
 159        struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
 160        int cpu, err = 0;
 161
 162        for_each_cpu(cpu, &armpmu->supported_cpus) {
 163                int irq = per_cpu(hw_events->irq, cpu);
 164                if (!irq)
 165                        continue;
 166
 167                err = armpmu_request_irq(irq, cpu);
 168                if (err)
 169                        break;
 170        }
 171
 172        return err;
 173}
 174
 175static void armpmu_free_irqs(struct arm_pmu *armpmu)
 176{
 177        int cpu;
 178        struct pmu_hw_events __percpu *hw_events = armpmu->hw_events;
 179
 180        for_each_cpu(cpu, &armpmu->supported_cpus) {
 181                int irq = per_cpu(hw_events->irq, cpu);
 182
 183                armpmu_free_irq(irq, cpu);
 184        }
 185}
 186
 187int arm_pmu_device_probe(struct platform_device *pdev,
 188                         const struct of_device_id *of_table,
 189                         const struct pmu_probe_info *probe_table)
 190{
 191        armpmu_init_fn init_fn;
 192        struct device *dev = &pdev->dev;
 193        struct arm_pmu *pmu;
 194        int ret = -ENODEV;
 195
 196        pmu = armpmu_alloc();
 197        if (!pmu)
 198                return -ENOMEM;
 199
 200        pmu->plat_device = pdev;
 201
 202        ret = pmu_parse_irqs(pmu);
 203        if (ret)
 204                goto out_free;
 205
 206        init_fn = of_device_get_match_data(dev);
 207        if (init_fn) {
 208                pmu->secure_access = of_property_read_bool(dev->of_node,
 209                                                           "secure-reg-access");
 210
 211                /* arm64 systems boot only as non-secure */
 212                if (IS_ENABLED(CONFIG_ARM64) && pmu->secure_access) {
 213                        dev_warn(dev, "ignoring \"secure-reg-access\" property for arm64\n");
 214                        pmu->secure_access = false;
 215                }
 216
 217                ret = init_fn(pmu);
 218        } else if (probe_table) {
 219                cpumask_setall(&pmu->supported_cpus);
 220                ret = probe_current_pmu(pmu, probe_table);
 221        }
 222
 223        if (ret) {
 224                dev_err(dev, "failed to probe PMU!\n");
 225                goto out_free;
 226        }
 227
 228        ret = armpmu_request_irqs(pmu);
 229        if (ret)
 230                goto out_free_irqs;
 231
 232        ret = armpmu_register(pmu);
 233        if (ret) {
 234                dev_err(dev, "failed to register PMU devices!\n");
 235                goto out_free_irqs;
 236        }
 237
 238        return 0;
 239
 240out_free_irqs:
 241        armpmu_free_irqs(pmu);
 242out_free:
 243        armpmu_free(pmu);
 244        return ret;
 245}
 246