xref: /linux/drivers/iio/light/vcnl4000.c (revision 816956c32d76b7c4fb932f9a5ebaa38ccf4fa62e)
136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
262a1efb9SPeter Meerwald /*
35a441aadSAngus Ainslie (Purism)  * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient
4d978bfddSPeter Meerwald-Stadler  * light and proximity sensor
562a1efb9SPeter Meerwald  *
662a1efb9SPeter Meerwald  * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
75a441aadSAngus Ainslie (Purism)  * Copyright 2019 Pursim SPC
862a1efb9SPeter Meerwald  *
9be38866fSTomas Novotny  * IIO driver for:
10be38866fSTomas Novotny  *   VCNL4000/10/20 (7-bit I2C slave address 0x13)
115a441aadSAngus Ainslie (Purism)  *   VCNL4040 (7-bit I2C slave address 0x60)
12be38866fSTomas Novotny  *   VCNL4200 (7-bit I2C slave address 0x51)
1362a1efb9SPeter Meerwald  *
1462a1efb9SPeter Meerwald  * TODO:
1562a1efb9SPeter Meerwald  *   allow to adjust IR current
1662a1efb9SPeter Meerwald  *   proximity threshold and event handling
17d978bfddSPeter Meerwald-Stadler  *   periodic ALS/proximity measurement (VCNL4010/20)
185a441aadSAngus Ainslie (Purism)  *   interrupts (VCNL4010/20/40, VCNL4200)
1962a1efb9SPeter Meerwald  */
2062a1efb9SPeter Meerwald 
2162a1efb9SPeter Meerwald #include <linux/module.h>
2262a1efb9SPeter Meerwald #include <linux/i2c.h>
2362a1efb9SPeter Meerwald #include <linux/err.h>
2462a1efb9SPeter Meerwald #include <linux/delay.h>
255e00708dSGuido Günther #include <linux/pm_runtime.h>
2662a1efb9SPeter Meerwald 
2762a1efb9SPeter Meerwald #include <linux/iio/iio.h>
2862a1efb9SPeter Meerwald #include <linux/iio/sysfs.h>
2962a1efb9SPeter Meerwald 
3062a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000"
311ebc787aSTomas Novotny #define VCNL4000_PROD_ID	0x01
321ebc787aSTomas Novotny #define VCNL4010_PROD_ID	0x02 /* for VCNL4020, VCNL4010 */
335a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID	0x86
34be38866fSTomas Novotny #define VCNL4200_PROD_ID	0x58
3562a1efb9SPeter Meerwald 
3662a1efb9SPeter Meerwald #define VCNL4000_COMMAND	0x80 /* Command register */
3762a1efb9SPeter Meerwald #define VCNL4000_PROD_REV	0x81 /* Product ID and Revision ID */
3862a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT	0x83 /* IR LED current for proximity mode */
3962a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM	0x84 /* Ambient light parameter register */
4062a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI	0x85 /* Ambient light result register, MSB */
4162a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO	0x86 /* Ambient light result register, LSB */
4262a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI	0x87 /* Proximity result register, MSB */
4362a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO	0x88 /* Proximity result register, LSB */
4462a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ	0x89 /* Proximity test signal frequency */
4562a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ	0x8a /* Proximity modulator timing adjustment */
4662a1efb9SPeter Meerwald 
47be38866fSTomas Novotny #define VCNL4200_AL_CONF	0x00 /* Ambient light configuration */
48be38866fSTomas Novotny #define VCNL4200_PS_CONF1	0x03 /* Proximity configuration */
49be38866fSTomas Novotny #define VCNL4200_PS_DATA	0x08 /* Proximity data */
50be38866fSTomas Novotny #define VCNL4200_AL_DATA	0x09 /* Ambient light data */
51be38866fSTomas Novotny #define VCNL4200_DEV_ID		0x0e /* Device ID, slave address and version */
52be38866fSTomas Novotny 
535a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID		0x0c /* Device ID and version */
545a441aadSAngus Ainslie (Purism) 
5562a1efb9SPeter Meerwald /* Bit masks for COMMAND register */
56ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY		BIT(6) /* ALS data ready? */
57ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY		BIT(5) /* proximity data ready? */
58ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD		BIT(4) /* start on-demand ALS measurement */
59ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD		BIT(3) /* start on-demand proximity measurement */
6062a1efb9SPeter Meerwald 
615e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS	2000 /* before we enter pm_runtime_suspend */
625e00708dSGuido Günther 
631ebc787aSTomas Novotny enum vcnl4000_device_ids {
641ebc787aSTomas Novotny 	VCNL4000,
6550c50b97STomas Novotny 	VCNL4010,
665a441aadSAngus Ainslie (Purism) 	VCNL4040,
67be38866fSTomas Novotny 	VCNL4200,
68be38866fSTomas Novotny };
69be38866fSTomas Novotny 
70be38866fSTomas Novotny struct vcnl4200_channel {
71be38866fSTomas Novotny 	u8 reg;
72be38866fSTomas Novotny 	ktime_t last_measurement;
73be38866fSTomas Novotny 	ktime_t sampling_rate;
74be38866fSTomas Novotny 	struct mutex lock;
751ebc787aSTomas Novotny };
761ebc787aSTomas Novotny 
7762a1efb9SPeter Meerwald struct vcnl4000_data {
7862a1efb9SPeter Meerwald 	struct i2c_client *client;
791ebc787aSTomas Novotny 	enum vcnl4000_device_ids id;
801ebc787aSTomas Novotny 	int rev;
811ebc787aSTomas Novotny 	int al_scale;
821ebc787aSTomas Novotny 	const struct vcnl4000_chip_spec *chip_spec;
83be38866fSTomas Novotny 	struct mutex vcnl4000_lock;
84be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_al;
85be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_ps;
86f5a98e1fSGuido Günther 	uint32_t near_level;
8762a1efb9SPeter Meerwald };
8862a1efb9SPeter Meerwald 
891ebc787aSTomas Novotny struct vcnl4000_chip_spec {
901ebc787aSTomas Novotny 	const char *prod;
911ebc787aSTomas Novotny 	int (*init)(struct vcnl4000_data *data);
921ebc787aSTomas Novotny 	int (*measure_light)(struct vcnl4000_data *data, int *val);
931ebc787aSTomas Novotny 	int (*measure_proximity)(struct vcnl4000_data *data, int *val);
945e00708dSGuido Günther 	int (*set_power_state)(struct vcnl4000_data *data, bool on);
951ebc787aSTomas Novotny };
961ebc787aSTomas Novotny 
9762a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = {
981ebc787aSTomas Novotny 	{ "vcnl4000", VCNL4000 },
9950c50b97STomas Novotny 	{ "vcnl4010", VCNL4010 },
10050c50b97STomas Novotny 	{ "vcnl4020", VCNL4010 },
1015a441aadSAngus Ainslie (Purism) 	{ "vcnl4040", VCNL4040 },
102be38866fSTomas Novotny 	{ "vcnl4200", VCNL4200 },
10362a1efb9SPeter Meerwald 	{ }
10462a1efb9SPeter Meerwald };
10562a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
10662a1efb9SPeter Meerwald 
1075e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
1085e00708dSGuido Günther {
1095e00708dSGuido Günther 	/* no suspend op */
1105e00708dSGuido Günther 	return 0;
1115e00708dSGuido Günther }
1125e00708dSGuido Günther 
1131ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data)
1141ebc787aSTomas Novotny {
1151ebc787aSTomas Novotny 	int ret, prod_id;
1161ebc787aSTomas Novotny 
1171ebc787aSTomas Novotny 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
1181ebc787aSTomas Novotny 	if (ret < 0)
1191ebc787aSTomas Novotny 		return ret;
1201ebc787aSTomas Novotny 
1211ebc787aSTomas Novotny 	prod_id = ret >> 4;
12258bf9aceSTomas Novotny 	switch (prod_id) {
12358bf9aceSTomas Novotny 	case VCNL4000_PROD_ID:
12458bf9aceSTomas Novotny 		if (data->id != VCNL4000)
12558bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
12658bf9aceSTomas Novotny 					"wrong device id, use vcnl4000");
12758bf9aceSTomas Novotny 		break;
12858bf9aceSTomas Novotny 	case VCNL4010_PROD_ID:
12958bf9aceSTomas Novotny 		if (data->id != VCNL4010)
13058bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
13158bf9aceSTomas Novotny 					"wrong device id, use vcnl4010/4020");
13258bf9aceSTomas Novotny 		break;
13358bf9aceSTomas Novotny 	default:
1341ebc787aSTomas Novotny 		return -ENODEV;
13558bf9aceSTomas Novotny 	}
1361ebc787aSTomas Novotny 
1371ebc787aSTomas Novotny 	data->rev = ret & 0xf;
1381ebc787aSTomas Novotny 	data->al_scale = 250000;
139be38866fSTomas Novotny 	mutex_init(&data->vcnl4000_lock);
140be38866fSTomas Novotny 
1415e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
142be38866fSTomas Novotny };
143be38866fSTomas Novotny 
1445e00708dSGuido Günther static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
1455e00708dSGuido Günther {
1465e00708dSGuido Günther 	u16 val = on ? 0 /* power on */ : 1 /* shut down */;
1475e00708dSGuido Günther 	int ret;
1485e00708dSGuido Günther 
1495e00708dSGuido Günther 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
1505e00708dSGuido Günther 	if (ret < 0)
1515e00708dSGuido Günther 		return ret;
1525e00708dSGuido Günther 
1535e00708dSGuido Günther 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
1545e00708dSGuido Günther 	if (ret < 0)
1555e00708dSGuido Günther 		return ret;
1565e00708dSGuido Günther 
1575e00708dSGuido Günther 	if (on) {
1585e00708dSGuido Günther 		/* Wait at least one integration cycle before fetching data */
1595e00708dSGuido Günther 		data->vcnl4200_al.last_measurement = ktime_get();
1605e00708dSGuido Günther 		data->vcnl4200_ps.last_measurement = ktime_get();
1615e00708dSGuido Günther 	}
1625e00708dSGuido Günther 
1635e00708dSGuido Günther 	return 0;
1645e00708dSGuido Günther }
1655e00708dSGuido Günther 
166be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data)
167be38866fSTomas Novotny {
1685a441aadSAngus Ainslie (Purism) 	int ret, id;
169be38866fSTomas Novotny 
170be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
171be38866fSTomas Novotny 	if (ret < 0)
172be38866fSTomas Novotny 		return ret;
173be38866fSTomas Novotny 
1745a441aadSAngus Ainslie (Purism) 	id = ret & 0xff;
1755a441aadSAngus Ainslie (Purism) 
1765a441aadSAngus Ainslie (Purism) 	if (id != VCNL4200_PROD_ID) {
1775a441aadSAngus Ainslie (Purism) 		ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
1785a441aadSAngus Ainslie (Purism) 		if (ret < 0)
1795a441aadSAngus Ainslie (Purism) 			return ret;
1805a441aadSAngus Ainslie (Purism) 
1815a441aadSAngus Ainslie (Purism) 		id = ret & 0xff;
1825a441aadSAngus Ainslie (Purism) 
1835a441aadSAngus Ainslie (Purism) 		if (id != VCNL4040_PROD_ID)
184be38866fSTomas Novotny 			return -ENODEV;
1855a441aadSAngus Ainslie (Purism) 	}
1865a441aadSAngus Ainslie (Purism) 
1875a441aadSAngus Ainslie (Purism) 	dev_dbg(&data->client->dev, "device id 0x%x", id);
188be38866fSTomas Novotny 
189be38866fSTomas Novotny 	data->rev = (ret >> 8) & 0xf;
190be38866fSTomas Novotny 
191be38866fSTomas Novotny 	data->vcnl4200_al.reg = VCNL4200_AL_DATA;
192be38866fSTomas Novotny 	data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
1935a441aadSAngus Ainslie (Purism) 	switch (id) {
1945a441aadSAngus Ainslie (Purism) 	case VCNL4200_PROD_ID:
195b42aa97eSTomas Novotny 		/* Default wait time is 50ms, add 20% tolerance. */
196b42aa97eSTomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
197b42aa97eSTomas Novotny 		/* Default wait time is 4.8ms, add 20% tolerance. */
198b42aa97eSTomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
199bc80573eSGuido Günther 		data->al_scale = 24000;
2005a441aadSAngus Ainslie (Purism) 		break;
2015a441aadSAngus Ainslie (Purism) 	case VCNL4040_PROD_ID:
2022ca5a879STomas Novotny 		/* Default wait time is 80ms, add 20% tolerance. */
2032ca5a879STomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
2042ca5a879STomas Novotny 		/* Default wait time is 5ms, add 20% tolerance. */
2052ca5a879STomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
206bc80573eSGuido Günther 		data->al_scale = 120000;
2075a441aadSAngus Ainslie (Purism) 		break;
2085a441aadSAngus Ainslie (Purism) 	}
209be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_al.lock);
210be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_ps.lock);
2111ebc787aSTomas Novotny 
2125e00708dSGuido Günther 	ret = data->chip_spec->set_power_state(data, true);
2135e00708dSGuido Günther 	if (ret < 0)
2145e00708dSGuido Günther 		return ret;
2155e00708dSGuido Günther 
2161ebc787aSTomas Novotny 	return 0;
2171ebc787aSTomas Novotny };
2181ebc787aSTomas Novotny 
219*816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val)
220*816956c3SMathieu Othacehe {
221*816956c3SMathieu Othacehe 	s32 ret;
222*816956c3SMathieu Othacehe 
223*816956c3SMathieu Othacehe 	ret = i2c_smbus_read_word_swapped(data->client, data_reg);
224*816956c3SMathieu Othacehe 	if (ret < 0)
225*816956c3SMathieu Othacehe 		return ret;
226*816956c3SMathieu Othacehe 
227*816956c3SMathieu Othacehe 	*val = ret;
228*816956c3SMathieu Othacehe 	return 0;
229*816956c3SMathieu Othacehe }
230*816956c3SMathieu Othacehe 
231*816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val)
232*816956c3SMathieu Othacehe {
233*816956c3SMathieu Othacehe 	if (val > U16_MAX)
234*816956c3SMathieu Othacehe 		return -ERANGE;
235*816956c3SMathieu Othacehe 
236*816956c3SMathieu Othacehe 	return i2c_smbus_write_word_swapped(data->client, data_reg, val);
237*816956c3SMathieu Othacehe }
238*816956c3SMathieu Othacehe 
239*816956c3SMathieu Othacehe 
24062a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
24162a1efb9SPeter Meerwald 				u8 rdy_mask, u8 data_reg, int *val)
24262a1efb9SPeter Meerwald {
24362a1efb9SPeter Meerwald 	int tries = 20;
24462a1efb9SPeter Meerwald 	int ret;
24562a1efb9SPeter Meerwald 
246be38866fSTomas Novotny 	mutex_lock(&data->vcnl4000_lock);
247ff34ed6dSPeter Meerwald-Stadler 
24862a1efb9SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
24962a1efb9SPeter Meerwald 					req_mask);
25062a1efb9SPeter Meerwald 	if (ret < 0)
251ff34ed6dSPeter Meerwald-Stadler 		goto fail;
25262a1efb9SPeter Meerwald 
25362a1efb9SPeter Meerwald 	/* wait for data to become ready */
25462a1efb9SPeter Meerwald 	while (tries--) {
25562a1efb9SPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
25662a1efb9SPeter Meerwald 		if (ret < 0)
257ff34ed6dSPeter Meerwald-Stadler 			goto fail;
25862a1efb9SPeter Meerwald 		if (ret & rdy_mask)
25962a1efb9SPeter Meerwald 			break;
26062a1efb9SPeter Meerwald 		msleep(20); /* measurement takes up to 100 ms */
26162a1efb9SPeter Meerwald 	}
26262a1efb9SPeter Meerwald 
26362a1efb9SPeter Meerwald 	if (tries < 0) {
26462a1efb9SPeter Meerwald 		dev_err(&data->client->dev,
26562a1efb9SPeter Meerwald 			"vcnl4000_measure() failed, data not ready\n");
266ff34ed6dSPeter Meerwald-Stadler 		ret = -EIO;
267ff34ed6dSPeter Meerwald-Stadler 		goto fail;
26862a1efb9SPeter Meerwald 	}
26962a1efb9SPeter Meerwald 
270*816956c3SMathieu Othacehe 	ret = vcnl4000_read_data(data, data_reg, val);
27162a1efb9SPeter Meerwald 	if (ret < 0)
272ff34ed6dSPeter Meerwald-Stadler 		goto fail;
27362a1efb9SPeter Meerwald 
274be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
27562a1efb9SPeter Meerwald 
27662a1efb9SPeter Meerwald 	return 0;
277ff34ed6dSPeter Meerwald-Stadler 
278ff34ed6dSPeter Meerwald-Stadler fail:
279be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
280ff34ed6dSPeter Meerwald-Stadler 	return ret;
28162a1efb9SPeter Meerwald }
28262a1efb9SPeter Meerwald 
283be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data,
284be38866fSTomas Novotny 		struct vcnl4200_channel *chan, int *val)
285be38866fSTomas Novotny {
286be38866fSTomas Novotny 	int ret;
287be38866fSTomas Novotny 	s64 delta;
288be38866fSTomas Novotny 	ktime_t next_measurement;
289be38866fSTomas Novotny 
290be38866fSTomas Novotny 	mutex_lock(&chan->lock);
291be38866fSTomas Novotny 
292be38866fSTomas Novotny 	next_measurement = ktime_add(chan->last_measurement,
293be38866fSTomas Novotny 			chan->sampling_rate);
294be38866fSTomas Novotny 	delta = ktime_us_delta(next_measurement, ktime_get());
295be38866fSTomas Novotny 	if (delta > 0)
296be38866fSTomas Novotny 		usleep_range(delta, delta + 500);
297be38866fSTomas Novotny 	chan->last_measurement = ktime_get();
298be38866fSTomas Novotny 
299be38866fSTomas Novotny 	mutex_unlock(&chan->lock);
300be38866fSTomas Novotny 
301be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, chan->reg);
302be38866fSTomas Novotny 	if (ret < 0)
303be38866fSTomas Novotny 		return ret;
304be38866fSTomas Novotny 
305be38866fSTomas Novotny 	*val = ret;
306be38866fSTomas Novotny 
307be38866fSTomas Novotny 	return 0;
308be38866fSTomas Novotny }
309be38866fSTomas Novotny 
3101ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
3111ebc787aSTomas Novotny {
3121ebc787aSTomas Novotny 	return vcnl4000_measure(data,
3131ebc787aSTomas Novotny 			VCNL4000_AL_OD, VCNL4000_AL_RDY,
3141ebc787aSTomas Novotny 			VCNL4000_AL_RESULT_HI, val);
3151ebc787aSTomas Novotny }
3161ebc787aSTomas Novotny 
317be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
318be38866fSTomas Novotny {
319be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_al, val);
320be38866fSTomas Novotny }
321be38866fSTomas Novotny 
3221ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
3231ebc787aSTomas Novotny {
3241ebc787aSTomas Novotny 	return vcnl4000_measure(data,
3251ebc787aSTomas Novotny 			VCNL4000_PS_OD, VCNL4000_PS_RDY,
3261ebc787aSTomas Novotny 			VCNL4000_PS_RESULT_HI, val);
3271ebc787aSTomas Novotny }
3281ebc787aSTomas Novotny 
329be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
330be38866fSTomas Novotny {
331be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_ps, val);
332be38866fSTomas Novotny }
333be38866fSTomas Novotny 
3341ebc787aSTomas Novotny static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
3351ebc787aSTomas Novotny 	[VCNL4000] = {
3361ebc787aSTomas Novotny 		.prod = "VCNL4000",
3371ebc787aSTomas Novotny 		.init = vcnl4000_init,
3381ebc787aSTomas Novotny 		.measure_light = vcnl4000_measure_light,
3391ebc787aSTomas Novotny 		.measure_proximity = vcnl4000_measure_proximity,
3405e00708dSGuido Günther 		.set_power_state = vcnl4000_set_power_state,
3411ebc787aSTomas Novotny 	},
34250c50b97STomas Novotny 	[VCNL4010] = {
34350c50b97STomas Novotny 		.prod = "VCNL4010/4020",
34450c50b97STomas Novotny 		.init = vcnl4000_init,
34550c50b97STomas Novotny 		.measure_light = vcnl4000_measure_light,
34650c50b97STomas Novotny 		.measure_proximity = vcnl4000_measure_proximity,
3475e00708dSGuido Günther 		.set_power_state = vcnl4000_set_power_state,
34850c50b97STomas Novotny 	},
3495a441aadSAngus Ainslie (Purism) 	[VCNL4040] = {
3505a441aadSAngus Ainslie (Purism) 		.prod = "VCNL4040",
3515a441aadSAngus Ainslie (Purism) 		.init = vcnl4200_init,
3525a441aadSAngus Ainslie (Purism) 		.measure_light = vcnl4200_measure_light,
3535a441aadSAngus Ainslie (Purism) 		.measure_proximity = vcnl4200_measure_proximity,
3545e00708dSGuido Günther 		.set_power_state = vcnl4200_set_power_state,
3555a441aadSAngus Ainslie (Purism) 	},
356be38866fSTomas Novotny 	[VCNL4200] = {
357be38866fSTomas Novotny 		.prod = "VCNL4200",
358be38866fSTomas Novotny 		.init = vcnl4200_init,
359be38866fSTomas Novotny 		.measure_light = vcnl4200_measure_light,
360be38866fSTomas Novotny 		.measure_proximity = vcnl4200_measure_proximity,
3615e00708dSGuido Günther 		.set_power_state = vcnl4200_set_power_state,
362be38866fSTomas Novotny 	},
3631ebc787aSTomas Novotny };
3641ebc787aSTomas Novotny 
365f5a98e1fSGuido Günther static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
366f5a98e1fSGuido Günther 					uintptr_t priv,
367f5a98e1fSGuido Günther 					const struct iio_chan_spec *chan,
368f5a98e1fSGuido Günther 					char *buf)
369f5a98e1fSGuido Günther {
370f5a98e1fSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
371f5a98e1fSGuido Günther 
372f5a98e1fSGuido Günther 	return sprintf(buf, "%u\n", data->near_level);
373f5a98e1fSGuido Günther }
374f5a98e1fSGuido Günther 
375f5a98e1fSGuido Günther static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
376f5a98e1fSGuido Günther 	{
377f5a98e1fSGuido Günther 		.name = "nearlevel",
378f5a98e1fSGuido Günther 		.shared = IIO_SEPARATE,
379f5a98e1fSGuido Günther 		.read = vcnl4000_read_near_level,
380f5a98e1fSGuido Günther 	},
381f5a98e1fSGuido Günther 	{ /* sentinel */ }
382f5a98e1fSGuido Günther };
383f5a98e1fSGuido Günther 
38462a1efb9SPeter Meerwald static const struct iio_chan_spec vcnl4000_channels[] = {
38562a1efb9SPeter Meerwald 	{
38662a1efb9SPeter Meerwald 		.type = IIO_LIGHT,
387bb7c5940SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
388bb7c5940SJonathan Cameron 			BIT(IIO_CHAN_INFO_SCALE),
38962a1efb9SPeter Meerwald 	}, {
39062a1efb9SPeter Meerwald 		.type = IIO_PROXIMITY,
391bb7c5940SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
392f5a98e1fSGuido Günther 		.ext_info = vcnl4000_ext_info,
39362a1efb9SPeter Meerwald 	}
39462a1efb9SPeter Meerwald };
39562a1efb9SPeter Meerwald 
3965e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
3975e00708dSGuido Günther {
3985e00708dSGuido Günther 	struct device *dev = &data->client->dev;
3995e00708dSGuido Günther 	int ret;
4005e00708dSGuido Günther 
4015e00708dSGuido Günther 	if (on) {
4025e00708dSGuido Günther 		ret = pm_runtime_get_sync(dev);
4035e00708dSGuido Günther 		if (ret < 0)
4045e00708dSGuido Günther 			pm_runtime_put_noidle(dev);
4055e00708dSGuido Günther 	} else {
4065e00708dSGuido Günther 		pm_runtime_mark_last_busy(dev);
4075e00708dSGuido Günther 		ret = pm_runtime_put_autosuspend(dev);
4085e00708dSGuido Günther 	}
4095e00708dSGuido Günther 
4105e00708dSGuido Günther 	return ret;
4115e00708dSGuido Günther }
4125e00708dSGuido Günther 
41362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev,
41462a1efb9SPeter Meerwald 				struct iio_chan_spec const *chan,
41562a1efb9SPeter Meerwald 				int *val, int *val2, long mask)
41662a1efb9SPeter Meerwald {
4175d693139SPeter Meerwald-Stadler 	int ret;
41862a1efb9SPeter Meerwald 	struct vcnl4000_data *data = iio_priv(indio_dev);
41962a1efb9SPeter Meerwald 
42062a1efb9SPeter Meerwald 	switch (mask) {
42162a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
4225e00708dSGuido Günther 		ret = vcnl4000_set_pm_runtime_state(data, true);
4235e00708dSGuido Günther 		if  (ret < 0)
4245e00708dSGuido Günther 			return ret;
4255e00708dSGuido Günther 
42662a1efb9SPeter Meerwald 		switch (chan->type) {
42762a1efb9SPeter Meerwald 		case IIO_LIGHT:
4281ebc787aSTomas Novotny 			ret = data->chip_spec->measure_light(data, val);
4294a818643SGuido Günther 			if (!ret)
4304a818643SGuido Günther 				ret = IIO_VAL_INT;
4314a818643SGuido Günther 			break;
43262a1efb9SPeter Meerwald 		case IIO_PROXIMITY:
4331ebc787aSTomas Novotny 			ret = data->chip_spec->measure_proximity(data, val);
4344a818643SGuido Günther 			if (!ret)
4354a818643SGuido Günther 				ret = IIO_VAL_INT;
4364a818643SGuido Günther 			break;
43762a1efb9SPeter Meerwald 		default:
4384a818643SGuido Günther 			ret = -EINVAL;
43962a1efb9SPeter Meerwald 		}
4405e00708dSGuido Günther 		vcnl4000_set_pm_runtime_state(data, false);
4414a818643SGuido Günther 		return ret;
44262a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
4435d693139SPeter Meerwald-Stadler 		if (chan->type != IIO_LIGHT)
4445d693139SPeter Meerwald-Stadler 			return -EINVAL;
4455d693139SPeter Meerwald-Stadler 
44662a1efb9SPeter Meerwald 		*val = 0;
4471ebc787aSTomas Novotny 		*val2 = data->al_scale;
4485d693139SPeter Meerwald-Stadler 		return IIO_VAL_INT_PLUS_MICRO;
44962a1efb9SPeter Meerwald 	default:
4505d693139SPeter Meerwald-Stadler 		return -EINVAL;
45162a1efb9SPeter Meerwald 	}
45262a1efb9SPeter Meerwald }
45362a1efb9SPeter Meerwald 
45462a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = {
45562a1efb9SPeter Meerwald 	.read_raw = vcnl4000_read_raw,
45662a1efb9SPeter Meerwald };
45762a1efb9SPeter Meerwald 
458fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client,
45962a1efb9SPeter Meerwald 			  const struct i2c_device_id *id)
46062a1efb9SPeter Meerwald {
46162a1efb9SPeter Meerwald 	struct vcnl4000_data *data;
46262a1efb9SPeter Meerwald 	struct iio_dev *indio_dev;
4631ebc787aSTomas Novotny 	int ret;
46462a1efb9SPeter Meerwald 
4652669d723SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
46662a1efb9SPeter Meerwald 	if (!indio_dev)
46762a1efb9SPeter Meerwald 		return -ENOMEM;
46862a1efb9SPeter Meerwald 
46962a1efb9SPeter Meerwald 	data = iio_priv(indio_dev);
47062a1efb9SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
47162a1efb9SPeter Meerwald 	data->client = client;
4721ebc787aSTomas Novotny 	data->id = id->driver_data;
4731ebc787aSTomas Novotny 	data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
47462a1efb9SPeter Meerwald 
4751ebc787aSTomas Novotny 	ret = data->chip_spec->init(data);
47662a1efb9SPeter Meerwald 	if (ret < 0)
4772669d723SPeter Meerwald 		return ret;
47862a1efb9SPeter Meerwald 
479d978bfddSPeter Meerwald-Stadler 	dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
4801ebc787aSTomas Novotny 		data->chip_spec->prod, data->rev);
48162a1efb9SPeter Meerwald 
482f5a98e1fSGuido Günther 	if (device_property_read_u32(&client->dev, "proximity-near-level",
483f5a98e1fSGuido Günther 				     &data->near_level))
484f5a98e1fSGuido Günther 		data->near_level = 0;
485f5a98e1fSGuido Günther 
48662a1efb9SPeter Meerwald 	indio_dev->dev.parent = &client->dev;
48762a1efb9SPeter Meerwald 	indio_dev->info = &vcnl4000_info;
48862a1efb9SPeter Meerwald 	indio_dev->channels = vcnl4000_channels;
48962a1efb9SPeter Meerwald 	indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
49062a1efb9SPeter Meerwald 	indio_dev->name = VCNL4000_DRV_NAME;
49162a1efb9SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
49262a1efb9SPeter Meerwald 
4935e00708dSGuido Günther 	ret = pm_runtime_set_active(&client->dev);
4945e00708dSGuido Günther 	if (ret < 0)
4955e00708dSGuido Günther 		goto fail_poweroff;
4965e00708dSGuido Günther 
4975e00708dSGuido Günther 	ret = iio_device_register(indio_dev);
4985e00708dSGuido Günther 	if (ret < 0)
4995e00708dSGuido Günther 		goto fail_poweroff;
5005e00708dSGuido Günther 
5015e00708dSGuido Günther 	pm_runtime_enable(&client->dev);
5025e00708dSGuido Günther 	pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
5035e00708dSGuido Günther 	pm_runtime_use_autosuspend(&client->dev);
5045e00708dSGuido Günther 
5055e00708dSGuido Günther 	return 0;
5065e00708dSGuido Günther fail_poweroff:
5075e00708dSGuido Günther 	data->chip_spec->set_power_state(data, false);
5085e00708dSGuido Günther 	return ret;
50962a1efb9SPeter Meerwald }
51062a1efb9SPeter Meerwald 
511ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = {
512ebd457d5SAngus Ainslie (Purism) 	{
513ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4000",
5141436a78cSMarco Felsch 		.data = (void *)VCNL4000,
515ebd457d5SAngus Ainslie (Purism) 	},
516ebd457d5SAngus Ainslie (Purism) 	{
517ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4010",
5181436a78cSMarco Felsch 		.data = (void *)VCNL4010,
519ebd457d5SAngus Ainslie (Purism) 	},
520ebd457d5SAngus Ainslie (Purism) 	{
5211436a78cSMarco Felsch 		.compatible = "vishay,vcnl4020",
5221436a78cSMarco Felsch 		.data = (void *)VCNL4010,
523ebd457d5SAngus Ainslie (Purism) 	},
524ebd457d5SAngus Ainslie (Purism) 	{
5257fd1c260SMarco Felsch 		.compatible = "vishay,vcnl4040",
5267fd1c260SMarco Felsch 		.data = (void *)VCNL4040,
5277fd1c260SMarco Felsch 	},
5287fd1c260SMarco Felsch 	{
529ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4200",
5301436a78cSMarco Felsch 		.data = (void *)VCNL4200,
531ebd457d5SAngus Ainslie (Purism) 	},
532ebd457d5SAngus Ainslie (Purism) 	{},
533ebd457d5SAngus Ainslie (Purism) };
534ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
535ebd457d5SAngus Ainslie (Purism) 
5365e00708dSGuido Günther static int vcnl4000_remove(struct i2c_client *client)
5375e00708dSGuido Günther {
5385e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
5395e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
5405e00708dSGuido Günther 
5415e00708dSGuido Günther 	pm_runtime_dont_use_autosuspend(&client->dev);
5425e00708dSGuido Günther 	pm_runtime_disable(&client->dev);
5435e00708dSGuido Günther 	iio_device_unregister(indio_dev);
5445e00708dSGuido Günther 	pm_runtime_set_suspended(&client->dev);
5455e00708dSGuido Günther 
5465e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
5475e00708dSGuido Günther }
5485e00708dSGuido Günther 
5495e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
5505e00708dSGuido Günther {
5515e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
5525e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
5535e00708dSGuido Günther 
5545e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
5555e00708dSGuido Günther }
5565e00708dSGuido Günther 
5575e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
5585e00708dSGuido Günther {
5595e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
5605e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
5615e00708dSGuido Günther 
5625e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
5635e00708dSGuido Günther }
5645e00708dSGuido Günther 
5655e00708dSGuido Günther static const struct dev_pm_ops vcnl4000_pm_ops = {
5665e00708dSGuido Günther 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
5675e00708dSGuido Günther 				pm_runtime_force_resume)
5685e00708dSGuido Günther 	SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
5695e00708dSGuido Günther 			   vcnl4000_runtime_resume, NULL)
5705e00708dSGuido Günther };
5715e00708dSGuido Günther 
57262a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = {
57362a1efb9SPeter Meerwald 	.driver = {
57462a1efb9SPeter Meerwald 		.name   = VCNL4000_DRV_NAME,
5755e00708dSGuido Günther 		.pm	= &vcnl4000_pm_ops,
576ebd457d5SAngus Ainslie (Purism) 		.of_match_table = vcnl_4000_of_match,
57762a1efb9SPeter Meerwald 	},
57862a1efb9SPeter Meerwald 	.probe  = vcnl4000_probe,
57962a1efb9SPeter Meerwald 	.id_table = vcnl4000_id,
5805e00708dSGuido Günther 	.remove	= vcnl4000_remove,
58162a1efb9SPeter Meerwald };
58262a1efb9SPeter Meerwald 
58362a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver);
58462a1efb9SPeter Meerwald 
58562a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
58662a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
58762a1efb9SPeter Meerwald MODULE_LICENSE("GPL");
588