[RFC PATCH 1/2] ARM: pmu: register IRQs at runtime
Will Deacon
will.deacon at arm.com
Wed Mar 10 09:58:18 EST 2010
The current PMU infrastructure for ARM requires that the IRQs for the PMU
device are fixed at compile time and are selected based on the ARCH_ or MACH_
flags. This has the disadvantage of tying the Kernel down to a particular board
as far as profiling is concerned.
This patch replaces the compile-time IRQ registration with a runtime mechanism
which allows the IRQs to be passed in from the board initialisation files using
the pmu_device_register() function.
A further advantage of this change is that there is scope for registering
different types of performance counters in the future by adding a new field
to the arm_pmu_type enumeration.
Signed-off-by: Will Deacon <will.deacon at arm.com>
---
arch/arm/include/asm/pmu.h | 36 +++++++++++----
arch/arm/kernel/perf_event.c | 6 +-
arch/arm/kernel/pmu.c | 103 +++++++++++++++++++++++-------------------
3 files changed, 86 insertions(+), 59 deletions(-)
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index 2829b9f..2ded693 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -12,22 +12,38 @@
#ifndef __ARM_PMU_H__
#define __ARM_PMU_H__
+enum arm_pmu_type {
+ ARM_PMU_DEVICE_CPU = 0,
+ ARM_NUM_PMU_DEVICES,
+};
+
#ifdef CONFIG_CPU_HAS_PMU
struct pmu_irqs {
- const int *irqs;
- int num_irqs;
+ enum arm_pmu_type device_type;
+ const int *irqs;
+ int num_irqs;
};
/**
+ * pmu_device_register() - register a new counter source with the kernel
+ *
+ * Register a new PMU device and its corresponding IRQs. Only one instance
+ * of each device type may be registered at once, but multiple IRQs can be
+ * specified in the corresponding struct pmu_irqs.
+ */
+void
+pmu_device_register(struct pmu_irqs *device);
+
+/**
* reserve_pmu() - reserve the hardware performance counters
*
* Reserve the hardware performance counters in the system for exclusive use.
* The 'struct pmu_irqs' for the system is returned on success, ERR_PTR()
* encoded error on failure.
*/
-extern const struct pmu_irqs *
-reserve_pmu(void);
+extern struct pmu_irqs *
+reserve_pmu(enum arm_pmu_type device);
/**
* release_pmu() - Relinquish control of the performance counters
@@ -38,7 +54,7 @@ reserve_pmu(void);
* a cookie.
*/
extern int
-release_pmu(const struct pmu_irqs *irqs);
+release_pmu(struct pmu_irqs *irqs);
/**
* init_pmu() - Initialise the PMU.
@@ -48,24 +64,24 @@ release_pmu(const struct pmu_irqs *irqs);
* the actual hardware initialisation.
*/
extern int
-init_pmu(void);
+init_pmu(enum arm_pmu_type device);
#else /* CONFIG_CPU_HAS_PMU */
-static inline const struct pmu_irqs *
-reserve_pmu(void)
+static inline struct pmu_irqs *
+reserve_pmu(enum arm_pmu_type device)
{
return ERR_PTR(-ENODEV);
}
static inline int
-release_pmu(const struct pmu_irqs *irqs)
+release_pmu(struct pmu_irqs *irqs)
{
return -ENODEV;
}
static inline int
-init_pmu(void)
+init_pmu(enum arm_pmu_type device)
{
return -ENODEV;
}
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index c45a155..752ada2 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -26,7 +26,7 @@
#include <asm/pmu.h>
#include <asm/stacktrace.h>
-static const struct pmu_irqs *pmu_irqs;
+static struct pmu_irqs *pmu_irqs;
/*
* Hardware lock to serialize accesses to PMU registers. Needed for the
@@ -317,13 +317,13 @@ armpmu_reserve_hardware(void)
int i;
int err;
- pmu_irqs = reserve_pmu();
+ pmu_irqs = reserve_pmu(ARM_PMU_DEVICE_CPU);
if (IS_ERR(pmu_irqs)) {
pr_warning("unable to reserve pmu\n");
return PTR_ERR(pmu_irqs);
}
- init_pmu();
+ init_pmu(ARM_PMU_DEVICE_CPU);
if (pmu_irqs->num_irqs < 1) {
pr_err("no irqs for PMUs defined\n");
diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c
index a124312..e58412f 100644
--- a/arch/arm/kernel/pmu.c
+++ b/arch/arm/kernel/pmu.c
@@ -2,6 +2,7 @@
* linux/arch/arm/kernel/pmu.c
*
* Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
+ * Copyright (C) 2010 ARM Ltd, Will Deacon
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -17,57 +18,48 @@
#include <asm/pmu.h>
-/*
- * Define the IRQs for the system. We could use something like a platform
- * device but that seems fairly heavyweight for this. Also, the performance
- * counters can't be removed or hotplugged.
- *
- * Ordering is important: init_pmu() will use the ordering to set the affinity
- * to the corresponding core. e.g. the first interrupt will go to cpu 0, the
- * second goes to cpu 1 etc.
- */
-static const int irqs[] = {
-#if defined(CONFIG_ARCH_OMAP2)
- 3,
-#elif defined(CONFIG_ARCH_BCMRING)
- IRQ_PMUIRQ,
-#elif defined(CONFIG_MACH_REALVIEW_EB)
- IRQ_EB11MP_PMU_CPU0,
- IRQ_EB11MP_PMU_CPU1,
- IRQ_EB11MP_PMU_CPU2,
- IRQ_EB11MP_PMU_CPU3,
-#elif defined(CONFIG_ARCH_OMAP3)
- INT_34XX_BENCH_MPU_EMUL,
-#elif defined(CONFIG_ARCH_IOP32X)
- IRQ_IOP32X_CORE_PMU,
-#elif defined(CONFIG_ARCH_IOP33X)
- IRQ_IOP33X_CORE_PMU,
-#elif defined(CONFIG_ARCH_PXA)
- IRQ_PMU,
-#endif
-};
-
-static const struct pmu_irqs pmu_irqs = {
- .irqs = irqs,
- .num_irqs = ARRAY_SIZE(irqs),
-};
+static struct pmu_irqs *pmu_irqs[ARM_NUM_PMU_DEVICES];
static volatile long pmu_lock;
-const struct pmu_irqs *
-reserve_pmu(void)
+void pmu_device_register(struct pmu_irqs *device)
{
- return test_and_set_bit_lock(0, &pmu_lock) ? ERR_PTR(-EBUSY) :
- &pmu_irqs;
+ int device_type = device->device_type;
+
+ if (pmu_irqs[device_type])
+ pr_warning("pmu: registering new device of type %d overwrites "
+ "previous definition!", device_type);
+ else
+ pr_info("pmu: registered new PMU device of type %d\n",
+ device_type);
+
+ pmu_irqs[device_type] = device;
+}
+
+struct pmu_irqs *
+reserve_pmu(enum arm_pmu_type device)
+{
+ struct pmu_irqs *irqs;
+
+ if (test_and_set_bit_lock(device, &pmu_lock)) {
+ irqs = ERR_PTR(-EBUSY);
+ } else if (pmu_irqs[device] == NULL) {
+ clear_bit_unlock(device, &pmu_lock);
+ irqs = ERR_PTR(-ENODEV);
+ } else {
+ irqs = pmu_irqs[device];
+ }
+
+ return irqs;
}
EXPORT_SYMBOL_GPL(reserve_pmu);
int
-release_pmu(const struct pmu_irqs *irqs)
+release_pmu(struct pmu_irqs *irqs)
{
- if (WARN_ON(irqs != &pmu_irqs))
+ if (WARN_ON(irqs != pmu_irqs[irqs->device_type]))
return -EINVAL;
- clear_bit_unlock(0, &pmu_lock);
+ clear_bit_unlock(irqs->device_type, &pmu_lock);
return 0;
}
EXPORT_SYMBOL_GPL(release_pmu);
@@ -87,17 +79,36 @@ set_irq_affinity(int irq,
#endif
}
-int
-init_pmu(void)
+static int
+init_cpu_pmu(void)
{
- int i, err = 0;
+ int i, err = 0, num_irqs = pmu_irqs[ARM_PMU_DEVICE_CPU]->num_irqs;
+ const int *irqs = pmu_irqs[ARM_PMU_DEVICE_CPU]->irqs;
- for (i = 0; i < pmu_irqs.num_irqs; ++i) {
- err = set_irq_affinity(pmu_irqs.irqs[i], i);
+ for (i = 0; i < num_irqs; ++i) {
+ err = set_irq_affinity(irqs[i], i);
if (err)
break;
}
return err;
}
+
+int
+init_pmu(enum arm_pmu_type device)
+{
+ int err = 0;
+
+ switch (device) {
+ case ARM_PMU_DEVICE_CPU:
+ err = init_cpu_pmu();
+ break;
+ default:
+ pr_warning("pmu: attempt to initialise unknown device %d\n",
+ device);
+ err = -EINVAL;
+ }
+
+ return err;
+}
EXPORT_SYMBOL_GPL(init_pmu);
--
1.6.3.3
More information about the linux-arm-kernel
mailing list