linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ 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; 8+ 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] 8+ messages in thread

end of thread, other threads:[~2016-03-16 21:39 UTC | newest]

Thread overview: 8+ 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).