diff options
author | Margarita Olaya <magi@slimlogic.co.uk> | 2011-06-09 15:50:27 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-07-31 17:28:22 -0400 |
commit | 9260ad98dcb0e6ec3a9ee6b13699cf52c684dfd2 (patch) | |
tree | 121bf806cdd048335172a06d88544c41be1c5561 /drivers/regulator | |
parent | 668a6cc710ee054af2b059d27bbec746ead0fbca (diff) |
tps65912: add regulator driver
The tps65912 consist of 4 DCDCs and 10 LDOs. The output voltages can be
configured by the SPI or I2C interface, they are meant to supply power
to the main processor and other components.
Signed-off-by: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 6 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/tps65912-regulator.c | 800 |
3 files changed, 807 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 118eb213eb3a..13722a7e7abd 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig | |||
@@ -249,6 +249,12 @@ config REGULATOR_TPS6507X | |||
249 | three step-down converters and two general-purpose LDO voltage regulators. | 249 | three step-down converters and two general-purpose LDO voltage regulators. |
250 | It supports TI's software based Class-2 SmartReflex implementation. | 250 | It supports TI's software based Class-2 SmartReflex implementation. |
251 | 251 | ||
252 | config REGULATOR_TPS65912 | ||
253 | tristate "TI TPS65912 Power regulator" | ||
254 | depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) | ||
255 | help | ||
256 | This driver supports TPS65912 voltage regulator chip. | ||
257 | |||
252 | config REGULATOR_88PM8607 | 258 | config REGULATOR_88PM8607 |
253 | bool "Marvell 88PM8607 Power regulators" | 259 | bool "Marvell 88PM8607 Power regulators" |
254 | depends on MFD_88PM860X=y | 260 | depends on MFD_88PM860X=y |
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3932d2ec38f3..87ba2fd1dbcb 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile | |||
@@ -38,6 +38,7 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o | |||
38 | obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o | 38 | obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o |
39 | obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o | 39 | obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o |
40 | obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o | 40 | obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o |
41 | obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o | ||
41 | obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o | 42 | obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o |
42 | obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o | 43 | obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o |
43 | obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o | 44 | obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o |
diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c new file mode 100644 index 000000000000..d2c6542776f1 --- /dev/null +++ b/drivers/regulator/tps65912-regulator.c | |||
@@ -0,0 +1,800 @@ | |||
1 | /* | ||
2 | * tps65912.c -- TI tps65912 | ||
3 | * | ||
4 | * Copyright 2011 Texas Instruments Inc. | ||
5 | * | ||
6 | * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk> | ||
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 | * This driver is based on wm8350 implementation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/regulator/driver.h> | ||
22 | #include <linux/regulator/machine.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/gpio.h> | ||
26 | #include <linux/mfd/tps65912.h> | ||
27 | |||
28 | /* DCDC's */ | ||
29 | #define TPS65912_REG_DCDC1 0 | ||
30 | #define TPS65912_REG_DCDC2 1 | ||
31 | #define TPS65912_REG_DCDC3 2 | ||
32 | #define TPS65912_REG_DCDC4 3 | ||
33 | |||
34 | /* LDOs */ | ||
35 | #define TPS65912_REG_LDO1 4 | ||
36 | #define TPS65912_REG_LDO2 5 | ||
37 | #define TPS65912_REG_LDO3 6 | ||
38 | #define TPS65912_REG_LDO4 7 | ||
39 | #define TPS65912_REG_LDO5 8 | ||
40 | #define TPS65912_REG_LDO6 9 | ||
41 | #define TPS65912_REG_LDO7 10 | ||
42 | #define TPS65912_REG_LDO8 11 | ||
43 | #define TPS65912_REG_LDO9 12 | ||
44 | #define TPS65912_REG_LDO10 13 | ||
45 | |||
46 | #define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10 | ||
47 | |||
48 | /* Number of step-down converters available */ | ||
49 | #define TPS65912_NUM_DCDC 4 | ||
50 | |||
51 | /* Number of LDO voltage regulators available */ | ||
52 | #define TPS65912_NUM_LDO 10 | ||
53 | |||
54 | /* Number of total regulators available */ | ||
55 | #define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO) | ||
56 | |||
57 | #define TPS65912_REG_ENABLED 0x80 | ||
58 | #define OP_SELREG_MASK 0x40 | ||
59 | #define OP_SELREG_SHIFT 6 | ||
60 | |||
61 | struct tps_info { | ||
62 | const char *name; | ||
63 | }; | ||
64 | |||
65 | static struct tps_info tps65912_regs[] = { | ||
66 | { | ||
67 | .name = "DCDC1", | ||
68 | }, | ||
69 | { | ||
70 | .name = "DCDC2", | ||
71 | }, | ||
72 | { | ||
73 | .name = "DCDC3", | ||
74 | }, | ||
75 | { | ||
76 | .name = "DCDC4", | ||
77 | }, | ||
78 | { | ||
79 | .name = "LDO1", | ||
80 | }, | ||
81 | { | ||
82 | .name = "LDO2", | ||
83 | }, | ||
84 | { | ||
85 | .name = "LDO3", | ||
86 | }, | ||
87 | { | ||
88 | .name = "LDO4", | ||
89 | }, | ||
90 | { | ||
91 | .name = "LDO5", | ||
92 | }, | ||
93 | { | ||
94 | .name = "LDO6", | ||
95 | }, | ||
96 | { | ||
97 | .name = "LDO7", | ||
98 | }, | ||
99 | { | ||
100 | .name = "LDO8", | ||
101 | }, | ||
102 | { | ||
103 | .name = "LDO9", | ||
104 | }, | ||
105 | { | ||
106 | .name = "LDO10", | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | struct tps65912_reg { | ||
111 | struct regulator_desc desc[TPS65912_NUM_REGULATOR]; | ||
112 | struct tps65912 *mfd; | ||
113 | struct regulator_dev *rdev[TPS65912_NUM_REGULATOR]; | ||
114 | struct tps_info *info[TPS65912_NUM_REGULATOR]; | ||
115 | /* for read/write access */ | ||
116 | struct mutex io_lock; | ||
117 | int mode; | ||
118 | int (*get_ctrl_reg)(int); | ||
119 | int dcdc1_range; | ||
120 | int dcdc2_range; | ||
121 | int dcdc3_range; | ||
122 | int dcdc4_range; | ||
123 | int pwm_mode_reg; | ||
124 | int eco_reg; | ||
125 | }; | ||
126 | |||
127 | static int tps65912_get_range(struct tps65912_reg *pmic, int id) | ||
128 | { | ||
129 | struct tps65912 *mfd = pmic->mfd; | ||
130 | |||
131 | if (id > TPS65912_REG_DCDC4) | ||
132 | return 0; | ||
133 | |||
134 | switch (id) { | ||
135 | case TPS65912_REG_DCDC1: | ||
136 | pmic->dcdc1_range = tps65912_reg_read(mfd, | ||
137 | TPS65912_DCDC1_LIMIT); | ||
138 | if (pmic->dcdc1_range < 0) | ||
139 | return pmic->dcdc1_range; | ||
140 | pmic->dcdc1_range = (pmic->dcdc1_range & | ||
141 | DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; | ||
142 | return pmic->dcdc1_range; | ||
143 | case TPS65912_REG_DCDC2: | ||
144 | pmic->dcdc2_range = tps65912_reg_read(mfd, | ||
145 | TPS65912_DCDC2_LIMIT); | ||
146 | if (pmic->dcdc2_range < 0) | ||
147 | return pmic->dcdc2_range; | ||
148 | pmic->dcdc2_range = (pmic->dcdc2_range & | ||
149 | DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; | ||
150 | return pmic->dcdc2_range; | ||
151 | case TPS65912_REG_DCDC3: | ||
152 | pmic->dcdc3_range = tps65912_reg_read(mfd, | ||
153 | TPS65912_DCDC3_LIMIT); | ||
154 | if (pmic->dcdc3_range < 0) | ||
155 | return pmic->dcdc3_range; | ||
156 | pmic->dcdc3_range = (pmic->dcdc3_range & | ||
157 | DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; | ||
158 | return pmic->dcdc3_range; | ||
159 | case TPS65912_REG_DCDC4: | ||
160 | pmic->dcdc4_range = tps65912_reg_read(mfd, | ||
161 | TPS65912_DCDC4_LIMIT); | ||
162 | if (pmic->dcdc4_range < 0) | ||
163 | return pmic->dcdc4_range; | ||
164 | pmic->dcdc4_range = (pmic->dcdc4_range & | ||
165 | DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; | ||
166 | return pmic->dcdc4_range; | ||
167 | default: | ||
168 | return 0; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static unsigned long tps65912_vsel_to_uv_range0(u8 vsel) | ||
173 | { | ||
174 | unsigned long uv; | ||
175 | |||
176 | uv = ((vsel * 12500) + 500000); | ||
177 | return uv; | ||
178 | } | ||
179 | |||
180 | static unsigned long tps65912_vsel_to_uv_range1(u8 vsel) | ||
181 | { | ||
182 | unsigned long uv; | ||
183 | |||
184 | uv = ((vsel * 12500) + 700000); | ||
185 | return uv; | ||
186 | } | ||
187 | |||
188 | static unsigned long tps65912_vsel_to_uv_range2(u8 vsel) | ||
189 | { | ||
190 | unsigned long uv; | ||
191 | |||
192 | uv = ((vsel * 25000) + 500000); | ||
193 | return uv; | ||
194 | } | ||
195 | |||
196 | static unsigned long tps65912_vsel_to_uv_range3(u8 vsel) | ||
197 | { | ||
198 | unsigned long uv; | ||
199 | |||
200 | if (vsel == 0x3f) | ||
201 | uv = 3800000; | ||
202 | else | ||
203 | uv = ((vsel * 50000) + 500000); | ||
204 | |||
205 | return uv; | ||
206 | } | ||
207 | |||
208 | static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel) | ||
209 | { | ||
210 | unsigned long uv = 0; | ||
211 | |||
212 | if (vsel <= 32) | ||
213 | uv = ((vsel * 25000) + 800000); | ||
214 | else if (vsel > 32 && vsel <= 60) | ||
215 | uv = (((vsel - 32) * 50000) + 1600000); | ||
216 | else if (vsel > 60) | ||
217 | uv = (((vsel - 60) * 100000) + 3000000); | ||
218 | |||
219 | return uv; | ||
220 | } | ||
221 | |||
222 | static int tps65912_get_ctrl_register(int id) | ||
223 | { | ||
224 | switch (id) { | ||
225 | case TPS65912_REG_DCDC1: | ||
226 | return TPS65912_DCDC1_AVS; | ||
227 | case TPS65912_REG_DCDC2: | ||
228 | return TPS65912_DCDC2_AVS; | ||
229 | case TPS65912_REG_DCDC3: | ||
230 | return TPS65912_DCDC3_AVS; | ||
231 | case TPS65912_REG_DCDC4: | ||
232 | return TPS65912_DCDC4_AVS; | ||
233 | case TPS65912_REG_LDO1: | ||
234 | return TPS65912_LDO1_AVS; | ||
235 | case TPS65912_REG_LDO2: | ||
236 | return TPS65912_LDO2_AVS; | ||
237 | case TPS65912_REG_LDO3: | ||
238 | return TPS65912_LDO3_AVS; | ||
239 | case TPS65912_REG_LDO4: | ||
240 | return TPS65912_LDO4_AVS; | ||
241 | case TPS65912_REG_LDO5: | ||
242 | return TPS65912_LDO5; | ||
243 | case TPS65912_REG_LDO6: | ||
244 | return TPS65912_LDO6; | ||
245 | case TPS65912_REG_LDO7: | ||
246 | return TPS65912_LDO7; | ||
247 | case TPS65912_REG_LDO8: | ||
248 | return TPS65912_LDO8; | ||
249 | case TPS65912_REG_LDO9: | ||
250 | return TPS65912_LDO9; | ||
251 | case TPS65912_REG_LDO10: | ||
252 | return TPS65912_LDO10; | ||
253 | default: | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id) | ||
259 | { | ||
260 | struct tps65912 *mfd = pmic->mfd; | ||
261 | int opvsel = 0, sr = 0; | ||
262 | u8 reg = 0; | ||
263 | |||
264 | if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4) | ||
265 | return -EINVAL; | ||
266 | |||
267 | switch (id) { | ||
268 | case TPS65912_REG_DCDC1: | ||
269 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); | ||
270 | sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT); | ||
271 | if (sr) | ||
272 | reg = TPS65912_DCDC1_AVS; | ||
273 | else | ||
274 | reg = TPS65912_DCDC1_OP; | ||
275 | break; | ||
276 | case TPS65912_REG_DCDC2: | ||
277 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); | ||
278 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
279 | if (sr) | ||
280 | reg = TPS65912_DCDC2_AVS; | ||
281 | else | ||
282 | reg = TPS65912_DCDC2_OP; | ||
283 | break; | ||
284 | case TPS65912_REG_DCDC3: | ||
285 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); | ||
286 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
287 | if (sr) | ||
288 | reg = TPS65912_DCDC3_AVS; | ||
289 | else | ||
290 | reg = TPS65912_DCDC3_OP; | ||
291 | break; | ||
292 | case TPS65912_REG_DCDC4: | ||
293 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); | ||
294 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
295 | if (sr) | ||
296 | reg = TPS65912_DCDC4_AVS; | ||
297 | else | ||
298 | reg = TPS65912_DCDC4_OP; | ||
299 | break; | ||
300 | } | ||
301 | return reg; | ||
302 | } | ||
303 | |||
304 | static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id) | ||
305 | { | ||
306 | struct tps65912 *mfd = pmic->mfd; | ||
307 | int opvsel = 0, sr = 0; | ||
308 | u8 reg = 0; | ||
309 | |||
310 | if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10) | ||
311 | return -EINVAL; | ||
312 | |||
313 | switch (id) { | ||
314 | case TPS65912_REG_LDO1: | ||
315 | opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP); | ||
316 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
317 | if (sr) | ||
318 | reg = TPS65912_LDO1_AVS; | ||
319 | else | ||
320 | reg = TPS65912_LDO1_OP; | ||
321 | break; | ||
322 | case TPS65912_REG_LDO2: | ||
323 | opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP); | ||
324 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
325 | if (sr) | ||
326 | reg = TPS65912_LDO2_AVS; | ||
327 | else | ||
328 | reg = TPS65912_LDO2_OP; | ||
329 | break; | ||
330 | case TPS65912_REG_LDO3: | ||
331 | opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP); | ||
332 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
333 | if (sr) | ||
334 | reg = TPS65912_LDO3_AVS; | ||
335 | else | ||
336 | reg = TPS65912_LDO3_OP; | ||
337 | break; | ||
338 | case TPS65912_REG_LDO4: | ||
339 | opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP); | ||
340 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
341 | if (sr) | ||
342 | reg = TPS65912_LDO4_AVS; | ||
343 | else | ||
344 | reg = TPS65912_LDO4_OP; | ||
345 | break; | ||
346 | case TPS65912_REG_LDO5: | ||
347 | reg = TPS65912_LDO5; | ||
348 | break; | ||
349 | case TPS65912_REG_LDO6: | ||
350 | reg = TPS65912_LDO6; | ||
351 | break; | ||
352 | case TPS65912_REG_LDO7: | ||
353 | reg = TPS65912_LDO7; | ||
354 | break; | ||
355 | case TPS65912_REG_LDO8: | ||
356 | reg = TPS65912_LDO8; | ||
357 | break; | ||
358 | case TPS65912_REG_LDO9: | ||
359 | reg = TPS65912_LDO9; | ||
360 | break; | ||
361 | case TPS65912_REG_LDO10: | ||
362 | reg = TPS65912_LDO10; | ||
363 | break; | ||
364 | } | ||
365 | |||
366 | return reg; | ||
367 | } | ||
368 | |||
369 | static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id) | ||
370 | { | ||
371 | switch (id) { | ||
372 | case TPS65912_REG_DCDC1: | ||
373 | pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL; | ||
374 | pmic->eco_reg = TPS65912_DCDC1_AVS; | ||
375 | break; | ||
376 | case TPS65912_REG_DCDC2: | ||
377 | pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL; | ||
378 | pmic->eco_reg = TPS65912_DCDC2_AVS; | ||
379 | break; | ||
380 | case TPS65912_REG_DCDC3: | ||
381 | pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL; | ||
382 | pmic->eco_reg = TPS65912_DCDC3_AVS; | ||
383 | break; | ||
384 | case TPS65912_REG_DCDC4: | ||
385 | pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL; | ||
386 | pmic->eco_reg = TPS65912_DCDC4_AVS; | ||
387 | break; | ||
388 | default: | ||
389 | return -EINVAL; | ||
390 | } | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int tps65912_reg_is_enabled(struct regulator_dev *dev) | ||
396 | { | ||
397 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
398 | struct tps65912 *mfd = pmic->mfd; | ||
399 | int reg, value, id = rdev_get_id(dev); | ||
400 | |||
401 | if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) | ||
402 | return -EINVAL; | ||
403 | |||
404 | reg = pmic->get_ctrl_reg(id); | ||
405 | if (reg < 0) | ||
406 | return reg; | ||
407 | |||
408 | value = tps65912_reg_read(mfd, reg); | ||
409 | if (value < 0) | ||
410 | return value; | ||
411 | |||
412 | return value & TPS65912_REG_ENABLED; | ||
413 | } | ||
414 | |||
415 | static int tps65912_reg_enable(struct regulator_dev *dev) | ||
416 | { | ||
417 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
418 | struct tps65912 *mfd = pmic->mfd; | ||
419 | int id = rdev_get_id(dev); | ||
420 | u8 reg; | ||
421 | |||
422 | if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) | ||
423 | return -EINVAL; | ||
424 | |||
425 | reg = pmic->get_ctrl_reg(id); | ||
426 | if (reg < 0) | ||
427 | return reg; | ||
428 | |||
429 | return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED); | ||
430 | } | ||
431 | |||
432 | static int tps65912_reg_disable(struct regulator_dev *dev) | ||
433 | { | ||
434 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
435 | struct tps65912 *mfd = pmic->mfd; | ||
436 | int id = rdev_get_id(dev), reg; | ||
437 | |||
438 | reg = pmic->get_ctrl_reg(id); | ||
439 | if (reg < 0) | ||
440 | return reg; | ||
441 | |||
442 | return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED); | ||
443 | } | ||
444 | |||
445 | static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode) | ||
446 | { | ||
447 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
448 | struct tps65912 *mfd = pmic->mfd; | ||
449 | int pwm_mode, eco, id = rdev_get_id(dev); | ||
450 | |||
451 | tps65912_get_mode_regiters(pmic, id); | ||
452 | |||
453 | pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); | ||
454 | eco = tps65912_reg_read(mfd, pmic->eco_reg); | ||
455 | |||
456 | pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; | ||
457 | eco &= DCDC_AVS_ECO_MASK; | ||
458 | |||
459 | switch (mode) { | ||
460 | case REGULATOR_MODE_FAST: | ||
461 | /* Verify if mode alredy set */ | ||
462 | if (pwm_mode && !eco) | ||
463 | break; | ||
464 | tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); | ||
465 | tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); | ||
466 | break; | ||
467 | case REGULATOR_MODE_NORMAL: | ||
468 | case REGULATOR_MODE_IDLE: | ||
469 | if (!pwm_mode && !eco) | ||
470 | break; | ||
471 | tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); | ||
472 | tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); | ||
473 | break; | ||
474 | case REGULATOR_MODE_STANDBY: | ||
475 | if (!pwm_mode && eco) | ||
476 | break; | ||
477 | tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); | ||
478 | tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); | ||
479 | break; | ||
480 | default: | ||
481 | return -EINVAL; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static unsigned int tps65912_get_mode(struct regulator_dev *dev) | ||
488 | { | ||
489 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
490 | struct tps65912 *mfd = pmic->mfd; | ||
491 | int pwm_mode, eco, mode = 0, id = rdev_get_id(dev); | ||
492 | |||
493 | tps65912_get_mode_regiters(pmic, id); | ||
494 | |||
495 | pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); | ||
496 | eco = tps65912_reg_read(mfd, pmic->eco_reg); | ||
497 | |||
498 | pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; | ||
499 | eco &= DCDC_AVS_ECO_MASK; | ||
500 | |||
501 | if (pwm_mode && !eco) | ||
502 | mode = REGULATOR_MODE_FAST; | ||
503 | else if (!pwm_mode && !eco) | ||
504 | mode = REGULATOR_MODE_NORMAL; | ||
505 | else if (!pwm_mode && eco) | ||
506 | mode = REGULATOR_MODE_STANDBY; | ||
507 | |||
508 | return mode; | ||
509 | } | ||
510 | |||
511 | static int tps65912_get_voltage_dcdc(struct regulator_dev *dev) | ||
512 | { | ||
513 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
514 | struct tps65912 *mfd = pmic->mfd; | ||
515 | int id = rdev_get_id(dev), voltage = 0, range; | ||
516 | int opvsel = 0, avsel = 0, sr, vsel; | ||
517 | |||
518 | switch (id) { | ||
519 | case TPS65912_REG_DCDC1: | ||
520 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); | ||
521 | avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS); | ||
522 | range = pmic->dcdc1_range; | ||
523 | break; | ||
524 | case TPS65912_REG_DCDC2: | ||
525 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); | ||
526 | avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS); | ||
527 | range = pmic->dcdc2_range; | ||
528 | break; | ||
529 | case TPS65912_REG_DCDC3: | ||
530 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); | ||
531 | avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS); | ||
532 | range = pmic->dcdc3_range; | ||
533 | break; | ||
534 | case TPS65912_REG_DCDC4: | ||
535 | opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); | ||
536 | avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS); | ||
537 | range = pmic->dcdc4_range; | ||
538 | break; | ||
539 | default: | ||
540 | return -EINVAL; | ||
541 | } | ||
542 | |||
543 | sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; | ||
544 | if (sr) | ||
545 | vsel = avsel; | ||
546 | else | ||
547 | vsel = opvsel; | ||
548 | vsel &= 0x3F; | ||
549 | |||
550 | switch (range) { | ||
551 | case 0: | ||
552 | /* 0.5 - 1.2875V in 12.5mV steps */ | ||
553 | voltage = tps65912_vsel_to_uv_range0(vsel); | ||
554 | break; | ||
555 | case 1: | ||
556 | /* 0.7 - 1.4875V in 12.5mV steps */ | ||
557 | voltage = tps65912_vsel_to_uv_range1(vsel); | ||
558 | break; | ||
559 | case 2: | ||
560 | /* 0.5 - 2.075V in 25mV steps */ | ||
561 | voltage = tps65912_vsel_to_uv_range2(vsel); | ||
562 | break; | ||
563 | case 3: | ||
564 | /* 0.5 - 3.8V in 50mV steps */ | ||
565 | voltage = tps65912_vsel_to_uv_range3(vsel); | ||
566 | break; | ||
567 | } | ||
568 | return voltage; | ||
569 | } | ||
570 | |||
571 | static int tps65912_set_voltage_dcdc(struct regulator_dev *dev, | ||
572 | unsigned selector) | ||
573 | { | ||
574 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
575 | struct tps65912 *mfd = pmic->mfd; | ||
576 | int id = rdev_get_id(dev); | ||
577 | int value; | ||
578 | u8 reg; | ||
579 | |||
580 | reg = tps65912_get_dcdc_sel_register(pmic, id); | ||
581 | value = tps65912_reg_read(mfd, reg); | ||
582 | value &= 0xC0; | ||
583 | return tps65912_reg_write(mfd, reg, selector | value); | ||
584 | } | ||
585 | |||
586 | static int tps65912_get_voltage_ldo(struct regulator_dev *dev) | ||
587 | { | ||
588 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
589 | struct tps65912 *mfd = pmic->mfd; | ||
590 | int id = rdev_get_id(dev); | ||
591 | int vsel = 0; | ||
592 | u8 reg; | ||
593 | |||
594 | reg = tps65912_get_ldo_sel_register(pmic, id); | ||
595 | vsel = tps65912_reg_read(mfd, reg); | ||
596 | vsel &= 0x3F; | ||
597 | |||
598 | return tps65912_vsel_to_uv_ldo(vsel); | ||
599 | } | ||
600 | |||
601 | static int tps65912_set_voltage_ldo(struct regulator_dev *dev, | ||
602 | unsigned selector) | ||
603 | { | ||
604 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
605 | struct tps65912 *mfd = pmic->mfd; | ||
606 | int id = rdev_get_id(dev), reg, value; | ||
607 | |||
608 | reg = tps65912_get_ldo_sel_register(pmic, id); | ||
609 | value = tps65912_reg_read(mfd, reg); | ||
610 | value &= 0xC0; | ||
611 | return tps65912_reg_write(mfd, reg, selector | value); | ||
612 | } | ||
613 | |||
614 | static int tps65912_list_voltage_dcdc(struct regulator_dev *dev, | ||
615 | unsigned selector) | ||
616 | { | ||
617 | struct tps65912_reg *pmic = rdev_get_drvdata(dev); | ||
618 | int range, voltage = 0, id = rdev_get_id(dev); | ||
619 | |||
620 | switch (id) { | ||
621 | case TPS65912_REG_DCDC1: | ||
622 | range = pmic->dcdc1_range; | ||
623 | break; | ||
624 | case TPS65912_REG_DCDC2: | ||
625 | range = pmic->dcdc2_range; | ||
626 | break; | ||
627 | case TPS65912_REG_DCDC3: | ||
628 | range = pmic->dcdc3_range; | ||
629 | break; | ||
630 | case TPS65912_REG_DCDC4: | ||
631 | range = pmic->dcdc4_range; | ||
632 | break; | ||
633 | default: | ||
634 | return -EINVAL; | ||
635 | } | ||
636 | |||
637 | switch (range) { | ||
638 | case 0: | ||
639 | /* 0.5 - 1.2875V in 12.5mV steps */ | ||
640 | voltage = tps65912_vsel_to_uv_range0(selector); | ||
641 | break; | ||
642 | case 1: | ||
643 | /* 0.7 - 1.4875V in 12.5mV steps */ | ||
644 | voltage = tps65912_vsel_to_uv_range1(selector); | ||
645 | break; | ||
646 | case 2: | ||
647 | /* 0.5 - 2.075V in 25mV steps */ | ||
648 | voltage = tps65912_vsel_to_uv_range2(selector); | ||
649 | break; | ||
650 | case 3: | ||
651 | /* 0.5 - 3.8V in 50mV steps */ | ||
652 | voltage = tps65912_vsel_to_uv_range3(selector); | ||
653 | break; | ||
654 | } | ||
655 | return voltage; | ||
656 | } | ||
657 | |||
658 | static int tps65912_list_voltage_ldo(struct regulator_dev *dev, | ||
659 | unsigned selector) | ||
660 | { | ||
661 | int ldo = rdev_get_id(dev); | ||
662 | |||
663 | if (ldo < TPS65912_REG_LDO1 || ldo > TPS65912_REG_LDO10) | ||
664 | return -EINVAL; | ||
665 | |||
666 | return tps65912_vsel_to_uv_ldo(selector); | ||
667 | } | ||
668 | |||
669 | /* Operations permitted on DCDCx */ | ||
670 | static struct regulator_ops tps65912_ops_dcdc = { | ||
671 | .is_enabled = tps65912_reg_is_enabled, | ||
672 | .enable = tps65912_reg_enable, | ||
673 | .disable = tps65912_reg_disable, | ||
674 | .set_mode = tps65912_set_mode, | ||
675 | .get_mode = tps65912_get_mode, | ||
676 | .get_voltage = tps65912_get_voltage_dcdc, | ||
677 | .set_voltage_sel = tps65912_set_voltage_dcdc, | ||
678 | .list_voltage = tps65912_list_voltage_dcdc, | ||
679 | }; | ||
680 | |||
681 | /* Operations permitted on LDOx */ | ||
682 | static struct regulator_ops tps65912_ops_ldo = { | ||
683 | .is_enabled = tps65912_reg_is_enabled, | ||
684 | .enable = tps65912_reg_enable, | ||
685 | .disable = tps65912_reg_disable, | ||
686 | .get_voltage = tps65912_get_voltage_ldo, | ||
687 | .set_voltage_sel = tps65912_set_voltage_ldo, | ||
688 | .list_voltage = tps65912_list_voltage_ldo, | ||
689 | }; | ||
690 | |||
691 | static __devinit int tps65912_probe(struct platform_device *pdev) | ||
692 | { | ||
693 | struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); | ||
694 | struct tps_info *info; | ||
695 | struct regulator_init_data *reg_data; | ||
696 | struct regulator_dev *rdev; | ||
697 | struct tps65912_reg *pmic; | ||
698 | struct tps65912_board *pmic_plat_data; | ||
699 | int i, err; | ||
700 | |||
701 | pmic_plat_data = dev_get_platdata(tps65912->dev); | ||
702 | if (!pmic_plat_data) | ||
703 | return -EINVAL; | ||
704 | |||
705 | reg_data = pmic_plat_data->tps65912_pmic_init_data; | ||
706 | |||
707 | pmic = kzalloc(sizeof(*pmic), GFP_KERNEL); | ||
708 | if (!pmic) | ||
709 | return -ENOMEM; | ||
710 | |||
711 | mutex_init(&pmic->io_lock); | ||
712 | pmic->mfd = tps65912; | ||
713 | platform_set_drvdata(pdev, pmic); | ||
714 | |||
715 | pmic->get_ctrl_reg = &tps65912_get_ctrl_register; | ||
716 | info = tps65912_regs; | ||
717 | |||
718 | for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) { | ||
719 | int range = 0; | ||
720 | /* Register the regulators */ | ||
721 | pmic->info[i] = info; | ||
722 | |||
723 | pmic->desc[i].name = info->name; | ||
724 | pmic->desc[i].id = i; | ||
725 | pmic->desc[i].n_voltages = 64; | ||
726 | pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ? | ||
727 | &tps65912_ops_ldo : &tps65912_ops_dcdc); | ||
728 | pmic->desc[i].type = REGULATOR_VOLTAGE; | ||
729 | pmic->desc[i].owner = THIS_MODULE; | ||
730 | range = tps65912_get_range(pmic, i); | ||
731 | rdev = regulator_register(&pmic->desc[i], | ||
732 | tps65912->dev, reg_data, pmic); | ||
733 | if (IS_ERR(rdev)) { | ||
734 | dev_err(tps65912->dev, | ||
735 | "failed to register %s regulator\n", | ||
736 | pdev->name); | ||
737 | err = PTR_ERR(rdev); | ||
738 | goto err; | ||
739 | } | ||
740 | |||
741 | /* Save regulator for cleanup */ | ||
742 | pmic->rdev[i] = rdev; | ||
743 | } | ||
744 | return 0; | ||
745 | |||
746 | err: | ||
747 | while (--i >= 0) | ||
748 | regulator_unregister(pmic->rdev[i]); | ||
749 | |||
750 | kfree(pmic); | ||
751 | return err; | ||
752 | } | ||
753 | |||
754 | static int __devexit tps65912_remove(struct platform_device *pdev) | ||
755 | { | ||
756 | struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev); | ||
757 | int i; | ||
758 | |||
759 | for (i = 0; i < TPS65912_NUM_REGULATOR; i++) | ||
760 | regulator_unregister(tps65912_reg->rdev[i]); | ||
761 | |||
762 | kfree(tps65912_reg); | ||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | static struct platform_driver tps65912_driver = { | ||
767 | .driver = { | ||
768 | .name = "tps65912-pmic", | ||
769 | .owner = THIS_MODULE, | ||
770 | }, | ||
771 | .probe = tps65912_probe, | ||
772 | .remove = __devexit_p(tps65912_remove), | ||
773 | }; | ||
774 | |||
775 | /** | ||
776 | * tps65912_init | ||
777 | * | ||
778 | * Module init function | ||
779 | */ | ||
780 | static int __init tps65912_init(void) | ||
781 | { | ||
782 | return platform_driver_register(&tps65912_driver); | ||
783 | } | ||
784 | subsys_initcall(tps65912_init); | ||
785 | |||
786 | /** | ||
787 | * tps65912_cleanup | ||
788 | * | ||
789 | * Module exit function | ||
790 | */ | ||
791 | static void __exit tps65912_cleanup(void) | ||
792 | { | ||
793 | platform_driver_unregister(&tps65912_driver); | ||
794 | } | ||
795 | module_exit(tps65912_cleanup); | ||
796 | |||
797 | MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>"); | ||
798 | MODULE_DESCRIPTION("TPS65912 voltage regulator driver"); | ||
799 | MODULE_LICENSE("GPL v2"); | ||
800 | MODULE_ALIAS("platform:tps65912-pmic"); | ||