blob: 6579bfdb0c2624e5130fdf6280c30a3ea6061ba7 [file] [log] [blame]
Axel Lin87dbc5e2019-04-17 22:16:31 +08001// SPDX-License-Identifier: GPL-2.0+
2//
3// wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC
4//
5// Copyright 2007, 2008 Wolfson Microelectronics PLC.
6//
7// Author: Liam Girdwood
8// linux@wolfsonmicro.com
Mark Brownda091552008-10-10 15:58:15 +01009
10#include <linux/module.h>
11#include <linux/moduleparam.h>
12#include <linux/init.h>
13#include <linux/bitops.h>
14#include <linux/err.h>
15#include <linux/i2c.h>
16#include <linux/mfd/wm8350/core.h>
17#include <linux/mfd/wm8350/pmic.h>
18#include <linux/platform_device.h>
19#include <linux/regulator/driver.h>
20#include <linux/regulator/machine.h>
21
Mark Brown221a7c72009-03-02 16:32:47 +000022/* Maximum value possible for VSEL */
23#define WM8350_DCDC_MAX_VSEL 0x66
24
Mark Brownda091552008-10-10 15:58:15 +010025/* Microamps */
Axel Lindfeb7a92019-03-13 00:33:57 +080026static const unsigned int isink_cur[] = {
Mark Brownda091552008-10-10 15:58:15 +010027 4,
28 5,
29 6,
30 7,
31 8,
32 10,
33 11,
34 14,
35 16,
36 19,
37 23,
38 27,
39 32,
40 39,
41 46,
42 54,
43 65,
44 77,
45 92,
46 109,
47 130,
48 154,
49 183,
50 218,
51 259,
52 308,
53 367,
54 436,
55 518,
56 616,
57 733,
58 872,
59 1037,
60 1233,
61 1466,
62 1744,
63 2073,
64 2466,
65 2933,
66 3487,
67 4147,
68 4932,
69 5865,
70 6975,
71 8294,
72 9864,
73 11730,
74 13949,
75 16589,
76 19728,
77 23460,
78 27899,
79 33178,
80 39455,
81 46920,
82 55798,
83 66355,
84 78910,
85 93840,
86 111596,
87 132710,
88 157820,
89 187681,
90 223191
91};
92
Mark Brownda091552008-10-10 15:58:15 +010093/* turn on ISINK followed by DCDC */
94static int wm8350_isink_enable(struct regulator_dev *rdev)
95{
96 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
97 int isink = rdev_get_id(rdev);
98
99 switch (isink) {
100 case WM8350_ISINK_A:
101 switch (wm8350->pmic.isink_A_dcdc) {
102 case WM8350_DCDC_2:
103 case WM8350_DCDC_5:
104 wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
105 WM8350_CS1_ENA);
106 wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL,
107 WM8350_CS1_DRIVE);
108 wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
109 1 << (wm8350->pmic.isink_A_dcdc -
110 WM8350_DCDC_1));
111 break;
112 default:
113 return -EINVAL;
114 }
115 break;
116 case WM8350_ISINK_B:
117 switch (wm8350->pmic.isink_B_dcdc) {
118 case WM8350_DCDC_2:
119 case WM8350_DCDC_5:
120 wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
121 WM8350_CS2_ENA);
122 wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL,
123 WM8350_CS2_DRIVE);
124 wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
125 1 << (wm8350->pmic.isink_B_dcdc -
126 WM8350_DCDC_1));
127 break;
128 default:
129 return -EINVAL;
130 }
131 break;
132 default:
133 return -EINVAL;
134 }
135 return 0;
136}
137
138static int wm8350_isink_disable(struct regulator_dev *rdev)
139{
140 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
141 int isink = rdev_get_id(rdev);
142
143 switch (isink) {
144 case WM8350_ISINK_A:
145 switch (wm8350->pmic.isink_A_dcdc) {
146 case WM8350_DCDC_2:
147 case WM8350_DCDC_5:
148 wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
149 1 << (wm8350->pmic.isink_A_dcdc -
150 WM8350_DCDC_1));
151 wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
152 WM8350_CS1_ENA);
153 break;
154 default:
155 return -EINVAL;
156 }
157 break;
158 case WM8350_ISINK_B:
159 switch (wm8350->pmic.isink_B_dcdc) {
160 case WM8350_DCDC_2:
161 case WM8350_DCDC_5:
162 wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
163 1 << (wm8350->pmic.isink_B_dcdc -
164 WM8350_DCDC_1));
165 wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
166 WM8350_CS2_ENA);
167 break;
168 default:
169 return -EINVAL;
170 }
171 break;
172 default:
173 return -EINVAL;
174 }
175 return 0;
176}
177
178static int wm8350_isink_is_enabled(struct regulator_dev *rdev)
179{
180 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
181 int isink = rdev_get_id(rdev);
182
183 switch (isink) {
184 case WM8350_ISINK_A:
185 return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
186 0x8000;
187 case WM8350_ISINK_B:
188 return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
189 0x8000;
190 }
191 return -EINVAL;
192}
193
Mark Brown75c8ac22010-01-04 17:24:01 +0000194static int wm8350_isink_enable_time(struct regulator_dev *rdev)
195{
196 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
197 int isink = rdev_get_id(rdev);
198 int reg;
199
200 switch (isink) {
201 case WM8350_ISINK_A:
202 reg = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL);
203 break;
204 case WM8350_ISINK_B:
205 reg = wm8350_reg_read(wm8350, WM8350_CSB_FLASH_CONTROL);
206 break;
207 default:
208 return -EINVAL;
209 }
210
211 if (reg & WM8350_CS1_FLASH_MODE) {
212 switch (reg & WM8350_CS1_ON_RAMP_MASK) {
213 case 0:
214 return 0;
215 case 1:
216 return 1950;
217 case 2:
218 return 3910;
219 case 3:
220 return 7800;
221 }
222 } else {
223 switch (reg & WM8350_CS1_ON_RAMP_MASK) {
224 case 0:
225 return 0;
226 case 1:
227 return 250000;
228 case 2:
229 return 500000;
230 case 3:
231 return 1000000;
232 }
233 }
234
235 return -EINVAL;
236}
237
238
Mark Brownda091552008-10-10 15:58:15 +0100239int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
240 u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp,
241 u16 drive)
242{
243 switch (isink) {
244 case WM8350_ISINK_A:
245 wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL,
246 (mode ? WM8350_CS1_FLASH_MODE : 0) |
247 (trigger ? WM8350_CS1_TRIGSRC : 0) |
248 duration | on_ramp | off_ramp | drive);
249 break;
250 case WM8350_ISINK_B:
251 wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL,
252 (mode ? WM8350_CS2_FLASH_MODE : 0) |
253 (trigger ? WM8350_CS2_TRIGSRC : 0) |
254 duration | on_ramp | off_ramp | drive);
255 break;
256 default:
257 return -EINVAL;
258 }
259 return 0;
260}
261EXPORT_SYMBOL_GPL(wm8350_isink_set_flash);
262
Mark Brownda091552008-10-10 15:58:15 +0100263static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV)
264{
265 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
Axel Linc7057422012-06-12 20:10:26 +0800266 int sel, volt_reg, dcdc = rdev_get_id(rdev);
Mark Brownda091552008-10-10 15:58:15 +0100267 u16 val;
268
Axel Linc7057422012-06-12 20:10:26 +0800269 dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, uV / 1000);
Mark Brownda091552008-10-10 15:58:15 +0100270
271 switch (dcdc) {
272 case WM8350_DCDC_1:
273 volt_reg = WM8350_DCDC1_LOW_POWER;
274 break;
275 case WM8350_DCDC_3:
276 volt_reg = WM8350_DCDC3_LOW_POWER;
277 break;
278 case WM8350_DCDC_4:
279 volt_reg = WM8350_DCDC4_LOW_POWER;
280 break;
281 case WM8350_DCDC_6:
282 volt_reg = WM8350_DCDC6_LOW_POWER;
283 break;
284 case WM8350_DCDC_2:
285 case WM8350_DCDC_5:
286 default:
287 return -EINVAL;
288 }
289
Axel Linc7057422012-06-12 20:10:26 +0800290 sel = regulator_map_voltage_linear(rdev, uV, uV);
291 if (sel < 0)
Sachin Kamat0b61ad12014-02-18 16:10:57 +0530292 return sel;
Axel Linc7057422012-06-12 20:10:26 +0800293
Mark Brownda091552008-10-10 15:58:15 +0100294 /* all DCDCs have same mV bits */
295 val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
Axel Linc7057422012-06-12 20:10:26 +0800296 wm8350_reg_write(wm8350, volt_reg, val | sel);
Mark Brownda091552008-10-10 15:58:15 +0100297 return 0;
298}
299
300static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev)
301{
302 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
303 int dcdc = rdev_get_id(rdev);
304 u16 val;
305
306 switch (dcdc) {
307 case WM8350_DCDC_1:
308 val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER)
309 & ~WM8350_DCDC_HIB_MODE_MASK;
310 wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
Axel Lineb416812012-03-23 06:25:05 +0800311 val | wm8350->pmic.dcdc1_hib_mode);
Mark Brownda091552008-10-10 15:58:15 +0100312 break;
313 case WM8350_DCDC_3:
314 val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER)
315 & ~WM8350_DCDC_HIB_MODE_MASK;
316 wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
Axel Lineb416812012-03-23 06:25:05 +0800317 val | wm8350->pmic.dcdc3_hib_mode);
Mark Brownda091552008-10-10 15:58:15 +0100318 break;
319 case WM8350_DCDC_4:
320 val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER)
321 & ~WM8350_DCDC_HIB_MODE_MASK;
322 wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
Axel Lineb416812012-03-23 06:25:05 +0800323 val | wm8350->pmic.dcdc4_hib_mode);
Mark Brownda091552008-10-10 15:58:15 +0100324 break;
325 case WM8350_DCDC_6:
326 val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER)
327 & ~WM8350_DCDC_HIB_MODE_MASK;
328 wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
Axel Lineb416812012-03-23 06:25:05 +0800329 val | wm8350->pmic.dcdc6_hib_mode);
Mark Brownda091552008-10-10 15:58:15 +0100330 break;
331 case WM8350_DCDC_2:
332 case WM8350_DCDC_5:
333 default:
334 return -EINVAL;
335 }
336
337 return 0;
338}
339
340static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev)
341{
342 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
343 int dcdc = rdev_get_id(rdev);
344 u16 val;
345
346 switch (dcdc) {
347 case WM8350_DCDC_1:
348 val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
349 wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
350 wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
Axel Lincee1a792012-03-29 10:47:36 +0800351 val | WM8350_DCDC_HIB_MODE_DIS);
Mark Brownda091552008-10-10 15:58:15 +0100352 break;
353 case WM8350_DCDC_3:
354 val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
355 wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
356 wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
Axel Lincee1a792012-03-29 10:47:36 +0800357 val | WM8350_DCDC_HIB_MODE_DIS);
Mark Brownda091552008-10-10 15:58:15 +0100358 break;
359 case WM8350_DCDC_4:
360 val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
361 wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
362 wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
Axel Lincee1a792012-03-29 10:47:36 +0800363 val | WM8350_DCDC_HIB_MODE_DIS);
Mark Brownda091552008-10-10 15:58:15 +0100364 break;
365 case WM8350_DCDC_6:
366 val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
367 wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
368 wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
Axel Lincee1a792012-03-29 10:47:36 +0800369 val | WM8350_DCDC_HIB_MODE_DIS);
Mark Brownda091552008-10-10 15:58:15 +0100370 break;
371 case WM8350_DCDC_2:
372 case WM8350_DCDC_5:
373 default:
374 return -EINVAL;
375 }
376
377 return 0;
378}
379
380static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev)
381{
382 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
383 int dcdc = rdev_get_id(rdev);
384 u16 val;
385
386 switch (dcdc) {
387 case WM8350_DCDC_2:
388 val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
389 & ~WM8350_DC2_HIB_MODE_MASK;
390 wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
Axel Lin93009282012-03-23 06:27:10 +0800391 (WM8350_DC2_HIB_MODE_ACTIVE << WM8350_DC2_HIB_MODE_SHIFT));
Mark Brownda091552008-10-10 15:58:15 +0100392 break;
393 case WM8350_DCDC_5:
394 val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
Axel Lin93009282012-03-23 06:27:10 +0800395 & ~WM8350_DC5_HIB_MODE_MASK;
Mark Brownda091552008-10-10 15:58:15 +0100396 wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
Axel Lin93009282012-03-23 06:27:10 +0800397 (WM8350_DC5_HIB_MODE_ACTIVE << WM8350_DC5_HIB_MODE_SHIFT));
Mark Brownda091552008-10-10 15:58:15 +0100398 break;
399 default:
400 return -EINVAL;
401 }
402 return 0;
403}
404
405static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev)
406{
407 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
408 int dcdc = rdev_get_id(rdev);
409 u16 val;
410
411 switch (dcdc) {
412 case WM8350_DCDC_2:
413 val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
414 & ~WM8350_DC2_HIB_MODE_MASK;
415 wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
Axel Lin93009282012-03-23 06:27:10 +0800416 (WM8350_DC2_HIB_MODE_DISABLE << WM8350_DC2_HIB_MODE_SHIFT));
Mark Brownda091552008-10-10 15:58:15 +0100417 break;
418 case WM8350_DCDC_5:
419 val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
Axel Lin93009282012-03-23 06:27:10 +0800420 & ~WM8350_DC5_HIB_MODE_MASK;
Mark Brownda091552008-10-10 15:58:15 +0100421 wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
Axel Lin93009282012-03-23 06:27:10 +0800422 (WM8350_DC5_HIB_MODE_DISABLE << WM8350_DC5_HIB_MODE_SHIFT));
Mark Brownda091552008-10-10 15:58:15 +0100423 break;
424 default:
425 return -EINVAL;
426 }
427 return 0;
428}
429
430static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev,
431 unsigned int mode)
432{
433 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
434 int dcdc = rdev_get_id(rdev);
435 u16 *hib_mode;
436
437 switch (dcdc) {
438 case WM8350_DCDC_1:
439 hib_mode = &wm8350->pmic.dcdc1_hib_mode;
440 break;
441 case WM8350_DCDC_3:
442 hib_mode = &wm8350->pmic.dcdc3_hib_mode;
443 break;
444 case WM8350_DCDC_4:
445 hib_mode = &wm8350->pmic.dcdc4_hib_mode;
446 break;
447 case WM8350_DCDC_6:
448 hib_mode = &wm8350->pmic.dcdc6_hib_mode;
449 break;
450 case WM8350_DCDC_2:
451 case WM8350_DCDC_5:
452 default:
453 return -EINVAL;
454 }
455
456 switch (mode) {
457 case REGULATOR_MODE_NORMAL:
458 *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE;
459 break;
460 case REGULATOR_MODE_IDLE:
461 *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY;
462 break;
463 case REGULATOR_MODE_STANDBY:
464 *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM;
465 break;
466 default:
467 return -EINVAL;
468 }
469
470 return 0;
471}
472
Matti Vaittinen60ab7f42020-05-08 18:43:36 +0300473static const struct linear_range wm8350_ldo_ranges[] = {
Axel Lin8828bae2013-10-11 09:32:18 +0800474 REGULATOR_LINEAR_RANGE(900000, 0, 15, 50000),
475 REGULATOR_LINEAR_RANGE(1800000, 16, 31, 100000),
Mark Brownc36a1cd2013-07-02 23:35:42 +0100476};
Axel Linc7057422012-06-12 20:10:26 +0800477
Mark Brownda091552008-10-10 15:58:15 +0100478static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV)
479{
480 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
Axel Linc7057422012-06-12 20:10:26 +0800481 int sel, volt_reg, ldo = rdev_get_id(rdev);
Mark Brownda091552008-10-10 15:58:15 +0100482 u16 val;
483
Axel Linc7057422012-06-12 20:10:26 +0800484 dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, uV / 1000);
Mark Brownda091552008-10-10 15:58:15 +0100485
486 switch (ldo) {
487 case WM8350_LDO_1:
488 volt_reg = WM8350_LDO1_LOW_POWER;
489 break;
490 case WM8350_LDO_2:
491 volt_reg = WM8350_LDO2_LOW_POWER;
492 break;
493 case WM8350_LDO_3:
494 volt_reg = WM8350_LDO3_LOW_POWER;
495 break;
496 case WM8350_LDO_4:
497 volt_reg = WM8350_LDO4_LOW_POWER;
498 break;
499 default:
500 return -EINVAL;
501 }
502
Mark Brownc36a1cd2013-07-02 23:35:42 +0100503 sel = regulator_map_voltage_linear_range(rdev, uV, uV);
Axel Linc7057422012-06-12 20:10:26 +0800504 if (sel < 0)
Sachin Kamat0b61ad12014-02-18 16:10:57 +0530505 return sel;
Axel Linc7057422012-06-12 20:10:26 +0800506
Mark Brownda091552008-10-10 15:58:15 +0100507 /* all LDOs have same mV bits */
508 val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
Axel Linc7057422012-06-12 20:10:26 +0800509 wm8350_reg_write(wm8350, volt_reg, val | sel);
Mark Brownda091552008-10-10 15:58:15 +0100510 return 0;
511}
512
513static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev)
514{
515 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
516 int volt_reg, ldo = rdev_get_id(rdev);
517 u16 val;
518
519 switch (ldo) {
520 case WM8350_LDO_1:
521 volt_reg = WM8350_LDO1_LOW_POWER;
522 break;
523 case WM8350_LDO_2:
524 volt_reg = WM8350_LDO2_LOW_POWER;
525 break;
526 case WM8350_LDO_3:
527 volt_reg = WM8350_LDO3_LOW_POWER;
528 break;
529 case WM8350_LDO_4:
530 volt_reg = WM8350_LDO4_LOW_POWER;
531 break;
532 default:
533 return -EINVAL;
534 }
535
536 /* all LDOs have same mV bits */
537 val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
538 wm8350_reg_write(wm8350, volt_reg, val);
539 return 0;
540}
541
542static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev)
543{
544 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
545 int volt_reg, ldo = rdev_get_id(rdev);
546 u16 val;
547
548 switch (ldo) {
549 case WM8350_LDO_1:
550 volt_reg = WM8350_LDO1_LOW_POWER;
551 break;
552 case WM8350_LDO_2:
553 volt_reg = WM8350_LDO2_LOW_POWER;
554 break;
555 case WM8350_LDO_3:
556 volt_reg = WM8350_LDO3_LOW_POWER;
557 break;
558 case WM8350_LDO_4:
559 volt_reg = WM8350_LDO4_LOW_POWER;
560 break;
561 default:
562 return -EINVAL;
563 }
564
565 /* all LDOs have same mV bits */
566 val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
Axel Lincee1a792012-03-29 10:47:36 +0800567 wm8350_reg_write(wm8350, volt_reg, val | WM8350_LDO1_HIB_MODE_DIS);
Mark Brownda091552008-10-10 15:58:15 +0100568 return 0;
569}
570
Mark Brownda091552008-10-10 15:58:15 +0100571int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start,
572 u16 stop, u16 fault)
573{
574 int slot_reg;
575 u16 val;
576
577 dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
578 __func__, dcdc, start, stop);
579
580 /* slot valid ? */
581 if (start > 15 || stop > 15)
582 return -EINVAL;
583
584 switch (dcdc) {
585 case WM8350_DCDC_1:
586 slot_reg = WM8350_DCDC1_TIMEOUTS;
587 break;
588 case WM8350_DCDC_2:
589 slot_reg = WM8350_DCDC2_TIMEOUTS;
590 break;
591 case WM8350_DCDC_3:
592 slot_reg = WM8350_DCDC3_TIMEOUTS;
593 break;
594 case WM8350_DCDC_4:
595 slot_reg = WM8350_DCDC4_TIMEOUTS;
596 break;
597 case WM8350_DCDC_5:
598 slot_reg = WM8350_DCDC5_TIMEOUTS;
599 break;
600 case WM8350_DCDC_6:
601 slot_reg = WM8350_DCDC6_TIMEOUTS;
602 break;
603 default:
604 return -EINVAL;
605 }
606
607 val = wm8350_reg_read(wm8350, slot_reg) &
608 ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK |
609 WM8350_DC1_ERRACT_MASK);
610 wm8350_reg_write(wm8350, slot_reg,
611 val | (start << WM8350_DC1_ENSLOT_SHIFT) |
612 (stop << WM8350_DC1_SDSLOT_SHIFT) |
613 (fault << WM8350_DC1_ERRACT_SHIFT));
614
615 return 0;
616}
617EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot);
618
619int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop)
620{
621 int slot_reg;
622 u16 val;
623
624 dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
625 __func__, ldo, start, stop);
626
627 /* slot valid ? */
628 if (start > 15 || stop > 15)
629 return -EINVAL;
630
631 switch (ldo) {
632 case WM8350_LDO_1:
633 slot_reg = WM8350_LDO1_TIMEOUTS;
634 break;
635 case WM8350_LDO_2:
636 slot_reg = WM8350_LDO2_TIMEOUTS;
637 break;
638 case WM8350_LDO_3:
639 slot_reg = WM8350_LDO3_TIMEOUTS;
640 break;
641 case WM8350_LDO_4:
642 slot_reg = WM8350_LDO4_TIMEOUTS;
643 break;
644 default:
645 return -EINVAL;
646 }
647
648 val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK;
649 wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6)));
650 return 0;
651}
652EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot);
653
654int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode,
655 u16 ilim, u16 ramp, u16 feedback)
656{
657 u16 val;
658
659 dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc,
660 mode ? "normal" : "boost", ilim ? "low" : "normal");
661
662 switch (dcdc) {
663 case WM8350_DCDC_2:
664 val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
665 & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK |
666 WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK);
667 wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
668 (mode << WM8350_DC2_MODE_SHIFT) |
669 (ilim << WM8350_DC2_ILIM_SHIFT) |
670 (ramp << WM8350_DC2_RMP_SHIFT) |
671 (feedback << WM8350_DC2_FBSRC_SHIFT));
672 break;
673 case WM8350_DCDC_5:
674 val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
675 & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK |
676 WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK);
677 wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
678 (mode << WM8350_DC5_MODE_SHIFT) |
679 (ilim << WM8350_DC5_ILIM_SHIFT) |
680 (ramp << WM8350_DC5_RMP_SHIFT) |
681 (feedback << WM8350_DC5_FBSRC_SHIFT));
682 break;
683 default:
684 return -EINVAL;
685 }
686
687 return 0;
688}
689EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode);
690
Mark Brownda091552008-10-10 15:58:15 +0100691static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable)
692{
693 int reg = 0, ret;
694
695 switch (dcdc) {
696 case WM8350_DCDC_1:
697 reg = WM8350_DCDC1_FORCE_PWM;
698 break;
699 case WM8350_DCDC_3:
700 reg = WM8350_DCDC3_FORCE_PWM;
701 break;
702 case WM8350_DCDC_4:
703 reg = WM8350_DCDC4_FORCE_PWM;
704 break;
705 case WM8350_DCDC_6:
706 reg = WM8350_DCDC6_FORCE_PWM;
707 break;
708 default:
709 return -EINVAL;
710 }
711
712 if (enable)
713 ret = wm8350_set_bits(wm8350, reg,
714 WM8350_DCDC1_FORCE_PWM_ENA);
715 else
716 ret = wm8350_clear_bits(wm8350, reg,
717 WM8350_DCDC1_FORCE_PWM_ENA);
718 return ret;
719}
720
721static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
722{
723 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
724 int dcdc = rdev_get_id(rdev);
725 u16 val;
726
727 if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
728 return -EINVAL;
729
730 if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
731 return -EINVAL;
732
733 val = 1 << (dcdc - WM8350_DCDC_1);
734
735 switch (mode) {
736 case REGULATOR_MODE_FAST:
737 /* force continuous mode */
738 wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
739 wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
740 force_continuous_enable(wm8350, dcdc, 1);
741 break;
742 case REGULATOR_MODE_NORMAL:
743 /* active / pulse skipping */
744 wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
745 wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
746 force_continuous_enable(wm8350, dcdc, 0);
747 break;
748 case REGULATOR_MODE_IDLE:
749 /* standby mode */
750 force_continuous_enable(wm8350, dcdc, 0);
751 wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
752 wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
753 break;
754 case REGULATOR_MODE_STANDBY:
755 /* LDO mode */
756 force_continuous_enable(wm8350, dcdc, 0);
757 wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
758 break;
759 }
760
761 return 0;
762}
763
764static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev)
765{
766 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
767 int dcdc = rdev_get_id(rdev);
768 u16 mask, sleep, active, force;
769 int mode = REGULATOR_MODE_NORMAL;
Mark Brown33f301a2009-02-26 19:24:20 +0000770 int reg;
Mark Brownda091552008-10-10 15:58:15 +0100771
Mark Brown33f301a2009-02-26 19:24:20 +0000772 switch (dcdc) {
773 case WM8350_DCDC_1:
774 reg = WM8350_DCDC1_FORCE_PWM;
775 break;
776 case WM8350_DCDC_3:
777 reg = WM8350_DCDC3_FORCE_PWM;
778 break;
779 case WM8350_DCDC_4:
780 reg = WM8350_DCDC4_FORCE_PWM;
781 break;
782 case WM8350_DCDC_6:
783 reg = WM8350_DCDC6_FORCE_PWM;
784 break;
785 default:
Mark Brownda091552008-10-10 15:58:15 +0100786 return -EINVAL;
Mark Brown33f301a2009-02-26 19:24:20 +0000787 }
Mark Brownda091552008-10-10 15:58:15 +0100788
789 mask = 1 << (dcdc - WM8350_DCDC_1);
790 active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask;
Mark Brown33f301a2009-02-26 19:24:20 +0000791 force = wm8350_reg_read(wm8350, reg) & WM8350_DCDC1_FORCE_PWM_ENA;
Mark Brownda091552008-10-10 15:58:15 +0100792 sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask;
Mark Brown33f301a2009-02-26 19:24:20 +0000793
Mark Brownda091552008-10-10 15:58:15 +0100794 dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x",
795 mask, active, sleep, force);
796
797 if (active && !sleep) {
798 if (force)
799 mode = REGULATOR_MODE_FAST;
800 else
801 mode = REGULATOR_MODE_NORMAL;
802 } else if (!active && !sleep)
803 mode = REGULATOR_MODE_IDLE;
Axel Lin8ecee362010-09-06 14:06:07 +0800804 else if (sleep)
Mark Brownda091552008-10-10 15:58:15 +0100805 mode = REGULATOR_MODE_STANDBY;
806
807 return mode;
808}
809
810static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev)
811{
812 return REGULATOR_MODE_NORMAL;
813}
814
815struct wm8350_dcdc_efficiency {
816 int uA_load_min;
817 int uA_load_max;
818 unsigned int mode;
819};
820
821static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = {
822 {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */
823 {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */
824 {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
825 {-1, -1, REGULATOR_MODE_NORMAL},
826};
827
828static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = {
829 {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */
830 {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */
831 {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
832 {-1, -1, REGULATOR_MODE_NORMAL},
833};
834
835static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff)
836{
837 int i = 0;
838
839 while (eff[i].uA_load_min != -1) {
840 if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max)
841 return eff[i].mode;
Colin Ian Kingf97a2362018-04-20 10:26:23 +0100842 i++;
Mark Brownda091552008-10-10 15:58:15 +0100843 }
844 return REGULATOR_MODE_NORMAL;
845}
846
847/* Query the regulator for it's most efficient mode @ uV,uA
848 * WM8350 regulator efficiency is pretty similar over
849 * different input and output uV.
850 */
851static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev,
852 int input_uV, int output_uV,
853 int output_uA)
854{
855 int dcdc = rdev_get_id(rdev), mode;
856
857 switch (dcdc) {
858 case WM8350_DCDC_1:
859 case WM8350_DCDC_6:
860 mode = get_mode(output_uA, dcdc1_6_efficiency);
861 break;
862 case WM8350_DCDC_3:
863 case WM8350_DCDC_4:
864 mode = get_mode(output_uA, dcdc3_4_efficiency);
865 break;
866 default:
867 mode = REGULATOR_MODE_NORMAL;
868 break;
869 }
870 return mode;
871}
872
Julia Lawallb0d6dd32015-12-19 16:31:24 +0100873static const struct regulator_ops wm8350_dcdc_ops = {
Mark Brown107a3962012-05-09 22:22:30 +0100874 .set_voltage_sel = regulator_set_voltage_sel_regmap,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100875 .get_voltage_sel = regulator_get_voltage_sel_regmap,
Mark Brown107a3962012-05-09 22:22:30 +0100876 .list_voltage = regulator_list_voltage_linear,
Axel Linfcbb71f2012-06-03 23:12:16 +0800877 .map_voltage = regulator_map_voltage_linear,
Mark Browna540f682012-04-30 21:08:59 +0100878 .enable = regulator_enable_regmap,
879 .disable = regulator_disable_regmap,
880 .is_enabled = regulator_is_enabled_regmap,
Mark Brownda091552008-10-10 15:58:15 +0100881 .get_mode = wm8350_dcdc_get_mode,
882 .set_mode = wm8350_dcdc_set_mode,
883 .get_optimum_mode = wm8350_dcdc_get_optimum_mode,
Mark Brownda091552008-10-10 15:58:15 +0100884 .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage,
885 .set_suspend_enable = wm8350_dcdc_set_suspend_enable,
886 .set_suspend_disable = wm8350_dcdc_set_suspend_disable,
887 .set_suspend_mode = wm8350_dcdc_set_suspend_mode,
888};
889
Julia Lawallb0d6dd32015-12-19 16:31:24 +0100890static const struct regulator_ops wm8350_dcdc2_5_ops = {
Mark Browna540f682012-04-30 21:08:59 +0100891 .enable = regulator_enable_regmap,
892 .disable = regulator_disable_regmap,
893 .is_enabled = regulator_is_enabled_regmap,
Mark Brownda091552008-10-10 15:58:15 +0100894 .set_suspend_enable = wm8350_dcdc25_set_suspend_enable,
895 .set_suspend_disable = wm8350_dcdc25_set_suspend_disable,
896};
897
Julia Lawallb0d6dd32015-12-19 16:31:24 +0100898static const struct regulator_ops wm8350_ldo_ops = {
Mark Brownc36a1cd2013-07-02 23:35:42 +0100899 .map_voltage = regulator_map_voltage_linear_range,
Mark Brownfc492f92012-05-09 22:27:41 +0100900 .set_voltage_sel = regulator_set_voltage_sel_regmap,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100901 .get_voltage_sel = regulator_get_voltage_sel_regmap,
Mark Brownc36a1cd2013-07-02 23:35:42 +0100902 .list_voltage = regulator_list_voltage_linear_range,
Mark Browna540f682012-04-30 21:08:59 +0100903 .enable = regulator_enable_regmap,
904 .disable = regulator_disable_regmap,
905 .is_enabled = regulator_is_enabled_regmap,
Mark Brownda091552008-10-10 15:58:15 +0100906 .get_mode = wm8350_ldo_get_mode,
907 .set_suspend_voltage = wm8350_ldo_set_suspend_voltage,
908 .set_suspend_enable = wm8350_ldo_set_suspend_enable,
909 .set_suspend_disable = wm8350_ldo_set_suspend_disable,
910};
911
Julia Lawallb0d6dd32015-12-19 16:31:24 +0100912static const struct regulator_ops wm8350_isink_ops = {
Axel Lindfeb7a92019-03-13 00:33:57 +0800913 .set_current_limit = regulator_set_current_limit_regmap,
914 .get_current_limit = regulator_get_current_limit_regmap,
Mark Brownda091552008-10-10 15:58:15 +0100915 .enable = wm8350_isink_enable,
916 .disable = wm8350_isink_disable,
917 .is_enabled = wm8350_isink_is_enabled,
Mark Brown75c8ac22010-01-04 17:24:01 +0000918 .enable_time = wm8350_isink_enable_time,
Mark Brownda091552008-10-10 15:58:15 +0100919};
920
Mark Brown47924b62012-04-03 20:47:15 +0100921static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
Mark Brownda091552008-10-10 15:58:15 +0100922 {
923 .name = "DCDC1",
924 .id = WM8350_DCDC_1,
925 .ops = &wm8350_dcdc_ops,
926 .irq = WM8350_IRQ_UV_DC1,
927 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +0000928 .n_voltages = WM8350_DCDC_MAX_VSEL + 1,
Mark Brown107a3962012-05-09 22:22:30 +0100929 .min_uV = 850000,
930 .uV_step = 25000,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100931 .vsel_reg = WM8350_DCDC1_CONTROL,
932 .vsel_mask = WM8350_DC1_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +0100933 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
934 .enable_mask = WM8350_DC1_ENA,
Mark Brownda091552008-10-10 15:58:15 +0100935 .owner = THIS_MODULE,
936 },
937 {
938 .name = "DCDC2",
939 .id = WM8350_DCDC_2,
940 .ops = &wm8350_dcdc2_5_ops,
941 .irq = WM8350_IRQ_UV_DC2,
942 .type = REGULATOR_VOLTAGE,
Mark Browna540f682012-04-30 21:08:59 +0100943 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
944 .enable_mask = WM8350_DC2_ENA,
Mark Brownda091552008-10-10 15:58:15 +0100945 .owner = THIS_MODULE,
946 },
947 {
948 .name = "DCDC3",
949 .id = WM8350_DCDC_3,
950 .ops = &wm8350_dcdc_ops,
951 .irq = WM8350_IRQ_UV_DC3,
952 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +0000953 .n_voltages = WM8350_DCDC_MAX_VSEL + 1,
Mark Brown107a3962012-05-09 22:22:30 +0100954 .min_uV = 850000,
955 .uV_step = 25000,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100956 .vsel_reg = WM8350_DCDC3_CONTROL,
957 .vsel_mask = WM8350_DC3_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +0100958 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
959 .enable_mask = WM8350_DC3_ENA,
Mark Brownda091552008-10-10 15:58:15 +0100960 .owner = THIS_MODULE,
961 },
962 {
963 .name = "DCDC4",
964 .id = WM8350_DCDC_4,
965 .ops = &wm8350_dcdc_ops,
966 .irq = WM8350_IRQ_UV_DC4,
967 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +0000968 .n_voltages = WM8350_DCDC_MAX_VSEL + 1,
Axel Lin0ff47242012-06-12 17:18:49 +0800969 .min_uV = 850000,
970 .uV_step = 25000,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100971 .vsel_reg = WM8350_DCDC4_CONTROL,
972 .vsel_mask = WM8350_DC4_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +0100973 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
974 .enable_mask = WM8350_DC4_ENA,
Mark Brownda091552008-10-10 15:58:15 +0100975 .owner = THIS_MODULE,
976 },
977 {
978 .name = "DCDC5",
979 .id = WM8350_DCDC_5,
980 .ops = &wm8350_dcdc2_5_ops,
981 .irq = WM8350_IRQ_UV_DC5,
982 .type = REGULATOR_VOLTAGE,
Mark Browna540f682012-04-30 21:08:59 +0100983 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
984 .enable_mask = WM8350_DC5_ENA,
Mark Brownda091552008-10-10 15:58:15 +0100985 .owner = THIS_MODULE,
986 },
987 {
988 .name = "DCDC6",
989 .id = WM8350_DCDC_6,
990 .ops = &wm8350_dcdc_ops,
991 .irq = WM8350_IRQ_UV_DC6,
992 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +0000993 .n_voltages = WM8350_DCDC_MAX_VSEL + 1,
Mark Brown107a3962012-05-09 22:22:30 +0100994 .min_uV = 850000,
995 .uV_step = 25000,
Mark Brownb4ec87a2012-04-30 21:00:10 +0100996 .vsel_reg = WM8350_DCDC6_CONTROL,
997 .vsel_mask = WM8350_DC6_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +0100998 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
999 .enable_mask = WM8350_DC6_ENA,
Mark Brownda091552008-10-10 15:58:15 +01001000 .owner = THIS_MODULE,
1001 },
1002 {
1003 .name = "LDO1",
1004 .id = WM8350_LDO_1,
1005 .ops = &wm8350_ldo_ops,
1006 .irq = WM8350_IRQ_UV_LDO1,
1007 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +00001008 .n_voltages = WM8350_LDO1_VSEL_MASK + 1,
Mark Brownc36a1cd2013-07-02 23:35:42 +01001009 .linear_ranges = wm8350_ldo_ranges,
1010 .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
Mark Brownb4ec87a2012-04-30 21:00:10 +01001011 .vsel_reg = WM8350_LDO1_CONTROL,
1012 .vsel_mask = WM8350_LDO1_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +01001013 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
1014 .enable_mask = WM8350_LDO1_ENA,
Mark Brownda091552008-10-10 15:58:15 +01001015 .owner = THIS_MODULE,
1016 },
1017 {
1018 .name = "LDO2",
1019 .id = WM8350_LDO_2,
1020 .ops = &wm8350_ldo_ops,
1021 .irq = WM8350_IRQ_UV_LDO2,
1022 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +00001023 .n_voltages = WM8350_LDO2_VSEL_MASK + 1,
Mark Brownc36a1cd2013-07-02 23:35:42 +01001024 .linear_ranges = wm8350_ldo_ranges,
1025 .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
Mark Brownb4ec87a2012-04-30 21:00:10 +01001026 .vsel_reg = WM8350_LDO2_CONTROL,
1027 .vsel_mask = WM8350_LDO2_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +01001028 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
1029 .enable_mask = WM8350_LDO2_ENA,
Mark Brownda091552008-10-10 15:58:15 +01001030 .owner = THIS_MODULE,
1031 },
1032 {
1033 .name = "LDO3",
1034 .id = WM8350_LDO_3,
1035 .ops = &wm8350_ldo_ops,
1036 .irq = WM8350_IRQ_UV_LDO3,
1037 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +00001038 .n_voltages = WM8350_LDO3_VSEL_MASK + 1,
Mark Brownc36a1cd2013-07-02 23:35:42 +01001039 .linear_ranges = wm8350_ldo_ranges,
1040 .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
Mark Brownb4ec87a2012-04-30 21:00:10 +01001041 .vsel_reg = WM8350_LDO3_CONTROL,
1042 .vsel_mask = WM8350_LDO3_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +01001043 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
1044 .enable_mask = WM8350_LDO3_ENA,
Mark Brownda091552008-10-10 15:58:15 +01001045 .owner = THIS_MODULE,
1046 },
1047 {
1048 .name = "LDO4",
1049 .id = WM8350_LDO_4,
1050 .ops = &wm8350_ldo_ops,
1051 .irq = WM8350_IRQ_UV_LDO4,
1052 .type = REGULATOR_VOLTAGE,
Mark Brown221a7c72009-03-02 16:32:47 +00001053 .n_voltages = WM8350_LDO4_VSEL_MASK + 1,
Mark Brownc36a1cd2013-07-02 23:35:42 +01001054 .linear_ranges = wm8350_ldo_ranges,
1055 .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
Mark Brownb4ec87a2012-04-30 21:00:10 +01001056 .vsel_reg = WM8350_LDO4_CONTROL,
1057 .vsel_mask = WM8350_LDO4_VSEL_MASK,
Mark Browna540f682012-04-30 21:08:59 +01001058 .enable_reg = WM8350_DCDC_LDO_REQUESTED,
1059 .enable_mask = WM8350_LDO4_ENA,
Mark Brownda091552008-10-10 15:58:15 +01001060 .owner = THIS_MODULE,
1061 },
1062 {
1063 .name = "ISINKA",
1064 .id = WM8350_ISINK_A,
1065 .ops = &wm8350_isink_ops,
1066 .irq = WM8350_IRQ_CS1,
1067 .type = REGULATOR_CURRENT,
1068 .owner = THIS_MODULE,
Axel Lindfeb7a92019-03-13 00:33:57 +08001069 .curr_table = isink_cur,
1070 .n_current_limits = ARRAY_SIZE(isink_cur),
1071 .csel_reg = WM8350_CURRENT_SINK_DRIVER_A,
1072 .csel_mask = WM8350_CS1_ISEL_MASK,
Mark Brownda091552008-10-10 15:58:15 +01001073 },
1074 {
1075 .name = "ISINKB",
1076 .id = WM8350_ISINK_B,
1077 .ops = &wm8350_isink_ops,
1078 .irq = WM8350_IRQ_CS2,
1079 .type = REGULATOR_CURRENT,
1080 .owner = THIS_MODULE,
Axel Lindfeb7a92019-03-13 00:33:57 +08001081 .curr_table = isink_cur,
1082 .n_current_limits = ARRAY_SIZE(isink_cur),
1083 .csel_reg = WM8350_CURRENT_SINK_DRIVER_B,
1084 .csel_mask = WM8350_CS2_ISEL_MASK,
Mark Brownda091552008-10-10 15:58:15 +01001085 },
1086};
1087
Mark Brown5a65edb2009-11-04 16:10:51 +00001088static irqreturn_t pmic_uv_handler(int irq, void *data)
Mark Brownda091552008-10-10 15:58:15 +01001089{
1090 struct regulator_dev *rdev = (struct regulator_dev *)data;
1091
1092 if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
1093 regulator_notifier_call_chain(rdev,
1094 REGULATOR_EVENT_REGULATION_OUT,
Geert Uytterhoeven8f45acb2015-02-23 17:13:31 +01001095 NULL);
Mark Brownda091552008-10-10 15:58:15 +01001096 else
1097 regulator_notifier_call_chain(rdev,
1098 REGULATOR_EVENT_UNDER_VOLTAGE,
Geert Uytterhoeven8f45acb2015-02-23 17:13:31 +01001099 NULL);
Mark Brown5a65edb2009-11-04 16:10:51 +00001100
1101 return IRQ_HANDLED;
Mark Brownda091552008-10-10 15:58:15 +01001102}
1103
1104static int wm8350_regulator_probe(struct platform_device *pdev)
1105{
1106 struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
Mark Brownc172708d2012-04-04 00:50:22 +01001107 struct regulator_config config = { };
Mark Brownda091552008-10-10 15:58:15 +01001108 struct regulator_dev *rdev;
1109 int ret;
1110 u16 val;
1111
1112 if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B)
1113 return -ENODEV;
1114
1115 /* do any regulatior specific init */
1116 switch (pdev->id) {
1117 case WM8350_DCDC_1:
1118 val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
1119 wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
1120 break;
1121 case WM8350_DCDC_3:
1122 val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
1123 wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
1124 break;
1125 case WM8350_DCDC_4:
1126 val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
1127 wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
1128 break;
1129 case WM8350_DCDC_6:
1130 val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
1131 wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
1132 break;
1133 }
1134
Mark Brownc172708d2012-04-04 00:50:22 +01001135 config.dev = &pdev->dev;
Jingoo Handff91d02013-07-30 17:20:47 +09001136 config.init_data = dev_get_platdata(&pdev->dev);
Mark Brownc172708d2012-04-04 00:50:22 +01001137 config.driver_data = dev_get_drvdata(&pdev->dev);
Mark Brownb4ec87a2012-04-30 21:00:10 +01001138 config.regmap = wm8350->regmap;
Mark Brownc172708d2012-04-04 00:50:22 +01001139
Mark Brownda091552008-10-10 15:58:15 +01001140 /* register regulator */
Mark Browne57e5462013-08-31 12:00:37 +01001141 rdev = devm_regulator_register(&pdev->dev, &wm8350_reg[pdev->id],
1142 &config);
Mark Brownda091552008-10-10 15:58:15 +01001143 if (IS_ERR(rdev)) {
1144 dev_err(&pdev->dev, "failed to register %s\n",
1145 wm8350_reg[pdev->id].name);
1146 return PTR_ERR(rdev);
1147 }
1148
1149 /* register regulator IRQ */
1150 ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq,
Mark Brown5a65edb2009-11-04 16:10:51 +00001151 pmic_uv_handler, 0, "UV", rdev);
Mark Brownda091552008-10-10 15:58:15 +01001152 if (ret < 0) {
Mark Brownda091552008-10-10 15:58:15 +01001153 dev_err(&pdev->dev, "failed to register regulator %s IRQ\n",
1154 wm8350_reg[pdev->id].name);
1155 return ret;
1156 }
1157
Mark Brownda091552008-10-10 15:58:15 +01001158 return 0;
1159}
1160
1161static int wm8350_regulator_remove(struct platform_device *pdev)
1162{
1163 struct regulator_dev *rdev = platform_get_drvdata(pdev);
1164 struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
1165
Mark Brownf99344f2010-01-05 13:59:07 +00001166 wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev);
Mark Brownda091552008-10-10 15:58:15 +01001167
Mark Brownda091552008-10-10 15:58:15 +01001168 return 0;
1169}
1170
1171int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
1172 struct regulator_init_data *initdata)
1173{
1174 struct platform_device *pdev;
1175 int ret;
Roel Kluin4dee4d42009-06-15 22:30:39 +02001176 if (reg < 0 || reg >= NUM_WM8350_REGULATORS)
1177 return -EINVAL;
Mark Brownda091552008-10-10 15:58:15 +01001178
1179 if (wm8350->pmic.pdev[reg])
1180 return -EBUSY;
1181
Mark Brown645524a2008-12-18 23:12:16 +01001182 if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 &&
1183 reg > wm8350->pmic.max_dcdc)
1184 return -ENODEV;
1185 if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B &&
1186 reg > wm8350->pmic.max_isink)
1187 return -ENODEV;
1188
Mark Brownda091552008-10-10 15:58:15 +01001189 pdev = platform_device_alloc("wm8350-regulator", reg);
1190 if (!pdev)
1191 return -ENOMEM;
1192
1193 wm8350->pmic.pdev[reg] = pdev;
1194
1195 initdata->driver_data = wm8350;
1196
1197 pdev->dev.platform_data = initdata;
1198 pdev->dev.parent = wm8350->dev;
1199 platform_set_drvdata(pdev, wm8350);
1200
1201 ret = platform_device_add(pdev);
1202
1203 if (ret != 0) {
1204 dev_err(wm8350->dev, "Failed to register regulator %d: %d\n",
1205 reg, ret);
Axel Line9a1c512010-07-26 10:41:58 +08001206 platform_device_put(pdev);
Mark Brownda091552008-10-10 15:58:15 +01001207 wm8350->pmic.pdev[reg] = NULL;
1208 }
1209
1210 return ret;
1211}
1212EXPORT_SYMBOL_GPL(wm8350_register_regulator);
1213
Mark Brown0081e802008-12-04 16:52:33 +00001214/**
1215 * wm8350_register_led - Register a WM8350 LED output
1216 *
Lee Jones2f5add12020-06-25 17:36:10 +01001217 * @wm8350: The WM8350 device to configure.
1218 * @lednum: LED device index to create.
1219 * @dcdc: The DCDC to use for the LED.
1220 * @isink: The ISINK to use for the LED.
1221 * @pdata: Configuration for the LED.
Mark Brown0081e802008-12-04 16:52:33 +00001222 *
1223 * The WM8350 supports the use of an ISINK together with a DCDC to
1224 * provide a power-efficient LED driver. This function registers the
1225 * regulators and instantiates the platform device for a LED. The
1226 * operating modes for the LED regulators must be configured using
1227 * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and
1228 * wm8350_dcdc_set_slot() prior to calling this function.
1229 */
1230int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
1231 struct wm8350_led_platform_data *pdata)
1232{
1233 struct wm8350_led *led;
1234 struct platform_device *pdev;
1235 int ret;
1236
Roel Kluin8dd2c9e2009-01-17 16:06:40 +01001237 if (lednum >= ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) {
Mark Brown0081e802008-12-04 16:52:33 +00001238 dev_err(wm8350->dev, "Invalid LED index %d\n", lednum);
1239 return -ENODEV;
1240 }
1241
1242 led = &wm8350->pmic.led[lednum];
1243
1244 if (led->pdev) {
1245 dev_err(wm8350->dev, "LED %d already allocated\n", lednum);
1246 return -EINVAL;
1247 }
1248
1249 pdev = platform_device_alloc("wm8350-led", lednum);
1250 if (pdev == NULL) {
1251 dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum);
1252 return -ENOMEM;
1253 }
1254
Mark Brown34ce8d02012-02-02 13:45:09 +00001255 led->isink_consumer.dev_name = dev_name(&pdev->dev);
Mark Brown0081e802008-12-04 16:52:33 +00001256 led->isink_consumer.supply = "led_isink";
1257 led->isink_init.num_consumer_supplies = 1;
1258 led->isink_init.consumer_supplies = &led->isink_consumer;
1259 led->isink_init.constraints.min_uA = 0;
1260 led->isink_init.constraints.max_uA = pdata->max_uA;
Mark Browna2fad9b2010-01-04 15:30:54 +00001261 led->isink_init.constraints.valid_ops_mask
1262 = REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_STATUS;
Mark Brown0081e802008-12-04 16:52:33 +00001263 led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
1264 ret = wm8350_register_regulator(wm8350, isink, &led->isink_init);
1265 if (ret != 0) {
1266 platform_device_put(pdev);
1267 return ret;
1268 }
1269
Mark Brown34ce8d02012-02-02 13:45:09 +00001270 led->dcdc_consumer.dev_name = dev_name(&pdev->dev);
Mark Brown0081e802008-12-04 16:52:33 +00001271 led->dcdc_consumer.supply = "led_vcc";
1272 led->dcdc_init.num_consumer_supplies = 1;
1273 led->dcdc_init.consumer_supplies = &led->dcdc_consumer;
1274 led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
Mark Browna2fad9b2010-01-04 15:30:54 +00001275 led->dcdc_init.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
Mark Brown0081e802008-12-04 16:52:33 +00001276 ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init);
1277 if (ret != 0) {
1278 platform_device_put(pdev);
1279 return ret;
1280 }
1281
1282 switch (isink) {
1283 case WM8350_ISINK_A:
1284 wm8350->pmic.isink_A_dcdc = dcdc;
1285 break;
1286 case WM8350_ISINK_B:
1287 wm8350->pmic.isink_B_dcdc = dcdc;
1288 break;
1289 }
1290
1291 pdev->dev.platform_data = pdata;
1292 pdev->dev.parent = wm8350->dev;
1293 ret = platform_device_add(pdev);
1294 if (ret != 0) {
1295 dev_err(wm8350->dev, "Failed to register LED %d: %d\n",
1296 lednum, ret);
1297 platform_device_put(pdev);
1298 return ret;
1299 }
1300
1301 led->pdev = pdev;
1302
1303 return 0;
1304}
1305EXPORT_SYMBOL_GPL(wm8350_register_led);
1306
Mark Brownda091552008-10-10 15:58:15 +01001307static struct platform_driver wm8350_regulator_driver = {
1308 .probe = wm8350_regulator_probe,
1309 .remove = wm8350_regulator_remove,
1310 .driver = {
1311 .name = "wm8350-regulator",
1312 },
1313};
1314
1315static int __init wm8350_regulator_init(void)
1316{
1317 return platform_driver_register(&wm8350_regulator_driver);
1318}
1319subsys_initcall(wm8350_regulator_init);
1320
1321static void __exit wm8350_regulator_exit(void)
1322{
1323 platform_driver_unregister(&wm8350_regulator_driver);
1324}
1325module_exit(wm8350_regulator_exit);
1326
1327/* Module information */
1328MODULE_AUTHOR("Liam Girdwood");
1329MODULE_DESCRIPTION("WM8350 voltage and current regulator driver");
1330MODULE_LICENSE("GPL");
Mark Brown38c53c82009-04-28 11:13:55 +01001331MODULE_ALIAS("platform:wm8350-regulator");