| /** |
| * @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 = { |
| set_kset_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 |
| |
| 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); |
| } |