aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-light-lm3533-als62
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/light/Kconfig22
-rw-r--r--drivers/iio/light/Makefile5
-rw-r--r--drivers/iio/light/lm3533-als.c932
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 @@
1What: /sys/.../events/in_illuminance0_thresh_either_en
2Date: April 2012
3KernelVersion: 3.5
4Contact: Johan Hovold <jhovold@gmail.com>
5Description:
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
11What: /sys/.../events/in_illuminance0_threshY_hysteresis
12Date: May 2012
13KernelVersion: 3.5
14Contact: Johan Hovold <jhovold@gmail.com>
15Description:
16 Get the hysteresis for thresholds Y, that is,
17
18 threshY_hysteresis = threshY_raising - threshY_falling
19
20What: /sys/.../events/illuminance_threshY_falling_value
21What: /sys/.../events/illuminance_threshY_raising_value
22Date: April 2012
23KernelVersion: 3.5
24Contact: Johan Hovold <jhovold@gmail.com>
25Description:
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
37What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_zone
38Date: April 2012
39KernelVersion: 3.5
40Contact: Johan Hovold <jhovold@gmail.com>
41Description:
42 Get the current light zone (0..4) as defined by the
43 in_illuminance0_threshY_{falling,rising} thresholds.
44
45What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw
46Date: May 2012
47KernelVersion: 3.5
48Contact: Johan Hovold <jhovold@gmail.com>
49Description:
50 Get output current for channel Y (0..255), that is,
51 out_currentY_currentZ_raw, where Z is the current zone.
52
53What: /sys/bus/iio/devices/iio:deviceX/out_currentY_currentZ_raw
54Date: May 2012
55KernelVersion: 3.5
56Contact: Johan Hovold <jhovold@gmail.com>
57Description:
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
51source "drivers/iio/adc/Kconfig" 51source "drivers/iio/adc/Kconfig"
52source "drivers/iio/amplifiers/Kconfig" 52source "drivers/iio/amplifiers/Kconfig"
53source "drivers/iio/light/Kconfig"
53 54
54endif # IIO 55endif # 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
12obj-y += adc/ 12obj-y += adc/
13obj-y += amplifiers/ 13obj-y += amplifiers/
14obj-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#
4menu "Light sensors"
5
6config 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.
22endmenu
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
5obj-$(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
54struct 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
66static 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
90static 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
108static 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 */
128static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone)
129{
130 return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone;
131}
132
133static 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
154static 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
175static 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
195static 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
237static 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
250static 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());
271out:
272 return IRQ_HANDLED;
273}
274
275static 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
297static 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
315static 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
322static 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
340static 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 }
375out:
376 mutex_unlock(&als->thresh_mutex);
377
378 return ret;
379}
380
381static 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;
401out:
402 mutex_unlock(&als->thresh_mutex);
403
404 return ret;
405}
406
407static 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
427static 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
470static 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
484enum 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
491struct lm3533_als_attribute {
492 struct device_attribute dev_attr;
493 enum lm3533_als_attribute_type type;
494 u8 val1;
495 u8 val2;
496};
497
498static inline struct lm3533_als_attribute *
499to_lm3533_als_attr(struct device_attribute *attr)
500{
501 return container_of(attr, struct lm3533_als_attribute, dev_attr);
502}
503
504static 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
540static 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 */
595static ALS_TARGET_ATTR_RW(0, 0);
596static ALS_TARGET_ATTR_RW(0, 1);
597static ALS_TARGET_ATTR_RW(0, 2);
598static ALS_TARGET_ATTR_RW(0, 3);
599static ALS_TARGET_ATTR_RW(0, 4);
600
601static ALS_TARGET_ATTR_RW(1, 0);
602static ALS_TARGET_ATTR_RW(1, 1);
603static ALS_TARGET_ATTR_RW(1, 2);
604static ALS_TARGET_ATTR_RW(1, 3);
605static ALS_TARGET_ATTR_RW(1, 4);
606
607static ALS_TARGET_ATTR_RW(2, 0);
608static ALS_TARGET_ATTR_RW(2, 1);
609static ALS_TARGET_ATTR_RW(2, 2);
610static ALS_TARGET_ATTR_RW(2, 3);
611static 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 */
630static ALS_THRESH_FALLING_ATTR_RW(0);
631static ALS_THRESH_FALLING_ATTR_RW(1);
632static ALS_THRESH_FALLING_ATTR_RW(2);
633static ALS_THRESH_FALLING_ATTR_RW(3);
634
635static ALS_THRESH_RAISING_ATTR_RW(0);
636static ALS_THRESH_RAISING_ATTR_RW(1);
637static ALS_THRESH_RAISING_ATTR_RW(2);
638static 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 */
652static ALS_HYSTERESIS_ATTR_RO(0);
653static ALS_HYSTERESIS_ATTR_RO(1);
654static ALS_HYSTERESIS_ATTR_RO(2);
655static 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 */
667static ILLUMINANCE_ATTR_RW(thresh_either_en);
668
669/*
670 * ALS Current Zone
671 *
672 * in_illuminance0_zone 0-4
673 */
674static ILLUMINANCE_ATTR_RO(zone);
675
676static 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
693static struct attribute_group lm3533_als_event_attribute_group = {
694 .attrs = lm3533_als_event_attributes
695};
696
697static 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
717static struct attribute_group lm3533_als_attribute_group = {
718 .attrs = lm3533_als_attributes
719};
720
721static 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
743static 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
759static 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
778static 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
802static 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
814static 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
826static 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
833static 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
893err_disable:
894 lm3533_als_disable(als);
895err_free_irq:
896 if (als->irq)
897 free_irq(als->irq, indio_dev);
898err_free_dev:
899 iio_device_free(indio_dev);
900
901 return ret;
902}
903
904static 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
919static 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};
927module_platform_driver(lm3533_als_driver);
928
929MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
930MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver");
931MODULE_LICENSE("GPL");
932MODULE_ALIAS("platform:lm3533-als");