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