xref: /linux/drivers/iio/light/vcnl4000.c (revision 5e00708df3816d47d0de8b3837a33d64ff3582ed)
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>
25*5e00708dSGuido 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 
61*5e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS	2000 /* before we enter pm_runtime_suspend */
62*5e00708dSGuido 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;
8662a1efb9SPeter Meerwald };
8762a1efb9SPeter Meerwald 
881ebc787aSTomas Novotny struct vcnl4000_chip_spec {
891ebc787aSTomas Novotny 	const char *prod;
901ebc787aSTomas Novotny 	int (*init)(struct vcnl4000_data *data);
911ebc787aSTomas Novotny 	int (*measure_light)(struct vcnl4000_data *data, int *val);
921ebc787aSTomas Novotny 	int (*measure_proximity)(struct vcnl4000_data *data, int *val);
93*5e00708dSGuido Günther 	int (*set_power_state)(struct vcnl4000_data *data, bool on);
941ebc787aSTomas Novotny };
951ebc787aSTomas Novotny 
9662a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = {
971ebc787aSTomas Novotny 	{ "vcnl4000", VCNL4000 },
9850c50b97STomas Novotny 	{ "vcnl4010", VCNL4010 },
9950c50b97STomas Novotny 	{ "vcnl4020", VCNL4010 },
1005a441aadSAngus Ainslie (Purism) 	{ "vcnl4040", VCNL4040 },
101be38866fSTomas Novotny 	{ "vcnl4200", VCNL4200 },
10262a1efb9SPeter Meerwald 	{ }
10362a1efb9SPeter Meerwald };
10462a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
10562a1efb9SPeter Meerwald 
106*5e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
107*5e00708dSGuido Günther {
108*5e00708dSGuido Günther 	/* no suspend op */
109*5e00708dSGuido Günther 	return 0;
110*5e00708dSGuido Günther }
111*5e00708dSGuido Günther 
1121ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data)
1131ebc787aSTomas Novotny {
1141ebc787aSTomas Novotny 	int ret, prod_id;
1151ebc787aSTomas Novotny 
1161ebc787aSTomas Novotny 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
1171ebc787aSTomas Novotny 	if (ret < 0)
1181ebc787aSTomas Novotny 		return ret;
1191ebc787aSTomas Novotny 
1201ebc787aSTomas Novotny 	prod_id = ret >> 4;
12158bf9aceSTomas Novotny 	switch (prod_id) {
12258bf9aceSTomas Novotny 	case VCNL4000_PROD_ID:
12358bf9aceSTomas Novotny 		if (data->id != VCNL4000)
12458bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
12558bf9aceSTomas Novotny 					"wrong device id, use vcnl4000");
12658bf9aceSTomas Novotny 		break;
12758bf9aceSTomas Novotny 	case VCNL4010_PROD_ID:
12858bf9aceSTomas Novotny 		if (data->id != VCNL4010)
12958bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
13058bf9aceSTomas Novotny 					"wrong device id, use vcnl4010/4020");
13158bf9aceSTomas Novotny 		break;
13258bf9aceSTomas Novotny 	default:
1331ebc787aSTomas Novotny 		return -ENODEV;
13458bf9aceSTomas Novotny 	}
1351ebc787aSTomas Novotny 
1361ebc787aSTomas Novotny 	data->rev = ret & 0xf;
1371ebc787aSTomas Novotny 	data->al_scale = 250000;
138be38866fSTomas Novotny 	mutex_init(&data->vcnl4000_lock);
139be38866fSTomas Novotny 
140*5e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
141be38866fSTomas Novotny };
142be38866fSTomas Novotny 
143*5e00708dSGuido Günther static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
144*5e00708dSGuido Günther {
145*5e00708dSGuido Günther 	u16 val = on ? 0 /* power on */ : 1 /* shut down */;
146*5e00708dSGuido Günther 	int ret;
147*5e00708dSGuido Günther 
148*5e00708dSGuido Günther 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
149*5e00708dSGuido Günther 	if (ret < 0)
150*5e00708dSGuido Günther 		return ret;
151*5e00708dSGuido Günther 
152*5e00708dSGuido Günther 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
153*5e00708dSGuido Günther 	if (ret < 0)
154*5e00708dSGuido Günther 		return ret;
155*5e00708dSGuido Günther 
156*5e00708dSGuido Günther 	if (on) {
157*5e00708dSGuido Günther 		/* Wait at least one integration cycle before fetching data */
158*5e00708dSGuido Günther 		data->vcnl4200_al.last_measurement = ktime_get();
159*5e00708dSGuido Günther 		data->vcnl4200_ps.last_measurement = ktime_get();
160*5e00708dSGuido Günther 	}
161*5e00708dSGuido Günther 
162*5e00708dSGuido Günther 	return 0;
163*5e00708dSGuido Günther }
164*5e00708dSGuido Günther 
165be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data)
166be38866fSTomas Novotny {
1675a441aadSAngus Ainslie (Purism) 	int ret, id;
168be38866fSTomas Novotny 
169be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
170be38866fSTomas Novotny 	if (ret < 0)
171be38866fSTomas Novotny 		return ret;
172be38866fSTomas Novotny 
1735a441aadSAngus Ainslie (Purism) 	id = ret & 0xff;
1745a441aadSAngus Ainslie (Purism) 
1755a441aadSAngus Ainslie (Purism) 	if (id != VCNL4200_PROD_ID) {
1765a441aadSAngus Ainslie (Purism) 		ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
1775a441aadSAngus Ainslie (Purism) 		if (ret < 0)
1785a441aadSAngus Ainslie (Purism) 			return ret;
1795a441aadSAngus Ainslie (Purism) 
1805a441aadSAngus Ainslie (Purism) 		id = ret & 0xff;
1815a441aadSAngus Ainslie (Purism) 
1825a441aadSAngus Ainslie (Purism) 		if (id != VCNL4040_PROD_ID)
183be38866fSTomas Novotny 			return -ENODEV;
1845a441aadSAngus Ainslie (Purism) 	}
1855a441aadSAngus Ainslie (Purism) 
1865a441aadSAngus Ainslie (Purism) 	dev_dbg(&data->client->dev, "device id 0x%x", id);
187be38866fSTomas Novotny 
188be38866fSTomas Novotny 	data->rev = (ret >> 8) & 0xf;
189be38866fSTomas Novotny 
190be38866fSTomas Novotny 	data->vcnl4200_al.reg = VCNL4200_AL_DATA;
191be38866fSTomas Novotny 	data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
1925a441aadSAngus Ainslie (Purism) 	switch (id) {
1935a441aadSAngus Ainslie (Purism) 	case VCNL4200_PROD_ID:
1945a441aadSAngus Ainslie (Purism) 		/* Integration time is 50ms, but the experiments */
1955a441aadSAngus Ainslie (Purism) 		/* show 54ms in total. */
196be38866fSTomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
197be38866fSTomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
198bc80573eSGuido Günther 		data->al_scale = 24000;
1995a441aadSAngus Ainslie (Purism) 		break;
2005a441aadSAngus Ainslie (Purism) 	case VCNL4040_PROD_ID:
2015a441aadSAngus Ainslie (Purism) 		/* Integration time is 80ms, add 10ms. */
2025a441aadSAngus Ainslie (Purism) 		data->vcnl4200_al.sampling_rate = ktime_set(0, 100000 * 1000);
2035a441aadSAngus Ainslie (Purism) 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 100000 * 1000);
204bc80573eSGuido Günther 		data->al_scale = 120000;
2055a441aadSAngus Ainslie (Purism) 		break;
2065a441aadSAngus Ainslie (Purism) 	}
207be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_al.lock);
208be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_ps.lock);
2091ebc787aSTomas Novotny 
210*5e00708dSGuido Günther 	ret = data->chip_spec->set_power_state(data, true);
211*5e00708dSGuido Günther 	if (ret < 0)
212*5e00708dSGuido Günther 		return ret;
213*5e00708dSGuido Günther 
2141ebc787aSTomas Novotny 	return 0;
2151ebc787aSTomas Novotny };
2161ebc787aSTomas Novotny 
21762a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
21862a1efb9SPeter Meerwald 				u8 rdy_mask, u8 data_reg, int *val)
21962a1efb9SPeter Meerwald {
22062a1efb9SPeter Meerwald 	int tries = 20;
22191f197e0SLars-Peter Clausen 	__be16 buf;
22262a1efb9SPeter Meerwald 	int ret;
22362a1efb9SPeter Meerwald 
224be38866fSTomas Novotny 	mutex_lock(&data->vcnl4000_lock);
225ff34ed6dSPeter Meerwald-Stadler 
22662a1efb9SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
22762a1efb9SPeter Meerwald 					req_mask);
22862a1efb9SPeter Meerwald 	if (ret < 0)
229ff34ed6dSPeter Meerwald-Stadler 		goto fail;
23062a1efb9SPeter Meerwald 
23162a1efb9SPeter Meerwald 	/* wait for data to become ready */
23262a1efb9SPeter Meerwald 	while (tries--) {
23362a1efb9SPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
23462a1efb9SPeter Meerwald 		if (ret < 0)
235ff34ed6dSPeter Meerwald-Stadler 			goto fail;
23662a1efb9SPeter Meerwald 		if (ret & rdy_mask)
23762a1efb9SPeter Meerwald 			break;
23862a1efb9SPeter Meerwald 		msleep(20); /* measurement takes up to 100 ms */
23962a1efb9SPeter Meerwald 	}
24062a1efb9SPeter Meerwald 
24162a1efb9SPeter Meerwald 	if (tries < 0) {
24262a1efb9SPeter Meerwald 		dev_err(&data->client->dev,
24362a1efb9SPeter Meerwald 			"vcnl4000_measure() failed, data not ready\n");
244ff34ed6dSPeter Meerwald-Stadler 		ret = -EIO;
245ff34ed6dSPeter Meerwald-Stadler 		goto fail;
24662a1efb9SPeter Meerwald 	}
24762a1efb9SPeter Meerwald 
24862a1efb9SPeter Meerwald 	ret = i2c_smbus_read_i2c_block_data(data->client,
24962a1efb9SPeter Meerwald 		data_reg, sizeof(buf), (u8 *) &buf);
25062a1efb9SPeter Meerwald 	if (ret < 0)
251ff34ed6dSPeter Meerwald-Stadler 		goto fail;
25262a1efb9SPeter Meerwald 
253be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
25462a1efb9SPeter Meerwald 	*val = be16_to_cpu(buf);
25562a1efb9SPeter Meerwald 
25662a1efb9SPeter Meerwald 	return 0;
257ff34ed6dSPeter Meerwald-Stadler 
258ff34ed6dSPeter Meerwald-Stadler fail:
259be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
260ff34ed6dSPeter Meerwald-Stadler 	return ret;
26162a1efb9SPeter Meerwald }
26262a1efb9SPeter Meerwald 
263be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data,
264be38866fSTomas Novotny 		struct vcnl4200_channel *chan, int *val)
265be38866fSTomas Novotny {
266be38866fSTomas Novotny 	int ret;
267be38866fSTomas Novotny 	s64 delta;
268be38866fSTomas Novotny 	ktime_t next_measurement;
269be38866fSTomas Novotny 
270be38866fSTomas Novotny 	mutex_lock(&chan->lock);
271be38866fSTomas Novotny 
272be38866fSTomas Novotny 	next_measurement = ktime_add(chan->last_measurement,
273be38866fSTomas Novotny 			chan->sampling_rate);
274be38866fSTomas Novotny 	delta = ktime_us_delta(next_measurement, ktime_get());
275be38866fSTomas Novotny 	if (delta > 0)
276be38866fSTomas Novotny 		usleep_range(delta, delta + 500);
277be38866fSTomas Novotny 	chan->last_measurement = ktime_get();
278be38866fSTomas Novotny 
279be38866fSTomas Novotny 	mutex_unlock(&chan->lock);
280be38866fSTomas Novotny 
281be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, chan->reg);
282be38866fSTomas Novotny 	if (ret < 0)
283be38866fSTomas Novotny 		return ret;
284be38866fSTomas Novotny 
285be38866fSTomas Novotny 	*val = ret;
286be38866fSTomas Novotny 
287be38866fSTomas Novotny 	return 0;
288be38866fSTomas Novotny }
289be38866fSTomas Novotny 
2901ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
2911ebc787aSTomas Novotny {
2921ebc787aSTomas Novotny 	return vcnl4000_measure(data,
2931ebc787aSTomas Novotny 			VCNL4000_AL_OD, VCNL4000_AL_RDY,
2941ebc787aSTomas Novotny 			VCNL4000_AL_RESULT_HI, val);
2951ebc787aSTomas Novotny }
2961ebc787aSTomas Novotny 
297be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
298be38866fSTomas Novotny {
299be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_al, val);
300be38866fSTomas Novotny }
301be38866fSTomas Novotny 
3021ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
3031ebc787aSTomas Novotny {
3041ebc787aSTomas Novotny 	return vcnl4000_measure(data,
3051ebc787aSTomas Novotny 			VCNL4000_PS_OD, VCNL4000_PS_RDY,
3061ebc787aSTomas Novotny 			VCNL4000_PS_RESULT_HI, val);
3071ebc787aSTomas Novotny }
3081ebc787aSTomas Novotny 
309be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
310be38866fSTomas Novotny {
311be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_ps, val);
312be38866fSTomas Novotny }
313be38866fSTomas Novotny 
3141ebc787aSTomas Novotny static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
3151ebc787aSTomas Novotny 	[VCNL4000] = {
3161ebc787aSTomas Novotny 		.prod = "VCNL4000",
3171ebc787aSTomas Novotny 		.init = vcnl4000_init,
3181ebc787aSTomas Novotny 		.measure_light = vcnl4000_measure_light,
3191ebc787aSTomas Novotny 		.measure_proximity = vcnl4000_measure_proximity,
320*5e00708dSGuido Günther 		.set_power_state = vcnl4000_set_power_state,
3211ebc787aSTomas Novotny 	},
32250c50b97STomas Novotny 	[VCNL4010] = {
32350c50b97STomas Novotny 		.prod = "VCNL4010/4020",
32450c50b97STomas Novotny 		.init = vcnl4000_init,
32550c50b97STomas Novotny 		.measure_light = vcnl4000_measure_light,
32650c50b97STomas Novotny 		.measure_proximity = vcnl4000_measure_proximity,
327*5e00708dSGuido Günther 		.set_power_state = vcnl4000_set_power_state,
32850c50b97STomas Novotny 	},
3295a441aadSAngus Ainslie (Purism) 	[VCNL4040] = {
3305a441aadSAngus Ainslie (Purism) 		.prod = "VCNL4040",
3315a441aadSAngus Ainslie (Purism) 		.init = vcnl4200_init,
3325a441aadSAngus Ainslie (Purism) 		.measure_light = vcnl4200_measure_light,
3335a441aadSAngus Ainslie (Purism) 		.measure_proximity = vcnl4200_measure_proximity,
334*5e00708dSGuido Günther 		.set_power_state = vcnl4200_set_power_state,
3355a441aadSAngus Ainslie (Purism) 	},
336be38866fSTomas Novotny 	[VCNL4200] = {
337be38866fSTomas Novotny 		.prod = "VCNL4200",
338be38866fSTomas Novotny 		.init = vcnl4200_init,
339be38866fSTomas Novotny 		.measure_light = vcnl4200_measure_light,
340be38866fSTomas Novotny 		.measure_proximity = vcnl4200_measure_proximity,
341*5e00708dSGuido Günther 		.set_power_state = vcnl4200_set_power_state,
342be38866fSTomas Novotny 	},
3431ebc787aSTomas Novotny };
3441ebc787aSTomas Novotny 
34562a1efb9SPeter Meerwald static const struct iio_chan_spec vcnl4000_channels[] = {
34662a1efb9SPeter Meerwald 	{
34762a1efb9SPeter Meerwald 		.type = IIO_LIGHT,
348bb7c5940SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
349bb7c5940SJonathan Cameron 			BIT(IIO_CHAN_INFO_SCALE),
35062a1efb9SPeter Meerwald 	}, {
35162a1efb9SPeter Meerwald 		.type = IIO_PROXIMITY,
352bb7c5940SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
35362a1efb9SPeter Meerwald 	}
35462a1efb9SPeter Meerwald };
35562a1efb9SPeter Meerwald 
356*5e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
357*5e00708dSGuido Günther {
358*5e00708dSGuido Günther 	struct device *dev = &data->client->dev;
359*5e00708dSGuido Günther 	int ret;
360*5e00708dSGuido Günther 
361*5e00708dSGuido Günther 	if (on) {
362*5e00708dSGuido Günther 		ret = pm_runtime_get_sync(dev);
363*5e00708dSGuido Günther 		if (ret < 0)
364*5e00708dSGuido Günther 			pm_runtime_put_noidle(dev);
365*5e00708dSGuido Günther 	} else {
366*5e00708dSGuido Günther 		pm_runtime_mark_last_busy(dev);
367*5e00708dSGuido Günther 		ret = pm_runtime_put_autosuspend(dev);
368*5e00708dSGuido Günther 	}
369*5e00708dSGuido Günther 
370*5e00708dSGuido Günther 	return ret;
371*5e00708dSGuido Günther }
372*5e00708dSGuido Günther 
37362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev,
37462a1efb9SPeter Meerwald 				struct iio_chan_spec const *chan,
37562a1efb9SPeter Meerwald 				int *val, int *val2, long mask)
37662a1efb9SPeter Meerwald {
3775d693139SPeter Meerwald-Stadler 	int ret;
37862a1efb9SPeter Meerwald 	struct vcnl4000_data *data = iio_priv(indio_dev);
37962a1efb9SPeter Meerwald 
38062a1efb9SPeter Meerwald 	switch (mask) {
38162a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
382*5e00708dSGuido Günther 		ret = vcnl4000_set_pm_runtime_state(data, true);
383*5e00708dSGuido Günther 		if  (ret < 0)
384*5e00708dSGuido Günther 			return ret;
385*5e00708dSGuido Günther 
38662a1efb9SPeter Meerwald 		switch (chan->type) {
38762a1efb9SPeter Meerwald 		case IIO_LIGHT:
3881ebc787aSTomas Novotny 			ret = data->chip_spec->measure_light(data, val);
3894a818643SGuido Günther 			if (!ret)
3904a818643SGuido Günther 				ret = IIO_VAL_INT;
3914a818643SGuido Günther 			break;
39262a1efb9SPeter Meerwald 		case IIO_PROXIMITY:
3931ebc787aSTomas Novotny 			ret = data->chip_spec->measure_proximity(data, val);
3944a818643SGuido Günther 			if (!ret)
3954a818643SGuido Günther 				ret = IIO_VAL_INT;
3964a818643SGuido Günther 			break;
39762a1efb9SPeter Meerwald 		default:
3984a818643SGuido Günther 			ret = -EINVAL;
39962a1efb9SPeter Meerwald 		}
400*5e00708dSGuido Günther 		vcnl4000_set_pm_runtime_state(data, false);
4014a818643SGuido Günther 		return ret;
40262a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
4035d693139SPeter Meerwald-Stadler 		if (chan->type != IIO_LIGHT)
4045d693139SPeter Meerwald-Stadler 			return -EINVAL;
4055d693139SPeter Meerwald-Stadler 
40662a1efb9SPeter Meerwald 		*val = 0;
4071ebc787aSTomas Novotny 		*val2 = data->al_scale;
4085d693139SPeter Meerwald-Stadler 		return IIO_VAL_INT_PLUS_MICRO;
40962a1efb9SPeter Meerwald 	default:
4105d693139SPeter Meerwald-Stadler 		return -EINVAL;
41162a1efb9SPeter Meerwald 	}
41262a1efb9SPeter Meerwald }
41362a1efb9SPeter Meerwald 
41462a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = {
41562a1efb9SPeter Meerwald 	.read_raw = vcnl4000_read_raw,
41662a1efb9SPeter Meerwald };
41762a1efb9SPeter Meerwald 
418fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client,
41962a1efb9SPeter Meerwald 			  const struct i2c_device_id *id)
42062a1efb9SPeter Meerwald {
42162a1efb9SPeter Meerwald 	struct vcnl4000_data *data;
42262a1efb9SPeter Meerwald 	struct iio_dev *indio_dev;
4231ebc787aSTomas Novotny 	int ret;
42462a1efb9SPeter Meerwald 
4252669d723SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
42662a1efb9SPeter Meerwald 	if (!indio_dev)
42762a1efb9SPeter Meerwald 		return -ENOMEM;
42862a1efb9SPeter Meerwald 
42962a1efb9SPeter Meerwald 	data = iio_priv(indio_dev);
43062a1efb9SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
43162a1efb9SPeter Meerwald 	data->client = client;
4321ebc787aSTomas Novotny 	data->id = id->driver_data;
4331ebc787aSTomas Novotny 	data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
43462a1efb9SPeter Meerwald 
4351ebc787aSTomas Novotny 	ret = data->chip_spec->init(data);
43662a1efb9SPeter Meerwald 	if (ret < 0)
4372669d723SPeter Meerwald 		return ret;
43862a1efb9SPeter Meerwald 
439d978bfddSPeter Meerwald-Stadler 	dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
4401ebc787aSTomas Novotny 		data->chip_spec->prod, data->rev);
44162a1efb9SPeter Meerwald 
44262a1efb9SPeter Meerwald 	indio_dev->dev.parent = &client->dev;
44362a1efb9SPeter Meerwald 	indio_dev->info = &vcnl4000_info;
44462a1efb9SPeter Meerwald 	indio_dev->channels = vcnl4000_channels;
44562a1efb9SPeter Meerwald 	indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
44662a1efb9SPeter Meerwald 	indio_dev->name = VCNL4000_DRV_NAME;
44762a1efb9SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
44862a1efb9SPeter Meerwald 
449*5e00708dSGuido Günther 	ret = pm_runtime_set_active(&client->dev);
450*5e00708dSGuido Günther 	if (ret < 0)
451*5e00708dSGuido Günther 		goto fail_poweroff;
452*5e00708dSGuido Günther 
453*5e00708dSGuido Günther 	ret = iio_device_register(indio_dev);
454*5e00708dSGuido Günther 	if (ret < 0)
455*5e00708dSGuido Günther 		goto fail_poweroff;
456*5e00708dSGuido Günther 
457*5e00708dSGuido Günther 	pm_runtime_enable(&client->dev);
458*5e00708dSGuido Günther 	pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
459*5e00708dSGuido Günther 	pm_runtime_use_autosuspend(&client->dev);
460*5e00708dSGuido Günther 
461*5e00708dSGuido Günther 	return 0;
462*5e00708dSGuido Günther fail_poweroff:
463*5e00708dSGuido Günther 	data->chip_spec->set_power_state(data, false);
464*5e00708dSGuido Günther 	return ret;
46562a1efb9SPeter Meerwald }
46662a1efb9SPeter Meerwald 
467ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = {
468ebd457d5SAngus Ainslie (Purism) 	{
469ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4000",
4701436a78cSMarco Felsch 		.data = (void *)VCNL4000,
471ebd457d5SAngus Ainslie (Purism) 	},
472ebd457d5SAngus Ainslie (Purism) 	{
473ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4010",
4741436a78cSMarco Felsch 		.data = (void *)VCNL4010,
475ebd457d5SAngus Ainslie (Purism) 	},
476ebd457d5SAngus Ainslie (Purism) 	{
4771436a78cSMarco Felsch 		.compatible = "vishay,vcnl4020",
4781436a78cSMarco Felsch 		.data = (void *)VCNL4010,
479ebd457d5SAngus Ainslie (Purism) 	},
480ebd457d5SAngus Ainslie (Purism) 	{
4817fd1c260SMarco Felsch 		.compatible = "vishay,vcnl4040",
4827fd1c260SMarco Felsch 		.data = (void *)VCNL4040,
4837fd1c260SMarco Felsch 	},
4847fd1c260SMarco Felsch 	{
485ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4200",
4861436a78cSMarco Felsch 		.data = (void *)VCNL4200,
487ebd457d5SAngus Ainslie (Purism) 	},
488ebd457d5SAngus Ainslie (Purism) 	{},
489ebd457d5SAngus Ainslie (Purism) };
490ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
491ebd457d5SAngus Ainslie (Purism) 
492*5e00708dSGuido Günther static int vcnl4000_remove(struct i2c_client *client)
493*5e00708dSGuido Günther {
494*5e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
495*5e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
496*5e00708dSGuido Günther 
497*5e00708dSGuido Günther 	pm_runtime_dont_use_autosuspend(&client->dev);
498*5e00708dSGuido Günther 	pm_runtime_disable(&client->dev);
499*5e00708dSGuido Günther 	iio_device_unregister(indio_dev);
500*5e00708dSGuido Günther 	pm_runtime_set_suspended(&client->dev);
501*5e00708dSGuido Günther 
502*5e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
503*5e00708dSGuido Günther }
504*5e00708dSGuido Günther 
505*5e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
506*5e00708dSGuido Günther {
507*5e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
508*5e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
509*5e00708dSGuido Günther 
510*5e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
511*5e00708dSGuido Günther }
512*5e00708dSGuido Günther 
513*5e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
514*5e00708dSGuido Günther {
515*5e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
516*5e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
517*5e00708dSGuido Günther 
518*5e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
519*5e00708dSGuido Günther }
520*5e00708dSGuido Günther 
521*5e00708dSGuido Günther static const struct dev_pm_ops vcnl4000_pm_ops = {
522*5e00708dSGuido Günther 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
523*5e00708dSGuido Günther 				pm_runtime_force_resume)
524*5e00708dSGuido Günther 	SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
525*5e00708dSGuido Günther 			   vcnl4000_runtime_resume, NULL)
526*5e00708dSGuido Günther };
527*5e00708dSGuido Günther 
52862a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = {
52962a1efb9SPeter Meerwald 	.driver = {
53062a1efb9SPeter Meerwald 		.name   = VCNL4000_DRV_NAME,
531*5e00708dSGuido Günther 		.pm	= &vcnl4000_pm_ops,
532ebd457d5SAngus Ainslie (Purism) 		.of_match_table = vcnl_4000_of_match,
53362a1efb9SPeter Meerwald 	},
53462a1efb9SPeter Meerwald 	.probe  = vcnl4000_probe,
53562a1efb9SPeter Meerwald 	.id_table = vcnl4000_id,
536*5e00708dSGuido Günther 	.remove	= vcnl4000_remove,
53762a1efb9SPeter Meerwald };
53862a1efb9SPeter Meerwald 
53962a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver);
54062a1efb9SPeter Meerwald 
54162a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
54262a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
54362a1efb9SPeter Meerwald MODULE_LICENSE("GPL");
544