1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * VEML6030 Ambient Light Sensor |
4 | * |
5 | * Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com> |
6 | * |
7 | * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf |
8 | * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/err.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/iio/iio.h> |
18 | #include <linux/iio/sysfs.h> |
19 | #include <linux/iio/events.h> |
20 | |
21 | /* Device registers */ |
22 | #define VEML6030_REG_ALS_CONF 0x00 |
23 | #define VEML6030_REG_ALS_WH 0x01 |
24 | #define VEML6030_REG_ALS_WL 0x02 |
25 | #define VEML6030_REG_ALS_PSM 0x03 |
26 | #define VEML6030_REG_ALS_DATA 0x04 |
27 | #define VEML6030_REG_WH_DATA 0x05 |
28 | #define VEML6030_REG_ALS_INT 0x06 |
29 | |
30 | /* Bit masks for specific functionality */ |
31 | #define VEML6030_ALS_IT GENMASK(9, 6) |
32 | #define VEML6030_PSM GENMASK(2, 1) |
33 | #define VEML6030_ALS_PERS GENMASK(5, 4) |
34 | #define VEML6030_ALS_GAIN GENMASK(12, 11) |
35 | #define VEML6030_PSM_EN BIT(0) |
36 | #define VEML6030_INT_TH_LOW BIT(15) |
37 | #define VEML6030_INT_TH_HIGH BIT(14) |
38 | #define VEML6030_ALS_INT_EN BIT(1) |
39 | #define VEML6030_ALS_SD BIT(0) |
40 | |
41 | /* |
42 | * The resolution depends on both gain and integration time. The |
43 | * cur_resolution stores one of the resolution mentioned in the |
44 | * table during startup and gets updated whenever integration time |
45 | * or gain is changed. |
46 | * |
47 | * Table 'resolution and maximum detection range' in appnote 84367 |
48 | * is visualized as a 2D array. The cur_gain stores index of gain |
49 | * in this table (0-3) while the cur_integration_time holds index |
50 | * of integration time (0-5). |
51 | */ |
52 | struct veml6030_data { |
53 | struct i2c_client *client; |
54 | struct regmap *regmap; |
55 | int cur_resolution; |
56 | int cur_gain; |
57 | int cur_integration_time; |
58 | }; |
59 | |
60 | /* Integration time available in seconds */ |
61 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, |
62 | "0.025 0.05 0.1 0.2 0.4 0.8" ); |
63 | |
64 | /* |
65 | * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is |
66 | * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2. |
67 | */ |
68 | static IIO_CONST_ATTR(in_illuminance_scale_available, |
69 | "0.125 0.25 1.0 2.0" ); |
70 | |
71 | static struct attribute *veml6030_attributes[] = { |
72 | &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, |
73 | &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, |
74 | NULL |
75 | }; |
76 | |
77 | static const struct attribute_group veml6030_attr_group = { |
78 | .attrs = veml6030_attributes, |
79 | }; |
80 | |
81 | /* |
82 | * Persistence = 1/2/4/8 x integration time |
83 | * Minimum time for which light readings must stay above configured |
84 | * threshold to assert the interrupt. |
85 | */ |
86 | static const char * const period_values[] = { |
87 | "0.1 0.2 0.4 0.8" , |
88 | "0.2 0.4 0.8 1.6" , |
89 | "0.4 0.8 1.6 3.2" , |
90 | "0.8 1.6 3.2 6.4" , |
91 | "0.05 0.1 0.2 0.4" , |
92 | "0.025 0.050 0.1 0.2" |
93 | }; |
94 | |
95 | /* |
96 | * Return list of valid period values in seconds corresponding to |
97 | * the currently active integration time. |
98 | */ |
99 | static ssize_t in_illuminance_period_available_show(struct device *dev, |
100 | struct device_attribute *attr, char *buf) |
101 | { |
102 | int ret, reg, x; |
103 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
104 | struct veml6030_data *data = iio_priv(indio_dev); |
105 | |
106 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_CONF, val: ®); |
107 | if (ret) { |
108 | dev_err(&data->client->dev, |
109 | "can't read als conf register %d\n" , ret); |
110 | return ret; |
111 | } |
112 | |
113 | ret = ((reg >> 6) & 0xF); |
114 | switch (ret) { |
115 | case 0: |
116 | case 1: |
117 | case 2: |
118 | case 3: |
119 | x = ret; |
120 | break; |
121 | case 8: |
122 | x = 4; |
123 | break; |
124 | case 12: |
125 | x = 5; |
126 | break; |
127 | default: |
128 | return -EINVAL; |
129 | } |
130 | |
131 | return sysfs_emit(buf, fmt: "%s\n" , period_values[x]); |
132 | } |
133 | |
134 | static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0); |
135 | |
136 | static struct attribute *veml6030_event_attributes[] = { |
137 | &iio_dev_attr_in_illuminance_period_available.dev_attr.attr, |
138 | NULL |
139 | }; |
140 | |
141 | static const struct attribute_group veml6030_event_attr_group = { |
142 | .attrs = veml6030_event_attributes, |
143 | }; |
144 | |
145 | static int veml6030_als_pwr_on(struct veml6030_data *data) |
146 | { |
147 | return regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
148 | VEML6030_ALS_SD, val: 0); |
149 | } |
150 | |
151 | static int veml6030_als_shut_down(struct veml6030_data *data) |
152 | { |
153 | return regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
154 | VEML6030_ALS_SD, val: 1); |
155 | } |
156 | |
157 | static void veml6030_als_shut_down_action(void *data) |
158 | { |
159 | veml6030_als_shut_down(data); |
160 | } |
161 | |
162 | static const struct iio_event_spec veml6030_event_spec[] = { |
163 | { |
164 | .type = IIO_EV_TYPE_THRESH, |
165 | .dir = IIO_EV_DIR_RISING, |
166 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
167 | }, { |
168 | .type = IIO_EV_TYPE_THRESH, |
169 | .dir = IIO_EV_DIR_FALLING, |
170 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
171 | }, { |
172 | .type = IIO_EV_TYPE_THRESH, |
173 | .dir = IIO_EV_DIR_EITHER, |
174 | .mask_separate = BIT(IIO_EV_INFO_PERIOD) | |
175 | BIT(IIO_EV_INFO_ENABLE), |
176 | }, |
177 | }; |
178 | |
179 | /* Channel number */ |
180 | enum veml6030_chan { |
181 | CH_ALS, |
182 | CH_WHITE, |
183 | }; |
184 | |
185 | static const struct iio_chan_spec veml6030_channels[] = { |
186 | { |
187 | .type = IIO_LIGHT, |
188 | .channel = CH_ALS, |
189 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
190 | BIT(IIO_CHAN_INFO_PROCESSED) | |
191 | BIT(IIO_CHAN_INFO_INT_TIME) | |
192 | BIT(IIO_CHAN_INFO_SCALE), |
193 | .event_spec = veml6030_event_spec, |
194 | .num_event_specs = ARRAY_SIZE(veml6030_event_spec), |
195 | }, |
196 | { |
197 | .type = IIO_INTENSITY, |
198 | .channel = CH_WHITE, |
199 | .modified = 1, |
200 | .channel2 = IIO_MOD_LIGHT_BOTH, |
201 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
202 | BIT(IIO_CHAN_INFO_PROCESSED), |
203 | }, |
204 | }; |
205 | |
206 | static const struct regmap_config veml6030_regmap_config = { |
207 | .name = "veml6030_regmap" , |
208 | .reg_bits = 8, |
209 | .val_bits = 16, |
210 | .max_register = VEML6030_REG_ALS_INT, |
211 | .val_format_endian = REGMAP_ENDIAN_LITTLE, |
212 | }; |
213 | |
214 | static int veml6030_get_intgrn_tm(struct iio_dev *indio_dev, |
215 | int *val, int *val2) |
216 | { |
217 | int ret, reg; |
218 | struct veml6030_data *data = iio_priv(indio_dev); |
219 | |
220 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_CONF, val: ®); |
221 | if (ret) { |
222 | dev_err(&data->client->dev, |
223 | "can't read als conf register %d\n" , ret); |
224 | return ret; |
225 | } |
226 | |
227 | switch ((reg >> 6) & 0xF) { |
228 | case 0: |
229 | *val2 = 100000; |
230 | break; |
231 | case 1: |
232 | *val2 = 200000; |
233 | break; |
234 | case 2: |
235 | *val2 = 400000; |
236 | break; |
237 | case 3: |
238 | *val2 = 800000; |
239 | break; |
240 | case 8: |
241 | *val2 = 50000; |
242 | break; |
243 | case 12: |
244 | *val2 = 25000; |
245 | break; |
246 | default: |
247 | return -EINVAL; |
248 | } |
249 | |
250 | *val = 0; |
251 | return IIO_VAL_INT_PLUS_MICRO; |
252 | } |
253 | |
254 | static int veml6030_set_intgrn_tm(struct iio_dev *indio_dev, |
255 | int val, int val2) |
256 | { |
257 | int ret, new_int_time, int_idx; |
258 | struct veml6030_data *data = iio_priv(indio_dev); |
259 | |
260 | if (val) |
261 | return -EINVAL; |
262 | |
263 | switch (val2) { |
264 | case 25000: |
265 | new_int_time = 0x300; |
266 | int_idx = 5; |
267 | break; |
268 | case 50000: |
269 | new_int_time = 0x200; |
270 | int_idx = 4; |
271 | break; |
272 | case 100000: |
273 | new_int_time = 0x00; |
274 | int_idx = 3; |
275 | break; |
276 | case 200000: |
277 | new_int_time = 0x40; |
278 | int_idx = 2; |
279 | break; |
280 | case 400000: |
281 | new_int_time = 0x80; |
282 | int_idx = 1; |
283 | break; |
284 | case 800000: |
285 | new_int_time = 0xC0; |
286 | int_idx = 0; |
287 | break; |
288 | default: |
289 | return -EINVAL; |
290 | } |
291 | |
292 | ret = regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
293 | VEML6030_ALS_IT, val: new_int_time); |
294 | if (ret) { |
295 | dev_err(&data->client->dev, |
296 | "can't update als integration time %d\n" , ret); |
297 | return ret; |
298 | } |
299 | |
300 | /* |
301 | * Cache current integration time and update resolution. For every |
302 | * increase in integration time to next level, resolution is halved |
303 | * and vice-versa. |
304 | */ |
305 | if (data->cur_integration_time < int_idx) |
306 | data->cur_resolution <<= int_idx - data->cur_integration_time; |
307 | else if (data->cur_integration_time > int_idx) |
308 | data->cur_resolution >>= data->cur_integration_time - int_idx; |
309 | |
310 | data->cur_integration_time = int_idx; |
311 | |
312 | return ret; |
313 | } |
314 | |
315 | static int veml6030_read_persistence(struct iio_dev *indio_dev, |
316 | int *val, int *val2) |
317 | { |
318 | int ret, reg, period, x, y; |
319 | struct veml6030_data *data = iio_priv(indio_dev); |
320 | |
321 | ret = veml6030_get_intgrn_tm(indio_dev, val: &x, val2: &y); |
322 | if (ret < 0) |
323 | return ret; |
324 | |
325 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_CONF, val: ®); |
326 | if (ret) { |
327 | dev_err(&data->client->dev, |
328 | "can't read als conf register %d\n" , ret); |
329 | } |
330 | |
331 | /* integration time multiplied by 1/2/4/8 */ |
332 | period = y * (1 << ((reg >> 4) & 0x03)); |
333 | |
334 | *val = period / 1000000; |
335 | *val2 = period % 1000000; |
336 | |
337 | return IIO_VAL_INT_PLUS_MICRO; |
338 | } |
339 | |
340 | static int veml6030_write_persistence(struct iio_dev *indio_dev, |
341 | int val, int val2) |
342 | { |
343 | int ret, period, x, y; |
344 | struct veml6030_data *data = iio_priv(indio_dev); |
345 | |
346 | ret = veml6030_get_intgrn_tm(indio_dev, val: &x, val2: &y); |
347 | if (ret < 0) |
348 | return ret; |
349 | |
350 | if (!val) { |
351 | period = val2 / y; |
352 | } else { |
353 | if ((val == 1) && (val2 == 600000)) |
354 | period = 1600000 / y; |
355 | else if ((val == 3) && (val2 == 200000)) |
356 | period = 3200000 / y; |
357 | else if ((val == 6) && (val2 == 400000)) |
358 | period = 6400000 / y; |
359 | else |
360 | period = -1; |
361 | } |
362 | |
363 | if (period <= 0 || period > 8 || hweight8(period) != 1) |
364 | return -EINVAL; |
365 | |
366 | ret = regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
367 | VEML6030_ALS_PERS, val: (ffs(period) - 1) << 4); |
368 | if (ret) |
369 | dev_err(&data->client->dev, |
370 | "can't set persistence value %d\n" , ret); |
371 | |
372 | return ret; |
373 | } |
374 | |
375 | static int veml6030_set_als_gain(struct iio_dev *indio_dev, |
376 | int val, int val2) |
377 | { |
378 | int ret, new_gain, gain_idx; |
379 | struct veml6030_data *data = iio_priv(indio_dev); |
380 | |
381 | if (val == 0 && val2 == 125000) { |
382 | new_gain = 0x1000; /* 0x02 << 11 */ |
383 | gain_idx = 3; |
384 | } else if (val == 0 && val2 == 250000) { |
385 | new_gain = 0x1800; |
386 | gain_idx = 2; |
387 | } else if (val == 1 && val2 == 0) { |
388 | new_gain = 0x00; |
389 | gain_idx = 1; |
390 | } else if (val == 2 && val2 == 0) { |
391 | new_gain = 0x800; |
392 | gain_idx = 0; |
393 | } else { |
394 | return -EINVAL; |
395 | } |
396 | |
397 | ret = regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
398 | VEML6030_ALS_GAIN, val: new_gain); |
399 | if (ret) { |
400 | dev_err(&data->client->dev, |
401 | "can't set als gain %d\n" , ret); |
402 | return ret; |
403 | } |
404 | |
405 | /* |
406 | * Cache currently set gain & update resolution. For every |
407 | * increase in the gain to next level, resolution is halved |
408 | * and vice-versa. |
409 | */ |
410 | if (data->cur_gain < gain_idx) |
411 | data->cur_resolution <<= gain_idx - data->cur_gain; |
412 | else if (data->cur_gain > gain_idx) |
413 | data->cur_resolution >>= data->cur_gain - gain_idx; |
414 | |
415 | data->cur_gain = gain_idx; |
416 | |
417 | return ret; |
418 | } |
419 | |
420 | static int veml6030_get_als_gain(struct iio_dev *indio_dev, |
421 | int *val, int *val2) |
422 | { |
423 | int ret, reg; |
424 | struct veml6030_data *data = iio_priv(indio_dev); |
425 | |
426 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_CONF, val: ®); |
427 | if (ret) { |
428 | dev_err(&data->client->dev, |
429 | "can't read als conf register %d\n" , ret); |
430 | return ret; |
431 | } |
432 | |
433 | switch ((reg >> 11) & 0x03) { |
434 | case 0: |
435 | *val = 1; |
436 | *val2 = 0; |
437 | break; |
438 | case 1: |
439 | *val = 2; |
440 | *val2 = 0; |
441 | break; |
442 | case 2: |
443 | *val = 0; |
444 | *val2 = 125000; |
445 | break; |
446 | case 3: |
447 | *val = 0; |
448 | *val2 = 250000; |
449 | break; |
450 | default: |
451 | return -EINVAL; |
452 | } |
453 | |
454 | return IIO_VAL_INT_PLUS_MICRO; |
455 | } |
456 | |
457 | static int veml6030_read_thresh(struct iio_dev *indio_dev, |
458 | int *val, int *val2, int dir) |
459 | { |
460 | int ret, reg; |
461 | struct veml6030_data *data = iio_priv(indio_dev); |
462 | |
463 | if (dir == IIO_EV_DIR_RISING) |
464 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_WH, val: ®); |
465 | else |
466 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_WL, val: ®); |
467 | if (ret) { |
468 | dev_err(&data->client->dev, |
469 | "can't read als threshold value %d\n" , ret); |
470 | return ret; |
471 | } |
472 | |
473 | *val = reg & 0xffff; |
474 | return IIO_VAL_INT; |
475 | } |
476 | |
477 | static int veml6030_write_thresh(struct iio_dev *indio_dev, |
478 | int val, int val2, int dir) |
479 | { |
480 | int ret; |
481 | struct veml6030_data *data = iio_priv(indio_dev); |
482 | |
483 | if (val > 0xFFFF || val < 0 || val2) |
484 | return -EINVAL; |
485 | |
486 | if (dir == IIO_EV_DIR_RISING) { |
487 | ret = regmap_write(map: data->regmap, VEML6030_REG_ALS_WH, val); |
488 | if (ret) |
489 | dev_err(&data->client->dev, |
490 | "can't set high threshold %d\n" , ret); |
491 | } else { |
492 | ret = regmap_write(map: data->regmap, VEML6030_REG_ALS_WL, val); |
493 | if (ret) |
494 | dev_err(&data->client->dev, |
495 | "can't set low threshold %d\n" , ret); |
496 | } |
497 | |
498 | return ret; |
499 | } |
500 | |
501 | /* |
502 | * Provide both raw as well as light reading in lux. |
503 | * light (in lux) = resolution * raw reading |
504 | */ |
505 | static int veml6030_read_raw(struct iio_dev *indio_dev, |
506 | struct iio_chan_spec const *chan, int *val, |
507 | int *val2, long mask) |
508 | { |
509 | int ret, reg; |
510 | struct veml6030_data *data = iio_priv(indio_dev); |
511 | struct regmap *regmap = data->regmap; |
512 | struct device *dev = &data->client->dev; |
513 | |
514 | switch (mask) { |
515 | case IIO_CHAN_INFO_RAW: |
516 | case IIO_CHAN_INFO_PROCESSED: |
517 | switch (chan->type) { |
518 | case IIO_LIGHT: |
519 | ret = regmap_read(map: regmap, VEML6030_REG_ALS_DATA, val: ®); |
520 | if (ret < 0) { |
521 | dev_err(dev, "can't read als data %d\n" , ret); |
522 | return ret; |
523 | } |
524 | if (mask == IIO_CHAN_INFO_PROCESSED) { |
525 | *val = (reg * data->cur_resolution) / 10000; |
526 | *val2 = (reg * data->cur_resolution) % 10000; |
527 | return IIO_VAL_INT_PLUS_MICRO; |
528 | } |
529 | *val = reg; |
530 | return IIO_VAL_INT; |
531 | case IIO_INTENSITY: |
532 | ret = regmap_read(map: regmap, VEML6030_REG_WH_DATA, val: ®); |
533 | if (ret < 0) { |
534 | dev_err(dev, "can't read white data %d\n" , ret); |
535 | return ret; |
536 | } |
537 | if (mask == IIO_CHAN_INFO_PROCESSED) { |
538 | *val = (reg * data->cur_resolution) / 10000; |
539 | *val2 = (reg * data->cur_resolution) % 10000; |
540 | return IIO_VAL_INT_PLUS_MICRO; |
541 | } |
542 | *val = reg; |
543 | return IIO_VAL_INT; |
544 | default: |
545 | return -EINVAL; |
546 | } |
547 | case IIO_CHAN_INFO_INT_TIME: |
548 | if (chan->type == IIO_LIGHT) |
549 | return veml6030_get_intgrn_tm(indio_dev, val, val2); |
550 | return -EINVAL; |
551 | case IIO_CHAN_INFO_SCALE: |
552 | if (chan->type == IIO_LIGHT) |
553 | return veml6030_get_als_gain(indio_dev, val, val2); |
554 | return -EINVAL; |
555 | default: |
556 | return -EINVAL; |
557 | } |
558 | } |
559 | |
560 | static int veml6030_write_raw(struct iio_dev *indio_dev, |
561 | struct iio_chan_spec const *chan, |
562 | int val, int val2, long mask) |
563 | { |
564 | switch (mask) { |
565 | case IIO_CHAN_INFO_INT_TIME: |
566 | switch (chan->type) { |
567 | case IIO_LIGHT: |
568 | return veml6030_set_intgrn_tm(indio_dev, val, val2); |
569 | default: |
570 | return -EINVAL; |
571 | } |
572 | case IIO_CHAN_INFO_SCALE: |
573 | switch (chan->type) { |
574 | case IIO_LIGHT: |
575 | return veml6030_set_als_gain(indio_dev, val, val2); |
576 | default: |
577 | return -EINVAL; |
578 | } |
579 | default: |
580 | return -EINVAL; |
581 | } |
582 | } |
583 | |
584 | static int veml6030_read_event_val(struct iio_dev *indio_dev, |
585 | const struct iio_chan_spec *chan, enum iio_event_type type, |
586 | enum iio_event_direction dir, enum iio_event_info info, |
587 | int *val, int *val2) |
588 | { |
589 | switch (info) { |
590 | case IIO_EV_INFO_VALUE: |
591 | switch (dir) { |
592 | case IIO_EV_DIR_RISING: |
593 | case IIO_EV_DIR_FALLING: |
594 | return veml6030_read_thresh(indio_dev, val, val2, dir); |
595 | default: |
596 | return -EINVAL; |
597 | } |
598 | break; |
599 | case IIO_EV_INFO_PERIOD: |
600 | return veml6030_read_persistence(indio_dev, val, val2); |
601 | default: |
602 | return -EINVAL; |
603 | } |
604 | } |
605 | |
606 | static int veml6030_write_event_val(struct iio_dev *indio_dev, |
607 | const struct iio_chan_spec *chan, enum iio_event_type type, |
608 | enum iio_event_direction dir, enum iio_event_info info, |
609 | int val, int val2) |
610 | { |
611 | switch (info) { |
612 | case IIO_EV_INFO_VALUE: |
613 | return veml6030_write_thresh(indio_dev, val, val2, dir); |
614 | case IIO_EV_INFO_PERIOD: |
615 | return veml6030_write_persistence(indio_dev, val, val2); |
616 | default: |
617 | return -EINVAL; |
618 | } |
619 | } |
620 | |
621 | static int veml6030_read_interrupt_config(struct iio_dev *indio_dev, |
622 | const struct iio_chan_spec *chan, enum iio_event_type type, |
623 | enum iio_event_direction dir) |
624 | { |
625 | int ret, reg; |
626 | struct veml6030_data *data = iio_priv(indio_dev); |
627 | |
628 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_CONF, val: ®); |
629 | if (ret) { |
630 | dev_err(&data->client->dev, |
631 | "can't read als conf register %d\n" , ret); |
632 | return ret; |
633 | } |
634 | |
635 | if (reg & VEML6030_ALS_INT_EN) |
636 | return 1; |
637 | else |
638 | return 0; |
639 | } |
640 | |
641 | /* |
642 | * Sensor should not be measuring light when interrupt is configured. |
643 | * Therefore correct sequence to configure interrupt functionality is: |
644 | * shut down -> enable/disable interrupt -> power on |
645 | * |
646 | * state = 1 enables interrupt, state = 0 disables interrupt |
647 | */ |
648 | static int veml6030_write_interrupt_config(struct iio_dev *indio_dev, |
649 | const struct iio_chan_spec *chan, enum iio_event_type type, |
650 | enum iio_event_direction dir, int state) |
651 | { |
652 | int ret; |
653 | struct veml6030_data *data = iio_priv(indio_dev); |
654 | |
655 | if (state < 0 || state > 1) |
656 | return -EINVAL; |
657 | |
658 | ret = veml6030_als_shut_down(data); |
659 | if (ret < 0) { |
660 | dev_err(&data->client->dev, |
661 | "can't disable als to configure interrupt %d\n" , ret); |
662 | return ret; |
663 | } |
664 | |
665 | /* enable interrupt + power on */ |
666 | ret = regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_CONF, |
667 | VEML6030_ALS_INT_EN | VEML6030_ALS_SD, val: state << 1); |
668 | if (ret) |
669 | dev_err(&data->client->dev, |
670 | "can't enable interrupt & poweron als %d\n" , ret); |
671 | |
672 | return ret; |
673 | } |
674 | |
675 | static const struct iio_info veml6030_info = { |
676 | .read_raw = veml6030_read_raw, |
677 | .write_raw = veml6030_write_raw, |
678 | .read_event_value = veml6030_read_event_val, |
679 | .write_event_value = veml6030_write_event_val, |
680 | .read_event_config = veml6030_read_interrupt_config, |
681 | .write_event_config = veml6030_write_interrupt_config, |
682 | .attrs = &veml6030_attr_group, |
683 | .event_attrs = &veml6030_event_attr_group, |
684 | }; |
685 | |
686 | static const struct iio_info veml6030_info_no_irq = { |
687 | .read_raw = veml6030_read_raw, |
688 | .write_raw = veml6030_write_raw, |
689 | .attrs = &veml6030_attr_group, |
690 | }; |
691 | |
692 | static irqreturn_t veml6030_event_handler(int irq, void *private) |
693 | { |
694 | int ret, reg, evtdir; |
695 | struct iio_dev *indio_dev = private; |
696 | struct veml6030_data *data = iio_priv(indio_dev); |
697 | |
698 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_INT, val: ®); |
699 | if (ret) { |
700 | dev_err(&data->client->dev, |
701 | "can't read als interrupt register %d\n" , ret); |
702 | return IRQ_HANDLED; |
703 | } |
704 | |
705 | /* Spurious interrupt handling */ |
706 | if (!(reg & (VEML6030_INT_TH_HIGH | VEML6030_INT_TH_LOW))) |
707 | return IRQ_NONE; |
708 | |
709 | if (reg & VEML6030_INT_TH_HIGH) |
710 | evtdir = IIO_EV_DIR_RISING; |
711 | else |
712 | evtdir = IIO_EV_DIR_FALLING; |
713 | |
714 | iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, |
715 | 0, IIO_EV_TYPE_THRESH, evtdir), |
716 | timestamp: iio_get_time_ns(indio_dev)); |
717 | |
718 | return IRQ_HANDLED; |
719 | } |
720 | |
721 | /* |
722 | * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2, |
723 | * persistence to 1 x integration time and the threshold |
724 | * interrupt disabled by default. First shutdown the sensor, |
725 | * update registers and then power on the sensor. |
726 | */ |
727 | static int veml6030_hw_init(struct iio_dev *indio_dev) |
728 | { |
729 | int ret, val; |
730 | struct veml6030_data *data = iio_priv(indio_dev); |
731 | struct i2c_client *client = data->client; |
732 | |
733 | ret = veml6030_als_shut_down(data); |
734 | if (ret) { |
735 | dev_err(&client->dev, "can't shutdown als %d\n" , ret); |
736 | return ret; |
737 | } |
738 | |
739 | ret = regmap_write(map: data->regmap, VEML6030_REG_ALS_CONF, val: 0x1001); |
740 | if (ret) { |
741 | dev_err(&client->dev, "can't setup als configs %d\n" , ret); |
742 | return ret; |
743 | } |
744 | |
745 | ret = regmap_update_bits(map: data->regmap, VEML6030_REG_ALS_PSM, |
746 | VEML6030_PSM | VEML6030_PSM_EN, val: 0x03); |
747 | if (ret) { |
748 | dev_err(&client->dev, "can't setup default PSM %d\n" , ret); |
749 | return ret; |
750 | } |
751 | |
752 | ret = regmap_write(map: data->regmap, VEML6030_REG_ALS_WH, val: 0xFFFF); |
753 | if (ret) { |
754 | dev_err(&client->dev, "can't setup high threshold %d\n" , ret); |
755 | return ret; |
756 | } |
757 | |
758 | ret = regmap_write(map: data->regmap, VEML6030_REG_ALS_WL, val: 0x0000); |
759 | if (ret) { |
760 | dev_err(&client->dev, "can't setup low threshold %d\n" , ret); |
761 | return ret; |
762 | } |
763 | |
764 | ret = veml6030_als_pwr_on(data); |
765 | if (ret) { |
766 | dev_err(&client->dev, "can't poweron als %d\n" , ret); |
767 | return ret; |
768 | } |
769 | |
770 | /* Wait 4 ms to let processor & oscillator start correctly */ |
771 | usleep_range(min: 4000, max: 4002); |
772 | |
773 | /* Clear stale interrupt status bits if any during start */ |
774 | ret = regmap_read(map: data->regmap, VEML6030_REG_ALS_INT, val: &val); |
775 | if (ret < 0) { |
776 | dev_err(&client->dev, |
777 | "can't clear als interrupt status %d\n" , ret); |
778 | return ret; |
779 | } |
780 | |
781 | /* Cache currently active measurement parameters */ |
782 | data->cur_gain = 3; |
783 | data->cur_resolution = 4608; |
784 | data->cur_integration_time = 3; |
785 | |
786 | return ret; |
787 | } |
788 | |
789 | static int veml6030_probe(struct i2c_client *client) |
790 | { |
791 | int ret; |
792 | struct veml6030_data *data; |
793 | struct iio_dev *indio_dev; |
794 | struct regmap *regmap; |
795 | |
796 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
797 | dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n" ); |
798 | return -EOPNOTSUPP; |
799 | } |
800 | |
801 | regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config); |
802 | if (IS_ERR(ptr: regmap)) { |
803 | dev_err(&client->dev, "can't setup regmap\n" ); |
804 | return PTR_ERR(ptr: regmap); |
805 | } |
806 | |
807 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
808 | if (!indio_dev) |
809 | return -ENOMEM; |
810 | |
811 | data = iio_priv(indio_dev); |
812 | i2c_set_clientdata(client, data: indio_dev); |
813 | data->client = client; |
814 | data->regmap = regmap; |
815 | |
816 | indio_dev->name = "veml6030" ; |
817 | indio_dev->channels = veml6030_channels; |
818 | indio_dev->num_channels = ARRAY_SIZE(veml6030_channels); |
819 | indio_dev->modes = INDIO_DIRECT_MODE; |
820 | |
821 | if (client->irq) { |
822 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
823 | NULL, thread_fn: veml6030_event_handler, |
824 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
825 | devname: "veml6030" , dev_id: indio_dev); |
826 | if (ret < 0) { |
827 | dev_err(&client->dev, |
828 | "irq %d request failed\n" , client->irq); |
829 | return ret; |
830 | } |
831 | indio_dev->info = &veml6030_info; |
832 | } else { |
833 | indio_dev->info = &veml6030_info_no_irq; |
834 | } |
835 | |
836 | ret = veml6030_hw_init(indio_dev); |
837 | if (ret < 0) |
838 | return ret; |
839 | |
840 | ret = devm_add_action_or_reset(&client->dev, |
841 | veml6030_als_shut_down_action, data); |
842 | if (ret < 0) |
843 | return ret; |
844 | |
845 | return devm_iio_device_register(&client->dev, indio_dev); |
846 | } |
847 | |
848 | static int veml6030_runtime_suspend(struct device *dev) |
849 | { |
850 | int ret; |
851 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
852 | struct veml6030_data *data = iio_priv(indio_dev); |
853 | |
854 | ret = veml6030_als_shut_down(data); |
855 | if (ret < 0) |
856 | dev_err(&data->client->dev, "can't suspend als %d\n" , ret); |
857 | |
858 | return ret; |
859 | } |
860 | |
861 | static int veml6030_runtime_resume(struct device *dev) |
862 | { |
863 | int ret; |
864 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
865 | struct veml6030_data *data = iio_priv(indio_dev); |
866 | |
867 | ret = veml6030_als_pwr_on(data); |
868 | if (ret < 0) |
869 | dev_err(&data->client->dev, "can't resume als %d\n" , ret); |
870 | |
871 | return ret; |
872 | } |
873 | |
874 | static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend, |
875 | veml6030_runtime_resume, NULL); |
876 | |
877 | static const struct of_device_id veml6030_of_match[] = { |
878 | { .compatible = "vishay,veml6030" }, |
879 | { } |
880 | }; |
881 | MODULE_DEVICE_TABLE(of, veml6030_of_match); |
882 | |
883 | static const struct i2c_device_id veml6030_id[] = { |
884 | { "veml6030" , 0 }, |
885 | { } |
886 | }; |
887 | MODULE_DEVICE_TABLE(i2c, veml6030_id); |
888 | |
889 | static struct i2c_driver veml6030_driver = { |
890 | .driver = { |
891 | .name = "veml6030" , |
892 | .of_match_table = veml6030_of_match, |
893 | .pm = pm_ptr(&veml6030_pm_ops), |
894 | }, |
895 | .probe = veml6030_probe, |
896 | .id_table = veml6030_id, |
897 | }; |
898 | module_i2c_driver(veml6030_driver); |
899 | |
900 | MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>" ); |
901 | MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor" ); |
902 | MODULE_LICENSE("GPL v2" ); |
903 | |