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");