1 /*
2  * wm8994-regulator.c  --  Regulator driver for the WM8994
3  *
4  * Copyright 2009 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 modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  */
13 
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/init.h>
17 #include <linux/bitops.h>
18 #include <linux/err.h>
19 #include <linux/platform_device.h>
20 #include <linux/regulator/driver.h>
21 #include <linux/gpio.h>
22 #include <linux/slab.h>
23 
24 #include <linux/mfd/wm8994/core.h>
25 #include <linux/mfd/wm8994/registers.h>
26 #include <linux/mfd/wm8994/pdata.h>
27 
28 struct wm8994_ldo {
29 	struct regulator_dev *regulator;
30 	struct wm8994 *wm8994;
31 };
32 
33 #define WM8994_LDO1_MAX_SELECTOR 0x7
34 #define WM8994_LDO2_MAX_SELECTOR 0x3
35 
36 static struct regulator_ops wm8994_ldo1_ops = {
37 	.list_voltage = regulator_list_voltage_linear,
38 	.map_voltage = regulator_map_voltage_linear,
39 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
40 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
41 };
42 
43 static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
44 				    unsigned int selector)
45 {
46 	struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
47 
48 	if (selector > WM8994_LDO2_MAX_SELECTOR)
49 		return -EINVAL;
50 
51 	switch (ldo->wm8994->type) {
52 	case WM8994:
53 		return (selector * 100000) + 900000;
54 	case WM8958:
55 		return (selector * 100000) + 1000000;
56 	case WM1811:
57 		switch (selector) {
58 		case 0:
59 			return -EINVAL;
60 		default:
61 			return (selector * 100000) + 950000;
62 		}
63 		break;
64 	default:
65 		return -EINVAL;
66 	}
67 }
68 
69 static struct regulator_ops wm8994_ldo2_ops = {
70 	.list_voltage = wm8994_ldo2_list_voltage,
71 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
72 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
73 };
74 
75 static const struct regulator_desc wm8994_ldo_desc[] = {
76 	{
77 		.name = "LDO1",
78 		.id = 1,
79 		.type = REGULATOR_VOLTAGE,
80 		.n_voltages = WM8994_LDO1_MAX_SELECTOR + 1,
81 		.vsel_reg = WM8994_LDO_1,
82 		.vsel_mask = WM8994_LDO1_VSEL_MASK,
83 		.ops = &wm8994_ldo1_ops,
84 		.min_uV = 2400000,
85 		.uV_step = 100000,
86 		.enable_time = 3000,
87 		.owner = THIS_MODULE,
88 	},
89 	{
90 		.name = "LDO2",
91 		.id = 2,
92 		.type = REGULATOR_VOLTAGE,
93 		.n_voltages = WM8994_LDO2_MAX_SELECTOR + 1,
94 		.vsel_reg = WM8994_LDO_2,
95 		.vsel_mask = WM8994_LDO2_VSEL_MASK,
96 		.ops = &wm8994_ldo2_ops,
97 		.enable_time = 3000,
98 		.owner = THIS_MODULE,
99 	},
100 };
101 
102 static int wm8994_ldo_probe(struct platform_device *pdev)
103 {
104 	struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
105 	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
106 	int id = pdev->id % ARRAY_SIZE(pdata->ldo);
107 	struct regulator_config config = { };
108 	struct wm8994_ldo *ldo;
109 	int ret;
110 
111 	dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
112 
113 	ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL);
114 	if (ldo == NULL) {
115 		dev_err(&pdev->dev, "Unable to allocate private data\n");
116 		return -ENOMEM;
117 	}
118 
119 	ldo->wm8994 = wm8994;
120 
121 	config.dev = wm8994->dev;
122 	config.driver_data = ldo;
123 	config.regmap = wm8994->regmap;
124 	if (pdata) {
125 		config.init_data = pdata->ldo[id].init_data;
126 		config.ena_gpio = pdata->ldo[id].enable;
127 	}
128 
129 	ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config);
130 	if (IS_ERR(ldo->regulator)) {
131 		ret = PTR_ERR(ldo->regulator);
132 		dev_err(wm8994->dev, "Failed to register LDO%d: %d\n",
133 			id + 1, ret);
134 		goto err;
135 	}
136 
137 	platform_set_drvdata(pdev, ldo);
138 
139 	return 0;
140 
141 err:
142 	return ret;
143 }
144 
145 static int wm8994_ldo_remove(struct platform_device *pdev)
146 {
147 	struct wm8994_ldo *ldo = platform_get_drvdata(pdev);
148 
149 	platform_set_drvdata(pdev, NULL);
150 
151 	regulator_unregister(ldo->regulator);
152 
153 	return 0;
154 }
155 
156 static struct platform_driver wm8994_ldo_driver = {
157 	.probe = wm8994_ldo_probe,
158 	.remove = wm8994_ldo_remove,
159 	.driver		= {
160 		.name	= "wm8994-ldo",
161 		.owner	= THIS_MODULE,
162 	},
163 };
164 
165 module_platform_driver(wm8994_ldo_driver);
166 
167 /* Module information */
168 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
169 MODULE_DESCRIPTION("WM8994 LDO driver");
170 MODULE_LICENSE("GPL");
171 MODULE_ALIAS("platform:wm8994-ldo");
172