blob: e57dde88289813d785b332de04be2eaebd98a6dd [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**
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
19static struct op_arm_model_spec *pmu_model;
20static int pmu_enabled;
21static struct semaphore pmu_sem;
22
23static int pmu_start(void);
24static int pmu_setup(void);
25static void pmu_stop(void);
26static int pmu_create_files(struct super_block *, struct dentry *);
27
28#ifdef CONFIG_PM
29static int pmu_suspend(struct sys_device *dev, pm_message_t state)
30{
31 if (pmu_enabled)
32 pmu_stop();
33 return 0;
34}
35
36static int pmu_resume(struct sys_device *dev)
37{
38 if (pmu_enabled)
39 pmu_start();
40 return 0;
41}
42
43static struct sysdev_class oprofile_sysclass = {
44 set_kset_name("oprofile"),
45 .resume = pmu_resume,
46 .suspend = pmu_suspend,
47};
48
49static struct sys_device device_oprofile = {
50 .id = 0,
51 .cls = &oprofile_sysclass,
52};
53
54static 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
64static 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
74struct op_counter_config counter_config[OP_MAX_COUNTER];
75
76static 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
97static 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
107static 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
120static 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
129int __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
149void pmu_exit(void)
150{
151 if (pmu_model) {
152 exit_driverfs();
153 pmu_model = NULL;
154 }
155}
156