|  | /** | 
|  | * @file common.c | 
|  | * | 
|  | * @remark Copyright 2004 Oprofile Authors | 
|  | * @remark Read the file COPYING | 
|  | * | 
|  | * @author Zwane Mwaikambo | 
|  | */ | 
|  |  | 
|  | #include <linux/init.h> | 
|  | #include <linux/oprofile.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/sysdev.h> | 
|  | #include <linux/mutex.h> | 
|  |  | 
|  | #include "op_counter.h" | 
|  | #include "op_arm_model.h" | 
|  |  | 
|  | 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 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++) { | 
|  | struct dentry *dir; | 
|  | char buf[4]; | 
|  |  | 
|  | snprintf(buf, sizeof buf, "%d", i); | 
|  | dir = oprofilefs_mkdir(sb, root, buf); | 
|  | oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); | 
|  | oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event); | 
|  | oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count); | 
|  | oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask); | 
|  | oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel); | 
|  | oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int op_arm_setup(void) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | spin_lock(&oprofilefs_lock); | 
|  | ret = op_arm_model->setup_ctrs(); | 
|  | spin_unlock(&oprofilefs_lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int op_arm_start(void) | 
|  | { | 
|  | int ret = -EBUSY; | 
|  |  | 
|  | mutex_lock(&op_arm_mutex); | 
|  | if (!op_arm_enabled) { | 
|  | ret = op_arm_model->start(); | 
|  | op_arm_enabled = !ret; | 
|  | } | 
|  | mutex_unlock(&op_arm_mutex); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void op_arm_stop(void) | 
|  | { | 
|  | mutex_lock(&op_arm_mutex); | 
|  | if (op_arm_enabled) | 
|  | op_arm_model->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) | 
|  | { | 
|  | mutex_lock(&op_arm_mutex); | 
|  | if (op_arm_enabled) | 
|  | op_arm_model->stop(); | 
|  | mutex_unlock(&op_arm_mutex); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int op_arm_resume(struct sys_device *dev) | 
|  | { | 
|  | mutex_lock(&op_arm_mutex); | 
|  | if (op_arm_enabled && op_arm_model->start()) | 
|  | op_arm_enabled = 0; | 
|  | mutex_unlock(&op_arm_mutex); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct sysdev_class oprofile_sysclass = { | 
|  | .name		= "oprofile", | 
|  | .resume		= op_arm_resume, | 
|  | .suspend	= op_arm_suspend, | 
|  | }; | 
|  |  | 
|  | static struct sys_device device_oprofile = { | 
|  | .id		= 0, | 
|  | .cls		= &oprofile_sysclass, | 
|  | }; | 
|  |  | 
|  | static int __init init_driverfs(void) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (!(ret = sysdev_class_register(&oprofile_sysclass))) | 
|  | ret = sysdev_register(&device_oprofile); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void  exit_driverfs(void) | 
|  | { | 
|  | sysdev_unregister(&device_oprofile); | 
|  | sysdev_class_unregister(&oprofile_sysclass); | 
|  | } | 
|  | #else | 
|  | #define init_driverfs()	do { } while (0) | 
|  | #define exit_driverfs() do { } while (0) | 
|  | #endif /* CONFIG_PM */ | 
|  |  | 
|  | int __init oprofile_arch_init(struct oprofile_operations *ops) | 
|  | { | 
|  | struct op_arm_model_spec *spec = NULL; | 
|  | int ret = -ENODEV; | 
|  |  | 
|  | ops->backtrace = arm_backtrace; | 
|  |  | 
|  | #ifdef CONFIG_CPU_XSCALE | 
|  | spec = &op_xscale_spec; | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_OPROFILE_ARMV6 | 
|  | spec = &op_armv6_spec; | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_OPROFILE_MPCORE | 
|  | spec = &op_mpcore_spec; | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_OPROFILE_ARMV7 | 
|  | spec = &op_armv7_spec; | 
|  | #endif | 
|  |  | 
|  | if (spec) { | 
|  | ret = spec->init(); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  |  | 
|  | counter_config = kcalloc(spec->num_counters, sizeof(struct op_counter_config), | 
|  | GFP_KERNEL); | 
|  | if (!counter_config) | 
|  | return -ENOMEM; | 
|  |  | 
|  | 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 ret; | 
|  | } | 
|  |  | 
|  | void oprofile_arch_exit(void) | 
|  | { | 
|  | if (op_arm_model) { | 
|  | exit_driverfs(); | 
|  | op_arm_model = NULL; | 
|  | } | 
|  | kfree(counter_config); | 
|  | } |