diff options
author | Johan Hovold <jhovold@gmail.com> | 2012-05-03 06:26:36 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2012-05-09 11:20:01 -0400 |
commit | 16c5c023aac86228e3e94c4bf6d19708ea861a05 (patch) | |
tree | 2fb7e87ef18d2520e4e0e957e4512c9f92a3886d /drivers/mfd/lm3533-core.c | |
parent | ae8406357eca7fde4ff047e858d285faee836804 (diff) |
mfd: Add LM3533 lighting-power core driver
Add support for National Semiconductor / TI LM3533 lighting power chips.
This is the core driver which provides register access over I2C and
registers the ambient-light-sensor, LED and backlight sub-drivers.
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/lm3533-core.c')
-rw-r--r-- | drivers/mfd/lm3533-core.c | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c new file mode 100644 index 000000000000..75f4b7f5a4fd --- /dev/null +++ b/drivers/mfd/lm3533-core.c | |||
@@ -0,0 +1,717 @@ | |||
1 | /* | ||
2 | * lm3533-core.c -- LM3533 Core | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 Texas Instruments | ||
5 | * | ||
6 | * Author: Johan Hovold <jhovold@gmail.com> | ||
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 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/mfd/core.h> | ||
21 | #include <linux/regmap.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/uaccess.h> | ||
25 | |||
26 | #include <linux/mfd/lm3533.h> | ||
27 | |||
28 | |||
29 | #define LM3533_BOOST_OVP_MAX 0x03 | ||
30 | #define LM3533_BOOST_OVP_MASK 0x06 | ||
31 | #define LM3533_BOOST_OVP_SHIFT 1 | ||
32 | |||
33 | #define LM3533_BOOST_FREQ_MAX 0x01 | ||
34 | #define LM3533_BOOST_FREQ_MASK 0x01 | ||
35 | #define LM3533_BOOST_FREQ_SHIFT 0 | ||
36 | |||
37 | #define LM3533_BL_ID_MASK 1 | ||
38 | #define LM3533_LED_ID_MASK 3 | ||
39 | #define LM3533_BL_ID_MAX 1 | ||
40 | #define LM3533_LED_ID_MAX 3 | ||
41 | |||
42 | #define LM3533_HVLED_ID_MAX 2 | ||
43 | #define LM3533_LVLED_ID_MAX 5 | ||
44 | |||
45 | #define LM3533_REG_OUTPUT_CONF1 0x10 | ||
46 | #define LM3533_REG_OUTPUT_CONF2 0x11 | ||
47 | #define LM3533_REG_BOOST_PWM 0x2c | ||
48 | |||
49 | #define LM3533_REG_MAX 0xb2 | ||
50 | |||
51 | |||
52 | static struct mfd_cell lm3533_als_devs[] = { | ||
53 | { | ||
54 | .name = "lm3533-als", | ||
55 | .id = -1, | ||
56 | }, | ||
57 | }; | ||
58 | |||
59 | static struct mfd_cell lm3533_bl_devs[] = { | ||
60 | { | ||
61 | .name = "lm3533-backlight", | ||
62 | .id = 0, | ||
63 | }, | ||
64 | { | ||
65 | .name = "lm3533-backlight", | ||
66 | .id = 1, | ||
67 | }, | ||
68 | }; | ||
69 | |||
70 | static struct mfd_cell lm3533_led_devs[] = { | ||
71 | { | ||
72 | .name = "lm3533-leds", | ||
73 | .id = 0, | ||
74 | }, | ||
75 | { | ||
76 | .name = "lm3533-leds", | ||
77 | .id = 1, | ||
78 | }, | ||
79 | { | ||
80 | .name = "lm3533-leds", | ||
81 | .id = 2, | ||
82 | }, | ||
83 | { | ||
84 | .name = "lm3533-leds", | ||
85 | .id = 3, | ||
86 | }, | ||
87 | }; | ||
88 | |||
89 | int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val) | ||
90 | { | ||
91 | int tmp; | ||
92 | int ret; | ||
93 | |||
94 | ret = regmap_read(lm3533->regmap, reg, &tmp); | ||
95 | if (ret < 0) { | ||
96 | dev_err(lm3533->dev, "failed to read register %02x: %d\n", | ||
97 | reg, ret); | ||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | *val = tmp; | ||
102 | |||
103 | dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | EXPORT_SYMBOL_GPL(lm3533_read); | ||
108 | |||
109 | int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val) | ||
110 | { | ||
111 | int ret; | ||
112 | |||
113 | dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val); | ||
114 | |||
115 | ret = regmap_write(lm3533->regmap, reg, val); | ||
116 | if (ret < 0) { | ||
117 | dev_err(lm3533->dev, "failed to write register %02x: %d\n", | ||
118 | reg, ret); | ||
119 | } | ||
120 | |||
121 | return ret; | ||
122 | } | ||
123 | EXPORT_SYMBOL_GPL(lm3533_write); | ||
124 | |||
125 | int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask) | ||
126 | { | ||
127 | int ret; | ||
128 | |||
129 | dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask); | ||
130 | |||
131 | ret = regmap_update_bits(lm3533->regmap, reg, val, mask); | ||
132 | if (ret < 0) { | ||
133 | dev_err(lm3533->dev, "failed to update register %02x: %d\n", | ||
134 | reg, ret); | ||
135 | } | ||
136 | |||
137 | return ret; | ||
138 | } | ||
139 | EXPORT_SYMBOL_GPL(lm3533_update); | ||
140 | |||
141 | /* | ||
142 | * HVLED output config -- output hvled controlled by backlight bl | ||
143 | */ | ||
144 | static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl) | ||
145 | { | ||
146 | u8 val; | ||
147 | u8 mask; | ||
148 | int shift; | ||
149 | int ret; | ||
150 | |||
151 | if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX) | ||
152 | return -EINVAL; | ||
153 | |||
154 | if (bl > LM3533_BL_ID_MAX) | ||
155 | return -EINVAL; | ||
156 | |||
157 | shift = hvled - 1; | ||
158 | mask = LM3533_BL_ID_MASK << shift; | ||
159 | val = bl << shift; | ||
160 | |||
161 | ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask); | ||
162 | if (ret) | ||
163 | dev_err(lm3533->dev, "failed to set hvled config\n"); | ||
164 | |||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * LVLED output config -- output lvled controlled by LED led | ||
170 | */ | ||
171 | static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led) | ||
172 | { | ||
173 | u8 reg; | ||
174 | u8 val; | ||
175 | u8 mask; | ||
176 | int shift; | ||
177 | int ret; | ||
178 | |||
179 | if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX) | ||
180 | return -EINVAL; | ||
181 | |||
182 | if (led > LM3533_LED_ID_MAX) | ||
183 | return -EINVAL; | ||
184 | |||
185 | if (lvled < 4) { | ||
186 | reg = LM3533_REG_OUTPUT_CONF1; | ||
187 | shift = 2 * lvled; | ||
188 | } else { | ||
189 | reg = LM3533_REG_OUTPUT_CONF2; | ||
190 | shift = 2 * (lvled - 4); | ||
191 | } | ||
192 | |||
193 | mask = LM3533_LED_ID_MASK << shift; | ||
194 | val = led << shift; | ||
195 | |||
196 | ret = lm3533_update(lm3533, reg, val, mask); | ||
197 | if (ret) | ||
198 | dev_err(lm3533->dev, "failed to set lvled config\n"); | ||
199 | |||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | static void lm3533_enable(struct lm3533 *lm3533) | ||
204 | { | ||
205 | if (gpio_is_valid(lm3533->gpio_hwen)) | ||
206 | gpio_set_value(lm3533->gpio_hwen, 1); | ||
207 | } | ||
208 | |||
209 | static void lm3533_disable(struct lm3533 *lm3533) | ||
210 | { | ||
211 | if (gpio_is_valid(lm3533->gpio_hwen)) | ||
212 | gpio_set_value(lm3533->gpio_hwen, 0); | ||
213 | } | ||
214 | |||
215 | enum lm3533_attribute_type { | ||
216 | LM3533_ATTR_TYPE_BACKLIGHT, | ||
217 | LM3533_ATTR_TYPE_LED, | ||
218 | }; | ||
219 | |||
220 | struct lm3533_device_attribute { | ||
221 | struct device_attribute dev_attr; | ||
222 | enum lm3533_attribute_type type; | ||
223 | union { | ||
224 | struct { | ||
225 | u8 id; | ||
226 | } output; | ||
227 | struct { | ||
228 | u8 reg; | ||
229 | u8 shift; | ||
230 | u8 mask; | ||
231 | u8 max; | ||
232 | } generic; | ||
233 | } u; | ||
234 | }; | ||
235 | |||
236 | #define to_lm3533_dev_attr(_attr) \ | ||
237 | container_of(_attr, struct lm3533_device_attribute, dev_attr) | ||
238 | |||
239 | static ssize_t show_lm3533_reg(struct device *dev, | ||
240 | struct device_attribute *attr, char *buf) | ||
241 | { | ||
242 | struct lm3533 *lm3533 = dev_get_drvdata(dev); | ||
243 | struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); | ||
244 | u8 val; | ||
245 | int ret; | ||
246 | |||
247 | ret = lm3533_read(lm3533, lattr->u.generic.reg, &val); | ||
248 | if (ret) | ||
249 | return ret; | ||
250 | |||
251 | val = (val & lattr->u.generic.mask) >> lattr->u.generic.shift; | ||
252 | |||
253 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | ||
254 | } | ||
255 | |||
256 | static ssize_t store_lm3533_reg(struct device *dev, | ||
257 | struct device_attribute *attr, | ||
258 | const char *buf, size_t len) | ||
259 | { | ||
260 | struct lm3533 *lm3533 = dev_get_drvdata(dev); | ||
261 | struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); | ||
262 | u8 val; | ||
263 | int ret; | ||
264 | |||
265 | if (kstrtou8(buf, 0, &val) || val > lattr->u.generic.max) | ||
266 | return -EINVAL; | ||
267 | |||
268 | val = val << lattr->u.generic.shift; | ||
269 | ret = lm3533_update(lm3533, lattr->u.generic.reg, val, | ||
270 | lattr->u.generic.mask); | ||
271 | if (ret) | ||
272 | return ret; | ||
273 | |||
274 | return len; | ||
275 | } | ||
276 | |||
277 | #define GENERIC_ATTR(_reg, _max, _mask, _shift) \ | ||
278 | { .reg = _reg, \ | ||
279 | .max = _max, \ | ||
280 | .mask = _mask, \ | ||
281 | .shift = _shift } | ||
282 | |||
283 | #define LM3533_GENERIC_ATTR(_name, _mode, _show, _store, _type, \ | ||
284 | _reg, _max, _mask, _shift) \ | ||
285 | struct lm3533_device_attribute lm3533_dev_attr_##_name = { \ | ||
286 | .dev_attr = __ATTR(_name, _mode, _show, _store), \ | ||
287 | .type = _type, \ | ||
288 | .u.generic = GENERIC_ATTR(_reg, _max, _mask, _shift) } | ||
289 | |||
290 | #define LM3533_GENERIC_ATTR_RW(_name, _type, _reg, _max, _mask, _shift) \ | ||
291 | LM3533_GENERIC_ATTR(_name, S_IRUGO | S_IWUSR, \ | ||
292 | show_lm3533_reg, store_lm3533_reg, \ | ||
293 | _type, _reg, _max, _mask, _shift) | ||
294 | |||
295 | #define LM3533_BOOST_ATTR_RW(_name, _NAME) \ | ||
296 | LM3533_GENERIC_ATTR_RW(_name, LM3533_ATTR_TYPE_BACKLIGHT, \ | ||
297 | LM3533_REG_BOOST_PWM, LM3533_##_NAME##_MAX, \ | ||
298 | LM3533_##_NAME##_MASK, LM3533_##_NAME##_SHIFT) | ||
299 | /* | ||
300 | * Boost Over Voltage Protection Select | ||
301 | * | ||
302 | * 0 - 16 V (default) | ||
303 | * 1 - 24 V | ||
304 | * 2 - 32 V | ||
305 | * 3 - 40 V | ||
306 | */ | ||
307 | static LM3533_BOOST_ATTR_RW(boost_ovp, BOOST_OVP); | ||
308 | |||
309 | /* | ||
310 | * Boost Frequency Select | ||
311 | * | ||
312 | * 0 - 500 kHz (default) | ||
313 | * 1 - 1 MHz | ||
314 | */ | ||
315 | static LM3533_BOOST_ATTR_RW(boost_freq, BOOST_FREQ); | ||
316 | |||
317 | static ssize_t show_output(struct device *dev, | ||
318 | struct device_attribute *attr, char *buf) | ||
319 | { | ||
320 | struct lm3533 *lm3533 = dev_get_drvdata(dev); | ||
321 | struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); | ||
322 | int id = lattr->u.output.id; | ||
323 | u8 reg; | ||
324 | u8 val; | ||
325 | u8 mask; | ||
326 | int shift; | ||
327 | int ret; | ||
328 | |||
329 | if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) { | ||
330 | reg = LM3533_REG_OUTPUT_CONF1; | ||
331 | shift = id - 1; | ||
332 | mask = LM3533_BL_ID_MASK << shift; | ||
333 | } else { | ||
334 | if (id < 4) { | ||
335 | reg = LM3533_REG_OUTPUT_CONF1; | ||
336 | shift = 2 * id; | ||
337 | } else { | ||
338 | reg = LM3533_REG_OUTPUT_CONF2; | ||
339 | shift = 2 * (id - 4); | ||
340 | } | ||
341 | mask = LM3533_LED_ID_MASK << shift; | ||
342 | } | ||
343 | |||
344 | ret = lm3533_read(lm3533, reg, &val); | ||
345 | if (ret) | ||
346 | return ret; | ||
347 | |||
348 | val = (val & mask) >> shift; | ||
349 | |||
350 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | ||
351 | } | ||
352 | |||
353 | static ssize_t store_output(struct device *dev, | ||
354 | struct device_attribute *attr, | ||
355 | const char *buf, size_t len) | ||
356 | { | ||
357 | struct lm3533 *lm3533 = dev_get_drvdata(dev); | ||
358 | struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); | ||
359 | int id = lattr->u.output.id; | ||
360 | u8 val; | ||
361 | int ret; | ||
362 | |||
363 | if (kstrtou8(buf, 0, &val)) | ||
364 | return -EINVAL; | ||
365 | |||
366 | if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) | ||
367 | ret = lm3533_set_hvled_config(lm3533, id, val); | ||
368 | else | ||
369 | ret = lm3533_set_lvled_config(lm3533, id, val); | ||
370 | |||
371 | if (ret) | ||
372 | return ret; | ||
373 | |||
374 | return len; | ||
375 | } | ||
376 | |||
377 | #define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \ | ||
378 | struct lm3533_device_attribute lm3533_dev_attr_##_name = \ | ||
379 | { .dev_attr = __ATTR(_name, _mode, _show, _store), \ | ||
380 | .type = _type, \ | ||
381 | .u.output = { .id = _id }, } | ||
382 | |||
383 | #define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \ | ||
384 | LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \ | ||
385 | show_output, store_output, _type, _id) | ||
386 | |||
387 | #define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \ | ||
388 | LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr) | ||
389 | #define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \ | ||
390 | LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr) | ||
391 | /* | ||
392 | * Output config: | ||
393 | * | ||
394 | * output_hvled<nr> 0-1 | ||
395 | * output_lvled<nr> 0-3 | ||
396 | */ | ||
397 | static LM3533_OUTPUT_HVLED_ATTR_RW(1); | ||
398 | static LM3533_OUTPUT_HVLED_ATTR_RW(2); | ||
399 | static LM3533_OUTPUT_LVLED_ATTR_RW(1); | ||
400 | static LM3533_OUTPUT_LVLED_ATTR_RW(2); | ||
401 | static LM3533_OUTPUT_LVLED_ATTR_RW(3); | ||
402 | static LM3533_OUTPUT_LVLED_ATTR_RW(4); | ||
403 | static LM3533_OUTPUT_LVLED_ATTR_RW(5); | ||
404 | |||
405 | static struct attribute *lm3533_attributes[] = { | ||
406 | &lm3533_dev_attr_boost_freq.dev_attr.attr, | ||
407 | &lm3533_dev_attr_boost_ovp.dev_attr.attr, | ||
408 | &lm3533_dev_attr_output_hvled1.dev_attr.attr, | ||
409 | &lm3533_dev_attr_output_hvled2.dev_attr.attr, | ||
410 | &lm3533_dev_attr_output_lvled1.dev_attr.attr, | ||
411 | &lm3533_dev_attr_output_lvled2.dev_attr.attr, | ||
412 | &lm3533_dev_attr_output_lvled3.dev_attr.attr, | ||
413 | &lm3533_dev_attr_output_lvled4.dev_attr.attr, | ||
414 | &lm3533_dev_attr_output_lvled5.dev_attr.attr, | ||
415 | NULL, | ||
416 | }; | ||
417 | |||
418 | #define to_dev_attr(_attr) \ | ||
419 | container_of(_attr, struct device_attribute, attr) | ||
420 | |||
421 | static mode_t lm3533_attr_is_visible(struct kobject *kobj, | ||
422 | struct attribute *attr, int n) | ||
423 | { | ||
424 | struct device *dev = container_of(kobj, struct device, kobj); | ||
425 | struct lm3533 *lm3533 = dev_get_drvdata(dev); | ||
426 | struct device_attribute *dattr = to_dev_attr(attr); | ||
427 | struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr); | ||
428 | enum lm3533_attribute_type type = lattr->type; | ||
429 | mode_t mode = attr->mode; | ||
430 | |||
431 | if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT) | ||
432 | mode = 0; | ||
433 | else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED) | ||
434 | mode = 0; | ||
435 | |||
436 | return mode; | ||
437 | }; | ||
438 | |||
439 | static struct attribute_group lm3533_attribute_group = { | ||
440 | .is_visible = lm3533_attr_is_visible, | ||
441 | .attrs = lm3533_attributes | ||
442 | }; | ||
443 | |||
444 | static int __devinit lm3533_device_als_init(struct lm3533 *lm3533) | ||
445 | { | ||
446 | struct lm3533_platform_data *pdata = lm3533->dev->platform_data; | ||
447 | int ret; | ||
448 | |||
449 | if (!pdata->als) | ||
450 | return 0; | ||
451 | |||
452 | lm3533_als_devs[0].platform_data = pdata->als; | ||
453 | lm3533_als_devs[0].pdata_size = sizeof(*pdata->als); | ||
454 | |||
455 | ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0); | ||
456 | if (ret) { | ||
457 | dev_err(lm3533->dev, "failed to add ALS device\n"); | ||
458 | return ret; | ||
459 | } | ||
460 | |||
461 | lm3533->have_als = 1; | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533) | ||
467 | { | ||
468 | struct lm3533_platform_data *pdata = lm3533->dev->platform_data; | ||
469 | int i; | ||
470 | int ret; | ||
471 | |||
472 | if (!pdata->backlights || pdata->num_backlights == 0) | ||
473 | return 0; | ||
474 | |||
475 | if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs)) | ||
476 | pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs); | ||
477 | |||
478 | for (i = 0; i < pdata->num_backlights; ++i) { | ||
479 | lm3533_bl_devs[i].platform_data = &pdata->backlights[i]; | ||
480 | lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]); | ||
481 | } | ||
482 | |||
483 | ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs, | ||
484 | pdata->num_backlights, NULL, 0); | ||
485 | if (ret) { | ||
486 | dev_err(lm3533->dev, "failed to add backlight devices\n"); | ||
487 | return ret; | ||
488 | } | ||
489 | |||
490 | lm3533->have_backlights = 1; | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int __devinit lm3533_device_led_init(struct lm3533 *lm3533) | ||
496 | { | ||
497 | struct lm3533_platform_data *pdata = lm3533->dev->platform_data; | ||
498 | int i; | ||
499 | int ret; | ||
500 | |||
501 | if (!pdata->leds || pdata->num_leds == 0) | ||
502 | return 0; | ||
503 | |||
504 | if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs)) | ||
505 | pdata->num_leds = ARRAY_SIZE(lm3533_led_devs); | ||
506 | |||
507 | for (i = 0; i < pdata->num_leds; ++i) { | ||
508 | lm3533_led_devs[i].platform_data = &pdata->leds[i]; | ||
509 | lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]); | ||
510 | } | ||
511 | |||
512 | ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs, | ||
513 | pdata->num_leds, NULL, 0); | ||
514 | if (ret) { | ||
515 | dev_err(lm3533->dev, "failed to add LED devices\n"); | ||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | lm3533->have_leds = 1; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int __devinit lm3533_device_init(struct lm3533 *lm3533) | ||
525 | { | ||
526 | struct lm3533_platform_data *pdata = lm3533->dev->platform_data; | ||
527 | int ret; | ||
528 | |||
529 | dev_dbg(lm3533->dev, "%s\n", __func__); | ||
530 | |||
531 | if (!pdata) { | ||
532 | dev_err(lm3533->dev, "no platform data\n"); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | |||
536 | lm3533->gpio_hwen = pdata->gpio_hwen; | ||
537 | |||
538 | dev_set_drvdata(lm3533->dev, lm3533); | ||
539 | |||
540 | if (gpio_is_valid(lm3533->gpio_hwen)) { | ||
541 | ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW, | ||
542 | "lm3533-hwen"); | ||
543 | if (ret < 0) { | ||
544 | dev_err(lm3533->dev, | ||
545 | "failed to request HWEN GPIO %d\n", | ||
546 | lm3533->gpio_hwen); | ||
547 | return ret; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | lm3533_enable(lm3533); | ||
552 | |||
553 | lm3533_device_als_init(lm3533); | ||
554 | lm3533_device_bl_init(lm3533); | ||
555 | lm3533_device_led_init(lm3533); | ||
556 | |||
557 | ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group); | ||
558 | if (ret < 0) { | ||
559 | dev_err(lm3533->dev, "failed to create sysfs attributes\n"); | ||
560 | goto err_unregister; | ||
561 | } | ||
562 | |||
563 | return 0; | ||
564 | |||
565 | err_unregister: | ||
566 | mfd_remove_devices(lm3533->dev); | ||
567 | lm3533_disable(lm3533); | ||
568 | if (gpio_is_valid(lm3533->gpio_hwen)) | ||
569 | gpio_free(lm3533->gpio_hwen); | ||
570 | |||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | static void __devexit lm3533_device_exit(struct lm3533 *lm3533) | ||
575 | { | ||
576 | dev_dbg(lm3533->dev, "%s\n", __func__); | ||
577 | |||
578 | sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group); | ||
579 | |||
580 | mfd_remove_devices(lm3533->dev); | ||
581 | lm3533_disable(lm3533); | ||
582 | if (gpio_is_valid(lm3533->gpio_hwen)) | ||
583 | gpio_free(lm3533->gpio_hwen); | ||
584 | } | ||
585 | |||
586 | static bool lm3533_readable_register(struct device *dev, unsigned int reg) | ||
587 | { | ||
588 | switch (reg) { | ||
589 | case 0x10 ... 0x2c: | ||
590 | case 0x30 ... 0x38: | ||
591 | case 0x40 ... 0x45: | ||
592 | case 0x50 ... 0x57: | ||
593 | case 0x60 ... 0x6e: | ||
594 | case 0x70 ... 0x75: | ||
595 | case 0x80 ... 0x85: | ||
596 | case 0x90 ... 0x95: | ||
597 | case 0xa0 ... 0xa5: | ||
598 | case 0xb0 ... 0xb2: | ||
599 | return true; | ||
600 | default: | ||
601 | return false; | ||
602 | } | ||
603 | } | ||
604 | |||
605 | static bool lm3533_volatile_register(struct device *dev, unsigned int reg) | ||
606 | { | ||
607 | switch (reg) { | ||
608 | case 0x34: /* zone */ | ||
609 | case 0x37 ... 0x38: /* adc */ | ||
610 | case 0xb0 ... 0xb1: /* fault */ | ||
611 | return true; | ||
612 | default: | ||
613 | return false; | ||
614 | } | ||
615 | } | ||
616 | |||
617 | static bool lm3533_precious_register(struct device *dev, unsigned int reg) | ||
618 | { | ||
619 | switch (reg) { | ||
620 | case 0x34: /* zone */ | ||
621 | return true; | ||
622 | default: | ||
623 | return false; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static struct regmap_config regmap_config = { | ||
628 | .reg_bits = 8, | ||
629 | .val_bits = 8, | ||
630 | .max_register = LM3533_REG_MAX, | ||
631 | .readable_reg = lm3533_readable_register, | ||
632 | .volatile_reg = lm3533_volatile_register, | ||
633 | .precious_reg = lm3533_precious_register, | ||
634 | }; | ||
635 | |||
636 | static int __devinit lm3533_i2c_probe(struct i2c_client *i2c, | ||
637 | const struct i2c_device_id *id) | ||
638 | { | ||
639 | struct lm3533 *lm3533; | ||
640 | int ret; | ||
641 | |||
642 | dev_dbg(&i2c->dev, "%s\n", __func__); | ||
643 | |||
644 | lm3533 = kzalloc(sizeof(*lm3533), GFP_KERNEL); | ||
645 | if (!lm3533) | ||
646 | return -ENOMEM; | ||
647 | |||
648 | i2c_set_clientdata(i2c, lm3533); | ||
649 | |||
650 | lm3533->regmap = regmap_init_i2c(i2c, ®map_config); | ||
651 | if (IS_ERR(lm3533->regmap)) { | ||
652 | ret = PTR_ERR(lm3533->regmap); | ||
653 | goto err_regmap; | ||
654 | } | ||
655 | |||
656 | lm3533->dev = &i2c->dev; | ||
657 | lm3533->irq = i2c->irq; | ||
658 | |||
659 | ret = lm3533_device_init(lm3533); | ||
660 | if (ret) | ||
661 | goto err_dev; | ||
662 | |||
663 | return 0; | ||
664 | |||
665 | err_dev: | ||
666 | regmap_exit(lm3533->regmap); | ||
667 | err_regmap: | ||
668 | kfree(lm3533); | ||
669 | |||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | static int __devexit lm3533_i2c_remove(struct i2c_client *i2c) | ||
674 | { | ||
675 | struct lm3533 *lm3533 = i2c_get_clientdata(i2c); | ||
676 | |||
677 | dev_dbg(&i2c->dev, "%s\n", __func__); | ||
678 | |||
679 | lm3533_device_exit(lm3533); | ||
680 | regmap_exit(lm3533->regmap); | ||
681 | |||
682 | kfree(lm3533); | ||
683 | |||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | static const struct i2c_device_id lm3533_i2c_ids[] = { | ||
688 | { "lm3533", 0 }, | ||
689 | { }, | ||
690 | }; | ||
691 | MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids); | ||
692 | |||
693 | static struct i2c_driver lm3533_i2c_driver = { | ||
694 | .driver = { | ||
695 | .name = "lm3533", | ||
696 | .owner = THIS_MODULE, | ||
697 | }, | ||
698 | .id_table = lm3533_i2c_ids, | ||
699 | .probe = lm3533_i2c_probe, | ||
700 | .remove = __devexit_p(lm3533_i2c_remove), | ||
701 | }; | ||
702 | |||
703 | static int __init lm3533_i2c_init(void) | ||
704 | { | ||
705 | return i2c_add_driver(&lm3533_i2c_driver); | ||
706 | } | ||
707 | subsys_initcall(lm3533_i2c_init); | ||
708 | |||
709 | static void __exit lm3533_i2c_exit(void) | ||
710 | { | ||
711 | i2c_del_driver(&lm3533_i2c_driver); | ||
712 | } | ||
713 | module_exit(lm3533_i2c_exit); | ||
714 | |||
715 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); | ||
716 | MODULE_DESCRIPTION("LM3533 Core"); | ||
717 | MODULE_LICENSE("GPL"); | ||