Merge branch 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / iio / light / vcnl4000.c
CommitLineData
36edc939 1// SPDX-License-Identifier: GPL-2.0-only
62a1efb9 2/*
5a441aad 3 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient
d978bfdd 4 * light and proximity sensor
62a1efb9
PM
5 *
6 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
5a441aad 7 * Copyright 2019 Pursim SPC
62a1efb9 8 *
be38866f
TN
9 * IIO driver for:
10 * VCNL4000/10/20 (7-bit I2C slave address 0x13)
5a441aad 11 * VCNL4040 (7-bit I2C slave address 0x60)
be38866f 12 * VCNL4200 (7-bit I2C slave address 0x51)
62a1efb9
PM
13 *
14 * TODO:
15 * allow to adjust IR current
16 * proximity threshold and event handling
d978bfdd 17 * periodic ALS/proximity measurement (VCNL4010/20)
5a441aad 18 * interrupts (VCNL4010/20/40, VCNL4200)
62a1efb9
PM
19 */
20
21#include <linux/module.h>
22#include <linux/i2c.h>
23#include <linux/err.h>
24#include <linux/delay.h>
5e00708d 25#include <linux/pm_runtime.h>
62a1efb9
PM
26
27#include <linux/iio/iio.h>
28#include <linux/iio/sysfs.h>
29
30#define VCNL4000_DRV_NAME "vcnl4000"
1ebc787a
TN
31#define VCNL4000_PROD_ID 0x01
32#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
5a441aad 33#define VCNL4040_PROD_ID 0x86
be38866f 34#define VCNL4200_PROD_ID 0x58
62a1efb9
PM
35
36#define VCNL4000_COMMAND 0x80 /* Command register */
37#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
38#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
39#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
40#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
41#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
42#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
43#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
44#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
45#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
46
be38866f
TN
47#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
48#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
49#define VCNL4200_PS_DATA 0x08 /* Proximity data */
50#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
51#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
52
5a441aad
AAP
53#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
54
62a1efb9 55/* Bit masks for COMMAND register */
ff6a5259
PMS
56#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
57#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
58#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
59#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
62a1efb9 60
5e00708d
GG
61#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
62
1ebc787a
TN
63enum vcnl4000_device_ids {
64 VCNL4000,
50c50b97 65 VCNL4010,
5a441aad 66 VCNL4040,
be38866f
TN
67 VCNL4200,
68};
69
70struct vcnl4200_channel {
71 u8 reg;
72 ktime_t last_measurement;
73 ktime_t sampling_rate;
74 struct mutex lock;
1ebc787a
TN
75};
76
62a1efb9
PM
77struct vcnl4000_data {
78 struct i2c_client *client;
1ebc787a
TN
79 enum vcnl4000_device_ids id;
80 int rev;
81 int al_scale;
82 const struct vcnl4000_chip_spec *chip_spec;
be38866f
TN
83 struct mutex vcnl4000_lock;
84 struct vcnl4200_channel vcnl4200_al;
85 struct vcnl4200_channel vcnl4200_ps;
62a1efb9
PM
86};
87
1ebc787a
TN
88struct vcnl4000_chip_spec {
89 const char *prod;
90 int (*init)(struct vcnl4000_data *data);
91 int (*measure_light)(struct vcnl4000_data *data, int *val);
92 int (*measure_proximity)(struct vcnl4000_data *data, int *val);
5e00708d 93 int (*set_power_state)(struct vcnl4000_data *data, bool on);
1ebc787a
TN
94};
95
62a1efb9 96static const struct i2c_device_id vcnl4000_id[] = {
1ebc787a 97 { "vcnl4000", VCNL4000 },
50c50b97
TN
98 { "vcnl4010", VCNL4010 },
99 { "vcnl4020", VCNL4010 },
5a441aad 100 { "vcnl4040", VCNL4040 },
be38866f 101 { "vcnl4200", VCNL4200 },
62a1efb9
PM
102 { }
103};
104MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
105
5e00708d
GG
106static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
107{
108 /* no suspend op */
109 return 0;
110}
111
1ebc787a
TN
112static int vcnl4000_init(struct vcnl4000_data *data)
113{
114 int ret, prod_id;
115
116 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
117 if (ret < 0)
118 return ret;
119
120 prod_id = ret >> 4;
58bf9ace
TN
121 switch (prod_id) {
122 case VCNL4000_PROD_ID:
123 if (data->id != VCNL4000)
124 dev_warn(&data->client->dev,
125 "wrong device id, use vcnl4000");
126 break;
127 case VCNL4010_PROD_ID:
128 if (data->id != VCNL4010)
129 dev_warn(&data->client->dev,
130 "wrong device id, use vcnl4010/4020");
131 break;
132 default:
1ebc787a 133 return -ENODEV;
58bf9ace 134 }
1ebc787a
TN
135
136 data->rev = ret & 0xf;
137 data->al_scale = 250000;
be38866f
TN
138 mutex_init(&data->vcnl4000_lock);
139
5e00708d 140 return data->chip_spec->set_power_state(data, true);
be38866f
TN
141};
142
5e00708d
GG
143static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
144{
145 u16 val = on ? 0 /* power on */ : 1 /* shut down */;
146 int ret;
147
148 ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
149 if (ret < 0)
150 return ret;
151
152 ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
153 if (ret < 0)
154 return ret;
155
156 if (on) {
157 /* Wait at least one integration cycle before fetching data */
158 data->vcnl4200_al.last_measurement = ktime_get();
159 data->vcnl4200_ps.last_measurement = ktime_get();
160 }
161
162 return 0;
163}
164
be38866f
TN
165static int vcnl4200_init(struct vcnl4000_data *data)
166{
5a441aad 167 int ret, id;
be38866f
TN
168
169 ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
170 if (ret < 0)
171 return ret;
172
5a441aad
AAP
173 id = ret & 0xff;
174
175 if (id != VCNL4200_PROD_ID) {
176 ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
177 if (ret < 0)
178 return ret;
179
180 id = ret & 0xff;
181
182 if (id != VCNL4040_PROD_ID)
183 return -ENODEV;
184 }
185
186 dev_dbg(&data->client->dev, "device id 0x%x", id);
be38866f
TN
187
188 data->rev = (ret >> 8) & 0xf;
189
be38866f
TN
190 data->vcnl4200_al.reg = VCNL4200_AL_DATA;
191 data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
5a441aad
AAP
192 switch (id) {
193 case VCNL4200_PROD_ID:
b42aa97e
TN
194 /* Default wait time is 50ms, add 20% tolerance. */
195 data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
196 /* Default wait time is 4.8ms, add 20% tolerance. */
197 data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
bc80573e 198 data->al_scale = 24000;
5a441aad
AAP
199 break;
200 case VCNL4040_PROD_ID:
2ca5a879
TN
201 /* Default wait time is 80ms, add 20% tolerance. */
202 data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
203 /* Default wait time is 5ms, add 20% tolerance. */
204 data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
bc80573e 205 data->al_scale = 120000;
5a441aad
AAP
206 break;
207 }
be38866f
TN
208 mutex_init(&data->vcnl4200_al.lock);
209 mutex_init(&data->vcnl4200_ps.lock);
1ebc787a 210
5e00708d
GG
211 ret = data->chip_spec->set_power_state(data, true);
212 if (ret < 0)
213 return ret;
214
1ebc787a
TN
215 return 0;
216};
217
62a1efb9
PM
218static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
219 u8 rdy_mask, u8 data_reg, int *val)
220{
221 int tries = 20;
91f197e0 222 __be16 buf;
62a1efb9
PM
223 int ret;
224
be38866f 225 mutex_lock(&data->vcnl4000_lock);
ff34ed6d 226
62a1efb9
PM
227 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
228 req_mask);
229 if (ret < 0)
ff34ed6d 230 goto fail;
62a1efb9
PM
231
232 /* wait for data to become ready */
233 while (tries--) {
234 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
235 if (ret < 0)
ff34ed6d 236 goto fail;
62a1efb9
PM
237 if (ret & rdy_mask)
238 break;
239 msleep(20); /* measurement takes up to 100 ms */
240 }
241
242 if (tries < 0) {
243 dev_err(&data->client->dev,
244 "vcnl4000_measure() failed, data not ready\n");
ff34ed6d
PMS
245 ret = -EIO;
246 goto fail;
62a1efb9
PM
247 }
248
249 ret = i2c_smbus_read_i2c_block_data(data->client,
250 data_reg, sizeof(buf), (u8 *) &buf);
251 if (ret < 0)
ff34ed6d 252 goto fail;
62a1efb9 253
be38866f 254 mutex_unlock(&data->vcnl4000_lock);
62a1efb9
PM
255 *val = be16_to_cpu(buf);
256
257 return 0;
ff34ed6d
PMS
258
259fail:
be38866f 260 mutex_unlock(&data->vcnl4000_lock);
ff34ed6d 261 return ret;
62a1efb9
PM
262}
263
be38866f
TN
264static int vcnl4200_measure(struct vcnl4000_data *data,
265 struct vcnl4200_channel *chan, int *val)
266{
267 int ret;
268 s64 delta;
269 ktime_t next_measurement;
270
271 mutex_lock(&chan->lock);
272
273 next_measurement = ktime_add(chan->last_measurement,
274 chan->sampling_rate);
275 delta = ktime_us_delta(next_measurement, ktime_get());
276 if (delta > 0)
277 usleep_range(delta, delta + 500);
278 chan->last_measurement = ktime_get();
279
280 mutex_unlock(&chan->lock);
281
282 ret = i2c_smbus_read_word_data(data->client, chan->reg);
283 if (ret < 0)
284 return ret;
285
286 *val = ret;
287
288 return 0;
289}
290
1ebc787a
TN
291static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
292{
293 return vcnl4000_measure(data,
294 VCNL4000_AL_OD, VCNL4000_AL_RDY,
295 VCNL4000_AL_RESULT_HI, val);
296}
297
be38866f
TN
298static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
299{
300 return vcnl4200_measure(data, &data->vcnl4200_al, val);
301}
302
1ebc787a
TN
303static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
304{
305 return vcnl4000_measure(data,
306 VCNL4000_PS_OD, VCNL4000_PS_RDY,
307 VCNL4000_PS_RESULT_HI, val);
308}
309
be38866f
TN
310static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
311{
312 return vcnl4200_measure(data, &data->vcnl4200_ps, val);
313}
314
1ebc787a
TN
315static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
316 [VCNL4000] = {
317 .prod = "VCNL4000",
318 .init = vcnl4000_init,
319 .measure_light = vcnl4000_measure_light,
320 .measure_proximity = vcnl4000_measure_proximity,
5e00708d 321 .set_power_state = vcnl4000_set_power_state,
1ebc787a 322 },
50c50b97
TN
323 [VCNL4010] = {
324 .prod = "VCNL4010/4020",
325 .init = vcnl4000_init,
326 .measure_light = vcnl4000_measure_light,
327 .measure_proximity = vcnl4000_measure_proximity,
5e00708d 328 .set_power_state = vcnl4000_set_power_state,
50c50b97 329 },
5a441aad
AAP
330 [VCNL4040] = {
331 .prod = "VCNL4040",
332 .init = vcnl4200_init,
333 .measure_light = vcnl4200_measure_light,
334 .measure_proximity = vcnl4200_measure_proximity,
5e00708d 335 .set_power_state = vcnl4200_set_power_state,
5a441aad 336 },
be38866f
TN
337 [VCNL4200] = {
338 .prod = "VCNL4200",
339 .init = vcnl4200_init,
340 .measure_light = vcnl4200_measure_light,
341 .measure_proximity = vcnl4200_measure_proximity,
5e00708d 342 .set_power_state = vcnl4200_set_power_state,
be38866f 343 },
1ebc787a
TN
344};
345
62a1efb9
PM
346static const struct iio_chan_spec vcnl4000_channels[] = {
347 {
348 .type = IIO_LIGHT,
bb7c5940
JC
349 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
350 BIT(IIO_CHAN_INFO_SCALE),
62a1efb9
PM
351 }, {
352 .type = IIO_PROXIMITY,
bb7c5940 353 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
62a1efb9
PM
354 }
355};
356
5e00708d
GG
357static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
358{
359 struct device *dev = &data->client->dev;
360 int ret;
361
362 if (on) {
363 ret = pm_runtime_get_sync(dev);
364 if (ret < 0)
365 pm_runtime_put_noidle(dev);
366 } else {
367 pm_runtime_mark_last_busy(dev);
368 ret = pm_runtime_put_autosuspend(dev);
369 }
370
371 return ret;
372}
373
62a1efb9
PM
374static int vcnl4000_read_raw(struct iio_dev *indio_dev,
375 struct iio_chan_spec const *chan,
376 int *val, int *val2, long mask)
377{
5d693139 378 int ret;
62a1efb9
PM
379 struct vcnl4000_data *data = iio_priv(indio_dev);
380
381 switch (mask) {
382 case IIO_CHAN_INFO_RAW:
5e00708d
GG
383 ret = vcnl4000_set_pm_runtime_state(data, true);
384 if (ret < 0)
385 return ret;
386
62a1efb9
PM
387 switch (chan->type) {
388 case IIO_LIGHT:
1ebc787a 389 ret = data->chip_spec->measure_light(data, val);
4a818643
GG
390 if (!ret)
391 ret = IIO_VAL_INT;
392 break;
62a1efb9 393 case IIO_PROXIMITY:
1ebc787a 394 ret = data->chip_spec->measure_proximity(data, val);
4a818643
GG
395 if (!ret)
396 ret = IIO_VAL_INT;
397 break;
62a1efb9 398 default:
4a818643 399 ret = -EINVAL;
62a1efb9 400 }
5e00708d 401 vcnl4000_set_pm_runtime_state(data, false);
4a818643 402 return ret;
62a1efb9 403 case IIO_CHAN_INFO_SCALE:
5d693139
PMS
404 if (chan->type != IIO_LIGHT)
405 return -EINVAL;
406
407 *val = 0;
1ebc787a 408 *val2 = data->al_scale;
5d693139 409 return IIO_VAL_INT_PLUS_MICRO;
62a1efb9 410 default:
5d693139 411 return -EINVAL;
62a1efb9 412 }
62a1efb9
PM
413}
414
415static const struct iio_info vcnl4000_info = {
416 .read_raw = vcnl4000_read_raw,
62a1efb9
PM
417};
418
fc52692c
GKH
419static int vcnl4000_probe(struct i2c_client *client,
420 const struct i2c_device_id *id)
62a1efb9
PM
421{
422 struct vcnl4000_data *data;
423 struct iio_dev *indio_dev;
1ebc787a 424 int ret;
62a1efb9 425
2669d723 426 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
62a1efb9
PM
427 if (!indio_dev)
428 return -ENOMEM;
429
430 data = iio_priv(indio_dev);
431 i2c_set_clientdata(client, indio_dev);
432 data->client = client;
1ebc787a
TN
433 data->id = id->driver_data;
434 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
62a1efb9 435
1ebc787a 436 ret = data->chip_spec->init(data);
62a1efb9 437 if (ret < 0)
2669d723 438 return ret;
62a1efb9 439
d978bfdd 440 dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
1ebc787a 441 data->chip_spec->prod, data->rev);
62a1efb9
PM
442
443 indio_dev->dev.parent = &client->dev;
444 indio_dev->info = &vcnl4000_info;
445 indio_dev->channels = vcnl4000_channels;
446 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
447 indio_dev->name = VCNL4000_DRV_NAME;
448 indio_dev->modes = INDIO_DIRECT_MODE;
449
5e00708d
GG
450 ret = pm_runtime_set_active(&client->dev);
451 if (ret < 0)
452 goto fail_poweroff;
453
454 ret = iio_device_register(indio_dev);
455 if (ret < 0)
456 goto fail_poweroff;
457
458 pm_runtime_enable(&client->dev);
459 pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
460 pm_runtime_use_autosuspend(&client->dev);
461
462 return 0;
463fail_poweroff:
464 data->chip_spec->set_power_state(data, false);
465 return ret;
62a1efb9
PM
466}
467
ebd457d5
AAP
468static const struct of_device_id vcnl_4000_of_match[] = {
469 {
470 .compatible = "vishay,vcnl4000",
1436a78c 471 .data = (void *)VCNL4000,
ebd457d5
AAP
472 },
473 {
474 .compatible = "vishay,vcnl4010",
1436a78c 475 .data = (void *)VCNL4010,
ebd457d5
AAP
476 },
477 {
1436a78c
MF
478 .compatible = "vishay,vcnl4020",
479 .data = (void *)VCNL4010,
ebd457d5 480 },
7fd1c260
MF
481 {
482 .compatible = "vishay,vcnl4040",
483 .data = (void *)VCNL4040,
484 },
ebd457d5
AAP
485 {
486 .compatible = "vishay,vcnl4200",
1436a78c 487 .data = (void *)VCNL4200,
ebd457d5
AAP
488 },
489 {},
490};
491MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
492
5e00708d
GG
493static int vcnl4000_remove(struct i2c_client *client)
494{
495 struct iio_dev *indio_dev = i2c_get_clientdata(client);
496 struct vcnl4000_data *data = iio_priv(indio_dev);
497
498 pm_runtime_dont_use_autosuspend(&client->dev);
499 pm_runtime_disable(&client->dev);
500 iio_device_unregister(indio_dev);
501 pm_runtime_set_suspended(&client->dev);
502
503 return data->chip_spec->set_power_state(data, false);
504}
505
506static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
507{
508 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
509 struct vcnl4000_data *data = iio_priv(indio_dev);
510
511 return data->chip_spec->set_power_state(data, false);
512}
513
514static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
515{
516 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
517 struct vcnl4000_data *data = iio_priv(indio_dev);
518
519 return data->chip_spec->set_power_state(data, true);
520}
521
522static const struct dev_pm_ops vcnl4000_pm_ops = {
523 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
524 pm_runtime_force_resume)
525 SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
526 vcnl4000_runtime_resume, NULL)
527};
528
62a1efb9
PM
529static struct i2c_driver vcnl4000_driver = {
530 .driver = {
531 .name = VCNL4000_DRV_NAME,
5e00708d 532 .pm = &vcnl4000_pm_ops,
ebd457d5 533 .of_match_table = vcnl_4000_of_match,
62a1efb9
PM
534 },
535 .probe = vcnl4000_probe,
62a1efb9 536 .id_table = vcnl4000_id,
5e00708d 537 .remove = vcnl4000_remove,
62a1efb9
PM
538};
539
540module_i2c_driver(vcnl4000_driver);
541
542MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
543MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
544MODULE_LICENSE("GPL");