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