1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Support for Vishay VCNL3020 proximity sensor on i2c bus. |
4 | * Based on Vishay VCNL4000 driver code. |
5 | */ |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/err.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/interrupt.h> |
13 | |
14 | #include <linux/iio/iio.h> |
15 | #include <linux/iio/events.h> |
16 | |
17 | #define VCNL3020_PROD_ID 0x21 |
18 | |
19 | #define VCNL_COMMAND 0x80 /* Command register */ |
20 | #define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */ |
21 | #define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */ |
22 | #define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */ |
23 | #define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ |
24 | #define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ |
25 | #define VCNL_PS_ICR 0x89 /* Interrupt Control Register */ |
26 | #define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */ |
27 | #define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */ |
28 | #define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */ |
29 | #define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */ |
30 | #define VCNL_ISR 0x8e /* Interrupt Status Register */ |
31 | #define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */ |
32 | |
33 | /* Bit masks for COMMAND register */ |
34 | #define VCNL_PS_RDY BIT(5) /* proximity data ready? */ |
35 | #define VCNL_PS_OD BIT(3) /* start on-demand proximity |
36 | * measurement |
37 | */ |
38 | |
39 | /* Enables periodic proximity measurement */ |
40 | #define VCNL_PS_EN BIT(1) |
41 | |
42 | /* Enables state machine and LP oscillator for self timed measurements */ |
43 | #define VCNL_PS_SELFTIMED_EN BIT(0) |
44 | |
45 | /* Bit masks for ICR */ |
46 | |
47 | /* Enable interrupts on low or high thresholds */ |
48 | #define VCNL_ICR_THRES_EN BIT(1) |
49 | |
50 | /* Bit masks for ISR */ |
51 | #define VCNL_INT_TH_HI BIT(0) /* High threshold hit */ |
52 | #define VCNL_INT_TH_LOW BIT(1) /* Low threshold hit */ |
53 | |
54 | #define VCNL_ON_DEMAND_TIMEOUT_US 100000 |
55 | #define VCNL_POLL_US 20000 |
56 | |
57 | static const int vcnl3020_prox_sampling_frequency[][2] = { |
58 | {1, 950000}, |
59 | {3, 906250}, |
60 | {7, 812500}, |
61 | {16, 625000}, |
62 | {31, 250000}, |
63 | {62, 500000}, |
64 | {125, 0}, |
65 | {250, 0}, |
66 | }; |
67 | |
68 | /** |
69 | * struct vcnl3020_data - vcnl3020 specific data. |
70 | * @regmap: device register map. |
71 | * @dev: vcnl3020 device. |
72 | * @rev: revision id. |
73 | * @lock: lock for protecting access to device hardware registers. |
74 | * @buf: __be16 buffer. |
75 | */ |
76 | struct vcnl3020_data { |
77 | struct regmap *regmap; |
78 | struct device *dev; |
79 | u8 rev; |
80 | struct mutex lock; |
81 | __be16 buf; |
82 | }; |
83 | |
84 | /** |
85 | * struct vcnl3020_property - vcnl3020 property. |
86 | * @name: property name. |
87 | * @reg: i2c register offset. |
88 | * @conversion_func: conversion function. |
89 | */ |
90 | struct vcnl3020_property { |
91 | const char *name; |
92 | u32 reg; |
93 | u32 (*conversion_func)(u32 *val); |
94 | }; |
95 | |
96 | static u32 microamp_to_reg(u32 *val) |
97 | { |
98 | /* |
99 | * An example of conversion from uA to reg val: |
100 | * 200000 uA == 200 mA == 20 |
101 | */ |
102 | return *val /= 10000; |
103 | }; |
104 | |
105 | static struct vcnl3020_property vcnl3020_led_current_property = { |
106 | .name = "vishay,led-current-microamp", |
107 | .reg = VCNL_LED_CURRENT, |
108 | .conversion_func = microamp_to_reg, |
109 | }; |
110 | |
111 | static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data, |
112 | struct vcnl3020_property prop) |
113 | { |
114 | int rc; |
115 | u32 val; |
116 | |
117 | rc = device_property_read_u32(dev: data->dev, propname: prop.name, val: &val); |
118 | if (rc) |
119 | return 0; |
120 | |
121 | if (prop.conversion_func) |
122 | prop.conversion_func(&val); |
123 | |
124 | rc = regmap_write(map: data->regmap, reg: prop.reg, val); |
125 | if (rc) { |
126 | dev_err(data->dev, "Error (%d) setting property (%s)\n", |
127 | rc, prop.name); |
128 | } |
129 | |
130 | return rc; |
131 | } |
132 | |
133 | static int vcnl3020_init(struct vcnl3020_data *data) |
134 | { |
135 | int rc; |
136 | unsigned int reg; |
137 | |
138 | rc = regmap_read(map: data->regmap, VCNL_PROD_REV, val: ®); |
139 | if (rc) { |
140 | dev_err(data->dev, |
141 | "Error (%d) reading product revision\n", rc); |
142 | return rc; |
143 | } |
144 | |
145 | if (reg != VCNL3020_PROD_ID) { |
146 | dev_err(data->dev, |
147 | "Product id (%x) did not match vcnl3020 (%x)\n", reg, |
148 | VCNL3020_PROD_ID); |
149 | return -ENODEV; |
150 | } |
151 | |
152 | data->rev = reg; |
153 | mutex_init(&data->lock); |
154 | |
155 | return vcnl3020_get_and_apply_property(data, |
156 | prop: vcnl3020_led_current_property); |
157 | }; |
158 | |
159 | static bool vcnl3020_is_in_periodic_mode(struct vcnl3020_data *data) |
160 | { |
161 | int rc; |
162 | unsigned int cmd; |
163 | |
164 | rc = regmap_read(map: data->regmap, VCNL_COMMAND, val: &cmd); |
165 | if (rc) { |
166 | dev_err(data->dev, |
167 | "Error (%d) reading command register\n", rc); |
168 | return false; |
169 | } |
170 | |
171 | return !!(cmd & VCNL_PS_SELFTIMED_EN); |
172 | } |
173 | |
174 | static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val) |
175 | { |
176 | int rc; |
177 | unsigned int reg; |
178 | |
179 | mutex_lock(&data->lock); |
180 | |
181 | /* Protect against event capture. */ |
182 | if (vcnl3020_is_in_periodic_mode(data)) { |
183 | rc = -EBUSY; |
184 | goto err_unlock; |
185 | } |
186 | |
187 | rc = regmap_write(map: data->regmap, VCNL_COMMAND, VCNL_PS_OD); |
188 | if (rc) |
189 | goto err_unlock; |
190 | |
191 | /* wait for data to become ready */ |
192 | rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg, |
193 | reg & VCNL_PS_RDY, VCNL_POLL_US, |
194 | VCNL_ON_DEMAND_TIMEOUT_US); |
195 | if (rc) { |
196 | dev_err(data->dev, |
197 | "Error (%d) reading vcnl3020 command register\n", rc); |
198 | goto err_unlock; |
199 | } |
200 | |
201 | /* high & low result bytes read */ |
202 | rc = regmap_bulk_read(map: data->regmap, VCNL_PS_RESULT_HI, val: &data->buf, |
203 | val_count: sizeof(data->buf)); |
204 | if (rc) |
205 | goto err_unlock; |
206 | |
207 | *val = be16_to_cpu(data->buf); |
208 | |
209 | err_unlock: |
210 | mutex_unlock(lock: &data->lock); |
211 | |
212 | return rc; |
213 | } |
214 | |
215 | static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val, |
216 | int *val2) |
217 | { |
218 | int rc; |
219 | unsigned int prox_rate; |
220 | |
221 | rc = regmap_read(map: data->regmap, VCNL_PROXIMITY_RATE, val: &prox_rate); |
222 | if (rc) |
223 | return rc; |
224 | |
225 | if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency)) |
226 | return -EINVAL; |
227 | |
228 | *val = vcnl3020_prox_sampling_frequency[prox_rate][0]; |
229 | *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1]; |
230 | |
231 | return 0; |
232 | } |
233 | |
234 | static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val, |
235 | int val2) |
236 | { |
237 | unsigned int i; |
238 | int index = -1; |
239 | int rc; |
240 | |
241 | mutex_lock(&data->lock); |
242 | |
243 | /* Protect against event capture. */ |
244 | if (vcnl3020_is_in_periodic_mode(data)) { |
245 | rc = -EBUSY; |
246 | goto err_unlock; |
247 | } |
248 | |
249 | for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) { |
250 | if (val == vcnl3020_prox_sampling_frequency[i][0] && |
251 | val2 == vcnl3020_prox_sampling_frequency[i][1]) { |
252 | index = i; |
253 | break; |
254 | } |
255 | } |
256 | |
257 | if (index < 0) { |
258 | rc = -EINVAL; |
259 | goto err_unlock; |
260 | } |
261 | |
262 | rc = regmap_write(map: data->regmap, VCNL_PROXIMITY_RATE, val: index); |
263 | if (rc) |
264 | dev_err(data->dev, |
265 | "Error (%d) writing proximity rate register\n", rc); |
266 | |
267 | err_unlock: |
268 | mutex_unlock(lock: &data->lock); |
269 | |
270 | return rc; |
271 | } |
272 | |
273 | static bool vcnl3020_is_thr_enabled(struct vcnl3020_data *data) |
274 | { |
275 | int rc; |
276 | unsigned int icr; |
277 | |
278 | rc = regmap_read(map: data->regmap, VCNL_PS_ICR, val: &icr); |
279 | if (rc) { |
280 | dev_err(data->dev, |
281 | "Error (%d) reading ICR register\n", rc); |
282 | return false; |
283 | } |
284 | |
285 | return !!(icr & VCNL_ICR_THRES_EN); |
286 | } |
287 | |
288 | static int vcnl3020_read_event(struct iio_dev *indio_dev, |
289 | const struct iio_chan_spec *chan, |
290 | enum iio_event_type type, |
291 | enum iio_event_direction dir, |
292 | enum iio_event_info info, |
293 | int *val, int *val2) |
294 | { |
295 | int rc; |
296 | struct vcnl3020_data *data = iio_priv(indio_dev); |
297 | |
298 | switch (info) { |
299 | case IIO_EV_INFO_VALUE: |
300 | switch (dir) { |
301 | case IIO_EV_DIR_RISING: |
302 | rc = regmap_bulk_read(map: data->regmap, VCNL_PS_HI_THR_HI, |
303 | val: &data->buf, val_count: sizeof(data->buf)); |
304 | if (rc < 0) |
305 | return rc; |
306 | *val = be16_to_cpu(data->buf); |
307 | return IIO_VAL_INT; |
308 | case IIO_EV_DIR_FALLING: |
309 | rc = regmap_bulk_read(map: data->regmap, VCNL_PS_LO_THR_HI, |
310 | val: &data->buf, val_count: sizeof(data->buf)); |
311 | if (rc < 0) |
312 | return rc; |
313 | *val = be16_to_cpu(data->buf); |
314 | return IIO_VAL_INT; |
315 | default: |
316 | return -EINVAL; |
317 | } |
318 | default: |
319 | return -EINVAL; |
320 | } |
321 | } |
322 | |
323 | static int vcnl3020_write_event(struct iio_dev *indio_dev, |
324 | const struct iio_chan_spec *chan, |
325 | enum iio_event_type type, |
326 | enum iio_event_direction dir, |
327 | enum iio_event_info info, |
328 | int val, int val2) |
329 | { |
330 | int rc; |
331 | struct vcnl3020_data *data = iio_priv(indio_dev); |
332 | |
333 | mutex_lock(&data->lock); |
334 | |
335 | switch (info) { |
336 | case IIO_EV_INFO_VALUE: |
337 | switch (dir) { |
338 | case IIO_EV_DIR_RISING: |
339 | /* 16 bit word/ low * high */ |
340 | data->buf = cpu_to_be16(val); |
341 | rc = regmap_bulk_write(map: data->regmap, VCNL_PS_HI_THR_HI, |
342 | val: &data->buf, val_count: sizeof(data->buf)); |
343 | if (rc < 0) |
344 | goto err_unlock; |
345 | rc = IIO_VAL_INT; |
346 | goto err_unlock; |
347 | case IIO_EV_DIR_FALLING: |
348 | data->buf = cpu_to_be16(val); |
349 | rc = regmap_bulk_write(map: data->regmap, VCNL_PS_LO_THR_HI, |
350 | val: &data->buf, val_count: sizeof(data->buf)); |
351 | if (rc < 0) |
352 | goto err_unlock; |
353 | rc = IIO_VAL_INT; |
354 | goto err_unlock; |
355 | default: |
356 | rc = -EINVAL; |
357 | goto err_unlock; |
358 | } |
359 | default: |
360 | rc = -EINVAL; |
361 | goto err_unlock; |
362 | } |
363 | err_unlock: |
364 | mutex_unlock(lock: &data->lock); |
365 | |
366 | return rc; |
367 | } |
368 | |
369 | static int vcnl3020_enable_periodic(struct iio_dev *indio_dev, |
370 | struct vcnl3020_data *data) |
371 | { |
372 | int rc; |
373 | int cmd; |
374 | |
375 | mutex_lock(&data->lock); |
376 | |
377 | /* Enable periodic measurement of proximity data. */ |
378 | cmd = VCNL_PS_EN | VCNL_PS_SELFTIMED_EN; |
379 | |
380 | rc = regmap_write(map: data->regmap, VCNL_COMMAND, val: cmd); |
381 | if (rc) { |
382 | dev_err(data->dev, |
383 | "Error (%d) writing command register\n", rc); |
384 | goto err_unlock; |
385 | } |
386 | |
387 | /* |
388 | * Enable interrupts on threshold, for proximity data by |
389 | * default. |
390 | */ |
391 | rc = regmap_write(map: data->regmap, VCNL_PS_ICR, VCNL_ICR_THRES_EN); |
392 | if (rc) |
393 | dev_err(data->dev, |
394 | "Error (%d) reading ICR register\n", rc); |
395 | |
396 | err_unlock: |
397 | mutex_unlock(lock: &data->lock); |
398 | |
399 | return rc; |
400 | } |
401 | |
402 | static int vcnl3020_disable_periodic(struct iio_dev *indio_dev, |
403 | struct vcnl3020_data *data) |
404 | { |
405 | int rc; |
406 | |
407 | mutex_lock(&data->lock); |
408 | |
409 | rc = regmap_write(map: data->regmap, VCNL_COMMAND, val: 0); |
410 | if (rc) { |
411 | dev_err(data->dev, |
412 | "Error (%d) writing command register\n", rc); |
413 | goto err_unlock; |
414 | } |
415 | |
416 | rc = regmap_write(map: data->regmap, VCNL_PS_ICR, val: 0); |
417 | if (rc) { |
418 | dev_err(data->dev, |
419 | "Error (%d) writing ICR register\n", rc); |
420 | goto err_unlock; |
421 | } |
422 | |
423 | /* Clear interrupt flag bit */ |
424 | rc = regmap_write(map: data->regmap, VCNL_ISR, val: 0); |
425 | if (rc) |
426 | dev_err(data->dev, |
427 | "Error (%d) writing ISR register\n", rc); |
428 | |
429 | err_unlock: |
430 | mutex_unlock(lock: &data->lock); |
431 | |
432 | return rc; |
433 | } |
434 | |
435 | static int vcnl3020_config_threshold(struct iio_dev *indio_dev, bool state) |
436 | { |
437 | struct vcnl3020_data *data = iio_priv(indio_dev); |
438 | |
439 | if (state) { |
440 | return vcnl3020_enable_periodic(indio_dev, data); |
441 | } else { |
442 | if (!vcnl3020_is_thr_enabled(data)) |
443 | return 0; |
444 | return vcnl3020_disable_periodic(indio_dev, data); |
445 | } |
446 | } |
447 | |
448 | static int vcnl3020_write_event_config(struct iio_dev *indio_dev, |
449 | const struct iio_chan_spec *chan, |
450 | enum iio_event_type type, |
451 | enum iio_event_direction dir, |
452 | int state) |
453 | { |
454 | switch (chan->type) { |
455 | case IIO_PROXIMITY: |
456 | return vcnl3020_config_threshold(indio_dev, state); |
457 | default: |
458 | return -EINVAL; |
459 | } |
460 | } |
461 | |
462 | static int vcnl3020_read_event_config(struct iio_dev *indio_dev, |
463 | const struct iio_chan_spec *chan, |
464 | enum iio_event_type type, |
465 | enum iio_event_direction dir) |
466 | { |
467 | struct vcnl3020_data *data = iio_priv(indio_dev); |
468 | |
469 | switch (chan->type) { |
470 | case IIO_PROXIMITY: |
471 | return vcnl3020_is_thr_enabled(data); |
472 | default: |
473 | return -EINVAL; |
474 | } |
475 | } |
476 | |
477 | static const struct iio_event_spec vcnl3020_event_spec[] = { |
478 | { |
479 | .type = IIO_EV_TYPE_THRESH, |
480 | .dir = IIO_EV_DIR_RISING, |
481 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
482 | }, { |
483 | .type = IIO_EV_TYPE_THRESH, |
484 | .dir = IIO_EV_DIR_FALLING, |
485 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
486 | }, { |
487 | .type = IIO_EV_TYPE_THRESH, |
488 | .dir = IIO_EV_DIR_EITHER, |
489 | .mask_separate = BIT(IIO_EV_INFO_ENABLE), |
490 | }, |
491 | }; |
492 | |
493 | static const struct iio_chan_spec vcnl3020_channels[] = { |
494 | { |
495 | .type = IIO_PROXIMITY, |
496 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
497 | BIT(IIO_CHAN_INFO_SAMP_FREQ), |
498 | .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), |
499 | .event_spec = vcnl3020_event_spec, |
500 | .num_event_specs = ARRAY_SIZE(vcnl3020_event_spec), |
501 | }, |
502 | }; |
503 | |
504 | static int vcnl3020_read_raw(struct iio_dev *indio_dev, |
505 | struct iio_chan_spec const *chan, int *val, |
506 | int *val2, long mask) |
507 | { |
508 | int rc; |
509 | struct vcnl3020_data *data = iio_priv(indio_dev); |
510 | |
511 | switch (mask) { |
512 | case IIO_CHAN_INFO_RAW: |
513 | rc = vcnl3020_measure_proximity(data, val); |
514 | if (rc) |
515 | return rc; |
516 | return IIO_VAL_INT; |
517 | case IIO_CHAN_INFO_SAMP_FREQ: |
518 | rc = vcnl3020_read_proxy_samp_freq(data, val, val2); |
519 | if (rc < 0) |
520 | return rc; |
521 | return IIO_VAL_INT_PLUS_MICRO; |
522 | default: |
523 | return -EINVAL; |
524 | } |
525 | } |
526 | |
527 | static int vcnl3020_write_raw(struct iio_dev *indio_dev, |
528 | struct iio_chan_spec const *chan, |
529 | int val, int val2, long mask) |
530 | { |
531 | struct vcnl3020_data *data = iio_priv(indio_dev); |
532 | |
533 | switch (mask) { |
534 | case IIO_CHAN_INFO_SAMP_FREQ: |
535 | return vcnl3020_write_proxy_samp_freq(data, val, val2); |
536 | default: |
537 | return -EINVAL; |
538 | } |
539 | } |
540 | |
541 | static int vcnl3020_read_avail(struct iio_dev *indio_dev, |
542 | struct iio_chan_spec const *chan, |
543 | const int **vals, int *type, int *length, |
544 | long mask) |
545 | { |
546 | switch (mask) { |
547 | case IIO_CHAN_INFO_SAMP_FREQ: |
548 | *vals = (int *)vcnl3020_prox_sampling_frequency; |
549 | *type = IIO_VAL_INT_PLUS_MICRO; |
550 | *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency); |
551 | return IIO_AVAIL_LIST; |
552 | default: |
553 | return -EINVAL; |
554 | } |
555 | } |
556 | |
557 | static const struct iio_info vcnl3020_info = { |
558 | .read_raw = vcnl3020_read_raw, |
559 | .write_raw = vcnl3020_write_raw, |
560 | .read_avail = vcnl3020_read_avail, |
561 | .read_event_value = vcnl3020_read_event, |
562 | .write_event_value = vcnl3020_write_event, |
563 | .read_event_config = vcnl3020_read_event_config, |
564 | .write_event_config = vcnl3020_write_event_config, |
565 | }; |
566 | |
567 | static const struct regmap_config vcnl3020_regmap_config = { |
568 | .reg_bits = 8, |
569 | .val_bits = 8, |
570 | .max_register = VCNL_PS_MOD_ADJ, |
571 | }; |
572 | |
573 | static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p) |
574 | { |
575 | struct iio_dev *indio_dev = p; |
576 | struct vcnl3020_data *data = iio_priv(indio_dev); |
577 | unsigned int isr; |
578 | int rc; |
579 | |
580 | rc = regmap_read(map: data->regmap, VCNL_ISR, val: &isr); |
581 | if (rc) { |
582 | dev_err(data->dev, "Error (%d) reading reg (0x%x)\n", |
583 | rc, VCNL_ISR); |
584 | return IRQ_HANDLED; |
585 | } |
586 | |
587 | if (!(isr & VCNL_ICR_THRES_EN)) |
588 | return IRQ_NONE; |
589 | |
590 | iio_push_event(indio_dev, |
591 | IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, |
592 | IIO_EV_TYPE_THRESH, |
593 | IIO_EV_DIR_RISING), |
594 | timestamp: iio_get_time_ns(indio_dev)); |
595 | |
596 | rc = regmap_write(map: data->regmap, VCNL_ISR, val: isr & VCNL_ICR_THRES_EN); |
597 | if (rc) |
598 | dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n", |
599 | rc, VCNL_ISR); |
600 | |
601 | return IRQ_HANDLED; |
602 | } |
603 | |
604 | static int vcnl3020_probe(struct i2c_client *client) |
605 | { |
606 | struct vcnl3020_data *data; |
607 | struct iio_dev *indio_dev; |
608 | struct regmap *regmap; |
609 | int rc; |
610 | |
611 | regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config); |
612 | if (IS_ERR(ptr: regmap)) { |
613 | dev_err(&client->dev, "regmap_init failed\n"); |
614 | return PTR_ERR(ptr: regmap); |
615 | } |
616 | |
617 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
618 | if (!indio_dev) |
619 | return -ENOMEM; |
620 | |
621 | data = iio_priv(indio_dev); |
622 | i2c_set_clientdata(client, data: indio_dev); |
623 | data->regmap = regmap; |
624 | data->dev = &client->dev; |
625 | |
626 | rc = vcnl3020_init(data); |
627 | if (rc) |
628 | return rc; |
629 | |
630 | indio_dev->info = &vcnl3020_info; |
631 | indio_dev->channels = vcnl3020_channels; |
632 | indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels); |
633 | indio_dev->name = "vcnl3020"; |
634 | indio_dev->modes = INDIO_DIRECT_MODE; |
635 | |
636 | if (client->irq) { |
637 | rc = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
638 | NULL, thread_fn: vcnl3020_handle_irq_thread, |
639 | IRQF_ONESHOT, devname: indio_dev->name, |
640 | dev_id: indio_dev); |
641 | if (rc) { |
642 | dev_err(&client->dev, |
643 | "Error (%d) irq request failed (%u)\n", rc, |
644 | client->irq); |
645 | return rc; |
646 | } |
647 | } |
648 | |
649 | return devm_iio_device_register(&client->dev, indio_dev); |
650 | } |
651 | |
652 | static const struct of_device_id vcnl3020_of_match[] = { |
653 | { |
654 | .compatible = "vishay,vcnl3020", |
655 | }, |
656 | {} |
657 | }; |
658 | MODULE_DEVICE_TABLE(of, vcnl3020_of_match); |
659 | |
660 | static struct i2c_driver vcnl3020_driver = { |
661 | .driver = { |
662 | .name = "vcnl3020", |
663 | .of_match_table = vcnl3020_of_match, |
664 | }, |
665 | .probe = vcnl3020_probe, |
666 | }; |
667 | module_i2c_driver(vcnl3020_driver); |
668 | |
669 | MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>"); |
670 | MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver"); |
671 | MODULE_LICENSE("GPL"); |
672 |
Definitions
- vcnl3020_prox_sampling_frequency
- vcnl3020_data
- vcnl3020_property
- microamp_to_reg
- vcnl3020_led_current_property
- vcnl3020_get_and_apply_property
- vcnl3020_init
- vcnl3020_is_in_periodic_mode
- vcnl3020_measure_proximity
- vcnl3020_read_proxy_samp_freq
- vcnl3020_write_proxy_samp_freq
- vcnl3020_is_thr_enabled
- vcnl3020_read_event
- vcnl3020_write_event
- vcnl3020_enable_periodic
- vcnl3020_disable_periodic
- vcnl3020_config_threshold
- vcnl3020_write_event_config
- vcnl3020_read_event_config
- vcnl3020_event_spec
- vcnl3020_channels
- vcnl3020_read_raw
- vcnl3020_write_raw
- vcnl3020_read_avail
- vcnl3020_info
- vcnl3020_regmap_config
- vcnl3020_handle_irq_thread
- vcnl3020_probe
- vcnl3020_of_match
Improve your Profiling and Debugging skills
Find out more