dm writecache: add cond_resched to loop in persistent_memory_claim()
[linux/fpc-iii.git] / drivers / iio / light / vcnl4000.c
blob5d476f174a901f776331c76a01d4b19e6fca8842
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient
4 * light and proximity sensor
6 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
7 * Copyright 2019 Pursim SPC
9 * IIO driver for:
10 * VCNL4000/10/20 (7-bit I2C slave address 0x13)
11 * VCNL4040 (7-bit I2C slave address 0x60)
12 * VCNL4200 (7-bit I2C slave address 0x51)
14 * TODO:
15 * allow to adjust IR current
16 * proximity threshold and event handling
17 * periodic ALS/proximity measurement (VCNL4010/20)
18 * interrupts (VCNL4010/20/40, VCNL4200)
21 #include <linux/module.h>
22 #include <linux/i2c.h>
23 #include <linux/err.h>
24 #include <linux/delay.h>
25 #include <linux/pm_runtime.h>
27 #include <linux/iio/iio.h>
28 #include <linux/iio/sysfs.h>
30 #define VCNL4000_DRV_NAME "vcnl4000"
31 #define VCNL4000_PROD_ID 0x01
32 #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
33 #define VCNL4040_PROD_ID 0x86
34 #define VCNL4200_PROD_ID 0x58
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 */
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 */
53 #define VCNL4040_DEV_ID 0x0c /* Device ID and version */
55 /* Bit masks for COMMAND register */
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 */
61 #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
63 enum vcnl4000_device_ids {
64 VCNL4000,
65 VCNL4010,
66 VCNL4040,
67 VCNL4200,
70 struct vcnl4200_channel {
71 u8 reg;
72 ktime_t last_measurement;
73 ktime_t sampling_rate;
74 struct mutex lock;
77 struct vcnl4000_data {
78 struct i2c_client *client;
79 enum vcnl4000_device_ids id;
80 int rev;
81 int al_scale;
82 const struct vcnl4000_chip_spec *chip_spec;
83 struct mutex vcnl4000_lock;
84 struct vcnl4200_channel vcnl4200_al;
85 struct vcnl4200_channel vcnl4200_ps;
88 struct 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);
93 int (*set_power_state)(struct vcnl4000_data *data, bool on);
96 static const struct i2c_device_id vcnl4000_id[] = {
97 { "vcnl4000", VCNL4000 },
98 { "vcnl4010", VCNL4010 },
99 { "vcnl4020", VCNL4010 },
100 { "vcnl4040", VCNL4040 },
101 { "vcnl4200", VCNL4200 },
104 MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
106 static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
108 /* no suspend op */
109 return 0;
112 static int vcnl4000_init(struct vcnl4000_data *data)
114 int ret, prod_id;
116 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
117 if (ret < 0)
118 return ret;
120 prod_id = ret >> 4;
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:
133 return -ENODEV;
136 data->rev = ret & 0xf;
137 data->al_scale = 250000;
138 mutex_init(&data->vcnl4000_lock);
140 return data->chip_spec->set_power_state(data, true);
143 static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
145 u16 val = on ? 0 /* power on */ : 1 /* shut down */;
146 int ret;
148 ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
149 if (ret < 0)
150 return ret;
152 ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
153 if (ret < 0)
154 return ret;
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();
162 return 0;
165 static int vcnl4200_init(struct vcnl4000_data *data)
167 int ret, id;
169 ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
170 if (ret < 0)
171 return ret;
173 id = ret & 0xff;
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;
180 id = ret & 0xff;
182 if (id != VCNL4040_PROD_ID)
183 return -ENODEV;
186 dev_dbg(&data->client->dev, "device id 0x%x", id);
188 data->rev = (ret >> 8) & 0xf;
190 data->vcnl4200_al.reg = VCNL4200_AL_DATA;
191 data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
192 switch (id) {
193 case VCNL4200_PROD_ID:
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);
198 data->al_scale = 24000;
199 break;
200 case VCNL4040_PROD_ID:
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);
205 data->al_scale = 120000;
206 break;
208 mutex_init(&data->vcnl4200_al.lock);
209 mutex_init(&data->vcnl4200_ps.lock);
211 ret = data->chip_spec->set_power_state(data, true);
212 if (ret < 0)
213 return ret;
215 return 0;
218 static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
219 u8 rdy_mask, u8 data_reg, int *val)
221 int tries = 20;
222 int ret;
224 mutex_lock(&data->vcnl4000_lock);
226 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
227 req_mask);
228 if (ret < 0)
229 goto fail;
231 /* wait for data to become ready */
232 while (tries--) {
233 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
234 if (ret < 0)
235 goto fail;
236 if (ret & rdy_mask)
237 break;
238 msleep(20); /* measurement takes up to 100 ms */
241 if (tries < 0) {
242 dev_err(&data->client->dev,
243 "vcnl4000_measure() failed, data not ready\n");
244 ret = -EIO;
245 goto fail;
248 ret = i2c_smbus_read_word_swapped(data->client, data_reg);
249 if (ret < 0)
250 goto fail;
252 mutex_unlock(&data->vcnl4000_lock);
253 *val = ret;
255 return 0;
257 fail:
258 mutex_unlock(&data->vcnl4000_lock);
259 return ret;
262 static int vcnl4200_measure(struct vcnl4000_data *data,
263 struct vcnl4200_channel *chan, int *val)
265 int ret;
266 s64 delta;
267 ktime_t next_measurement;
269 mutex_lock(&chan->lock);
271 next_measurement = ktime_add(chan->last_measurement,
272 chan->sampling_rate);
273 delta = ktime_us_delta(next_measurement, ktime_get());
274 if (delta > 0)
275 usleep_range(delta, delta + 500);
276 chan->last_measurement = ktime_get();
278 mutex_unlock(&chan->lock);
280 ret = i2c_smbus_read_word_data(data->client, chan->reg);
281 if (ret < 0)
282 return ret;
284 *val = ret;
286 return 0;
289 static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
291 return vcnl4000_measure(data,
292 VCNL4000_AL_OD, VCNL4000_AL_RDY,
293 VCNL4000_AL_RESULT_HI, val);
296 static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
298 return vcnl4200_measure(data, &data->vcnl4200_al, val);
301 static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
303 return vcnl4000_measure(data,
304 VCNL4000_PS_OD, VCNL4000_PS_RDY,
305 VCNL4000_PS_RESULT_HI, val);
308 static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
310 return vcnl4200_measure(data, &data->vcnl4200_ps, val);
313 static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
314 [VCNL4000] = {
315 .prod = "VCNL4000",
316 .init = vcnl4000_init,
317 .measure_light = vcnl4000_measure_light,
318 .measure_proximity = vcnl4000_measure_proximity,
319 .set_power_state = vcnl4000_set_power_state,
321 [VCNL4010] = {
322 .prod = "VCNL4010/4020",
323 .init = vcnl4000_init,
324 .measure_light = vcnl4000_measure_light,
325 .measure_proximity = vcnl4000_measure_proximity,
326 .set_power_state = vcnl4000_set_power_state,
328 [VCNL4040] = {
329 .prod = "VCNL4040",
330 .init = vcnl4200_init,
331 .measure_light = vcnl4200_measure_light,
332 .measure_proximity = vcnl4200_measure_proximity,
333 .set_power_state = vcnl4200_set_power_state,
335 [VCNL4200] = {
336 .prod = "VCNL4200",
337 .init = vcnl4200_init,
338 .measure_light = vcnl4200_measure_light,
339 .measure_proximity = vcnl4200_measure_proximity,
340 .set_power_state = vcnl4200_set_power_state,
344 static const struct iio_chan_spec vcnl4000_channels[] = {
346 .type = IIO_LIGHT,
347 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
348 BIT(IIO_CHAN_INFO_SCALE),
349 }, {
350 .type = IIO_PROXIMITY,
351 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
355 static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
357 struct device *dev = &data->client->dev;
358 int ret;
360 if (on) {
361 ret = pm_runtime_get_sync(dev);
362 if (ret < 0)
363 pm_runtime_put_noidle(dev);
364 } else {
365 pm_runtime_mark_last_busy(dev);
366 ret = pm_runtime_put_autosuspend(dev);
369 return ret;
372 static int vcnl4000_read_raw(struct iio_dev *indio_dev,
373 struct iio_chan_spec const *chan,
374 int *val, int *val2, long mask)
376 int ret;
377 struct vcnl4000_data *data = iio_priv(indio_dev);
379 switch (mask) {
380 case IIO_CHAN_INFO_RAW:
381 ret = vcnl4000_set_pm_runtime_state(data, true);
382 if (ret < 0)
383 return ret;
385 switch (chan->type) {
386 case IIO_LIGHT:
387 ret = data->chip_spec->measure_light(data, val);
388 if (!ret)
389 ret = IIO_VAL_INT;
390 break;
391 case IIO_PROXIMITY:
392 ret = data->chip_spec->measure_proximity(data, val);
393 if (!ret)
394 ret = IIO_VAL_INT;
395 break;
396 default:
397 ret = -EINVAL;
399 vcnl4000_set_pm_runtime_state(data, false);
400 return ret;
401 case IIO_CHAN_INFO_SCALE:
402 if (chan->type != IIO_LIGHT)
403 return -EINVAL;
405 *val = 0;
406 *val2 = data->al_scale;
407 return IIO_VAL_INT_PLUS_MICRO;
408 default:
409 return -EINVAL;
413 static const struct iio_info vcnl4000_info = {
414 .read_raw = vcnl4000_read_raw,
417 static int vcnl4000_probe(struct i2c_client *client,
418 const struct i2c_device_id *id)
420 struct vcnl4000_data *data;
421 struct iio_dev *indio_dev;
422 int ret;
424 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
425 if (!indio_dev)
426 return -ENOMEM;
428 data = iio_priv(indio_dev);
429 i2c_set_clientdata(client, indio_dev);
430 data->client = client;
431 data->id = id->driver_data;
432 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
434 ret = data->chip_spec->init(data);
435 if (ret < 0)
436 return ret;
438 dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
439 data->chip_spec->prod, data->rev);
441 indio_dev->dev.parent = &client->dev;
442 indio_dev->info = &vcnl4000_info;
443 indio_dev->channels = vcnl4000_channels;
444 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
445 indio_dev->name = VCNL4000_DRV_NAME;
446 indio_dev->modes = INDIO_DIRECT_MODE;
448 ret = pm_runtime_set_active(&client->dev);
449 if (ret < 0)
450 goto fail_poweroff;
452 ret = iio_device_register(indio_dev);
453 if (ret < 0)
454 goto fail_poweroff;
456 pm_runtime_enable(&client->dev);
457 pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
458 pm_runtime_use_autosuspend(&client->dev);
460 return 0;
461 fail_poweroff:
462 data->chip_spec->set_power_state(data, false);
463 return ret;
466 static const struct of_device_id vcnl_4000_of_match[] = {
468 .compatible = "vishay,vcnl4000",
469 .data = (void *)VCNL4000,
472 .compatible = "vishay,vcnl4010",
473 .data = (void *)VCNL4010,
476 .compatible = "vishay,vcnl4020",
477 .data = (void *)VCNL4010,
480 .compatible = "vishay,vcnl4040",
481 .data = (void *)VCNL4040,
484 .compatible = "vishay,vcnl4200",
485 .data = (void *)VCNL4200,
489 MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
491 static int vcnl4000_remove(struct i2c_client *client)
493 struct iio_dev *indio_dev = i2c_get_clientdata(client);
494 struct vcnl4000_data *data = iio_priv(indio_dev);
496 pm_runtime_dont_use_autosuspend(&client->dev);
497 pm_runtime_disable(&client->dev);
498 iio_device_unregister(indio_dev);
499 pm_runtime_set_suspended(&client->dev);
501 return data->chip_spec->set_power_state(data, false);
504 static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
506 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
507 struct vcnl4000_data *data = iio_priv(indio_dev);
509 return data->chip_spec->set_power_state(data, false);
512 static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
514 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
515 struct vcnl4000_data *data = iio_priv(indio_dev);
517 return data->chip_spec->set_power_state(data, true);
520 static const struct dev_pm_ops vcnl4000_pm_ops = {
521 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
522 pm_runtime_force_resume)
523 SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
524 vcnl4000_runtime_resume, NULL)
527 static struct i2c_driver vcnl4000_driver = {
528 .driver = {
529 .name = VCNL4000_DRV_NAME,
530 .pm = &vcnl4000_pm_ops,
531 .of_match_table = vcnl_4000_of_match,
533 .probe = vcnl4000_probe,
534 .id_table = vcnl4000_id,
535 .remove = vcnl4000_remove,
538 module_i2c_driver(vcnl4000_driver);
540 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
541 MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
542 MODULE_LICENSE("GPL");