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 | |
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>
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 | 38 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 13 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/lm3533-core.c | 717 | ||||
-rw-r--r-- | drivers/mfd/lm3533-ctrlbank.c | 134 | ||||
-rw-r--r-- | include/linux/mfd/lm3533.h | 89 |
6 files changed, 992 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 new file mode 100644 index 000000000000..570072180b8d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 | |||
@@ -0,0 +1,38 @@ | |||
1 | What: /sys/bus/i2c/devices/.../boost_freq | ||
2 | Date: April 2012 | ||
3 | KernelVersion: 3.5 | ||
4 | Contact: Johan Hovold <jhovold@gmail.com> | ||
5 | Description: | ||
6 | Set the boost converter switching frequency (0, 1), where | ||
7 | |||
8 | 0 - 500Hz | ||
9 | 1 - 1000Hz | ||
10 | |||
11 | What: /sys/bus/i2c/devices/.../boost_ovp | ||
12 | Date: April 2012 | ||
13 | KernelVersion: 3.5 | ||
14 | Contact: Johan Hovold <jhovold@gmail.com> | ||
15 | Description: | ||
16 | Set the boost converter over-voltage protection threshold | ||
17 | (0..3), where | ||
18 | |||
19 | 0 - 16V | ||
20 | 1 - 24V | ||
21 | 2 - 32V | ||
22 | 3 - 40V | ||
23 | |||
24 | What: /sys/bus/i2c/devices/.../output_hvled[n] | ||
25 | Date: April 2012 | ||
26 | KernelVersion: 3.5 | ||
27 | Contact: Johan Hovold <jhovold@gmail.com> | ||
28 | Description: | ||
29 | Set the controlling backlight device for high-voltage current | ||
30 | sink HVLED[n] (n = 1, 2) (0, 1). | ||
31 | |||
32 | What: /sys/bus/i2c/devices/.../output_lvled[n] | ||
33 | Date: April 2012 | ||
34 | KernelVersion: 3.5 | ||
35 | Contact: Johan Hovold <jhovold@gmail.com> | ||
36 | Description: | ||
37 | Set the controlling led device for low-voltage current sink | ||
38 | LVLED[n] (n = 1..5) (0..3). | ||
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 48eed22c65a5..211f5dee9b68 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -106,6 +106,19 @@ config UCB1400_CORE | |||
106 | To compile this driver as a module, choose M here: the | 106 | To compile this driver as a module, choose M here: the |
107 | module will be called ucb1400_core. | 107 | module will be called ucb1400_core. |
108 | 108 | ||
109 | config MFD_LM3533 | ||
110 | tristate "LM3533 Lighting Power chip" | ||
111 | depends on I2C | ||
112 | select MFD_CORE | ||
113 | select REGMAP_I2C | ||
114 | help | ||
115 | Say yes here to enable support for National Semiconductor / TI | ||
116 | LM3533 Lighting Power chips. | ||
117 | |||
118 | This driver provides common support for accessing the device; | ||
119 | additional drivers must be enabled in order to use the LED, | ||
120 | backlight or ambient-light-sensor functionality of the device. | ||
121 | |||
109 | config TPS6105X | 122 | config TPS6105X |
110 | tristate "TPS61050/61052 Boost Converters" | 123 | tristate "TPS61050/61052 Boost Converters" |
111 | depends on I2C | 124 | depends on I2C |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0dc55cbefa09..d3dae9567800 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -120,3 +120,4 @@ obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o | |||
120 | obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o | 120 | obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o |
121 | obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o | 121 | obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o |
122 | obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o | 122 | obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o |
123 | obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o | ||
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"); | ||
diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c new file mode 100644 index 000000000000..c2732a37c65a --- /dev/null +++ b/drivers/mfd/lm3533-ctrlbank.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface | ||
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/device.h> | ||
15 | #include <linux/module.h> | ||
16 | |||
17 | #include <linux/mfd/lm3533.h> | ||
18 | |||
19 | |||
20 | #define LM3533_BRIGHTNESS_MAX 255 | ||
21 | #define LM3533_MAX_CURRENT_MAX 31 | ||
22 | #define LM3533_PWM_MAX 0x3f | ||
23 | |||
24 | #define LM3533_REG_PWM_BASE 0x14 | ||
25 | #define LM3533_REG_MAX_CURRENT_BASE 0x1f | ||
26 | #define LM3533_REG_CTRLBANK_ENABLE 0x27 | ||
27 | #define LM3533_REG_BRIGHTNESS_BASE 0x40 | ||
28 | |||
29 | |||
30 | static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base) | ||
31 | { | ||
32 | return base + cb->id; | ||
33 | } | ||
34 | |||
35 | int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb) | ||
36 | { | ||
37 | u8 mask; | ||
38 | int ret; | ||
39 | |||
40 | dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); | ||
41 | |||
42 | mask = 1 << cb->id; | ||
43 | ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, | ||
44 | mask, mask); | ||
45 | if (ret) | ||
46 | dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id); | ||
47 | |||
48 | return ret; | ||
49 | } | ||
50 | EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable); | ||
51 | |||
52 | int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb) | ||
53 | { | ||
54 | u8 mask; | ||
55 | int ret; | ||
56 | |||
57 | dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); | ||
58 | |||
59 | mask = 1 << cb->id; | ||
60 | ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask); | ||
61 | if (ret) | ||
62 | dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id); | ||
63 | |||
64 | return ret; | ||
65 | } | ||
66 | EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable); | ||
67 | |||
68 | #define lm3533_ctrlbank_set(_name, _NAME) \ | ||
69 | int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val) \ | ||
70 | { \ | ||
71 | u8 reg; \ | ||
72 | int ret; \ | ||
73 | \ | ||
74 | if (val > LM3533_##_NAME##_MAX) \ | ||
75 | return -EINVAL; \ | ||
76 | \ | ||
77 | reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ | ||
78 | ret = lm3533_write(cb->lm3533, reg, val); \ | ||
79 | if (ret) \ | ||
80 | dev_err(cb->dev, "failed to set " #_name "\n"); \ | ||
81 | \ | ||
82 | return ret; \ | ||
83 | } \ | ||
84 | EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name); | ||
85 | |||
86 | #define lm3533_ctrlbank_get(_name, _NAME) \ | ||
87 | int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val) \ | ||
88 | { \ | ||
89 | u8 reg; \ | ||
90 | int ret; \ | ||
91 | \ | ||
92 | reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ | ||
93 | ret = lm3533_read(cb->lm3533, reg, val); \ | ||
94 | if (ret) \ | ||
95 | dev_err(cb->dev, "failed to get " #_name "\n"); \ | ||
96 | \ | ||
97 | return ret; \ | ||
98 | } \ | ||
99 | EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name); | ||
100 | |||
101 | lm3533_ctrlbank_set(brightness, BRIGHTNESS); | ||
102 | lm3533_ctrlbank_get(brightness, BRIGHTNESS); | ||
103 | |||
104 | /* | ||
105 | * Full scale current. | ||
106 | * | ||
107 | * Imax = 5 + val * 0.8 mA, e.g.: | ||
108 | * | ||
109 | * 0 - 5 mA | ||
110 | * ... | ||
111 | * 19 - 20.2 mA (default) | ||
112 | * ... | ||
113 | * 31 - 29.8 mA | ||
114 | */ | ||
115 | lm3533_ctrlbank_set(max_current, MAX_CURRENT); | ||
116 | lm3533_ctrlbank_get(max_current, MAX_CURRENT); | ||
117 | |||
118 | /* | ||
119 | * PWM-input control mask: | ||
120 | * | ||
121 | * bit 5 - PWM-input enabled in Zone 4 | ||
122 | * bit 4 - PWM-input enabled in Zone 3 | ||
123 | * bit 3 - PWM-input enabled in Zone 2 | ||
124 | * bit 2 - PWM-input enabled in Zone 1 | ||
125 | * bit 1 - PWM-input enabled in Zone 0 | ||
126 | * bit 0 - PWM-input enabled | ||
127 | */ | ||
128 | lm3533_ctrlbank_set(pwm, PWM); | ||
129 | lm3533_ctrlbank_get(pwm, PWM); | ||
130 | |||
131 | |||
132 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); | ||
133 | MODULE_DESCRIPTION("LM3533 Control Bank interface"); | ||
134 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h new file mode 100644 index 000000000000..75f85f3fbd90 --- /dev/null +++ b/include/linux/mfd/lm3533.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * lm3533.h -- LM3533 interface | ||
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 | #ifndef __LINUX_MFD_LM3533_H | ||
15 | #define __LINUX_MFD_LM3533_H | ||
16 | |||
17 | #define LM3533_ATTR_RO(_name) \ | ||
18 | DEVICE_ATTR(_name, S_IRUGO, show_##_name, NULL) | ||
19 | #define LM3533_ATTR_RW(_name) \ | ||
20 | DEVICE_ATTR(_name, S_IRUGO | S_IWUSR , show_##_name, store_##_name) | ||
21 | |||
22 | struct device; | ||
23 | struct regmap; | ||
24 | |||
25 | struct lm3533 { | ||
26 | struct device *dev; | ||
27 | |||
28 | struct regmap *regmap; | ||
29 | |||
30 | int gpio_hwen; | ||
31 | int irq; | ||
32 | |||
33 | unsigned have_als:1; | ||
34 | unsigned have_backlights:1; | ||
35 | unsigned have_leds:1; | ||
36 | }; | ||
37 | |||
38 | struct lm3533_ctrlbank { | ||
39 | struct lm3533 *lm3533; | ||
40 | struct device *dev; | ||
41 | int id; | ||
42 | }; | ||
43 | |||
44 | struct lm3533_als_platform_data { | ||
45 | unsigned pwm_mode:1; /* PWM input mode (default analog) */ | ||
46 | }; | ||
47 | |||
48 | struct lm3533_bl_platform_data { | ||
49 | char *name; | ||
50 | u8 default_brightness; /* 0 - 255 */ | ||
51 | u8 max_current; /* 0 - 31 */ | ||
52 | u8 pwm; /* 0 - 0x3f */ | ||
53 | }; | ||
54 | |||
55 | struct lm3533_led_platform_data { | ||
56 | char *name; | ||
57 | const char *default_trigger; | ||
58 | u8 max_current; /* 0 - 31 */ | ||
59 | u8 pwm; /* 0 - 0x3f */ | ||
60 | }; | ||
61 | |||
62 | struct lm3533_platform_data { | ||
63 | int gpio_hwen; | ||
64 | |||
65 | struct lm3533_als_platform_data *als; | ||
66 | |||
67 | struct lm3533_bl_platform_data *backlights; | ||
68 | int num_backlights; | ||
69 | |||
70 | struct lm3533_led_platform_data *leds; | ||
71 | int num_leds; | ||
72 | }; | ||
73 | |||
74 | extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); | ||
75 | extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); | ||
76 | |||
77 | extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val); | ||
78 | extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val); | ||
79 | extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u8 val); | ||
80 | extern int lm3533_ctrlbank_get_max_current(struct lm3533_ctrlbank *cb, | ||
81 | u8 *val); | ||
82 | extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val); | ||
83 | extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val); | ||
84 | |||
85 | extern int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val); | ||
86 | extern int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val); | ||
87 | extern int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask); | ||
88 | |||
89 | #endif /* __LINUX_MFD_LM3533_H */ | ||