EDP: adding debugfs support
[linux-3.10.git] / drivers / edp / edp_debug.c
1 /*
2  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/edp.h>
20 #include <linux/debugfs.h>
21 #include "edp_internal.h"
22
23 struct dentry *edp_debugfs_dir;
24
25 static int __manager_cap_set(struct edp_manager *m, unsigned int new_max)
26 {
27         if (new_max < e0_current_sum(m))
28                 return -EINVAL;
29
30         if (new_max < m->max - m->remaining)
31                 return -EBUSY;
32
33         if (new_max > m->max) {
34                 m->remaining += new_max - m->max;
35                 m->max = new_max;
36                 schedule_promotion(m);
37         } else {
38                 m->remaining -= m->max - new_max;
39                 m->max = new_max;
40         }
41
42         return 0;
43 }
44
45 static int manager_status_show(struct seq_file *file, void *data)
46 {
47         struct edp_manager *m;
48         struct edp_client *c;
49
50         if (!file->private)
51                 return -ENODEV;
52
53         m = file->private;
54
55         mutex_lock(&edp_lock);
56
57         seq_printf(file, "cap      : %u\n", m->max);
58         seq_printf(file, "remaining: %u\n", m->remaining);
59
60         seq_printf(file, "------------------------------------------\n");
61         seq_printf(file, "%-16s %3s %5s %7s %7s\n",
62                         "client", "pri", "E0", "request", "current");
63         seq_printf(file, "------------------------------------------\n");
64
65         list_for_each_entry(c, &m->clients, link)
66                 seq_printf(file, "%-16s %3d %5u %7u %7u\n", c->name,
67                                 c->priority, e0_level(c), req_level(c),
68                                 cur_level(c));
69
70         mutex_unlock(&edp_lock);
71         return 0;
72 }
73
74 static int manager_status_open(struct inode *inode, struct file *file)
75 {
76         return single_open(file, manager_status_show, inode->i_private);
77 }
78
79 static const struct file_operations manager_status_fops = {
80         .open = manager_status_open,
81         .read = seq_read,
82 };
83
84 static int manager_cap_set(void *data, u64 val)
85 {
86         struct edp_manager *m = data;
87         int r;
88
89         mutex_lock(&edp_lock);
90         r = __manager_cap_set(m, val);
91         mutex_unlock(&edp_lock);
92         return r;
93 }
94
95 static int manager_cap_get(void *data, u64 *val)
96 {
97         struct edp_manager *m = data;
98
99         mutex_lock(&edp_lock);
100         *val = m->max;
101         mutex_unlock(&edp_lock);
102         return 0;
103 }
104
105 DEFINE_SIMPLE_ATTRIBUTE(manager_cap_fops, manager_cap_get,
106                 manager_cap_set, "%lld\n");
107
108 void manager_add_dentry(struct edp_manager *m)
109 {
110         struct dentry *d;
111
112         if (!edp_debugfs_dir)
113                 return;
114
115         d = debugfs_create_dir(m->name, edp_debugfs_dir);
116         if (IS_ERR_OR_NULL(d))
117                 return;
118
119         m->dentry = d;
120
121         d = debugfs_create_file("status", S_IRUGO, m->dentry, m,
122                         &manager_status_fops);
123         WARN_ON(IS_ERR_OR_NULL(d));
124
125         d = debugfs_create_file("cap", S_IRUGO | S_IWUSR, m->dentry, m,
126                         &manager_cap_fops);
127         WARN_ON(IS_ERR_OR_NULL(d));
128 }
129
130 void manager_remove_dentry(struct edp_manager *m)
131 {
132         debugfs_remove_recursive(m->dentry);
133         m->dentry = NULL;
134 }
135
136 static int __client_current_set(struct edp_client *c, unsigned int new)
137 {
138         struct edp_manager *m;
139         unsigned int nl;
140         unsigned int cl;
141
142         if (new >= c->num_states)
143                 return -EINVAL;
144
145         nl = c->states[new];
146         cl = cur_level(c);
147         m = c->manager;
148
149         if (nl > cl && nl - cl > m->remaining)
150                 return -EBUSY;
151
152         c->cur = c->states + new;
153         c->req = c->states + new;
154
155         if (nl < cl) {
156                 m->remaining += cl - nl;
157                 if (c->throttle)
158                         c->throttle(new, c->private_data);
159         } else if (nl > cl) {
160                 m->remaining -= nl - cl;
161                 if (c->notify_promotion)
162                         c->notify_promotion(new, c->private_data);
163         }
164
165         return 0;
166 }
167
168 static int client_current_set(void *data, u64 val)
169 {
170         struct edp_client *c = data;
171         int r;
172
173         mutex_lock(&edp_lock);
174         r = __client_current_set(c, val);
175         mutex_unlock(&edp_lock);
176         return r;
177 }
178
179 static int client_current_get(void *data, u64 *val)
180 {
181         struct edp_client *c = data;
182
183         mutex_lock(&edp_lock);
184         *val = cur_level(c);
185         mutex_unlock(&edp_lock);
186         return 0;
187 }
188
189 DEFINE_SIMPLE_ATTRIBUTE(client_current_fops, client_current_get,
190                 client_current_set, "%lld\n");
191
192 void client_add_dentry(struct edp_client *c)
193 {
194         struct dentry *d;
195
196         if (!c->manager->dentry)
197                 return;
198
199         d = debugfs_create_dir(c->name, c->manager->dentry);
200         if (IS_ERR_OR_NULL(d)) {
201                 WARN_ON(1);
202                 return;
203         }
204
205         c->dentry = d;
206
207         d = debugfs_create_file("current", S_IRUGO | S_IWUSR, c->dentry,
208                         c, &client_current_fops);
209         WARN_ON(IS_ERR_OR_NULL(d));
210 }
211
212 void client_remove_dentry(struct edp_client *c)
213 {
214         debugfs_remove_recursive(c->dentry);
215         c->dentry = NULL;
216 }
217
218 static void dbg_update_request(struct edp_client *c, const unsigned int *r) {}
219 static void dbg_update_loans(struct edp_client *c) {}
220 static void dbg_promote(struct edp_manager *mgr) {}
221
222 static struct edp_governor dbg_governor = {
223         .name = "debug",
224         .owner = THIS_MODULE,
225         .update_request = dbg_update_request,
226         .update_loans = dbg_update_loans,
227         .promote = dbg_promote
228 };
229
230 static int __init debug_init(void)
231 {
232         struct dentry *d;
233
234         d = debugfs_create_dir("edp", NULL);
235         if (IS_ERR_OR_NULL(d)) {
236                 WARN_ON(1);
237                 return -EFAULT;
238         }
239
240         edp_debugfs_dir = d;
241         return edp_register_governor(&dbg_governor);
242 }
243 postcore_initcall(debug_init);