blob: 5c1d75662b8272a6c8c5429a2df41aeadb10b068 [file] [log] [blame]
Mark Brownc0809092008-04-30 17:05:33 +01001/*
2 * reg-virtual-consumer.c
3 *
4 * Copyright 2008 Wolfson Microelectronics PLC.
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 */
13
14#include <linux/err.h>
15#include <linux/mutex.h>
16#include <linux/platform_device.h>
17#include <linux/regulator/consumer.h>
18
19struct virtual_consumer_data {
20 struct mutex lock;
21 struct regulator *regulator;
22 int enabled;
23 int min_uV;
24 int max_uV;
25 int min_uA;
26 int max_uA;
27 unsigned int mode;
28};
29
Mark Browna07ac212009-06-17 15:45:06 +010030static void update_voltage_constraints(struct device *dev,
31 struct virtual_consumer_data *data)
Mark Brownc0809092008-04-30 17:05:33 +010032{
33 int ret;
34
35 if (data->min_uV && data->max_uV
36 && data->min_uV <= data->max_uV) {
37 ret = regulator_set_voltage(data->regulator,
Mark Browna07ac212009-06-17 15:45:06 +010038 data->min_uV, data->max_uV);
Mark Brownc0809092008-04-30 17:05:33 +010039 if (ret != 0) {
Mark Browna07ac212009-06-17 15:45:06 +010040 dev_err(dev,
41 "regulator_set_voltage() failed: %d\n", ret);
Mark Brownc0809092008-04-30 17:05:33 +010042 return;
43 }
44 }
45
46 if (data->min_uV && data->max_uV && !data->enabled) {
47 ret = regulator_enable(data->regulator);
48 if (ret == 0)
49 data->enabled = 1;
50 else
Mark Browna07ac212009-06-17 15:45:06 +010051 dev_err(dev, "regulator_enable() failed: %d\n",
Mark Brownc0809092008-04-30 17:05:33 +010052 ret);
53 }
54
55 if (!(data->min_uV && data->max_uV) && data->enabled) {
56 ret = regulator_disable(data->regulator);
57 if (ret == 0)
58 data->enabled = 0;
59 else
Mark Browna07ac212009-06-17 15:45:06 +010060 dev_err(dev, "regulator_disable() failed: %d\n",
Mark Brownc0809092008-04-30 17:05:33 +010061 ret);
62 }
63}
64
Mark Browna07ac212009-06-17 15:45:06 +010065static void update_current_limit_constraints(struct device *dev,
66 struct virtual_consumer_data *data)
Mark Brownc0809092008-04-30 17:05:33 +010067{
68 int ret;
69
70 if (data->max_uA
71 && data->min_uA <= data->max_uA) {
72 ret = regulator_set_current_limit(data->regulator,
73 data->min_uA, data->max_uA);
74 if (ret != 0) {
Mark Browna07ac212009-06-17 15:45:06 +010075 dev_err(dev,
76 "regulator_set_current_limit() failed: %d\n",
77 ret);
Mark Brownc0809092008-04-30 17:05:33 +010078 return;
79 }
80 }
81
82 if (data->max_uA && !data->enabled) {
83 ret = regulator_enable(data->regulator);
84 if (ret == 0)
85 data->enabled = 1;
86 else
Mark Browna07ac212009-06-17 15:45:06 +010087 dev_err(dev, "regulator_enable() failed: %d\n",
Mark Brownc0809092008-04-30 17:05:33 +010088 ret);
89 }
90
91 if (!(data->min_uA && data->max_uA) && data->enabled) {
92 ret = regulator_disable(data->regulator);
93 if (ret == 0)
94 data->enabled = 0;
95 else
Mark Browna07ac212009-06-17 15:45:06 +010096 dev_err(dev, "regulator_disable() failed: %d\n",
Mark Brownc0809092008-04-30 17:05:33 +010097 ret);
98 }
99}
100
101static ssize_t show_min_uV(struct device *dev,
102 struct device_attribute *attr, char *buf)
103{
104 struct virtual_consumer_data *data = dev_get_drvdata(dev);
105 return sprintf(buf, "%d\n", data->min_uV);
106}
107
108static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr,
109 const char *buf, size_t count)
110{
111 struct virtual_consumer_data *data = dev_get_drvdata(dev);
112 long val;
113
114 if (strict_strtol(buf, 10, &val) != 0)
115 return count;
116
117 mutex_lock(&data->lock);
118
119 data->min_uV = val;
Mark Browna07ac212009-06-17 15:45:06 +0100120 update_voltage_constraints(dev, data);
Mark Brownc0809092008-04-30 17:05:33 +0100121
122 mutex_unlock(&data->lock);
123
124 return count;
125}
126
127static ssize_t show_max_uV(struct device *dev,
128 struct device_attribute *attr, char *buf)
129{
130 struct virtual_consumer_data *data = dev_get_drvdata(dev);
131 return sprintf(buf, "%d\n", data->max_uV);
132}
133
134static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr,
135 const char *buf, size_t count)
136{
137 struct virtual_consumer_data *data = dev_get_drvdata(dev);
138 long val;
139
140 if (strict_strtol(buf, 10, &val) != 0)
141 return count;
142
143 mutex_lock(&data->lock);
144
145 data->max_uV = val;
Mark Browna07ac212009-06-17 15:45:06 +0100146 update_voltage_constraints(dev, data);
Mark Brownc0809092008-04-30 17:05:33 +0100147
148 mutex_unlock(&data->lock);
149
150 return count;
151}
152
153static ssize_t show_min_uA(struct device *dev,
154 struct device_attribute *attr, char *buf)
155{
156 struct virtual_consumer_data *data = dev_get_drvdata(dev);
157 return sprintf(buf, "%d\n", data->min_uA);
158}
159
160static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr,
161 const char *buf, size_t count)
162{
163 struct virtual_consumer_data *data = dev_get_drvdata(dev);
164 long val;
165
166 if (strict_strtol(buf, 10, &val) != 0)
167 return count;
168
169 mutex_lock(&data->lock);
170
171 data->min_uA = val;
Mark Browna07ac212009-06-17 15:45:06 +0100172 update_current_limit_constraints(dev, data);
Mark Brownc0809092008-04-30 17:05:33 +0100173
174 mutex_unlock(&data->lock);
175
176 return count;
177}
178
179static ssize_t show_max_uA(struct device *dev,
180 struct device_attribute *attr, char *buf)
181{
182 struct virtual_consumer_data *data = dev_get_drvdata(dev);
183 return sprintf(buf, "%d\n", data->max_uA);
184}
185
186static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr,
187 const char *buf, size_t count)
188{
189 struct virtual_consumer_data *data = dev_get_drvdata(dev);
190 long val;
191
192 if (strict_strtol(buf, 10, &val) != 0)
193 return count;
194
195 mutex_lock(&data->lock);
196
197 data->max_uA = val;
Mark Browna07ac212009-06-17 15:45:06 +0100198 update_current_limit_constraints(dev, data);
Mark Brownc0809092008-04-30 17:05:33 +0100199
200 mutex_unlock(&data->lock);
201
202 return count;
203}
204
205static ssize_t show_mode(struct device *dev,
206 struct device_attribute *attr, char *buf)
207{
208 struct virtual_consumer_data *data = dev_get_drvdata(dev);
209
210 switch (data->mode) {
211 case REGULATOR_MODE_FAST:
212 return sprintf(buf, "fast\n");
213 case REGULATOR_MODE_NORMAL:
214 return sprintf(buf, "normal\n");
215 case REGULATOR_MODE_IDLE:
216 return sprintf(buf, "idle\n");
217 case REGULATOR_MODE_STANDBY:
218 return sprintf(buf, "standby\n");
219 default:
220 return sprintf(buf, "unknown\n");
221 }
222}
223
224static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
225 const char *buf, size_t count)
226{
227 struct virtual_consumer_data *data = dev_get_drvdata(dev);
228 unsigned int mode;
229 int ret;
230
Andrew Morton94853972009-01-15 16:13:01 -0800231 /*
232 * sysfs_streq() doesn't need the \n's, but we add them so the strings
233 * will be shared with show_mode(), above.
234 */
Mike Rapoportaa61d552009-04-26 11:49:30 +0300235 if (sysfs_streq(buf, "fast\n"))
Mark Brownc0809092008-04-30 17:05:33 +0100236 mode = REGULATOR_MODE_FAST;
Mike Rapoportaa61d552009-04-26 11:49:30 +0300237 else if (sysfs_streq(buf, "normal\n"))
Mark Brownc0809092008-04-30 17:05:33 +0100238 mode = REGULATOR_MODE_NORMAL;
Mike Rapoportaa61d552009-04-26 11:49:30 +0300239 else if (sysfs_streq(buf, "idle\n"))
Mark Brownc0809092008-04-30 17:05:33 +0100240 mode = REGULATOR_MODE_IDLE;
Mike Rapoportaa61d552009-04-26 11:49:30 +0300241 else if (sysfs_streq(buf, "standby\n"))
Mark Brownc0809092008-04-30 17:05:33 +0100242 mode = REGULATOR_MODE_STANDBY;
243 else {
244 dev_err(dev, "Configuring invalid mode\n");
245 return count;
246 }
247
248 mutex_lock(&data->lock);
249 ret = regulator_set_mode(data->regulator, mode);
250 if (ret == 0)
251 data->mode = mode;
252 else
253 dev_err(dev, "Failed to configure mode: %d\n", ret);
254 mutex_unlock(&data->lock);
255
256 return count;
257}
258
259static DEVICE_ATTR(min_microvolts, 0666, show_min_uV, set_min_uV);
260static DEVICE_ATTR(max_microvolts, 0666, show_max_uV, set_max_uV);
261static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA);
262static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA);
263static DEVICE_ATTR(mode, 0666, show_mode, set_mode);
264
Mark Brownfefdae42009-01-19 13:37:06 +0000265static struct device_attribute *attributes[] = {
Mark Brownc0809092008-04-30 17:05:33 +0100266 &dev_attr_min_microvolts,
267 &dev_attr_max_microvolts,
268 &dev_attr_min_microamps,
269 &dev_attr_max_microamps,
270 &dev_attr_mode,
271};
272
273static int regulator_virtual_consumer_probe(struct platform_device *pdev)
274{
275 char *reg_id = pdev->dev.platform_data;
276 struct virtual_consumer_data *drvdata;
277 int ret, i;
278
279 drvdata = kzalloc(sizeof(struct virtual_consumer_data), GFP_KERNEL);
280 if (drvdata == NULL) {
281 ret = -ENOMEM;
282 goto err;
283 }
284
285 mutex_init(&drvdata->lock);
286
287 drvdata->regulator = regulator_get(&pdev->dev, reg_id);
288 if (IS_ERR(drvdata->regulator)) {
289 ret = PTR_ERR(drvdata->regulator);
Mark Brownd61c3d52009-06-15 20:01:01 +0100290 dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n",
291 reg_id, ret);
Mark Brownc0809092008-04-30 17:05:33 +0100292 goto err;
293 }
294
295 for (i = 0; i < ARRAY_SIZE(attributes); i++) {
296 ret = device_create_file(&pdev->dev, attributes[i]);
297 if (ret != 0)
298 goto err;
299 }
300
301 drvdata->mode = regulator_get_mode(drvdata->regulator);
302
303 platform_set_drvdata(pdev, drvdata);
304
305 return 0;
306
307err:
308 for (i = 0; i < ARRAY_SIZE(attributes); i++)
309 device_remove_file(&pdev->dev, attributes[i]);
310 kfree(drvdata);
311 return ret;
312}
313
314static int regulator_virtual_consumer_remove(struct platform_device *pdev)
315{
316 struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
317 int i;
318
319 for (i = 0; i < ARRAY_SIZE(attributes); i++)
320 device_remove_file(&pdev->dev, attributes[i]);
321 if (drvdata->enabled)
322 regulator_disable(drvdata->regulator);
323 regulator_put(drvdata->regulator);
324
325 kfree(drvdata);
326
327 return 0;
328}
329
330static struct platform_driver regulator_virtual_consumer_driver = {
331 .probe = regulator_virtual_consumer_probe,
332 .remove = regulator_virtual_consumer_remove,
333 .driver = {
334 .name = "reg-virt-consumer",
335 },
336};
337
338
339static int __init regulator_virtual_consumer_init(void)
340{
341 return platform_driver_register(&regulator_virtual_consumer_driver);
342}
343module_init(regulator_virtual_consumer_init);
344
345static void __exit regulator_virtual_consumer_exit(void)
346{
347 platform_driver_unregister(&regulator_virtual_consumer_driver);
348}
349module_exit(regulator_virtual_consumer_exit);
350
351MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
352MODULE_DESCRIPTION("Virtual regulator consumer");
353MODULE_LICENSE("GPL");
Mark Brown38c53c82009-04-28 11:13:55 +0100354MODULE_ALIAS("platform:reg-virt-consumer");