Blame view

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