xref: /linux/drivers/iio/light/vcnl4000.c (revision add9846676600b35e1bbb6bbc2703746687ae4ec)
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
88fe78d52SMathieu Othacehe  * Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com>
962a1efb9SPeter Meerwald  *
10be38866fSTomas Novotny  * IIO driver for:
11be38866fSTomas Novotny  *   VCNL4000/10/20 (7-bit I2C slave address 0x13)
125a441aadSAngus Ainslie (Purism)  *   VCNL4040 (7-bit I2C slave address 0x60)
13be38866fSTomas Novotny  *   VCNL4200 (7-bit I2C slave address 0x51)
1462a1efb9SPeter Meerwald  *
1562a1efb9SPeter Meerwald  * TODO:
1662a1efb9SPeter Meerwald  *   allow to adjust IR current
178fe78d52SMathieu Othacehe  *   interrupts (VCNL4040, VCNL4200)
1862a1efb9SPeter Meerwald  */
1962a1efb9SPeter Meerwald 
2085e2c6a2SMårten Lindahl #include <linux/bitfield.h>
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>
26d35567fcSMathieu Othacehe #include <linux/interrupt.h>
277f865127SAstrid Rost #include <linux/units.h>
2862a1efb9SPeter Meerwald 
298fe78d52SMathieu Othacehe #include <linux/iio/buffer.h>
30d35567fcSMathieu Othacehe #include <linux/iio/events.h>
3162a1efb9SPeter Meerwald #include <linux/iio/iio.h>
3262a1efb9SPeter Meerwald #include <linux/iio/sysfs.h>
338fe78d52SMathieu Othacehe #include <linux/iio/trigger.h>
348fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h>
358fe78d52SMathieu Othacehe #include <linux/iio/triggered_buffer.h>
3662a1efb9SPeter Meerwald 
3762a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000"
381ebc787aSTomas Novotny #define VCNL4000_PROD_ID	0x01
391ebc787aSTomas Novotny #define VCNL4010_PROD_ID	0x02 /* for VCNL4020, VCNL4010 */
405a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID	0x86
41be38866fSTomas Novotny #define VCNL4200_PROD_ID	0x58
4262a1efb9SPeter Meerwald 
4362a1efb9SPeter Meerwald #define VCNL4000_COMMAND	0x80 /* Command register */
4462a1efb9SPeter Meerwald #define VCNL4000_PROD_REV	0x81 /* Product ID and Revision ID */
45d35567fcSMathieu Othacehe #define VCNL4010_PROX_RATE      0x82 /* Proximity rate */
4662a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT	0x83 /* IR LED current for proximity mode */
4762a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM	0x84 /* Ambient light parameter register */
48d35567fcSMathieu Othacehe #define VCNL4010_ALS_PARAM      0x84 /* ALS rate */
4962a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI	0x85 /* Ambient light result register, MSB */
5062a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO	0x86 /* Ambient light result register, LSB */
5162a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI	0x87 /* Proximity result register, MSB */
5262a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO	0x88 /* Proximity result register, LSB */
5362a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ	0x89 /* Proximity test signal frequency */
54d35567fcSMathieu Othacehe #define VCNL4010_INT_CTRL	0x89 /* Interrupt control */
5562a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ	0x8a /* Proximity modulator timing adjustment */
56d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_HI     0x8a /* Low threshold, MSB */
57d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_LO     0x8b /* Low threshold, LSB */
58d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_HI    0x8c /* High threshold, MSB */
59d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_LO    0x8d /* High threshold, LSB */
60d35567fcSMathieu Othacehe #define VCNL4010_ISR		0x8e /* Interrupt status */
6162a1efb9SPeter Meerwald 
62be38866fSTomas Novotny #define VCNL4200_AL_CONF	0x00 /* Ambient light configuration */
63be38866fSTomas Novotny #define VCNL4200_PS_CONF1	0x03 /* Proximity configuration */
64*add98466SAstrid Rost #define VCNL4200_PS_CONF3	0x04 /* Proximity configuration */
6554667612SMårten Lindahl #define VCNL4040_PS_THDL_LM	0x06 /* Proximity threshold low */
6654667612SMårten Lindahl #define VCNL4040_PS_THDH_LM	0x07 /* Proximity threshold high */
67bc292aafSAstrid Rost #define VCNL4040_ALS_THDL_LM	0x02 /* Ambient light threshold low */
68bc292aafSAstrid Rost #define VCNL4040_ALS_THDH_LM	0x01 /* Ambient light threshold high */
69be38866fSTomas Novotny #define VCNL4200_PS_DATA	0x08 /* Proximity data */
70be38866fSTomas Novotny #define VCNL4200_AL_DATA	0x09 /* Ambient light data */
7154667612SMårten Lindahl #define VCNL4040_INT_FLAGS	0x0b /* Interrupt register */
72854965b7SAstrid Rost #define VCNL4200_INT_FLAGS	0x0d /* Interrupt register */
73be38866fSTomas Novotny #define VCNL4200_DEV_ID		0x0e /* Device ID, slave address and version */
74be38866fSTomas Novotny 
755a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID		0x0c /* Device ID and version */
765a441aadSAngus Ainslie (Purism) 
7762a1efb9SPeter Meerwald /* Bit masks for COMMAND register */
78ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY		BIT(6) /* ALS data ready? */
79ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY		BIT(5) /* proximity data ready? */
80ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD		BIT(4) /* start on-demand ALS measurement */
81ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD		BIT(3) /* start on-demand proximity measurement */
82d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN		BIT(2) /* start ALS measurement */
83d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN	BIT(1) /* start proximity measurement */
84d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN	BIT(0) /* start self-timed measurement */
85d35567fcSMathieu Othacehe 
86e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN	BIT(0)
87fea2c97dSAstrid Rost #define VCNL4040_ALS_CONF_IT		GENMASK(7, 6) /* Ambient integration time */
88bc292aafSAstrid Rost #define VCNL4040_ALS_CONF_INT_EN	BIT(1) /* Ambient light Interrupt enable */
897f865127SAstrid Rost #define VCNL4040_ALS_CONF_PERS	GENMASK(3, 2) /* Ambient interrupt persistence setting */
90e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN	BIT(0)
9185e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT	GENMASK(3, 1) /* Proximity integration time */
927f865127SAstrid Rost #define VCNL4040_CONF1_PS_PERS	GENMASK(5, 4) /* Proximity interrupt persistence setting */
9354667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT	GENMASK(9, 8) /* Proximity interrupt mode */
94*add98466SAstrid Rost #define VCNL4040_PS_CONF3_MPS		GENMASK(6, 5) /* Proximity multi pulse number */
9554667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY		BIT(8) /* Proximity event cross low threshold */
9654667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE		BIT(9) /* Proximity event cross high threshold */
97bc292aafSAstrid Rost #define VCNL4040_ALS_RISING		BIT(12) /* Ambient Light cross high threshold */
98bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING		BIT(13) /* Ambient Light cross low threshold */
99e21b5b1fSMårten Lindahl 
100d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */
101d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL	BIT(0) /* Select threshold interrupt source */
102d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN	BIT(1) /* Threshold interrupt type */
103d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN	BIT(2) /* Enable on ALS data ready */
104d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN	BIT(3) /* Enable on proximity data ready */
105d35567fcSMathieu Othacehe 
106d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH	0 /* High threshold exceeded */
107d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW	1 /* Low threshold exceeded */
108d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS	2 /* ALS data ready */
109d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY	3 /* Proximity data ready */
110d35567fcSMathieu Othacehe 
111d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \
112d35567fcSMathieu Othacehe 	(BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH))
113d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \
114d35567fcSMathieu Othacehe 	(BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
115d35567fcSMathieu Othacehe 
116f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = {
117f6889c1bSMathieu Othacehe 	{1, 950000},
118f6889c1bSMathieu Othacehe 	{3, 906250},
119f6889c1bSMathieu Othacehe 	{7, 812500},
120f6889c1bSMathieu Othacehe 	{16, 625000},
121f6889c1bSMathieu Othacehe 	{31, 250000},
122f6889c1bSMathieu Othacehe 	{62, 500000},
123f6889c1bSMathieu Othacehe 	{125, 0},
124f6889c1bSMathieu Othacehe 	{250, 0},
125f6889c1bSMathieu Othacehe };
12662a1efb9SPeter Meerwald 
12785e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = {
12885e2c6a2SMårten Lindahl 	{0, 100},
12985e2c6a2SMårten Lindahl 	{0, 150},
13085e2c6a2SMårten Lindahl 	{0, 200},
13185e2c6a2SMårten Lindahl 	{0, 250},
13285e2c6a2SMårten Lindahl 	{0, 300},
13385e2c6a2SMårten Lindahl 	{0, 350},
13485e2c6a2SMårten Lindahl 	{0, 400},
13585e2c6a2SMårten Lindahl 	{0, 800},
13685e2c6a2SMårten Lindahl };
13785e2c6a2SMårten Lindahl 
138e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = {
139e55c96daSAstrid Rost 	{0, 96},
140e55c96daSAstrid Rost 	{0, 144},
141e55c96daSAstrid Rost 	{0, 192},
142e55c96daSAstrid Rost 	{0, 384},
143e55c96daSAstrid Rost 	{0, 768},
144e55c96daSAstrid Rost 	{0, 864},
145e55c96daSAstrid Rost };
146e55c96daSAstrid Rost 
147fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = {
148fea2c97dSAstrid Rost 	{0, 80000},
149fea2c97dSAstrid Rost 	{0, 160000},
150fea2c97dSAstrid Rost 	{0, 320000},
151fea2c97dSAstrid Rost 	{0, 640000},
152fea2c97dSAstrid Rost };
153fea2c97dSAstrid Rost 
154fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = {
155fea2c97dSAstrid Rost 	{0, 50000},
156fea2c97dSAstrid Rost 	{0, 100000},
157fea2c97dSAstrid Rost 	{0, 200000},
158fea2c97dSAstrid Rost 	{0, 400000},
159fea2c97dSAstrid Rost };
160fea2c97dSAstrid Rost 
1617f865127SAstrid Rost static const int vcnl4040_als_persistence[] = {1, 2, 4, 8};
1627f865127SAstrid Rost static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4};
163*add98466SAstrid Rost static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8};
1647f865127SAstrid Rost 
1655e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS	2000 /* before we enter pm_runtime_suspend */
1665e00708dSGuido Günther 
1671ebc787aSTomas Novotny enum vcnl4000_device_ids {
1681ebc787aSTomas Novotny 	VCNL4000,
16950c50b97STomas Novotny 	VCNL4010,
1705a441aadSAngus Ainslie (Purism) 	VCNL4040,
171be38866fSTomas Novotny 	VCNL4200,
172be38866fSTomas Novotny };
173be38866fSTomas Novotny 
174be38866fSTomas Novotny struct vcnl4200_channel {
175be38866fSTomas Novotny 	u8 reg;
176be38866fSTomas Novotny 	ktime_t last_measurement;
177be38866fSTomas Novotny 	ktime_t sampling_rate;
178be38866fSTomas Novotny 	struct mutex lock;
1791ebc787aSTomas Novotny };
1801ebc787aSTomas Novotny 
18162a1efb9SPeter Meerwald struct vcnl4000_data {
18262a1efb9SPeter Meerwald 	struct i2c_client *client;
1831ebc787aSTomas Novotny 	enum vcnl4000_device_ids id;
1841ebc787aSTomas Novotny 	int rev;
1851ebc787aSTomas Novotny 	int al_scale;
18654667612SMårten Lindahl 	u8 ps_int;		/* proximity interrupt mode */
187bc292aafSAstrid Rost 	u8 als_int;		/* ambient light interrupt mode*/
1881ebc787aSTomas Novotny 	const struct vcnl4000_chip_spec *chip_spec;
189be38866fSTomas Novotny 	struct mutex vcnl4000_lock;
190be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_al;
191be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_ps;
192f5a98e1fSGuido Günther 	uint32_t near_level;
19362a1efb9SPeter Meerwald };
19462a1efb9SPeter Meerwald 
1951ebc787aSTomas Novotny struct vcnl4000_chip_spec {
1961ebc787aSTomas Novotny 	const char *prod;
197d35567fcSMathieu Othacehe 	struct iio_chan_spec const *channels;
198d35567fcSMathieu Othacehe 	const int num_channels;
199d35567fcSMathieu Othacehe 	const struct iio_info *info;
200bfb6cfeeSMårten Lindahl 	const struct iio_buffer_setup_ops *buffer_setup_ops;
2011ebc787aSTomas Novotny 	int (*init)(struct vcnl4000_data *data);
2021ebc787aSTomas Novotny 	int (*measure_light)(struct vcnl4000_data *data, int *val);
2031ebc787aSTomas Novotny 	int (*measure_proximity)(struct vcnl4000_data *data, int *val);
2045e00708dSGuido Günther 	int (*set_power_state)(struct vcnl4000_data *data, bool on);
205bfb6cfeeSMårten Lindahl 	irqreturn_t (*irq_thread)(int irq, void *priv);
206bfb6cfeeSMårten Lindahl 	irqreturn_t (*trig_buffer_func)(int irq, void *priv);
207854965b7SAstrid Rost 
208854965b7SAstrid Rost 	u8 int_reg;
209e55c96daSAstrid Rost 	const int(*ps_it_times)[][2];
210e55c96daSAstrid Rost 	const int num_ps_it_times;
211fea2c97dSAstrid Rost 	const int(*als_it_times)[][2];
212fea2c97dSAstrid Rost 	const int num_als_it_times;
213fea2c97dSAstrid Rost 	const unsigned int ulux_step;
2141ebc787aSTomas Novotny };
2151ebc787aSTomas Novotny 
21662a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = {
2171ebc787aSTomas Novotny 	{ "vcnl4000", VCNL4000 },
21850c50b97STomas Novotny 	{ "vcnl4010", VCNL4010 },
21950c50b97STomas Novotny 	{ "vcnl4020", VCNL4010 },
2205a441aadSAngus Ainslie (Purism) 	{ "vcnl4040", VCNL4040 },
221be38866fSTomas Novotny 	{ "vcnl4200", VCNL4200 },
22262a1efb9SPeter Meerwald 	{ }
22362a1efb9SPeter Meerwald };
22462a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
22562a1efb9SPeter Meerwald 
2265e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
2275e00708dSGuido Günther {
2285e00708dSGuido Günther 	/* no suspend op */
2295e00708dSGuido Günther 	return 0;
2305e00708dSGuido Günther }
2315e00708dSGuido Günther 
2321ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data)
2331ebc787aSTomas Novotny {
2341ebc787aSTomas Novotny 	int ret, prod_id;
2351ebc787aSTomas Novotny 
2361ebc787aSTomas Novotny 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
2371ebc787aSTomas Novotny 	if (ret < 0)
2381ebc787aSTomas Novotny 		return ret;
2391ebc787aSTomas Novotny 
2401ebc787aSTomas Novotny 	prod_id = ret >> 4;
24158bf9aceSTomas Novotny 	switch (prod_id) {
24258bf9aceSTomas Novotny 	case VCNL4000_PROD_ID:
24358bf9aceSTomas Novotny 		if (data->id != VCNL4000)
24458bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
24558bf9aceSTomas Novotny 					"wrong device id, use vcnl4000");
24658bf9aceSTomas Novotny 		break;
24758bf9aceSTomas Novotny 	case VCNL4010_PROD_ID:
24858bf9aceSTomas Novotny 		if (data->id != VCNL4010)
24958bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
25058bf9aceSTomas Novotny 					"wrong device id, use vcnl4010/4020");
25158bf9aceSTomas Novotny 		break;
25258bf9aceSTomas Novotny 	default:
2531ebc787aSTomas Novotny 		return -ENODEV;
25458bf9aceSTomas Novotny 	}
2551ebc787aSTomas Novotny 
2561ebc787aSTomas Novotny 	data->rev = ret & 0xf;
2571ebc787aSTomas Novotny 	data->al_scale = 250000;
258be38866fSTomas Novotny 
2595e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
260be38866fSTomas Novotny };
261be38866fSTomas Novotny 
262e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en)
2635e00708dSGuido Günther {
2645e00708dSGuido Günther 	int ret;
2655e00708dSGuido Günther 
266e21b5b1fSMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
267e21b5b1fSMårten Lindahl 
268e21b5b1fSMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
269e21b5b1fSMårten Lindahl 	if (ret < 0)
270e21b5b1fSMårten Lindahl 		goto out;
271e21b5b1fSMårten Lindahl 
272e21b5b1fSMårten Lindahl 	if (en)
273e21b5b1fSMårten Lindahl 		ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN;
274e21b5b1fSMårten Lindahl 	else
275e21b5b1fSMårten Lindahl 		ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN;
276e21b5b1fSMårten Lindahl 
277e21b5b1fSMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret);
278e21b5b1fSMårten Lindahl 
279e21b5b1fSMårten Lindahl out:
280e21b5b1fSMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
281e21b5b1fSMårten Lindahl 
282e21b5b1fSMårten Lindahl 	return ret;
283e21b5b1fSMårten Lindahl }
284e21b5b1fSMårten Lindahl 
285e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en)
286e21b5b1fSMårten Lindahl {
287e21b5b1fSMårten Lindahl 	int ret;
288e21b5b1fSMårten Lindahl 
289e21b5b1fSMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
290e21b5b1fSMårten Lindahl 
291e21b5b1fSMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
292e21b5b1fSMårten Lindahl 	if (ret < 0)
293e21b5b1fSMårten Lindahl 		goto out;
294e21b5b1fSMårten Lindahl 
295e21b5b1fSMårten Lindahl 	if (en)
296e21b5b1fSMårten Lindahl 		ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN;
297e21b5b1fSMårten Lindahl 	else
298e21b5b1fSMårten Lindahl 		ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN;
299e21b5b1fSMårten Lindahl 
300e21b5b1fSMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret);
301e21b5b1fSMårten Lindahl 
302e21b5b1fSMårten Lindahl out:
303e21b5b1fSMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
304e21b5b1fSMårten Lindahl 
305e21b5b1fSMårten Lindahl 	return ret;
306e21b5b1fSMårten Lindahl }
307e21b5b1fSMårten Lindahl 
308e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
309e21b5b1fSMårten Lindahl {
310e21b5b1fSMårten Lindahl 	int ret;
311e21b5b1fSMårten Lindahl 
31254667612SMårten Lindahl 	/* Do not power down if interrupts are enabled */
313bc292aafSAstrid Rost 	if (!on && (data->ps_int || data->als_int))
31454667612SMårten Lindahl 		return 0;
31554667612SMårten Lindahl 
316e21b5b1fSMårten Lindahl 	ret = vcnl4000_write_als_enable(data, on);
3175e00708dSGuido Günther 	if (ret < 0)
3185e00708dSGuido Günther 		return ret;
3195e00708dSGuido Günther 
320e21b5b1fSMårten Lindahl 	ret = vcnl4000_write_ps_enable(data, on);
3215e00708dSGuido Günther 	if (ret < 0)
3225e00708dSGuido Günther 		return ret;
3235e00708dSGuido Günther 
3245e00708dSGuido Günther 	if (on) {
3255e00708dSGuido Günther 		/* Wait at least one integration cycle before fetching data */
3265e00708dSGuido Günther 		data->vcnl4200_al.last_measurement = ktime_get();
3275e00708dSGuido Günther 		data->vcnl4200_ps.last_measurement = ktime_get();
3285e00708dSGuido Günther 	}
3295e00708dSGuido Günther 
3305e00708dSGuido Günther 	return 0;
3315e00708dSGuido Günther }
3325e00708dSGuido Günther 
333be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data)
334be38866fSTomas Novotny {
3355a441aadSAngus Ainslie (Purism) 	int ret, id;
336be38866fSTomas Novotny 
337be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
338be38866fSTomas Novotny 	if (ret < 0)
339be38866fSTomas Novotny 		return ret;
340be38866fSTomas Novotny 
3415a441aadSAngus Ainslie (Purism) 	id = ret & 0xff;
3425a441aadSAngus Ainslie (Purism) 
3435a441aadSAngus Ainslie (Purism) 	if (id != VCNL4200_PROD_ID) {
3445a441aadSAngus Ainslie (Purism) 		ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
3455a441aadSAngus Ainslie (Purism) 		if (ret < 0)
3465a441aadSAngus Ainslie (Purism) 			return ret;
3475a441aadSAngus Ainslie (Purism) 
3485a441aadSAngus Ainslie (Purism) 		id = ret & 0xff;
3495a441aadSAngus Ainslie (Purism) 
3505a441aadSAngus Ainslie (Purism) 		if (id != VCNL4040_PROD_ID)
351be38866fSTomas Novotny 			return -ENODEV;
3525a441aadSAngus Ainslie (Purism) 	}
3535a441aadSAngus Ainslie (Purism) 
3545a441aadSAngus Ainslie (Purism) 	dev_dbg(&data->client->dev, "device id 0x%x", id);
355be38866fSTomas Novotny 
356be38866fSTomas Novotny 	data->rev = (ret >> 8) & 0xf;
35754667612SMårten Lindahl 	data->ps_int = 0;
358bc292aafSAstrid Rost 	data->als_int = 0;
359be38866fSTomas Novotny 
360be38866fSTomas Novotny 	data->vcnl4200_al.reg = VCNL4200_AL_DATA;
361be38866fSTomas Novotny 	data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
3625a441aadSAngus Ainslie (Purism) 	switch (id) {
3635a441aadSAngus Ainslie (Purism) 	case VCNL4200_PROD_ID:
364b42aa97eSTomas Novotny 		/* Default wait time is 50ms, add 20% tolerance. */
365b42aa97eSTomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
366b42aa97eSTomas Novotny 		/* Default wait time is 4.8ms, add 20% tolerance. */
367b42aa97eSTomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
3685a441aadSAngus Ainslie (Purism) 		break;
3695a441aadSAngus Ainslie (Purism) 	case VCNL4040_PROD_ID:
3702ca5a879STomas Novotny 		/* Default wait time is 80ms, add 20% tolerance. */
3712ca5a879STomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
3722ca5a879STomas Novotny 		/* Default wait time is 5ms, add 20% tolerance. */
3732ca5a879STomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
3745a441aadSAngus Ainslie (Purism) 		break;
3755a441aadSAngus Ainslie (Purism) 	}
376fea2c97dSAstrid Rost 	data->al_scale = data->chip_spec->ulux_step;
377be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_al.lock);
378be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_ps.lock);
3791ebc787aSTomas Novotny 
3805e00708dSGuido Günther 	ret = data->chip_spec->set_power_state(data, true);
3815e00708dSGuido Günther 	if (ret < 0)
3825e00708dSGuido Günther 		return ret;
3835e00708dSGuido Günther 
3841ebc787aSTomas Novotny 	return 0;
3851ebc787aSTomas Novotny };
3861ebc787aSTomas Novotny 
387816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val)
388816956c3SMathieu Othacehe {
389816956c3SMathieu Othacehe 	s32 ret;
390816956c3SMathieu Othacehe 
391816956c3SMathieu Othacehe 	ret = i2c_smbus_read_word_swapped(data->client, data_reg);
392816956c3SMathieu Othacehe 	if (ret < 0)
393816956c3SMathieu Othacehe 		return ret;
394816956c3SMathieu Othacehe 
395816956c3SMathieu Othacehe 	*val = ret;
396816956c3SMathieu Othacehe 	return 0;
397816956c3SMathieu Othacehe }
398816956c3SMathieu Othacehe 
399816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val)
400816956c3SMathieu Othacehe {
401816956c3SMathieu Othacehe 	if (val > U16_MAX)
402816956c3SMathieu Othacehe 		return -ERANGE;
403816956c3SMathieu Othacehe 
404816956c3SMathieu Othacehe 	return i2c_smbus_write_word_swapped(data->client, data_reg, val);
405816956c3SMathieu Othacehe }
406816956c3SMathieu Othacehe 
407816956c3SMathieu Othacehe 
40862a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
40962a1efb9SPeter Meerwald 				u8 rdy_mask, u8 data_reg, int *val)
41062a1efb9SPeter Meerwald {
41162a1efb9SPeter Meerwald 	int tries = 20;
41262a1efb9SPeter Meerwald 	int ret;
41362a1efb9SPeter Meerwald 
414be38866fSTomas Novotny 	mutex_lock(&data->vcnl4000_lock);
415ff34ed6dSPeter Meerwald-Stadler 
41662a1efb9SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
41762a1efb9SPeter Meerwald 					req_mask);
41862a1efb9SPeter Meerwald 	if (ret < 0)
419ff34ed6dSPeter Meerwald-Stadler 		goto fail;
42062a1efb9SPeter Meerwald 
42162a1efb9SPeter Meerwald 	/* wait for data to become ready */
42262a1efb9SPeter Meerwald 	while (tries--) {
42362a1efb9SPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
42462a1efb9SPeter Meerwald 		if (ret < 0)
425ff34ed6dSPeter Meerwald-Stadler 			goto fail;
42662a1efb9SPeter Meerwald 		if (ret & rdy_mask)
42762a1efb9SPeter Meerwald 			break;
42862a1efb9SPeter Meerwald 		msleep(20); /* measurement takes up to 100 ms */
42962a1efb9SPeter Meerwald 	}
43062a1efb9SPeter Meerwald 
43162a1efb9SPeter Meerwald 	if (tries < 0) {
43262a1efb9SPeter Meerwald 		dev_err(&data->client->dev,
43362a1efb9SPeter Meerwald 			"vcnl4000_measure() failed, data not ready\n");
434ff34ed6dSPeter Meerwald-Stadler 		ret = -EIO;
435ff34ed6dSPeter Meerwald-Stadler 		goto fail;
43662a1efb9SPeter Meerwald 	}
43762a1efb9SPeter Meerwald 
438816956c3SMathieu Othacehe 	ret = vcnl4000_read_data(data, data_reg, val);
43962a1efb9SPeter Meerwald 	if (ret < 0)
440ff34ed6dSPeter Meerwald-Stadler 		goto fail;
44162a1efb9SPeter Meerwald 
442be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
44362a1efb9SPeter Meerwald 
44462a1efb9SPeter Meerwald 	return 0;
445ff34ed6dSPeter Meerwald-Stadler 
446ff34ed6dSPeter Meerwald-Stadler fail:
447be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
448ff34ed6dSPeter Meerwald-Stadler 	return ret;
44962a1efb9SPeter Meerwald }
45062a1efb9SPeter Meerwald 
451be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data,
452be38866fSTomas Novotny 		struct vcnl4200_channel *chan, int *val)
453be38866fSTomas Novotny {
454be38866fSTomas Novotny 	int ret;
455be38866fSTomas Novotny 	s64 delta;
456be38866fSTomas Novotny 	ktime_t next_measurement;
457be38866fSTomas Novotny 
458be38866fSTomas Novotny 	mutex_lock(&chan->lock);
459be38866fSTomas Novotny 
460be38866fSTomas Novotny 	next_measurement = ktime_add(chan->last_measurement,
461be38866fSTomas Novotny 			chan->sampling_rate);
462be38866fSTomas Novotny 	delta = ktime_us_delta(next_measurement, ktime_get());
463be38866fSTomas Novotny 	if (delta > 0)
464be38866fSTomas Novotny 		usleep_range(delta, delta + 500);
465be38866fSTomas Novotny 	chan->last_measurement = ktime_get();
466be38866fSTomas Novotny 
467be38866fSTomas Novotny 	mutex_unlock(&chan->lock);
468be38866fSTomas Novotny 
469be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, chan->reg);
470be38866fSTomas Novotny 	if (ret < 0)
471be38866fSTomas Novotny 		return ret;
472be38866fSTomas Novotny 
473be38866fSTomas Novotny 	*val = ret;
474be38866fSTomas Novotny 
475be38866fSTomas Novotny 	return 0;
476be38866fSTomas Novotny }
477be38866fSTomas Novotny 
4781ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
4791ebc787aSTomas Novotny {
4801ebc787aSTomas Novotny 	return vcnl4000_measure(data,
4811ebc787aSTomas Novotny 			VCNL4000_AL_OD, VCNL4000_AL_RDY,
4821ebc787aSTomas Novotny 			VCNL4000_AL_RESULT_HI, val);
4831ebc787aSTomas Novotny }
4841ebc787aSTomas Novotny 
485be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
486be38866fSTomas Novotny {
487be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_al, val);
488be38866fSTomas Novotny }
489be38866fSTomas Novotny 
4901ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
4911ebc787aSTomas Novotny {
4921ebc787aSTomas Novotny 	return vcnl4000_measure(data,
4931ebc787aSTomas Novotny 			VCNL4000_PS_OD, VCNL4000_PS_RDY,
4941ebc787aSTomas Novotny 			VCNL4000_PS_RESULT_HI, val);
4951ebc787aSTomas Novotny }
4961ebc787aSTomas Novotny 
497be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
498be38866fSTomas Novotny {
499be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_ps, val);
500be38866fSTomas Novotny }
501be38866fSTomas Novotny 
502f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val,
503f6889c1bSMathieu Othacehe 					 int *val2)
504f6889c1bSMathieu Othacehe {
505f6889c1bSMathieu Othacehe 	int ret;
506f6889c1bSMathieu Othacehe 
507f6889c1bSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE);
508f6889c1bSMathieu Othacehe 	if (ret < 0)
509f6889c1bSMathieu Othacehe 		return ret;
510f6889c1bSMathieu Othacehe 
511f6889c1bSMathieu Othacehe 	if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency))
512f6889c1bSMathieu Othacehe 		return -EINVAL;
513f6889c1bSMathieu Othacehe 
514f6889c1bSMathieu Othacehe 	*val = vcnl4010_prox_sampling_frequency[ret][0];
515f6889c1bSMathieu Othacehe 	*val2 = vcnl4010_prox_sampling_frequency[ret][1];
516f6889c1bSMathieu Othacehe 
517f6889c1bSMathieu Othacehe 	return 0;
518f6889c1bSMathieu Othacehe }
519f6889c1bSMathieu Othacehe 
520d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data)
521f5a98e1fSGuido Günther {
522d35567fcSMathieu Othacehe 	int ret;
523f5a98e1fSGuido Günther 
524d35567fcSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
525d35567fcSMathieu Othacehe 	if (ret < 0)
526d35567fcSMathieu Othacehe 		return false;
527d35567fcSMathieu Othacehe 
528d35567fcSMathieu Othacehe 	return !!(ret & VCNL4000_SELF_TIMED_EN);
529f5a98e1fSGuido Günther }
530f5a98e1fSGuido Günther 
5315e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
5325e00708dSGuido Günther {
5335e00708dSGuido Günther 	struct device *dev = &data->client->dev;
5345e00708dSGuido Günther 	int ret;
5355e00708dSGuido Günther 
5365e00708dSGuido Günther 	if (on) {
537db27fdb3SJonathan Cameron 		ret = pm_runtime_resume_and_get(dev);
5385e00708dSGuido Günther 	} else {
5395e00708dSGuido Günther 		pm_runtime_mark_last_busy(dev);
5405e00708dSGuido Günther 		ret = pm_runtime_put_autosuspend(dev);
5415e00708dSGuido Günther 	}
5425e00708dSGuido Günther 
5435e00708dSGuido Günther 	return ret;
5445e00708dSGuido Günther }
5455e00708dSGuido Günther 
546fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
547fea2c97dSAstrid Rost {
548fea2c97dSAstrid Rost 	int ret;
549fea2c97dSAstrid Rost 
550fea2c97dSAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
551fea2c97dSAstrid Rost 	if (ret < 0)
552fea2c97dSAstrid Rost 		return ret;
553fea2c97dSAstrid Rost 
554fea2c97dSAstrid Rost 	ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
555fea2c97dSAstrid Rost 	if (ret >= data->chip_spec->num_als_it_times)
556fea2c97dSAstrid Rost 		return -EINVAL;
557fea2c97dSAstrid Rost 
558fea2c97dSAstrid Rost 	*val = (*data->chip_spec->als_it_times)[ret][0];
559fea2c97dSAstrid Rost 	*val2 = (*data->chip_spec->als_it_times)[ret][1];
560fea2c97dSAstrid Rost 
561fea2c97dSAstrid Rost 	return 0;
562fea2c97dSAstrid Rost }
563fea2c97dSAstrid Rost 
564fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val)
565fea2c97dSAstrid Rost {
566fea2c97dSAstrid Rost 	unsigned int i;
567fea2c97dSAstrid Rost 	int ret;
568fea2c97dSAstrid Rost 	u16 regval;
569fea2c97dSAstrid Rost 
570fea2c97dSAstrid Rost 	for (i = 0; i < data->chip_spec->num_als_it_times; i++) {
571fea2c97dSAstrid Rost 		if (val == (*data->chip_spec->als_it_times)[i][1])
572fea2c97dSAstrid Rost 			break;
573fea2c97dSAstrid Rost 	}
574fea2c97dSAstrid Rost 
575fea2c97dSAstrid Rost 	if (i == data->chip_spec->num_als_it_times)
576fea2c97dSAstrid Rost 		return -EINVAL;
577fea2c97dSAstrid Rost 
578fea2c97dSAstrid Rost 	data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200);
579fea2c97dSAstrid Rost 	data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step,
580fea2c97dSAstrid Rost 			 (*data->chip_spec->als_it_times)[0][1]),
581fea2c97dSAstrid Rost 			 val);
582fea2c97dSAstrid Rost 
583fea2c97dSAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
584fea2c97dSAstrid Rost 
585fea2c97dSAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
586fea2c97dSAstrid Rost 	if (ret < 0)
587fea2c97dSAstrid Rost 		goto out_unlock;
588fea2c97dSAstrid Rost 
589fea2c97dSAstrid Rost 	regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i);
590fea2c97dSAstrid Rost 	regval |= (ret & ~VCNL4040_ALS_CONF_IT);
591fea2c97dSAstrid Rost 	ret = i2c_smbus_write_word_data(data->client,
592fea2c97dSAstrid Rost 					VCNL4200_AL_CONF,
593fea2c97dSAstrid Rost 					regval);
594fea2c97dSAstrid Rost 
595fea2c97dSAstrid Rost out_unlock:
596fea2c97dSAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
597fea2c97dSAstrid Rost 	return ret;
598fea2c97dSAstrid Rost }
599fea2c97dSAstrid Rost 
60085e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
60185e2c6a2SMårten Lindahl {
60285e2c6a2SMårten Lindahl 	int ret;
60385e2c6a2SMårten Lindahl 
60485e2c6a2SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
60585e2c6a2SMårten Lindahl 	if (ret < 0)
60685e2c6a2SMårten Lindahl 		return ret;
60785e2c6a2SMårten Lindahl 
60885e2c6a2SMårten Lindahl 	ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
60985e2c6a2SMårten Lindahl 
610e55c96daSAstrid Rost 	if (ret >= data->chip_spec->num_ps_it_times)
61185e2c6a2SMårten Lindahl 		return -EINVAL;
61285e2c6a2SMårten Lindahl 
613e55c96daSAstrid Rost 	*val = (*data->chip_spec->ps_it_times)[ret][0];
614e55c96daSAstrid Rost 	*val2 = (*data->chip_spec->ps_it_times)[ret][1];
61585e2c6a2SMårten Lindahl 
61685e2c6a2SMårten Lindahl 	return 0;
61785e2c6a2SMårten Lindahl }
61885e2c6a2SMårten Lindahl 
61985e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
62085e2c6a2SMårten Lindahl {
62185e2c6a2SMårten Lindahl 	unsigned int i;
62285e2c6a2SMårten Lindahl 	int ret, index = -1;
62385e2c6a2SMårten Lindahl 	u16 regval;
62485e2c6a2SMårten Lindahl 
625e55c96daSAstrid Rost 	for (i = 0; i < data->chip_spec->num_ps_it_times; i++) {
626e55c96daSAstrid Rost 		if (val == (*data->chip_spec->ps_it_times)[i][1]) {
62785e2c6a2SMårten Lindahl 			index = i;
62885e2c6a2SMårten Lindahl 			break;
62985e2c6a2SMårten Lindahl 		}
63085e2c6a2SMårten Lindahl 	}
63185e2c6a2SMårten Lindahl 
63285e2c6a2SMårten Lindahl 	if (index < 0)
63385e2c6a2SMårten Lindahl 		return -EINVAL;
63485e2c6a2SMårten Lindahl 
635e55c96daSAstrid Rost 	data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC);
636e55c96daSAstrid Rost 
63785e2c6a2SMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
63885e2c6a2SMårten Lindahl 
63985e2c6a2SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
64085e2c6a2SMårten Lindahl 	if (ret < 0)
64185e2c6a2SMårten Lindahl 		goto out;
64285e2c6a2SMårten Lindahl 
64385e2c6a2SMårten Lindahl 	regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) |
64485e2c6a2SMårten Lindahl 	    FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index);
64585e2c6a2SMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
64685e2c6a2SMårten Lindahl 					regval);
64785e2c6a2SMårten Lindahl 
64885e2c6a2SMårten Lindahl out:
64985e2c6a2SMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
65085e2c6a2SMårten Lindahl 	return ret;
65185e2c6a2SMårten Lindahl }
65285e2c6a2SMårten Lindahl 
6537f865127SAstrid Rost static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2)
6547f865127SAstrid Rost {
6557f865127SAstrid Rost 	int ret, ret_pers, it;
6567f865127SAstrid Rost 	int64_t val_c;
6577f865127SAstrid Rost 
6587f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
6597f865127SAstrid Rost 	if (ret < 0)
6607f865127SAstrid Rost 		return ret;
6617f865127SAstrid Rost 
6627f865127SAstrid Rost 	ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret);
6637f865127SAstrid Rost 	if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence))
6647f865127SAstrid Rost 		return -EINVAL;
6657f865127SAstrid Rost 
6667f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
6677f865127SAstrid Rost 	if (it >= data->chip_spec->num_als_it_times)
6687f865127SAstrid Rost 		return -EINVAL;
6697f865127SAstrid Rost 
6707f865127SAstrid Rost 	val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1],
6717f865127SAstrid Rost 			    vcnl4040_als_persistence[ret_pers]);
6727f865127SAstrid Rost 	*val = div_u64_rem(val_c, MICRO, val2);
6737f865127SAstrid Rost 
6747f865127SAstrid Rost 	return IIO_VAL_INT_PLUS_MICRO;
6757f865127SAstrid Rost }
6767f865127SAstrid Rost 
6777f865127SAstrid Rost static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2)
6787f865127SAstrid Rost {
6797f865127SAstrid Rost 	unsigned int i;
6807f865127SAstrid Rost 	int ret, it;
6817f865127SAstrid Rost 	u16 regval;
6827f865127SAstrid Rost 	u64 val_n = mul_u32_u32(val, MICRO) + val2;
6837f865127SAstrid Rost 
6847f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
6857f865127SAstrid Rost 	if (ret < 0)
6867f865127SAstrid Rost 		return ret;
6877f865127SAstrid Rost 
6887f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
6897f865127SAstrid Rost 	if (it >= data->chip_spec->num_als_it_times)
6907f865127SAstrid Rost 		return -EINVAL;
6917f865127SAstrid Rost 
6927f865127SAstrid Rost 	for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) {
6937f865127SAstrid Rost 		if (val_n < mul_u32_u32(vcnl4040_als_persistence[i],
6947f865127SAstrid Rost 					(*data->chip_spec->als_it_times)[it][1]))
6957f865127SAstrid Rost 			break;
6967f865127SAstrid Rost 	}
6977f865127SAstrid Rost 
6987f865127SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
6997f865127SAstrid Rost 
7007f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
7017f865127SAstrid Rost 	if (ret < 0)
7027f865127SAstrid Rost 		goto out_unlock;
7037f865127SAstrid Rost 
7047f865127SAstrid Rost 	regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i);
7057f865127SAstrid Rost 	regval |= (ret & ~VCNL4040_ALS_CONF_PERS);
7067f865127SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
7077f865127SAstrid Rost 					regval);
7087f865127SAstrid Rost 
7097f865127SAstrid Rost out_unlock:
7107f865127SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
7117f865127SAstrid Rost 	return ret;
7127f865127SAstrid Rost }
7137f865127SAstrid Rost 
7147f865127SAstrid Rost static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2)
7157f865127SAstrid Rost {
7167f865127SAstrid Rost 	int ret, ret_pers, it;
7177f865127SAstrid Rost 
7187f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
7197f865127SAstrid Rost 	if (ret < 0)
7207f865127SAstrid Rost 		return ret;
7217f865127SAstrid Rost 
7227f865127SAstrid Rost 	ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret);
7237f865127SAstrid Rost 	if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence))
7247f865127SAstrid Rost 		return -EINVAL;
7257f865127SAstrid Rost 
7267f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
7277f865127SAstrid Rost 	if (it >= data->chip_spec->num_ps_it_times)
7287f865127SAstrid Rost 		return -EINVAL;
7297f865127SAstrid Rost 
7307f865127SAstrid Rost 	*val = (*data->chip_spec->ps_it_times)[it][0];
7317f865127SAstrid Rost 	*val2 = (*data->chip_spec->ps_it_times)[it][1] *
7327f865127SAstrid Rost 		vcnl4040_ps_persistence[ret_pers];
7337f865127SAstrid Rost 
7347f865127SAstrid Rost 	return IIO_VAL_INT_PLUS_MICRO;
7357f865127SAstrid Rost }
7367f865127SAstrid Rost 
7377f865127SAstrid Rost static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2)
7387f865127SAstrid Rost {
7397f865127SAstrid Rost 	int ret, it, i;
7407f865127SAstrid Rost 	u16 regval;
7417f865127SAstrid Rost 
7427f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
7437f865127SAstrid Rost 	if (ret < 0)
7447f865127SAstrid Rost 		return ret;
7457f865127SAstrid Rost 
7467f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
7477f865127SAstrid Rost 	if (it >= data->chip_spec->num_ps_it_times)
7487f865127SAstrid Rost 		return -EINVAL;
7497f865127SAstrid Rost 
7507f865127SAstrid Rost 	if (val > 0)
7517f865127SAstrid Rost 		i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1;
7527f865127SAstrid Rost 	else {
7537f865127SAstrid Rost 		for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) {
7547f865127SAstrid Rost 			if (val2 <= vcnl4040_ps_persistence[i] *
7557f865127SAstrid Rost 					(*data->chip_spec->ps_it_times)[it][1])
7567f865127SAstrid Rost 				break;
7577f865127SAstrid Rost 		}
7587f865127SAstrid Rost 	}
7597f865127SAstrid Rost 
7607f865127SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
7617f865127SAstrid Rost 
7627f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
7637f865127SAstrid Rost 	if (ret < 0)
7647f865127SAstrid Rost 		goto out_unlock;
7657f865127SAstrid Rost 
7667f865127SAstrid Rost 	regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i);
7677f865127SAstrid Rost 	regval |= (ret & ~VCNL4040_CONF1_PS_PERS);
7687f865127SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
7697f865127SAstrid Rost 					regval);
7707f865127SAstrid Rost 
7717f865127SAstrid Rost out_unlock:
7727f865127SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
7737f865127SAstrid Rost 	return ret;
7747f865127SAstrid Rost }
7757f865127SAstrid Rost 
776*add98466SAstrid Rost static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val)
777*add98466SAstrid Rost {
778*add98466SAstrid Rost 	int ret;
779*add98466SAstrid Rost 
780*add98466SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
781*add98466SAstrid Rost 	if (ret < 0)
782*add98466SAstrid Rost 		return ret;
783*add98466SAstrid Rost 
784*add98466SAstrid Rost 	ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret);
785*add98466SAstrid Rost 	if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
786*add98466SAstrid Rost 		return -EINVAL;
787*add98466SAstrid Rost 
788*add98466SAstrid Rost 	*val = vcnl4040_ps_oversampling_ratio[ret];
789*add98466SAstrid Rost 
790*add98466SAstrid Rost 	return ret;
791*add98466SAstrid Rost }
792*add98466SAstrid Rost 
793*add98466SAstrid Rost static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val)
794*add98466SAstrid Rost {
795*add98466SAstrid Rost 	unsigned int i;
796*add98466SAstrid Rost 	int ret;
797*add98466SAstrid Rost 	u16 regval;
798*add98466SAstrid Rost 
799*add98466SAstrid Rost 	for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) {
800*add98466SAstrid Rost 		if (val == vcnl4040_ps_oversampling_ratio[i])
801*add98466SAstrid Rost 			break;
802*add98466SAstrid Rost 	}
803*add98466SAstrid Rost 
804*add98466SAstrid Rost 	if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
805*add98466SAstrid Rost 		return -EINVAL;
806*add98466SAstrid Rost 
807*add98466SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
808*add98466SAstrid Rost 
809*add98466SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
810*add98466SAstrid Rost 	if (ret < 0)
811*add98466SAstrid Rost 		goto out_unlock;
812*add98466SAstrid Rost 
813*add98466SAstrid Rost 	regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i);
814*add98466SAstrid Rost 	regval |= (ret & ~VCNL4040_PS_CONF3_MPS);
815*add98466SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
816*add98466SAstrid Rost 					regval);
817*add98466SAstrid Rost 
818*add98466SAstrid Rost out_unlock:
819*add98466SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
820*add98466SAstrid Rost 	return ret;
821*add98466SAstrid Rost }
822*add98466SAstrid Rost 
82362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev,
82462a1efb9SPeter Meerwald 				struct iio_chan_spec const *chan,
82562a1efb9SPeter Meerwald 				int *val, int *val2, long mask)
82662a1efb9SPeter Meerwald {
8275d693139SPeter Meerwald-Stadler 	int ret;
82862a1efb9SPeter Meerwald 	struct vcnl4000_data *data = iio_priv(indio_dev);
82962a1efb9SPeter Meerwald 
83062a1efb9SPeter Meerwald 	switch (mask) {
83162a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
8325e00708dSGuido Günther 		ret = vcnl4000_set_pm_runtime_state(data, true);
8335e00708dSGuido Günther 		if  (ret < 0)
8345e00708dSGuido Günther 			return ret;
8355e00708dSGuido Günther 
83662a1efb9SPeter Meerwald 		switch (chan->type) {
83762a1efb9SPeter Meerwald 		case IIO_LIGHT:
8381ebc787aSTomas Novotny 			ret = data->chip_spec->measure_light(data, val);
8394a818643SGuido Günther 			if (!ret)
8404a818643SGuido Günther 				ret = IIO_VAL_INT;
8414a818643SGuido Günther 			break;
84262a1efb9SPeter Meerwald 		case IIO_PROXIMITY:
8431ebc787aSTomas Novotny 			ret = data->chip_spec->measure_proximity(data, val);
8444a818643SGuido Günther 			if (!ret)
8454a818643SGuido Günther 				ret = IIO_VAL_INT;
8464a818643SGuido Günther 			break;
84762a1efb9SPeter Meerwald 		default:
8484a818643SGuido Günther 			ret = -EINVAL;
84962a1efb9SPeter Meerwald 		}
8505e00708dSGuido Günther 		vcnl4000_set_pm_runtime_state(data, false);
8514a818643SGuido Günther 		return ret;
85262a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
8535d693139SPeter Meerwald-Stadler 		if (chan->type != IIO_LIGHT)
8545d693139SPeter Meerwald-Stadler 			return -EINVAL;
8555d693139SPeter Meerwald-Stadler 
85662a1efb9SPeter Meerwald 		*val = 0;
8571ebc787aSTomas Novotny 		*val2 = data->al_scale;
8585d693139SPeter Meerwald-Stadler 		return IIO_VAL_INT_PLUS_MICRO;
85985e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
8602be17b68SAstrid Rost 		switch (chan->type) {
861fea2c97dSAstrid Rost 		case IIO_LIGHT:
862fea2c97dSAstrid Rost 			ret = vcnl4040_read_als_it(data, val, val2);
863fea2c97dSAstrid Rost 			break;
8642be17b68SAstrid Rost 		case IIO_PROXIMITY:
86585e2c6a2SMårten Lindahl 			ret = vcnl4040_read_ps_it(data, val, val2);
8662be17b68SAstrid Rost 			break;
8672be17b68SAstrid Rost 		default:
8682be17b68SAstrid Rost 			return -EINVAL;
8692be17b68SAstrid Rost 		}
87085e2c6a2SMårten Lindahl 		if (ret < 0)
87185e2c6a2SMårten Lindahl 			return ret;
87285e2c6a2SMårten Lindahl 		return IIO_VAL_INT_PLUS_MICRO;
873*add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
874*add98466SAstrid Rost 		switch (chan->type) {
875*add98466SAstrid Rost 		case IIO_PROXIMITY:
876*add98466SAstrid Rost 			ret = vcnl4040_read_ps_oversampling_ratio(data, val);
877*add98466SAstrid Rost 			if (ret < 0)
878*add98466SAstrid Rost 				return ret;
879*add98466SAstrid Rost 			return IIO_VAL_INT;
880*add98466SAstrid Rost 		default:
881*add98466SAstrid Rost 			return -EINVAL;
882*add98466SAstrid Rost 		}
88385e2c6a2SMårten Lindahl 	default:
88485e2c6a2SMårten Lindahl 		return -EINVAL;
88585e2c6a2SMårten Lindahl 	}
88685e2c6a2SMårten Lindahl }
88785e2c6a2SMårten Lindahl 
88885e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev,
88985e2c6a2SMårten Lindahl 			      struct iio_chan_spec const *chan,
89085e2c6a2SMårten Lindahl 			      int val, int val2, long mask)
89185e2c6a2SMårten Lindahl {
89285e2c6a2SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
89385e2c6a2SMårten Lindahl 
89485e2c6a2SMårten Lindahl 	switch (mask) {
89585e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
89685e2c6a2SMårten Lindahl 		if (val != 0)
89785e2c6a2SMårten Lindahl 			return -EINVAL;
8982be17b68SAstrid Rost 		switch (chan->type) {
899fea2c97dSAstrid Rost 		case IIO_LIGHT:
900fea2c97dSAstrid Rost 			return vcnl4040_write_als_it(data, val2);
9012be17b68SAstrid Rost 		case IIO_PROXIMITY:
90285e2c6a2SMårten Lindahl 			return vcnl4040_write_ps_it(data, val2);
90385e2c6a2SMårten Lindahl 		default:
90485e2c6a2SMårten Lindahl 			return -EINVAL;
90585e2c6a2SMårten Lindahl 		}
906*add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
907*add98466SAstrid Rost 		switch (chan->type) {
908*add98466SAstrid Rost 		case IIO_PROXIMITY:
909*add98466SAstrid Rost 			return vcnl4040_write_ps_oversampling_ratio(data, val);
910*add98466SAstrid Rost 		default:
911*add98466SAstrid Rost 			return -EINVAL;
912*add98466SAstrid Rost 		}
9132be17b68SAstrid Rost 	default:
9142be17b68SAstrid Rost 		return -EINVAL;
9152be17b68SAstrid Rost 	}
91685e2c6a2SMårten Lindahl }
91785e2c6a2SMårten Lindahl 
91885e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev,
91985e2c6a2SMårten Lindahl 			       struct iio_chan_spec const *chan,
92085e2c6a2SMårten Lindahl 			       const int **vals, int *type, int *length,
92185e2c6a2SMårten Lindahl 			       long mask)
92285e2c6a2SMårten Lindahl {
923e55c96daSAstrid Rost 	struct vcnl4000_data *data = iio_priv(indio_dev);
924e55c96daSAstrid Rost 
92585e2c6a2SMårten Lindahl 	switch (mask) {
92685e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
9272be17b68SAstrid Rost 		switch (chan->type) {
928fea2c97dSAstrid Rost 		case IIO_LIGHT:
929fea2c97dSAstrid Rost 			*vals = (int *)(*data->chip_spec->als_it_times);
930fea2c97dSAstrid Rost 			*length = 2 * data->chip_spec->num_als_it_times;
931fea2c97dSAstrid Rost 			break;
9322be17b68SAstrid Rost 		case IIO_PROXIMITY:
933e55c96daSAstrid Rost 			*vals = (int *)(*data->chip_spec->ps_it_times);
934e55c96daSAstrid Rost 			*length = 2 * data->chip_spec->num_ps_it_times;
9352be17b68SAstrid Rost 			break;
9362be17b68SAstrid Rost 		default:
9372be17b68SAstrid Rost 			return -EINVAL;
9382be17b68SAstrid Rost 		}
9392be17b68SAstrid Rost 		*type = IIO_VAL_INT_PLUS_MICRO;
94085e2c6a2SMårten Lindahl 		return IIO_AVAIL_LIST;
941*add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
942*add98466SAstrid Rost 		switch (chan->type) {
943*add98466SAstrid Rost 		case IIO_PROXIMITY:
944*add98466SAstrid Rost 			*vals = (int *)vcnl4040_ps_oversampling_ratio;
945*add98466SAstrid Rost 			*length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio);
946*add98466SAstrid Rost 			*type = IIO_VAL_INT;
947*add98466SAstrid Rost 			return IIO_AVAIL_LIST;
948*add98466SAstrid Rost 		default:
949*add98466SAstrid Rost 			return -EINVAL;
950*add98466SAstrid Rost 		}
95162a1efb9SPeter Meerwald 	default:
9525d693139SPeter Meerwald-Stadler 		return -EINVAL;
95362a1efb9SPeter Meerwald 	}
95462a1efb9SPeter Meerwald }
95562a1efb9SPeter Meerwald 
956d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev,
957d35567fcSMathieu Othacehe 			     struct iio_chan_spec const *chan,
958d35567fcSMathieu Othacehe 			     int *val, int *val2, long mask)
959d35567fcSMathieu Othacehe {
960d35567fcSMathieu Othacehe 	int ret;
961d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
962d35567fcSMathieu Othacehe 
963d35567fcSMathieu Othacehe 	switch (mask) {
964d35567fcSMathieu Othacehe 	case IIO_CHAN_INFO_RAW:
965d35567fcSMathieu Othacehe 	case IIO_CHAN_INFO_SCALE:
966d35567fcSMathieu Othacehe 		ret = iio_device_claim_direct_mode(indio_dev);
967d35567fcSMathieu Othacehe 		if (ret)
968d35567fcSMathieu Othacehe 			return ret;
969d35567fcSMathieu Othacehe 
970d35567fcSMathieu Othacehe 		/* Protect against event capture. */
971d35567fcSMathieu Othacehe 		if (vcnl4010_is_in_periodic_mode(data)) {
972d35567fcSMathieu Othacehe 			ret = -EBUSY;
973d35567fcSMathieu Othacehe 		} else {
974d35567fcSMathieu Othacehe 			ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
975d35567fcSMathieu Othacehe 						mask);
976d35567fcSMathieu Othacehe 		}
977d35567fcSMathieu Othacehe 
978d35567fcSMathieu Othacehe 		iio_device_release_direct_mode(indio_dev);
979d35567fcSMathieu Othacehe 		return ret;
980f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
981f6889c1bSMathieu Othacehe 		switch (chan->type) {
982f6889c1bSMathieu Othacehe 		case IIO_PROXIMITY:
983f6889c1bSMathieu Othacehe 			ret = vcnl4010_read_proxy_samp_freq(data, val, val2);
984f6889c1bSMathieu Othacehe 			if (ret < 0)
985f6889c1bSMathieu Othacehe 				return ret;
986f6889c1bSMathieu Othacehe 			return IIO_VAL_INT_PLUS_MICRO;
987d35567fcSMathieu Othacehe 		default:
988d35567fcSMathieu Othacehe 			return -EINVAL;
989d35567fcSMathieu Othacehe 		}
990f6889c1bSMathieu Othacehe 	default:
991f6889c1bSMathieu Othacehe 		return -EINVAL;
992f6889c1bSMathieu Othacehe 	}
993f6889c1bSMathieu Othacehe }
994f6889c1bSMathieu Othacehe 
995f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev,
996f6889c1bSMathieu Othacehe 			       struct iio_chan_spec const *chan,
997f6889c1bSMathieu Othacehe 			       const int **vals, int *type, int *length,
998f6889c1bSMathieu Othacehe 			       long mask)
999f6889c1bSMathieu Othacehe {
1000f6889c1bSMathieu Othacehe 	switch (mask) {
1001f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
1002f6889c1bSMathieu Othacehe 		*vals = (int *)vcnl4010_prox_sampling_frequency;
1003f6889c1bSMathieu Othacehe 		*type = IIO_VAL_INT_PLUS_MICRO;
1004f6889c1bSMathieu Othacehe 		*length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency);
1005f6889c1bSMathieu Othacehe 		return IIO_AVAIL_LIST;
1006f6889c1bSMathieu Othacehe 	default:
1007f6889c1bSMathieu Othacehe 		return -EINVAL;
1008f6889c1bSMathieu Othacehe 	}
1009f6889c1bSMathieu Othacehe }
1010f6889c1bSMathieu Othacehe 
1011f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val,
1012f6889c1bSMathieu Othacehe 					  int val2)
1013f6889c1bSMathieu Othacehe {
1014f6889c1bSMathieu Othacehe 	unsigned int i;
1015f6889c1bSMathieu Othacehe 	int index = -1;
1016f6889c1bSMathieu Othacehe 
1017f6889c1bSMathieu Othacehe 	for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) {
1018f6889c1bSMathieu Othacehe 		if (val == vcnl4010_prox_sampling_frequency[i][0] &&
1019f6889c1bSMathieu Othacehe 		    val2 == vcnl4010_prox_sampling_frequency[i][1]) {
1020f6889c1bSMathieu Othacehe 			index = i;
1021f6889c1bSMathieu Othacehe 			break;
1022f6889c1bSMathieu Othacehe 		}
1023f6889c1bSMathieu Othacehe 	}
1024f6889c1bSMathieu Othacehe 
1025f6889c1bSMathieu Othacehe 	if (index < 0)
1026f6889c1bSMathieu Othacehe 		return -EINVAL;
1027f6889c1bSMathieu Othacehe 
1028f6889c1bSMathieu Othacehe 	return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE,
1029f6889c1bSMathieu Othacehe 					 index);
1030f6889c1bSMathieu Othacehe }
1031f6889c1bSMathieu Othacehe 
1032f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev,
1033f6889c1bSMathieu Othacehe 			      struct iio_chan_spec const *chan,
1034f6889c1bSMathieu Othacehe 			      int val, int val2, long mask)
1035f6889c1bSMathieu Othacehe {
1036f6889c1bSMathieu Othacehe 	int ret;
1037f6889c1bSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1038f6889c1bSMathieu Othacehe 
1039f6889c1bSMathieu Othacehe 	ret = iio_device_claim_direct_mode(indio_dev);
1040f6889c1bSMathieu Othacehe 	if (ret)
1041f6889c1bSMathieu Othacehe 		return ret;
1042f6889c1bSMathieu Othacehe 
1043f6889c1bSMathieu Othacehe 	/* Protect against event capture. */
1044f6889c1bSMathieu Othacehe 	if (vcnl4010_is_in_periodic_mode(data)) {
1045f6889c1bSMathieu Othacehe 		ret = -EBUSY;
1046f6889c1bSMathieu Othacehe 		goto end;
1047f6889c1bSMathieu Othacehe 	}
1048f6889c1bSMathieu Othacehe 
1049f6889c1bSMathieu Othacehe 	switch (mask) {
1050f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
1051f6889c1bSMathieu Othacehe 		switch (chan->type) {
1052f6889c1bSMathieu Othacehe 		case IIO_PROXIMITY:
1053f6889c1bSMathieu Othacehe 			ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
1054f6889c1bSMathieu Othacehe 			goto end;
1055f6889c1bSMathieu Othacehe 		default:
1056f6889c1bSMathieu Othacehe 			ret = -EINVAL;
1057f6889c1bSMathieu Othacehe 			goto end;
1058f6889c1bSMathieu Othacehe 		}
1059f6889c1bSMathieu Othacehe 	default:
1060f6889c1bSMathieu Othacehe 		ret = -EINVAL;
1061f6889c1bSMathieu Othacehe 		goto end;
1062f6889c1bSMathieu Othacehe 	}
1063f6889c1bSMathieu Othacehe 
1064f6889c1bSMathieu Othacehe end:
1065f6889c1bSMathieu Othacehe 	iio_device_release_direct_mode(indio_dev);
1066f6889c1bSMathieu Othacehe 	return ret;
1067d35567fcSMathieu Othacehe }
1068d35567fcSMathieu Othacehe 
1069d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev,
1070d35567fcSMathieu Othacehe 			       const struct iio_chan_spec *chan,
1071d35567fcSMathieu Othacehe 			       enum iio_event_type type,
1072d35567fcSMathieu Othacehe 			       enum iio_event_direction dir,
1073d35567fcSMathieu Othacehe 			       enum iio_event_info info,
1074d35567fcSMathieu Othacehe 			       int *val, int *val2)
1075d35567fcSMathieu Othacehe {
1076d35567fcSMathieu Othacehe 	int ret;
1077d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1078d35567fcSMathieu Othacehe 
1079d35567fcSMathieu Othacehe 	switch (info) {
1080d35567fcSMathieu Othacehe 	case IIO_EV_INFO_VALUE:
1081d35567fcSMathieu Othacehe 		switch (dir) {
1082d35567fcSMathieu Othacehe 		case IIO_EV_DIR_RISING:
1083d35567fcSMathieu Othacehe 			ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI,
1084d35567fcSMathieu Othacehe 						 val);
1085d35567fcSMathieu Othacehe 			if (ret < 0)
1086d35567fcSMathieu Othacehe 				return ret;
1087d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1088d35567fcSMathieu Othacehe 		case IIO_EV_DIR_FALLING:
1089d35567fcSMathieu Othacehe 			ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI,
1090d35567fcSMathieu Othacehe 						 val);
1091d35567fcSMathieu Othacehe 			if (ret < 0)
1092d35567fcSMathieu Othacehe 				return ret;
1093d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1094d35567fcSMathieu Othacehe 		default:
1095d35567fcSMathieu Othacehe 			return -EINVAL;
1096d35567fcSMathieu Othacehe 		}
1097d35567fcSMathieu Othacehe 	default:
1098d35567fcSMathieu Othacehe 		return -EINVAL;
1099d35567fcSMathieu Othacehe 	}
1100d35567fcSMathieu Othacehe }
1101d35567fcSMathieu Othacehe 
1102d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev,
1103d35567fcSMathieu Othacehe 				const struct iio_chan_spec *chan,
1104d35567fcSMathieu Othacehe 				enum iio_event_type type,
1105d35567fcSMathieu Othacehe 				enum iio_event_direction dir,
1106d35567fcSMathieu Othacehe 				enum iio_event_info info,
1107d35567fcSMathieu Othacehe 				int val, int val2)
1108d35567fcSMathieu Othacehe {
1109d35567fcSMathieu Othacehe 	int ret;
1110d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1111d35567fcSMathieu Othacehe 
1112d35567fcSMathieu Othacehe 	switch (info) {
1113d35567fcSMathieu Othacehe 	case IIO_EV_INFO_VALUE:
1114d35567fcSMathieu Othacehe 		switch (dir) {
1115d35567fcSMathieu Othacehe 		case IIO_EV_DIR_RISING:
1116d35567fcSMathieu Othacehe 			ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI,
1117d35567fcSMathieu Othacehe 						  val);
1118d35567fcSMathieu Othacehe 			if (ret < 0)
1119d35567fcSMathieu Othacehe 				return ret;
1120d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1121d35567fcSMathieu Othacehe 		case IIO_EV_DIR_FALLING:
1122d35567fcSMathieu Othacehe 			ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI,
1123d35567fcSMathieu Othacehe 						  val);
1124d35567fcSMathieu Othacehe 			if (ret < 0)
1125d35567fcSMathieu Othacehe 				return ret;
1126d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1127d35567fcSMathieu Othacehe 		default:
1128d35567fcSMathieu Othacehe 			return -EINVAL;
1129d35567fcSMathieu Othacehe 		}
1130d35567fcSMathieu Othacehe 	default:
1131d35567fcSMathieu Othacehe 		return -EINVAL;
1132d35567fcSMathieu Othacehe 	}
1133d35567fcSMathieu Othacehe }
1134d35567fcSMathieu Othacehe 
113554667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev,
113654667612SMårten Lindahl 			       const struct iio_chan_spec *chan,
113754667612SMårten Lindahl 			       enum iio_event_type type,
113854667612SMårten Lindahl 			       enum iio_event_direction dir,
113954667612SMårten Lindahl 			       enum iio_event_info info,
114054667612SMårten Lindahl 			       int *val, int *val2)
114154667612SMårten Lindahl {
114254667612SMårten Lindahl 	int ret;
114354667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
114454667612SMårten Lindahl 
11452be17b68SAstrid Rost 	switch (chan->type) {
1146bc292aafSAstrid Rost 	case IIO_LIGHT:
1147bc292aafSAstrid Rost 		switch (info) {
11487f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
11497f865127SAstrid Rost 			return vcnl4040_read_als_period(data, val, val2);
1150bc292aafSAstrid Rost 		case IIO_EV_INFO_VALUE:
1151bc292aafSAstrid Rost 			switch (dir) {
1152bc292aafSAstrid Rost 			case IIO_EV_DIR_RISING:
1153bc292aafSAstrid Rost 				ret = i2c_smbus_read_word_data(data->client,
1154bc292aafSAstrid Rost 							       VCNL4040_ALS_THDH_LM);
1155bc292aafSAstrid Rost 				break;
1156bc292aafSAstrid Rost 			case IIO_EV_DIR_FALLING:
1157bc292aafSAstrid Rost 				ret = i2c_smbus_read_word_data(data->client,
1158bc292aafSAstrid Rost 							       VCNL4040_ALS_THDL_LM);
1159bc292aafSAstrid Rost 				break;
1160bc292aafSAstrid Rost 			default:
1161bc292aafSAstrid Rost 				return -EINVAL;
1162bc292aafSAstrid Rost 			}
1163bc292aafSAstrid Rost 			break;
1164bc292aafSAstrid Rost 		default:
1165bc292aafSAstrid Rost 			return -EINVAL;
1166bc292aafSAstrid Rost 		}
1167bc292aafSAstrid Rost 		break;
11682be17b68SAstrid Rost 	case IIO_PROXIMITY:
11692be17b68SAstrid Rost 		switch (info) {
11707f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
11717f865127SAstrid Rost 			return vcnl4040_read_ps_period(data, val, val2);
11722be17b68SAstrid Rost 		case IIO_EV_INFO_VALUE:
117354667612SMårten Lindahl 			switch (dir) {
117454667612SMårten Lindahl 			case IIO_EV_DIR_RISING:
117554667612SMårten Lindahl 				ret = i2c_smbus_read_word_data(data->client,
117654667612SMårten Lindahl 							       VCNL4040_PS_THDH_LM);
11772be17b68SAstrid Rost 				break;
117854667612SMårten Lindahl 			case IIO_EV_DIR_FALLING:
117954667612SMårten Lindahl 				ret = i2c_smbus_read_word_data(data->client,
118054667612SMårten Lindahl 							       VCNL4040_PS_THDL_LM);
11812be17b68SAstrid Rost 				break;
11822be17b68SAstrid Rost 			default:
11832be17b68SAstrid Rost 				return -EINVAL;
11842be17b68SAstrid Rost 			}
11852be17b68SAstrid Rost 			break;
11862be17b68SAstrid Rost 		default:
11872be17b68SAstrid Rost 			return -EINVAL;
11882be17b68SAstrid Rost 		}
11892be17b68SAstrid Rost 		break;
11902be17b68SAstrid Rost 	default:
11912be17b68SAstrid Rost 		return -EINVAL;
11922be17b68SAstrid Rost 	}
119354667612SMårten Lindahl 	if (ret < 0)
119454667612SMårten Lindahl 		return ret;
119554667612SMårten Lindahl 	*val = ret;
119654667612SMårten Lindahl 	return IIO_VAL_INT;
119754667612SMårten Lindahl }
119854667612SMårten Lindahl 
119954667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev,
120054667612SMårten Lindahl 				const struct iio_chan_spec *chan,
120154667612SMårten Lindahl 				enum iio_event_type type,
120254667612SMårten Lindahl 				enum iio_event_direction dir,
120354667612SMårten Lindahl 				enum iio_event_info info,
120454667612SMårten Lindahl 				int val, int val2)
120554667612SMårten Lindahl {
120654667612SMårten Lindahl 	int ret;
120754667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
120854667612SMårten Lindahl 
12092be17b68SAstrid Rost 	switch (chan->type) {
1210bc292aafSAstrid Rost 	case IIO_LIGHT:
1211bc292aafSAstrid Rost 		switch (info) {
12127f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
12137f865127SAstrid Rost 			return vcnl4040_write_als_period(data, val, val2);
1214bc292aafSAstrid Rost 		case IIO_EV_INFO_VALUE:
1215bc292aafSAstrid Rost 			switch (dir) {
1216bc292aafSAstrid Rost 			case IIO_EV_DIR_RISING:
1217bc292aafSAstrid Rost 				ret = i2c_smbus_write_word_data(data->client,
1218bc292aafSAstrid Rost 								VCNL4040_ALS_THDH_LM,
1219bc292aafSAstrid Rost 								val);
1220bc292aafSAstrid Rost 				break;
1221bc292aafSAstrid Rost 			case IIO_EV_DIR_FALLING:
1222bc292aafSAstrid Rost 				ret = i2c_smbus_write_word_data(data->client,
1223bc292aafSAstrid Rost 								VCNL4040_ALS_THDL_LM,
1224bc292aafSAstrid Rost 								val);
1225bc292aafSAstrid Rost 				break;
1226bc292aafSAstrid Rost 			default:
1227bc292aafSAstrid Rost 				return -EINVAL;
1228bc292aafSAstrid Rost 			}
1229bc292aafSAstrid Rost 			break;
1230bc292aafSAstrid Rost 		default:
1231bc292aafSAstrid Rost 			return -EINVAL;
1232bc292aafSAstrid Rost 		}
1233bc292aafSAstrid Rost 		break;
12342be17b68SAstrid Rost 	case IIO_PROXIMITY:
12352be17b68SAstrid Rost 		switch (info) {
12367f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
12377f865127SAstrid Rost 			return vcnl4040_write_ps_period(data, val, val2);
12382be17b68SAstrid Rost 		case IIO_EV_INFO_VALUE:
123954667612SMårten Lindahl 			switch (dir) {
124054667612SMårten Lindahl 			case IIO_EV_DIR_RISING:
124154667612SMårten Lindahl 				ret = i2c_smbus_write_word_data(data->client,
12422be17b68SAstrid Rost 								VCNL4040_PS_THDH_LM,
12432be17b68SAstrid Rost 								val);
12442be17b68SAstrid Rost 				break;
124554667612SMårten Lindahl 			case IIO_EV_DIR_FALLING:
124654667612SMårten Lindahl 				ret = i2c_smbus_write_word_data(data->client,
12472be17b68SAstrid Rost 								VCNL4040_PS_THDL_LM,
12482be17b68SAstrid Rost 								val);
12492be17b68SAstrid Rost 				break;
125054667612SMårten Lindahl 			default:
125154667612SMårten Lindahl 				return -EINVAL;
125254667612SMårten Lindahl 			}
12532be17b68SAstrid Rost 			break;
12542be17b68SAstrid Rost 		default:
12552be17b68SAstrid Rost 			return -EINVAL;
12562be17b68SAstrid Rost 		}
12572be17b68SAstrid Rost 		break;
12582be17b68SAstrid Rost 	default:
12592be17b68SAstrid Rost 		return -EINVAL;
12602be17b68SAstrid Rost 	}
12612be17b68SAstrid Rost 	if (ret < 0)
12622be17b68SAstrid Rost 		return ret;
12632be17b68SAstrid Rost 	return IIO_VAL_INT;
126454667612SMårten Lindahl }
126554667612SMårten Lindahl 
1266d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
1267d35567fcSMathieu Othacehe {
1268d35567fcSMathieu Othacehe 	int ret;
1269d35567fcSMathieu Othacehe 
1270d35567fcSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL);
1271d35567fcSMathieu Othacehe 	if (ret < 0)
1272d35567fcSMathieu Othacehe 		return false;
1273d35567fcSMathieu Othacehe 
1274d35567fcSMathieu Othacehe 	return !!(ret & VCNL4010_INT_THR_EN);
1275d35567fcSMathieu Othacehe }
1276d35567fcSMathieu Othacehe 
1277d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev,
1278d35567fcSMathieu Othacehe 				      const struct iio_chan_spec *chan,
1279d35567fcSMathieu Othacehe 				      enum iio_event_type type,
1280d35567fcSMathieu Othacehe 				      enum iio_event_direction dir)
1281d35567fcSMathieu Othacehe {
1282d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1283d35567fcSMathieu Othacehe 
1284d35567fcSMathieu Othacehe 	switch (chan->type) {
1285d35567fcSMathieu Othacehe 	case IIO_PROXIMITY:
1286d35567fcSMathieu Othacehe 		return vcnl4010_is_thr_enabled(data);
1287d35567fcSMathieu Othacehe 	default:
1288d35567fcSMathieu Othacehe 		return -EINVAL;
1289d35567fcSMathieu Othacehe 	}
1290d35567fcSMathieu Othacehe }
1291d35567fcSMathieu Othacehe 
1292d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
1293d35567fcSMathieu Othacehe {
1294d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1295d35567fcSMathieu Othacehe 	int ret;
1296d35567fcSMathieu Othacehe 	int icr;
1297d35567fcSMathieu Othacehe 	int command;
1298d35567fcSMathieu Othacehe 
1299d35567fcSMathieu Othacehe 	if (state) {
1300d35567fcSMathieu Othacehe 		ret = iio_device_claim_direct_mode(indio_dev);
1301d35567fcSMathieu Othacehe 		if (ret)
1302d35567fcSMathieu Othacehe 			return ret;
1303d35567fcSMathieu Othacehe 
1304d35567fcSMathieu Othacehe 		/* Enable periodic measurement of proximity data. */
1305d35567fcSMathieu Othacehe 		command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
1306d35567fcSMathieu Othacehe 
1307d35567fcSMathieu Othacehe 		/*
1308d35567fcSMathieu Othacehe 		 * Enable interrupts on threshold, for proximity data by
1309d35567fcSMathieu Othacehe 		 * default.
1310d35567fcSMathieu Othacehe 		 */
1311d35567fcSMathieu Othacehe 		icr = VCNL4010_INT_THR_EN;
1312d35567fcSMathieu Othacehe 	} else {
1313d35567fcSMathieu Othacehe 		if (!vcnl4010_is_thr_enabled(data))
1314d35567fcSMathieu Othacehe 			return 0;
1315d35567fcSMathieu Othacehe 
1316d35567fcSMathieu Othacehe 		command = 0;
1317d35567fcSMathieu Othacehe 		icr = 0;
1318d35567fcSMathieu Othacehe 	}
1319d35567fcSMathieu Othacehe 
1320d35567fcSMathieu Othacehe 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
1321d35567fcSMathieu Othacehe 					command);
1322d35567fcSMathieu Othacehe 	if (ret < 0)
1323d35567fcSMathieu Othacehe 		goto end;
1324d35567fcSMathieu Othacehe 
1325d35567fcSMathieu Othacehe 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr);
1326d35567fcSMathieu Othacehe 
1327d35567fcSMathieu Othacehe end:
1328d35567fcSMathieu Othacehe 	if (state)
1329d35567fcSMathieu Othacehe 		iio_device_release_direct_mode(indio_dev);
1330d35567fcSMathieu Othacehe 
1331d35567fcSMathieu Othacehe 	return ret;
1332d35567fcSMathieu Othacehe }
1333d35567fcSMathieu Othacehe 
1334d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
1335d35567fcSMathieu Othacehe 				       const struct iio_chan_spec *chan,
1336d35567fcSMathieu Othacehe 				       enum iio_event_type type,
1337d35567fcSMathieu Othacehe 				       enum iio_event_direction dir,
1338d35567fcSMathieu Othacehe 				       int state)
1339d35567fcSMathieu Othacehe {
1340d35567fcSMathieu Othacehe 	switch (chan->type) {
1341d35567fcSMathieu Othacehe 	case IIO_PROXIMITY:
1342d35567fcSMathieu Othacehe 		return vcnl4010_config_threshold(indio_dev, state);
1343d35567fcSMathieu Othacehe 	default:
1344d35567fcSMathieu Othacehe 		return -EINVAL;
1345d35567fcSMathieu Othacehe 	}
1346d35567fcSMathieu Othacehe }
1347d35567fcSMathieu Othacehe 
134854667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
134954667612SMårten Lindahl 				      const struct iio_chan_spec *chan,
135054667612SMårten Lindahl 				      enum iio_event_type type,
135154667612SMårten Lindahl 				      enum iio_event_direction dir)
135254667612SMårten Lindahl {
135354667612SMårten Lindahl 	int ret;
135454667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
135554667612SMårten Lindahl 
13562be17b68SAstrid Rost 	switch (chan->type) {
1357bc292aafSAstrid Rost 	case IIO_LIGHT:
1358bc292aafSAstrid Rost 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
1359bc292aafSAstrid Rost 		if (ret < 0)
1360bc292aafSAstrid Rost 			return ret;
1361bc292aafSAstrid Rost 
1362bc292aafSAstrid Rost 		data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret);
1363bc292aafSAstrid Rost 
1364bc292aafSAstrid Rost 		return data->als_int;
13652be17b68SAstrid Rost 	case IIO_PROXIMITY:
136654667612SMårten Lindahl 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
136754667612SMårten Lindahl 		if (ret < 0)
136854667612SMårten Lindahl 			return ret;
136954667612SMårten Lindahl 
137054667612SMårten Lindahl 		data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
137154667612SMårten Lindahl 
137254667612SMårten Lindahl 		return (dir == IIO_EV_DIR_RISING) ?
137354667612SMårten Lindahl 			FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
137454667612SMårten Lindahl 			FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
13752be17b68SAstrid Rost 	default:
13762be17b68SAstrid Rost 		return -EINVAL;
13772be17b68SAstrid Rost 	}
137854667612SMårten Lindahl }
137954667612SMårten Lindahl 
138054667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
138154667612SMårten Lindahl 				       const struct iio_chan_spec *chan,
138254667612SMårten Lindahl 				       enum iio_event_type type,
138354667612SMårten Lindahl 				       enum iio_event_direction dir, int state)
138454667612SMårten Lindahl {
13852be17b68SAstrid Rost 	int ret = -EINVAL;
138654667612SMårten Lindahl 	u16 val, mask;
138754667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
138854667612SMårten Lindahl 
138954667612SMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
139054667612SMårten Lindahl 
13912be17b68SAstrid Rost 	switch (chan->type) {
1392bc292aafSAstrid Rost 	case IIO_LIGHT:
1393bc292aafSAstrid Rost 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
1394bc292aafSAstrid Rost 		if (ret < 0)
1395bc292aafSAstrid Rost 			goto out;
1396bc292aafSAstrid Rost 
1397bc292aafSAstrid Rost 		mask = VCNL4040_ALS_CONF_INT_EN;
1398bc292aafSAstrid Rost 		if (state)
1399bc292aafSAstrid Rost 			val = (ret | mask);
1400bc292aafSAstrid Rost 		else
1401bc292aafSAstrid Rost 			val = (ret & ~mask);
1402bc292aafSAstrid Rost 
1403bc292aafSAstrid Rost 		data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
1404bc292aafSAstrid Rost 		ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
1405bc292aafSAstrid Rost 						val);
1406bc292aafSAstrid Rost 		break;
14072be17b68SAstrid Rost 	case IIO_PROXIMITY:
140854667612SMårten Lindahl 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
140954667612SMårten Lindahl 		if (ret < 0)
141054667612SMårten Lindahl 			goto out;
141154667612SMårten Lindahl 
141254667612SMårten Lindahl 		if (dir == IIO_EV_DIR_RISING)
141354667612SMårten Lindahl 			mask = VCNL4040_PS_IF_AWAY;
141454667612SMårten Lindahl 		else
141554667612SMårten Lindahl 			mask = VCNL4040_PS_IF_CLOSE;
141654667612SMårten Lindahl 
141754667612SMårten Lindahl 		val = state ? (ret | mask) : (ret & ~mask);
141854667612SMårten Lindahl 
141954667612SMårten Lindahl 		data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
14202be17b68SAstrid Rost 		ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
14212be17b68SAstrid Rost 						val);
14222be17b68SAstrid Rost 		break;
14232be17b68SAstrid Rost 	default:
14242be17b68SAstrid Rost 		break;
14252be17b68SAstrid Rost 	}
142654667612SMårten Lindahl 
142754667612SMårten Lindahl out:
142854667612SMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
1429bc292aafSAstrid Rost 	data->chip_spec->set_power_state(data, data->ps_int || data->als_int);
143054667612SMårten Lindahl 
143154667612SMårten Lindahl 	return ret;
143254667612SMårten Lindahl }
143354667612SMårten Lindahl 
143454667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
143554667612SMårten Lindahl {
143654667612SMårten Lindahl 	struct iio_dev *indio_dev = p;
143754667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
143854667612SMårten Lindahl 	int ret;
143954667612SMårten Lindahl 
1440854965b7SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
144154667612SMårten Lindahl 	if (ret < 0)
144254667612SMårten Lindahl 		return IRQ_HANDLED;
144354667612SMårten Lindahl 
144454667612SMårten Lindahl 	if (ret & VCNL4040_PS_IF_CLOSE) {
144554667612SMårten Lindahl 		iio_push_event(indio_dev,
144654667612SMårten Lindahl 			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
144754667612SMårten Lindahl 						    IIO_EV_TYPE_THRESH,
144854667612SMårten Lindahl 						    IIO_EV_DIR_RISING),
144954667612SMårten Lindahl 			       iio_get_time_ns(indio_dev));
145054667612SMårten Lindahl 	}
145154667612SMårten Lindahl 
145254667612SMårten Lindahl 	if (ret & VCNL4040_PS_IF_AWAY) {
145354667612SMårten Lindahl 		iio_push_event(indio_dev,
145454667612SMårten Lindahl 			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
145554667612SMårten Lindahl 						    IIO_EV_TYPE_THRESH,
145654667612SMårten Lindahl 						    IIO_EV_DIR_FALLING),
145754667612SMårten Lindahl 			       iio_get_time_ns(indio_dev));
145854667612SMårten Lindahl 	}
145954667612SMårten Lindahl 
1460bc292aafSAstrid Rost 	if (ret & VCNL4040_ALS_FALLING) {
1461bc292aafSAstrid Rost 		iio_push_event(indio_dev,
1462bc292aafSAstrid Rost 			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
1463bc292aafSAstrid Rost 						    IIO_EV_TYPE_THRESH,
1464bc292aafSAstrid Rost 						    IIO_EV_DIR_FALLING),
1465bc292aafSAstrid Rost 			       iio_get_time_ns(indio_dev));
1466bc292aafSAstrid Rost 	}
1467bc292aafSAstrid Rost 
1468bc292aafSAstrid Rost 	if (ret & VCNL4040_ALS_RISING) {
1469bc292aafSAstrid Rost 		iio_push_event(indio_dev,
1470bc292aafSAstrid Rost 			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
1471bc292aafSAstrid Rost 						    IIO_EV_TYPE_THRESH,
1472bc292aafSAstrid Rost 						    IIO_EV_DIR_RISING),
1473bc292aafSAstrid Rost 			       iio_get_time_ns(indio_dev));
1474bc292aafSAstrid Rost 	}
1475bc292aafSAstrid Rost 
147654667612SMårten Lindahl 	return IRQ_HANDLED;
147754667612SMårten Lindahl }
147854667612SMårten Lindahl 
1479d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
1480d35567fcSMathieu Othacehe 					uintptr_t priv,
1481d35567fcSMathieu Othacehe 					const struct iio_chan_spec *chan,
1482d35567fcSMathieu Othacehe 					char *buf)
1483d35567fcSMathieu Othacehe {
1484d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1485d35567fcSMathieu Othacehe 
1486d35567fcSMathieu Othacehe 	return sprintf(buf, "%u\n", data->near_level);
1487d35567fcSMathieu Othacehe }
1488d35567fcSMathieu Othacehe 
14893a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
14903a52d32aSMårten Lindahl {
14913a52d32aSMårten Lindahl 	struct iio_dev *indio_dev = p;
14923a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
14933a52d32aSMårten Lindahl 	unsigned long isr;
14943a52d32aSMårten Lindahl 	int ret;
14953a52d32aSMårten Lindahl 
14963a52d32aSMårten Lindahl 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
14973a52d32aSMårten Lindahl 	if (ret < 0)
14983a52d32aSMårten Lindahl 		goto end;
14993a52d32aSMårten Lindahl 
15003a52d32aSMårten Lindahl 	isr = ret;
15013a52d32aSMårten Lindahl 
15023a52d32aSMårten Lindahl 	if (isr & VCNL4010_INT_THR) {
15033a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_THR_LOW, &isr)) {
15043a52d32aSMårten Lindahl 			iio_push_event(indio_dev,
15053a52d32aSMårten Lindahl 				       IIO_UNMOD_EVENT_CODE(
15063a52d32aSMårten Lindahl 					       IIO_PROXIMITY,
15073a52d32aSMårten Lindahl 					       1,
15083a52d32aSMårten Lindahl 					       IIO_EV_TYPE_THRESH,
15093a52d32aSMårten Lindahl 					       IIO_EV_DIR_FALLING),
15103a52d32aSMårten Lindahl 				       iio_get_time_ns(indio_dev));
15113a52d32aSMårten Lindahl 		}
15123a52d32aSMårten Lindahl 
15133a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) {
15143a52d32aSMårten Lindahl 			iio_push_event(indio_dev,
15153a52d32aSMårten Lindahl 				       IIO_UNMOD_EVENT_CODE(
15163a52d32aSMårten Lindahl 					       IIO_PROXIMITY,
15173a52d32aSMårten Lindahl 					       1,
15183a52d32aSMårten Lindahl 					       IIO_EV_TYPE_THRESH,
15193a52d32aSMårten Lindahl 					       IIO_EV_DIR_RISING),
15203a52d32aSMårten Lindahl 				       iio_get_time_ns(indio_dev));
15213a52d32aSMårten Lindahl 		}
15223a52d32aSMårten Lindahl 
15233a52d32aSMårten Lindahl 		i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
15243a52d32aSMårten Lindahl 					  isr & VCNL4010_INT_THR);
15253a52d32aSMårten Lindahl 	}
15263a52d32aSMårten Lindahl 
15273a52d32aSMårten Lindahl 	if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
1528f700e55eSMehdi Djait 		iio_trigger_poll_nested(indio_dev->trig);
15293a52d32aSMårten Lindahl 
15303a52d32aSMårten Lindahl end:
15313a52d32aSMårten Lindahl 	return IRQ_HANDLED;
15323a52d32aSMårten Lindahl }
15333a52d32aSMårten Lindahl 
15343a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p)
15353a52d32aSMårten Lindahl {
15363a52d32aSMårten Lindahl 	struct iio_poll_func *pf = p;
15373a52d32aSMårten Lindahl 	struct iio_dev *indio_dev = pf->indio_dev;
15383a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
15393a52d32aSMårten Lindahl 	const unsigned long *active_scan_mask = indio_dev->active_scan_mask;
15403a52d32aSMårten Lindahl 	u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */
15413a52d32aSMårten Lindahl 	bool data_read = false;
15423a52d32aSMårten Lindahl 	unsigned long isr;
15433a52d32aSMårten Lindahl 	int val = 0;
15443a52d32aSMårten Lindahl 	int ret;
15453a52d32aSMårten Lindahl 
15463a52d32aSMårten Lindahl 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
15473a52d32aSMårten Lindahl 	if (ret < 0)
15483a52d32aSMårten Lindahl 		goto end;
15493a52d32aSMårten Lindahl 
15503a52d32aSMårten Lindahl 	isr = ret;
15513a52d32aSMårten Lindahl 
15523a52d32aSMårten Lindahl 	if (test_bit(0, active_scan_mask)) {
15533a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) {
15543a52d32aSMårten Lindahl 			ret = vcnl4000_read_data(data,
15553a52d32aSMårten Lindahl 						 VCNL4000_PS_RESULT_HI,
15563a52d32aSMårten Lindahl 						 &val);
15573a52d32aSMårten Lindahl 			if (ret < 0)
15583a52d32aSMårten Lindahl 				goto end;
15593a52d32aSMårten Lindahl 
15603a52d32aSMårten Lindahl 			buffer[0] = val;
15613a52d32aSMårten Lindahl 			data_read = true;
15623a52d32aSMårten Lindahl 		}
15633a52d32aSMårten Lindahl 	}
15643a52d32aSMårten Lindahl 
15653a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
15663a52d32aSMårten Lindahl 					isr & VCNL4010_INT_DRDY);
15673a52d32aSMårten Lindahl 	if (ret < 0)
15683a52d32aSMårten Lindahl 		goto end;
15693a52d32aSMårten Lindahl 
15703a52d32aSMårten Lindahl 	if (!data_read)
15713a52d32aSMårten Lindahl 		goto end;
15723a52d32aSMårten Lindahl 
15733a52d32aSMårten Lindahl 	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
15743a52d32aSMårten Lindahl 					   iio_get_time_ns(indio_dev));
15753a52d32aSMårten Lindahl 
15763a52d32aSMårten Lindahl end:
15773a52d32aSMårten Lindahl 	iio_trigger_notify_done(indio_dev->trig);
15783a52d32aSMårten Lindahl 	return IRQ_HANDLED;
15793a52d32aSMårten Lindahl }
15803a52d32aSMårten Lindahl 
15813a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
15823a52d32aSMårten Lindahl {
15833a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
15843a52d32aSMårten Lindahl 	int ret;
15853a52d32aSMårten Lindahl 	int cmd;
15863a52d32aSMårten Lindahl 
15873a52d32aSMårten Lindahl 	/* Do not enable the buffer if we are already capturing events. */
15883a52d32aSMårten Lindahl 	if (vcnl4010_is_in_periodic_mode(data))
15893a52d32aSMårten Lindahl 		return -EBUSY;
15903a52d32aSMårten Lindahl 
15913a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
15923a52d32aSMårten Lindahl 					VCNL4010_INT_PROX_EN);
15933a52d32aSMårten Lindahl 	if (ret < 0)
15943a52d32aSMårten Lindahl 		return ret;
15953a52d32aSMårten Lindahl 
15963a52d32aSMårten Lindahl 	cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
15973a52d32aSMårten Lindahl 	return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd);
15983a52d32aSMårten Lindahl }
15993a52d32aSMårten Lindahl 
16003a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
16013a52d32aSMårten Lindahl {
16023a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
16033a52d32aSMårten Lindahl 	int ret;
16043a52d32aSMårten Lindahl 
16053a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
16063a52d32aSMårten Lindahl 	if (ret < 0)
16073a52d32aSMårten Lindahl 		return ret;
16083a52d32aSMårten Lindahl 
16093a52d32aSMårten Lindahl 	return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
16103a52d32aSMårten Lindahl }
16113a52d32aSMårten Lindahl 
16123a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
16133a52d32aSMårten Lindahl 	.postenable = &vcnl4010_buffer_postenable,
16143a52d32aSMårten Lindahl 	.predisable = &vcnl4010_buffer_predisable,
16153a52d32aSMårten Lindahl };
16163a52d32aSMårten Lindahl 
1617d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
1618d35567fcSMathieu Othacehe 	{
1619d35567fcSMathieu Othacehe 		.name = "nearlevel",
1620d35567fcSMathieu Othacehe 		.shared = IIO_SEPARATE,
1621d35567fcSMathieu Othacehe 		.read = vcnl4000_read_near_level,
1622d35567fcSMathieu Othacehe 	},
1623d35567fcSMathieu Othacehe 	{ /* sentinel */ }
1624d35567fcSMathieu Othacehe };
1625d35567fcSMathieu Othacehe 
1626d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = {
1627d35567fcSMathieu Othacehe 	{
1628d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1629d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_RISING,
1630d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
1631d35567fcSMathieu Othacehe 	}, {
1632d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1633d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_FALLING,
1634d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
1635d35567fcSMathieu Othacehe 	}, {
1636d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1637d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_EITHER,
1638d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
1639d35567fcSMathieu Othacehe 	}
1640d35567fcSMathieu Othacehe };
1641d35567fcSMathieu Othacehe 
16427f865127SAstrid Rost static const struct iio_event_spec vcnl4040_als_event_spec[] = {
16437f865127SAstrid Rost 	{
16447f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
16457f865127SAstrid Rost 		.dir = IIO_EV_DIR_RISING,
16467f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
16477f865127SAstrid Rost 	}, {
16487f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
16497f865127SAstrid Rost 		.dir = IIO_EV_DIR_FALLING,
16507f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
16517f865127SAstrid Rost 	}, {
16527f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
16537f865127SAstrid Rost 		.dir = IIO_EV_DIR_EITHER,
16547f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD),
16557f865127SAstrid Rost 	},
16567f865127SAstrid Rost };
16577f865127SAstrid Rost 
165854667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = {
165954667612SMårten Lindahl 	{
166054667612SMårten Lindahl 		.type = IIO_EV_TYPE_THRESH,
166154667612SMårten Lindahl 		.dir = IIO_EV_DIR_RISING,
166254667612SMårten Lindahl 		.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
166354667612SMårten Lindahl 	}, {
166454667612SMårten Lindahl 		.type = IIO_EV_TYPE_THRESH,
166554667612SMårten Lindahl 		.dir = IIO_EV_DIR_FALLING,
166654667612SMårten Lindahl 		.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
16677f865127SAstrid Rost 	}, {
16687f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
16697f865127SAstrid Rost 		.dir = IIO_EV_DIR_EITHER,
16707f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_PERIOD),
167154667612SMårten Lindahl 	},
167254667612SMårten Lindahl };
167354667612SMårten Lindahl 
1674d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = {
1675d35567fcSMathieu Othacehe 	{
1676d35567fcSMathieu Othacehe 		.type = IIO_LIGHT,
1677d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1678d35567fcSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SCALE),
1679d35567fcSMathieu Othacehe 	}, {
1680d35567fcSMathieu Othacehe 		.type = IIO_PROXIMITY,
1681d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
1682d35567fcSMathieu Othacehe 		.ext_info = vcnl4000_ext_info,
1683d35567fcSMathieu Othacehe 	}
1684d35567fcSMathieu Othacehe };
1685d35567fcSMathieu Othacehe 
1686d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = {
1687d35567fcSMathieu Othacehe 	{
1688d35567fcSMathieu Othacehe 		.type = IIO_LIGHT,
16898fe78d52SMathieu Othacehe 		.scan_index = -1,
1690d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1691d35567fcSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SCALE),
1692d35567fcSMathieu Othacehe 	}, {
1693d35567fcSMathieu Othacehe 		.type = IIO_PROXIMITY,
16948fe78d52SMathieu Othacehe 		.scan_index = 0,
1695f6889c1bSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1696f6889c1bSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SAMP_FREQ),
1697f6889c1bSMathieu Othacehe 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
1698d35567fcSMathieu Othacehe 		.event_spec = vcnl4000_event_spec,
1699d35567fcSMathieu Othacehe 		.num_event_specs = ARRAY_SIZE(vcnl4000_event_spec),
1700d35567fcSMathieu Othacehe 		.ext_info = vcnl4000_ext_info,
17018fe78d52SMathieu Othacehe 		.scan_type = {
17028fe78d52SMathieu Othacehe 			.sign = 'u',
17038fe78d52SMathieu Othacehe 			.realbits = 16,
17048fe78d52SMathieu Othacehe 			.storagebits = 16,
17058fe78d52SMathieu Othacehe 			.endianness = IIO_CPU,
1706d35567fcSMathieu Othacehe 		},
17078fe78d52SMathieu Othacehe 	},
17088fe78d52SMathieu Othacehe 	IIO_CHAN_SOFT_TIMESTAMP(1),
1709d35567fcSMathieu Othacehe };
1710d35567fcSMathieu Othacehe 
171185e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = {
171285e2c6a2SMårten Lindahl 	{
171385e2c6a2SMårten Lindahl 		.type = IIO_LIGHT,
171485e2c6a2SMårten Lindahl 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1715fea2c97dSAstrid Rost 			BIT(IIO_CHAN_INFO_SCALE) |
1716fea2c97dSAstrid Rost 			BIT(IIO_CHAN_INFO_INT_TIME),
1717fea2c97dSAstrid Rost 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
17187f865127SAstrid Rost 		.event_spec = vcnl4040_als_event_spec,
17197f865127SAstrid Rost 		.num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec),
172085e2c6a2SMårten Lindahl 	}, {
172185e2c6a2SMårten Lindahl 		.type = IIO_PROXIMITY,
172285e2c6a2SMårten Lindahl 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1723*add98466SAstrid Rost 			BIT(IIO_CHAN_INFO_INT_TIME) |
1724*add98466SAstrid Rost 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
1725*add98466SAstrid Rost 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
1726*add98466SAstrid Rost 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
172785e2c6a2SMårten Lindahl 		.ext_info = vcnl4000_ext_info,
172854667612SMårten Lindahl 		.event_spec = vcnl4040_event_spec,
172954667612SMårten Lindahl 		.num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
173085e2c6a2SMårten Lindahl 	}
173185e2c6a2SMårten Lindahl };
173285e2c6a2SMårten Lindahl 
173362a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = {
173462a1efb9SPeter Meerwald 	.read_raw = vcnl4000_read_raw,
173562a1efb9SPeter Meerwald };
173662a1efb9SPeter Meerwald 
1737d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = {
1738d35567fcSMathieu Othacehe 	.read_raw = vcnl4010_read_raw,
1739f6889c1bSMathieu Othacehe 	.read_avail = vcnl4010_read_avail,
1740f6889c1bSMathieu Othacehe 	.write_raw = vcnl4010_write_raw,
1741d35567fcSMathieu Othacehe 	.read_event_value = vcnl4010_read_event,
1742d35567fcSMathieu Othacehe 	.write_event_value = vcnl4010_write_event,
1743d35567fcSMathieu Othacehe 	.read_event_config = vcnl4010_read_event_config,
1744d35567fcSMathieu Othacehe 	.write_event_config = vcnl4010_write_event_config,
1745d35567fcSMathieu Othacehe };
1746d35567fcSMathieu Othacehe 
174785e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = {
174885e2c6a2SMårten Lindahl 	.read_raw = vcnl4000_read_raw,
174985e2c6a2SMårten Lindahl 	.write_raw = vcnl4040_write_raw,
175054667612SMårten Lindahl 	.read_event_value = vcnl4040_read_event,
175154667612SMårten Lindahl 	.write_event_value = vcnl4040_write_event,
175254667612SMårten Lindahl 	.read_event_config = vcnl4040_read_event_config,
175354667612SMårten Lindahl 	.write_event_config = vcnl4040_write_event_config,
175485e2c6a2SMårten Lindahl 	.read_avail = vcnl4040_read_avail,
175585e2c6a2SMårten Lindahl };
175685e2c6a2SMårten Lindahl 
1757d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
1758d35567fcSMathieu Othacehe 	[VCNL4000] = {
1759d35567fcSMathieu Othacehe 		.prod = "VCNL4000",
1760d35567fcSMathieu Othacehe 		.init = vcnl4000_init,
1761d35567fcSMathieu Othacehe 		.measure_light = vcnl4000_measure_light,
1762d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4000_measure_proximity,
1763d35567fcSMathieu Othacehe 		.set_power_state = vcnl4000_set_power_state,
1764d35567fcSMathieu Othacehe 		.channels = vcnl4000_channels,
1765d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4000_channels),
1766d35567fcSMathieu Othacehe 		.info = &vcnl4000_info,
1767d35567fcSMathieu Othacehe 	},
1768d35567fcSMathieu Othacehe 	[VCNL4010] = {
1769d35567fcSMathieu Othacehe 		.prod = "VCNL4010/4020",
1770d35567fcSMathieu Othacehe 		.init = vcnl4000_init,
1771d35567fcSMathieu Othacehe 		.measure_light = vcnl4000_measure_light,
1772d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4000_measure_proximity,
1773d35567fcSMathieu Othacehe 		.set_power_state = vcnl4000_set_power_state,
1774d35567fcSMathieu Othacehe 		.channels = vcnl4010_channels,
1775d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4010_channels),
1776d35567fcSMathieu Othacehe 		.info = &vcnl4010_info,
1777bfb6cfeeSMårten Lindahl 		.irq_thread = vcnl4010_irq_thread,
1778bfb6cfeeSMårten Lindahl 		.trig_buffer_func = vcnl4010_trigger_handler,
1779bfb6cfeeSMårten Lindahl 		.buffer_setup_ops = &vcnl4010_buffer_ops,
1780d35567fcSMathieu Othacehe 	},
1781d35567fcSMathieu Othacehe 	[VCNL4040] = {
1782d35567fcSMathieu Othacehe 		.prod = "VCNL4040",
1783d35567fcSMathieu Othacehe 		.init = vcnl4200_init,
1784d35567fcSMathieu Othacehe 		.measure_light = vcnl4200_measure_light,
1785d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4200_measure_proximity,
1786d35567fcSMathieu Othacehe 		.set_power_state = vcnl4200_set_power_state,
178785e2c6a2SMårten Lindahl 		.channels = vcnl4040_channels,
178885e2c6a2SMårten Lindahl 		.num_channels = ARRAY_SIZE(vcnl4040_channels),
178985e2c6a2SMårten Lindahl 		.info = &vcnl4040_info,
179054667612SMårten Lindahl 		.irq_thread = vcnl4040_irq_thread,
1791854965b7SAstrid Rost 		.int_reg = VCNL4040_INT_FLAGS,
1792e55c96daSAstrid Rost 		.ps_it_times = &vcnl4040_ps_it_times,
1793e55c96daSAstrid Rost 		.num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times),
1794fea2c97dSAstrid Rost 		.als_it_times = &vcnl4040_als_it_times,
1795fea2c97dSAstrid Rost 		.num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times),
1796fea2c97dSAstrid Rost 		.ulux_step = 100000,
1797d35567fcSMathieu Othacehe 	},
1798d35567fcSMathieu Othacehe 	[VCNL4200] = {
1799d35567fcSMathieu Othacehe 		.prod = "VCNL4200",
1800d35567fcSMathieu Othacehe 		.init = vcnl4200_init,
1801d35567fcSMathieu Othacehe 		.measure_light = vcnl4200_measure_light,
1802d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4200_measure_proximity,
1803d35567fcSMathieu Othacehe 		.set_power_state = vcnl4200_set_power_state,
1804854965b7SAstrid Rost 		.channels = vcnl4040_channels,
1805d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4000_channels),
1806854965b7SAstrid Rost 		.info = &vcnl4040_info,
1807854965b7SAstrid Rost 		.irq_thread = vcnl4040_irq_thread,
1808854965b7SAstrid Rost 		.int_reg = VCNL4200_INT_FLAGS,
1809e55c96daSAstrid Rost 		.ps_it_times = &vcnl4200_ps_it_times,
1810e55c96daSAstrid Rost 		.num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times),
1811fea2c97dSAstrid Rost 		.als_it_times = &vcnl4200_als_it_times,
1812fea2c97dSAstrid Rost 		.num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times),
1813fea2c97dSAstrid Rost 		.ulux_step = 24000,
1814d35567fcSMathieu Othacehe 	},
1815d35567fcSMathieu Othacehe };
1816d35567fcSMathieu Othacehe 
18178fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = {
18188fe78d52SMathieu Othacehe 	.validate_device = iio_trigger_validate_own_device,
18198fe78d52SMathieu Othacehe };
18208fe78d52SMathieu Othacehe 
18218fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev)
18228fe78d52SMathieu Othacehe {
18238fe78d52SMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
18248fe78d52SMathieu Othacehe 	struct i2c_client *client = data->client;
18258fe78d52SMathieu Othacehe 	struct iio_trigger *trigger;
18268fe78d52SMathieu Othacehe 
18278fe78d52SMathieu Othacehe 	trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
182815ea2878SJonathan Cameron 					 indio_dev->name,
182915ea2878SJonathan Cameron 					 iio_device_id(indio_dev));
18308fe78d52SMathieu Othacehe 	if (!trigger)
18318fe78d52SMathieu Othacehe 		return -ENOMEM;
18328fe78d52SMathieu Othacehe 
18338fe78d52SMathieu Othacehe 	trigger->ops = &vcnl4010_trigger_ops;
18348fe78d52SMathieu Othacehe 	iio_trigger_set_drvdata(trigger, indio_dev);
18358fe78d52SMathieu Othacehe 
18368fe78d52SMathieu Othacehe 	return devm_iio_trigger_register(&client->dev, trigger);
18378fe78d52SMathieu Othacehe }
18388fe78d52SMathieu Othacehe 
1839e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client)
184062a1efb9SPeter Meerwald {
1841e61295e0SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
184262a1efb9SPeter Meerwald 	struct vcnl4000_data *data;
184362a1efb9SPeter Meerwald 	struct iio_dev *indio_dev;
18441ebc787aSTomas Novotny 	int ret;
184562a1efb9SPeter Meerwald 
18462669d723SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
184762a1efb9SPeter Meerwald 	if (!indio_dev)
184862a1efb9SPeter Meerwald 		return -ENOMEM;
184962a1efb9SPeter Meerwald 
185062a1efb9SPeter Meerwald 	data = iio_priv(indio_dev);
185162a1efb9SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
185262a1efb9SPeter Meerwald 	data->client = client;
18531ebc787aSTomas Novotny 	data->id = id->driver_data;
18541ebc787aSTomas Novotny 	data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
185562a1efb9SPeter Meerwald 
185642ec40b0SMårten Lindahl 	mutex_init(&data->vcnl4000_lock);
185742ec40b0SMårten Lindahl 
18581ebc787aSTomas Novotny 	ret = data->chip_spec->init(data);
185962a1efb9SPeter Meerwald 	if (ret < 0)
18602669d723SPeter Meerwald 		return ret;
186162a1efb9SPeter Meerwald 
1862d978bfddSPeter Meerwald-Stadler 	dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
18631ebc787aSTomas Novotny 		data->chip_spec->prod, data->rev);
186462a1efb9SPeter Meerwald 
1865f5a98e1fSGuido Günther 	if (device_property_read_u32(&client->dev, "proximity-near-level",
1866f5a98e1fSGuido Günther 				     &data->near_level))
1867f5a98e1fSGuido Günther 		data->near_level = 0;
1868f5a98e1fSGuido Günther 
1869d35567fcSMathieu Othacehe 	indio_dev->info = data->chip_spec->info;
1870d35567fcSMathieu Othacehe 	indio_dev->channels = data->chip_spec->channels;
1871d35567fcSMathieu Othacehe 	indio_dev->num_channels = data->chip_spec->num_channels;
187262a1efb9SPeter Meerwald 	indio_dev->name = VCNL4000_DRV_NAME;
187362a1efb9SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
187462a1efb9SPeter Meerwald 
1875bfb6cfeeSMårten Lindahl 	if (data->chip_spec->trig_buffer_func &&
1876bfb6cfeeSMårten Lindahl 	    data->chip_spec->buffer_setup_ops) {
18778fe78d52SMathieu Othacehe 		ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
18788fe78d52SMathieu Othacehe 						      NULL,
1879bfb6cfeeSMårten Lindahl 						      data->chip_spec->trig_buffer_func,
1880bfb6cfeeSMårten Lindahl 						      data->chip_spec->buffer_setup_ops);
18818fe78d52SMathieu Othacehe 		if (ret < 0) {
18828fe78d52SMathieu Othacehe 			dev_err(&client->dev,
18838fe78d52SMathieu Othacehe 				"unable to setup iio triggered buffer\n");
18848fe78d52SMathieu Othacehe 			return ret;
18858fe78d52SMathieu Othacehe 		}
1886bfb6cfeeSMårten Lindahl 	}
18878fe78d52SMathieu Othacehe 
1888bfb6cfeeSMårten Lindahl 	if (client->irq && data->chip_spec->irq_thread) {
1889d35567fcSMathieu Othacehe 		ret = devm_request_threaded_irq(&client->dev, client->irq,
1890bfb6cfeeSMårten Lindahl 						NULL, data->chip_spec->irq_thread,
1891d35567fcSMathieu Othacehe 						IRQF_TRIGGER_FALLING |
1892d35567fcSMathieu Othacehe 						IRQF_ONESHOT,
1893bfb6cfeeSMårten Lindahl 						"vcnl4000_irq",
1894d35567fcSMathieu Othacehe 						indio_dev);
1895d35567fcSMathieu Othacehe 		if (ret < 0) {
1896d35567fcSMathieu Othacehe 			dev_err(&client->dev, "irq request failed\n");
1897d35567fcSMathieu Othacehe 			return ret;
1898d35567fcSMathieu Othacehe 		}
18998fe78d52SMathieu Othacehe 
19008fe78d52SMathieu Othacehe 		ret = vcnl4010_probe_trigger(indio_dev);
19018fe78d52SMathieu Othacehe 		if (ret < 0)
19028fe78d52SMathieu Othacehe 			return ret;
1903d35567fcSMathieu Othacehe 	}
1904d35567fcSMathieu Othacehe 
19055e00708dSGuido Günther 	ret = pm_runtime_set_active(&client->dev);
19065e00708dSGuido Günther 	if (ret < 0)
19075e00708dSGuido Günther 		goto fail_poweroff;
19085e00708dSGuido Günther 
19095e00708dSGuido Günther 	ret = iio_device_register(indio_dev);
19105e00708dSGuido Günther 	if (ret < 0)
19115e00708dSGuido Günther 		goto fail_poweroff;
19125e00708dSGuido Günther 
19135e00708dSGuido Günther 	pm_runtime_enable(&client->dev);
19145e00708dSGuido Günther 	pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
19155e00708dSGuido Günther 	pm_runtime_use_autosuspend(&client->dev);
19165e00708dSGuido Günther 
19175e00708dSGuido Günther 	return 0;
19185e00708dSGuido Günther fail_poweroff:
19195e00708dSGuido Günther 	data->chip_spec->set_power_state(data, false);
19205e00708dSGuido Günther 	return ret;
192162a1efb9SPeter Meerwald }
192262a1efb9SPeter Meerwald 
1923ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = {
1924ebd457d5SAngus Ainslie (Purism) 	{
1925ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4000",
19261436a78cSMarco Felsch 		.data = (void *)VCNL4000,
1927ebd457d5SAngus Ainslie (Purism) 	},
1928ebd457d5SAngus Ainslie (Purism) 	{
1929ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4010",
19301436a78cSMarco Felsch 		.data = (void *)VCNL4010,
1931ebd457d5SAngus Ainslie (Purism) 	},
1932ebd457d5SAngus Ainslie (Purism) 	{
19331436a78cSMarco Felsch 		.compatible = "vishay,vcnl4020",
19341436a78cSMarco Felsch 		.data = (void *)VCNL4010,
1935ebd457d5SAngus Ainslie (Purism) 	},
1936ebd457d5SAngus Ainslie (Purism) 	{
19377fd1c260SMarco Felsch 		.compatible = "vishay,vcnl4040",
19387fd1c260SMarco Felsch 		.data = (void *)VCNL4040,
19397fd1c260SMarco Felsch 	},
19407fd1c260SMarco Felsch 	{
1941ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4200",
19421436a78cSMarco Felsch 		.data = (void *)VCNL4200,
1943ebd457d5SAngus Ainslie (Purism) 	},
1944ebd457d5SAngus Ainslie (Purism) 	{},
1945ebd457d5SAngus Ainslie (Purism) };
1946ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
1947ebd457d5SAngus Ainslie (Purism) 
1948ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client)
19495e00708dSGuido Günther {
19505e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
19515e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
1952ab91da2fSUwe Kleine-König 	int ret;
19535e00708dSGuido Günther 
19545e00708dSGuido Günther 	pm_runtime_dont_use_autosuspend(&client->dev);
19555e00708dSGuido Günther 	pm_runtime_disable(&client->dev);
19565e00708dSGuido Günther 	iio_device_unregister(indio_dev);
19575e00708dSGuido Günther 	pm_runtime_set_suspended(&client->dev);
19585e00708dSGuido Günther 
1959ab91da2fSUwe Kleine-König 	ret = data->chip_spec->set_power_state(data, false);
1960ab91da2fSUwe Kleine-König 	if (ret)
1961ab91da2fSUwe Kleine-König 		dev_warn(&client->dev, "Failed to power down (%pe)\n",
1962ab91da2fSUwe Kleine-König 			 ERR_PTR(ret));
19635e00708dSGuido Günther }
19645e00708dSGuido Günther 
1965cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev)
19665e00708dSGuido Günther {
19675e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
19685e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
19695e00708dSGuido Günther 
19705e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
19715e00708dSGuido Günther }
19725e00708dSGuido Günther 
1973cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev)
19745e00708dSGuido Günther {
19755e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
19765e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
19775e00708dSGuido Günther 
19785e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
19795e00708dSGuido Günther }
19805e00708dSGuido Günther 
1981cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend,
1982cd4d10b1SJonathan Cameron 				 vcnl4000_runtime_resume, NULL);
19835e00708dSGuido Günther 
198462a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = {
198562a1efb9SPeter Meerwald 	.driver = {
198662a1efb9SPeter Meerwald 		.name   = VCNL4000_DRV_NAME,
1987cd4d10b1SJonathan Cameron 		.pm	= pm_ptr(&vcnl4000_pm_ops),
1988ebd457d5SAngus Ainslie (Purism) 		.of_match_table = vcnl_4000_of_match,
198962a1efb9SPeter Meerwald 	},
19907cf15f42SUwe Kleine-König 	.probe = vcnl4000_probe,
199162a1efb9SPeter Meerwald 	.id_table = vcnl4000_id,
19925e00708dSGuido Günther 	.remove	= vcnl4000_remove,
199362a1efb9SPeter Meerwald };
199462a1efb9SPeter Meerwald 
199562a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver);
199662a1efb9SPeter Meerwald 
199762a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
19988fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
199962a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
200062a1efb9SPeter Meerwald MODULE_LICENSE("GPL");
2001