summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/pmu.txt12
-rw-r--r--arch/arm/kernel/perf_event.c65
2 files changed, 53 insertions, 24 deletions
diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 3b5f5d1088c6..435251fa9ce0 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -26,13 +26,19 @@ Required properties:
Optional properties:
-- interrupt-affinity : Valid only when using SPIs, specifies a list of phandles
- to CPU nodes corresponding directly to the affinity of
+- interrupt-affinity : When using SPIs, specifies a list of phandles to CPU
+ nodes corresponding directly to the affinity of
the SPIs listed in the interrupts property.
- This property should be present when there is more than
+ When using a PPI, specifies a list of phandles to CPU
+ nodes corresponding to the set of CPUs which have
+ a PMU of this type signalling the PPI listed in the
+ interrupts property.
+
+ This property should be present when there is more than
a single SPI.
+
- qcom,no-pc-write : Indicates that this PMU doesn't support the 0xc and 0xd
events.
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 7d5379c1c443..5a8f17bfcc60 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -790,32 +790,39 @@ static int probe_current_pmu(struct arm_pmu *pmu,
static int of_pmu_irq_cfg(struct arm_pmu *pmu)
{
- int i, irq, *irqs;
+ int *irqs, i = 0;
+ bool using_spi = false;
struct platform_device *pdev = pmu->plat_device;
- /* Don't bother with PPIs; they're already affine */
- irq = platform_get_irq(pdev, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
- cpumask_setall(&pmu->supported_cpus);
- return 0;
- }
-
irqs = kcalloc(pdev->num_resources, sizeof(*irqs), GFP_KERNEL);
if (!irqs)
return -ENOMEM;
- for (i = 0; i < pdev->num_resources; ++i) {
+ do {
struct device_node *dn;
- int cpu;
+ int cpu, irq;
- dn = of_parse_phandle(pdev->dev.of_node, "interrupt-affinity",
- i);
- if (!dn) {
- pr_warn("Failed to parse %s/interrupt-affinity[%d]\n",
- of_node_full_name(pdev->dev.of_node), i);
+ /* See if we have an affinity entry */
+ dn = of_parse_phandle(pdev->dev.of_node, "interrupt-affinity", i);
+ if (!dn)
break;
+
+ /* Check the IRQ type and prohibit a mix of PPIs and SPIs */
+ irq = platform_get_irq(pdev, i);
+ if (irq >= 0) {
+ bool spi = !irq_is_percpu(irq);
+
+ if (i > 0 && spi != using_spi) {
+ pr_err("PPI/SPI IRQ type mismatch for %s!\n",
+ dn->name);
+ kfree(irqs);
+ return -EINVAL;
+ }
+
+ using_spi = spi;
}
+ /* Now look up the logical CPU number */
for_each_possible_cpu(cpu)
if (arch_find_n_match_cpu_physical_id(dn, cpu, NULL))
break;
@@ -824,20 +831,36 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
pr_warn("Failed to find logical CPU for %s\n",
dn->name);
of_node_put(dn);
+ cpumask_setall(&pmu->supported_cpus);
break;
}
of_node_put(dn);
- irqs[i] = cpu;
+ /* For SPIs, we need to track the affinity per IRQ */
+ if (using_spi) {
+ if (i >= pdev->num_resources) {
+ of_node_put(dn);
+ break;
+ }
+
+ irqs[i] = cpu;
+ }
+
+ /* Keep track of the CPUs containing this PMU type */
cpumask_set_cpu(cpu, &pmu->supported_cpus);
- }
+ of_node_put(dn);
+ i++;
+ } while (1);
- if (i == pdev->num_resources) {
+ /* If we didn't manage to parse anything, claim to support all CPUs */
+ if (cpumask_weight(&pmu->supported_cpus) == 0)
+ cpumask_setall(&pmu->supported_cpus);
+
+ /* If we matched up the IRQ affinities, use them to route the SPIs */
+ if (using_spi && i == pdev->num_resources)
pmu->irq_affinity = irqs;
- } else {
+ else
kfree(irqs);
- cpumask_setall(&pmu->supported_cpus);
- }
return 0;
}