Linux-2.6.12-rc2
[safe/jmp/linux-2.6] / arch / arm / oprofile / common.c
1 /**
2  * @file common.c
3  *
4  * @remark Copyright 2004 Oprofile Authors
5  * @remark Read the file COPYING
6  *
7  * @author Zwane Mwaikambo
8  */
9
10 #include <linux/init.h>
11 #include <linux/oprofile.h>
12 #include <linux/errno.h>
13 #include <asm/semaphore.h>
14 #include <linux/sysdev.h>
15
16 #include "op_counter.h"
17 #include "op_arm_model.h"
18
19 static struct op_arm_model_spec *pmu_model;
20 static int pmu_enabled;
21 static struct semaphore pmu_sem;
22
23 static int pmu_start(void);
24 static int pmu_setup(void);
25 static void pmu_stop(void);
26 static int pmu_create_files(struct super_block *, struct dentry *);
27
28 #ifdef CONFIG_PM
29 static int pmu_suspend(struct sys_device *dev, pm_message_t state)
30 {
31         if (pmu_enabled)
32                 pmu_stop();
33         return 0;
34 }
35
36 static int pmu_resume(struct sys_device *dev)
37 {
38         if (pmu_enabled)
39                 pmu_start();
40         return 0;
41 }
42
43 static struct sysdev_class oprofile_sysclass = {
44         set_kset_name("oprofile"),
45         .resume         = pmu_resume,
46         .suspend        = pmu_suspend,
47 };
48
49 static struct sys_device device_oprofile = {
50         .id             = 0,
51         .cls            = &oprofile_sysclass,
52 };
53
54 static int __init init_driverfs(void)
55 {
56         int ret;
57
58         if (!(ret = sysdev_class_register(&oprofile_sysclass)))
59                 ret = sysdev_register(&device_oprofile);
60
61         return ret;
62 }
63
64 static void  exit_driverfs(void)
65 {
66         sysdev_unregister(&device_oprofile);
67         sysdev_class_unregister(&oprofile_sysclass);
68 }
69 #else
70 #define init_driverfs() do { } while (0)
71 #define exit_driverfs() do { } while (0)
72 #endif /* CONFIG_PM */
73
74 struct op_counter_config counter_config[OP_MAX_COUNTER];
75
76 static int pmu_create_files(struct super_block *sb, struct dentry *root)
77 {
78         unsigned int i;
79
80         for (i = 0; i < pmu_model->num_counters; i++) {
81                 struct dentry *dir;
82                 char buf[2];
83
84                 snprintf(buf, sizeof buf, "%d", i);
85                 dir = oprofilefs_mkdir(sb, root, buf);
86                 oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
87                 oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
88                 oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
89                 oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
90                 oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
91                 oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
92         }
93
94         return 0;
95 }
96
97 static int pmu_setup(void)
98 {
99         int ret;
100
101         spin_lock(&oprofilefs_lock);
102         ret = pmu_model->setup_ctrs();
103         spin_unlock(&oprofilefs_lock);
104         return ret;
105 }
106
107 static int pmu_start(void)
108 {
109         int ret = -EBUSY;
110
111         down(&pmu_sem);
112         if (!pmu_enabled) {
113                 ret = pmu_model->start();
114                 pmu_enabled = !ret;
115         }
116         up(&pmu_sem);
117         return ret;
118 }
119
120 static void pmu_stop(void)
121 {
122         down(&pmu_sem);
123         if (pmu_enabled)
124                 pmu_model->stop();
125         pmu_enabled = 0;
126         up(&pmu_sem);
127 }
128
129 int __init pmu_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec)
130 {
131         init_MUTEX(&pmu_sem);
132
133         if (spec->init() < 0)
134                 return -ENODEV;
135
136         pmu_model = spec;
137         init_driverfs();
138         ops->create_files = pmu_create_files;
139         ops->setup = pmu_setup;
140         ops->shutdown = pmu_stop;
141         ops->start = pmu_start;
142         ops->stop = pmu_stop;
143         ops->cpu_type = pmu_model->name;
144         printk(KERN_INFO "oprofile: using %s PMU\n", spec->name);
145
146         return 0;
147 }
148
149 void pmu_exit(void)
150 {
151         if (pmu_model) {
152                 exit_driverfs();
153                 pmu_model = NULL;
154         }
155 }
156