* [RFC 0/4 v3] arm64/perf: Add ACPI support
@ 2016-03-13 23:23 Jeremy Linton
2016-03-13 23:23 ` [PATCH 1/4] arm: pmu: Fix non-devicetree probing Jeremy Linton
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Jeremy Linton @ 2016-03-13 23:23 UTC (permalink / raw)
To: linux-arm-kernel
This patch set expands upon the patches published by Mark Salter to clean up
a few of the previous review comments, as well as add support for A72's, and
big/little configurations.
I've been testing this patch in concert with the ACPI/PCIe and ACPI/GICv3
patches. It works well on seattle/juno, but there appears to be a problem with
at least one platform using PPIs (which is questionable as ACPI defines the pmu
interrupt as a GSI). I said a couple weeks ago I would post it, so here it is.
Jeremy Linton (1):
arm64: pmu: add A72 cpu type, support multiple PMU types
Mark Salter (3):
arm: pmu: Fix non-devicetree probing
arm64: pmu: add fallback probe table
arm64: pmu: Add support for probing with ACPI
arch/arm64/include/asm/cputype.h | 1 +
arch/arm64/kernel/perf_event.c | 11 +-
arch/arm64/kernel/smp.c | 5 +
drivers/perf/Kconfig | 4 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu.c | 54 ++++++++--
drivers/perf/arm_pmu_acpi.c | 212 +++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 10 ++
8 files changed, 287 insertions(+), 11 deletions(-)
create mode 100644 drivers/perf/arm_pmu_acpi.c
--
2.4.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/4] arm: pmu: Fix non-devicetree probing
2016-03-13 23:23 [RFC 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
@ 2016-03-13 23:23 ` Jeremy Linton
2016-03-13 23:23 ` [PATCH 2/4] arm64: pmu: add fallback probe table Jeremy Linton
` (2 subsequent siblings)
3 siblings, 0 replies; 9+ messages in thread
From: Jeremy Linton @ 2016-03-13 23:23 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Salter <msalter@redhat.com>
There is a problem in the non-devicetree PMU probing where some
probe functions may get the number of supported events through
smp_call_function_any() using the arm_pmu supported_cpus mask.
But at the time the probe function is called, the supported_cpus
mask is empty so the call fails. This patch makes sure the mask
is set before calling the init function rather than after.
Signed-off-by: Mark Salter <msalter@redhat.com>
---
drivers/perf/arm_pmu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 11bacc7..0f33c96 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -997,8 +997,8 @@ int arm_pmu_device_probe(struct platform_device *pdev,
if (!ret)
ret = init_fn(pmu);
} else {
- ret = probe_current_pmu(pmu, probe_table);
cpumask_setall(&pmu->supported_cpus);
+ ret = probe_current_pmu(pmu, probe_table);
}
if (ret) {
--
2.4.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/4] arm64: pmu: add fallback probe table
2016-03-13 23:23 [RFC 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
2016-03-13 23:23 ` [PATCH 1/4] arm: pmu: Fix non-devicetree probing Jeremy Linton
@ 2016-03-13 23:23 ` Jeremy Linton
2016-03-16 21:39 ` nleeder at codeaurora.org
2016-03-13 23:23 ` [PATCH 3/4] arm64: pmu: Add support for probing with ACPI Jeremy Linton
2016-03-13 23:23 ` [PATCH 4/4] arm64: pmu: add A72 cpu type, support multiple PMU types Jeremy Linton
3 siblings, 1 reply; 9+ messages in thread
From: Jeremy Linton @ 2016-03-13 23:23 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Salter <msalter@redhat.com>
In preparation for ACPI support, add a pmu_probe_info table to
the arm_pmu_device_probe() call. This table gets used when
probing in the absence of a devicetree node for PMU.
Signed-off-by: Mark Salter <msalter@redhat.com>
---
arch/arm64/kernel/perf_event.c | 10 +++++++++-
include/linux/perf/arm_pmu.h | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 767c4f6..03e0957 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -901,9 +901,17 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {
{},
};
+static const struct pmu_probe_info armv8_pmu_probe_table[] = {
+ ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A53, armv8_a53_pmu_init),
+ ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A57, armv8_a57_pmu_init),
+ PMU_PROBE(0, 0, armv8_pmuv3_init), /* if all else fails... */
+ { /* sentinel value */ }
+};
+
static int armv8_pmu_device_probe(struct platform_device *pdev)
{
- return arm_pmu_device_probe(pdev, armv8_pmu_of_device_ids, NULL);
+ return arm_pmu_device_probe(pdev, armv8_pmu_of_device_ids,
+ armv8_pmu_probe_table);
}
static struct platform_driver armv8_pmu_driver = {
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 4196c90..495332f 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -145,6 +145,9 @@ struct pmu_probe_info {
#define XSCALE_PMU_PROBE(_version, _fn) \
PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn)
+#define ARMV8_PMU_PART_PROBE(_part, _fn) \
+ PMU_PROBE((_part) << MIDR_PARTNUM_SHIFT, MIDR_PARTNUM_MASK, _fn)
+
int arm_pmu_device_probe(struct platform_device *pdev,
const struct of_device_id *of_table,
const struct pmu_probe_info *probe_table);
--
2.4.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/4] arm64: pmu: Add support for probing with ACPI
2016-03-13 23:23 [RFC 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
2016-03-13 23:23 ` [PATCH 1/4] arm: pmu: Fix non-devicetree probing Jeremy Linton
2016-03-13 23:23 ` [PATCH 2/4] arm64: pmu: add fallback probe table Jeremy Linton
@ 2016-03-13 23:23 ` Jeremy Linton
2016-03-13 23:28 ` Timur Tabi
2016-03-13 23:23 ` [PATCH 4/4] arm64: pmu: add A72 cpu type, support multiple PMU types Jeremy Linton
3 siblings, 1 reply; 9+ messages in thread
From: Jeremy Linton @ 2016-03-13 23:23 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Salter <msalter@redhat.com>
In the case of ACPI, the PMU IRQ information is contained in the
MADT table. Also, since the PMU does not exist as a device in the
ACPI DSDT table, it is necessary to create a platform device so
that the appropriate driver probing is triggered.
Signed-off-by: Mark Salter <msalter@redhat.com>
---
arch/arm64/kernel/smp.c | 5 ++
drivers/perf/Kconfig | 4 ++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 125 +++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 +++
5 files changed, 142 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index b2d5f4e..c6f2c53 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -37,6 +37,7 @@
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/irq_work.h>
+#include <linux/perf/arm_pmu.h>
#include <asm/alternative.h>
#include <asm/atomic.h>
@@ -502,6 +503,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
return;
}
bootcpu_valid = true;
+ arm_pmu_parse_acpi(0, processor);
return;
}
@@ -522,6 +524,9 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
*/
acpi_set_mailbox_entry(cpu_count, processor);
+ /* get PMU irq info */
+ arm_pmu_parse_acpi(cpu_count, processor);
+
cpu_count++;
}
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 04e2653..818fa3b 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -12,4 +12,8 @@ config ARM_PMU
Say y if you want to use CPU performance monitors on ARM-based
systems.
+config ARM_PMU_ACPI
+ def_bool y
+ depends on ARM_PMU && ACPI
+
endmenu
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index acd2397..fd8090d 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
+obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c
new file mode 100644
index 0000000..722f4ca
--- /dev/null
+++ b/drivers/perf/arm_pmu_acpi.c
@@ -0,0 +1,125 @@
+/*
+ * PMU support
+ *
+ * Copyright (C) 2015 Red Hat Inc.
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/perf/arm_pmu.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+
+#define PMU_PDEV_NAME "armv8-pmu"
+
+struct pmu_irq {
+ int gsi;
+ int trigger;
+};
+
+static struct pmu_irq pmu_irqs[NR_CPUS] __initdata;
+
+void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)
+{
+ pmu_irqs[cpu].gsi = gic->performance_interrupt;
+ if (gic->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE)
+ pmu_irqs[cpu].trigger = ACPI_EDGE_SENSITIVE;
+ else
+ pmu_irqs[cpu].trigger = ACPI_LEVEL_SENSITIVE;
+}
+
+#ifndef CONFIG_SMP
+/*
+ * In !SMP case, we parse for boot CPU IRQ here.
+ */
+static int __init acpi_parse_pmu_irqs(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *gic;
+
+ gic = (struct acpi_madt_generic_interrupt *)header;
+
+ if (cpu_logical_map(0) == (gic->arm_mpidr & MPIDR_HWID_BITMASK))
+ arm_pmu_parse_acpi(0, gic);
+
+ return 0;
+}
+
+static void __init acpi_parse_boot_cpu(void)
+{
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ acpi_parse_pmu_irqs, 0);
+}
+#else
+#define acpi_parse_boot_cpu() do {} while (0)
+#endif
+
+static int __init pmu_acpi_init(void)
+{
+ struct platform_device *pdev;
+ struct pmu_irq *pirq = pmu_irqs;
+ struct resource *res, *r;
+ int err = -ENOMEM;
+ int i, count, irq;
+
+ if (acpi_disabled)
+ return 0;
+
+ acpi_parse_boot_cpu();
+
+ /* Must have irq for boot boot cpu, at least */
+ if (pirq->gsi == 0)
+ return -EINVAL;
+
+ irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq_is_percpu(irq))
+ count = 1;
+ else
+ for (i = 1, count = 1; i < NR_CPUS; i++)
+ if (pmu_irqs[i].gsi)
+ ++count;
+
+ pdev = platform_device_alloc(PMU_PDEV_NAME, -1);
+ if (!pdev)
+ goto err_free_gsi;
+
+ res = kcalloc(count, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ goto err_free_device;
+
+ for (i = 0, r = res; i < count; i++, pirq++, r++) {
+ if (i)
+ irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
+ ACPI_ACTIVE_HIGH);
+ r->start = r->end = irq;
+ r->flags = IORESOURCE_IRQ;
+ if (pirq->trigger == ACPI_EDGE_SENSITIVE)
+ r->flags |= IORESOURCE_IRQ_HIGHEDGE;
+ else
+ r->flags |= IORESOURCE_IRQ_HIGHLEVEL;
+ }
+
+ err = platform_device_add_resources(pdev, res, count);
+ if (!err)
+ err = platform_device_add(pdev);
+ kfree(res);
+ if (!err)
+ return 0;
+
+err_free_device:
+ platform_device_put(pdev);
+
+err_free_gsi:
+ for (i = 0; i < count; i++)
+ acpi_unregister_gsi(pmu_irqs[i].gsi);
+
+ return err;
+}
+arch_initcall(pmu_acpi_init);
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 495332f..d002269 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -154,4 +154,11 @@ int arm_pmu_device_probe(struct platform_device *pdev,
#endif /* CONFIG_ARM_PMU */
+#ifdef CONFIG_ARM_PMU_ACPI
+struct acpi_madt_generic_interrupt;
+void arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic);
+#else
+#define arm_pmu_parse_acpi(a, b) do { } while (0)
+#endif /* CONFIG_ARM_PMU_ACPI */
+
#endif /* __ARM_PMU_H__ */
--
2.4.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/4] arm64: pmu: add A72 cpu type, support multiple PMU types
2016-03-13 23:23 [RFC 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
` (2 preceding siblings ...)
2016-03-13 23:23 ` [PATCH 3/4] arm64: pmu: Add support for probing with ACPI Jeremy Linton
@ 2016-03-13 23:23 ` Jeremy Linton
3 siblings, 0 replies; 9+ messages in thread
From: Jeremy Linton @ 2016-03-13 23:23 UTC (permalink / raw)
To: linux-arm-kernel
ARM big/little machines can have PMU's with differing PMU counters.
ACPI systems should be able to support this as well. Also add support
for A72 PMU counters.
Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
---
arch/arm64/include/asm/cputype.h | 1 +
arch/arm64/kernel/perf_event.c | 1 +
drivers/perf/arm_pmu.c | 54 +++++++--
drivers/perf/arm_pmu_acpi.c | 229 +++++++++++++++++++++++++++------------
4 files changed, 204 insertions(+), 81 deletions(-)
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 87e1985..1e40799 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -74,6 +74,7 @@
#define ARM_CPU_PART_AEM_V8 0xD0F
#define ARM_CPU_PART_FOUNDATION 0xD00
+#define ARM_CPU_PART_CORTEX_A72 0xD08
#define ARM_CPU_PART_CORTEX_A57 0xD07
#define ARM_CPU_PART_CORTEX_A53 0xD03
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 03e0957..b3ea97f 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -904,6 +904,7 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {
static const struct pmu_probe_info armv8_pmu_probe_table[] = {
ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A53, armv8_a53_pmu_init),
ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A57, armv8_a57_pmu_init),
+ ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A72, armv8_a72_pmu_init),
PMU_PROBE(0, 0, armv8_pmuv3_init), /* if all else fails... */
{ /* sentinel value */ }
};
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 0f33c96..87f975f 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -11,6 +11,7 @@
*/
#define pr_fmt(fmt) "hw perfevents: " fmt
+#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/cpumask.h>
#include <linux/cpu_pm.h>
@@ -24,6 +25,7 @@
#include <linux/irq.h>
#include <linux/irqdesc.h>
+#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/irq_regs.h>
@@ -853,25 +855,51 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
}
/*
- * CPU PMU identification and probing.
+ * CPU PMU identification and probing. Its possible to have
+ * multiple CPU types in an ARM machine. Assure that we are
+ * picking the right PMU types based on the CPU in question
*/
-static int probe_current_pmu(struct arm_pmu *pmu,
- const struct pmu_probe_info *info)
+static int probe_plat_pmu(struct arm_pmu *pmu,
+ const struct pmu_probe_info *info,
+ unsigned int pmuid)
{
- int cpu = get_cpu();
- unsigned int cpuid = read_cpuid_id();
int ret = -ENODEV;
+ int cpu;
+ int aff_ctr = 0;
+ struct platform_device *pdev = pmu->plat_device;
+ int irq = platform_get_irq(pdev, 0);
- pr_info("probing PMU on CPU %d\n", cpu);
+ if (irq >= 0 && !irq_is_percpu(irq)) {
+ pmu->irq_affinity = kcalloc(pdev->num_resources, sizeof(int),
+ GFP_KERNEL);
+ if (!pmu->irq_affinity)
+ return -ENOMEM;
+ }
+ for_each_possible_cpu(cpu) {
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, cpu);
+ unsigned int cpuid = cinfo->reg_midr;
+
+ if (cpuid == pmuid) {
+ cpumask_set_cpu(cpu, &pmu->supported_cpus);
+ pr_devel("enable pmu on cpu %d\n", cpu);
+ if (pmu->irq_affinity) {
+ pmu->irq_affinity[aff_ctr] = cpu;
+ aff_ctr++;
+ }
+ }
+ }
+
+ pr_debug("probing PMU %X\n", pmuid);
+ /* find the type of PMU given the CPU */
for (; info->init != NULL; info++) {
- if ((cpuid & info->mask) != info->cpuid)
+ if ((pmuid & info->mask) != info->cpuid)
continue;
+ pr_devel("Found PMU\n");
ret = info->init(pmu);
break;
}
- put_cpu();
return ret;
}
@@ -997,8 +1025,14 @@ int arm_pmu_device_probe(struct platform_device *pdev,
if (!ret)
ret = init_fn(pmu);
} else {
- cpumask_setall(&pmu->supported_cpus);
- ret = probe_current_pmu(pmu, probe_table);
+ if (acpi_disabled) {
+ /* use the boot cpu. */
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, 0);
+ unsigned int cpuid = cinfo->reg_midr;
+
+ ret = probe_plat_pmu(pmu, probe_table, cpuid);
+ } else
+ ret = probe_plat_pmu(pmu, probe_table, pdev->id);
}
if (ret) {
diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c
index 722f4ca..793092c 100644
--- a/drivers/perf/arm_pmu_acpi.c
+++ b/drivers/perf/arm_pmu_acpi.c
@@ -2,6 +2,7 @@
* PMU support
*
* Copyright (C) 2015 Red Hat Inc.
+ * Copyright (C) 2016 ARM Ltd.
* Author: Mark Salter <msalter@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
@@ -9,21 +10,35 @@
*
*/
+#define pr_fmt(fmt) "ACPI-PMU: " fmt
#include <linux/perf/arm_pmu.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
+#include <asm/cpu.h>
+
#define PMU_PDEV_NAME "armv8-pmu"
struct pmu_irq {
- int gsi;
- int trigger;
+ int gsi;
+ int trigger;
+ bool registered;
+};
+
+struct pmu_types {
+ int cpu_type;
+ int cpu_count;
};
static struct pmu_irq pmu_irqs[NR_CPUS] __initdata;
+/*
+ * called from acpi_map_gic_cpu_interface()'s MADT parsing callback during boot
+ * this routine saves off the GSI's and their trigger state for use when we are
+ * ready to build the PMU platform device.
+*/
void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)
{
pmu_irqs[cpu].gsi = gic->performance_interrupt;
@@ -31,95 +46,167 @@ void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)
pmu_irqs[cpu].trigger = ACPI_EDGE_SENSITIVE;
else
pmu_irqs[cpu].trigger = ACPI_LEVEL_SENSITIVE;
+ pr_info("Assign CPU %d girq %d level %d\n", cpu, pmu_irqs[cpu].gsi,
+ pmu_irqs[cpu].trigger);
}
-#ifndef CONFIG_SMP
-/*
- * In !SMP case, we parse for boot CPU IRQ here.
- */
-static int __init acpi_parse_pmu_irqs(struct acpi_subtable_header *header,
- const unsigned long end)
-{
- struct acpi_madt_generic_interrupt *gic;
-
- gic = (struct acpi_madt_generic_interrupt *)header;
-
- if (cpu_logical_map(0) == (gic->arm_mpidr & MPIDR_HWID_BITMASK))
- arm_pmu_parse_acpi(0, gic);
-
- return 0;
-}
-
-static void __init acpi_parse_boot_cpu(void)
+/* count number and type of CPU's in system */
+static void __init arm_pmu_acpi_determine_cpu_types(struct pmu_types *pmus)
{
- count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
- acpi_parse_pmu_irqs, 0);
+ int i, j;
+
+ for_each_possible_cpu(i) {
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, i);
+
+ pr_devel("Present CPU %d is a %X\n", i,
+ MIDR_PARTNUM(cinfo->reg_midr));
+ for (j = 0; j < NR_CPUS; j++) {
+ if (pmus[j].cpu_type == MIDR_PARTNUM(cinfo->reg_midr)) {
+ pmus[j].cpu_count++;
+ break;
+ }
+ if (pmus[j].cpu_count == 0) {
+ pmus[j].cpu_type = MIDR_PARTNUM(cinfo->reg_midr);
+ pmus[j].cpu_count++;
+ break;
+ }
+ }
+ }
}
-#else
-#define acpi_parse_boot_cpu() do {} while (0)
-#endif
-static int __init pmu_acpi_init(void)
+static int __init arm_pmu_acpi_register_pmu(int count, struct resource *res,
+ int last_cpu_id)
{
- struct platform_device *pdev;
- struct pmu_irq *pirq = pmu_irqs;
- struct resource *res, *r;
+ int i;
int err = -ENOMEM;
- int i, count, irq;
+ bool free_gsi = false;
+ struct platform_device *pdev;
- if (acpi_disabled)
- return 0;
+ if (count) {
+ pdev = platform_device_alloc(PMU_PDEV_NAME, last_cpu_id);
+
+ if (pdev) {
+ err = platform_device_add_resources(pdev,
+ res, count);
+ if (!err) {
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_warn("Unable to register PMU device\n");
+ free_gsi = true;
+ }
+ } else {
+ pr_warn("Unable to add resources to device\n");
+ free_gsi = true;
+ platform_device_put(pdev);
+ }
+ } else {
+ pr_warn("Unable to allocate platform device\n");
+ free_gsi = true;
+ }
+ }
- acpi_parse_boot_cpu();
+ /* unmark (and possibly unregister) registered GSIs */
+ for_each_possible_cpu(i) {
+ if (pmu_irqs[i].registered) {
+ if (free_gsi)
+ acpi_unregister_gsi(pmu_irqs[i].gsi);
+ pmu_irqs[i].registered = false;
+ }
+ }
- /* Must have irq for boot boot cpu, at least */
- if (pirq->gsi == 0)
- return -EINVAL;
+ return err;
+}
- irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
- ACPI_ACTIVE_HIGH);
+/*
+ * For the given cpu/pmu type, walk all known GSIs, register them, and add
+ * them to the resource structure. Return the number of GSI's contained
+ * in the res structure, and the id of the last CPU/PMU we added.
+ */
+static int __init arm_pmu_acpi_gsi_res(struct pmu_types *pmus,
+ struct resource *res, int *last_cpu_id)
+{
+ int i, count;
+ int irq;
+
+ pr_info("Setting up %d PMUs for CPU type %X\n", pmus->cpu_count,
+ pmus->cpu_type);
+ /* lets group all the PMU's from similar CPU's together */
+ count = 0;
+ for_each_possible_cpu(i) {
+ struct cpuinfo_arm64 *cinfo = per_cpu_ptr(&cpu_data, i);
+
+ if (pmus->cpu_type == MIDR_PARTNUM(cinfo->reg_midr)) {
+ pr_devel("Setting up CPU %d\n", i);
+ if (pmu_irqs[i].gsi == 0)
+ continue;
+
+ irq = acpi_register_gsi(NULL, pmu_irqs[i].gsi,
+ pmu_irqs[i].trigger,
+ ACPI_ACTIVE_HIGH);
- if (irq_is_percpu(irq))
- count = 1;
- else
- for (i = 1, count = 1; i < NR_CPUS; i++)
- if (pmu_irqs[i].gsi)
- ++count;
+ res[count].start = res[count].end = irq;
+ res[count].flags = IORESOURCE_IRQ;
- pdev = platform_device_alloc(PMU_PDEV_NAME, -1);
- if (!pdev)
- goto err_free_gsi;
+ if (pmu_irqs[i].trigger == ACPI_EDGE_SENSITIVE)
+ res[count].flags |= IORESOURCE_IRQ_HIGHEDGE;
+ else
+ res[count].flags |= IORESOURCE_IRQ_HIGHLEVEL;
- res = kcalloc(count, sizeof(*res), GFP_KERNEL);
- if (!res)
- goto err_free_device;
+ pmu_irqs[i].registered = true;
+ count++;
+ (*last_cpu_id) = cinfo->reg_midr;
- for (i = 0, r = res; i < count; i++, pirq++, r++) {
- if (i)
- irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
- ACPI_ACTIVE_HIGH);
- r->start = r->end = irq;
- r->flags = IORESOURCE_IRQ;
- if (pirq->trigger == ACPI_EDGE_SENSITIVE)
- r->flags |= IORESOURCE_IRQ_HIGHEDGE;
- else
- r->flags |= IORESOURCE_IRQ_HIGHLEVEL;
+ if (irq_is_percpu(irq))
+ pr_debug("PPI detected\n");
+ }
}
+ return count;
+}
- err = platform_device_add_resources(pdev, res, count);
- if (!err)
- err = platform_device_add(pdev);
- kfree(res);
- if (!err)
- return 0;
+static int __init pmu_acpi_init(void)
+{
+ struct resource *res;
+ int err = -ENOMEM;
+ int count;
+ int j, last_cpu_id;
+ struct pmu_types *pmus;
-err_free_device:
- platform_device_put(pdev);
+ pr_debug("Prepare registration\n");
+ if (acpi_disabled)
+ return 0;
-err_free_gsi:
- for (i = 0; i < count; i++)
- acpi_unregister_gsi(pmu_irqs[i].gsi);
+ pmus = kcalloc(NR_CPUS, sizeof(struct pmu_types), GFP_KERNEL);
+
+ if (pmus) {
+ arm_pmu_acpi_determine_cpu_types(pmus);
+
+ for (j = 0; pmus[j].cpu_count; j++) {
+ pr_devel("CPU type %d, count %d\n", pmus[j].cpu_type,
+ pmus[j].cpu_count);
+ res = kcalloc(pmus[j].cpu_count,
+ sizeof(struct resource), GFP_KERNEL);
+
+ /* for a given PMU type collect all the GSIs. */
+ if (res) {
+ count = arm_pmu_acpi_gsi_res(&pmus[j], res,
+ &last_cpu_id);
+ /*
+ * register this set of interrupts
+ * with a new PMU device
+ */
+ err = arm_pmu_acpi_register_pmu(count,
+ res,
+ last_cpu_id);
+ kfree(res);
+ } else
+ pr_warn("PMU unable to allocate interrupt resource space\n");
+ }
+
+ kfree(pmus);
+ } else
+ pr_warn("PMU: Unable to allocate pmu count structures\n");
return err;
}
+
arch_initcall(pmu_acpi_init);
--
2.4.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/4] arm64: pmu: Add support for probing with ACPI
2016-03-13 23:23 ` [PATCH 3/4] arm64: pmu: Add support for probing with ACPI Jeremy Linton
@ 2016-03-13 23:28 ` Timur Tabi
2016-03-14 1:44 ` Jeremy Linton
0 siblings, 1 reply; 9+ messages in thread
From: Timur Tabi @ 2016-03-13 23:28 UTC (permalink / raw)
To: linux-arm-kernel
Jeremy Linton wrote:
> + int i, count, irq;
> +
> + if (acpi_disabled)
> + return 0;
> +
> + acpi_parse_boot_cpu();
> +
> + /* Must have irq for boot boot cpu, at least */
> + if (pirq->gsi == 0)
> + return -EINVAL;
> +
> + irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
> + ACPI_ACTIVE_HIGH);
> +
> + if (irq_is_percpu(irq))
> + count = 1;
> + else
> + for (i = 1, count = 1; i < NR_CPUS; i++)
> + if (pmu_irqs[i].gsi)
> + ++count;
You could more simply with:
int count = 1
...
if (!irq_is_percpu(irq))
for (i = 1; i < NR_CPUS; i++)
if (pmu_irqs[i].gsi)
++count;
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the
Code Aurora Forum, hosted by The Linux Foundation.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/4] arm64: pmu: Add support for probing with ACPI
2016-03-13 23:28 ` Timur Tabi
@ 2016-03-14 1:44 ` Jeremy Linton
0 siblings, 0 replies; 9+ messages in thread
From: Jeremy Linton @ 2016-03-14 1:44 UTC (permalink / raw)
To: linux-arm-kernel
On 03/13/2016 06:28 PM, Timur Tabi wrote:
> Jeremy Linton wrote:
>> + int i, count, irq;
>> +
>> + if (acpi_disabled)
>> + return 0;
>> +
>> + acpi_parse_boot_cpu();
>> +
>> + /* Must have irq for boot boot cpu, at least */
>> + if (pirq->gsi == 0)
>> + return -EINVAL;
>> +
>> + irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
>> + ACPI_ACTIVE_HIGH);
>> +
>> + if (irq_is_percpu(irq))
>> + count = 1;
>> + else
>> + for (i = 1, count = 1; i < NR_CPUS; i++)
>> + if (pmu_irqs[i].gsi)
>> + ++count;
>
> You could more simply with:
>
> int count = 1
> ...
> if (!irq_is_percpu(irq))
> for (i = 1; i < NR_CPUS; i++)
> if (pmu_irqs[i].gsi)
> ++count;
I think you will notice that the following patch rewrites a lot of this
function.
I kept the original patches from Mark to identify/segment his
contribution because they run/bisect by themselves. They should probably
be squashed together for review purposes.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/4] arm64: pmu: add fallback probe table
2016-03-13 23:23 ` [PATCH 2/4] arm64: pmu: add fallback probe table Jeremy Linton
@ 2016-03-16 21:39 ` nleeder at codeaurora.org
0 siblings, 0 replies; 9+ messages in thread
From: nleeder at codeaurora.org @ 2016-03-16 21:39 UTC (permalink / raw)
To: linux-arm-kernel
On 2016-03-13 19:23, Jeremy Linton wrote:
> From: Mark Salter <msalter@redhat.com>
>
> In preparation for ACPI support, add a pmu_probe_info table to
> the arm_pmu_device_probe() call. This table gets used when
> probing in the absence of a devicetree node for PMU.
>
> Signed-off-by: Mark Salter <msalter@redhat.com>
> ---
> arch/arm64/kernel/perf_event.c | 10 +++++++++-
> include/linux/perf/arm_pmu.h | 3 +++
> 2 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/perf_event.c
> b/arch/arm64/kernel/perf_event.c
> index 767c4f6..03e0957 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -901,9 +901,17 @@ static const struct of_device_id
> armv8_pmu_of_device_ids[] = {
> {},
> };
>
> +static const struct pmu_probe_info armv8_pmu_probe_table[] = {
> + ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A53, armv8_a53_pmu_init),
> + ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A57, armv8_a57_pmu_init),
> + PMU_PROBE(0, 0, armv8_pmuv3_init), /* if all else fails... */
> + { /* sentinel value */ }
> +};
> +
> static int armv8_pmu_device_probe(struct platform_device *pdev)
> {
> - return arm_pmu_device_probe(pdev, armv8_pmu_of_device_ids, NULL);
> + return arm_pmu_device_probe(pdev, armv8_pmu_of_device_ids,
> + armv8_pmu_probe_table);
> }
>
> static struct platform_driver armv8_pmu_driver = {
> diff --git a/include/linux/perf/arm_pmu.h
> b/include/linux/perf/arm_pmu.h
> index 4196c90..495332f 100644
> --- a/include/linux/perf/arm_pmu.h
> +++ b/include/linux/perf/arm_pmu.h
> @@ -145,6 +145,9 @@ struct pmu_probe_info {
> #define XSCALE_PMU_PROBE(_version, _fn) \
> PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK,
> _fn)
>
> +#define ARMV8_PMU_PART_PROBE(_part, _fn) \
> + PMU_PROBE((_part) << MIDR_PARTNUM_SHIFT, MIDR_PARTNUM_MASK, _fn)
> +
> int arm_pmu_device_probe(struct platform_device *pdev,
> const struct of_device_id *of_table,
> const struct pmu_probe_info *probe_table);
Hi Jeremy,
Thanks for pulling these patches together.
Just one comment: MIDR part numbers are not guaranteed unique across
implementors, so I'd suggest that ARMV8_PMU_PART_PROBE include the
MIDR_IMPLEMENTOR in the cpuid and mask.
Otherwise everything looks good - I successfully used this patchset as a
base for getting my pmu driver running with ACPI.
-- Neil
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum, a Linux Foundation Collaborative Project
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/4] arm64: pmu: Add support for probing with ACPI
2016-04-08 21:57 [PATCH 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
@ 2016-04-08 21:57 ` Jeremy Linton
0 siblings, 0 replies; 9+ messages in thread
From: Jeremy Linton @ 2016-04-08 21:57 UTC (permalink / raw)
To: linux-kernel
Cc: will.deacon, mark.rutland, peterz, mingo, catalin.marinas,
msalter, timur, nleeder, agustinv, sfr, Jeremy Linton
From: Mark Salter <msalter@redhat.com>
In the case of ACPI, the PMU IRQ information is contained in the
MADT table. Also, since the PMU does not exist as a device in the
ACPI DSDT table, it is necessary to create a platform device so
that the appropriate driver probing is triggered.
Signed-off-by: Mark Salter <msalter@redhat.com>
Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
---
arch/arm64/kernel/smp.c | 5 ++
drivers/perf/Kconfig | 4 ++
drivers/perf/Makefile | 1 +
drivers/perf/arm_pmu_acpi.c | 125 +++++++++++++++++++++++++++++++++++++++++++
include/linux/perf/arm_pmu.h | 7 +++
5 files changed, 142 insertions(+)
create mode 100644 drivers/perf/arm_pmu_acpi.c
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index b2d5f4e..c6f2c53 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -37,6 +37,7 @@
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/irq_work.h>
+#include <linux/perf/arm_pmu.h>
#include <asm/alternative.h>
#include <asm/atomic.h>
@@ -502,6 +503,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
return;
}
bootcpu_valid = true;
+ arm_pmu_parse_acpi(0, processor);
return;
}
@@ -522,6 +524,9 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
*/
acpi_set_mailbox_entry(cpu_count, processor);
+ /* get PMU irq info */
+ arm_pmu_parse_acpi(cpu_count, processor);
+
cpu_count++;
}
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 04e2653..818fa3b 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -12,4 +12,8 @@ config ARM_PMU
Say y if you want to use CPU performance monitors on ARM-based
systems.
+config ARM_PMU_ACPI
+ def_bool y
+ depends on ARM_PMU && ACPI
+
endmenu
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index acd2397..fd8090d 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
+obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
diff --git a/drivers/perf/arm_pmu_acpi.c b/drivers/perf/arm_pmu_acpi.c
new file mode 100644
index 0000000..722f4ca
--- /dev/null
+++ b/drivers/perf/arm_pmu_acpi.c
@@ -0,0 +1,125 @@
+/*
+ * PMU support
+ *
+ * Copyright (C) 2015 Red Hat Inc.
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/perf/arm_pmu.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+
+#define PMU_PDEV_NAME "armv8-pmu"
+
+struct pmu_irq {
+ int gsi;
+ int trigger;
+};
+
+static struct pmu_irq pmu_irqs[NR_CPUS] __initdata;
+
+void __init arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic)
+{
+ pmu_irqs[cpu].gsi = gic->performance_interrupt;
+ if (gic->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE)
+ pmu_irqs[cpu].trigger = ACPI_EDGE_SENSITIVE;
+ else
+ pmu_irqs[cpu].trigger = ACPI_LEVEL_SENSITIVE;
+}
+
+#ifndef CONFIG_SMP
+/*
+ * In !SMP case, we parse for boot CPU IRQ here.
+ */
+static int __init acpi_parse_pmu_irqs(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *gic;
+
+ gic = (struct acpi_madt_generic_interrupt *)header;
+
+ if (cpu_logical_map(0) == (gic->arm_mpidr & MPIDR_HWID_BITMASK))
+ arm_pmu_parse_acpi(0, gic);
+
+ return 0;
+}
+
+static void __init acpi_parse_boot_cpu(void)
+{
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ acpi_parse_pmu_irqs, 0);
+}
+#else
+#define acpi_parse_boot_cpu() do {} while (0)
+#endif
+
+static int __init pmu_acpi_init(void)
+{
+ struct platform_device *pdev;
+ struct pmu_irq *pirq = pmu_irqs;
+ struct resource *res, *r;
+ int err = -ENOMEM;
+ int i, count, irq;
+
+ if (acpi_disabled)
+ return 0;
+
+ acpi_parse_boot_cpu();
+
+ /* Must have irq for boot boot cpu, at least */
+ if (pirq->gsi == 0)
+ return -EINVAL;
+
+ irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq_is_percpu(irq))
+ count = 1;
+ else
+ for (i = 1, count = 1; i < NR_CPUS; i++)
+ if (pmu_irqs[i].gsi)
+ ++count;
+
+ pdev = platform_device_alloc(PMU_PDEV_NAME, -1);
+ if (!pdev)
+ goto err_free_gsi;
+
+ res = kcalloc(count, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ goto err_free_device;
+
+ for (i = 0, r = res; i < count; i++, pirq++, r++) {
+ if (i)
+ irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger,
+ ACPI_ACTIVE_HIGH);
+ r->start = r->end = irq;
+ r->flags = IORESOURCE_IRQ;
+ if (pirq->trigger == ACPI_EDGE_SENSITIVE)
+ r->flags |= IORESOURCE_IRQ_HIGHEDGE;
+ else
+ r->flags |= IORESOURCE_IRQ_HIGHLEVEL;
+ }
+
+ err = platform_device_add_resources(pdev, res, count);
+ if (!err)
+ err = platform_device_add(pdev);
+ kfree(res);
+ if (!err)
+ return 0;
+
+err_free_device:
+ platform_device_put(pdev);
+
+err_free_gsi:
+ for (i = 0; i < count; i++)
+ acpi_unregister_gsi(pmu_irqs[i].gsi);
+
+ return err;
+}
+arch_initcall(pmu_acpi_init);
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 495332f..d002269 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -154,4 +154,11 @@ int arm_pmu_device_probe(struct platform_device *pdev,
#endif /* CONFIG_ARM_PMU */
+#ifdef CONFIG_ARM_PMU_ACPI
+struct acpi_madt_generic_interrupt;
+void arm_pmu_parse_acpi(int cpu, struct acpi_madt_generic_interrupt *gic);
+#else
+#define arm_pmu_parse_acpi(a, b) do { } while (0)
+#endif /* CONFIG_ARM_PMU_ACPI */
+
#endif /* __ARM_PMU_H__ */
--
2.4.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2016-04-08 21:57 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-03-13 23:23 [RFC 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
2016-03-13 23:23 ` [PATCH 1/4] arm: pmu: Fix non-devicetree probing Jeremy Linton
2016-03-13 23:23 ` [PATCH 2/4] arm64: pmu: add fallback probe table Jeremy Linton
2016-03-16 21:39 ` nleeder at codeaurora.org
2016-03-13 23:23 ` [PATCH 3/4] arm64: pmu: Add support for probing with ACPI Jeremy Linton
2016-03-13 23:28 ` Timur Tabi
2016-03-14 1:44 ` Jeremy Linton
2016-03-13 23:23 ` [PATCH 4/4] arm64: pmu: add A72 cpu type, support multiple PMU types Jeremy Linton
-- strict thread matches above, loose matches on Subject: below --
2016-04-08 21:57 [PATCH 0/4 v3] arm64/perf: Add ACPI support Jeremy Linton
2016-04-08 21:57 ` [PATCH 3/4] arm64: pmu: Add support for probing with ACPI Jeremy Linton
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.