diff options
author | Kim, Milo <Milo.Kim@ti.com> | 2012-06-19 03:08:22 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-19 18:14:29 -0400 |
commit | af8b5fc31099abd7f3b297332c9e280ec0b30a71 (patch) | |
tree | 1e313d1f453224c38668786e11ea39da90a5e857 /drivers/regulator/lp872x.c | |
parent | e90a84473ee941c0056c773923e9cc90a550c266 (diff) |
regulator: add new regulator driver for lp872x
This driver supports TI/National LP8720, LP8725 PMIC.
Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com>
Reviewed-by: Axel Lin <axel.lin@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/regulator/lp872x.c')
-rw-r--r-- | drivers/regulator/lp872x.c | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c new file mode 100644 index 000000000000..d51d09852041 --- /dev/null +++ b/drivers/regulator/lp872x.c | |||
@@ -0,0 +1,957 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Texas Instruments | ||
3 | * | ||
4 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/regmap.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/regulator/lp872x.h> | ||
19 | #include <linux/regulator/driver.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | |||
22 | /* Registers : LP8720/8725 shared */ | ||
23 | #define LP872X_GENERAL_CFG 0x00 | ||
24 | #define LP872X_LDO1_VOUT 0x01 | ||
25 | #define LP872X_LDO2_VOUT 0x02 | ||
26 | #define LP872X_LDO3_VOUT 0x03 | ||
27 | #define LP872X_LDO4_VOUT 0x04 | ||
28 | #define LP872X_LDO5_VOUT 0x05 | ||
29 | |||
30 | /* Registers : LP8720 */ | ||
31 | #define LP8720_BUCK_VOUT1 0x06 | ||
32 | #define LP8720_BUCK_VOUT2 0x07 | ||
33 | #define LP8720_ENABLE 0x08 | ||
34 | |||
35 | /* Registers : LP8725 */ | ||
36 | #define LP8725_LILO1_VOUT 0x06 | ||
37 | #define LP8725_LILO2_VOUT 0x07 | ||
38 | #define LP8725_BUCK1_VOUT1 0x08 | ||
39 | #define LP8725_BUCK1_VOUT2 0x09 | ||
40 | #define LP8725_BUCK2_VOUT1 0x0A | ||
41 | #define LP8725_BUCK2_VOUT2 0x0B | ||
42 | #define LP8725_BUCK_CTRL 0x0C | ||
43 | #define LP8725_LDO_CTRL 0x0D | ||
44 | |||
45 | /* Mask/shift : LP8720/LP8725 shared */ | ||
46 | #define LP872X_VOUT_M 0x1F | ||
47 | #define LP872X_START_DELAY_M 0xE0 | ||
48 | #define LP872X_START_DELAY_S 5 | ||
49 | #define LP872X_EN_LDO1_M BIT(0) | ||
50 | #define LP872X_EN_LDO2_M BIT(1) | ||
51 | #define LP872X_EN_LDO3_M BIT(2) | ||
52 | #define LP872X_EN_LDO4_M BIT(3) | ||
53 | #define LP872X_EN_LDO5_M BIT(4) | ||
54 | |||
55 | /* Mask/shift : LP8720 */ | ||
56 | #define LP8720_TIMESTEP_S 0 /* Addr 00h */ | ||
57 | #define LP8720_TIMESTEP_M BIT(0) | ||
58 | #define LP8720_EXT_DVS_M BIT(2) | ||
59 | #define LP8720_BUCK_FPWM_S 5 /* Addr 07h */ | ||
60 | #define LP8720_BUCK_FPWM_M BIT(5) | ||
61 | #define LP8720_EN_BUCK_M BIT(5) /* Addr 08h */ | ||
62 | #define LP8720_DVS_SEL_M BIT(7) | ||
63 | |||
64 | /* Mask/shift : LP8725 */ | ||
65 | #define LP8725_TIMESTEP_M 0xC0 /* Addr 00h */ | ||
66 | #define LP8725_TIMESTEP_S 6 | ||
67 | #define LP8725_BUCK1_EN_M BIT(0) | ||
68 | #define LP8725_DVS1_M BIT(2) | ||
69 | #define LP8725_DVS2_M BIT(3) | ||
70 | #define LP8725_BUCK2_EN_M BIT(4) | ||
71 | #define LP8725_BUCK_CL_M 0xC0 /* Addr 09h, 0Bh */ | ||
72 | #define LP8725_BUCK_CL_S 6 | ||
73 | #define LP8725_BUCK1_FPWM_S 1 /* Addr 0Ch */ | ||
74 | #define LP8725_BUCK1_FPWM_M BIT(1) | ||
75 | #define LP8725_BUCK2_FPWM_S 5 | ||
76 | #define LP8725_BUCK2_FPWM_M BIT(5) | ||
77 | #define LP8725_EN_LILO1_M BIT(5) /* Addr 0Dh */ | ||
78 | #define LP8725_EN_LILO2_M BIT(6) | ||
79 | |||
80 | /* PWM mode */ | ||
81 | #define LP872X_FORCE_PWM 1 | ||
82 | #define LP872X_AUTO_PWM 0 | ||
83 | |||
84 | #define LP8720_NUM_REGULATORS 6 | ||
85 | #define LP8725_NUM_REGULATORS 9 | ||
86 | #define EXTERN_DVS_USED 0 | ||
87 | #define MAX_DELAY 6 | ||
88 | |||
89 | /* dump registers in regmap-debugfs */ | ||
90 | #define MAX_REGISTERS 0x0F | ||
91 | |||
92 | enum lp872x_id { | ||
93 | LP8720, | ||
94 | LP8725, | ||
95 | }; | ||
96 | |||
97 | struct lp872x { | ||
98 | struct regmap *regmap; | ||
99 | struct device *dev; | ||
100 | enum lp872x_id chipid; | ||
101 | struct lp872x_platform_data *pdata; | ||
102 | struct regulator_dev **regulators; | ||
103 | int num_regulators; | ||
104 | enum lp872x_dvs_state dvs_pin; | ||
105 | int dvs_gpio; | ||
106 | }; | ||
107 | |||
108 | /* LP8720/LP8725 shared voltage table for LDOs */ | ||
109 | static const unsigned int lp872x_ldo_vtbl[] = { | ||
110 | 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, | ||
111 | 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 2000000, | ||
112 | 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2650000, 2700000, | ||
113 | 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3100000, 3300000, | ||
114 | }; | ||
115 | |||
116 | /* LP8720 LDO4 voltage table */ | ||
117 | static const unsigned int lp8720_ldo4_vtbl[] = { | ||
118 | 800000, 850000, 900000, 1000000, 1100000, 1200000, 1250000, 1300000, | ||
119 | 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, | ||
120 | 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, | ||
121 | 2400000, 2500000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, | ||
122 | }; | ||
123 | |||
124 | /* LP8725 LILO(Low Input Low Output) voltage table */ | ||
125 | static const unsigned int lp8725_lilo_vtbl[] = { | ||
126 | 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, | ||
127 | 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, | ||
128 | 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, | ||
129 | 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, | ||
130 | }; | ||
131 | |||
132 | /* LP8720 BUCK voltage table */ | ||
133 | #define EXT_R 0 /* external resistor divider */ | ||
134 | static const unsigned int lp8720_buck_vtbl[] = { | ||
135 | EXT_R, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, | ||
136 | 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, | ||
137 | 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, | ||
138 | 1950000, 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, | ||
139 | }; | ||
140 | |||
141 | /* LP8725 BUCK voltage table */ | ||
142 | static const unsigned int lp8725_buck_vtbl[] = { | ||
143 | 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, | ||
144 | 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, | ||
145 | 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, | ||
146 | 2400000, 2500000, 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, | ||
147 | }; | ||
148 | |||
149 | /* LP8725 BUCK current limit */ | ||
150 | static const unsigned int lp8725_buck_uA[] = { | ||
151 | 460000, 780000, 1050000, 1370000, | ||
152 | }; | ||
153 | |||
154 | static int lp872x_read_byte(struct lp872x *lp, u8 addr, u8 *data) | ||
155 | { | ||
156 | int ret; | ||
157 | unsigned int val; | ||
158 | |||
159 | ret = regmap_read(lp->regmap, addr, &val); | ||
160 | if (ret < 0) { | ||
161 | dev_err(lp->dev, "failed to read 0x%.2x\n", addr); | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | *data = (u8)val; | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static inline int lp872x_write_byte(struct lp872x *lp, u8 addr, u8 data) | ||
170 | { | ||
171 | return regmap_write(lp->regmap, addr, data); | ||
172 | } | ||
173 | |||
174 | static inline int lp872x_update_bits(struct lp872x *lp, u8 addr, | ||
175 | unsigned int mask, u8 data) | ||
176 | { | ||
177 | return regmap_update_bits(lp->regmap, addr, mask, data); | ||
178 | } | ||
179 | |||
180 | static int _rdev_to_offset(struct regulator_dev *rdev) | ||
181 | { | ||
182 | enum lp872x_regulator_id id = rdev_get_id(rdev); | ||
183 | |||
184 | switch (id) { | ||
185 | case LP8720_ID_LDO1 ... LP8720_ID_BUCK: | ||
186 | return id; | ||
187 | case LP8725_ID_LDO1 ... LP8725_ID_BUCK2: | ||
188 | return id - LP8725_ID_BASE; | ||
189 | default: | ||
190 | return -EINVAL; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | static int lp872x_get_timestep_usec(struct lp872x *lp) | ||
195 | { | ||
196 | enum lp872x_id chip = lp->chipid; | ||
197 | u8 val, mask, shift; | ||
198 | int *time_usec, size, ret; | ||
199 | int lp8720_time_usec[] = { 25, 50 }; | ||
200 | int lp8725_time_usec[] = { 32, 64, 128, 256 }; | ||
201 | |||
202 | switch (chip) { | ||
203 | case LP8720: | ||
204 | mask = LP8720_TIMESTEP_M; | ||
205 | shift = LP8720_TIMESTEP_S; | ||
206 | time_usec = &lp8720_time_usec[0]; | ||
207 | size = ARRAY_SIZE(lp8720_time_usec); | ||
208 | break; | ||
209 | case LP8725: | ||
210 | mask = LP8725_TIMESTEP_M; | ||
211 | shift = LP8725_TIMESTEP_S; | ||
212 | time_usec = &lp8725_time_usec[0]; | ||
213 | size = ARRAY_SIZE(lp8725_time_usec); | ||
214 | break; | ||
215 | default: | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); | ||
220 | if (ret) | ||
221 | return -EINVAL; | ||
222 | |||
223 | val = (val & mask) >> shift; | ||
224 | if (val >= size) | ||
225 | return -EINVAL; | ||
226 | |||
227 | return *(time_usec + val); | ||
228 | } | ||
229 | |||
230 | static int lp872x_regulator_enable_time(struct regulator_dev *rdev) | ||
231 | { | ||
232 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
233 | enum lp872x_regulator_id regulator = rdev_get_id(rdev); | ||
234 | int time_step_us = lp872x_get_timestep_usec(lp); | ||
235 | int ret, offset; | ||
236 | u8 addr, val; | ||
237 | |||
238 | if (time_step_us < 0) | ||
239 | return -EINVAL; | ||
240 | |||
241 | switch (regulator) { | ||
242 | case LP8720_ID_LDO1 ... LP8720_ID_LDO5: | ||
243 | case LP8725_ID_LDO1 ... LP8725_ID_LILO2: | ||
244 | offset = _rdev_to_offset(rdev); | ||
245 | if (offset < 0) | ||
246 | return -EINVAL; | ||
247 | |||
248 | addr = LP872X_LDO1_VOUT + offset; | ||
249 | break; | ||
250 | case LP8720_ID_BUCK: | ||
251 | addr = LP8720_BUCK_VOUT1; | ||
252 | break; | ||
253 | case LP8725_ID_BUCK1: | ||
254 | addr = LP8725_BUCK1_VOUT1; | ||
255 | break; | ||
256 | case LP8725_ID_BUCK2: | ||
257 | addr = LP8725_BUCK2_VOUT1; | ||
258 | break; | ||
259 | default: | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | |||
263 | ret = lp872x_read_byte(lp, addr, &val); | ||
264 | if (ret) | ||
265 | return ret; | ||
266 | |||
267 | val = (val & LP872X_START_DELAY_M) >> LP872X_START_DELAY_S; | ||
268 | |||
269 | return val > MAX_DELAY ? 0 : val * time_step_us; | ||
270 | } | ||
271 | |||
272 | static void lp872x_set_dvs(struct lp872x *lp, int gpio) | ||
273 | { | ||
274 | enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel; | ||
275 | enum lp872x_dvs_state state; | ||
276 | |||
277 | state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW; | ||
278 | gpio_set_value(gpio, state); | ||
279 | lp->dvs_pin = state; | ||
280 | } | ||
281 | |||
282 | static u8 lp872x_select_buck_vout_addr(struct lp872x *lp, | ||
283 | enum lp872x_regulator_id buck) | ||
284 | { | ||
285 | u8 val, addr; | ||
286 | |||
287 | if (lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val)) | ||
288 | return 0; | ||
289 | |||
290 | switch (buck) { | ||
291 | case LP8720_ID_BUCK: | ||
292 | if (val & LP8720_EXT_DVS_M) { | ||
293 | addr = (lp->dvs_pin == DVS_HIGH) ? | ||
294 | LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; | ||
295 | } else { | ||
296 | if (lp872x_read_byte(lp, LP8720_ENABLE, &val)) | ||
297 | return 0; | ||
298 | |||
299 | addr = val & LP8720_DVS_SEL_M ? | ||
300 | LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; | ||
301 | } | ||
302 | break; | ||
303 | case LP8725_ID_BUCK1: | ||
304 | if (val & LP8725_DVS1_M) | ||
305 | addr = LP8725_BUCK1_VOUT1; | ||
306 | else | ||
307 | addr = (lp->dvs_pin == DVS_HIGH) ? | ||
308 | LP8725_BUCK1_VOUT1 : LP8725_BUCK1_VOUT2; | ||
309 | break; | ||
310 | case LP8725_ID_BUCK2: | ||
311 | addr = val & LP8725_DVS2_M ? | ||
312 | LP8725_BUCK2_VOUT1 : LP8725_BUCK2_VOUT2; | ||
313 | break; | ||
314 | default: | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | return addr; | ||
319 | } | ||
320 | |||
321 | static bool lp872x_is_valid_buck_addr(u8 addr) | ||
322 | { | ||
323 | switch (addr) { | ||
324 | case LP8720_BUCK_VOUT1: | ||
325 | case LP8720_BUCK_VOUT2: | ||
326 | case LP8725_BUCK1_VOUT1: | ||
327 | case LP8725_BUCK1_VOUT2: | ||
328 | case LP8725_BUCK2_VOUT1: | ||
329 | case LP8725_BUCK2_VOUT2: | ||
330 | return true; | ||
331 | default: | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev, | ||
337 | unsigned selector) | ||
338 | { | ||
339 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
340 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
341 | u8 addr, mask = LP872X_VOUT_M; | ||
342 | struct lp872x_dvs *dvs = lp->pdata->dvs; | ||
343 | |||
344 | if (dvs && gpio_is_valid(dvs->gpio)) | ||
345 | lp872x_set_dvs(lp, dvs->gpio); | ||
346 | |||
347 | addr = lp872x_select_buck_vout_addr(lp, buck); | ||
348 | if (!lp872x_is_valid_buck_addr(addr)) | ||
349 | return -EINVAL; | ||
350 | |||
351 | return lp872x_update_bits(lp, addr, mask, selector); | ||
352 | } | ||
353 | |||
354 | static int lp872x_buck_get_voltage_sel(struct regulator_dev *rdev) | ||
355 | { | ||
356 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
357 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
358 | u8 addr, val; | ||
359 | int ret; | ||
360 | |||
361 | addr = lp872x_select_buck_vout_addr(lp, buck); | ||
362 | if (!lp872x_is_valid_buck_addr(addr)) | ||
363 | return -EINVAL; | ||
364 | |||
365 | ret = lp872x_read_byte(lp, addr, &val); | ||
366 | if (ret) | ||
367 | return ret; | ||
368 | |||
369 | return val & LP872X_VOUT_M; | ||
370 | } | ||
371 | |||
372 | static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, | ||
373 | int min_uA, int max_uA) | ||
374 | { | ||
375 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
376 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
377 | int i, max = ARRAY_SIZE(lp8725_buck_uA); | ||
378 | u8 addr, val; | ||
379 | |||
380 | switch (buck) { | ||
381 | case LP8725_ID_BUCK1: | ||
382 | addr = LP8725_BUCK1_VOUT2; | ||
383 | break; | ||
384 | case LP8725_ID_BUCK2: | ||
385 | addr = LP8725_BUCK2_VOUT2; | ||
386 | break; | ||
387 | default: | ||
388 | return -EINVAL; | ||
389 | } | ||
390 | |||
391 | for (i = 0 ; i < max ; i++) | ||
392 | if (lp8725_buck_uA[i] >= min_uA && | ||
393 | lp8725_buck_uA[i] <= max_uA) | ||
394 | break; | ||
395 | |||
396 | if (i == max) | ||
397 | return -EINVAL; | ||
398 | |||
399 | val = i << LP8725_BUCK_CL_S; | ||
400 | |||
401 | return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val); | ||
402 | } | ||
403 | |||
404 | static int lp8725_buck_get_current_limit(struct regulator_dev *rdev) | ||
405 | { | ||
406 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
407 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
408 | u8 addr, val; | ||
409 | int ret; | ||
410 | |||
411 | switch (buck) { | ||
412 | case LP8725_ID_BUCK1: | ||
413 | addr = LP8725_BUCK1_VOUT2; | ||
414 | break; | ||
415 | case LP8725_ID_BUCK2: | ||
416 | addr = LP8725_BUCK2_VOUT2; | ||
417 | break; | ||
418 | default: | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | |||
422 | ret = lp872x_read_byte(lp, addr, &val); | ||
423 | if (ret) | ||
424 | return ret; | ||
425 | |||
426 | val = (val & LP8725_BUCK_CL_M) >> LP8725_BUCK_CL_S; | ||
427 | |||
428 | return (val < ARRAY_SIZE(lp8725_buck_uA)) ? | ||
429 | lp8725_buck_uA[val] : -EINVAL; | ||
430 | } | ||
431 | |||
432 | static int lp872x_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) | ||
433 | { | ||
434 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
435 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
436 | u8 addr, mask, shift, val; | ||
437 | |||
438 | switch (buck) { | ||
439 | case LP8720_ID_BUCK: | ||
440 | addr = LP8720_BUCK_VOUT2; | ||
441 | mask = LP8720_BUCK_FPWM_M; | ||
442 | shift = LP8720_BUCK_FPWM_S; | ||
443 | break; | ||
444 | case LP8725_ID_BUCK1: | ||
445 | addr = LP8725_BUCK_CTRL; | ||
446 | mask = LP8725_BUCK1_FPWM_M; | ||
447 | shift = LP8725_BUCK1_FPWM_S; | ||
448 | break; | ||
449 | case LP8725_ID_BUCK2: | ||
450 | addr = LP8725_BUCK_CTRL; | ||
451 | mask = LP8725_BUCK2_FPWM_M; | ||
452 | shift = LP8725_BUCK2_FPWM_S; | ||
453 | break; | ||
454 | default: | ||
455 | return -EINVAL; | ||
456 | } | ||
457 | |||
458 | if (mode == REGULATOR_MODE_FAST) | ||
459 | val = LP872X_FORCE_PWM << shift; | ||
460 | else if (mode == REGULATOR_MODE_NORMAL) | ||
461 | val = LP872X_AUTO_PWM << shift; | ||
462 | else | ||
463 | return -EINVAL; | ||
464 | |||
465 | return lp872x_update_bits(lp, addr, mask, val); | ||
466 | } | ||
467 | |||
468 | static unsigned int lp872x_buck_get_mode(struct regulator_dev *rdev) | ||
469 | { | ||
470 | struct lp872x *lp = rdev_get_drvdata(rdev); | ||
471 | enum lp872x_regulator_id buck = rdev_get_id(rdev); | ||
472 | u8 addr, mask, val; | ||
473 | int ret; | ||
474 | |||
475 | switch (buck) { | ||
476 | case LP8720_ID_BUCK: | ||
477 | addr = LP8720_BUCK_VOUT2; | ||
478 | mask = LP8720_BUCK_FPWM_M; | ||
479 | break; | ||
480 | case LP8725_ID_BUCK1: | ||
481 | addr = LP8725_BUCK_CTRL; | ||
482 | mask = LP8725_BUCK1_FPWM_M; | ||
483 | break; | ||
484 | case LP8725_ID_BUCK2: | ||
485 | addr = LP8725_BUCK_CTRL; | ||
486 | mask = LP8725_BUCK2_FPWM_M; | ||
487 | break; | ||
488 | default: | ||
489 | return -EINVAL; | ||
490 | } | ||
491 | |||
492 | ret = lp872x_read_byte(lp, addr, &val); | ||
493 | if (ret) | ||
494 | return ret; | ||
495 | |||
496 | return val & mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; | ||
497 | } | ||
498 | |||
499 | static struct regulator_ops lp872x_ldo_ops = { | ||
500 | .list_voltage = regulator_list_voltage_table, | ||
501 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | ||
502 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | ||
503 | .enable = regulator_enable_regmap, | ||
504 | .disable = regulator_disable_regmap, | ||
505 | .is_enabled = regulator_is_enabled_regmap, | ||
506 | .enable_time = lp872x_regulator_enable_time, | ||
507 | }; | ||
508 | |||
509 | static struct regulator_ops lp8720_buck_ops = { | ||
510 | .list_voltage = regulator_list_voltage_table, | ||
511 | .set_voltage_sel = lp872x_buck_set_voltage_sel, | ||
512 | .get_voltage_sel = lp872x_buck_get_voltage_sel, | ||
513 | .enable = regulator_enable_regmap, | ||
514 | .disable = regulator_disable_regmap, | ||
515 | .is_enabled = regulator_is_enabled_regmap, | ||
516 | .enable_time = lp872x_regulator_enable_time, | ||
517 | .set_mode = lp872x_buck_set_mode, | ||
518 | .get_mode = lp872x_buck_get_mode, | ||
519 | }; | ||
520 | |||
521 | static struct regulator_ops lp8725_buck_ops = { | ||
522 | .list_voltage = regulator_list_voltage_table, | ||
523 | .set_voltage_sel = lp872x_buck_set_voltage_sel, | ||
524 | .get_voltage_sel = lp872x_buck_get_voltage_sel, | ||
525 | .enable = regulator_enable_regmap, | ||
526 | .disable = regulator_disable_regmap, | ||
527 | .is_enabled = regulator_is_enabled_regmap, | ||
528 | .enable_time = lp872x_regulator_enable_time, | ||
529 | .set_mode = lp872x_buck_set_mode, | ||
530 | .get_mode = lp872x_buck_get_mode, | ||
531 | .set_current_limit = lp8725_buck_set_current_limit, | ||
532 | .get_current_limit = lp8725_buck_get_current_limit, | ||
533 | }; | ||
534 | |||
535 | static struct regulator_desc lp8720_regulator_desc[] = { | ||
536 | { | ||
537 | .name = "ldo1", | ||
538 | .id = LP8720_ID_LDO1, | ||
539 | .ops = &lp872x_ldo_ops, | ||
540 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
541 | .volt_table = lp872x_ldo_vtbl, | ||
542 | .type = REGULATOR_VOLTAGE, | ||
543 | .owner = THIS_MODULE, | ||
544 | .vsel_reg = LP872X_LDO1_VOUT, | ||
545 | .vsel_mask = LP872X_VOUT_M, | ||
546 | .enable_reg = LP8720_ENABLE, | ||
547 | .enable_mask = LP872X_EN_LDO1_M, | ||
548 | }, | ||
549 | { | ||
550 | .name = "ldo2", | ||
551 | .id = LP8720_ID_LDO2, | ||
552 | .ops = &lp872x_ldo_ops, | ||
553 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
554 | .volt_table = lp872x_ldo_vtbl, | ||
555 | .type = REGULATOR_VOLTAGE, | ||
556 | .owner = THIS_MODULE, | ||
557 | .vsel_reg = LP872X_LDO2_VOUT, | ||
558 | .vsel_mask = LP872X_VOUT_M, | ||
559 | .enable_reg = LP8720_ENABLE, | ||
560 | .enable_mask = LP872X_EN_LDO2_M, | ||
561 | }, | ||
562 | { | ||
563 | .name = "ldo3", | ||
564 | .id = LP8720_ID_LDO3, | ||
565 | .ops = &lp872x_ldo_ops, | ||
566 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
567 | .volt_table = lp872x_ldo_vtbl, | ||
568 | .type = REGULATOR_VOLTAGE, | ||
569 | .owner = THIS_MODULE, | ||
570 | .vsel_reg = LP872X_LDO3_VOUT, | ||
571 | .vsel_mask = LP872X_VOUT_M, | ||
572 | .enable_reg = LP8720_ENABLE, | ||
573 | .enable_mask = LP872X_EN_LDO3_M, | ||
574 | }, | ||
575 | { | ||
576 | .name = "ldo4", | ||
577 | .id = LP8720_ID_LDO4, | ||
578 | .ops = &lp872x_ldo_ops, | ||
579 | .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), | ||
580 | .volt_table = lp8720_ldo4_vtbl, | ||
581 | .type = REGULATOR_VOLTAGE, | ||
582 | .owner = THIS_MODULE, | ||
583 | .vsel_reg = LP872X_LDO4_VOUT, | ||
584 | .vsel_mask = LP872X_VOUT_M, | ||
585 | .enable_reg = LP8720_ENABLE, | ||
586 | .enable_mask = LP872X_EN_LDO4_M, | ||
587 | }, | ||
588 | { | ||
589 | .name = "ldo5", | ||
590 | .id = LP8720_ID_LDO5, | ||
591 | .ops = &lp872x_ldo_ops, | ||
592 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
593 | .volt_table = lp872x_ldo_vtbl, | ||
594 | .type = REGULATOR_VOLTAGE, | ||
595 | .owner = THIS_MODULE, | ||
596 | .vsel_reg = LP872X_LDO5_VOUT, | ||
597 | .vsel_mask = LP872X_VOUT_M, | ||
598 | .enable_reg = LP8720_ENABLE, | ||
599 | .enable_mask = LP872X_EN_LDO5_M, | ||
600 | }, | ||
601 | { | ||
602 | .name = "buck", | ||
603 | .id = LP8720_ID_BUCK, | ||
604 | .ops = &lp8720_buck_ops, | ||
605 | .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), | ||
606 | .volt_table = lp8720_buck_vtbl, | ||
607 | .type = REGULATOR_VOLTAGE, | ||
608 | .owner = THIS_MODULE, | ||
609 | .enable_reg = LP8720_ENABLE, | ||
610 | .enable_mask = LP8720_EN_BUCK_M, | ||
611 | }, | ||
612 | }; | ||
613 | |||
614 | static struct regulator_desc lp8725_regulator_desc[] = { | ||
615 | { | ||
616 | .name = "ldo1", | ||
617 | .id = LP8725_ID_LDO1, | ||
618 | .ops = &lp872x_ldo_ops, | ||
619 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
620 | .volt_table = lp872x_ldo_vtbl, | ||
621 | .type = REGULATOR_VOLTAGE, | ||
622 | .owner = THIS_MODULE, | ||
623 | .vsel_reg = LP872X_LDO1_VOUT, | ||
624 | .vsel_mask = LP872X_VOUT_M, | ||
625 | .enable_reg = LP8725_LDO_CTRL, | ||
626 | .enable_mask = LP872X_EN_LDO1_M, | ||
627 | }, | ||
628 | { | ||
629 | .name = "ldo2", | ||
630 | .id = LP8725_ID_LDO2, | ||
631 | .ops = &lp872x_ldo_ops, | ||
632 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
633 | .volt_table = lp872x_ldo_vtbl, | ||
634 | .type = REGULATOR_VOLTAGE, | ||
635 | .owner = THIS_MODULE, | ||
636 | .vsel_reg = LP872X_LDO2_VOUT, | ||
637 | .vsel_mask = LP872X_VOUT_M, | ||
638 | .enable_reg = LP8725_LDO_CTRL, | ||
639 | .enable_mask = LP872X_EN_LDO2_M, | ||
640 | }, | ||
641 | { | ||
642 | .name = "ldo3", | ||
643 | .id = LP8725_ID_LDO3, | ||
644 | .ops = &lp872x_ldo_ops, | ||
645 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
646 | .volt_table = lp872x_ldo_vtbl, | ||
647 | .type = REGULATOR_VOLTAGE, | ||
648 | .owner = THIS_MODULE, | ||
649 | .vsel_reg = LP872X_LDO3_VOUT, | ||
650 | .vsel_mask = LP872X_VOUT_M, | ||
651 | .enable_reg = LP8725_LDO_CTRL, | ||
652 | .enable_mask = LP872X_EN_LDO3_M, | ||
653 | }, | ||
654 | { | ||
655 | .name = "ldo4", | ||
656 | .id = LP8725_ID_LDO4, | ||
657 | .ops = &lp872x_ldo_ops, | ||
658 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
659 | .volt_table = lp872x_ldo_vtbl, | ||
660 | .type = REGULATOR_VOLTAGE, | ||
661 | .owner = THIS_MODULE, | ||
662 | .vsel_reg = LP872X_LDO4_VOUT, | ||
663 | .vsel_mask = LP872X_VOUT_M, | ||
664 | .enable_reg = LP8725_LDO_CTRL, | ||
665 | .enable_mask = LP872X_EN_LDO4_M, | ||
666 | }, | ||
667 | { | ||
668 | .name = "ldo5", | ||
669 | .id = LP8725_ID_LDO5, | ||
670 | .ops = &lp872x_ldo_ops, | ||
671 | .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), | ||
672 | .volt_table = lp872x_ldo_vtbl, | ||
673 | .type = REGULATOR_VOLTAGE, | ||
674 | .owner = THIS_MODULE, | ||
675 | .vsel_reg = LP872X_LDO5_VOUT, | ||
676 | .vsel_mask = LP872X_VOUT_M, | ||
677 | .enable_reg = LP8725_LDO_CTRL, | ||
678 | .enable_mask = LP872X_EN_LDO5_M, | ||
679 | }, | ||
680 | { | ||
681 | .name = "lilo1", | ||
682 | .id = LP8725_ID_LILO1, | ||
683 | .ops = &lp872x_ldo_ops, | ||
684 | .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), | ||
685 | .volt_table = lp8725_lilo_vtbl, | ||
686 | .type = REGULATOR_VOLTAGE, | ||
687 | .owner = THIS_MODULE, | ||
688 | .vsel_reg = LP8725_LILO1_VOUT, | ||
689 | .vsel_mask = LP872X_VOUT_M, | ||
690 | .enable_reg = LP8725_LDO_CTRL, | ||
691 | .enable_mask = LP8725_EN_LILO1_M, | ||
692 | }, | ||
693 | { | ||
694 | .name = "lilo2", | ||
695 | .id = LP8725_ID_LILO2, | ||
696 | .ops = &lp872x_ldo_ops, | ||
697 | .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), | ||
698 | .volt_table = lp8725_lilo_vtbl, | ||
699 | .type = REGULATOR_VOLTAGE, | ||
700 | .owner = THIS_MODULE, | ||
701 | .vsel_reg = LP8725_LILO2_VOUT, | ||
702 | .vsel_mask = LP872X_VOUT_M, | ||
703 | .enable_reg = LP8725_LDO_CTRL, | ||
704 | .enable_mask = LP8725_EN_LILO2_M, | ||
705 | }, | ||
706 | { | ||
707 | .name = "buck1", | ||
708 | .id = LP8725_ID_BUCK1, | ||
709 | .ops = &lp8725_buck_ops, | ||
710 | .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), | ||
711 | .volt_table = lp8725_buck_vtbl, | ||
712 | .type = REGULATOR_VOLTAGE, | ||
713 | .owner = THIS_MODULE, | ||
714 | .enable_reg = LP872X_GENERAL_CFG, | ||
715 | .enable_mask = LP8725_BUCK1_EN_M, | ||
716 | }, | ||
717 | { | ||
718 | .name = "buck2", | ||
719 | .id = LP8725_ID_BUCK2, | ||
720 | .ops = &lp8725_buck_ops, | ||
721 | .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), | ||
722 | .volt_table = lp8725_buck_vtbl, | ||
723 | .type = REGULATOR_VOLTAGE, | ||
724 | .owner = THIS_MODULE, | ||
725 | .enable_reg = LP872X_GENERAL_CFG, | ||
726 | .enable_mask = LP8725_BUCK2_EN_M, | ||
727 | }, | ||
728 | }; | ||
729 | |||
730 | static int lp872x_check_dvs_validity(struct lp872x *lp) | ||
731 | { | ||
732 | struct lp872x_dvs *dvs = lp->pdata->dvs; | ||
733 | u8 val = 0; | ||
734 | int ret; | ||
735 | |||
736 | ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); | ||
737 | if (ret) | ||
738 | return ret; | ||
739 | |||
740 | ret = 0; | ||
741 | if (lp->chipid == LP8720) { | ||
742 | if (val & LP8720_EXT_DVS_M) | ||
743 | ret = dvs ? 0 : -EINVAL; | ||
744 | } else { | ||
745 | if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED) | ||
746 | ret = dvs ? 0 : -EINVAL; | ||
747 | } | ||
748 | |||
749 | return ret; | ||
750 | } | ||
751 | |||
752 | static int lp872x_init_dvs(struct lp872x *lp) | ||
753 | { | ||
754 | int ret, gpio; | ||
755 | struct lp872x_dvs *dvs = lp->pdata->dvs; | ||
756 | enum lp872x_dvs_state pinstate; | ||
757 | |||
758 | ret = lp872x_check_dvs_validity(lp); | ||
759 | if (ret) { | ||
760 | dev_warn(lp->dev, "invalid dvs data: %d\n", ret); | ||
761 | return ret; | ||
762 | } | ||
763 | |||
764 | gpio = dvs->gpio; | ||
765 | if (!gpio_is_valid(gpio)) { | ||
766 | dev_err(lp->dev, "invalid gpio: %d\n", gpio); | ||
767 | return -EINVAL; | ||
768 | } | ||
769 | |||
770 | pinstate = dvs->init_state; | ||
771 | ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); | ||
772 | if (ret) { | ||
773 | dev_err(lp->dev, "gpio request err: %d\n", ret); | ||
774 | return ret; | ||
775 | } | ||
776 | |||
777 | lp->dvs_pin = pinstate; | ||
778 | lp->dvs_gpio = gpio; | ||
779 | |||
780 | return 0; | ||
781 | } | ||
782 | |||
783 | static int lp872x_config(struct lp872x *lp) | ||
784 | { | ||
785 | struct lp872x_platform_data *pdata = lp->pdata; | ||
786 | int ret; | ||
787 | |||
788 | if (!pdata) { | ||
789 | dev_warn(lp->dev, "no platform data\n"); | ||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | if (!pdata->update_config) | ||
794 | return 0; | ||
795 | |||
796 | ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config); | ||
797 | if (ret) | ||
798 | return ret; | ||
799 | |||
800 | return lp872x_init_dvs(lp); | ||
801 | } | ||
802 | |||
803 | static struct regulator_init_data | ||
804 | *lp872x_find_regulator_init_data(int idx, struct lp872x *lp) | ||
805 | { | ||
806 | int i, base, id, max_regulators; | ||
807 | |||
808 | switch (lp->chipid) { | ||
809 | case LP8720: | ||
810 | base = LP8720_ID_BASE; | ||
811 | max_regulators = LP8720_NUM_REGULATORS; | ||
812 | break; | ||
813 | case LP8725: | ||
814 | base = LP8725_ID_BASE; | ||
815 | max_regulators = LP8725_NUM_REGULATORS; | ||
816 | break; | ||
817 | default: | ||
818 | return NULL; | ||
819 | } | ||
820 | |||
821 | id = base + idx; | ||
822 | for (i = 0 ; i < max_regulators ; i++) | ||
823 | if (lp->pdata->regulator_data[i].id == id) | ||
824 | break; | ||
825 | |||
826 | return (i == max_regulators) ? NULL : | ||
827 | lp->pdata->regulator_data[i].init_data; | ||
828 | } | ||
829 | |||
830 | static int lp872x_regulator_register(struct lp872x *lp) | ||
831 | { | ||
832 | struct regulator_desc *desc; | ||
833 | struct regulator_config cfg = { }; | ||
834 | struct regulator_dev *rdev; | ||
835 | int i, ret; | ||
836 | |||
837 | for (i = 0 ; i < lp->num_regulators ; i++) { | ||
838 | desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : | ||
839 | &lp8725_regulator_desc[i]; | ||
840 | |||
841 | cfg.dev = lp->dev; | ||
842 | cfg.init_data = lp872x_find_regulator_init_data(i, lp); | ||
843 | cfg.driver_data = lp; | ||
844 | cfg.regmap = lp->regmap; | ||
845 | |||
846 | rdev = regulator_register(desc, &cfg); | ||
847 | if (IS_ERR(rdev)) { | ||
848 | dev_err(lp->dev, "regulator register err"); | ||
849 | ret = PTR_ERR(rdev); | ||
850 | goto err; | ||
851 | } | ||
852 | |||
853 | *(lp->regulators + i) = rdev; | ||
854 | } | ||
855 | |||
856 | return 0; | ||
857 | err: | ||
858 | while (--i >= 0) { | ||
859 | rdev = *(lp->regulators + i); | ||
860 | regulator_unregister(rdev); | ||
861 | } | ||
862 | return ret; | ||
863 | } | ||
864 | |||
865 | static void lp872x_regulator_unregister(struct lp872x *lp) | ||
866 | { | ||
867 | struct regulator_dev *rdev; | ||
868 | int i; | ||
869 | |||
870 | for (i = 0 ; i < lp->num_regulators ; i++) { | ||
871 | rdev = *(lp->regulators + i); | ||
872 | regulator_unregister(rdev); | ||
873 | } | ||
874 | } | ||
875 | |||
876 | static const struct regmap_config lp872x_regmap_config = { | ||
877 | .reg_bits = 8, | ||
878 | .val_bits = 8, | ||
879 | .max_register = MAX_REGISTERS, | ||
880 | }; | ||
881 | |||
882 | static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) | ||
883 | { | ||
884 | struct lp872x *lp; | ||
885 | struct lp872x_platform_data *pdata = cl->dev.platform_data; | ||
886 | int ret, size, num_regulators; | ||
887 | const int lp872x_num_regulators[] = { | ||
888 | [LP8720] = LP8720_NUM_REGULATORS, | ||
889 | [LP8725] = LP8725_NUM_REGULATORS, | ||
890 | }; | ||
891 | |||
892 | lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); | ||
893 | if (!lp) | ||
894 | goto err_mem; | ||
895 | |||
896 | num_regulators = lp872x_num_regulators[id->driver_data]; | ||
897 | size = sizeof(struct regulator_dev *) * num_regulators; | ||
898 | |||
899 | lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); | ||
900 | if (!lp->regulators) | ||
901 | goto err_mem; | ||
902 | |||
903 | lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); | ||
904 | if (IS_ERR(lp->regmap)) { | ||
905 | ret = PTR_ERR(lp->regmap); | ||
906 | dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); | ||
907 | goto err_dev; | ||
908 | } | ||
909 | |||
910 | lp->dev = &cl->dev; | ||
911 | lp->pdata = pdata; | ||
912 | lp->chipid = id->driver_data; | ||
913 | lp->num_regulators = num_regulators; | ||
914 | i2c_set_clientdata(cl, lp); | ||
915 | |||
916 | ret = lp872x_config(lp); | ||
917 | if (ret) | ||
918 | goto err_dev; | ||
919 | |||
920 | return lp872x_regulator_register(lp); | ||
921 | |||
922 | err_mem: | ||
923 | return -ENOMEM; | ||
924 | err_dev: | ||
925 | return ret; | ||
926 | } | ||
927 | |||
928 | static int __devexit lp872x_remove(struct i2c_client *cl) | ||
929 | { | ||
930 | struct lp872x *lp = i2c_get_clientdata(cl); | ||
931 | |||
932 | lp872x_regulator_unregister(lp); | ||
933 | return 0; | ||
934 | } | ||
935 | |||
936 | static const struct i2c_device_id lp872x_ids[] = { | ||
937 | {"lp8720", LP8720}, | ||
938 | {"lp8725", LP8725}, | ||
939 | { } | ||
940 | }; | ||
941 | MODULE_DEVICE_TABLE(i2c, lp872x_ids); | ||
942 | |||
943 | static struct i2c_driver lp872x_driver = { | ||
944 | .driver = { | ||
945 | .name = "lp872x", | ||
946 | .owner = THIS_MODULE, | ||
947 | }, | ||
948 | .probe = lp872x_probe, | ||
949 | .remove = __devexit_p(lp872x_remove), | ||
950 | .id_table = lp872x_ids, | ||
951 | }; | ||
952 | |||
953 | module_i2c_driver(lp872x_driver); | ||
954 | |||
955 | MODULE_DESCRIPTION("TI/National Semiconductor LP872x PMU Regulator Driver"); | ||
956 | MODULE_AUTHOR("Milo Kim"); | ||
957 | MODULE_LICENSE("GPL"); | ||