xref: /linux/drivers/regulator/wm8400-regulator.c (revision 42fad570b666256a3fd009e23e74cbb365a29ca8)
1*42fad570SMark Brown /*
2*42fad570SMark Brown  * Regulator support for WM8400
3*42fad570SMark Brown  *
4*42fad570SMark Brown  * Copyright 2008 Wolfson Microelectronics PLC.
5*42fad570SMark Brown  *
6*42fad570SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7*42fad570SMark Brown  *
8*42fad570SMark Brown  * This program is free software; you can redistribute it and/or
9*42fad570SMark Brown  * modify it under the terms of the GNU General Public License as
10*42fad570SMark Brown  * published by the Free Software Foundation; either version 2 of the
11*42fad570SMark Brown  * License, or (at your option) any later version.
12*42fad570SMark Brown  *
13*42fad570SMark Brown  */
14*42fad570SMark Brown 
15*42fad570SMark Brown #include <linux/bug.h>
16*42fad570SMark Brown #include <linux/err.h>
17*42fad570SMark Brown #include <linux/kernel.h>
18*42fad570SMark Brown #include <linux/regulator/driver.h>
19*42fad570SMark Brown #include <linux/mfd/wm8400-private.h>
20*42fad570SMark Brown 
21*42fad570SMark Brown static int wm8400_ldo_is_enabled(struct regulator_dev *dev)
22*42fad570SMark Brown {
23*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
24*42fad570SMark Brown 	u16 val;
25*42fad570SMark Brown 
26*42fad570SMark Brown 	val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
27*42fad570SMark Brown 	return (val & WM8400_LDO1_ENA) != 0;
28*42fad570SMark Brown }
29*42fad570SMark Brown 
30*42fad570SMark Brown static int wm8400_ldo_enable(struct regulator_dev *dev)
31*42fad570SMark Brown {
32*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
33*42fad570SMark Brown 
34*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
35*42fad570SMark Brown 			       WM8400_LDO1_ENA, WM8400_LDO1_ENA);
36*42fad570SMark Brown }
37*42fad570SMark Brown 
38*42fad570SMark Brown static int wm8400_ldo_disable(struct regulator_dev *dev)
39*42fad570SMark Brown {
40*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
41*42fad570SMark Brown 
42*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
43*42fad570SMark Brown 			       WM8400_LDO1_ENA, 0);
44*42fad570SMark Brown }
45*42fad570SMark Brown 
46*42fad570SMark Brown static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
47*42fad570SMark Brown {
48*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
49*42fad570SMark Brown 	u16 val;
50*42fad570SMark Brown 
51*42fad570SMark Brown 	val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
52*42fad570SMark Brown 	val &= WM8400_LDO1_VSEL_MASK;
53*42fad570SMark Brown 
54*42fad570SMark Brown 	if (val < 15)
55*42fad570SMark Brown 		return 900000 + (val * 50000);
56*42fad570SMark Brown 	else
57*42fad570SMark Brown 		return 1600000 + ((val - 14) * 100000);
58*42fad570SMark Brown }
59*42fad570SMark Brown 
60*42fad570SMark Brown static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
61*42fad570SMark Brown 				  int min_uV, int max_uV)
62*42fad570SMark Brown {
63*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
64*42fad570SMark Brown 	u16 val;
65*42fad570SMark Brown 
66*42fad570SMark Brown 	if (min_uV < 900000 || min_uV > 3300000)
67*42fad570SMark Brown 		return -EINVAL;
68*42fad570SMark Brown 
69*42fad570SMark Brown 	if (min_uV < 1700000) {
70*42fad570SMark Brown 		/* Steps of 50mV from 900mV;  */
71*42fad570SMark Brown 		val = (min_uV - 850001) / 50000;
72*42fad570SMark Brown 
73*42fad570SMark Brown 		if ((val * 50000) + 900000 > max_uV)
74*42fad570SMark Brown 			return -EINVAL;
75*42fad570SMark Brown 		BUG_ON((val * 50000) + 900000 < min_uV);
76*42fad570SMark Brown 	} else {
77*42fad570SMark Brown 		/* Steps of 100mV from 1700mV */
78*42fad570SMark Brown 		val = ((min_uV - 1600001) / 100000);
79*42fad570SMark Brown 
80*42fad570SMark Brown 		if ((val * 100000) + 1700000 > max_uV)
81*42fad570SMark Brown 			return -EINVAL;
82*42fad570SMark Brown 		BUG_ON((val * 100000) + 1700000 < min_uV);
83*42fad570SMark Brown 
84*42fad570SMark Brown 		val += 0xf;
85*42fad570SMark Brown 	}
86*42fad570SMark Brown 
87*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
88*42fad570SMark Brown 			       WM8400_LDO1_VSEL_MASK, val);
89*42fad570SMark Brown }
90*42fad570SMark Brown 
91*42fad570SMark Brown static struct regulator_ops wm8400_ldo_ops = {
92*42fad570SMark Brown 	.is_enabled = wm8400_ldo_is_enabled,
93*42fad570SMark Brown 	.enable = wm8400_ldo_enable,
94*42fad570SMark Brown 	.disable = wm8400_ldo_disable,
95*42fad570SMark Brown 	.get_voltage = wm8400_ldo_get_voltage,
96*42fad570SMark Brown 	.set_voltage = wm8400_ldo_set_voltage,
97*42fad570SMark Brown };
98*42fad570SMark Brown 
99*42fad570SMark Brown static int wm8400_dcdc_is_enabled(struct regulator_dev *dev)
100*42fad570SMark Brown {
101*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
102*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
103*42fad570SMark Brown 	u16 val;
104*42fad570SMark Brown 
105*42fad570SMark Brown 	val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
106*42fad570SMark Brown 	return (val & WM8400_DC1_ENA) != 0;
107*42fad570SMark Brown }
108*42fad570SMark Brown 
109*42fad570SMark Brown static int wm8400_dcdc_enable(struct regulator_dev *dev)
110*42fad570SMark Brown {
111*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
112*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
113*42fad570SMark Brown 
114*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
115*42fad570SMark Brown 			       WM8400_DC1_ENA, WM8400_DC1_ENA);
116*42fad570SMark Brown }
117*42fad570SMark Brown 
118*42fad570SMark Brown static int wm8400_dcdc_disable(struct regulator_dev *dev)
119*42fad570SMark Brown {
120*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
121*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
122*42fad570SMark Brown 
123*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
124*42fad570SMark Brown 			       WM8400_DC1_ENA, 0);
125*42fad570SMark Brown }
126*42fad570SMark Brown 
127*42fad570SMark Brown static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
128*42fad570SMark Brown {
129*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
130*42fad570SMark Brown 	u16 val;
131*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
132*42fad570SMark Brown 
133*42fad570SMark Brown 	val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
134*42fad570SMark Brown 	val &= WM8400_DC1_VSEL_MASK;
135*42fad570SMark Brown 
136*42fad570SMark Brown 	return 850000 + (25000 * val);
137*42fad570SMark Brown }
138*42fad570SMark Brown 
139*42fad570SMark Brown static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
140*42fad570SMark Brown 				  int min_uV, int max_uV)
141*42fad570SMark Brown {
142*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
143*42fad570SMark Brown 	u16 val;
144*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
145*42fad570SMark Brown 
146*42fad570SMark Brown 	if (min_uV < 850000)
147*42fad570SMark Brown 		return -EINVAL;
148*42fad570SMark Brown 
149*42fad570SMark Brown 	val = (min_uV - 825001) / 25000;
150*42fad570SMark Brown 
151*42fad570SMark Brown 	if (850000 + (25000 * val) > max_uV)
152*42fad570SMark Brown 		return -EINVAL;
153*42fad570SMark Brown 	BUG_ON(850000 + (25000 * val) < min_uV);
154*42fad570SMark Brown 
155*42fad570SMark Brown 	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
156*42fad570SMark Brown 			       WM8400_DC1_VSEL_MASK, val);
157*42fad570SMark Brown }
158*42fad570SMark Brown 
159*42fad570SMark Brown static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
160*42fad570SMark Brown {
161*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
162*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
163*42fad570SMark Brown 	u16 data[2];
164*42fad570SMark Brown 	int ret;
165*42fad570SMark Brown 
166*42fad570SMark Brown 	ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2,
167*42fad570SMark Brown 				data);
168*42fad570SMark Brown 	if (ret != 0)
169*42fad570SMark Brown 		return 0;
170*42fad570SMark Brown 
171*42fad570SMark Brown 	/* Datasheet: hibernate */
172*42fad570SMark Brown 	if (data[0] & WM8400_DC1_SLEEP)
173*42fad570SMark Brown 		return REGULATOR_MODE_STANDBY;
174*42fad570SMark Brown 
175*42fad570SMark Brown 	/* Datasheet: standby */
176*42fad570SMark Brown 	if (!(data[0] & WM8400_DC1_ACTIVE))
177*42fad570SMark Brown 		return REGULATOR_MODE_IDLE;
178*42fad570SMark Brown 
179*42fad570SMark Brown 	/* Datasheet: active with or without force PWM */
180*42fad570SMark Brown 	if (data[1] & WM8400_DC1_FRC_PWM)
181*42fad570SMark Brown 		return REGULATOR_MODE_FAST;
182*42fad570SMark Brown 	else
183*42fad570SMark Brown 		return REGULATOR_MODE_NORMAL;
184*42fad570SMark Brown }
185*42fad570SMark Brown 
186*42fad570SMark Brown static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
187*42fad570SMark Brown {
188*42fad570SMark Brown 	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
189*42fad570SMark Brown 	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
190*42fad570SMark Brown 	int ret;
191*42fad570SMark Brown 
192*42fad570SMark Brown 	switch (mode) {
193*42fad570SMark Brown 	case REGULATOR_MODE_FAST:
194*42fad570SMark Brown 		/* Datasheet: active with force PWM */
195*42fad570SMark Brown 		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
196*42fad570SMark Brown 				      WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
197*42fad570SMark Brown 		if (ret != 0)
198*42fad570SMark Brown 			return ret;
199*42fad570SMark Brown 
200*42fad570SMark Brown 		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
201*42fad570SMark Brown 				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
202*42fad570SMark Brown 				       WM8400_DC1_ACTIVE);
203*42fad570SMark Brown 
204*42fad570SMark Brown 	case REGULATOR_MODE_NORMAL:
205*42fad570SMark Brown 		/* Datasheet: active */
206*42fad570SMark Brown 		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
207*42fad570SMark Brown 				      WM8400_DC1_FRC_PWM, 0);
208*42fad570SMark Brown 		if (ret != 0)
209*42fad570SMark Brown 			return ret;
210*42fad570SMark Brown 
211*42fad570SMark Brown 		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
212*42fad570SMark Brown 				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
213*42fad570SMark Brown 				       WM8400_DC1_ACTIVE);
214*42fad570SMark Brown 
215*42fad570SMark Brown 	case REGULATOR_MODE_IDLE:
216*42fad570SMark Brown 		/* Datasheet: standby */
217*42fad570SMark Brown 		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
218*42fad570SMark Brown 				      WM8400_DC1_ACTIVE, 0);
219*42fad570SMark Brown 		if (ret != 0)
220*42fad570SMark Brown 			return ret;
221*42fad570SMark Brown 		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
222*42fad570SMark Brown 				       WM8400_DC1_SLEEP, 0);
223*42fad570SMark Brown 
224*42fad570SMark Brown 	default:
225*42fad570SMark Brown 		return -EINVAL;
226*42fad570SMark Brown 	}
227*42fad570SMark Brown }
228*42fad570SMark Brown 
229*42fad570SMark Brown static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
230*42fad570SMark Brown 						 int input_uV, int output_uV,
231*42fad570SMark Brown 						 int load_uA)
232*42fad570SMark Brown {
233*42fad570SMark Brown 	return REGULATOR_MODE_NORMAL;
234*42fad570SMark Brown }
235*42fad570SMark Brown 
236*42fad570SMark Brown static struct regulator_ops wm8400_dcdc_ops = {
237*42fad570SMark Brown 	.is_enabled = wm8400_dcdc_is_enabled,
238*42fad570SMark Brown 	.enable = wm8400_dcdc_enable,
239*42fad570SMark Brown 	.disable = wm8400_dcdc_disable,
240*42fad570SMark Brown 	.get_voltage = wm8400_dcdc_get_voltage,
241*42fad570SMark Brown 	.set_voltage = wm8400_dcdc_set_voltage,
242*42fad570SMark Brown 	.get_mode = wm8400_dcdc_get_mode,
243*42fad570SMark Brown 	.set_mode = wm8400_dcdc_set_mode,
244*42fad570SMark Brown 	.get_optimum_mode = wm8400_dcdc_get_optimum_mode,
245*42fad570SMark Brown };
246*42fad570SMark Brown 
247*42fad570SMark Brown static struct regulator_desc regulators[] = {
248*42fad570SMark Brown 	{
249*42fad570SMark Brown 		.name = "LDO1",
250*42fad570SMark Brown 		.id = WM8400_LDO1,
251*42fad570SMark Brown 		.ops = &wm8400_ldo_ops,
252*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
253*42fad570SMark Brown 		.owner = THIS_MODULE,
254*42fad570SMark Brown 	},
255*42fad570SMark Brown 	{
256*42fad570SMark Brown 		.name = "LDO2",
257*42fad570SMark Brown 		.id = WM8400_LDO2,
258*42fad570SMark Brown 		.ops = &wm8400_ldo_ops,
259*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
260*42fad570SMark Brown 		.owner = THIS_MODULE,
261*42fad570SMark Brown 	},
262*42fad570SMark Brown 	{
263*42fad570SMark Brown 		.name = "LDO3",
264*42fad570SMark Brown 		.id = WM8400_LDO3,
265*42fad570SMark Brown 		.ops = &wm8400_ldo_ops,
266*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
267*42fad570SMark Brown 		.owner = THIS_MODULE,
268*42fad570SMark Brown 	},
269*42fad570SMark Brown 	{
270*42fad570SMark Brown 		.name = "LDO4",
271*42fad570SMark Brown 		.id = WM8400_LDO4,
272*42fad570SMark Brown 		.ops = &wm8400_ldo_ops,
273*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
274*42fad570SMark Brown 		.owner = THIS_MODULE,
275*42fad570SMark Brown 	},
276*42fad570SMark Brown 	{
277*42fad570SMark Brown 		.name = "DCDC1",
278*42fad570SMark Brown 		.id = WM8400_DCDC1,
279*42fad570SMark Brown 		.ops = &wm8400_dcdc_ops,
280*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
281*42fad570SMark Brown 		.owner = THIS_MODULE,
282*42fad570SMark Brown 	},
283*42fad570SMark Brown 	{
284*42fad570SMark Brown 		.name = "DCDC2",
285*42fad570SMark Brown 		.id = WM8400_DCDC2,
286*42fad570SMark Brown 		.ops = &wm8400_dcdc_ops,
287*42fad570SMark Brown 		.type = REGULATOR_VOLTAGE,
288*42fad570SMark Brown 		.owner = THIS_MODULE,
289*42fad570SMark Brown 	},
290*42fad570SMark Brown };
291*42fad570SMark Brown 
292*42fad570SMark Brown static int __init wm8400_regulator_probe(struct platform_device *pdev)
293*42fad570SMark Brown {
294*42fad570SMark Brown 	struct regulator_dev *rdev;
295*42fad570SMark Brown 
296*42fad570SMark Brown 	rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
297*42fad570SMark Brown 		pdev->dev.driver_data);
298*42fad570SMark Brown 
299*42fad570SMark Brown 	if (IS_ERR(rdev))
300*42fad570SMark Brown 		return PTR_ERR(rdev);
301*42fad570SMark Brown 
302*42fad570SMark Brown 	return 0;
303*42fad570SMark Brown }
304*42fad570SMark Brown 
305*42fad570SMark Brown static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
306*42fad570SMark Brown {
307*42fad570SMark Brown 	struct regulator_dev *rdev = platform_get_drvdata(pdev);
308*42fad570SMark Brown 
309*42fad570SMark Brown 	regulator_unregister(rdev);
310*42fad570SMark Brown 
311*42fad570SMark Brown 	return 0;
312*42fad570SMark Brown }
313*42fad570SMark Brown 
314*42fad570SMark Brown static struct platform_driver wm8400_regulator_driver = {
315*42fad570SMark Brown 	.driver = {
316*42fad570SMark Brown 		.name = "wm8400-regulator",
317*42fad570SMark Brown 	},
318*42fad570SMark Brown 	.probe = wm8400_regulator_probe,
319*42fad570SMark Brown 	.remove = __devexit_p(wm8400_regulator_remove),
320*42fad570SMark Brown };
321*42fad570SMark Brown 
322*42fad570SMark Brown /**
323*42fad570SMark Brown  * wm8400_register_regulator - enable software control of a WM8400 regulator
324*42fad570SMark Brown  *
325*42fad570SMark Brown  * This function enables software control of a WM8400 regulator via
326*42fad570SMark Brown  * the regulator API.  It is intended to be called from the
327*42fad570SMark Brown  * platform_init() callback of the WM8400 MFD driver.
328*42fad570SMark Brown  *
329*42fad570SMark Brown  * @param dev      The WM8400 device to operate on.
330*42fad570SMark Brown  * @param reg      The regulator to control.
331*42fad570SMark Brown  * @param initdata Regulator initdata for the regulator.
332*42fad570SMark Brown  */
333*42fad570SMark Brown int wm8400_register_regulator(struct device *dev, int reg,
334*42fad570SMark Brown 			      struct regulator_init_data *initdata)
335*42fad570SMark Brown {
336*42fad570SMark Brown 	struct wm8400 *wm8400 = dev->driver_data;
337*42fad570SMark Brown 
338*42fad570SMark Brown 	if (wm8400->regulators[reg].name)
339*42fad570SMark Brown 		return -EBUSY;
340*42fad570SMark Brown 
341*42fad570SMark Brown 	initdata->driver_data = wm8400;
342*42fad570SMark Brown 
343*42fad570SMark Brown 	wm8400->regulators[reg].name = "wm8400-regulator";
344*42fad570SMark Brown 	wm8400->regulators[reg].id = reg;
345*42fad570SMark Brown 	wm8400->regulators[reg].dev.parent = dev;
346*42fad570SMark Brown 	wm8400->regulators[reg].dev.driver_data = wm8400;
347*42fad570SMark Brown 	wm8400->regulators[reg].dev.platform_data = initdata;
348*42fad570SMark Brown 
349*42fad570SMark Brown 	return platform_device_register(&wm8400->regulators[reg]);
350*42fad570SMark Brown }
351*42fad570SMark Brown EXPORT_SYMBOL_GPL(wm8400_register_regulator);
352*42fad570SMark Brown 
353*42fad570SMark Brown static int __init wm8400_regulator_init(void)
354*42fad570SMark Brown {
355*42fad570SMark Brown 	return platform_driver_register(&wm8400_regulator_driver);
356*42fad570SMark Brown }
357*42fad570SMark Brown module_init(wm8400_regulator_init);
358*42fad570SMark Brown 
359*42fad570SMark Brown static void __exit wm8400_regulator_exit(void)
360*42fad570SMark Brown {
361*42fad570SMark Brown 	platform_driver_unregister(&wm8400_regulator_driver);
362*42fad570SMark Brown }
363*42fad570SMark Brown module_exit(wm8400_regulator_exit);
364*42fad570SMark Brown 
365*42fad570SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
366*42fad570SMark Brown MODULE_DESCRIPTION("WM8400 regulator driver");
367*42fad570SMark Brown MODULE_LICENSE("GPL");
368*42fad570SMark Brown MODULE_ALIAS("platform:wm8400-regulator");
369