blob: 85c59726f3c8ba554766a9362a49c95e9ea0d4a0 [file] [log] [blame]
Michael Bohan9b7ac512012-02-24 14:42:44 -08001/*
David Collins8839df22012-10-15 12:04:18 -07002 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
Michael Bohan9b7ac512012-02-24 14:42:44 -08003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/module.h>
17#include <linux/err.h>
18#include <linux/kernel.h>
David Collins8839df22012-10-15 12:04:18 -070019#include <linux/of.h>
20#include <linux/of_device.h>
Michael Bohan9b7ac512012-02-24 14:42:44 -080021#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/types.h>
David Collins8839df22012-10-15 12:04:18 -070024#include <linux/regulator/driver.h>
25#include <linux/regulator/of_regulator.h>
26#include <linux/regulator/stub-regulator.h>
Michael Bohan9b7ac512012-02-24 14:42:44 -080027
28#define STUB_REGULATOR_MAX_NAME 40
29
30struct regulator_stub {
31 struct regulator_desc rdesc;
32 struct regulator_dev *rdev;
33 int voltage;
34 bool enabled;
35 int mode;
36 int hpm_min_load;
37 int system_uA;
38 char name[STUB_REGULATOR_MAX_NAME];
39};
40
41static int regulator_stub_set_voltage(struct regulator_dev *rdev, int min_uV,
42 int max_uV, unsigned *selector)
43{
44 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
45 vreg_priv->voltage = min_uV;
46 return 0;
47}
48
49static int regulator_stub_get_voltage(struct regulator_dev *rdev)
50{
51 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
52 return vreg_priv->voltage;
53}
54
55static int regulator_stub_list_voltage(struct regulator_dev *rdev,
56 unsigned selector)
57{
58 struct regulation_constraints *constraints = rdev->constraints;
59
60 if (selector >= 2)
61 return -EINVAL;
62 else if (selector == 0)
63 return constraints->min_uV;
64 else
65 return constraints->max_uV;
66}
67
68static unsigned int regulator_stub_get_mode(struct regulator_dev *rdev)
69{
70 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
71 return vreg_priv->mode;
72}
73
74static int regulator_stub_set_mode(struct regulator_dev *rdev,
75 unsigned int mode)
76{
77 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
78
79 if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
80 dev_err(&rdev->dev, "%s: invalid mode requested %u\n",
81 __func__, mode);
82 return -EINVAL;
83 }
84 vreg_priv->mode = mode;
85 return 0;
86}
87
88static unsigned int regulator_stub_get_optimum_mode(struct regulator_dev *rdev,
89 int input_uV, int output_uV, int load_uA)
90{
91 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
92 unsigned int mode;
93
94 if (load_uA + vreg_priv->system_uA >= vreg_priv->hpm_min_load)
95 mode = REGULATOR_MODE_NORMAL;
96 else
97 mode = REGULATOR_MODE_IDLE;
98
99 return mode;
100}
101
102static int regulator_stub_enable(struct regulator_dev *rdev)
103{
104 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
105 vreg_priv->enabled = true;
106 return 0;
107}
108
109static int regulator_stub_disable(struct regulator_dev *rdev)
110{
111 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
112 vreg_priv->enabled = false;
113 return 0;
114}
115
116static int regulator_stub_is_enabled(struct regulator_dev *rdev)
117{
118 struct regulator_stub *vreg_priv = rdev_get_drvdata(rdev);
119 return vreg_priv->enabled;
120}
121
122/* Real regulator operations. */
123static struct regulator_ops regulator_stub_ops = {
124 .enable = regulator_stub_enable,
125 .disable = regulator_stub_disable,
126 .is_enabled = regulator_stub_is_enabled,
127 .set_voltage = regulator_stub_set_voltage,
128 .get_voltage = regulator_stub_get_voltage,
129 .list_voltage = regulator_stub_list_voltage,
130 .set_mode = regulator_stub_set_mode,
131 .get_mode = regulator_stub_get_mode,
132 .get_optimum_mode = regulator_stub_get_optimum_mode,
133};
134
135static void regulator_stub_cleanup(struct regulator_stub *vreg_priv)
136{
137 if (vreg_priv && vreg_priv->rdev)
138 regulator_unregister(vreg_priv->rdev);
139 kfree(vreg_priv);
140}
141
142static int __devinit regulator_stub_probe(struct platform_device *pdev)
143{
David Collins8839df22012-10-15 12:04:18 -0700144 struct regulator_init_data *init_data = NULL;
145 struct device *dev = &pdev->dev;
Michael Bohan9b7ac512012-02-24 14:42:44 -0800146 struct stub_regulator_pdata *vreg_pdata;
147 struct regulator_desc *rdesc;
148 struct regulator_stub *vreg_priv;
149 int rc;
150
Michael Bohan9b7ac512012-02-24 14:42:44 -0800151 vreg_priv = kzalloc(sizeof(*vreg_priv), GFP_KERNEL);
152 if (!vreg_priv) {
David Collins8839df22012-10-15 12:04:18 -0700153 dev_err(dev, "%s: Unable to allocate memory\n",
Michael Bohan9b7ac512012-02-24 14:42:44 -0800154 __func__);
155 return -ENOMEM;
156 }
David Collins8839df22012-10-15 12:04:18 -0700157
158 if (dev->of_node) {
159 /* Use device tree. */
160 init_data = of_get_regulator_init_data(dev,
161 dev->of_node);
162 if (!init_data) {
163 dev_err(dev, "%s: unable to allocate memory\n",
164 __func__);
165 rc = -ENOMEM;
166 goto err_probe;
167 }
168
169 if (init_data->constraints.name == NULL) {
170 dev_err(dev, "%s: regulator name not specified\n",
171 __func__);
172 rc = -EINVAL;
173 goto err_probe;
174 }
175
176 if (of_get_property(dev->of_node, "parent-supply", NULL))
177 init_data->supply_regulator = "parent";
178
179 of_property_read_u32(dev->of_node, "qcom,system-load",
180 &vreg_priv->system_uA);
181 of_property_read_u32(dev->of_node, "qcom,hpm-min-load",
182 &vreg_priv->hpm_min_load);
183
184 init_data->constraints.input_uV = init_data->constraints.max_uV;
185
186 init_data->constraints.valid_ops_mask
187 |= REGULATOR_CHANGE_STATUS;
188 init_data->constraints.valid_ops_mask
189 |= REGULATOR_CHANGE_VOLTAGE;
190 init_data->constraints.valid_ops_mask
191 |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
192 init_data->constraints.valid_modes_mask
193 = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
194 } else {
195 /* Use platform data. */
196 vreg_pdata = dev->platform_data;
197 if (!vreg_pdata) {
198 dev_err(dev, "%s: no platform data\n", __func__);
199 rc = -EINVAL;
200 goto err_probe;
201 }
202 init_data = &vreg_pdata->init_data;
203
204 vreg_priv->system_uA = vreg_pdata->system_uA;
205 vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
206 }
207
208 dev_set_drvdata(dev, vreg_priv);
Michael Bohan9b7ac512012-02-24 14:42:44 -0800209
210 rdesc = &vreg_priv->rdesc;
David Collins8839df22012-10-15 12:04:18 -0700211 strlcpy(vreg_priv->name, init_data->constraints.name,
Michael Bohan9b7ac512012-02-24 14:42:44 -0800212 STUB_REGULATOR_MAX_NAME);
213 rdesc->name = vreg_priv->name;
214 rdesc->ops = &regulator_stub_ops;
215
216 /*
217 * Ensure that voltage set points are handled correctly for regulators
218 * which have a specified voltage constraint range, as well as those
219 * that do not.
220 */
David Collins8839df22012-10-15 12:04:18 -0700221 if (init_data->constraints.min_uV == 0 &&
222 init_data->constraints.max_uV == 0)
Michael Bohan9b7ac512012-02-24 14:42:44 -0800223 rdesc->n_voltages = 0;
224 else
225 rdesc->n_voltages = 2;
226
227 rdesc->id = pdev->id;
228 rdesc->owner = THIS_MODULE;
229 rdesc->type = REGULATOR_VOLTAGE;
David Collins8839df22012-10-15 12:04:18 -0700230 vreg_priv->voltage = init_data->constraints.min_uV;
231 if (vreg_priv->system_uA >= vreg_priv->hpm_min_load)
232 vreg_priv->mode = REGULATOR_MODE_NORMAL;
233 else
234 vreg_priv->mode = REGULATOR_MODE_IDLE;
Michael Bohan9b7ac512012-02-24 14:42:44 -0800235
David Collins8839df22012-10-15 12:04:18 -0700236 vreg_priv->rdev = regulator_register(rdesc, dev, init_data, vreg_priv,
237 dev->of_node);
238
Michael Bohan9b7ac512012-02-24 14:42:44 -0800239 if (IS_ERR(vreg_priv->rdev)) {
240 rc = PTR_ERR(vreg_priv->rdev);
241 vreg_priv->rdev = NULL;
David Collins8839df22012-10-15 12:04:18 -0700242 if (rc != -EPROBE_DEFER)
243 dev_err(dev, "%s: regulator_register failed\n",
Michael Bohan9b7ac512012-02-24 14:42:44 -0800244 __func__);
245 goto err_probe;
246 }
247
248 return 0;
249
250err_probe:
251 regulator_stub_cleanup(vreg_priv);
252 return rc;
253}
254
255static int __devexit regulator_stub_remove(struct platform_device *pdev)
256{
257 struct regulator_stub *vreg_priv = dev_get_drvdata(&pdev->dev);
258
259 regulator_stub_cleanup(vreg_priv);
260 return 0;
261}
262
David Collins8839df22012-10-15 12:04:18 -0700263static struct of_device_id regulator_stub_match_table[] = {
264 { .compatible = "qcom," STUB_REGULATOR_DRIVER_NAME, },
265 {}
266};
267
Michael Bohan9b7ac512012-02-24 14:42:44 -0800268static struct platform_driver regulator_stub_driver = {
269 .probe = regulator_stub_probe,
270 .remove = __devexit_p(regulator_stub_remove),
271 .driver = {
272 .name = STUB_REGULATOR_DRIVER_NAME,
273 .owner = THIS_MODULE,
David Collins8839df22012-10-15 12:04:18 -0700274 .of_match_table = regulator_stub_match_table,
Michael Bohan9b7ac512012-02-24 14:42:44 -0800275 },
276};
277
Michael Bohan83b00652012-03-30 14:19:38 -0700278int __init regulator_stub_init(void)
Michael Bohan9b7ac512012-02-24 14:42:44 -0800279{
Michael Bohan83b00652012-03-30 14:19:38 -0700280 static int registered;
281
282 if (registered)
283 return 0;
284 else
285 registered = 1;
Michael Bohan9b7ac512012-02-24 14:42:44 -0800286 return platform_driver_register(&regulator_stub_driver);
287}
288postcore_initcall(regulator_stub_init);
Michael Bohan83b00652012-03-30 14:19:38 -0700289EXPORT_SYMBOL(regulator_stub_init);
Michael Bohan9b7ac512012-02-24 14:42:44 -0800290
291static void __exit regulator_stub_exit(void)
292{
293 platform_driver_unregister(&regulator_stub_driver);
294}
295module_exit(regulator_stub_exit);
296
297MODULE_LICENSE("GPL v2");
298MODULE_DESCRIPTION("stub regulator driver");
299MODULE_VERSION("1.0");
300MODULE_ALIAS("platform: " STUB_REGULATOR_DRIVER_NAME);