blob: bb292791b88d740347d09656a554a34591954900 [file] [log] [blame]
Brian Swetlandf030d7b2008-09-29 14:07:14 -07001/* arch/arm/mach-msm/vreg.c
2 *
3 * Copyright (C) 2008 Google, Inc.
Pankaj Kumar679671e2012-03-14 19:32:21 +05304 * Copyright (c) 2009-2012 Code Aurora Forum. All rights reserved.
Brian Swetlandf030d7b2008-09-29 14:07:14 -07005 * Author: Brian Swetland <swetland@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/kernel.h>
19#include <linux/device.h>
20#include <linux/init.h>
Justin Paupore0f1f2ac2011-08-25 18:13:50 -070021#include <linux/slab.h>
22#include <linux/list.h>
23#include <linux/mutex.h>
24#include <linux/atomic.h>
Brian Swetlandf030d7b2008-09-29 14:07:14 -070025#include <linux/debugfs.h>
Justin Paupore0f1f2ac2011-08-25 18:13:50 -070026#include <linux/regulator/consumer.h>
Matt Wilson0e441062009-06-29 14:13:04 -050027#include <linux/string.h>
Brian Swetlandf030d7b2008-09-29 14:07:14 -070028#include <mach/vreg.h>
29
30#include "proc_comm.h"
31
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#if defined(CONFIG_MSM_VREG_SWITCH_INVERTED)
33#define VREG_SWITCH_ENABLE 0
34#define VREG_SWITCH_DISABLE 1
35#else
36#define VREG_SWITCH_ENABLE 1
37#define VREG_SWITCH_DISABLE 0
38#endif
39
Brian Swetlandf030d7b2008-09-29 14:07:14 -070040struct vreg {
Justin Paupore0f1f2ac2011-08-25 18:13:50 -070041 struct list_head list;
42 struct mutex lock;
43 const char *name;
44 u64 refcnt;
45 unsigned mv;
46 struct regulator *reg;
Brian Swetlandf030d7b2008-09-29 14:07:14 -070047};
48
Justin Paupore0f1f2ac2011-08-25 18:13:50 -070049static LIST_HEAD(vreg_list);
50static DEFINE_MUTEX(vreg_lock);
Brian Swetlandf030d7b2008-09-29 14:07:14 -070051
Justin Paupore0f1f2ac2011-08-25 18:13:50 -070052#ifdef CONFIG_DEBUG_FS
53static void vreg_add_debugfs(struct vreg *vreg);
54#else
55static inline void vreg_add_debugfs(struct vreg *vreg) { }
56#endif
57
58static struct vreg *vreg_create(const char *id)
59{
60 int rc;
61 struct vreg *vreg;
62
63 vreg = kzalloc(sizeof(*vreg), GFP_KERNEL);
64 if (!vreg) {
65 rc = -ENOMEM;
66 goto error;
67 }
68
69 INIT_LIST_HEAD(&vreg->list);
70 mutex_init(&vreg->lock);
71
72 vreg->reg = regulator_get(NULL, id);
73 if (IS_ERR(vreg->reg)) {
74 rc = PTR_ERR(vreg->reg);
75 goto free_vreg;
76 }
77
78 vreg->name = kstrdup(id, GFP_KERNEL);
79 if (!vreg->name) {
80 rc = -ENOMEM;
81 goto put_reg;
82 }
83
84 list_add_tail(&vreg->list, &vreg_list);
85 vreg_add_debugfs(vreg);
86
87 return vreg;
88
89put_reg:
90 regulator_put(vreg->reg);
91free_vreg:
92 kfree(vreg);
93error:
94 return ERR_PTR(rc);
95}
96
97static void vreg_destroy(struct vreg *vreg)
98{
99 if (!vreg)
100 return;
101
102 if (vreg->refcnt)
103 regulator_disable(vreg->reg);
104
105 kfree(vreg->name);
106 regulator_put(vreg->reg);
107 kfree(vreg);
108}
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700109
110struct vreg *vreg_get(struct device *dev, const char *id)
111{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700112 struct vreg *vreg = NULL;
113
114 if (!id)
115 return ERR_PTR(-EINVAL);
116
117 mutex_lock(&vreg_lock);
118 list_for_each_entry(vreg, &vreg_list, list) {
119 if (!strncmp(vreg->name, id, 10))
120 goto ret;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700121 }
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700122
123 vreg = vreg_create(id);
124
125ret:
126 mutex_unlock(&vreg_lock);
127 return vreg;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700128}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129EXPORT_SYMBOL(vreg_get);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700130
131void vreg_put(struct vreg *vreg)
132{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700133 kfree(vreg->name);
134 regulator_put(vreg->reg);
Pankaj Kumar679671e2012-03-14 19:32:21 +0530135 list_del(&vreg->list);
136 kfree(vreg);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700137}
138
139int vreg_enable(struct vreg *vreg)
140{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700141 int rc = 0;
142 if (!vreg)
143 return -ENODEV;
Steve Muckle4783de92008-12-12 19:33:55 -0500144
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700145 mutex_lock(&vreg->lock);
146 if (vreg->refcnt == 0) {
147 rc = regulator_enable(vreg->reg);
148 if (!rc)
149 vreg->refcnt++;
150 } else {
151 rc = 0;
152 if (vreg->refcnt < UINT_MAX)
153 vreg->refcnt++;
154 }
155 mutex_unlock(&vreg->lock);
Matt Wilson0e441062009-06-29 14:13:04 -0500156
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700157 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700158}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159EXPORT_SYMBOL(vreg_enable);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700160
Steve Muckle4783de92008-12-12 19:33:55 -0500161int vreg_disable(struct vreg *vreg)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700162{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700163 int rc = 0;
164 if (!vreg)
165 return -ENODEV;
Steve Muckle4783de92008-12-12 19:33:55 -0500166
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700167 mutex_lock(&vreg->lock);
168 if (vreg->refcnt == 0) {
169 pr_warn("%s: unbalanced disables for vreg %s\n",
170 __func__, vreg->name);
171 rc = -EINVAL;
172 } else if (vreg->refcnt == 1) {
173 rc = regulator_disable(vreg->reg);
174 if (!rc)
175 vreg->refcnt--;
176 } else {
177 rc = 0;
Matt Wilson0e441062009-06-29 14:13:04 -0500178 vreg->refcnt--;
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700179 }
180 mutex_unlock(&vreg->lock);
Matt Wilson0e441062009-06-29 14:13:04 -0500181
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700182 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700183}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184EXPORT_SYMBOL(vreg_disable);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700185
186int vreg_set_level(struct vreg *vreg, unsigned mv)
187{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700188 unsigned uv;
189 int rc;
Steve Muckle4783de92008-12-12 19:33:55 -0500190
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700191 if (!vreg)
192 return -EINVAL;
193
194 if (mv > (UINT_MAX / 1000))
195 return -ERANGE;
196
197 uv = mv * 1000;
198
199 mutex_lock(&vreg->lock);
200 rc = regulator_set_voltage(vreg->reg, uv, uv);
201 if (!rc)
202 vreg->mv = mv;
203 mutex_unlock(&vreg->lock);
204
205 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700206}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207EXPORT_SYMBOL(vreg_set_level);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700208
209#if defined(CONFIG_DEBUG_FS)
210
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700211static int vreg_debug_enabled_set(void *data, u64 val)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700212{
Steve Muckle4783de92008-12-12 19:33:55 -0500213 struct vreg *vreg = data;
214
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700215 if (val == 0)
216 return vreg_disable(vreg);
217 else if (val == 1)
218 return vreg_enable(vreg);
Steve Muckle4783de92008-12-12 19:33:55 -0500219 else
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700220 return -EINVAL;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700221}
222
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700223static int vreg_debug_enabled_get(void *data, u64 *val)
Matt Wilson0e441062009-06-29 14:13:04 -0500224{
225 struct vreg *vreg = data;
226
227 *val = vreg->refcnt;
228
229 return 0;
230}
231
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700232static int vreg_debug_voltage_set(void *data, u64 val)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700233{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700234 struct vreg *vreg = data;
235 return vreg_set_level(vreg, val);
236}
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700237
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700238static int vreg_debug_voltage_get(void *data, u64 *val)
239{
240 struct vreg *vreg = data;
241 *val = vreg->mv;
242 return 0;
243}
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700244
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700245DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_enabled, vreg_debug_enabled_get,
246 vreg_debug_enabled_set, "%llu");
247DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_voltage, vreg_debug_voltage_get,
248 vreg_debug_voltage_set, "%llu");
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700249
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700250static struct dentry *root;
251
252static void vreg_add_debugfs(struct vreg *vreg)
253{
254 struct dentry *dir;
255
256 if (!root)
257 return;
258
259 dir = debugfs_create_dir(vreg->name, root);
260
261 if (IS_ERR_OR_NULL(dir))
262 goto err;
263
264 if (IS_ERR_OR_NULL(debugfs_create_file("enabled", 0644, dir, vreg,
265 &vreg_debug_enabled)))
266 goto destroy;
267
268 if (IS_ERR_OR_NULL(debugfs_create_file("voltage", 0644, dir, vreg,
269 &vreg_debug_voltage)))
270 goto destroy;
271
272 return;
273
274destroy:
275 debugfs_remove_recursive(dir);
276err:
277 pr_warn("%s: could not create debugfs for vreg %s\n",
278 __func__, vreg->name);
279}
280
281static int __devinit vreg_debug_init(void)
282{
283 root = debugfs_create_dir("vreg", NULL);
284
285 if (IS_ERR_OR_NULL(root)) {
286 pr_debug("%s: error initializing debugfs: %ld - "
287 "disabling debugfs\n",
288 __func__, root ? PTR_ERR(root) : 0);
289 root = NULL;
Matt Wilson0e441062009-06-29 14:13:04 -0500290 }
291
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700292 return 0;
293}
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700294static void __devexit vreg_debug_exit(void)
295{
296 if (root)
297 debugfs_remove_recursive(root);
298 root = NULL;
299}
300#else
301static inline int __init vreg_debug_init(void) { return 0; }
302static inline void __exit vreg_debug_exit(void) { return 0; }
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700303#endif
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700304
305static int __init vreg_init(void)
306{
307 return vreg_debug_init();
308}
309module_init(vreg_init);
310
311static void __exit vreg_exit(void)
312{
313 struct vreg *vreg, *next;
314 vreg_debug_exit();
315
316 mutex_lock(&vreg_lock);
317 list_for_each_entry_safe(vreg, next, &vreg_list, list)
318 vreg_destroy(vreg);
319 mutex_unlock(&vreg_lock);
320}
321module_exit(vreg_exit);
322
323MODULE_LICENSE("GPL v2");
324MODULE_DESCRIPTION("vreg.c regulator shim");
325MODULE_VERSION("1.0");