Commit | Line | Data |
---|---|---|
03eff7b6 OK |
1 | /* |
2 | * apds9300.c - IIO driver for Avago APDS9300 ambient light sensor | |
3 | * | |
4 | * Copyright 2013 Oleksandr Kravchenko <o.v.kravchenko@globallogic.com> | |
5 | * | |
6 | * This file is subject to the terms and conditions of version 2 of | |
7 | * the GNU General Public License. See the file COPYING in the main | |
8 | * directory of this archive for more details. | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/pm.h> | |
14 | #include <linux/i2c.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/mutex.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/iio/iio.h> | |
19 | #include <linux/iio/sysfs.h> | |
20 | #include <linux/iio/events.h> | |
21 | ||
22 | #define APDS9300_DRV_NAME "apds9300" | |
23 | #define APDS9300_IRQ_NAME "apds9300_event" | |
24 | ||
25 | /* Command register bits */ | |
26 | #define APDS9300_CMD BIT(7) /* Select command register. Must write as 1 */ | |
27 | #define APDS9300_WORD BIT(5) /* I2C write/read: if 1 word, if 0 byte */ | |
28 | #define APDS9300_CLEAR BIT(6) /* Interrupt clear. Clears pending interrupt */ | |
29 | ||
30 | /* Register set */ | |
31 | #define APDS9300_CONTROL 0x00 /* Control of basic functions */ | |
32 | #define APDS9300_THRESHLOWLOW 0x02 /* Low byte of low interrupt threshold */ | |
33 | #define APDS9300_THRESHHIGHLOW 0x04 /* Low byte of high interrupt threshold */ | |
34 | #define APDS9300_INTERRUPT 0x06 /* Interrupt control */ | |
35 | #define APDS9300_DATA0LOW 0x0c /* Low byte of ADC channel 0 */ | |
36 | #define APDS9300_DATA1LOW 0x0e /* Low byte of ADC channel 1 */ | |
37 | ||
38 | /* Power on/off value for APDS9300_CONTROL register */ | |
39 | #define APDS9300_POWER_ON 0x03 | |
40 | #define APDS9300_POWER_OFF 0x00 | |
41 | ||
42 | /* Interrupts */ | |
43 | #define APDS9300_INTR_ENABLE 0x10 | |
44 | /* Interrupt Persist Function: Any value outside of threshold range */ | |
45 | #define APDS9300_THRESH_INTR 0x01 | |
46 | ||
47 | #define APDS9300_THRESH_MAX 0xffff /* Max threshold value */ | |
48 | ||
49 | struct apds9300_data { | |
50 | struct i2c_client *client; | |
51 | struct mutex mutex; | |
52 | int power_state; | |
53 | int thresh_low; | |
54 | int thresh_hi; | |
55 | int intr_en; | |
56 | }; | |
57 | ||
58 | /* Lux calculation */ | |
59 | ||
60 | /* Calculated values 1000 * (CH1/CH0)^1.4 for CH1/CH0 from 0 to 0.52 */ | |
61 | static const u16 apds9300_lux_ratio[] = { | |
62 | 0, 2, 4, 7, 11, 15, 19, 24, 29, 34, 40, 45, 51, 57, 64, 70, 77, 84, 91, | |
63 | 98, 105, 112, 120, 128, 136, 144, 152, 160, 168, 177, 185, 194, 203, | |
64 | 212, 221, 230, 239, 249, 258, 268, 277, 287, 297, 307, 317, 327, 337, | |
65 | 347, 358, 368, 379, 390, 400, | |
66 | }; | |
67 | ||
68 | static unsigned long apds9300_calculate_lux(u16 ch0, u16 ch1) | |
69 | { | |
70 | unsigned long lux, tmp; | |
71 | ||
72 | /* avoid division by zero */ | |
73 | if (ch0 == 0) | |
74 | return 0; | |
75 | ||
76 | tmp = DIV_ROUND_UP(ch1 * 100, ch0); | |
77 | if (tmp <= 52) { | |
78 | lux = 3150 * ch0 - (unsigned long)DIV_ROUND_UP_ULL(ch0 | |
79 | * apds9300_lux_ratio[tmp] * 5930ull, 1000); | |
80 | } else if (tmp <= 65) { | |
81 | lux = 2290 * ch0 - 2910 * ch1; | |
82 | } else if (tmp <= 80) { | |
83 | lux = 1570 * ch0 - 1800 * ch1; | |
84 | } else if (tmp <= 130) { | |
85 | lux = 338 * ch0 - 260 * ch1; | |
86 | } else { | |
87 | lux = 0; | |
88 | } | |
89 | ||
90 | return lux / 100000; | |
91 | } | |
92 | ||
93 | static int apds9300_get_adc_val(struct apds9300_data *data, int adc_number) | |
94 | { | |
95 | int ret; | |
96 | u8 flags = APDS9300_CMD | APDS9300_WORD; | |
97 | ||
98 | if (!data->power_state) | |
99 | return -EBUSY; | |
100 | ||
101 | /* Select ADC0 or ADC1 data register */ | |
102 | flags |= adc_number ? APDS9300_DATA1LOW : APDS9300_DATA0LOW; | |
103 | ||
104 | ret = i2c_smbus_read_word_data(data->client, flags); | |
105 | if (ret < 0) | |
106 | dev_err(&data->client->dev, | |
107 | "failed to read ADC%d value\n", adc_number); | |
108 | ||
109 | return ret; | |
110 | } | |
111 | ||
112 | static int apds9300_set_thresh_low(struct apds9300_data *data, int value) | |
113 | { | |
114 | int ret; | |
115 | ||
116 | if (!data->power_state) | |
117 | return -EBUSY; | |
118 | ||
119 | if (value > APDS9300_THRESH_MAX) | |
120 | return -EINVAL; | |
121 | ||
122 | ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHLOWLOW | |
123 | | APDS9300_CMD | APDS9300_WORD, value); | |
124 | if (ret) { | |
125 | dev_err(&data->client->dev, "failed to set thresh_low\n"); | |
126 | return ret; | |
127 | } | |
128 | data->thresh_low = value; | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static int apds9300_set_thresh_hi(struct apds9300_data *data, int value) | |
134 | { | |
135 | int ret; | |
136 | ||
137 | if (!data->power_state) | |
138 | return -EBUSY; | |
139 | ||
140 | if (value > APDS9300_THRESH_MAX) | |
141 | return -EINVAL; | |
142 | ||
143 | ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHHIGHLOW | |
144 | | APDS9300_CMD | APDS9300_WORD, value); | |
145 | if (ret) { | |
146 | dev_err(&data->client->dev, "failed to set thresh_hi\n"); | |
147 | return ret; | |
148 | } | |
149 | data->thresh_hi = value; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int apds9300_set_intr_state(struct apds9300_data *data, int state) | |
155 | { | |
156 | int ret; | |
157 | u8 cmd; | |
158 | ||
159 | if (!data->power_state) | |
160 | return -EBUSY; | |
161 | ||
162 | cmd = state ? APDS9300_INTR_ENABLE | APDS9300_THRESH_INTR : 0x00; | |
163 | ret = i2c_smbus_write_byte_data(data->client, | |
164 | APDS9300_INTERRUPT | APDS9300_CMD, cmd); | |
165 | if (ret) { | |
166 | dev_err(&data->client->dev, | |
167 | "failed to set interrupt state %d\n", state); | |
168 | return ret; | |
169 | } | |
170 | data->intr_en = state; | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | static int apds9300_set_power_state(struct apds9300_data *data, int state) | |
176 | { | |
177 | int ret; | |
178 | u8 cmd; | |
179 | ||
180 | cmd = state ? APDS9300_POWER_ON : APDS9300_POWER_OFF; | |
181 | ret = i2c_smbus_write_byte_data(data->client, | |
182 | APDS9300_CONTROL | APDS9300_CMD, cmd); | |
183 | if (ret) { | |
184 | dev_err(&data->client->dev, | |
185 | "failed to set power state %d\n", state); | |
186 | return ret; | |
187 | } | |
188 | data->power_state = state; | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void apds9300_clear_intr(struct apds9300_data *data) | |
194 | { | |
195 | int ret; | |
196 | ||
197 | ret = i2c_smbus_write_byte(data->client, APDS9300_CLEAR | APDS9300_CMD); | |
198 | if (ret < 0) | |
199 | dev_err(&data->client->dev, "failed to clear interrupt\n"); | |
200 | } | |
201 | ||
202 | static int apds9300_chip_init(struct apds9300_data *data) | |
203 | { | |
204 | int ret; | |
205 | ||
206 | /* Need to set power off to ensure that the chip is off */ | |
207 | ret = apds9300_set_power_state(data, 0); | |
208 | if (ret < 0) | |
209 | goto err; | |
210 | /* | |
211 | * Probe the chip. To do so we try to power up the device and then to | |
212 | * read back the 0x03 code | |
213 | */ | |
214 | ret = apds9300_set_power_state(data, 1); | |
215 | if (ret < 0) | |
216 | goto err; | |
217 | ret = i2c_smbus_read_byte_data(data->client, | |
218 | APDS9300_CONTROL | APDS9300_CMD); | |
219 | if (ret != APDS9300_POWER_ON) { | |
220 | ret = -ENODEV; | |
221 | goto err; | |
222 | } | |
223 | /* | |
224 | * Disable interrupt to ensure thai it is doesn't enable | |
225 | * i.e. after device soft reset | |
226 | */ | |
227 | ret = apds9300_set_intr_state(data, 0); | |
228 | if (ret < 0) | |
229 | goto err; | |
230 | ||
231 | return 0; | |
232 | ||
233 | err: | |
234 | dev_err(&data->client->dev, "failed to init the chip\n"); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | static int apds9300_read_raw(struct iio_dev *indio_dev, | |
239 | struct iio_chan_spec const *chan, int *val, int *val2, | |
240 | long mask) | |
241 | { | |
242 | int ch0, ch1, ret = -EINVAL; | |
243 | struct apds9300_data *data = iio_priv(indio_dev); | |
244 | ||
245 | mutex_lock(&data->mutex); | |
246 | switch (chan->type) { | |
247 | case IIO_LIGHT: | |
248 | ch0 = apds9300_get_adc_val(data, 0); | |
249 | if (ch0 < 0) { | |
250 | ret = ch0; | |
251 | break; | |
252 | } | |
253 | ch1 = apds9300_get_adc_val(data, 1); | |
254 | if (ch1 < 0) { | |
255 | ret = ch1; | |
256 | break; | |
257 | } | |
258 | *val = apds9300_calculate_lux(ch0, ch1); | |
259 | ret = IIO_VAL_INT; | |
260 | break; | |
261 | case IIO_INTENSITY: | |
262 | ret = apds9300_get_adc_val(data, chan->channel); | |
263 | if (ret < 0) | |
264 | break; | |
265 | *val = ret; | |
266 | ret = IIO_VAL_INT; | |
267 | break; | |
268 | default: | |
269 | break; | |
270 | } | |
271 | mutex_unlock(&data->mutex); | |
272 | ||
273 | return ret; | |
274 | } | |
275 | ||
7e97e7bd LPC |
276 | static int apds9300_read_thresh(struct iio_dev *indio_dev, |
277 | const struct iio_chan_spec *chan, enum iio_event_type type, | |
278 | enum iio_event_direction dir, enum iio_event_info info, | |
279 | int *val, int *val2) | |
03eff7b6 OK |
280 | { |
281 | struct apds9300_data *data = iio_priv(indio_dev); | |
282 | ||
7e97e7bd | 283 | switch (dir) { |
03eff7b6 OK |
284 | case IIO_EV_DIR_RISING: |
285 | *val = data->thresh_hi; | |
286 | break; | |
287 | case IIO_EV_DIR_FALLING: | |
288 | *val = data->thresh_low; | |
289 | break; | |
290 | default: | |
291 | return -EINVAL; | |
292 | } | |
293 | ||
7e97e7bd | 294 | return IIO_VAL_INT; |
03eff7b6 OK |
295 | } |
296 | ||
7e97e7bd LPC |
297 | static int apds9300_write_thresh(struct iio_dev *indio_dev, |
298 | const struct iio_chan_spec *chan, enum iio_event_type type, | |
299 | enum iio_event_direction dir, enum iio_event_info info, int val, | |
300 | int val2) | |
03eff7b6 OK |
301 | { |
302 | struct apds9300_data *data = iio_priv(indio_dev); | |
303 | int ret; | |
304 | ||
305 | mutex_lock(&data->mutex); | |
7e97e7bd | 306 | if (dir == IIO_EV_DIR_RISING) |
03eff7b6 OK |
307 | ret = apds9300_set_thresh_hi(data, val); |
308 | else | |
309 | ret = apds9300_set_thresh_low(data, val); | |
310 | mutex_unlock(&data->mutex); | |
311 | ||
312 | return ret; | |
313 | } | |
314 | ||
315 | static int apds9300_read_interrupt_config(struct iio_dev *indio_dev, | |
7e97e7bd LPC |
316 | const struct iio_chan_spec *chan, |
317 | enum iio_event_type type, | |
318 | enum iio_event_direction dir) | |
03eff7b6 OK |
319 | { |
320 | struct apds9300_data *data = iio_priv(indio_dev); | |
321 | ||
322 | return data->intr_en; | |
323 | } | |
324 | ||
325 | static int apds9300_write_interrupt_config(struct iio_dev *indio_dev, | |
7e97e7bd LPC |
326 | const struct iio_chan_spec *chan, enum iio_event_type type, |
327 | enum iio_event_direction dir, int state) | |
03eff7b6 OK |
328 | { |
329 | struct apds9300_data *data = iio_priv(indio_dev); | |
330 | int ret; | |
331 | ||
332 | mutex_lock(&data->mutex); | |
333 | ret = apds9300_set_intr_state(data, state); | |
334 | mutex_unlock(&data->mutex); | |
335 | ||
336 | return ret; | |
337 | } | |
338 | ||
339 | static const struct iio_info apds9300_info_no_irq = { | |
340 | .driver_module = THIS_MODULE, | |
341 | .read_raw = apds9300_read_raw, | |
342 | }; | |
343 | ||
344 | static const struct iio_info apds9300_info = { | |
345 | .driver_module = THIS_MODULE, | |
346 | .read_raw = apds9300_read_raw, | |
cb955852 LPC |
347 | .read_event_value = apds9300_read_thresh, |
348 | .write_event_value = apds9300_write_thresh, | |
349 | .read_event_config = apds9300_read_interrupt_config, | |
350 | .write_event_config = apds9300_write_interrupt_config, | |
7e97e7bd LPC |
351 | }; |
352 | ||
353 | static const struct iio_event_spec apds9300_event_spec[] = { | |
354 | { | |
355 | .type = IIO_EV_TYPE_THRESH, | |
356 | .dir = IIO_EV_DIR_RISING, | |
357 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
358 | BIT(IIO_EV_INFO_ENABLE), | |
359 | }, { | |
360 | .type = IIO_EV_TYPE_THRESH, | |
361 | .dir = IIO_EV_DIR_FALLING, | |
362 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
363 | BIT(IIO_EV_INFO_ENABLE), | |
364 | }, | |
03eff7b6 OK |
365 | }; |
366 | ||
367 | static const struct iio_chan_spec apds9300_channels[] = { | |
368 | { | |
369 | .type = IIO_LIGHT, | |
370 | .channel = 0, | |
371 | .indexed = true, | |
372 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
373 | }, { | |
374 | .type = IIO_INTENSITY, | |
375 | .channel = 0, | |
376 | .channel2 = IIO_MOD_LIGHT_BOTH, | |
377 | .indexed = true, | |
378 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
7e97e7bd LPC |
379 | .event_spec = apds9300_event_spec, |
380 | .num_event_specs = ARRAY_SIZE(apds9300_event_spec), | |
03eff7b6 OK |
381 | }, { |
382 | .type = IIO_INTENSITY, | |
383 | .channel = 1, | |
384 | .channel2 = IIO_MOD_LIGHT_IR, | |
385 | .indexed = true, | |
386 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
387 | }, | |
388 | }; | |
389 | ||
390 | static irqreturn_t apds9300_interrupt_handler(int irq, void *private) | |
391 | { | |
392 | struct iio_dev *dev_info = private; | |
393 | struct apds9300_data *data = iio_priv(dev_info); | |
394 | ||
395 | iio_push_event(dev_info, | |
396 | IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, | |
397 | IIO_EV_TYPE_THRESH, | |
398 | IIO_EV_DIR_EITHER), | |
399 | iio_get_time_ns()); | |
400 | ||
401 | apds9300_clear_intr(data); | |
402 | ||
403 | return IRQ_HANDLED; | |
404 | } | |
405 | ||
406 | static int apds9300_probe(struct i2c_client *client, | |
407 | const struct i2c_device_id *id) | |
408 | { | |
409 | struct apds9300_data *data; | |
410 | struct iio_dev *indio_dev; | |
411 | int ret; | |
412 | ||
413 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
414 | if (!indio_dev) | |
415 | return -ENOMEM; | |
416 | ||
417 | data = iio_priv(indio_dev); | |
418 | i2c_set_clientdata(client, indio_dev); | |
419 | data->client = client; | |
420 | ||
421 | ret = apds9300_chip_init(data); | |
422 | if (ret < 0) | |
423 | goto err; | |
424 | ||
425 | mutex_init(&data->mutex); | |
426 | ||
427 | indio_dev->dev.parent = &client->dev; | |
428 | indio_dev->channels = apds9300_channels; | |
429 | indio_dev->num_channels = ARRAY_SIZE(apds9300_channels); | |
430 | indio_dev->name = APDS9300_DRV_NAME; | |
431 | indio_dev->modes = INDIO_DIRECT_MODE; | |
432 | ||
433 | if (client->irq) | |
434 | indio_dev->info = &apds9300_info; | |
435 | else | |
436 | indio_dev->info = &apds9300_info_no_irq; | |
437 | ||
438 | if (client->irq) { | |
439 | ret = devm_request_threaded_irq(&client->dev, client->irq, | |
440 | NULL, apds9300_interrupt_handler, | |
441 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
442 | APDS9300_IRQ_NAME, indio_dev); | |
443 | if (ret) { | |
444 | dev_err(&client->dev, "irq request error %d\n", -ret); | |
445 | goto err; | |
446 | } | |
447 | } | |
448 | ||
449 | ret = iio_device_register(indio_dev); | |
450 | if (ret < 0) | |
451 | goto err; | |
452 | ||
453 | return 0; | |
454 | ||
455 | err: | |
456 | /* Ensure that power off in case of error */ | |
457 | apds9300_set_power_state(data, 0); | |
458 | return ret; | |
459 | } | |
460 | ||
461 | static int apds9300_remove(struct i2c_client *client) | |
462 | { | |
463 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
464 | struct apds9300_data *data = iio_priv(indio_dev); | |
465 | ||
466 | iio_device_unregister(indio_dev); | |
467 | ||
468 | /* Ensure that power off and interrupts are disabled */ | |
469 | apds9300_set_intr_state(data, 0); | |
470 | apds9300_set_power_state(data, 0); | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
475 | #ifdef CONFIG_PM_SLEEP | |
476 | static int apds9300_suspend(struct device *dev) | |
477 | { | |
478 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | |
479 | struct apds9300_data *data = iio_priv(indio_dev); | |
480 | int ret; | |
481 | ||
482 | mutex_lock(&data->mutex); | |
483 | ret = apds9300_set_power_state(data, 0); | |
484 | mutex_unlock(&data->mutex); | |
485 | ||
486 | return ret; | |
487 | } | |
488 | ||
489 | static int apds9300_resume(struct device *dev) | |
490 | { | |
491 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | |
492 | struct apds9300_data *data = iio_priv(indio_dev); | |
493 | int ret; | |
494 | ||
495 | mutex_lock(&data->mutex); | |
496 | ret = apds9300_set_power_state(data, 1); | |
497 | mutex_unlock(&data->mutex); | |
498 | ||
499 | return ret; | |
500 | } | |
501 | ||
502 | static SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend, apds9300_resume); | |
503 | #define APDS9300_PM_OPS (&apds9300_pm_ops) | |
504 | #else | |
505 | #define APDS9300_PM_OPS NULL | |
506 | #endif | |
507 | ||
508 | static struct i2c_device_id apds9300_id[] = { | |
509 | { APDS9300_DRV_NAME, 0 }, | |
510 | { } | |
511 | }; | |
512 | ||
513 | MODULE_DEVICE_TABLE(i2c, apds9300_id); | |
514 | ||
515 | static struct i2c_driver apds9300_driver = { | |
516 | .driver = { | |
517 | .name = APDS9300_DRV_NAME, | |
03eff7b6 OK |
518 | .pm = APDS9300_PM_OPS, |
519 | }, | |
520 | .probe = apds9300_probe, | |
521 | .remove = apds9300_remove, | |
522 | .id_table = apds9300_id, | |
523 | }; | |
524 | ||
525 | module_i2c_driver(apds9300_driver); | |
526 | ||
527 | MODULE_AUTHOR("Kravchenko Oleksandr <o.v.kravchenko@globallogic.com>"); | |
528 | MODULE_AUTHOR("GlobalLogic inc."); | |
529 | MODULE_DESCRIPTION("APDS9300 ambient light photo sensor driver"); | |
530 | MODULE_LICENSE("GPL"); |