diff options
Diffstat (limited to 'arch/arm/oprofile')
-rw-r--r-- | arch/arm/oprofile/Makefile | 7 | ||||
-rw-r--r-- | arch/arm/oprofile/backtrace.c | 83 | ||||
-rw-r--r-- | arch/arm/oprofile/common.c | 375 | ||||
-rw-r--r-- | arch/arm/oprofile/op_arm_model.h | 35 | ||||
-rw-r--r-- | arch/arm/oprofile/op_counter.h | 27 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.c | 162 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.h | 45 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_mpcore.c | 306 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_mpcore.h | 61 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_v6.c | 78 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_v7.c | 415 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_v7.h | 103 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_xscale.c | 444 |
13 files changed, 310 insertions, 1831 deletions
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index 88e31f549f5..e666eafed15 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile @@ -6,9 +6,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofilefs.o oprofile_stats.o \ timer_int.o ) -oprofile-y := $(DRIVER_OBJS) common.o backtrace.o -oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o -oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o -oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o -oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o -oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o +oprofile-y := $(DRIVER_OBJS) common.o diff --git a/arch/arm/oprofile/backtrace.c b/arch/arm/oprofile/backtrace.c deleted file mode 100644 index d805a52b503..00000000000 --- a/arch/arm/oprofile/backtrace.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Arm specific backtracing code for oprofile - * - * Copyright 2005 Openedhand Ltd. - * - * Author: Richard Purdie <rpurdie@openedhand.com> - * - * Based on i386 oprofile backtrace code by John Levon, David Smith - * - * 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/oprofile.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/uaccess.h> -#include <asm/ptrace.h> -#include <asm/stacktrace.h> - -static int report_trace(struct stackframe *frame, void *d) -{ - unsigned int *depth = d; - - if (*depth) { - oprofile_add_trace(frame->pc); - (*depth)--; - } - - return *depth == 0; -} - -/* - * The registers we're interested in are at the end of the variable - * length saved register structure. The fp points at the end of this - * structure so the address of this struct is: - * (struct frame_tail *)(xxx->fp)-1 - */ -struct frame_tail { - struct frame_tail *fp; - unsigned long sp; - unsigned long lr; -} __attribute__((packed)); - -static struct frame_tail* user_backtrace(struct frame_tail *tail) -{ - struct frame_tail buftail[2]; - - /* Also check accessibility of one struct frame_tail beyond */ - if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) - return NULL; - if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) - return NULL; - - oprofile_add_trace(buftail[0].lr); - - /* frame pointers should strictly progress back up the stack - * (towards higher addresses) */ - if (tail >= buftail[0].fp) - return NULL; - - return buftail[0].fp-1; -} - -void arm_backtrace(struct pt_regs * const regs, unsigned int depth) -{ - struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; - - if (!user_mode(regs)) { - struct stackframe frame; - frame.fp = regs->ARM_fp; - frame.sp = regs->ARM_sp; - frame.lr = regs->ARM_lr; - frame.pc = regs->ARM_pc; - walk_stackframe(&frame, report_trace, &depth); - return; - } - - while (depth-- && tail && !((unsigned long) tail & 3)) - tail = user_backtrace(tail); -} diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 3fcd752d614..0691176899f 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -2,32 +2,184 @@ * @file common.c * * @remark Copyright 2004 Oprofile Authors + * @remark Copyright 2010 ARM Ltd. * @remark Read the file COPYING * * @author Zwane Mwaikambo + * @author Will Deacon [move to perf] */ +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/errno.h> #include <linux/init.h> +#include <linux/mutex.h> #include <linux/oprofile.h> -#include <linux/errno.h> +#include <linux/perf_event.h> +#include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/sysdev.h> -#include <linux/mutex.h> +#include <asm/stacktrace.h> +#include <linux/uaccess.h> -#include "op_counter.h" -#include "op_arm_model.h" +#include <asm/perf_event.h> +#include <asm/ptrace.h> + +#ifdef CONFIG_HW_PERF_EVENTS +/* + * Per performance monitor configuration as set via oprofilefs. + */ +struct op_counter_config { + unsigned long count; + unsigned long enabled; + unsigned long event; + unsigned long unit_mask; + unsigned long kernel; + unsigned long user; + struct perf_event_attr attr; +}; -static struct op_arm_model_spec *op_arm_model; static int op_arm_enabled; static DEFINE_MUTEX(op_arm_mutex); -struct op_counter_config *counter_config; +static struct op_counter_config *counter_config; +static struct perf_event **perf_events[nr_cpumask_bits]; +static int perf_num_counters; + +/* + * Overflow callback for oprofile. + */ +static void op_overflow_handler(struct perf_event *event, int unused, + struct perf_sample_data *data, struct pt_regs *regs) +{ + int id; + u32 cpu = smp_processor_id(); + + for (id = 0; id < perf_num_counters; ++id) + if (perf_events[cpu][id] == event) + break; + + if (id != perf_num_counters) + oprofile_add_sample(regs, id); + else + pr_warning("oprofile: ignoring spurious overflow " + "on cpu %u\n", cpu); +} + +/* + * Called by op_arm_setup to create perf attributes to mirror the oprofile + * settings in counter_config. Attributes are created as `pinned' events and + * so are permanently scheduled on the PMU. + */ +static void op_perf_setup(void) +{ + int i; + u32 size = sizeof(struct perf_event_attr); + struct perf_event_attr *attr; + + for (i = 0; i < perf_num_counters; ++i) { + attr = &counter_config[i].attr; + memset(attr, 0, size); + attr->type = PERF_TYPE_RAW; + attr->size = size; + attr->config = counter_config[i].event; + attr->sample_period = counter_config[i].count; + attr->pinned = 1; + } +} + +static int op_create_counter(int cpu, int event) +{ + int ret = 0; + struct perf_event *pevent; + + if (!counter_config[event].enabled || (perf_events[cpu][event] != NULL)) + return ret; + + pevent = perf_event_create_kernel_counter(&counter_config[event].attr, + cpu, -1, + op_overflow_handler); + + if (IS_ERR(pevent)) { + ret = PTR_ERR(pevent); + } else if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + pr_warning("oprofile: failed to enable event %d " + "on CPU %d\n", event, cpu); + ret = -EBUSY; + } else { + perf_events[cpu][event] = pevent; + } + + return ret; +} + +static void op_destroy_counter(int cpu, int event) +{ + struct perf_event *pevent = perf_events[cpu][event]; + + if (pevent) { + perf_event_release_kernel(pevent); + perf_events[cpu][event] = NULL; + } +} + +/* + * Called by op_arm_start to create active perf events based on the + * perviously configured attributes. + */ +static int op_perf_start(void) +{ + int cpu, event, ret = 0; + + for_each_online_cpu(cpu) { + for (event = 0; event < perf_num_counters; ++event) { + ret = op_create_counter(cpu, event); + if (ret) + goto out; + } + } + +out: + return ret; +} + +/* + * Called by op_arm_stop at the end of a profiling run. + */ +static void op_perf_stop(void) +{ + int cpu, event; + + for_each_online_cpu(cpu) + for (event = 0; event < perf_num_counters; ++event) + op_destroy_counter(cpu, event); +} + + +static char *op_name_from_perf_id(enum arm_perf_pmu_ids id) +{ + switch (id) { + case ARM_PERF_PMU_ID_XSCALE1: + return "arm/xscale1"; + case ARM_PERF_PMU_ID_XSCALE2: + return "arm/xscale2"; + case ARM_PERF_PMU_ID_V6: + return "arm/armv6"; + case ARM_PERF_PMU_ID_V6MP: + return "arm/mpcore"; + case ARM_PERF_PMU_ID_CA8: + return "arm/armv7"; + case ARM_PERF_PMU_ID_CA9: + return "arm/armv7-ca9"; + default: + return NULL; + } +} static int op_arm_create_files(struct super_block *sb, struct dentry *root) { unsigned int i; - for (i = 0; i < op_arm_model->num_counters; i++) { + for (i = 0; i < perf_num_counters; i++) { struct dentry *dir; char buf[4]; @@ -46,12 +198,10 @@ static int op_arm_create_files(struct super_block *sb, struct dentry *root) static int op_arm_setup(void) { - int ret; - spin_lock(&oprofilefs_lock); - ret = op_arm_model->setup_ctrs(); + op_perf_setup(); spin_unlock(&oprofilefs_lock); - return ret; + return 0; } static int op_arm_start(void) @@ -60,8 +210,9 @@ static int op_arm_start(void) mutex_lock(&op_arm_mutex); if (!op_arm_enabled) { - ret = op_arm_model->start(); - op_arm_enabled = !ret; + ret = 0; + op_perf_start(); + op_arm_enabled = 1; } mutex_unlock(&op_arm_mutex); return ret; @@ -71,113 +222,205 @@ static void op_arm_stop(void) { mutex_lock(&op_arm_mutex); if (op_arm_enabled) - op_arm_model->stop(); + op_perf_stop(); op_arm_enabled = 0; mutex_unlock(&op_arm_mutex); } #ifdef CONFIG_PM -static int op_arm_suspend(struct sys_device *dev, pm_message_t state) +static int op_arm_suspend(struct platform_device *dev, pm_message_t state) { mutex_lock(&op_arm_mutex); if (op_arm_enabled) - op_arm_model->stop(); + op_perf_stop(); mutex_unlock(&op_arm_mutex); return 0; } -static int op_arm_resume(struct sys_device *dev) +static int op_arm_resume(struct platform_device *dev) { mutex_lock(&op_arm_mutex); - if (op_arm_enabled && op_arm_model->start()) + if (op_arm_enabled && op_perf_start()) op_arm_enabled = 0; mutex_unlock(&op_arm_mutex); return 0; } -static struct sysdev_class oprofile_sysclass = { - .name = "oprofile", +static struct platform_driver oprofile_driver = { + .driver = { + .name = "arm-oprofile", + }, .resume = op_arm_resume, .suspend = op_arm_suspend, }; -static struct sys_device device_oprofile = { - .id = 0, - .cls = &oprofile_sysclass, -}; +static struct platform_device *oprofile_pdev; static int __init init_driverfs(void) { int ret; - if (!(ret = sysdev_class_register(&oprofile_sysclass))) - ret = sysdev_register(&device_oprofile); + ret = platform_driver_register(&oprofile_driver); + if (ret) + goto out; + oprofile_pdev = platform_device_register_simple( + oprofile_driver.driver.name, 0, NULL, 0); + if (IS_ERR(oprofile_pdev)) { + ret = PTR_ERR(oprofile_pdev); + platform_driver_unregister(&oprofile_driver); + } + +out: return ret; } static void exit_driverfs(void) { - sysdev_unregister(&device_oprofile); - sysdev_class_unregister(&oprofile_sysclass); + platform_device_unregister(oprofile_pdev); + platform_driver_unregister(&oprofile_driver); } #else -#define init_driverfs() do { } while (0) +static int __init init_driverfs(void) { return 0; } #define exit_driverfs() do { } while (0) #endif /* CONFIG_PM */ -int __init oprofile_arch_init(struct oprofile_operations *ops) +static int report_trace(struct stackframe *frame, void *d) { - struct op_arm_model_spec *spec = NULL; - int ret = -ENODEV; + unsigned int *depth = d; - ops->backtrace = arm_backtrace; + if (*depth) { + oprofile_add_trace(frame->pc); + (*depth)--; + } -#ifdef CONFIG_CPU_XSCALE - spec = &op_xscale_spec; -#endif + return *depth == 0; +} -#ifdef CONFIG_OPROFILE_ARMV6 - spec = &op_armv6_spec; -#endif +/* + * The registers we're interested in are at the end of the variable + * length saved register structure. The fp points at the end of this + * structure so the address of this struct is: + * (struct frame_tail *)(xxx->fp)-1 + */ +struct frame_tail { + struct frame_tail *fp; + unsigned long sp; + unsigned long lr; +} __attribute__((packed)); -#ifdef CONFIG_OPROFILE_MPCORE - spec = &op_mpcore_spec; -#endif +static struct frame_tail* user_backtrace(struct frame_tail *tail) +{ + struct frame_tail buftail[2]; -#ifdef CONFIG_OPROFILE_ARMV7 - spec = &op_armv7_spec; -#endif + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) + return NULL; + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) + return NULL; - if (spec) { - ret = spec->init(); - if (ret < 0) - return ret; + oprofile_add_trace(buftail[0].lr); - counter_config = kcalloc(spec->num_counters, sizeof(struct op_counter_config), - GFP_KERNEL); - if (!counter_config) - return -ENOMEM; + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (tail >= buftail[0].fp) + return NULL; - op_arm_model = spec; - init_driverfs(); - ops->create_files = op_arm_create_files; - ops->setup = op_arm_setup; - ops->shutdown = op_arm_stop; - ops->start = op_arm_start; - ops->stop = op_arm_stop; - ops->cpu_type = op_arm_model->name; - printk(KERN_INFO "oprofile: using %s\n", spec->name); + return buftail[0].fp-1; +} + +static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) +{ + struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; + + if (!user_mode(regs)) { + struct stackframe frame; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + walk_stackframe(&frame, report_trace, &depth); + return; } + while (depth-- && tail && !((unsigned long) tail & 3)) + tail = user_backtrace(tail); +} + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + int cpu, ret = 0; + + perf_num_counters = armpmu_get_max_events(); + + counter_config = kcalloc(perf_num_counters, + sizeof(struct op_counter_config), GFP_KERNEL); + + if (!counter_config) { + pr_info("oprofile: failed to allocate %d " + "counters\n", perf_num_counters); + return -ENOMEM; + } + + ret = init_driverfs(); + if (ret) { + kfree(counter_config); + return ret; + } + + for_each_possible_cpu(cpu) { + perf_events[cpu] = kcalloc(perf_num_counters, + sizeof(struct perf_event *), GFP_KERNEL); + if (!perf_events[cpu]) { + pr_info("oprofile: failed to allocate %d perf events " + "for cpu %d\n", perf_num_counters, cpu); + while (--cpu >= 0) + kfree(perf_events[cpu]); + return -ENOMEM; + } + } + + ops->backtrace = arm_backtrace; + ops->create_files = op_arm_create_files; + ops->setup = op_arm_setup; + ops->start = op_arm_start; + ops->stop = op_arm_stop; + ops->shutdown = op_arm_stop; + ops->cpu_type = op_name_from_perf_id(armpmu_get_pmu_id()); + + if (!ops->cpu_type) + ret = -ENODEV; + else + pr_info("oprofile: using %s\n", ops->cpu_type); + return ret; } void oprofile_arch_exit(void) { - if (op_arm_model) { + int cpu, id; + struct perf_event *event; + + if (*perf_events) { exit_driverfs(); - op_arm_model = NULL; + for_each_possible_cpu(cpu) { + for (id = 0; id < perf_num_counters; ++id) { + event = perf_events[cpu][id]; + if (event != NULL) + perf_event_release_kernel(event); + } + kfree(perf_events[cpu]); + } } - kfree(counter_config); + + if (counter_config) + kfree(counter_config); +} +#else +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + pr_info("oprofile: hardware counters not available\n"); + return -ENODEV; } +void oprofile_arch_exit(void) {} +#endif /* CONFIG_HW_PERF_EVENTS */ diff --git a/arch/arm/oprofile/op_arm_model.h b/arch/arm/oprofile/op_arm_model.h deleted file mode 100644 index 8c4e4f6a1de..00000000000 --- a/arch/arm/oprofile/op_arm_model.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @file op_arm_model.h - * interface to ARM machine specific operations - * - * @remark Copyright 2004 Oprofile Authors - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - */ - -#ifndef OP_ARM_MODEL_H -#define OP_ARM_MODEL_H - -struct op_arm_model_spec { - int (*init)(void); - unsigned int num_counters; - int (*setup_ctrs)(void); - int (*start)(void); - void (*stop)(void); - char *name; -}; - -#ifdef CONFIG_CPU_XSCALE -extern struct op_arm_model_spec op_xscale_spec; -#endif - -extern struct op_arm_model_spec op_armv6_spec; -extern struct op_arm_model_spec op_mpcore_spec; -extern struct op_arm_model_spec op_armv7_spec; - -extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); - -extern int __init op_arm_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec); -extern void op_arm_exit(void); -#endif /* OP_ARM_MODEL_H */ diff --git a/arch/arm/oprofile/op_counter.h b/arch/arm/oprofile/op_counter.h deleted file mode 100644 index ca942a63b52..00000000000 --- a/arch/arm/oprofile/op_counter.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file op_counter.h - * - * @remark Copyright 2004 Oprofile Authors - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - */ - -#ifndef OP_COUNTER_H -#define OP_COUNTER_H - -/* Per performance monitor configuration as set via - * oprofilefs. - */ -struct op_counter_config { - unsigned long count; - unsigned long enabled; - unsigned long event; - unsigned long unit_mask; - unsigned long kernel; - unsigned long user; -}; - -extern struct op_counter_config *counter_config; - -#endif /* OP_COUNTER_H */ diff --git a/arch/arm/oprofile/op_model_arm11_core.c b/arch/arm/oprofile/op_model_arm11_core.c deleted file mode 100644 index ef3e2653b90..00000000000 --- a/arch/arm/oprofile/op_model_arm11_core.c +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @file op_model_arm11_core.c - * ARM11 Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/oprofile.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/smp.h> - -#include "op_counter.h" -#include "op_arm_model.h" -#include "op_model_arm11_core.h" - -/* - * ARM11 PMU support - */ -static inline void arm11_write_pmnc(u32 val) -{ - /* upper 4bits and 7, 11 are write-as-0 */ - val &= 0x0ffff77f; - asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); -} - -static inline u32 arm11_read_pmnc(void) -{ - u32 val; - asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); - return val; -} - -static void arm11_reset_counter(unsigned int cnt) -{ - u32 val = -(u32)counter_config[CPU_COUNTER(smp_processor_id(), cnt)].count; - switch (cnt) { - case CCNT: - asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); - break; - - case PMN0: - asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); - break; - - case PMN1: - asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); - break; - } -} - -int arm11_setup_pmu(void) -{ - unsigned int cnt; - u32 pmnc; - - if (arm11_read_pmnc() & PMCR_E) { - printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", smp_processor_id()); - return -EBUSY; - } - - /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ - arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | - PMCR_C | PMCR_P); - - for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { - unsigned long event; - - if (!counter_config[CPU_COUNTER(smp_processor_id(), cnt)].enabled) - continue; - - event = counter_config[CPU_COUNTER(smp_processor_id(), cnt)].event & 255; - - /* - * Set event (if destined for PMNx counters) - */ - if (cnt == PMN0) { - pmnc |= event << 20; - } else if (cnt == PMN1) { - pmnc |= event << 12; - } - - /* - * We don't need to set the event if it's a cycle count - * Enable interrupt for this counter - */ - pmnc |= PMCR_IEN_PMN0 << cnt; - arm11_reset_counter(cnt); - } - arm11_write_pmnc(pmnc); - - return 0; -} - -int arm11_start_pmu(void) -{ - arm11_write_pmnc(arm11_read_pmnc() | PMCR_E); - return 0; -} - -int arm11_stop_pmu(void) -{ - unsigned int cnt; - - arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E); - - for (cnt = PMN0; cnt <= CCNT; cnt++) - arm11_reset_counter(cnt); - - return 0; -} - -/* - * CPU counters' IRQ handler (one IRQ per CPU) - */ -static irqreturn_t arm11_pmu_interrupt(int irq, void *arg) -{ - struct pt_regs *regs = get_irq_regs(); - unsigned int cnt; - u32 pmnc; - - pmnc = arm11_read_pmnc(); - - for (cnt = PMN0; cnt <= CCNT; cnt++) { - if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) { - arm11_reset_counter(cnt); - oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt)); - } - } - /* Clear counter flag(s) */ - arm11_write_pmnc(pmnc); - return IRQ_HANDLED; -} - -int arm11_request_interrupts(const int *irqs, int nr) -{ - unsigned int i; - int ret = 0; - - for(i = 0; i < nr; i++) { - ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL); - if (ret != 0) { - printk(KERN_ERR "oprofile: unable to request IRQ%u for MPCORE-EM\n", - irqs[i]); - break; - } - } - - if (i != nr) - while (i-- != 0) - free_irq(irqs[i], NULL); - - return ret; -} - -void arm11_release_interrupts(const int *irqs, int nr) -{ - unsigned int i; - - for (i = 0; i < nr; i++) - free_irq(irqs[i], NULL); -} diff --git a/arch/arm/oprofile/op_model_arm11_core.h b/arch/arm/oprofile/op_model_arm11_core.h deleted file mode 100644 index 1902b99d9df..00000000000 --- a/arch/arm/oprofile/op_model_arm11_core.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file op_model_arm11_core.h - * ARM11 Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * @remark Copyright 2000-2004 MontaVista Software Inc - * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * @remark Copyright 2004 Intel Corporation - * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * @remark Copyright 2004 Oprofile Authors - * - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - */ -#ifndef OP_MODEL_ARM11_CORE_H -#define OP_MODEL_ARM11_CORE_H - -/* - * Per-CPU PMCR - */ -#define PMCR_E (1 << 0) /* Enable */ -#define PMCR_P (1 << 1) /* Count reset */ -#define PMCR_C (1 << 2) /* Cycle counter reset */ -#define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */ -#define PMCR_IEN_PMN0 (1 << 4) /* Interrupt enable count reg 0 */ -#define PMCR_IEN_PMN1 (1 << 5) /* Interrupt enable count reg 1 */ -#define PMCR_IEN_CCNT (1 << 6) /* Interrupt enable cycle counter */ -#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ -#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ -#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ - -#define PMN0 0 -#define PMN1 1 -#define CCNT 2 - -#define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter)) - -int arm11_setup_pmu(void); -int arm11_start_pmu(void); -int arm11_stop_pmu(void); -int arm11_request_interrupts(const int *, int); -void arm11_release_interrupts(const int *, int); - -#endif diff --git a/arch/arm/oprofile/op_model_mpcore.c b/arch/arm/oprofile/op_model_mpcore.c deleted file mode 100644 index f73ce875a39..00000000000 --- a/arch/arm/oprofile/op_model_mpcore.c +++ /dev/null @@ -1,306 +0,0 @@ -/** - * @file op_model_mpcore.c - * MPCORE Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * @remark Copyright 2000-2004 MontaVista Software Inc - * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * @remark Copyright 2004 Intel Corporation - * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * @remark Copyright 2004 Oprofile Authors - * - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - * - * Counters: - * 0: PMN0 on CPU0, per-cpu configurable event counter - * 1: PMN1 on CPU0, per-cpu configurable event counter - * 2: CCNT on CPU0 - * 3: PMN0 on CPU1 - * 4: PMN1 on CPU1 - * 5: CCNT on CPU1 - * 6: PMN0 on CPU1 - * 7: PMN1 on CPU1 - * 8: CCNT on CPU1 - * 9: PMN0 on CPU1 - * 10: PMN1 on CPU1 - * 11: CCNT on CPU1 - * 12-19: configurable SCU event counters - */ - -/* #define DEBUG */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/sched.h> -#include <linux/oprofile.h> -#include <linux/interrupt.h> -#include <linux/smp.h> -#include <linux/io.h> - -#include <asm/irq.h> -#include <asm/mach/irq.h> -#include <mach/hardware.h> -#include <mach/board-eb.h> -#include <asm/system.h> -#include <asm/pmu.h> - -#include "op_counter.h" -#include "op_arm_model.h" -#include "op_model_arm11_core.h" -#include "op_model_mpcore.h" - -/* - * MPCore SCU event monitor support - */ -#define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_EB11MP_SCU_BASE + 0x10) - -/* - * Bitmask of used SCU counters - */ -static unsigned int scu_em_used; -static const struct pmu_irqs *pmu_irqs; - -/* - * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number) - */ -static inline void scu_reset_counter(struct eventmonitor __iomem *emc, unsigned int n) -{ - writel(-(u32)counter_config[SCU_COUNTER(n)].count, &emc->MC[n]); -} - -static inline void scu_set_event(struct eventmonitor __iomem *emc, unsigned int n, u32 event) -{ - event &= 0xff; - writeb(event, &emc->MCEB[n]); -} - -/* - * SCU counters' IRQ handler (one IRQ per counter => 2 IRQs per CPU) - */ -static irqreturn_t scu_em_interrupt(int irq, void *arg) -{ - struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; - unsigned int cnt; - - cnt = irq - IRQ_EB11MP_PMU_SCU0; - oprofile_add_sample(get_irq_regs(), SCU_COUNTER(cnt)); - scu_reset_counter(emc, cnt); - - /* Clear overflow flag for this counter */ - writel(1 << (cnt + 16), &emc->PMCR); - - return IRQ_HANDLED; -} - -/* Configure just the SCU counters that the user has requested */ -static void scu_setup(void) -{ - struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; - unsigned int i; - - scu_em_used = 0; - - for (i = 0; i < NUM_SCU_COUNTERS; i++) { - if (counter_config[SCU_COUNTER(i)].enabled && - counter_config[SCU_COUNTER(i)].event) { - scu_set_event(emc, i, 0); /* disable counter for now */ - scu_em_used |= 1 << i; - } - } -} - -static int scu_start(void) -{ - struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; - unsigned int temp, i; - unsigned long event; - int ret = 0; - - /* - * request the SCU counter interrupts that we need - */ - for (i = 0; i < NUM_SCU_COUNTERS; i++) { - if (scu_em_used & (1 << i)) { - ret = request_irq(IRQ_EB11MP_PMU_SCU0 + i, scu_em_interrupt, IRQF_DISABLED, "SCU PMU", NULL); - if (ret) { - printk(KERN_ERR "oprofile: unable to request IRQ%u for SCU Event Monitor\n", - IRQ_EB11MP_PMU_SCU0 + i); - goto err_free_scu; - } - } - } - - /* - * clear overflow and enable interrupt for all used counters - */ - temp = readl(&emc->PMCR); - for (i = 0; i < NUM_SCU_COUNTERS; i++) { - if (scu_em_used & (1 << i)) { - scu_reset_counter(emc, i); - event = counter_config[SCU_COUNTER(i)].event; - scu_set_event(emc, i, event); - - /* clear overflow/interrupt */ - temp |= 1 << (i + 16); - /* enable interrupt*/ - temp |= 1 << (i + 8); - } - } - - /* Enable all 8 counters */ - temp |= PMCR_E; - writel(temp, &emc->PMCR); - - return 0; - - err_free_scu: - while (i--) - free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); - return ret; -} - -static void scu_stop(void) -{ - struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; - unsigned int temp, i; - - /* Disable counter interrupts */ - /* Don't disable all 8 counters (with the E bit) as they may be in use */ - temp = readl(&emc->PMCR); - for (i = 0; i < NUM_SCU_COUNTERS; i++) { - if (scu_em_used & (1 << i)) - temp &= ~(1 << (i + 8)); - } - writel(temp, &emc->PMCR); - - /* Free counter interrupts and reset counters */ - for (i = 0; i < NUM_SCU_COUNTERS; i++) { - if (scu_em_used & (1 << i)) { - scu_reset_counter(emc, i); - free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); - } - } -} - -struct em_function_data { - int (*fn)(void); - int ret; -}; - -static void em_func(void *data) -{ - struct em_function_data *d = data; - int ret = d->fn(); - if (ret) - d->ret = ret; -} - -static int em_call_function(int (*fn)(void)) -{ - struct em_function_data data; - - data.fn = fn; - data.ret = 0; - - preempt_disable(); - smp_call_function(em_func, &data, 1); - em_func(&data); - preempt_enable(); - - return data.ret; -} - -/* - * Glue to stick the individual ARM11 PMUs and the SCU - * into the oprofile framework. - */ -static int em_setup_ctrs(void) -{ - int ret; - - /* Configure CPU counters by cross-calling to the other CPUs */ - ret = em_call_function(arm11_setup_pmu); - if (ret == 0) - scu_setup(); - - return 0; -} - -static int em_start(void) -{ - int ret; - - pmu_irqs = reserve_pmu(); - if (IS_ERR(pmu_irqs)) { - ret = PTR_ERR(pmu_irqs); - goto out; - } - - ret = arm11_request_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs); - if (ret == 0) { - em_call_function(arm11_start_pmu); - - ret = scu_start(); - if (ret) { - arm11_release_interrupts(pmu_irqs->irqs, - pmu_irqs->num_irqs); - } else { - release_pmu(pmu_irqs); - pmu_irqs = NULL; - } - } - -out: - return ret; -} - -static void em_stop(void) -{ - em_call_function(arm11_stop_pmu); |