msm: Convert vreg.c to regulator API
Convert vreg.c to call into the regulator framework. This allows
out-of-tree and proprietary consumers of vreg.c to continue working
within the regulator framework.
Change-Id: I1ea414aa6040466c42e12c28150b1dae69447c13
Signed-off-by: Justin Paupore <jpaupore@codeaurora.org>
Signed-off-by: Pankaj Kumar <pakuma@codeaurora.org>
diff --git a/arch/arm/mach-msm/vreg.c b/arch/arm/mach-msm/vreg.c
index 8f782a9..cffa5c7 100644
--- a/arch/arm/mach-msm/vreg.c
+++ b/arch/arm/mach-msm/vreg.c
@@ -18,7 +18,12 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <mach/vreg.h>
@@ -33,173 +38,187 @@
#endif
struct vreg {
- const char *name;
- unsigned id;
- int status;
- unsigned refcnt;
+ struct list_head list;
+ struct mutex lock;
+ const char *name;
+ u64 refcnt;
+ unsigned mv;
+ struct regulator *reg;
};
-#define VREG(_name, _id, _status, _refcnt) \
- { .name = _name, .id = _id, .status = _status, .refcnt = _refcnt }
+static LIST_HEAD(vreg_list);
+static DEFINE_MUTEX(vreg_lock);
-static struct vreg vregs[] = {
- VREG("msma", 0, 0, 0),
- VREG("msmp", 1, 0, 0),
- VREG("msme1", 2, 0, 0),
- VREG("msmc1", 3, 0, 0),
- VREG("msmc2", 4, 0, 0),
- VREG("gp3", 5, 0, 0),
- VREG("msme2", 6, 0, 0),
- VREG("gp4", 7, 0, 0),
- VREG("gp1", 8, 0, 0),
- VREG("tcxo", 9, 0, 0),
- VREG("pa", 10, 0, 0),
- VREG("rftx", 11, 0, 0),
- VREG("rfrx1", 12, 0, 0),
- VREG("rfrx2", 13, 0, 0),
- VREG("synt", 14, 0, 0),
- VREG("wlan", 15, 0, 0),
- VREG("usb", 16, 0, 0),
- VREG("boost", 17, 0, 0),
- VREG("mmc", 18, 0, 0),
- VREG("ruim", 19, 0, 0),
- VREG("msmc0", 20, 0, 0),
- VREG("gp2", 21, 0, 0),
- VREG("gp5", 22, 0, 0),
- VREG("gp6", 23, 0, 0),
- VREG("rf", 24, 0, 0),
- VREG("rf_vco", 26, 0, 0),
- VREG("mpll", 27, 0, 0),
- VREG("s2", 28, 0, 0),
- VREG("s3", 29, 0, 0),
- VREG("rfubm", 30, 0, 0),
- VREG("ncp", 31, 0, 0),
- VREG("gp7", 32, 0, 0),
- VREG("gp8", 33, 0, 0),
- VREG("gp9", 34, 0, 0),
- VREG("gp10", 35, 0, 0),
- VREG("gp11", 36, 0, 0),
- VREG("gp12", 37, 0, 0),
- VREG("gp13", 38, 0, 0),
- VREG("gp14", 39, 0, 0),
- VREG("gp15", 40, 0, 0),
- VREG("gp16", 41, 0, 0),
- VREG("gp17", 42, 0, 0),
- VREG("s4", 43, 0, 0),
- VREG("usb2", 44, 0, 0),
- VREG("wlan2", 45, 0, 0),
- VREG("xo_out", 46, 0, 0),
- VREG("lvsw0", 47, 0, 0),
- VREG("lvsw1", 48, 0, 0),
- VREG("mddi", 49, 0, 0),
- VREG("pllx", 50, 0, 0),
- VREG("wlan3", 51, 0, 0),
- VREG("emmc", 52, 0, 0),
- VREG("wlan_tcx0", 53, 0, 0),
- VREG("usim2", 54, 0, 0),
- VREG("usim", 55, 0, 0),
- VREG("bt", 56, 0, 0),
- VREG("wlan4", 57, 0, 0),
-};
+#ifdef CONFIG_DEBUG_FS
+static void vreg_add_debugfs(struct vreg *vreg);
+#else
+static inline void vreg_add_debugfs(struct vreg *vreg) { }
+#endif
+
+static struct vreg *vreg_create(const char *id)
+{
+ int rc;
+ struct vreg *vreg;
+
+ vreg = kzalloc(sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ INIT_LIST_HEAD(&vreg->list);
+ mutex_init(&vreg->lock);
+
+ vreg->reg = regulator_get(NULL, id);
+ if (IS_ERR(vreg->reg)) {
+ rc = PTR_ERR(vreg->reg);
+ goto free_vreg;
+ }
+
+ vreg->name = kstrdup(id, GFP_KERNEL);
+ if (!vreg->name) {
+ rc = -ENOMEM;
+ goto put_reg;
+ }
+
+ list_add_tail(&vreg->list, &vreg_list);
+ vreg_add_debugfs(vreg);
+
+ return vreg;
+
+put_reg:
+ regulator_put(vreg->reg);
+free_vreg:
+ kfree(vreg);
+error:
+ return ERR_PTR(rc);
+}
+
+static void vreg_destroy(struct vreg *vreg)
+{
+ if (!vreg)
+ return;
+
+ if (vreg->refcnt)
+ regulator_disable(vreg->reg);
+
+ kfree(vreg->name);
+ regulator_put(vreg->reg);
+ kfree(vreg);
+}
struct vreg *vreg_get(struct device *dev, const char *id)
{
- int n;
- for (n = 0; n < ARRAY_SIZE(vregs); n++) {
- if (!strcmp(vregs[n].name, id))
- return vregs + n;
+ struct vreg *vreg = NULL;
+
+ if (!id)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&vreg_lock);
+ list_for_each_entry(vreg, &vreg_list, list) {
+ if (!strncmp(vreg->name, id, 10))
+ goto ret;
}
- return ERR_PTR(-ENOENT);
+
+ vreg = vreg_create(id);
+
+ret:
+ mutex_unlock(&vreg_lock);
+ return vreg;
}
EXPORT_SYMBOL(vreg_get);
void vreg_put(struct vreg *vreg)
{
+ kfree(vreg->name);
+ regulator_put(vreg->reg);
}
int vreg_enable(struct vreg *vreg)
{
- unsigned id = vreg->id;
- int enable = VREG_SWITCH_ENABLE;
+ int rc = 0;
+ if (!vreg)
+ return -ENODEV;
- if (vreg->refcnt == 0)
- vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable);
+ mutex_lock(&vreg->lock);
+ if (vreg->refcnt == 0) {
+ rc = regulator_enable(vreg->reg);
+ if (!rc)
+ vreg->refcnt++;
+ } else {
+ rc = 0;
+ if (vreg->refcnt < UINT_MAX)
+ vreg->refcnt++;
+ }
+ mutex_unlock(&vreg->lock);
- if ((vreg->refcnt < UINT_MAX) && (!vreg->status))
- vreg->refcnt++;
-
- return vreg->status;
+ return rc;
}
EXPORT_SYMBOL(vreg_enable);
int vreg_disable(struct vreg *vreg)
{
- unsigned id = vreg->id;
- int disable = VREG_SWITCH_DISABLE;
+ int rc = 0;
+ if (!vreg)
+ return -ENODEV;
- if (!vreg->refcnt)
- return 0;
-
- if (vreg->refcnt == 1)
- vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &disable);
-
- if (!vreg->status)
+ mutex_lock(&vreg->lock);
+ if (vreg->refcnt == 0) {
+ pr_warn("%s: unbalanced disables for vreg %s\n",
+ __func__, vreg->name);
+ rc = -EINVAL;
+ } else if (vreg->refcnt == 1) {
+ rc = regulator_disable(vreg->reg);
+ if (!rc)
+ vreg->refcnt--;
+ } else {
+ rc = 0;
vreg->refcnt--;
+ }
+ mutex_unlock(&vreg->lock);
- return vreg->status;
+ return rc;
}
EXPORT_SYMBOL(vreg_disable);
int vreg_set_level(struct vreg *vreg, unsigned mv)
{
- unsigned id = vreg->id;
+ unsigned uv;
+ int rc;
- vreg->status = msm_proc_comm(PCOM_VREG_SET_LEVEL, &id, &mv);
- return vreg->status;
+ if (!vreg)
+ return -EINVAL;
+
+ if (mv > (UINT_MAX / 1000))
+ return -ERANGE;
+
+ uv = mv * 1000;
+
+ mutex_lock(&vreg->lock);
+ rc = regulator_set_voltage(vreg->reg, uv, uv);
+ if (!rc)
+ vreg->mv = mv;
+ mutex_unlock(&vreg->lock);
+
+ return rc;
}
EXPORT_SYMBOL(vreg_set_level);
#if defined(CONFIG_DEBUG_FS)
-static int vreg_debug_set(void *data, u64 val)
-{
- struct vreg *vreg = data;
- switch (val) {
- case 0:
- vreg_disable(vreg);
- break;
- case 1:
- vreg_enable(vreg);
- break;
- default:
- vreg_set_level(vreg, val);
- break;
- }
- return 0;
-}
-
-static int vreg_debug_get(void *data, u64 *val)
+static int vreg_debug_enabled_set(void *data, u64 val)
{
struct vreg *vreg = data;
- if (!vreg->status)
- *val = 0;
+ if (val == 0)
+ return vreg_disable(vreg);
+ else if (val == 1)
+ return vreg_enable(vreg);
else
- *val = 1;
-
- return 0;
+ return -EINVAL;
}
-static int vreg_debug_count_set(void *data, u64 val)
-{
- struct vreg *vreg = data;
- if (val > UINT_MAX)
- val = UINT_MAX;
- vreg->refcnt = val;
- return 0;
-}
-
-static int vreg_debug_count_get(void *data, u64 *val)
+static int vreg_debug_enabled_get(void *data, u64 *val)
{
struct vreg *vreg = data;
@@ -208,33 +227,97 @@
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(vreg_fops, vreg_debug_get, vreg_debug_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(vreg_count_fops, vreg_debug_count_get,
- vreg_debug_count_set, "%llu\n");
-
-static int __init vreg_debug_init(void)
+static int vreg_debug_voltage_set(void *data, u64 val)
{
- struct dentry *dent;
- int n;
- char name[32];
- const char *refcnt_name = "_refcnt";
+ struct vreg *vreg = data;
+ return vreg_set_level(vreg, val);
+}
- dent = debugfs_create_dir("vreg", 0);
- if (IS_ERR(dent))
- return 0;
+static int vreg_debug_voltage_get(void *data, u64 *val)
+{
+ struct vreg *vreg = data;
+ *val = vreg->mv;
+ return 0;
+}
- for (n = 0; n < ARRAY_SIZE(vregs); n++) {
- (void) debugfs_create_file(vregs[n].name, 0644,
- dent, vregs + n, &vreg_fops);
+DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_enabled, vreg_debug_enabled_get,
+ vreg_debug_enabled_set, "%llu");
+DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_voltage, vreg_debug_voltage_get,
+ vreg_debug_voltage_set, "%llu");
- strlcpy(name, vregs[n].name, sizeof(name));
- strlcat(name, refcnt_name, sizeof(name));
- (void) debugfs_create_file(name, 0644,
- dent, vregs + n, &vreg_count_fops);
+static struct dentry *root;
+
+static void vreg_add_debugfs(struct vreg *vreg)
+{
+ struct dentry *dir;
+
+ if (!root)
+ return;
+
+ dir = debugfs_create_dir(vreg->name, root);
+
+ if (IS_ERR_OR_NULL(dir))
+ goto err;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("enabled", 0644, dir, vreg,
+ &vreg_debug_enabled)))
+ goto destroy;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("voltage", 0644, dir, vreg,
+ &vreg_debug_voltage)))
+ goto destroy;
+
+ return;
+
+destroy:
+ debugfs_remove_recursive(dir);
+err:
+ pr_warn("%s: could not create debugfs for vreg %s\n",
+ __func__, vreg->name);
+}
+
+static int __devinit vreg_debug_init(void)
+{
+ root = debugfs_create_dir("vreg", NULL);
+
+ if (IS_ERR_OR_NULL(root)) {
+ pr_debug("%s: error initializing debugfs: %ld - "
+ "disabling debugfs\n",
+ __func__, root ? PTR_ERR(root) : 0);
+ root = NULL;
}
return 0;
}
-
-device_initcall(vreg_debug_init);
+static void __devexit vreg_debug_exit(void)
+{
+ if (root)
+ debugfs_remove_recursive(root);
+ root = NULL;
+}
+#else
+static inline int __init vreg_debug_init(void) { return 0; }
+static inline void __exit vreg_debug_exit(void) { return 0; }
#endif
+
+static int __init vreg_init(void)
+{
+ return vreg_debug_init();
+}
+module_init(vreg_init);
+
+static void __exit vreg_exit(void)
+{
+ struct vreg *vreg, *next;
+ vreg_debug_exit();
+
+ mutex_lock(&vreg_lock);
+ list_for_each_entry_safe(vreg, next, &vreg_list, list)
+ vreg_destroy(vreg);
+ mutex_unlock(&vreg_lock);
+}
+module_exit(vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("vreg.c regulator shim");
+MODULE_VERSION("1.0");