diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als | 62 | ||||
-rw-r--r-- | drivers/iio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/iio/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/Kconfig | 22 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 5 | ||||
-rw-r--r-- | drivers/iio/light/lm3533-als.c | 932 |
6 files changed, 1023 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als b/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als new file mode 100644 index 000000000000..694a52c8cf82 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als | |||
@@ -0,0 +1,62 @@ | |||
1 | What: /sys/.../events/in_illuminance0_thresh_either_en | ||
2 | Date: April 2012 | ||
3 | KernelVersion: 3.5 | ||
4 | Contact: Johan Hovold <jhovold@gmail.com> | ||
5 | Description: | ||
6 | Event generated when channel passes one of the four thresholds | ||
7 | in each direction (rising|falling) and a zone change occurs. | ||
8 | The corresponding light zone can be read from | ||
9 | in_illuminance0_zone. | ||
10 | |||
11 | What: /sys/.../events/in_illuminance0_threshY_hysteresis | ||
12 | Date: May 2012 | ||
13 | KernelVersion: 3.5 | ||
14 | Contact: Johan Hovold <jhovold@gmail.com> | ||
15 | Description: | ||
16 | Get the hysteresis for thresholds Y, that is, | ||
17 | |||
18 | threshY_hysteresis = threshY_raising - threshY_falling | ||
19 | |||
20 | What: /sys/.../events/illuminance_threshY_falling_value | ||
21 | What: /sys/.../events/illuminance_threshY_raising_value | ||
22 | Date: April 2012 | ||
23 | KernelVersion: 3.5 | ||
24 | Contact: Johan Hovold <jhovold@gmail.com> | ||
25 | Description: | ||
26 | Specifies the value of threshold that the device is comparing | ||
27 | against for the events enabled by | ||
28 | in_illuminance0_thresh_either_en (0..255), where Y in 0..3. | ||
29 | |||
30 | Note that threshY_falling must be less than or equal to | ||
31 | threshY_raising. | ||
32 | |||
33 | These thresholds correspond to the eight zone-boundary | ||
34 | registers (boundaryY_{low,high}) and defines the five light | ||
35 | zones. | ||
36 | |||
37 | What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_zone | ||
38 | Date: April 2012 | ||
39 | KernelVersion: 3.5 | ||
40 | Contact: Johan Hovold <jhovold@gmail.com> | ||
41 | Description: | ||
42 | Get the current light zone (0..4) as defined by the | ||
43 | in_illuminance0_threshY_{falling,rising} thresholds. | ||
44 | |||
45 | What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw | ||
46 | Date: May 2012 | ||
47 | KernelVersion: 3.5 | ||
48 | Contact: Johan Hovold <jhovold@gmail.com> | ||
49 | Description: | ||
50 | Get output current for channel Y (0..255), that is, | ||
51 | out_currentY_currentZ_raw, where Z is the current zone. | ||
52 | |||
53 | What: /sys/bus/iio/devices/iio:deviceX/out_currentY_currentZ_raw | ||
54 | Date: May 2012 | ||
55 | KernelVersion: 3.5 | ||
56 | Contact: Johan Hovold <jhovold@gmail.com> | ||
57 | Description: | ||
58 | Set the output current for channel out_currentY when in zone | ||
59 | Z (0..255), where Y in 0..2 and Z in 0..4. | ||
60 | |||
61 | These values correspond to the ALS-mapper target registers for | ||
62 | ALS-mapper Y + 1. | ||
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 56eecefcec75..cacc74d70241 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig | |||
@@ -50,5 +50,6 @@ config IIO_CONSUMERS_PER_TRIGGER | |||
50 | 50 | ||
51 | source "drivers/iio/adc/Kconfig" | 51 | source "drivers/iio/adc/Kconfig" |
52 | source "drivers/iio/amplifiers/Kconfig" | 52 | source "drivers/iio/amplifiers/Kconfig" |
53 | source "drivers/iio/light/Kconfig" | ||
53 | 54 | ||
54 | endif # IIO | 55 | endif # IIO |
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index e425afd1480c..060b674d278c 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile | |||
@@ -11,3 +11,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o | |||
11 | 11 | ||
12 | obj-y += adc/ | 12 | obj-y += adc/ |
13 | obj-y += amplifiers/ | 13 | obj-y += amplifiers/ |
14 | obj-y += light/ | ||
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig new file mode 100644 index 000000000000..db5618e7d90b --- /dev/null +++ b/drivers/iio/light/Kconfig | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # Light sensors | ||
3 | # | ||
4 | menu "Light sensors" | ||
5 | |||
6 | config SENSORS_LM3533 | ||
7 | tristate "LM3533 ambient light sensor" | ||
8 | depends on MFD_LM3533 | ||
9 | help | ||
10 | If you say yes here you get support for the ambient light sensor | ||
11 | interface on National Semiconductor / TI LM3533 Lighting Power | ||
12 | chips. | ||
13 | |||
14 | The sensor interface can be used to control the LEDs and backlights | ||
15 | of the chip through defining five light zones and three sets of | ||
16 | corresponding output-current values. | ||
17 | |||
18 | The driver provides raw and mean adc readings along with the current | ||
19 | light zone through sysfs. A threshold event can be generated on zone | ||
20 | changes. The ALS-control output values can be set per zone for the | ||
21 | three current output channels. | ||
22 | endmenu | ||
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile new file mode 100644 index 000000000000..c1c23a024cd2 --- /dev/null +++ b/drivers/iio/light/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for IIO Light sensors | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o | ||
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c new file mode 100644 index 000000000000..c3e7bac13123 --- /dev/null +++ b/drivers/iio/light/lm3533-als.c | |||
@@ -0,0 +1,932 @@ | |||
1 | /* | ||
2 | * lm3533-als.c -- LM3533 Ambient Light Sensor driver | ||
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/atomic.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/iio/events.h> | ||
19 | #include <linux/iio/iio.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/mfd/core.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/uaccess.h> | ||
26 | |||
27 | #include <linux/mfd/lm3533.h> | ||
28 | |||
29 | |||
30 | #define LM3533_ALS_RESISTOR_MIN 1 | ||
31 | #define LM3533_ALS_RESISTOR_MAX 127 | ||
32 | #define LM3533_ALS_CHANNEL_CURRENT_MAX 2 | ||
33 | #define LM3533_ALS_THRESH_MAX 3 | ||
34 | #define LM3533_ALS_ZONE_MAX 4 | ||
35 | |||
36 | #define LM3533_REG_ALS_RESISTOR_SELECT 0x30 | ||
37 | #define LM3533_REG_ALS_CONF 0x31 | ||
38 | #define LM3533_REG_ALS_ZONE_INFO 0x34 | ||
39 | #define LM3533_REG_ALS_READ_ADC_RAW 0x37 | ||
40 | #define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38 | ||
41 | #define LM3533_REG_ALS_BOUNDARY_BASE 0x50 | ||
42 | #define LM3533_REG_ALS_TARGET_BASE 0x60 | ||
43 | |||
44 | #define LM3533_ALS_ENABLE_MASK 0x01 | ||
45 | #define LM3533_ALS_INPUT_MODE_MASK 0x02 | ||
46 | #define LM3533_ALS_INT_ENABLE_MASK 0x01 | ||
47 | |||
48 | #define LM3533_ALS_ZONE_SHIFT 2 | ||
49 | #define LM3533_ALS_ZONE_MASK 0x1c | ||
50 | |||
51 | #define LM3533_ALS_FLAG_INT_ENABLED 1 | ||
52 | |||
53 | |||
54 | struct lm3533_als { | ||
55 | struct lm3533 *lm3533; | ||
56 | struct platform_device *pdev; | ||
57 | |||
58 | unsigned long flags; | ||
59 | int irq; | ||
60 | |||
61 | atomic_t zone; | ||
62 | struct mutex thresh_mutex; | ||
63 | }; | ||
64 | |||
65 | |||
66 | static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, | ||
67 | int *adc) | ||
68 | { | ||
69 | struct lm3533_als *als = iio_priv(indio_dev); | ||
70 | u8 reg; | ||
71 | u8 val; | ||
72 | int ret; | ||
73 | |||
74 | if (average) | ||
75 | reg = LM3533_REG_ALS_READ_ADC_AVERAGE; | ||
76 | else | ||
77 | reg = LM3533_REG_ALS_READ_ADC_RAW; | ||
78 | |||
79 | ret = lm3533_read(als->lm3533, reg, &val); | ||
80 | if (ret) { | ||
81 | dev_err(&indio_dev->dev, "failed to read adc\n"); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | *adc = val; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) | ||
91 | { | ||
92 | struct lm3533_als *als = iio_priv(indio_dev); | ||
93 | u8 val; | ||
94 | int ret; | ||
95 | |||
96 | ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); | ||
97 | if (ret) { | ||
98 | dev_err(&indio_dev->dev, "failed to read zone\n"); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT; | ||
103 | *zone = min_t(u8, val, LM3533_ALS_ZONE_MAX); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) | ||
109 | { | ||
110 | struct lm3533_als *als = iio_priv(indio_dev); | ||
111 | int ret; | ||
112 | |||
113 | if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) { | ||
114 | *zone = atomic_read(&als->zone); | ||
115 | } else { | ||
116 | ret = _lm3533_als_get_zone(indio_dev, zone); | ||
117 | if (ret) | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * channel output channel 0..2 | ||
126 | * zone zone 0..4 | ||
127 | */ | ||
128 | static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone) | ||
129 | { | ||
130 | return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone; | ||
131 | } | ||
132 | |||
133 | static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, | ||
134 | unsigned zone, u8 *val) | ||
135 | { | ||
136 | struct lm3533_als *als = iio_priv(indio_dev); | ||
137 | u8 reg; | ||
138 | int ret; | ||
139 | |||
140 | if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) | ||
141 | return -EINVAL; | ||
142 | |||
143 | if (zone > LM3533_ALS_ZONE_MAX) | ||
144 | return -EINVAL; | ||
145 | |||
146 | reg = lm3533_als_get_target_reg(channel, zone); | ||
147 | ret = lm3533_read(als->lm3533, reg, val); | ||
148 | if (ret) | ||
149 | dev_err(&indio_dev->dev, "failed to get target current\n"); | ||
150 | |||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, | ||
155 | unsigned zone, u8 val) | ||
156 | { | ||
157 | struct lm3533_als *als = iio_priv(indio_dev); | ||
158 | u8 reg; | ||
159 | int ret; | ||
160 | |||
161 | if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) | ||
162 | return -EINVAL; | ||
163 | |||
164 | if (zone > LM3533_ALS_ZONE_MAX) | ||
165 | return -EINVAL; | ||
166 | |||
167 | reg = lm3533_als_get_target_reg(channel, zone); | ||
168 | ret = lm3533_write(als->lm3533, reg, val); | ||
169 | if (ret) | ||
170 | dev_err(&indio_dev->dev, "failed to set target current\n"); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel, | ||
176 | int *val) | ||
177 | { | ||
178 | u8 zone; | ||
179 | u8 target; | ||
180 | int ret; | ||
181 | |||
182 | ret = lm3533_als_get_zone(indio_dev, &zone); | ||
183 | if (ret) | ||
184 | return ret; | ||
185 | |||
186 | ret = lm3533_als_get_target(indio_dev, channel, zone, &target); | ||
187 | if (ret) | ||
188 | return ret; | ||
189 | |||
190 | *val = target; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int lm3533_als_read_raw(struct iio_dev *indio_dev, | ||
196 | struct iio_chan_spec const *chan, | ||
197 | int *val, int *val2, long mask) | ||
198 | { | ||
199 | int ret; | ||
200 | |||
201 | switch (mask) { | ||
202 | case 0: | ||
203 | switch (chan->type) { | ||
204 | case IIO_LIGHT: | ||
205 | ret = lm3533_als_get_adc(indio_dev, false, val); | ||
206 | break; | ||
207 | case IIO_CURRENT: | ||
208 | ret = lm3533_als_get_current(indio_dev, chan->channel, | ||
209 | val); | ||
210 | break; | ||
211 | default: | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | break; | ||
215 | case IIO_CHAN_INFO_AVERAGE_RAW: | ||
216 | ret = lm3533_als_get_adc(indio_dev, true, val); | ||
217 | break; | ||
218 | default: | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | if (ret) | ||
223 | return ret; | ||
224 | |||
225 | return IIO_VAL_INT; | ||
226 | } | ||
227 | |||
228 | #define CHANNEL_CURRENT(_channel) \ | ||
229 | { \ | ||
230 | .type = IIO_CURRENT, \ | ||
231 | .channel = _channel, \ | ||
232 | .indexed = true, \ | ||
233 | .output = true, \ | ||
234 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \ | ||
235 | } | ||
236 | |||
237 | static const struct iio_chan_spec lm3533_als_channels[] = { | ||
238 | { | ||
239 | .type = IIO_LIGHT, | ||
240 | .channel = 0, | ||
241 | .indexed = true, | ||
242 | .info_mask = (IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT | | ||
243 | IIO_CHAN_INFO_RAW_SEPARATE_BIT), | ||
244 | }, | ||
245 | CHANNEL_CURRENT(0), | ||
246 | CHANNEL_CURRENT(1), | ||
247 | CHANNEL_CURRENT(2), | ||
248 | }; | ||
249 | |||
250 | static irqreturn_t lm3533_als_isr(int irq, void *dev_id) | ||
251 | { | ||
252 | |||
253 | struct iio_dev *indio_dev = dev_id; | ||
254 | struct lm3533_als *als = iio_priv(indio_dev); | ||
255 | u8 zone; | ||
256 | int ret; | ||
257 | |||
258 | /* Clear interrupt by reading the ALS zone register. */ | ||
259 | ret = _lm3533_als_get_zone(indio_dev, &zone); | ||
260 | if (ret) | ||
261 | goto out; | ||
262 | |||
263 | atomic_set(&als->zone, zone); | ||
264 | |||
265 | iio_push_event(indio_dev, | ||
266 | IIO_UNMOD_EVENT_CODE(IIO_LIGHT, | ||
267 | 0, | ||
268 | IIO_EV_TYPE_THRESH, | ||
269 | IIO_EV_DIR_EITHER), | ||
270 | iio_get_time_ns()); | ||
271 | out: | ||
272 | return IRQ_HANDLED; | ||
273 | } | ||
274 | |||
275 | static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) | ||
276 | { | ||
277 | struct lm3533_als *als = iio_priv(indio_dev); | ||
278 | u8 mask = LM3533_ALS_INT_ENABLE_MASK; | ||
279 | u8 val; | ||
280 | int ret; | ||
281 | |||
282 | if (enable) | ||
283 | val = mask; | ||
284 | else | ||
285 | val = 0; | ||
286 | |||
287 | ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask); | ||
288 | if (ret) { | ||
289 | dev_err(&indio_dev->dev, "failed to set int mode %d\n", | ||
290 | enable); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable) | ||
298 | { | ||
299 | struct lm3533_als *als = iio_priv(indio_dev); | ||
300 | u8 mask = LM3533_ALS_INT_ENABLE_MASK; | ||
301 | u8 val; | ||
302 | int ret; | ||
303 | |||
304 | ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); | ||
305 | if (ret) { | ||
306 | dev_err(&indio_dev->dev, "failed to get int mode\n"); | ||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | *enable = !!(val & mask); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising) | ||
316 | { | ||
317 | u8 offset = !raising; | ||
318 | |||
319 | return LM3533_REG_ALS_BOUNDARY_BASE + 2 * nr + offset; | ||
320 | } | ||
321 | |||
322 | static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, | ||
323 | bool raising, u8 *val) | ||
324 | { | ||
325 | struct lm3533_als *als = iio_priv(indio_dev); | ||
326 | u8 reg; | ||
327 | int ret; | ||
328 | |||
329 | if (nr > LM3533_ALS_THRESH_MAX) | ||
330 | return -EINVAL; | ||
331 | |||
332 | reg = lm3533_als_get_threshold_reg(nr, raising); | ||
333 | ret = lm3533_read(als->lm3533, reg, val); | ||
334 | if (ret) | ||
335 | dev_err(&indio_dev->dev, "failed to get threshold\n"); | ||
336 | |||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, | ||
341 | bool raising, u8 val) | ||
342 | { | ||
343 | struct lm3533_als *als = iio_priv(indio_dev); | ||
344 | u8 val2; | ||
345 | u8 reg, reg2; | ||
346 | int ret; | ||
347 | |||
348 | if (nr > LM3533_ALS_THRESH_MAX) | ||
349 | return -EINVAL; | ||
350 | |||
351 | reg = lm3533_als_get_threshold_reg(nr, raising); | ||
352 | reg2 = lm3533_als_get_threshold_reg(nr, !raising); | ||
353 | |||
354 | mutex_lock(&als->thresh_mutex); | ||
355 | ret = lm3533_read(als->lm3533, reg2, &val2); | ||
356 | if (ret) { | ||
357 | dev_err(&indio_dev->dev, "failed to get threshold\n"); | ||
358 | goto out; | ||
359 | } | ||
360 | /* | ||
361 | * This device does not allow negative hysteresis (in fact, it uses | ||
362 | * whichever value is smaller as the lower bound) so we need to make | ||
363 | * sure that thresh_falling <= thresh_raising. | ||
364 | */ | ||
365 | if ((raising && (val < val2)) || (!raising && (val > val2))) { | ||
366 | ret = -EINVAL; | ||
367 | goto out; | ||
368 | } | ||
369 | |||
370 | ret = lm3533_write(als->lm3533, reg, val); | ||
371 | if (ret) { | ||
372 | dev_err(&indio_dev->dev, "failed to set threshold\n"); | ||
373 | goto out; | ||
374 | } | ||
375 | out: | ||
376 | mutex_unlock(&als->thresh_mutex); | ||
377 | |||
378 | return ret; | ||
379 | } | ||
380 | |||
381 | static int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr, | ||
382 | u8 *val) | ||
383 | { | ||
384 | struct lm3533_als *als = iio_priv(indio_dev); | ||
385 | u8 falling; | ||
386 | u8 raising; | ||
387 | int ret; | ||
388 | |||
389 | if (nr > LM3533_ALS_THRESH_MAX) | ||
390 | return -EINVAL; | ||
391 | |||
392 | mutex_lock(&als->thresh_mutex); | ||
393 | ret = lm3533_als_get_threshold(indio_dev, nr, false, &falling); | ||
394 | if (ret) | ||
395 | goto out; | ||
396 | ret = lm3533_als_get_threshold(indio_dev, nr, true, &raising); | ||
397 | if (ret) | ||
398 | goto out; | ||
399 | |||
400 | *val = raising - falling; | ||
401 | out: | ||
402 | mutex_unlock(&als->thresh_mutex); | ||
403 | |||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | static int show_thresh_either_en(struct device *dev, | ||
408 | struct device_attribute *attr, | ||
409 | char *buf) | ||
410 | { | ||
411 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
412 | struct lm3533_als *als = iio_priv(indio_dev); | ||
413 | int enable; | ||
414 | int ret; | ||
415 | |||
416 | if (als->irq) { | ||
417 | ret = lm3533_als_get_int_mode(indio_dev, &enable); | ||
418 | if (ret) | ||
419 | return ret; | ||
420 | } else { | ||
421 | enable = 0; | ||
422 | } | ||
423 | |||
424 | return scnprintf(buf, PAGE_SIZE, "%u\n", enable); | ||
425 | } | ||
426 | |||
427 | static int store_thresh_either_en(struct device *dev, | ||
428 | struct device_attribute *attr, | ||
429 | const char *buf, size_t len) | ||
430 | { | ||
431 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
432 | struct lm3533_als *als = iio_priv(indio_dev); | ||
433 | unsigned long enable; | ||
434 | bool int_enabled; | ||
435 | u8 zone; | ||
436 | int ret; | ||
437 | |||
438 | if (!als->irq) | ||
439 | return -EBUSY; | ||
440 | |||
441 | if (kstrtoul(buf, 0, &enable)) | ||
442 | return -EINVAL; | ||
443 | |||
444 | int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); | ||
445 | |||
446 | if (enable && !int_enabled) { | ||
447 | ret = lm3533_als_get_zone(indio_dev, &zone); | ||
448 | if (ret) | ||
449 | return ret; | ||
450 | |||
451 | atomic_set(&als->zone, zone); | ||
452 | |||
453 | set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); | ||
454 | } | ||
455 | |||
456 | ret = lm3533_als_set_int_mode(indio_dev, enable); | ||
457 | if (ret) { | ||
458 | if (!int_enabled) | ||
459 | clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); | ||
460 | |||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | if (!enable) | ||
465 | clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); | ||
466 | |||
467 | return len; | ||
468 | } | ||
469 | |||
470 | static ssize_t show_zone(struct device *dev, | ||
471 | struct device_attribute *attr, char *buf) | ||
472 | { | ||
473 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
474 | u8 zone; | ||
475 | int ret; | ||
476 | |||
477 | ret = lm3533_als_get_zone(indio_dev, &zone); | ||
478 | if (ret) | ||
479 | return ret; | ||
480 | |||
481 | return scnprintf(buf, PAGE_SIZE, "%u\n", zone); | ||
482 | } | ||
483 | |||
484 | enum lm3533_als_attribute_type { | ||
485 | LM3533_ATTR_TYPE_HYSTERESIS, | ||
486 | LM3533_ATTR_TYPE_TARGET, | ||
487 | LM3533_ATTR_TYPE_THRESH_FALLING, | ||
488 | LM3533_ATTR_TYPE_THRESH_RAISING, | ||
489 | }; | ||
490 | |||
491 | struct lm3533_als_attribute { | ||
492 | struct device_attribute dev_attr; | ||
493 | enum lm3533_als_attribute_type type; | ||
494 | u8 val1; | ||
495 | u8 val2; | ||
496 | }; | ||
497 | |||
498 | static inline struct lm3533_als_attribute * | ||
499 | to_lm3533_als_attr(struct device_attribute *attr) | ||
500 | { | ||
501 | return container_of(attr, struct lm3533_als_attribute, dev_attr); | ||
502 | } | ||
503 | |||
504 | static ssize_t show_als_attr(struct device *dev, | ||
505 | struct device_attribute *attr, | ||
506 | char *buf) | ||
507 | { | ||
508 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
509 | struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); | ||
510 | u8 val; | ||
511 | int ret; | ||
512 | |||
513 | switch (als_attr->type) { | ||
514 | case LM3533_ATTR_TYPE_HYSTERESIS: | ||
515 | ret = lm3533_als_get_hysteresis(indio_dev, als_attr->val1, | ||
516 | &val); | ||
517 | break; | ||
518 | case LM3533_ATTR_TYPE_TARGET: | ||
519 | ret = lm3533_als_get_target(indio_dev, als_attr->val1, | ||
520 | als_attr->val2, &val); | ||
521 | break; | ||
522 | case LM3533_ATTR_TYPE_THRESH_FALLING: | ||
523 | ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, | ||
524 | false, &val); | ||
525 | break; | ||
526 | case LM3533_ATTR_TYPE_THRESH_RAISING: | ||
527 | ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, | ||
528 | true, &val); | ||
529 | break; | ||
530 | default: | ||
531 | ret = -ENXIO; | ||
532 | } | ||
533 | |||
534 | if (ret) | ||
535 | return ret; | ||
536 | |||
537 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | ||
538 | } | ||
539 | |||
540 | static ssize_t store_als_attr(struct device *dev, | ||
541 | struct device_attribute *attr, | ||
542 | const char *buf, size_t len) | ||
543 | { | ||
544 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
545 | struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); | ||
546 | u8 val; | ||
547 | int ret; | ||
548 | |||
549 | if (kstrtou8(buf, 0, &val)) | ||
550 | return -EINVAL; | ||
551 | |||
552 | switch (als_attr->type) { | ||
553 | case LM3533_ATTR_TYPE_TARGET: | ||
554 | ret = lm3533_als_set_target(indio_dev, als_attr->val1, | ||
555 | als_attr->val2, val); | ||
556 | break; | ||
557 | case LM3533_ATTR_TYPE_THRESH_FALLING: | ||
558 | ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, | ||
559 | false, val); | ||
560 | break; | ||
561 | case LM3533_ATTR_TYPE_THRESH_RAISING: | ||
562 | ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, | ||
563 | true, val); | ||
564 | break; | ||
565 | default: | ||
566 | ret = -ENXIO; | ||
567 | } | ||
568 | |||
569 | if (ret) | ||
570 | return ret; | ||
571 | |||
572 | return len; | ||
573 | } | ||
574 | |||
575 | #define ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ | ||
576 | { .dev_attr = __ATTR(_name, _mode, _show, _store), \ | ||
577 | .type = _type, \ | ||
578 | .val1 = _val1, \ | ||
579 | .val2 = _val2 } | ||
580 | |||
581 | #define LM3533_ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ | ||
582 | struct lm3533_als_attribute lm3533_als_attr_##_name = \ | ||
583 | ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) | ||
584 | |||
585 | #define ALS_TARGET_ATTR_RW(_channel, _zone) \ | ||
586 | LM3533_ALS_ATTR(out_current##_channel##_current##_zone##_raw, \ | ||
587 | S_IRUGO | S_IWUSR, \ | ||
588 | show_als_attr, store_als_attr, \ | ||
589 | LM3533_ATTR_TYPE_TARGET, _channel, _zone) | ||
590 | /* | ||
591 | * ALS output current values (ALS mapper targets) | ||
592 | * | ||
593 | * out_current[0-2]_current[0-4]_raw 0-255 | ||
594 | */ | ||
595 | static ALS_TARGET_ATTR_RW(0, 0); | ||
596 | static ALS_TARGET_ATTR_RW(0, 1); | ||
597 | static ALS_TARGET_ATTR_RW(0, 2); | ||
598 | static ALS_TARGET_ATTR_RW(0, 3); | ||
599 | static ALS_TARGET_ATTR_RW(0, 4); | ||
600 | |||
601 | static ALS_TARGET_ATTR_RW(1, 0); | ||
602 | static ALS_TARGET_ATTR_RW(1, 1); | ||
603 | static ALS_TARGET_ATTR_RW(1, 2); | ||
604 | static ALS_TARGET_ATTR_RW(1, 3); | ||
605 | static ALS_TARGET_ATTR_RW(1, 4); | ||
606 | |||
607 | static ALS_TARGET_ATTR_RW(2, 0); | ||
608 | static ALS_TARGET_ATTR_RW(2, 1); | ||
609 | static ALS_TARGET_ATTR_RW(2, 2); | ||
610 | static ALS_TARGET_ATTR_RW(2, 3); | ||
611 | static ALS_TARGET_ATTR_RW(2, 4); | ||
612 | |||
613 | #define ALS_THRESH_FALLING_ATTR_RW(_nr) \ | ||
614 | LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_falling_value, \ | ||
615 | S_IRUGO | S_IWUSR, \ | ||
616 | show_als_attr, store_als_attr, \ | ||
617 | LM3533_ATTR_TYPE_THRESH_FALLING, _nr, 0) | ||
618 | |||
619 | #define ALS_THRESH_RAISING_ATTR_RW(_nr) \ | ||
620 | LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_raising_value, \ | ||
621 | S_IRUGO | S_IWUSR, \ | ||
622 | show_als_attr, store_als_attr, \ | ||
623 | LM3533_ATTR_TYPE_THRESH_RAISING, _nr, 0) | ||
624 | /* | ||
625 | * ALS Zone thresholds (boundaries) | ||
626 | * | ||
627 | * in_illuminance0_thresh[0-3]_falling_value 0-255 | ||
628 | * in_illuminance0_thresh[0-3]_raising_value 0-255 | ||
629 | */ | ||
630 | static ALS_THRESH_FALLING_ATTR_RW(0); | ||
631 | static ALS_THRESH_FALLING_ATTR_RW(1); | ||
632 | static ALS_THRESH_FALLING_ATTR_RW(2); | ||
633 | static ALS_THRESH_FALLING_ATTR_RW(3); | ||
634 | |||
635 | static ALS_THRESH_RAISING_ATTR_RW(0); | ||
636 | static ALS_THRESH_RAISING_ATTR_RW(1); | ||
637 | static ALS_THRESH_RAISING_ATTR_RW(2); | ||
638 | static ALS_THRESH_RAISING_ATTR_RW(3); | ||
639 | |||
640 | #define ALS_HYSTERESIS_ATTR_RO(_nr) \ | ||
641 | LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_hysteresis, \ | ||
642 | S_IRUGO, show_als_attr, NULL, \ | ||
643 | LM3533_ATTR_TYPE_HYSTERESIS, _nr, 0) | ||
644 | /* | ||
645 | * ALS Zone threshold hysteresis | ||
646 | * | ||
647 | * threshY_hysteresis = threshY_raising - threshY_falling | ||
648 | * | ||
649 | * in_illuminance0_thresh[0-3]_hysteresis 0-255 | ||
650 | * in_illuminance0_thresh[0-3]_hysteresis 0-255 | ||
651 | */ | ||
652 | static ALS_HYSTERESIS_ATTR_RO(0); | ||
653 | static ALS_HYSTERESIS_ATTR_RO(1); | ||
654 | static ALS_HYSTERESIS_ATTR_RO(2); | ||
655 | static ALS_HYSTERESIS_ATTR_RO(3); | ||
656 | |||
657 | #define ILLUMINANCE_ATTR_RO(_name) \ | ||
658 | DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL) | ||
659 | #define ILLUMINANCE_ATTR_RW(_name) \ | ||
660 | DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \ | ||
661 | show_##_name, store_##_name) | ||
662 | /* | ||
663 | * ALS Zone threshold-event enable | ||
664 | * | ||
665 | * in_illuminance0_thresh_either_en 0,1 | ||
666 | */ | ||
667 | static ILLUMINANCE_ATTR_RW(thresh_either_en); | ||
668 | |||
669 | /* | ||
670 | * ALS Current Zone | ||
671 | * | ||
672 | * in_illuminance0_zone 0-4 | ||
673 | */ | ||
674 | static ILLUMINANCE_ATTR_RO(zone); | ||
675 | |||
676 | static struct attribute *lm3533_als_event_attributes[] = { | ||
677 | &dev_attr_in_illuminance0_thresh_either_en.attr, | ||
678 | &lm3533_als_attr_in_illuminance0_thresh0_falling_value.dev_attr.attr, | ||
679 | &lm3533_als_attr_in_illuminance0_thresh0_hysteresis.dev_attr.attr, | ||
680 | &lm3533_als_attr_in_illuminance0_thresh0_raising_value.dev_attr.attr, | ||
681 | &lm3533_als_attr_in_illuminance0_thresh1_falling_value.dev_attr.attr, | ||
682 | &lm3533_als_attr_in_illuminance0_thresh1_hysteresis.dev_attr.attr, | ||
683 | &lm3533_als_attr_in_illuminance0_thresh1_raising_value.dev_attr.attr, | ||
684 | &lm3533_als_attr_in_illuminance0_thresh2_falling_value.dev_attr.attr, | ||
685 | &lm3533_als_attr_in_illuminance0_thresh2_hysteresis.dev_attr.attr, | ||
686 | &lm3533_als_attr_in_illuminance0_thresh2_raising_value.dev_attr.attr, | ||
687 | &lm3533_als_attr_in_illuminance0_thresh3_falling_value.dev_attr.attr, | ||
688 | &lm3533_als_attr_in_illuminance0_thresh3_hysteresis.dev_attr.attr, | ||
689 | &lm3533_als_attr_in_illuminance0_thresh3_raising_value.dev_attr.attr, | ||
690 | NULL | ||
691 | }; | ||
692 | |||
693 | static struct attribute_group lm3533_als_event_attribute_group = { | ||
694 | .attrs = lm3533_als_event_attributes | ||
695 | }; | ||
696 | |||
697 | static struct attribute *lm3533_als_attributes[] = { | ||
698 | &dev_attr_in_illuminance0_zone.attr, | ||
699 | &lm3533_als_attr_out_current0_current0_raw.dev_attr.attr, | ||
700 | &lm3533_als_attr_out_current0_current1_raw.dev_attr.attr, | ||
701 | &lm3533_als_attr_out_current0_current2_raw.dev_attr.attr, | ||
702 | &lm3533_als_attr_out_current0_current3_raw.dev_attr.attr, | ||
703 | &lm3533_als_attr_out_current0_current4_raw.dev_attr.attr, | ||
704 | &lm3533_als_attr_out_current1_current0_raw.dev_attr.attr, | ||
705 | &lm3533_als_attr_out_current1_current1_raw.dev_attr.attr, | ||
706 | &lm3533_als_attr_out_current1_current2_raw.dev_attr.attr, | ||
707 | &lm3533_als_attr_out_current1_current3_raw.dev_attr.attr, | ||
708 | &lm3533_als_attr_out_current1_current4_raw.dev_attr.attr, | ||
709 | &lm3533_als_attr_out_current2_current0_raw.dev_attr.attr, | ||
710 | &lm3533_als_attr_out_current2_current1_raw.dev_attr.attr, | ||
711 | &lm3533_als_attr_out_current2_current2_raw.dev_attr.attr, | ||
712 | &lm3533_als_attr_out_current2_current3_raw.dev_attr.attr, | ||
713 | &lm3533_als_attr_out_current2_current4_raw.dev_attr.attr, | ||
714 | NULL | ||
715 | }; | ||
716 | |||
717 | static struct attribute_group lm3533_als_attribute_group = { | ||
718 | .attrs = lm3533_als_attributes | ||
719 | }; | ||
720 | |||
721 | static int __devinit lm3533_als_set_input_mode(struct lm3533_als *als, | ||
722 | bool pwm_mode) | ||
723 | { | ||
724 | u8 mask = LM3533_ALS_INPUT_MODE_MASK; | ||
725 | u8 val; | ||
726 | int ret; | ||
727 | |||
728 | if (pwm_mode) | ||
729 | val = mask; /* pwm input */ | ||
730 | else | ||
731 | val = 0; /* analog input */ | ||
732 | |||
733 | ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask); | ||
734 | if (ret) { | ||
735 | dev_err(&als->pdev->dev, "failed to set input mode %d\n", | ||
736 | pwm_mode); | ||
737 | return ret; | ||
738 | } | ||
739 | |||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | static int __devinit lm3533_als_set_resistor(struct lm3533_als *als, u8 val) | ||
744 | { | ||
745 | int ret; | ||
746 | |||
747 | if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) | ||
748 | return -EINVAL; | ||
749 | |||
750 | ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); | ||
751 | if (ret) { | ||
752 | dev_err(&als->pdev->dev, "failed to set resistor\n"); | ||
753 | return ret; | ||
754 | } | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int __devinit lm3533_als_setup(struct lm3533_als *als, | ||
760 | struct lm3533_als_platform_data *pdata) | ||
761 | { | ||
762 | int ret; | ||
763 | |||
764 | ret = lm3533_als_set_input_mode(als, pdata->pwm_mode); | ||
765 | if (ret) | ||
766 | return ret; | ||
767 | |||
768 | /* ALS input is always high impedance in PWM-mode. */ | ||
769 | if (!pdata->pwm_mode) { | ||
770 | ret = lm3533_als_set_resistor(als, pdata->r_select); | ||
771 | if (ret) | ||
772 | return ret; | ||
773 | } | ||
774 | |||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | static int __devinit lm3533_als_setup_irq(struct lm3533_als *als, void *dev) | ||
779 | { | ||
780 | u8 mask = LM3533_ALS_INT_ENABLE_MASK; | ||
781 | int ret; | ||
782 | |||
783 | /* Make sure interrupts are disabled. */ | ||
784 | ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask); | ||
785 | if (ret) { | ||
786 | dev_err(&als->pdev->dev, "failed to disable interrupts\n"); | ||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr, | ||
791 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
792 | dev_name(&als->pdev->dev), dev); | ||
793 | if (ret) { | ||
794 | dev_err(&als->pdev->dev, "failed to request irq %d\n", | ||
795 | als->irq); | ||
796 | return ret; | ||
797 | } | ||
798 | |||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | static int __devinit lm3533_als_enable(struct lm3533_als *als) | ||
803 | { | ||
804 | u8 mask = LM3533_ALS_ENABLE_MASK; | ||
805 | int ret; | ||
806 | |||
807 | ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask); | ||
808 | if (ret) | ||
809 | dev_err(&als->pdev->dev, "failed to enable ALS\n"); | ||
810 | |||
811 | return ret; | ||
812 | } | ||
813 | |||
814 | static int lm3533_als_disable(struct lm3533_als *als) | ||
815 | { | ||
816 | u8 mask = LM3533_ALS_ENABLE_MASK; | ||
817 | int ret; | ||
818 | |||
819 | ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask); | ||
820 | if (ret) | ||
821 | dev_err(&als->pdev->dev, "failed to disable ALS\n"); | ||
822 | |||
823 | return ret; | ||
824 | } | ||
825 | |||
826 | static const struct iio_info lm3533_als_info = { | ||
827 | .attrs = &lm3533_als_attribute_group, | ||
828 | .event_attrs = &lm3533_als_event_attribute_group, | ||
829 | .driver_module = THIS_MODULE, | ||
830 | .read_raw = &lm3533_als_read_raw, | ||
831 | }; | ||
832 | |||
833 | static int __devinit lm3533_als_probe(struct platform_device *pdev) | ||
834 | { | ||
835 | struct lm3533 *lm3533; | ||
836 | struct lm3533_als_platform_data *pdata; | ||
837 | struct lm3533_als *als; | ||
838 | struct iio_dev *indio_dev; | ||
839 | int ret; | ||
840 | |||
841 | lm3533 = dev_get_drvdata(pdev->dev.parent); | ||
842 | if (!lm3533) | ||
843 | return -EINVAL; | ||
844 | |||
845 | pdata = pdev->dev.platform_data; | ||
846 | if (!pdata) { | ||
847 | dev_err(&pdev->dev, "no platform data\n"); | ||
848 | return -EINVAL; | ||
849 | } | ||
850 | |||
851 | indio_dev = iio_device_alloc(sizeof(*als)); | ||
852 | if (!indio_dev) | ||
853 | return -ENOMEM; | ||
854 | |||
855 | indio_dev->info = &lm3533_als_info; | ||
856 | indio_dev->channels = lm3533_als_channels; | ||
857 | indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels); | ||
858 | indio_dev->name = dev_name(&pdev->dev); | ||
859 | indio_dev->dev.parent = pdev->dev.parent; | ||
860 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
861 | |||
862 | als = iio_priv(indio_dev); | ||
863 | als->lm3533 = lm3533; | ||
864 | als->pdev = pdev; | ||
865 | als->irq = lm3533->irq; | ||
866 | atomic_set(&als->zone, 0); | ||
867 | mutex_init(&als->thresh_mutex); | ||
868 | |||
869 | platform_set_drvdata(pdev, indio_dev); | ||
870 | |||
871 | if (als->irq) { | ||
872 | ret = lm3533_als_setup_irq(als, indio_dev); | ||
873 | if (ret) | ||
874 | goto err_free_dev; | ||
875 | } | ||
876 | |||
877 | ret = lm3533_als_setup(als, pdata); | ||
878 | if (ret) | ||
879 | goto err_free_irq; | ||
880 | |||
881 | ret = lm3533_als_enable(als); | ||
882 | if (ret) | ||
883 | goto err_free_irq; | ||
884 | |||
885 | ret = iio_device_register(indio_dev); | ||
886 | if (ret) { | ||
887 | dev_err(&pdev->dev, "failed to register ALS\n"); | ||
888 | goto err_disable; | ||
889 | } | ||
890 | |||
891 | return 0; | ||
892 | |||
893 | err_disable: | ||
894 | lm3533_als_disable(als); | ||
895 | err_free_irq: | ||
896 | if (als->irq) | ||
897 | free_irq(als->irq, indio_dev); | ||
898 | err_free_dev: | ||
899 | iio_device_free(indio_dev); | ||
900 | |||
901 | return ret; | ||
902 | } | ||
903 | |||
904 | static int __devexit lm3533_als_remove(struct platform_device *pdev) | ||
905 | { | ||
906 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
907 | struct lm3533_als *als = iio_priv(indio_dev); | ||
908 | |||
909 | lm3533_als_set_int_mode(indio_dev, false); | ||
910 | iio_device_unregister(indio_dev); | ||
911 | lm3533_als_disable(als); | ||
912 | if (als->irq) | ||
913 | free_irq(als->irq, indio_dev); | ||
914 | iio_device_free(indio_dev); | ||
915 | |||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | static struct platform_driver lm3533_als_driver = { | ||
920 | .driver = { | ||
921 | .name = "lm3533-als", | ||
922 | .owner = THIS_MODULE, | ||
923 | }, | ||
924 | .probe = lm3533_als_probe, | ||
925 | .remove = __devexit_p(lm3533_als_remove), | ||
926 | }; | ||
927 | module_platform_driver(lm3533_als_driver); | ||
928 | |||
929 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); | ||
930 | MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver"); | ||
931 | MODULE_LICENSE("GPL"); | ||
932 | MODULE_ALIAS("platform:lm3533-als"); | ||