blob: cffa5c7d888bfbf24ea6e9bca92ab12bef37a6c6 [file] [log] [blame]
Brian Swetlandf030d7b2008-09-29 14:07:14 -07001/* arch/arm/mach-msm/vreg.c
2 *
3 * Copyright (C) 2008 Google, Inc.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 * Copyright (c) 2009-2011 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);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700135}
136
137int vreg_enable(struct vreg *vreg)
138{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700139 int rc = 0;
140 if (!vreg)
141 return -ENODEV;
Steve Muckle4783de92008-12-12 19:33:55 -0500142
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700143 mutex_lock(&vreg->lock);
144 if (vreg->refcnt == 0) {
145 rc = regulator_enable(vreg->reg);
146 if (!rc)
147 vreg->refcnt++;
148 } else {
149 rc = 0;
150 if (vreg->refcnt < UINT_MAX)
151 vreg->refcnt++;
152 }
153 mutex_unlock(&vreg->lock);
Matt Wilson0e441062009-06-29 14:13:04 -0500154
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700155 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700156}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157EXPORT_SYMBOL(vreg_enable);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700158
Steve Muckle4783de92008-12-12 19:33:55 -0500159int vreg_disable(struct vreg *vreg)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700160{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700161 int rc = 0;
162 if (!vreg)
163 return -ENODEV;
Steve Muckle4783de92008-12-12 19:33:55 -0500164
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700165 mutex_lock(&vreg->lock);
166 if (vreg->refcnt == 0) {
167 pr_warn("%s: unbalanced disables for vreg %s\n",
168 __func__, vreg->name);
169 rc = -EINVAL;
170 } else if (vreg->refcnt == 1) {
171 rc = regulator_disable(vreg->reg);
172 if (!rc)
173 vreg->refcnt--;
174 } else {
175 rc = 0;
Matt Wilson0e441062009-06-29 14:13:04 -0500176 vreg->refcnt--;
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700177 }
178 mutex_unlock(&vreg->lock);
Matt Wilson0e441062009-06-29 14:13:04 -0500179
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700180 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700181}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182EXPORT_SYMBOL(vreg_disable);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700183
184int vreg_set_level(struct vreg *vreg, unsigned mv)
185{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700186 unsigned uv;
187 int rc;
Steve Muckle4783de92008-12-12 19:33:55 -0500188
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700189 if (!vreg)
190 return -EINVAL;
191
192 if (mv > (UINT_MAX / 1000))
193 return -ERANGE;
194
195 uv = mv * 1000;
196
197 mutex_lock(&vreg->lock);
198 rc = regulator_set_voltage(vreg->reg, uv, uv);
199 if (!rc)
200 vreg->mv = mv;
201 mutex_unlock(&vreg->lock);
202
203 return rc;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700204}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205EXPORT_SYMBOL(vreg_set_level);
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700206
207#if defined(CONFIG_DEBUG_FS)
208
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700209static int vreg_debug_enabled_set(void *data, u64 val)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700210{
Steve Muckle4783de92008-12-12 19:33:55 -0500211 struct vreg *vreg = data;
212
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700213 if (val == 0)
214 return vreg_disable(vreg);
215 else if (val == 1)
216 return vreg_enable(vreg);
Steve Muckle4783de92008-12-12 19:33:55 -0500217 else
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700218 return -EINVAL;
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700219}
220
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700221static int vreg_debug_enabled_get(void *data, u64 *val)
Matt Wilson0e441062009-06-29 14:13:04 -0500222{
223 struct vreg *vreg = data;
224
225 *val = vreg->refcnt;
226
227 return 0;
228}
229
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700230static int vreg_debug_voltage_set(void *data, u64 val)
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700231{
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700232 struct vreg *vreg = data;
233 return vreg_set_level(vreg, val);
234}
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700235
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700236static int vreg_debug_voltage_get(void *data, u64 *val)
237{
238 struct vreg *vreg = data;
239 *val = vreg->mv;
240 return 0;
241}
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700242
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700243DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_enabled, vreg_debug_enabled_get,
244 vreg_debug_enabled_set, "%llu");
245DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_voltage, vreg_debug_voltage_get,
246 vreg_debug_voltage_set, "%llu");
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700247
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700248static struct dentry *root;
249
250static void vreg_add_debugfs(struct vreg *vreg)
251{
252 struct dentry *dir;
253
254 if (!root)
255 return;
256
257 dir = debugfs_create_dir(vreg->name, root);
258
259 if (IS_ERR_OR_NULL(dir))
260 goto err;
261
262 if (IS_ERR_OR_NULL(debugfs_create_file("enabled", 0644, dir, vreg,
263 &vreg_debug_enabled)))
264 goto destroy;
265
266 if (IS_ERR_OR_NULL(debugfs_create_file("voltage", 0644, dir, vreg,
267 &vreg_debug_voltage)))
268 goto destroy;
269
270 return;
271
272destroy:
273 debugfs_remove_recursive(dir);
274err:
275 pr_warn("%s: could not create debugfs for vreg %s\n",
276 __func__, vreg->name);
277}
278
279static int __devinit vreg_debug_init(void)
280{
281 root = debugfs_create_dir("vreg", NULL);
282
283 if (IS_ERR_OR_NULL(root)) {
284 pr_debug("%s: error initializing debugfs: %ld - "
285 "disabling debugfs\n",
286 __func__, root ? PTR_ERR(root) : 0);
287 root = NULL;
Matt Wilson0e441062009-06-29 14:13:04 -0500288 }
289
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700290 return 0;
291}
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700292static void __devexit vreg_debug_exit(void)
293{
294 if (root)
295 debugfs_remove_recursive(root);
296 root = NULL;
297}
298#else
299static inline int __init vreg_debug_init(void) { return 0; }
300static inline void __exit vreg_debug_exit(void) { return 0; }
Brian Swetlandf030d7b2008-09-29 14:07:14 -0700301#endif
Justin Paupore0f1f2ac2011-08-25 18:13:50 -0700302
303static int __init vreg_init(void)
304{
305 return vreg_debug_init();
306}
307module_init(vreg_init);
308
309static void __exit vreg_exit(void)
310{
311 struct vreg *vreg, *next;
312 vreg_debug_exit();
313
314 mutex_lock(&vreg_lock);
315 list_for_each_entry_safe(vreg, next, &vreg_list, list)
316 vreg_destroy(vreg);
317 mutex_unlock(&vreg_lock);
318}
319module_exit(vreg_exit);
320
321MODULE_LICENSE("GPL v2");
322MODULE_DESCRIPTION("vreg.c regulator shim");
323MODULE_VERSION("1.0");