xref: /linux/drivers/iio/proximity/vcnl3020.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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(data->dev, prop.name, &val);
118 	if (rc)
119 		return 0;
120 
121 	if (prop.conversion_func)
122 		prop.conversion_func(&val);
123 
124 	rc = regmap_write(data->regmap, 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(data->regmap, VCNL_PROD_REV, &reg);
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 					       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(data->regmap, VCNL_COMMAND, &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(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(data->regmap, VCNL_PS_RESULT_HI, &data->buf,
203 			      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(&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(data->regmap, VCNL_PROXIMITY_RATE, &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(data->regmap, VCNL_PROXIMITY_RATE, 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(&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(data->regmap, VCNL_PS_ICR, &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(data->regmap, VCNL_PS_HI_THR_HI,
303 					      &data->buf, 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(data->regmap, VCNL_PS_LO_THR_HI,
310 					      &data->buf, 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(data->regmap, VCNL_PS_HI_THR_HI,
342 					       &data->buf, 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(data->regmap, VCNL_PS_LO_THR_HI,
350 					       &data->buf, 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(&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(data->regmap, VCNL_COMMAND, 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(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(&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(data->regmap, VCNL_COMMAND, 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(data->regmap, VCNL_PS_ICR, 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(data->regmap, VCNL_ISR, 0);
425 	if (rc)
426 		dev_err(data->dev,
427 			"Error (%d) writing ISR register\n", rc);
428 
429 err_unlock:
430 	mutex_unlock(&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(data->regmap, VCNL_ISR, &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 		       iio_get_time_ns(indio_dev));
595 
596 	rc = regmap_write(data->regmap, VCNL_ISR, 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(regmap)) {
613 		dev_err(&client->dev, "regmap_init failed\n");
614 		return PTR_ERR(regmap);
615 	}
616 
617 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
618 	if (!indio_dev)
619 		return -ENOMEM;
620 
621 	data = iio_priv(indio_dev);
622 	i2c_set_clientdata(client, 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(&client->dev, client->irq,
638 					       NULL, vcnl3020_handle_irq_thread,
639 					       IRQF_ONESHOT, indio_dev->name,
640 					       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