[PATCH 1/5] arm: provide a mechanism to reserve performance counters
Jamie Iles
jamie.iles at picochip.com
Mon Dec 14 09:04:37 EST 2009
To add support for perf events and to allow the hardware
counters to be shared with oprofile, we need a way to reserve
access to the pmu (performance monitor unit).
Cc: Will Deacon <will.deacon at arm.com>
Signed-off-by: Jamie Iles <jamie.iles at picochip.com>
---
arch/arm/include/asm/pmu.h | 76 +++++++++++++++++++++++++++++++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/pmu.c | 108 ++++++++++++++++++++++++++++++++++++++++++++
arch/arm/mm/Kconfig | 6 +++
4 files changed, 191 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/asm/pmu.h
create mode 100644 arch/arm/kernel/pmu.c
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
new file mode 100644
index 0000000..d66a7cd
--- /dev/null
+++ b/arch/arm/include/asm/pmu.h
@@ -0,0 +1,76 @@
+/*
+ * linux/arch/arm/include/asm/pmu.h
+ *
+ * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ARM_PMU_H__
+#define __ARM_PMU_H__
+
+#ifdef CONFIG_CPU_HAS_PMU
+
+#define MAX_PMU_IRQS 8
+
+struct pmu_irqs {
+ int irqs[MAX_PMU_IRQS];
+ unsigned num_irqs;
+};
+
+/**
+ * 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);
+
+/**
+ * release_pmu() - Relinquish control of the performance counters
+ *
+ * Release the performance counters and allow someone else to use them.
+ * Callers must have disabled the counters and released IRQs before calling
+ * this. The 'struct pmu_irqs' returned from reserve_pmu() must be passed as
+ * a cookie.
+ */
+extern void
+release_pmu(const struct pmu_irqs *irqs);
+
+/**
+ * init_pmu() - Initialise the PMU.
+ *
+ * Initialise the system ready for PMU enabling. This should typically set the
+ * IRQ affinity and nothing else. The users (oprofile/perf events etc) will do
+ * the actual hardware initialisation.
+ */
+extern int
+init_pmu(void);
+
+#else /* CONFIG_CPU_HAS_PMU */
+
+static inline const struct pmu_irqs *
+reserve_pmu(void)
+{
+ ERR_PTR(-ENODEV);
+}
+
+static inline void
+release_pmu(const struct pmu_irqs *irqs)
+{
+}
+
+static inline int
+init_pmu(void)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_CPU_HAS_PMU */
+
+#endif /* __ARM_PMU_H__ */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index e7ccf7e..286a276 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o
+obj-$(CONFIG_CPU_HAS_PMU) += pmu.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
ifneq ($(CONFIG_ARCH_EBSA110),y)
diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c
new file mode 100644
index 0000000..881e526
--- /dev/null
+++ b/arch/arm/kernel/pmu.c
@@ -0,0 +1,108 @@
+/*
+ * linux/arch/arm/kernel/pmu.c
+ *
+ * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+
+#include <asm/pmu.h>
+#include <asm/irq.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 struct pmu_irqs pmu_irqs = {
+#ifdef CONFIG_ARCH_PC3XX
+ .irqs = { IRQ_NPMUIRQ },
+ .num_irqs = 1,
+#elif defined(CONFIG_ARCH_OMAP2)
+ .irqs = { 3 },
+ .num_irqs = 1,
+#elif defined(CONFIG_ARCH_BCMRING)
+ .irqs = { IRQ_PMUIRQ },
+ .num_irqs = 1,
+#elif defined(CONFIG_MACH_REALVIEW_EB)
+ .irqs = {
+ [0] = IRQ_EB11MP_PMU_CPU0,
+ [1] = IRQ_EB11MP_PMU_CPU1,
+ [2] = IRQ_EB11MP_PMU_CPU2,
+ [3] = IRQ_EB11MP_PMU_CPU3
+ },
+ .num_irqs = 4,
+#elif defined(CONFIG_ARCH_OMAP3)
+ .irqs = { INT_34XX_BENCH_MPU_EMUL },
+ .num_irqs = 1,
+#elif defined(CONFIG_ARCH_IOP32X)
+ .irqs = { IRQ_IOP32X_CORE_PMU },
+ .num_irqs = 1,
+#elif defined(CONFIG_ARCH_IOP33X)
+ .irqs = { IRQ_IOP33X_CORE_PMU },
+ .num_irqs = 1,
+#elif defined(CONFIG_ARCH_PXA)
+ .irqs = { IRQ_PMU },
+ .num_irqs = 1,
+#endif
+};
+
+static DECLARE_MUTEX(pmu_mutex);
+
+const struct pmu_irqs *
+reserve_pmu(void)
+{
+ int ret = down_trylock(&pmu_mutex) ? -EBUSY : 0;
+
+ return ret ? ERR_PTR(ret) : &pmu_irqs;
+}
+EXPORT_SYMBOL_GPL(reserve_pmu);
+
+void
+release_pmu(const struct pmu_irqs *irqs)
+{
+ WARN_ON(irqs != &pmu_irqs);
+ up(&pmu_mutex);
+}
+EXPORT_SYMBOL_GPL(release_pmu);
+
+static void
+set_irq_affinity(int irq,
+ unsigned int cpu)
+{
+#ifdef CONFIG_SMP
+ struct irq_desc *desc = irq_desc + irq;
+ const struct cpumask *mask = cpumask_of(cpu);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+ cpumask_copy(desc->affinity, mask);
+ desc->chip->set_affinity(irq, mask);
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+#endif
+}
+
+int
+init_pmu(void)
+{
+ int i;
+
+ for (i = 0; i < pmu_irqs.num_irqs; ++i)
+ set_irq_affinity(pmu_irqs.irqs[i], i);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(init_pmu);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index dd4698c..fc5c05b 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -342,6 +342,7 @@ config CPU_XSCALE
select CPU_PABRT_LEGACY
select CPU_CACHE_VIVT
select CPU_CP15_MMU
+ select CPU_HAS_PMU
select CPU_TLB_V4WBI if MMU
# XScale Core Version 3
@@ -398,6 +399,7 @@ config CPU_V6
select CPU_HAS_ASID if MMU
select CPU_COPY_V6 if MMU
select CPU_TLB_V6 if MMU
+ select CPU_HAS_PMU
# ARMv6k
config CPU_32v6K
@@ -421,6 +423,7 @@ config CPU_V7
select CPU_CACHE_V7
select CPU_CACHE_VIPT
select CPU_CP15_MMU
+ select CPU_HAS_PMU
select CPU_HAS_ASID if MMU
select CPU_COPY_V6 if MMU
select CPU_TLB_V7 if MMU
@@ -536,6 +539,9 @@ config CPU_COPY_FA
config CPU_COPY_V6
bool
+config CPU_HAS_PMU
+ bool
+
# This selects the TLB model
config CPU_TLB_V3
bool
--
1.6.5.4
More information about the linux-arm-kernel
mailing list