pm: EDP: positive E-state handing
[linux-2.6.git] / drivers / edp / edp.c
1 /*
2  * Copyright (c) 2012 NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that 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 along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/string.h>
22 #include <linux/mutex.h>
23 #include <linux/errno.h>
24 #include <linux/edp.h>
25
26 DEFINE_MUTEX(edp_lock);
27 static LIST_HEAD(edp_managers);
28
29 static struct edp_manager *find_manager(const char *name)
30 {
31         struct edp_manager *mgr;
32
33         if (!name)
34                 return NULL;
35
36         list_for_each_entry(mgr, &edp_managers, link)
37                 if (!strcmp(mgr->name, name))
38                         return mgr;
39
40         return NULL;
41 }
42
43 int edp_register_manager(struct edp_manager *mgr)
44 {
45         int r = -EEXIST;
46
47         if (!mgr)
48                 return -EINVAL;
49
50         mutex_lock(&edp_lock);
51         if (!find_manager(mgr->name)) {
52                 list_add_tail(&mgr->link, &edp_managers);
53                 mgr->registered = true;
54                 mgr->remaining = mgr->imax;
55                 INIT_LIST_HEAD(&mgr->clients);
56                 r = 0;
57         }
58         mutex_unlock(&edp_lock);
59
60         return r;
61 }
62 EXPORT_SYMBOL(edp_register_manager);
63
64 int edp_unregister_manager(struct edp_manager *mgr)
65 {
66         int r = 0;
67
68         if (!mgr)
69                 return -EINVAL;
70
71         mutex_lock(&edp_lock);
72         if (!mgr->registered) {
73                 r = -ENODEV;
74         } else if (!list_empty(&mgr->clients)) {
75                 r = -EBUSY;
76         } else {
77                 list_del(&mgr->link);
78                 mgr->registered = false;
79         }
80         mutex_unlock(&edp_lock);
81
82         return r;
83 }
84 EXPORT_SYMBOL(edp_unregister_manager);
85
86 struct edp_manager *edp_get_manager(const char *name)
87 {
88         struct edp_manager *mgr;
89
90         mutex_lock(&edp_lock);
91         mgr = find_manager(name);
92         mutex_unlock(&edp_lock);
93
94         return mgr;
95 }
96 EXPORT_SYMBOL(edp_get_manager);
97
98 static struct edp_client *find_client(struct edp_manager *mgr,
99                 const char *name)
100 {
101         struct edp_client *p;
102
103         if (!name)
104                 return NULL;
105
106         list_for_each_entry(p, &mgr->clients, link)
107                 if (!strcmp(p->name, name))
108                         return p;
109
110         return NULL;
111 }
112
113 static unsigned int e0_current_sum(struct edp_manager *mgr)
114 {
115         struct edp_client *p;
116         unsigned int sum = 0;
117
118         list_for_each_entry(p, &mgr->clients, link)
119                 sum += p->states[p->e0_index];
120
121         return sum;
122 }
123
124 static bool states_ok(struct edp_client *client)
125 {
126         int i;
127
128         if (!client->states || !client->num_states ||
129                         client->e0_index >= client->num_states)
130                 return false;
131
132         /* state array should be sorted in descending order */
133         for (i = 1; i < client->num_states; i++)
134                 if (client->states[i] >= client->states[i - 1])
135                         return false;
136
137         return true;
138 }
139
140 static int register_client(struct edp_manager *mgr, struct edp_client *client)
141 {
142         if (!mgr || !client)
143                 return -EINVAL;
144
145         if (client->manager || find_client(mgr, client->name))
146                 return -EEXIST;
147
148         if (!mgr->registered)
149                 return -ENODEV;
150
151         if (!states_ok(client))
152                 return -EINVAL;
153
154         /* make sure that we can satisfy E0 for all registered clients */
155         if (e0_current_sum(mgr) + client->states[client->e0_index] > mgr->imax)
156                 return -E2BIG;
157
158         list_add_tail(&client->link, &mgr->clients);
159         client->manager = mgr;
160         client->req = NULL;
161         client->cur = NULL;
162
163         return 0;
164 }
165
166 int edp_register_client(struct edp_manager *mgr, struct edp_client *client)
167 {
168         int r;
169
170         mutex_lock(&edp_lock);
171         r = register_client(mgr, client);
172         mutex_unlock(&edp_lock);
173
174         return r;
175 }
176 EXPORT_SYMBOL(edp_register_client);
177
178 static int mod_request(struct edp_client *client, const unsigned int *req)
179 {
180         unsigned int old = client->cur ? *client->cur : 0;
181         unsigned int new = req ? *req : 0;
182         unsigned int need;
183
184         if (new < old) {
185                 client->cur = req;
186                 client->manager->remaining += old - new;
187         } else {
188                 need = new - old;
189                 if (need > client->manager->remaining)
190                         return -ENODEV;
191                 client->manager->remaining -= need;
192                 client->cur = req;
193         }
194
195         return 0;
196 }
197
198 static int unregister_client(struct edp_client *client)
199 {
200         if (!client)
201                 return -EINVAL;
202
203         if (!client->manager)
204                 return -ENODEV;
205
206         mod_request(client, NULL);
207         list_del(&client->link);
208         client->manager = NULL;
209
210         return 0;
211 }
212
213 int edp_unregister_client(struct edp_client *client)
214 {
215         int r;
216
217         mutex_lock(&edp_lock);
218         r = unregister_client(client);
219         mutex_unlock(&edp_lock);
220
221         return r;
222 }
223 EXPORT_SYMBOL(edp_unregister_client);
224
225 static int update_client_request(struct edp_client *client, unsigned int req,
226                 int *approved)
227 {
228         int r;
229
230         if (!client)
231                 return -EINVAL;
232
233         if (!client->manager)
234                 return -ENODEV;
235
236         if (req >= client->num_states)
237                 return -EINVAL;
238
239         r = mod_request(client, client->states + req);
240         if (!r && approved)
241                 *approved = client->cur - client->states;
242
243         return r;
244 }
245
246 int edp_update_client_request(struct edp_client *client, unsigned int req,
247                 unsigned int *approved)
248 {
249         int r;
250
251         mutex_lock(&edp_lock);
252         r = update_client_request(client, req, approved);
253         mutex_unlock(&edp_lock);
254
255         return r;
256 }
257 EXPORT_SYMBOL(edp_update_client_request);