diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/regulator/max8907c-regulator.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/regulator/max8907c-regulator.c')
-rw-r--r-- | drivers/regulator/max8907c-regulator.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/drivers/regulator/max8907c-regulator.c b/drivers/regulator/max8907c-regulator.c new file mode 100644 index 00000000000..925f161d992 --- /dev/null +++ b/drivers/regulator/max8907c-regulator.c | |||
@@ -0,0 +1,421 @@ | |||
1 | /* | ||
2 | * max8907c-regulator.c -- support regulators in max8907c | ||
3 | * | ||
4 | * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.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 | #include <linux/module.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/regulator/driver.h> | ||
17 | #include <linux/mfd/max8907c.h> | ||
18 | #include <linux/regulator/max8907c-regulator.h> | ||
19 | |||
20 | #define MAX8907C_II2RR_VERSION_MASK 0xF0 | ||
21 | #define MAX8907C_II2RR_VERSION_REV_A 0x00 | ||
22 | #define MAX8907C_II2RR_VERSION_REV_B 0x10 | ||
23 | #define MAX8907C_II2RR_VERSION_REV_C 0x30 | ||
24 | |||
25 | #define MAX8907C_REGULATOR_CNT (ARRAY_SIZE(max8907c_regulators)) | ||
26 | |||
27 | struct max8907c_regulator_info { | ||
28 | u32 min_uV; | ||
29 | u32 max_uV; | ||
30 | u32 step_uV; | ||
31 | u8 reg_base; | ||
32 | struct regulator_desc desc; | ||
33 | struct i2c_client *i2c; | ||
34 | }; | ||
35 | |||
36 | #define REG_LDO(ids, base, min, max, step) \ | ||
37 | { \ | ||
38 | .min_uV = (min), \ | ||
39 | .max_uV = (max), \ | ||
40 | .step_uV = (step), \ | ||
41 | .reg_base = (base), \ | ||
42 | .desc = { \ | ||
43 | .name = #ids, \ | ||
44 | .id = MAX8907C_##ids, \ | ||
45 | .n_voltages = ((max) - (min)) / (step) + 1, \ | ||
46 | .ops = &max8907c_ldo_ops, \ | ||
47 | .type = REGULATOR_VOLTAGE, \ | ||
48 | .owner = THIS_MODULE, \ | ||
49 | }, \ | ||
50 | } | ||
51 | |||
52 | #define REG_FIXED(ids, voltage) \ | ||
53 | { \ | ||
54 | .min_uV = (voltage), \ | ||
55 | .max_uV = (voltage), \ | ||
56 | .desc = { \ | ||
57 | .name = #ids, \ | ||
58 | .id = MAX8907C_##ids, \ | ||
59 | .n_voltages = 1, \ | ||
60 | .ops = &max8907c_fixed_ops, \ | ||
61 | .type = REGULATOR_VOLTAGE, \ | ||
62 | .owner = THIS_MODULE, \ | ||
63 | }, \ | ||
64 | } | ||
65 | |||
66 | #define REG_OUT5V(ids, base, voltage) \ | ||
67 | { \ | ||
68 | .min_uV = (voltage), \ | ||
69 | .max_uV = (voltage), \ | ||
70 | .reg_base = (base), \ | ||
71 | .desc = { \ | ||
72 | .name = #ids, \ | ||
73 | .id = MAX8907C_##ids, \ | ||
74 | .n_voltages = 1, \ | ||
75 | .ops = &max8907c_out5v_ops, \ | ||
76 | .type = REGULATOR_VOLTAGE, \ | ||
77 | .owner = THIS_MODULE, \ | ||
78 | }, \ | ||
79 | } | ||
80 | |||
81 | #define REG_BBAT(ids, base, min, max, step) \ | ||
82 | { \ | ||
83 | .min_uV = (min), \ | ||
84 | .max_uV = (max), \ | ||
85 | .step_uV = (step), \ | ||
86 | .reg_base = (base), \ | ||
87 | .desc = { \ | ||
88 | .name = #ids, \ | ||
89 | .id = MAX8907C_##ids, \ | ||
90 | .n_voltages = ((max) - (min)) / (step) + 1, \ | ||
91 | .ops = &max8907c_bbat_ops, \ | ||
92 | .type = REGULATOR_VOLTAGE, \ | ||
93 | .owner = THIS_MODULE, \ | ||
94 | }, \ | ||
95 | } | ||
96 | |||
97 | #define REG_WLED(ids, base, voltage) \ | ||
98 | { \ | ||
99 | .min_uV = (voltage), \ | ||
100 | .max_uV = (voltage), \ | ||
101 | .reg_base = (base), \ | ||
102 | .desc = { \ | ||
103 | .name = #ids, \ | ||
104 | .id = MAX8907C_##ids, \ | ||
105 | .n_voltages = 1, \ | ||
106 | .ops = &max8907c_wled_ops, \ | ||
107 | .type = REGULATOR_CURRENT, \ | ||
108 | .owner = THIS_MODULE, \ | ||
109 | }, \ | ||
110 | } | ||
111 | |||
112 | #define LDO_750_50(id, base) REG_LDO(id, (base), 750000, 3900000, 50000) | ||
113 | #define LDO_650_25(id, base) REG_LDO(id, (base), 650000, 2225000, 25000) | ||
114 | |||
115 | static int max8907c_regulator_list_voltage(struct regulator_dev *dev, | ||
116 | unsigned index); | ||
117 | static int max8907c_regulator_ldo_set_voltage(struct regulator_dev *dev, | ||
118 | int min_uV, int max_uV); | ||
119 | static int max8907c_regulator_bbat_set_voltage(struct regulator_dev *dev, | ||
120 | int min_uV, int max_uV); | ||
121 | static int max8907c_regulator_ldo_get_voltage(struct regulator_dev *dev); | ||
122 | static int max8907c_regulator_fixed_get_voltage(struct regulator_dev *dev); | ||
123 | static int max8907c_regulator_bbat_get_voltage(struct regulator_dev *dev); | ||
124 | static int max8907c_regulator_wled_set_current_limit(struct regulator_dev *dev, | ||
125 | int min_uA, int max_uA); | ||
126 | static int max8907c_regulator_wled_get_current_limit(struct regulator_dev *dev); | ||
127 | static int max8907c_regulator_ldo_enable(struct regulator_dev *dev); | ||
128 | static int max8907c_regulator_out5v_enable(struct regulator_dev *dev); | ||
129 | static int max8907c_regulator_ldo_disable(struct regulator_dev *dev); | ||
130 | static int max8907c_regulator_out5v_disable(struct regulator_dev *dev); | ||
131 | static int max8907c_regulator_ldo_is_enabled(struct regulator_dev *dev); | ||
132 | static int max8907c_regulator_out5v_is_enabled(struct regulator_dev *dev); | ||
133 | |||
134 | static struct regulator_ops max8907c_ldo_ops = { | ||
135 | .list_voltage = max8907c_regulator_list_voltage, | ||
136 | .set_voltage = max8907c_regulator_ldo_set_voltage, | ||
137 | .get_voltage = max8907c_regulator_ldo_get_voltage, | ||
138 | .enable = max8907c_regulator_ldo_enable, | ||
139 | .disable = max8907c_regulator_ldo_disable, | ||
140 | .is_enabled = max8907c_regulator_ldo_is_enabled, | ||
141 | }; | ||
142 | |||
143 | static struct regulator_ops max8907c_fixed_ops = { | ||
144 | .list_voltage = max8907c_regulator_list_voltage, | ||
145 | .get_voltage = max8907c_regulator_fixed_get_voltage, | ||
146 | }; | ||
147 | |||
148 | static struct regulator_ops max8907c_out5v_ops = { | ||
149 | .list_voltage = max8907c_regulator_list_voltage, | ||
150 | .get_voltage = max8907c_regulator_fixed_get_voltage, | ||
151 | .enable = max8907c_regulator_out5v_enable, | ||
152 | .disable = max8907c_regulator_out5v_disable, | ||
153 | .is_enabled = max8907c_regulator_out5v_is_enabled, | ||
154 | }; | ||
155 | |||
156 | static struct regulator_ops max8907c_bbat_ops = { | ||
157 | .list_voltage = max8907c_regulator_list_voltage, | ||
158 | .set_voltage = max8907c_regulator_bbat_set_voltage, | ||
159 | .get_voltage = max8907c_regulator_bbat_get_voltage, | ||
160 | }; | ||
161 | |||
162 | static struct regulator_ops max8907c_wled_ops = { | ||
163 | .list_voltage = max8907c_regulator_list_voltage, | ||
164 | .set_current_limit = max8907c_regulator_wled_set_current_limit, | ||
165 | .get_current_limit = max8907c_regulator_wled_get_current_limit, | ||
166 | .get_voltage = max8907c_regulator_fixed_get_voltage, | ||
167 | }; | ||
168 | |||
169 | static struct max8907c_regulator_info max8907c_regulators[] = { | ||
170 | REG_LDO(SD1, MAX8907C_REG_SDCTL1, 650000, 2225000, 25000), | ||
171 | REG_LDO(SD2, MAX8907C_REG_SDCTL2, 637500, 1425000, 12500), | ||
172 | REG_LDO(SD3, MAX8907C_REG_SDCTL3, 750000, 3900000, 50000), | ||
173 | LDO_750_50(LDO1, MAX8907C_REG_LDOCTL1), | ||
174 | LDO_650_25(LDO2, MAX8907C_REG_LDOCTL2), | ||
175 | LDO_650_25(LDO3, MAX8907C_REG_LDOCTL3), | ||
176 | LDO_750_50(LDO4, MAX8907C_REG_LDOCTL4), | ||
177 | LDO_750_50(LDO5, MAX8907C_REG_LDOCTL5), | ||
178 | LDO_750_50(LDO6, MAX8907C_REG_LDOCTL6), | ||
179 | LDO_750_50(LDO7, MAX8907C_REG_LDOCTL7), | ||
180 | LDO_750_50(LDO8, MAX8907C_REG_LDOCTL8), | ||
181 | LDO_750_50(LDO9, MAX8907C_REG_LDOCTL9), | ||
182 | LDO_750_50(LDO10, MAX8907C_REG_LDOCTL10), | ||
183 | LDO_750_50(LDO11, MAX8907C_REG_LDOCTL11), | ||
184 | LDO_750_50(LDO12, MAX8907C_REG_LDOCTL12), | ||
185 | LDO_750_50(LDO13, MAX8907C_REG_LDOCTL13), | ||
186 | LDO_750_50(LDO14, MAX8907C_REG_LDOCTL14), | ||
187 | LDO_750_50(LDO15, MAX8907C_REG_LDOCTL15), | ||
188 | LDO_750_50(LDO16, MAX8907C_REG_LDOCTL16), | ||
189 | LDO_650_25(LDO17, MAX8907C_REG_LDOCTL17), | ||
190 | LDO_650_25(LDO18, MAX8907C_REG_LDOCTL18), | ||
191 | LDO_750_50(LDO19, MAX8907C_REG_LDOCTL19), | ||
192 | LDO_750_50(LDO20, MAX8907C_REG_LDOCTL20), | ||
193 | REG_OUT5V(OUT5V, MAX8907C_REG_OUT5VEN, 5000000), | ||
194 | REG_OUT5V(OUT33V, MAX8907C_REG_OUT33VEN, 3300000), | ||
195 | REG_BBAT(BBAT, MAX8907C_REG_BBAT_CNFG, 2400000, 3000000, 200000), | ||
196 | REG_FIXED(SDBY, 1200000), | ||
197 | REG_FIXED(VRTC, 3300000), | ||
198 | REG_WLED(WLED, MAX8907C_REG_ILED_CNTL, 0), | ||
199 | }; | ||
200 | |||
201 | static int max8907c_regulator_list_voltage(struct regulator_dev *rdev, | ||
202 | unsigned index) | ||
203 | { | ||
204 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
205 | |||
206 | return info->min_uV + info->step_uV * index; | ||
207 | } | ||
208 | |||
209 | static int max8907c_regulator_ldo_set_voltage(struct regulator_dev *rdev, | ||
210 | int min_uV, int max_uV) | ||
211 | { | ||
212 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
213 | int val; | ||
214 | |||
215 | if (min_uV < info->min_uV || max_uV > info->max_uV) | ||
216 | return -EDOM; | ||
217 | |||
218 | val = (min_uV - info->min_uV) / info->step_uV; | ||
219 | |||
220 | return max8907c_reg_write(info->i2c, info->reg_base + MAX8907C_VOUT, val); | ||
221 | } | ||
222 | |||
223 | static int max8907c_regulator_bbat_set_voltage(struct regulator_dev *rdev, | ||
224 | int min_uV, int max_uV) | ||
225 | { | ||
226 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
227 | int val; | ||
228 | |||
229 | if (min_uV < info->min_uV || max_uV > info->max_uV) | ||
230 | return -EDOM; | ||
231 | |||
232 | val = (min_uV - info->min_uV) / info->step_uV; | ||
233 | |||
234 | return max8907c_set_bits(info->i2c, info->reg_base, MAX8907C_MASK_VBBATTCV, | ||
235 | val); | ||
236 | } | ||
237 | |||
238 | static int max8907c_regulator_ldo_get_voltage(struct regulator_dev *rdev) | ||
239 | { | ||
240 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
241 | int val; | ||
242 | |||
243 | val = max8907c_reg_read(info->i2c, info->reg_base + MAX8907C_VOUT); | ||
244 | return val * info->step_uV + info->min_uV; | ||
245 | } | ||
246 | |||
247 | static int max8907c_regulator_fixed_get_voltage(struct regulator_dev *rdev) | ||
248 | { | ||
249 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
250 | |||
251 | return info->min_uV; | ||
252 | } | ||
253 | |||
254 | static int max8907c_regulator_bbat_get_voltage(struct regulator_dev *rdev) | ||
255 | { | ||
256 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
257 | int val; | ||
258 | |||
259 | val = | ||
260 | max8907c_reg_read(info->i2c, info->reg_base) & MAX8907C_MASK_VBBATTCV; | ||
261 | return val * info->step_uV + info->min_uV; | ||
262 | } | ||
263 | |||
264 | static int max8907c_regulator_wled_set_current_limit(struct regulator_dev *rdev, | ||
265 | int min_uA, int max_uA) | ||
266 | { | ||
267 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
268 | |||
269 | if (min_uA > 25500) | ||
270 | return -EDOM; | ||
271 | |||
272 | return max8907c_reg_write(info->i2c, info->reg_base, min_uA / 100); | ||
273 | } | ||
274 | |||
275 | static int max8907c_regulator_wled_get_current_limit(struct regulator_dev *rdev) | ||
276 | { | ||
277 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
278 | int val; | ||
279 | |||
280 | val = max8907c_reg_read(info->i2c, info->reg_base); | ||
281 | return val * 100; | ||
282 | } | ||
283 | |||
284 | static int max8907c_regulator_ldo_enable(struct regulator_dev *rdev) | ||
285 | { | ||
286 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
287 | |||
288 | return max8907c_set_bits(info->i2c, info->reg_base + MAX8907C_CTL, | ||
289 | MAX8907C_MASK_LDO_EN | MAX8907C_MASK_LDO_SEQ, | ||
290 | MAX8907C_MASK_LDO_EN | MAX8907C_MASK_LDO_SEQ); | ||
291 | } | ||
292 | |||
293 | static int max8907c_regulator_out5v_enable(struct regulator_dev *rdev) | ||
294 | { | ||
295 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
296 | |||
297 | return max8907c_set_bits(info->i2c, info->reg_base, | ||
298 | MAX8907C_MASK_OUT5V_VINEN | | ||
299 | MAX8907C_MASK_OUT5V_ENSRC | | ||
300 | MAX8907C_MASK_OUT5V_EN, | ||
301 | MAX8907C_MASK_OUT5V_ENSRC | | ||
302 | MAX8907C_MASK_OUT5V_EN); | ||
303 | } | ||
304 | |||
305 | static int max8907c_regulator_ldo_disable(struct regulator_dev *rdev) | ||
306 | { | ||
307 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
308 | |||
309 | return max8907c_set_bits(info->i2c, info->reg_base + MAX8907C_CTL, | ||
310 | MAX8907C_MASK_LDO_EN | MAX8907C_MASK_LDO_SEQ, | ||
311 | MAX8907C_MASK_LDO_SEQ); | ||
312 | } | ||
313 | |||
314 | static int max8907c_regulator_out5v_disable(struct regulator_dev *rdev) | ||
315 | { | ||
316 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
317 | |||
318 | return max8907c_set_bits(info->i2c, info->reg_base, | ||
319 | MAX8907C_MASK_OUT5V_VINEN | | ||
320 | MAX8907C_MASK_OUT5V_ENSRC | | ||
321 | MAX8907C_MASK_OUT5V_EN, | ||
322 | MAX8907C_MASK_OUT5V_ENSRC); | ||
323 | } | ||
324 | |||
325 | static int max8907c_regulator_ldo_is_enabled(struct regulator_dev *rdev) | ||
326 | { | ||
327 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
328 | int val; | ||
329 | |||
330 | val = max8907c_reg_read(info->i2c, info->reg_base + MAX8907C_CTL); | ||
331 | if (val < 0) | ||
332 | return -EDOM; | ||
333 | |||
334 | return (val & MAX8907C_MASK_LDO_EN) || !(val & MAX8907C_MASK_LDO_SEQ); | ||
335 | } | ||
336 | |||
337 | static int max8907c_regulator_out5v_is_enabled(struct regulator_dev *rdev) | ||
338 | { | ||
339 | const struct max8907c_regulator_info *info = rdev_get_drvdata(rdev); | ||
340 | int val; | ||
341 | |||
342 | val = max8907c_reg_read(info->i2c, info->reg_base); | ||
343 | if (val < 0) | ||
344 | return -EDOM; | ||
345 | |||
346 | if ((val & | ||
347 | (MAX8907C_MASK_OUT5V_VINEN | MAX8907C_MASK_OUT5V_ENSRC | | ||
348 | MAX8907C_MASK_OUT5V_EN)) | ||
349 | == MAX8907C_MASK_OUT5V_ENSRC) | ||
350 | return 1; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int max8907c_regulator_probe(struct platform_device *pdev) | ||
356 | { | ||
357 | struct max8907c *max8907c = dev_get_drvdata(pdev->dev.parent); | ||
358 | struct max8907c_regulator_info *info; | ||
359 | struct regulator_dev *rdev; | ||
360 | u8 version; | ||
361 | |||
362 | /* Backwards compatibility with max8907b, SD1 uses different voltages */ | ||
363 | version = max8907c_reg_read(max8907c->i2c_power, MAX8907C_REG_II2RR); | ||
364 | if ((version & MAX8907C_II2RR_VERSION_MASK) == MAX8907C_II2RR_VERSION_REV_B) { | ||
365 | max8907c_regulators[MAX8907C_SD1].min_uV = 637500; | ||
366 | max8907c_regulators[MAX8907C_SD1].max_uV = 1425000; | ||
367 | max8907c_regulators[MAX8907C_SD1].step_uV = 12500; | ||
368 | } | ||
369 | |||
370 | info = &max8907c_regulators[pdev->id]; | ||
371 | info->i2c = max8907c->i2c_power; | ||
372 | |||
373 | rdev = regulator_register(&info->desc, | ||
374 | &pdev->dev, pdev->dev.platform_data, info); | ||
375 | if (IS_ERR(rdev)) { | ||
376 | dev_err(&pdev->dev, "Cannot register regulator \"%s\", %ld\n", | ||
377 | info->desc.name, PTR_ERR(rdev)); | ||
378 | goto error; | ||
379 | } | ||
380 | |||
381 | platform_set_drvdata(pdev, rdev); | ||
382 | return 0; | ||
383 | |||
384 | error: | ||
385 | return PTR_ERR(rdev); | ||
386 | } | ||
387 | |||
388 | static int max8907c_regulator_remove(struct platform_device *pdev) | ||
389 | { | ||
390 | struct regulator_dev *rdev = platform_get_drvdata(pdev); | ||
391 | |||
392 | regulator_unregister(rdev); | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static struct platform_driver max8907c_regulator_driver = { | ||
397 | .driver = { | ||
398 | .name = "max8907c-regulator", | ||
399 | .owner = THIS_MODULE, | ||
400 | }, | ||
401 | .probe = max8907c_regulator_probe, | ||
402 | .remove = __devexit_p(max8907c_regulator_remove), | ||
403 | }; | ||
404 | |||
405 | static int __init max8907c_regulator_init(void) | ||
406 | { | ||
407 | return platform_driver_register(&max8907c_regulator_driver); | ||
408 | } | ||
409 | |||
410 | subsys_initcall(max8907c_regulator_init); | ||
411 | |||
412 | static void __exit max8907c_reg_exit(void) | ||
413 | { | ||
414 | platform_driver_unregister(&max8907c_regulator_driver); | ||
415 | } | ||
416 | |||
417 | module_exit(max8907c_reg_exit); | ||
418 | |||
419 | MODULE_DESCRIPTION("MAX8907C regulator driver"); | ||
420 | MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>"); | ||
421 | MODULE_LICENSE("GPL"); | ||