xref: /linux/drivers/iio/light/vcnl4000.c (revision 44b90383453c6664b119ec985e70f87c90e050f1)
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 */
64add98466SAstrid 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 */
93*44b90383SMårten Lindahl #define VCNL4040_PS_CONF2_PS_HD		BIT(11)	/* Proximity high definition */
9454667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT	GENMASK(9, 8) /* Proximity interrupt mode */
95add98466SAstrid Rost #define VCNL4040_PS_CONF3_MPS		GENMASK(6, 5) /* Proximity multi pulse number */
96bb33e751SAstrid Rost #define VCNL4040_PS_MS_LED_I		GENMASK(10, 8) /* Proximity current */
9754667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY		BIT(8) /* Proximity event cross low threshold */
9854667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE		BIT(9) /* Proximity event cross high threshold */
99bc292aafSAstrid Rost #define VCNL4040_ALS_RISING		BIT(12) /* Ambient Light cross high threshold */
100bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING		BIT(13) /* Ambient Light cross low threshold */
101e21b5b1fSMårten Lindahl 
102d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */
103d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL	BIT(0) /* Select threshold interrupt source */
104d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN	BIT(1) /* Threshold interrupt type */
105d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN	BIT(2) /* Enable on ALS data ready */
106d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN	BIT(3) /* Enable on proximity data ready */
107d35567fcSMathieu Othacehe 
108d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH	0 /* High threshold exceeded */
109d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW	1 /* Low threshold exceeded */
110d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS	2 /* ALS data ready */
111d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY	3 /* Proximity data ready */
112d35567fcSMathieu Othacehe 
113d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \
114d35567fcSMathieu Othacehe 	(BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH))
115d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \
116d35567fcSMathieu Othacehe 	(BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
117d35567fcSMathieu Othacehe 
118*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_MPS_16BITS	3	/* 8 multi pulses */
119*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_LED_I_16BITS	3	/* 120 mA */
120*44b90383SMårten Lindahl 
121*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_SAMPLE_16BITS \
122*44b90383SMårten Lindahl 	(FIELD_PREP(VCNL4040_PS_CONF3_MPS, VCNL4040_CONF3_PS_MPS_16BITS) | \
123*44b90383SMårten Lindahl 	 FIELD_PREP(VCNL4040_PS_MS_LED_I, VCNL4040_CONF3_PS_LED_I_16BITS))
124*44b90383SMårten Lindahl 
125f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = {
126f6889c1bSMathieu Othacehe 	{1, 950000},
127f6889c1bSMathieu Othacehe 	{3, 906250},
128f6889c1bSMathieu Othacehe 	{7, 812500},
129f6889c1bSMathieu Othacehe 	{16, 625000},
130f6889c1bSMathieu Othacehe 	{31, 250000},
131f6889c1bSMathieu Othacehe 	{62, 500000},
132f6889c1bSMathieu Othacehe 	{125, 0},
133f6889c1bSMathieu Othacehe 	{250, 0},
134f6889c1bSMathieu Othacehe };
13562a1efb9SPeter Meerwald 
13685e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = {
13785e2c6a2SMårten Lindahl 	{0, 100},
13885e2c6a2SMårten Lindahl 	{0, 150},
13985e2c6a2SMårten Lindahl 	{0, 200},
14085e2c6a2SMårten Lindahl 	{0, 250},
14185e2c6a2SMårten Lindahl 	{0, 300},
14285e2c6a2SMårten Lindahl 	{0, 350},
14385e2c6a2SMårten Lindahl 	{0, 400},
14485e2c6a2SMårten Lindahl 	{0, 800},
14585e2c6a2SMårten Lindahl };
14685e2c6a2SMårten Lindahl 
147e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = {
148e55c96daSAstrid Rost 	{0, 96},
149e55c96daSAstrid Rost 	{0, 144},
150e55c96daSAstrid Rost 	{0, 192},
151e55c96daSAstrid Rost 	{0, 384},
152e55c96daSAstrid Rost 	{0, 768},
153e55c96daSAstrid Rost 	{0, 864},
154e55c96daSAstrid Rost };
155e55c96daSAstrid Rost 
156fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = {
157fea2c97dSAstrid Rost 	{0, 80000},
158fea2c97dSAstrid Rost 	{0, 160000},
159fea2c97dSAstrid Rost 	{0, 320000},
160fea2c97dSAstrid Rost 	{0, 640000},
161fea2c97dSAstrid Rost };
162fea2c97dSAstrid Rost 
163fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = {
164fea2c97dSAstrid Rost 	{0, 50000},
165fea2c97dSAstrid Rost 	{0, 100000},
166fea2c97dSAstrid Rost 	{0, 200000},
167fea2c97dSAstrid Rost 	{0, 400000},
168fea2c97dSAstrid Rost };
169fea2c97dSAstrid Rost 
170bb33e751SAstrid Rost static const int vcnl4040_ps_calibbias_ua[][2] = {
171bb33e751SAstrid Rost 	{0, 50000},
172bb33e751SAstrid Rost 	{0, 75000},
173bb33e751SAstrid Rost 	{0, 100000},
174bb33e751SAstrid Rost 	{0, 120000},
175bb33e751SAstrid Rost 	{0, 140000},
176bb33e751SAstrid Rost 	{0, 160000},
177bb33e751SAstrid Rost 	{0, 180000},
178bb33e751SAstrid Rost 	{0, 200000},
179bb33e751SAstrid Rost };
180bb33e751SAstrid Rost 
1817f865127SAstrid Rost static const int vcnl4040_als_persistence[] = {1, 2, 4, 8};
1827f865127SAstrid Rost static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4};
183add98466SAstrid Rost static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8};
1847f865127SAstrid Rost 
1855e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS	2000 /* before we enter pm_runtime_suspend */
1865e00708dSGuido Günther 
1871ebc787aSTomas Novotny enum vcnl4000_device_ids {
1881ebc787aSTomas Novotny 	VCNL4000,
18950c50b97STomas Novotny 	VCNL4010,
1905a441aadSAngus Ainslie (Purism) 	VCNL4040,
191be38866fSTomas Novotny 	VCNL4200,
192be38866fSTomas Novotny };
193be38866fSTomas Novotny 
194be38866fSTomas Novotny struct vcnl4200_channel {
195be38866fSTomas Novotny 	u8 reg;
196be38866fSTomas Novotny 	ktime_t last_measurement;
197be38866fSTomas Novotny 	ktime_t sampling_rate;
198be38866fSTomas Novotny 	struct mutex lock;
1991ebc787aSTomas Novotny };
2001ebc787aSTomas Novotny 
20162a1efb9SPeter Meerwald struct vcnl4000_data {
20262a1efb9SPeter Meerwald 	struct i2c_client *client;
2031ebc787aSTomas Novotny 	enum vcnl4000_device_ids id;
2041ebc787aSTomas Novotny 	int rev;
2051ebc787aSTomas Novotny 	int al_scale;
206*44b90383SMårten Lindahl 	int ps_scale;
20754667612SMårten Lindahl 	u8 ps_int;		/* proximity interrupt mode */
208bc292aafSAstrid Rost 	u8 als_int;		/* ambient light interrupt mode*/
2091ebc787aSTomas Novotny 	const struct vcnl4000_chip_spec *chip_spec;
210be38866fSTomas Novotny 	struct mutex vcnl4000_lock;
211be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_al;
212be38866fSTomas Novotny 	struct vcnl4200_channel vcnl4200_ps;
213f5a98e1fSGuido Günther 	uint32_t near_level;
21462a1efb9SPeter Meerwald };
21562a1efb9SPeter Meerwald 
2161ebc787aSTomas Novotny struct vcnl4000_chip_spec {
2171ebc787aSTomas Novotny 	const char *prod;
218d35567fcSMathieu Othacehe 	struct iio_chan_spec const *channels;
219d35567fcSMathieu Othacehe 	const int num_channels;
220d35567fcSMathieu Othacehe 	const struct iio_info *info;
221bfb6cfeeSMårten Lindahl 	const struct iio_buffer_setup_ops *buffer_setup_ops;
2221ebc787aSTomas Novotny 	int (*init)(struct vcnl4000_data *data);
2231ebc787aSTomas Novotny 	int (*measure_light)(struct vcnl4000_data *data, int *val);
2241ebc787aSTomas Novotny 	int (*measure_proximity)(struct vcnl4000_data *data, int *val);
2255e00708dSGuido Günther 	int (*set_power_state)(struct vcnl4000_data *data, bool on);
226bfb6cfeeSMårten Lindahl 	irqreturn_t (*irq_thread)(int irq, void *priv);
227bfb6cfeeSMårten Lindahl 	irqreturn_t (*trig_buffer_func)(int irq, void *priv);
228854965b7SAstrid Rost 
229854965b7SAstrid Rost 	u8 int_reg;
230e55c96daSAstrid Rost 	const int(*ps_it_times)[][2];
231e55c96daSAstrid Rost 	const int num_ps_it_times;
232fea2c97dSAstrid Rost 	const int(*als_it_times)[][2];
233fea2c97dSAstrid Rost 	const int num_als_it_times;
234fea2c97dSAstrid Rost 	const unsigned int ulux_step;
2351ebc787aSTomas Novotny };
2361ebc787aSTomas Novotny 
23762a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = {
2381ebc787aSTomas Novotny 	{ "vcnl4000", VCNL4000 },
23950c50b97STomas Novotny 	{ "vcnl4010", VCNL4010 },
24050c50b97STomas Novotny 	{ "vcnl4020", VCNL4010 },
2415a441aadSAngus Ainslie (Purism) 	{ "vcnl4040", VCNL4040 },
242be38866fSTomas Novotny 	{ "vcnl4200", VCNL4200 },
24362a1efb9SPeter Meerwald 	{ }
24462a1efb9SPeter Meerwald };
24562a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
24662a1efb9SPeter Meerwald 
2475e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
2485e00708dSGuido Günther {
2495e00708dSGuido Günther 	/* no suspend op */
2505e00708dSGuido Günther 	return 0;
2515e00708dSGuido Günther }
2525e00708dSGuido Günther 
2531ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data)
2541ebc787aSTomas Novotny {
2551ebc787aSTomas Novotny 	int ret, prod_id;
2561ebc787aSTomas Novotny 
2571ebc787aSTomas Novotny 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
2581ebc787aSTomas Novotny 	if (ret < 0)
2591ebc787aSTomas Novotny 		return ret;
2601ebc787aSTomas Novotny 
2611ebc787aSTomas Novotny 	prod_id = ret >> 4;
26258bf9aceSTomas Novotny 	switch (prod_id) {
26358bf9aceSTomas Novotny 	case VCNL4000_PROD_ID:
26458bf9aceSTomas Novotny 		if (data->id != VCNL4000)
26558bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
26658bf9aceSTomas Novotny 					"wrong device id, use vcnl4000");
26758bf9aceSTomas Novotny 		break;
26858bf9aceSTomas Novotny 	case VCNL4010_PROD_ID:
26958bf9aceSTomas Novotny 		if (data->id != VCNL4010)
27058bf9aceSTomas Novotny 			dev_warn(&data->client->dev,
27158bf9aceSTomas Novotny 					"wrong device id, use vcnl4010/4020");
27258bf9aceSTomas Novotny 		break;
27358bf9aceSTomas Novotny 	default:
2741ebc787aSTomas Novotny 		return -ENODEV;
27558bf9aceSTomas Novotny 	}
2761ebc787aSTomas Novotny 
2771ebc787aSTomas Novotny 	data->rev = ret & 0xf;
2781ebc787aSTomas Novotny 	data->al_scale = 250000;
279be38866fSTomas Novotny 
2805e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
281be38866fSTomas Novotny };
282be38866fSTomas Novotny 
283e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en)
2845e00708dSGuido Günther {
2855e00708dSGuido Günther 	int ret;
2865e00708dSGuido Günther 
287e21b5b1fSMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
288e21b5b1fSMårten Lindahl 
289e21b5b1fSMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
290e21b5b1fSMårten Lindahl 	if (ret < 0)
291e21b5b1fSMårten Lindahl 		goto out;
292e21b5b1fSMårten Lindahl 
293e21b5b1fSMårten Lindahl 	if (en)
294e21b5b1fSMårten Lindahl 		ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN;
295e21b5b1fSMårten Lindahl 	else
296e21b5b1fSMårten Lindahl 		ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN;
297e21b5b1fSMårten Lindahl 
298e21b5b1fSMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret);
299e21b5b1fSMårten Lindahl 
300e21b5b1fSMårten Lindahl out:
301e21b5b1fSMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
302e21b5b1fSMårten Lindahl 
303e21b5b1fSMårten Lindahl 	return ret;
304e21b5b1fSMårten Lindahl }
305e21b5b1fSMårten Lindahl 
306e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en)
307e21b5b1fSMårten Lindahl {
308e21b5b1fSMårten Lindahl 	int ret;
309e21b5b1fSMårten Lindahl 
310e21b5b1fSMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
311e21b5b1fSMårten Lindahl 
312e21b5b1fSMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
313e21b5b1fSMårten Lindahl 	if (ret < 0)
314e21b5b1fSMårten Lindahl 		goto out;
315e21b5b1fSMårten Lindahl 
316e21b5b1fSMårten Lindahl 	if (en)
317e21b5b1fSMårten Lindahl 		ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN;
318e21b5b1fSMårten Lindahl 	else
319e21b5b1fSMårten Lindahl 		ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN;
320e21b5b1fSMårten Lindahl 
321e21b5b1fSMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret);
322e21b5b1fSMårten Lindahl 
323e21b5b1fSMårten Lindahl out:
324e21b5b1fSMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
325e21b5b1fSMårten Lindahl 
326e21b5b1fSMårten Lindahl 	return ret;
327e21b5b1fSMårten Lindahl }
328e21b5b1fSMårten Lindahl 
329e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
330e21b5b1fSMårten Lindahl {
331e21b5b1fSMårten Lindahl 	int ret;
332e21b5b1fSMårten Lindahl 
33354667612SMårten Lindahl 	/* Do not power down if interrupts are enabled */
334bc292aafSAstrid Rost 	if (!on && (data->ps_int || data->als_int))
33554667612SMårten Lindahl 		return 0;
33654667612SMårten Lindahl 
337e21b5b1fSMårten Lindahl 	ret = vcnl4000_write_als_enable(data, on);
3385e00708dSGuido Günther 	if (ret < 0)
3395e00708dSGuido Günther 		return ret;
3405e00708dSGuido Günther 
341e21b5b1fSMårten Lindahl 	ret = vcnl4000_write_ps_enable(data, on);
3425e00708dSGuido Günther 	if (ret < 0)
3435e00708dSGuido Günther 		return ret;
3445e00708dSGuido Günther 
3455e00708dSGuido Günther 	if (on) {
3465e00708dSGuido Günther 		/* Wait at least one integration cycle before fetching data */
3475e00708dSGuido Günther 		data->vcnl4200_al.last_measurement = ktime_get();
3485e00708dSGuido Günther 		data->vcnl4200_ps.last_measurement = ktime_get();
3495e00708dSGuido Günther 	}
3505e00708dSGuido Günther 
3515e00708dSGuido Günther 	return 0;
3525e00708dSGuido Günther }
3535e00708dSGuido Günther 
354be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data)
355be38866fSTomas Novotny {
3565a441aadSAngus Ainslie (Purism) 	int ret, id;
357*44b90383SMårten Lindahl 	u16 regval;
358be38866fSTomas Novotny 
359be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
360be38866fSTomas Novotny 	if (ret < 0)
361be38866fSTomas Novotny 		return ret;
362be38866fSTomas Novotny 
3635a441aadSAngus Ainslie (Purism) 	id = ret & 0xff;
3645a441aadSAngus Ainslie (Purism) 
3655a441aadSAngus Ainslie (Purism) 	if (id != VCNL4200_PROD_ID) {
3665a441aadSAngus Ainslie (Purism) 		ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
3675a441aadSAngus Ainslie (Purism) 		if (ret < 0)
3685a441aadSAngus Ainslie (Purism) 			return ret;
3695a441aadSAngus Ainslie (Purism) 
3705a441aadSAngus Ainslie (Purism) 		id = ret & 0xff;
3715a441aadSAngus Ainslie (Purism) 
3725a441aadSAngus Ainslie (Purism) 		if (id != VCNL4040_PROD_ID)
373be38866fSTomas Novotny 			return -ENODEV;
3745a441aadSAngus Ainslie (Purism) 	}
3755a441aadSAngus Ainslie (Purism) 
3765a441aadSAngus Ainslie (Purism) 	dev_dbg(&data->client->dev, "device id 0x%x", id);
377be38866fSTomas Novotny 
378be38866fSTomas Novotny 	data->rev = (ret >> 8) & 0xf;
37954667612SMårten Lindahl 	data->ps_int = 0;
380bc292aafSAstrid Rost 	data->als_int = 0;
381be38866fSTomas Novotny 
382be38866fSTomas Novotny 	data->vcnl4200_al.reg = VCNL4200_AL_DATA;
383be38866fSTomas Novotny 	data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
3845a441aadSAngus Ainslie (Purism) 	switch (id) {
3855a441aadSAngus Ainslie (Purism) 	case VCNL4200_PROD_ID:
386b42aa97eSTomas Novotny 		/* Default wait time is 50ms, add 20% tolerance. */
387b42aa97eSTomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
388b42aa97eSTomas Novotny 		/* Default wait time is 4.8ms, add 20% tolerance. */
389b42aa97eSTomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
3905a441aadSAngus Ainslie (Purism) 		break;
3915a441aadSAngus Ainslie (Purism) 	case VCNL4040_PROD_ID:
3922ca5a879STomas Novotny 		/* Default wait time is 80ms, add 20% tolerance. */
3932ca5a879STomas Novotny 		data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
3942ca5a879STomas Novotny 		/* Default wait time is 5ms, add 20% tolerance. */
3952ca5a879STomas Novotny 		data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
3965a441aadSAngus Ainslie (Purism) 		break;
3975a441aadSAngus Ainslie (Purism) 	}
398fea2c97dSAstrid Rost 	data->al_scale = data->chip_spec->ulux_step;
399*44b90383SMårten Lindahl 	data->ps_scale = 16;
400be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_al.lock);
401be38866fSTomas Novotny 	mutex_init(&data->vcnl4200_ps.lock);
4021ebc787aSTomas Novotny 
403*44b90383SMårten Lindahl 	/* Use 16 bits proximity sensor readings */
404*44b90383SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
405*44b90383SMårten Lindahl 	if (ret < 0)
406*44b90383SMårten Lindahl 		return ret;
407*44b90383SMårten Lindahl 
408*44b90383SMårten Lindahl 	regval = ret | VCNL4040_PS_CONF2_PS_HD;
409*44b90383SMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
410*44b90383SMårten Lindahl 					regval);
411*44b90383SMårten Lindahl 	if (ret < 0)
412*44b90383SMårten Lindahl 		return ret;
413*44b90383SMårten Lindahl 
414*44b90383SMårten Lindahl 	/* Align proximity sensor sample rate to 16 bits data width */
415*44b90383SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
416*44b90383SMårten Lindahl 	if (ret < 0)
417*44b90383SMårten Lindahl 		return ret;
418*44b90383SMårten Lindahl 
419*44b90383SMårten Lindahl 	regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS;
420*44b90383SMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
421*44b90383SMårten Lindahl 					regval);
422*44b90383SMårten Lindahl 	if (ret < 0)
423*44b90383SMårten Lindahl 		return ret;
424*44b90383SMårten Lindahl 
4255e00708dSGuido Günther 	ret = data->chip_spec->set_power_state(data, true);
4265e00708dSGuido Günther 	if (ret < 0)
4275e00708dSGuido Günther 		return ret;
4285e00708dSGuido Günther 
4291ebc787aSTomas Novotny 	return 0;
4301ebc787aSTomas Novotny };
4311ebc787aSTomas Novotny 
432816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val)
433816956c3SMathieu Othacehe {
434816956c3SMathieu Othacehe 	s32 ret;
435816956c3SMathieu Othacehe 
436816956c3SMathieu Othacehe 	ret = i2c_smbus_read_word_swapped(data->client, data_reg);
437816956c3SMathieu Othacehe 	if (ret < 0)
438816956c3SMathieu Othacehe 		return ret;
439816956c3SMathieu Othacehe 
440816956c3SMathieu Othacehe 	*val = ret;
441816956c3SMathieu Othacehe 	return 0;
442816956c3SMathieu Othacehe }
443816956c3SMathieu Othacehe 
444816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val)
445816956c3SMathieu Othacehe {
446816956c3SMathieu Othacehe 	if (val > U16_MAX)
447816956c3SMathieu Othacehe 		return -ERANGE;
448816956c3SMathieu Othacehe 
449816956c3SMathieu Othacehe 	return i2c_smbus_write_word_swapped(data->client, data_reg, val);
450816956c3SMathieu Othacehe }
451816956c3SMathieu Othacehe 
452816956c3SMathieu Othacehe 
45362a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
45462a1efb9SPeter Meerwald 				u8 rdy_mask, u8 data_reg, int *val)
45562a1efb9SPeter Meerwald {
45662a1efb9SPeter Meerwald 	int tries = 20;
45762a1efb9SPeter Meerwald 	int ret;
45862a1efb9SPeter Meerwald 
459be38866fSTomas Novotny 	mutex_lock(&data->vcnl4000_lock);
460ff34ed6dSPeter Meerwald-Stadler 
46162a1efb9SPeter Meerwald 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
46262a1efb9SPeter Meerwald 					req_mask);
46362a1efb9SPeter Meerwald 	if (ret < 0)
464ff34ed6dSPeter Meerwald-Stadler 		goto fail;
46562a1efb9SPeter Meerwald 
46662a1efb9SPeter Meerwald 	/* wait for data to become ready */
46762a1efb9SPeter Meerwald 	while (tries--) {
46862a1efb9SPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
46962a1efb9SPeter Meerwald 		if (ret < 0)
470ff34ed6dSPeter Meerwald-Stadler 			goto fail;
47162a1efb9SPeter Meerwald 		if (ret & rdy_mask)
47262a1efb9SPeter Meerwald 			break;
47362a1efb9SPeter Meerwald 		msleep(20); /* measurement takes up to 100 ms */
47462a1efb9SPeter Meerwald 	}
47562a1efb9SPeter Meerwald 
47662a1efb9SPeter Meerwald 	if (tries < 0) {
47762a1efb9SPeter Meerwald 		dev_err(&data->client->dev,
47862a1efb9SPeter Meerwald 			"vcnl4000_measure() failed, data not ready\n");
479ff34ed6dSPeter Meerwald-Stadler 		ret = -EIO;
480ff34ed6dSPeter Meerwald-Stadler 		goto fail;
48162a1efb9SPeter Meerwald 	}
48262a1efb9SPeter Meerwald 
483816956c3SMathieu Othacehe 	ret = vcnl4000_read_data(data, data_reg, val);
48462a1efb9SPeter Meerwald 	if (ret < 0)
485ff34ed6dSPeter Meerwald-Stadler 		goto fail;
48662a1efb9SPeter Meerwald 
487be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
48862a1efb9SPeter Meerwald 
48962a1efb9SPeter Meerwald 	return 0;
490ff34ed6dSPeter Meerwald-Stadler 
491ff34ed6dSPeter Meerwald-Stadler fail:
492be38866fSTomas Novotny 	mutex_unlock(&data->vcnl4000_lock);
493ff34ed6dSPeter Meerwald-Stadler 	return ret;
49462a1efb9SPeter Meerwald }
49562a1efb9SPeter Meerwald 
496be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data,
497be38866fSTomas Novotny 		struct vcnl4200_channel *chan, int *val)
498be38866fSTomas Novotny {
499be38866fSTomas Novotny 	int ret;
500be38866fSTomas Novotny 	s64 delta;
501be38866fSTomas Novotny 	ktime_t next_measurement;
502be38866fSTomas Novotny 
503be38866fSTomas Novotny 	mutex_lock(&chan->lock);
504be38866fSTomas Novotny 
505be38866fSTomas Novotny 	next_measurement = ktime_add(chan->last_measurement,
506be38866fSTomas Novotny 			chan->sampling_rate);
507be38866fSTomas Novotny 	delta = ktime_us_delta(next_measurement, ktime_get());
508be38866fSTomas Novotny 	if (delta > 0)
509be38866fSTomas Novotny 		usleep_range(delta, delta + 500);
510be38866fSTomas Novotny 	chan->last_measurement = ktime_get();
511be38866fSTomas Novotny 
512be38866fSTomas Novotny 	mutex_unlock(&chan->lock);
513be38866fSTomas Novotny 
514be38866fSTomas Novotny 	ret = i2c_smbus_read_word_data(data->client, chan->reg);
515be38866fSTomas Novotny 	if (ret < 0)
516be38866fSTomas Novotny 		return ret;
517be38866fSTomas Novotny 
518be38866fSTomas Novotny 	*val = ret;
519be38866fSTomas Novotny 
520be38866fSTomas Novotny 	return 0;
521be38866fSTomas Novotny }
522be38866fSTomas Novotny 
5231ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
5241ebc787aSTomas Novotny {
5251ebc787aSTomas Novotny 	return vcnl4000_measure(data,
5261ebc787aSTomas Novotny 			VCNL4000_AL_OD, VCNL4000_AL_RDY,
5271ebc787aSTomas Novotny 			VCNL4000_AL_RESULT_HI, val);
5281ebc787aSTomas Novotny }
5291ebc787aSTomas Novotny 
530be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
531be38866fSTomas Novotny {
532be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_al, val);
533be38866fSTomas Novotny }
534be38866fSTomas Novotny 
5351ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
5361ebc787aSTomas Novotny {
5371ebc787aSTomas Novotny 	return vcnl4000_measure(data,
5381ebc787aSTomas Novotny 			VCNL4000_PS_OD, VCNL4000_PS_RDY,
5391ebc787aSTomas Novotny 			VCNL4000_PS_RESULT_HI, val);
5401ebc787aSTomas Novotny }
5411ebc787aSTomas Novotny 
542be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
543be38866fSTomas Novotny {
544be38866fSTomas Novotny 	return vcnl4200_measure(data, &data->vcnl4200_ps, val);
545be38866fSTomas Novotny }
546be38866fSTomas Novotny 
547f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val,
548f6889c1bSMathieu Othacehe 					 int *val2)
549f6889c1bSMathieu Othacehe {
550f6889c1bSMathieu Othacehe 	int ret;
551f6889c1bSMathieu Othacehe 
552f6889c1bSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE);
553f6889c1bSMathieu Othacehe 	if (ret < 0)
554f6889c1bSMathieu Othacehe 		return ret;
555f6889c1bSMathieu Othacehe 
556f6889c1bSMathieu Othacehe 	if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency))
557f6889c1bSMathieu Othacehe 		return -EINVAL;
558f6889c1bSMathieu Othacehe 
559f6889c1bSMathieu Othacehe 	*val = vcnl4010_prox_sampling_frequency[ret][0];
560f6889c1bSMathieu Othacehe 	*val2 = vcnl4010_prox_sampling_frequency[ret][1];
561f6889c1bSMathieu Othacehe 
562f6889c1bSMathieu Othacehe 	return 0;
563f6889c1bSMathieu Othacehe }
564f6889c1bSMathieu Othacehe 
565d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data)
566f5a98e1fSGuido Günther {
567d35567fcSMathieu Othacehe 	int ret;
568f5a98e1fSGuido Günther 
569d35567fcSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
570d35567fcSMathieu Othacehe 	if (ret < 0)
571d35567fcSMathieu Othacehe 		return false;
572d35567fcSMathieu Othacehe 
573d35567fcSMathieu Othacehe 	return !!(ret & VCNL4000_SELF_TIMED_EN);
574f5a98e1fSGuido Günther }
575f5a98e1fSGuido Günther 
5765e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
5775e00708dSGuido Günther {
5785e00708dSGuido Günther 	struct device *dev = &data->client->dev;
5795e00708dSGuido Günther 	int ret;
5805e00708dSGuido Günther 
5815e00708dSGuido Günther 	if (on) {
582db27fdb3SJonathan Cameron 		ret = pm_runtime_resume_and_get(dev);
5835e00708dSGuido Günther 	} else {
5845e00708dSGuido Günther 		pm_runtime_mark_last_busy(dev);
5855e00708dSGuido Günther 		ret = pm_runtime_put_autosuspend(dev);
5865e00708dSGuido Günther 	}
5875e00708dSGuido Günther 
5885e00708dSGuido Günther 	return ret;
5895e00708dSGuido Günther }
5905e00708dSGuido Günther 
591fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
592fea2c97dSAstrid Rost {
593fea2c97dSAstrid Rost 	int ret;
594fea2c97dSAstrid Rost 
595fea2c97dSAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
596fea2c97dSAstrid Rost 	if (ret < 0)
597fea2c97dSAstrid Rost 		return ret;
598fea2c97dSAstrid Rost 
599fea2c97dSAstrid Rost 	ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
600fea2c97dSAstrid Rost 	if (ret >= data->chip_spec->num_als_it_times)
601fea2c97dSAstrid Rost 		return -EINVAL;
602fea2c97dSAstrid Rost 
603fea2c97dSAstrid Rost 	*val = (*data->chip_spec->als_it_times)[ret][0];
604fea2c97dSAstrid Rost 	*val2 = (*data->chip_spec->als_it_times)[ret][1];
605fea2c97dSAstrid Rost 
606fea2c97dSAstrid Rost 	return 0;
607fea2c97dSAstrid Rost }
608fea2c97dSAstrid Rost 
609fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val)
610fea2c97dSAstrid Rost {
611fea2c97dSAstrid Rost 	unsigned int i;
612fea2c97dSAstrid Rost 	int ret;
613fea2c97dSAstrid Rost 	u16 regval;
614fea2c97dSAstrid Rost 
615fea2c97dSAstrid Rost 	for (i = 0; i < data->chip_spec->num_als_it_times; i++) {
616fea2c97dSAstrid Rost 		if (val == (*data->chip_spec->als_it_times)[i][1])
617fea2c97dSAstrid Rost 			break;
618fea2c97dSAstrid Rost 	}
619fea2c97dSAstrid Rost 
620fea2c97dSAstrid Rost 	if (i == data->chip_spec->num_als_it_times)
621fea2c97dSAstrid Rost 		return -EINVAL;
622fea2c97dSAstrid Rost 
623fea2c97dSAstrid Rost 	data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200);
624fea2c97dSAstrid Rost 	data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step,
625fea2c97dSAstrid Rost 			 (*data->chip_spec->als_it_times)[0][1]),
626fea2c97dSAstrid Rost 			 val);
627fea2c97dSAstrid Rost 
628fea2c97dSAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
629fea2c97dSAstrid Rost 
630fea2c97dSAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
631fea2c97dSAstrid Rost 	if (ret < 0)
632fea2c97dSAstrid Rost 		goto out_unlock;
633fea2c97dSAstrid Rost 
634fea2c97dSAstrid Rost 	regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i);
635fea2c97dSAstrid Rost 	regval |= (ret & ~VCNL4040_ALS_CONF_IT);
636fea2c97dSAstrid Rost 	ret = i2c_smbus_write_word_data(data->client,
637fea2c97dSAstrid Rost 					VCNL4200_AL_CONF,
638fea2c97dSAstrid Rost 					regval);
639fea2c97dSAstrid Rost 
640fea2c97dSAstrid Rost out_unlock:
641fea2c97dSAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
642fea2c97dSAstrid Rost 	return ret;
643fea2c97dSAstrid Rost }
644fea2c97dSAstrid Rost 
64585e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
64685e2c6a2SMårten Lindahl {
64785e2c6a2SMårten Lindahl 	int ret;
64885e2c6a2SMårten Lindahl 
64985e2c6a2SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
65085e2c6a2SMårten Lindahl 	if (ret < 0)
65185e2c6a2SMårten Lindahl 		return ret;
65285e2c6a2SMårten Lindahl 
65385e2c6a2SMårten Lindahl 	ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
65485e2c6a2SMårten Lindahl 
655e55c96daSAstrid Rost 	if (ret >= data->chip_spec->num_ps_it_times)
65685e2c6a2SMårten Lindahl 		return -EINVAL;
65785e2c6a2SMårten Lindahl 
658e55c96daSAstrid Rost 	*val = (*data->chip_spec->ps_it_times)[ret][0];
659e55c96daSAstrid Rost 	*val2 = (*data->chip_spec->ps_it_times)[ret][1];
66085e2c6a2SMårten Lindahl 
66185e2c6a2SMårten Lindahl 	return 0;
66285e2c6a2SMårten Lindahl }
66385e2c6a2SMårten Lindahl 
66485e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
66585e2c6a2SMårten Lindahl {
66685e2c6a2SMårten Lindahl 	unsigned int i;
66785e2c6a2SMårten Lindahl 	int ret, index = -1;
66885e2c6a2SMårten Lindahl 	u16 regval;
66985e2c6a2SMårten Lindahl 
670e55c96daSAstrid Rost 	for (i = 0; i < data->chip_spec->num_ps_it_times; i++) {
671e55c96daSAstrid Rost 		if (val == (*data->chip_spec->ps_it_times)[i][1]) {
67285e2c6a2SMårten Lindahl 			index = i;
67385e2c6a2SMårten Lindahl 			break;
67485e2c6a2SMårten Lindahl 		}
67585e2c6a2SMårten Lindahl 	}
67685e2c6a2SMårten Lindahl 
67785e2c6a2SMårten Lindahl 	if (index < 0)
67885e2c6a2SMårten Lindahl 		return -EINVAL;
67985e2c6a2SMårten Lindahl 
680e55c96daSAstrid Rost 	data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC);
681e55c96daSAstrid Rost 
68285e2c6a2SMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
68385e2c6a2SMårten Lindahl 
68485e2c6a2SMårten Lindahl 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
68585e2c6a2SMårten Lindahl 	if (ret < 0)
68685e2c6a2SMårten Lindahl 		goto out;
68785e2c6a2SMårten Lindahl 
68885e2c6a2SMårten Lindahl 	regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) |
68985e2c6a2SMårten Lindahl 	    FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index);
69085e2c6a2SMårten Lindahl 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
69185e2c6a2SMårten Lindahl 					regval);
69285e2c6a2SMårten Lindahl 
69385e2c6a2SMårten Lindahl out:
69485e2c6a2SMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
69585e2c6a2SMårten Lindahl 	return ret;
69685e2c6a2SMårten Lindahl }
69785e2c6a2SMårten Lindahl 
6987f865127SAstrid Rost static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2)
6997f865127SAstrid Rost {
7007f865127SAstrid Rost 	int ret, ret_pers, it;
7017f865127SAstrid Rost 	int64_t val_c;
7027f865127SAstrid Rost 
7037f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
7047f865127SAstrid Rost 	if (ret < 0)
7057f865127SAstrid Rost 		return ret;
7067f865127SAstrid Rost 
7077f865127SAstrid Rost 	ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret);
7087f865127SAstrid Rost 	if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence))
7097f865127SAstrid Rost 		return -EINVAL;
7107f865127SAstrid Rost 
7117f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
7127f865127SAstrid Rost 	if (it >= data->chip_spec->num_als_it_times)
7137f865127SAstrid Rost 		return -EINVAL;
7147f865127SAstrid Rost 
7157f865127SAstrid Rost 	val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1],
7167f865127SAstrid Rost 			    vcnl4040_als_persistence[ret_pers]);
7177f865127SAstrid Rost 	*val = div_u64_rem(val_c, MICRO, val2);
7187f865127SAstrid Rost 
7197f865127SAstrid Rost 	return IIO_VAL_INT_PLUS_MICRO;
7207f865127SAstrid Rost }
7217f865127SAstrid Rost 
7227f865127SAstrid Rost static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2)
7237f865127SAstrid Rost {
7247f865127SAstrid Rost 	unsigned int i;
7257f865127SAstrid Rost 	int ret, it;
7267f865127SAstrid Rost 	u16 regval;
7277f865127SAstrid Rost 	u64 val_n = mul_u32_u32(val, MICRO) + val2;
7287f865127SAstrid Rost 
7297f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
7307f865127SAstrid Rost 	if (ret < 0)
7317f865127SAstrid Rost 		return ret;
7327f865127SAstrid Rost 
7337f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
7347f865127SAstrid Rost 	if (it >= data->chip_spec->num_als_it_times)
7357f865127SAstrid Rost 		return -EINVAL;
7367f865127SAstrid Rost 
7377f865127SAstrid Rost 	for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) {
7387f865127SAstrid Rost 		if (val_n < mul_u32_u32(vcnl4040_als_persistence[i],
7397f865127SAstrid Rost 					(*data->chip_spec->als_it_times)[it][1]))
7407f865127SAstrid Rost 			break;
7417f865127SAstrid Rost 	}
7427f865127SAstrid Rost 
7437f865127SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
7447f865127SAstrid Rost 
7457f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
7467f865127SAstrid Rost 	if (ret < 0)
7477f865127SAstrid Rost 		goto out_unlock;
7487f865127SAstrid Rost 
7497f865127SAstrid Rost 	regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i);
7507f865127SAstrid Rost 	regval |= (ret & ~VCNL4040_ALS_CONF_PERS);
7517f865127SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
7527f865127SAstrid Rost 					regval);
7537f865127SAstrid Rost 
7547f865127SAstrid Rost out_unlock:
7557f865127SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
7567f865127SAstrid Rost 	return ret;
7577f865127SAstrid Rost }
7587f865127SAstrid Rost 
7597f865127SAstrid Rost static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2)
7607f865127SAstrid Rost {
7617f865127SAstrid Rost 	int ret, ret_pers, it;
7627f865127SAstrid Rost 
7637f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
7647f865127SAstrid Rost 	if (ret < 0)
7657f865127SAstrid Rost 		return ret;
7667f865127SAstrid Rost 
7677f865127SAstrid Rost 	ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret);
7687f865127SAstrid Rost 	if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence))
7697f865127SAstrid Rost 		return -EINVAL;
7707f865127SAstrid Rost 
7717f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
7727f865127SAstrid Rost 	if (it >= data->chip_spec->num_ps_it_times)
7737f865127SAstrid Rost 		return -EINVAL;
7747f865127SAstrid Rost 
7757f865127SAstrid Rost 	*val = (*data->chip_spec->ps_it_times)[it][0];
7767f865127SAstrid Rost 	*val2 = (*data->chip_spec->ps_it_times)[it][1] *
7777f865127SAstrid Rost 		vcnl4040_ps_persistence[ret_pers];
7787f865127SAstrid Rost 
7797f865127SAstrid Rost 	return IIO_VAL_INT_PLUS_MICRO;
7807f865127SAstrid Rost }
7817f865127SAstrid Rost 
7827f865127SAstrid Rost static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2)
7837f865127SAstrid Rost {
7847f865127SAstrid Rost 	int ret, it, i;
7857f865127SAstrid Rost 	u16 regval;
7867f865127SAstrid Rost 
7877f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
7887f865127SAstrid Rost 	if (ret < 0)
7897f865127SAstrid Rost 		return ret;
7907f865127SAstrid Rost 
7917f865127SAstrid Rost 	it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
7927f865127SAstrid Rost 	if (it >= data->chip_spec->num_ps_it_times)
7937f865127SAstrid Rost 		return -EINVAL;
7947f865127SAstrid Rost 
7957f865127SAstrid Rost 	if (val > 0)
7967f865127SAstrid Rost 		i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1;
7977f865127SAstrid Rost 	else {
7987f865127SAstrid Rost 		for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) {
7997f865127SAstrid Rost 			if (val2 <= vcnl4040_ps_persistence[i] *
8007f865127SAstrid Rost 					(*data->chip_spec->ps_it_times)[it][1])
8017f865127SAstrid Rost 				break;
8027f865127SAstrid Rost 		}
8037f865127SAstrid Rost 	}
8047f865127SAstrid Rost 
8057f865127SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
8067f865127SAstrid Rost 
8077f865127SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
8087f865127SAstrid Rost 	if (ret < 0)
8097f865127SAstrid Rost 		goto out_unlock;
8107f865127SAstrid Rost 
8117f865127SAstrid Rost 	regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i);
8127f865127SAstrid Rost 	regval |= (ret & ~VCNL4040_CONF1_PS_PERS);
8137f865127SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
8147f865127SAstrid Rost 					regval);
8157f865127SAstrid Rost 
8167f865127SAstrid Rost out_unlock:
8177f865127SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
8187f865127SAstrid Rost 	return ret;
8197f865127SAstrid Rost }
8207f865127SAstrid Rost 
821add98466SAstrid Rost static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val)
822add98466SAstrid Rost {
823add98466SAstrid Rost 	int ret;
824add98466SAstrid Rost 
825add98466SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
826add98466SAstrid Rost 	if (ret < 0)
827add98466SAstrid Rost 		return ret;
828add98466SAstrid Rost 
829add98466SAstrid Rost 	ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret);
830add98466SAstrid Rost 	if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
831add98466SAstrid Rost 		return -EINVAL;
832add98466SAstrid Rost 
833add98466SAstrid Rost 	*val = vcnl4040_ps_oversampling_ratio[ret];
834add98466SAstrid Rost 
835add98466SAstrid Rost 	return ret;
836add98466SAstrid Rost }
837add98466SAstrid Rost 
838add98466SAstrid Rost static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val)
839add98466SAstrid Rost {
840add98466SAstrid Rost 	unsigned int i;
841add98466SAstrid Rost 	int ret;
842add98466SAstrid Rost 	u16 regval;
843add98466SAstrid Rost 
844add98466SAstrid Rost 	for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) {
845add98466SAstrid Rost 		if (val == vcnl4040_ps_oversampling_ratio[i])
846add98466SAstrid Rost 			break;
847add98466SAstrid Rost 	}
848add98466SAstrid Rost 
849add98466SAstrid Rost 	if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
850add98466SAstrid Rost 		return -EINVAL;
851add98466SAstrid Rost 
852add98466SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
853add98466SAstrid Rost 
854add98466SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
855add98466SAstrid Rost 	if (ret < 0)
856add98466SAstrid Rost 		goto out_unlock;
857add98466SAstrid Rost 
858add98466SAstrid Rost 	regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i);
859add98466SAstrid Rost 	regval |= (ret & ~VCNL4040_PS_CONF3_MPS);
860add98466SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
861add98466SAstrid Rost 					regval);
862add98466SAstrid Rost 
863add98466SAstrid Rost out_unlock:
864add98466SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
865add98466SAstrid Rost 	return ret;
866add98466SAstrid Rost }
867add98466SAstrid Rost 
868bb33e751SAstrid Rost static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2)
869bb33e751SAstrid Rost {
870bb33e751SAstrid Rost 	int ret;
871bb33e751SAstrid Rost 
872bb33e751SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
873bb33e751SAstrid Rost 	if (ret < 0)
874bb33e751SAstrid Rost 		return ret;
875bb33e751SAstrid Rost 
876bb33e751SAstrid Rost 	ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret);
877bb33e751SAstrid Rost 	if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
878bb33e751SAstrid Rost 		return -EINVAL;
879bb33e751SAstrid Rost 
880bb33e751SAstrid Rost 	*val = vcnl4040_ps_calibbias_ua[ret][0];
881bb33e751SAstrid Rost 	*val2 = vcnl4040_ps_calibbias_ua[ret][1];
882bb33e751SAstrid Rost 
883bb33e751SAstrid Rost 	return ret;
884bb33e751SAstrid Rost }
885bb33e751SAstrid Rost 
886bb33e751SAstrid Rost static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val)
887bb33e751SAstrid Rost {
888bb33e751SAstrid Rost 	unsigned int i;
889bb33e751SAstrid Rost 	int ret;
890bb33e751SAstrid Rost 	u16 regval;
891bb33e751SAstrid Rost 
892bb33e751SAstrid Rost 	for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) {
893bb33e751SAstrid Rost 		if (val == vcnl4040_ps_calibbias_ua[i][1])
894bb33e751SAstrid Rost 			break;
895bb33e751SAstrid Rost 	}
896bb33e751SAstrid Rost 
897bb33e751SAstrid Rost 	if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
898bb33e751SAstrid Rost 		return -EINVAL;
899bb33e751SAstrid Rost 
900bb33e751SAstrid Rost 	mutex_lock(&data->vcnl4000_lock);
901bb33e751SAstrid Rost 
902bb33e751SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
903bb33e751SAstrid Rost 	if (ret < 0)
904bb33e751SAstrid Rost 		goto out_unlock;
905bb33e751SAstrid Rost 
906bb33e751SAstrid Rost 	regval = (ret & ~VCNL4040_PS_MS_LED_I);
907bb33e751SAstrid Rost 	regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i);
908bb33e751SAstrid Rost 	ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
909bb33e751SAstrid Rost 					regval);
910bb33e751SAstrid Rost 
911bb33e751SAstrid Rost out_unlock:
912bb33e751SAstrid Rost 	mutex_unlock(&data->vcnl4000_lock);
913bb33e751SAstrid Rost 	return ret;
914bb33e751SAstrid Rost }
915bb33e751SAstrid Rost 
91662a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev,
91762a1efb9SPeter Meerwald 				struct iio_chan_spec const *chan,
91862a1efb9SPeter Meerwald 				int *val, int *val2, long mask)
91962a1efb9SPeter Meerwald {
9205d693139SPeter Meerwald-Stadler 	int ret;
92162a1efb9SPeter Meerwald 	struct vcnl4000_data *data = iio_priv(indio_dev);
92262a1efb9SPeter Meerwald 
92362a1efb9SPeter Meerwald 	switch (mask) {
92462a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_RAW:
9255e00708dSGuido Günther 		ret = vcnl4000_set_pm_runtime_state(data, true);
9265e00708dSGuido Günther 		if  (ret < 0)
9275e00708dSGuido Günther 			return ret;
9285e00708dSGuido Günther 
92962a1efb9SPeter Meerwald 		switch (chan->type) {
93062a1efb9SPeter Meerwald 		case IIO_LIGHT:
9311ebc787aSTomas Novotny 			ret = data->chip_spec->measure_light(data, val);
9324a818643SGuido Günther 			if (!ret)
9334a818643SGuido Günther 				ret = IIO_VAL_INT;
9344a818643SGuido Günther 			break;
93562a1efb9SPeter Meerwald 		case IIO_PROXIMITY:
9361ebc787aSTomas Novotny 			ret = data->chip_spec->measure_proximity(data, val);
937*44b90383SMårten Lindahl 			*val2 = data->ps_scale;
9384a818643SGuido Günther 			if (!ret)
939*44b90383SMårten Lindahl 				ret = IIO_VAL_FRACTIONAL;
9404a818643SGuido Günther 			break;
94162a1efb9SPeter Meerwald 		default:
9424a818643SGuido Günther 			ret = -EINVAL;
94362a1efb9SPeter Meerwald 		}
9445e00708dSGuido Günther 		vcnl4000_set_pm_runtime_state(data, false);
9454a818643SGuido Günther 		return ret;
94662a1efb9SPeter Meerwald 	case IIO_CHAN_INFO_SCALE:
9475d693139SPeter Meerwald-Stadler 		if (chan->type != IIO_LIGHT)
9485d693139SPeter Meerwald-Stadler 			return -EINVAL;
9495d693139SPeter Meerwald-Stadler 
95062a1efb9SPeter Meerwald 		*val = 0;
9511ebc787aSTomas Novotny 		*val2 = data->al_scale;
9525d693139SPeter Meerwald-Stadler 		return IIO_VAL_INT_PLUS_MICRO;
95385e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
9542be17b68SAstrid Rost 		switch (chan->type) {
955fea2c97dSAstrid Rost 		case IIO_LIGHT:
956fea2c97dSAstrid Rost 			ret = vcnl4040_read_als_it(data, val, val2);
957fea2c97dSAstrid Rost 			break;
9582be17b68SAstrid Rost 		case IIO_PROXIMITY:
95985e2c6a2SMårten Lindahl 			ret = vcnl4040_read_ps_it(data, val, val2);
9602be17b68SAstrid Rost 			break;
9612be17b68SAstrid Rost 		default:
9622be17b68SAstrid Rost 			return -EINVAL;
9632be17b68SAstrid Rost 		}
96485e2c6a2SMårten Lindahl 		if (ret < 0)
96585e2c6a2SMårten Lindahl 			return ret;
96685e2c6a2SMårten Lindahl 		return IIO_VAL_INT_PLUS_MICRO;
967add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
968add98466SAstrid Rost 		switch (chan->type) {
969add98466SAstrid Rost 		case IIO_PROXIMITY:
970add98466SAstrid Rost 			ret = vcnl4040_read_ps_oversampling_ratio(data, val);
971add98466SAstrid Rost 			if (ret < 0)
972add98466SAstrid Rost 				return ret;
973add98466SAstrid Rost 			return IIO_VAL_INT;
974add98466SAstrid Rost 		default:
975add98466SAstrid Rost 			return -EINVAL;
976add98466SAstrid Rost 		}
977bb33e751SAstrid Rost 	case IIO_CHAN_INFO_CALIBBIAS:
978bb33e751SAstrid Rost 		switch (chan->type) {
979bb33e751SAstrid Rost 		case IIO_PROXIMITY:
980bb33e751SAstrid Rost 			ret = vcnl4040_read_ps_calibbias(data, val, val2);
981bb33e751SAstrid Rost 			if (ret < 0)
982bb33e751SAstrid Rost 				return ret;
983bb33e751SAstrid Rost 			return IIO_VAL_INT_PLUS_MICRO;
984bb33e751SAstrid Rost 		default:
985bb33e751SAstrid Rost 			return -EINVAL;
986bb33e751SAstrid Rost 		}
98785e2c6a2SMårten Lindahl 	default:
98885e2c6a2SMårten Lindahl 		return -EINVAL;
98985e2c6a2SMårten Lindahl 	}
99085e2c6a2SMårten Lindahl }
99185e2c6a2SMårten Lindahl 
99285e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev,
99385e2c6a2SMårten Lindahl 			      struct iio_chan_spec const *chan,
99485e2c6a2SMårten Lindahl 			      int val, int val2, long mask)
99585e2c6a2SMårten Lindahl {
99685e2c6a2SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
99785e2c6a2SMårten Lindahl 
99885e2c6a2SMårten Lindahl 	switch (mask) {
99985e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
100085e2c6a2SMårten Lindahl 		if (val != 0)
100185e2c6a2SMårten Lindahl 			return -EINVAL;
10022be17b68SAstrid Rost 		switch (chan->type) {
1003fea2c97dSAstrid Rost 		case IIO_LIGHT:
1004fea2c97dSAstrid Rost 			return vcnl4040_write_als_it(data, val2);
10052be17b68SAstrid Rost 		case IIO_PROXIMITY:
100685e2c6a2SMårten Lindahl 			return vcnl4040_write_ps_it(data, val2);
100785e2c6a2SMårten Lindahl 		default:
100885e2c6a2SMårten Lindahl 			return -EINVAL;
100985e2c6a2SMårten Lindahl 		}
1010add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
1011add98466SAstrid Rost 		switch (chan->type) {
1012add98466SAstrid Rost 		case IIO_PROXIMITY:
1013add98466SAstrid Rost 			return vcnl4040_write_ps_oversampling_ratio(data, val);
1014add98466SAstrid Rost 		default:
1015add98466SAstrid Rost 			return -EINVAL;
1016add98466SAstrid Rost 		}
1017bb33e751SAstrid Rost 	case IIO_CHAN_INFO_CALIBBIAS:
1018bb33e751SAstrid Rost 		switch (chan->type) {
1019bb33e751SAstrid Rost 		case IIO_PROXIMITY:
1020bb33e751SAstrid Rost 			return vcnl4040_write_ps_calibbias(data, val2);
1021bb33e751SAstrid Rost 		default:
1022bb33e751SAstrid Rost 			return -EINVAL;
1023bb33e751SAstrid Rost 		}
10242be17b68SAstrid Rost 	default:
10252be17b68SAstrid Rost 		return -EINVAL;
10262be17b68SAstrid Rost 	}
102785e2c6a2SMårten Lindahl }
102885e2c6a2SMårten Lindahl 
102985e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev,
103085e2c6a2SMårten Lindahl 			       struct iio_chan_spec const *chan,
103185e2c6a2SMårten Lindahl 			       const int **vals, int *type, int *length,
103285e2c6a2SMårten Lindahl 			       long mask)
103385e2c6a2SMårten Lindahl {
1034e55c96daSAstrid Rost 	struct vcnl4000_data *data = iio_priv(indio_dev);
1035e55c96daSAstrid Rost 
103685e2c6a2SMårten Lindahl 	switch (mask) {
103785e2c6a2SMårten Lindahl 	case IIO_CHAN_INFO_INT_TIME:
10382be17b68SAstrid Rost 		switch (chan->type) {
1039fea2c97dSAstrid Rost 		case IIO_LIGHT:
1040fea2c97dSAstrid Rost 			*vals = (int *)(*data->chip_spec->als_it_times);
1041fea2c97dSAstrid Rost 			*length = 2 * data->chip_spec->num_als_it_times;
1042fea2c97dSAstrid Rost 			break;
10432be17b68SAstrid Rost 		case IIO_PROXIMITY:
1044e55c96daSAstrid Rost 			*vals = (int *)(*data->chip_spec->ps_it_times);
1045e55c96daSAstrid Rost 			*length = 2 * data->chip_spec->num_ps_it_times;
10462be17b68SAstrid Rost 			break;
10472be17b68SAstrid Rost 		default:
10482be17b68SAstrid Rost 			return -EINVAL;
10492be17b68SAstrid Rost 		}
10502be17b68SAstrid Rost 		*type = IIO_VAL_INT_PLUS_MICRO;
105185e2c6a2SMårten Lindahl 		return IIO_AVAIL_LIST;
1052add98466SAstrid Rost 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
1053add98466SAstrid Rost 		switch (chan->type) {
1054add98466SAstrid Rost 		case IIO_PROXIMITY:
1055add98466SAstrid Rost 			*vals = (int *)vcnl4040_ps_oversampling_ratio;
1056add98466SAstrid Rost 			*length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio);
1057add98466SAstrid Rost 			*type = IIO_VAL_INT;
1058add98466SAstrid Rost 			return IIO_AVAIL_LIST;
1059add98466SAstrid Rost 		default:
1060add98466SAstrid Rost 			return -EINVAL;
1061add98466SAstrid Rost 		}
1062bb33e751SAstrid Rost 	case IIO_CHAN_INFO_CALIBBIAS:
1063bb33e751SAstrid Rost 		switch (chan->type) {
1064bb33e751SAstrid Rost 		case IIO_PROXIMITY:
1065bb33e751SAstrid Rost 			*vals = (int *)vcnl4040_ps_calibbias_ua;
1066bb33e751SAstrid Rost 			*length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua);
1067bb33e751SAstrid Rost 			*type = IIO_VAL_INT_PLUS_MICRO;
1068bb33e751SAstrid Rost 			return IIO_AVAIL_LIST;
1069bb33e751SAstrid Rost 		default:
1070bb33e751SAstrid Rost 			return -EINVAL;
1071bb33e751SAstrid Rost 		}
107262a1efb9SPeter Meerwald 	default:
10735d693139SPeter Meerwald-Stadler 		return -EINVAL;
107462a1efb9SPeter Meerwald 	}
107562a1efb9SPeter Meerwald }
107662a1efb9SPeter Meerwald 
1077d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev,
1078d35567fcSMathieu Othacehe 			     struct iio_chan_spec const *chan,
1079d35567fcSMathieu Othacehe 			     int *val, int *val2, long mask)
1080d35567fcSMathieu Othacehe {
1081d35567fcSMathieu Othacehe 	int ret;
1082d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1083d35567fcSMathieu Othacehe 
1084d35567fcSMathieu Othacehe 	switch (mask) {
1085d35567fcSMathieu Othacehe 	case IIO_CHAN_INFO_RAW:
1086d35567fcSMathieu Othacehe 	case IIO_CHAN_INFO_SCALE:
1087d35567fcSMathieu Othacehe 		ret = iio_device_claim_direct_mode(indio_dev);
1088d35567fcSMathieu Othacehe 		if (ret)
1089d35567fcSMathieu Othacehe 			return ret;
1090d35567fcSMathieu Othacehe 
1091d35567fcSMathieu Othacehe 		/* Protect against event capture. */
1092d35567fcSMathieu Othacehe 		if (vcnl4010_is_in_periodic_mode(data)) {
1093d35567fcSMathieu Othacehe 			ret = -EBUSY;
1094d35567fcSMathieu Othacehe 		} else {
1095d35567fcSMathieu Othacehe 			ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
1096d35567fcSMathieu Othacehe 						mask);
1097d35567fcSMathieu Othacehe 		}
1098d35567fcSMathieu Othacehe 
1099d35567fcSMathieu Othacehe 		iio_device_release_direct_mode(indio_dev);
1100d35567fcSMathieu Othacehe 		return ret;
1101f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
1102f6889c1bSMathieu Othacehe 		switch (chan->type) {
1103f6889c1bSMathieu Othacehe 		case IIO_PROXIMITY:
1104f6889c1bSMathieu Othacehe 			ret = vcnl4010_read_proxy_samp_freq(data, val, val2);
1105f6889c1bSMathieu Othacehe 			if (ret < 0)
1106f6889c1bSMathieu Othacehe 				return ret;
1107f6889c1bSMathieu Othacehe 			return IIO_VAL_INT_PLUS_MICRO;
1108d35567fcSMathieu Othacehe 		default:
1109d35567fcSMathieu Othacehe 			return -EINVAL;
1110d35567fcSMathieu Othacehe 		}
1111f6889c1bSMathieu Othacehe 	default:
1112f6889c1bSMathieu Othacehe 		return -EINVAL;
1113f6889c1bSMathieu Othacehe 	}
1114f6889c1bSMathieu Othacehe }
1115f6889c1bSMathieu Othacehe 
1116f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev,
1117f6889c1bSMathieu Othacehe 			       struct iio_chan_spec const *chan,
1118f6889c1bSMathieu Othacehe 			       const int **vals, int *type, int *length,
1119f6889c1bSMathieu Othacehe 			       long mask)
1120f6889c1bSMathieu Othacehe {
1121f6889c1bSMathieu Othacehe 	switch (mask) {
1122f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
1123f6889c1bSMathieu Othacehe 		*vals = (int *)vcnl4010_prox_sampling_frequency;
1124f6889c1bSMathieu Othacehe 		*type = IIO_VAL_INT_PLUS_MICRO;
1125f6889c1bSMathieu Othacehe 		*length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency);
1126f6889c1bSMathieu Othacehe 		return IIO_AVAIL_LIST;
1127f6889c1bSMathieu Othacehe 	default:
1128f6889c1bSMathieu Othacehe 		return -EINVAL;
1129f6889c1bSMathieu Othacehe 	}
1130f6889c1bSMathieu Othacehe }
1131f6889c1bSMathieu Othacehe 
1132f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val,
1133f6889c1bSMathieu Othacehe 					  int val2)
1134f6889c1bSMathieu Othacehe {
1135f6889c1bSMathieu Othacehe 	unsigned int i;
1136f6889c1bSMathieu Othacehe 	int index = -1;
1137f6889c1bSMathieu Othacehe 
1138f6889c1bSMathieu Othacehe 	for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) {
1139f6889c1bSMathieu Othacehe 		if (val == vcnl4010_prox_sampling_frequency[i][0] &&
1140f6889c1bSMathieu Othacehe 		    val2 == vcnl4010_prox_sampling_frequency[i][1]) {
1141f6889c1bSMathieu Othacehe 			index = i;
1142f6889c1bSMathieu Othacehe 			break;
1143f6889c1bSMathieu Othacehe 		}
1144f6889c1bSMathieu Othacehe 	}
1145f6889c1bSMathieu Othacehe 
1146f6889c1bSMathieu Othacehe 	if (index < 0)
1147f6889c1bSMathieu Othacehe 		return -EINVAL;
1148f6889c1bSMathieu Othacehe 
1149f6889c1bSMathieu Othacehe 	return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE,
1150f6889c1bSMathieu Othacehe 					 index);
1151f6889c1bSMathieu Othacehe }
1152f6889c1bSMathieu Othacehe 
1153f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev,
1154f6889c1bSMathieu Othacehe 			      struct iio_chan_spec const *chan,
1155f6889c1bSMathieu Othacehe 			      int val, int val2, long mask)
1156f6889c1bSMathieu Othacehe {
1157f6889c1bSMathieu Othacehe 	int ret;
1158f6889c1bSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1159f6889c1bSMathieu Othacehe 
1160f6889c1bSMathieu Othacehe 	ret = iio_device_claim_direct_mode(indio_dev);
1161f6889c1bSMathieu Othacehe 	if (ret)
1162f6889c1bSMathieu Othacehe 		return ret;
1163f6889c1bSMathieu Othacehe 
1164f6889c1bSMathieu Othacehe 	/* Protect against event capture. */
1165f6889c1bSMathieu Othacehe 	if (vcnl4010_is_in_periodic_mode(data)) {
1166f6889c1bSMathieu Othacehe 		ret = -EBUSY;
1167f6889c1bSMathieu Othacehe 		goto end;
1168f6889c1bSMathieu Othacehe 	}
1169f6889c1bSMathieu Othacehe 
1170f6889c1bSMathieu Othacehe 	switch (mask) {
1171f6889c1bSMathieu Othacehe 	case IIO_CHAN_INFO_SAMP_FREQ:
1172f6889c1bSMathieu Othacehe 		switch (chan->type) {
1173f6889c1bSMathieu Othacehe 		case IIO_PROXIMITY:
1174f6889c1bSMathieu Othacehe 			ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
1175f6889c1bSMathieu Othacehe 			goto end;
1176f6889c1bSMathieu Othacehe 		default:
1177f6889c1bSMathieu Othacehe 			ret = -EINVAL;
1178f6889c1bSMathieu Othacehe 			goto end;
1179f6889c1bSMathieu Othacehe 		}
1180f6889c1bSMathieu Othacehe 	default:
1181f6889c1bSMathieu Othacehe 		ret = -EINVAL;
1182f6889c1bSMathieu Othacehe 		goto end;
1183f6889c1bSMathieu Othacehe 	}
1184f6889c1bSMathieu Othacehe 
1185f6889c1bSMathieu Othacehe end:
1186f6889c1bSMathieu Othacehe 	iio_device_release_direct_mode(indio_dev);
1187f6889c1bSMathieu Othacehe 	return ret;
1188d35567fcSMathieu Othacehe }
1189d35567fcSMathieu Othacehe 
1190d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev,
1191d35567fcSMathieu Othacehe 			       const struct iio_chan_spec *chan,
1192d35567fcSMathieu Othacehe 			       enum iio_event_type type,
1193d35567fcSMathieu Othacehe 			       enum iio_event_direction dir,
1194d35567fcSMathieu Othacehe 			       enum iio_event_info info,
1195d35567fcSMathieu Othacehe 			       int *val, int *val2)
1196d35567fcSMathieu Othacehe {
1197d35567fcSMathieu Othacehe 	int ret;
1198d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1199d35567fcSMathieu Othacehe 
1200d35567fcSMathieu Othacehe 	switch (info) {
1201d35567fcSMathieu Othacehe 	case IIO_EV_INFO_VALUE:
1202d35567fcSMathieu Othacehe 		switch (dir) {
1203d35567fcSMathieu Othacehe 		case IIO_EV_DIR_RISING:
1204d35567fcSMathieu Othacehe 			ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI,
1205d35567fcSMathieu Othacehe 						 val);
1206d35567fcSMathieu Othacehe 			if (ret < 0)
1207d35567fcSMathieu Othacehe 				return ret;
1208d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1209d35567fcSMathieu Othacehe 		case IIO_EV_DIR_FALLING:
1210d35567fcSMathieu Othacehe 			ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI,
1211d35567fcSMathieu Othacehe 						 val);
1212d35567fcSMathieu Othacehe 			if (ret < 0)
1213d35567fcSMathieu Othacehe 				return ret;
1214d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1215d35567fcSMathieu Othacehe 		default:
1216d35567fcSMathieu Othacehe 			return -EINVAL;
1217d35567fcSMathieu Othacehe 		}
1218d35567fcSMathieu Othacehe 	default:
1219d35567fcSMathieu Othacehe 		return -EINVAL;
1220d35567fcSMathieu Othacehe 	}
1221d35567fcSMathieu Othacehe }
1222d35567fcSMathieu Othacehe 
1223d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev,
1224d35567fcSMathieu Othacehe 				const struct iio_chan_spec *chan,
1225d35567fcSMathieu Othacehe 				enum iio_event_type type,
1226d35567fcSMathieu Othacehe 				enum iio_event_direction dir,
1227d35567fcSMathieu Othacehe 				enum iio_event_info info,
1228d35567fcSMathieu Othacehe 				int val, int val2)
1229d35567fcSMathieu Othacehe {
1230d35567fcSMathieu Othacehe 	int ret;
1231d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1232d35567fcSMathieu Othacehe 
1233d35567fcSMathieu Othacehe 	switch (info) {
1234d35567fcSMathieu Othacehe 	case IIO_EV_INFO_VALUE:
1235d35567fcSMathieu Othacehe 		switch (dir) {
1236d35567fcSMathieu Othacehe 		case IIO_EV_DIR_RISING:
1237d35567fcSMathieu Othacehe 			ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI,
1238d35567fcSMathieu Othacehe 						  val);
1239d35567fcSMathieu Othacehe 			if (ret < 0)
1240d35567fcSMathieu Othacehe 				return ret;
1241d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1242d35567fcSMathieu Othacehe 		case IIO_EV_DIR_FALLING:
1243d35567fcSMathieu Othacehe 			ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI,
1244d35567fcSMathieu Othacehe 						  val);
1245d35567fcSMathieu Othacehe 			if (ret < 0)
1246d35567fcSMathieu Othacehe 				return ret;
1247d35567fcSMathieu Othacehe 			return IIO_VAL_INT;
1248d35567fcSMathieu Othacehe 		default:
1249d35567fcSMathieu Othacehe 			return -EINVAL;
1250d35567fcSMathieu Othacehe 		}
1251d35567fcSMathieu Othacehe 	default:
1252d35567fcSMathieu Othacehe 		return -EINVAL;
1253d35567fcSMathieu Othacehe 	}
1254d35567fcSMathieu Othacehe }
1255d35567fcSMathieu Othacehe 
125654667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev,
125754667612SMårten Lindahl 			       const struct iio_chan_spec *chan,
125854667612SMårten Lindahl 			       enum iio_event_type type,
125954667612SMårten Lindahl 			       enum iio_event_direction dir,
126054667612SMårten Lindahl 			       enum iio_event_info info,
126154667612SMårten Lindahl 			       int *val, int *val2)
126254667612SMårten Lindahl {
126354667612SMårten Lindahl 	int ret;
126454667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
126554667612SMårten Lindahl 
12662be17b68SAstrid Rost 	switch (chan->type) {
1267bc292aafSAstrid Rost 	case IIO_LIGHT:
1268bc292aafSAstrid Rost 		switch (info) {
12697f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
12707f865127SAstrid Rost 			return vcnl4040_read_als_period(data, val, val2);
1271bc292aafSAstrid Rost 		case IIO_EV_INFO_VALUE:
1272bc292aafSAstrid Rost 			switch (dir) {
1273bc292aafSAstrid Rost 			case IIO_EV_DIR_RISING:
1274bc292aafSAstrid Rost 				ret = i2c_smbus_read_word_data(data->client,
1275bc292aafSAstrid Rost 							       VCNL4040_ALS_THDH_LM);
1276bc292aafSAstrid Rost 				break;
1277bc292aafSAstrid Rost 			case IIO_EV_DIR_FALLING:
1278bc292aafSAstrid Rost 				ret = i2c_smbus_read_word_data(data->client,
1279bc292aafSAstrid Rost 							       VCNL4040_ALS_THDL_LM);
1280bc292aafSAstrid Rost 				break;
1281bc292aafSAstrid Rost 			default:
1282bc292aafSAstrid Rost 				return -EINVAL;
1283bc292aafSAstrid Rost 			}
1284bc292aafSAstrid Rost 			break;
1285bc292aafSAstrid Rost 		default:
1286bc292aafSAstrid Rost 			return -EINVAL;
1287bc292aafSAstrid Rost 		}
1288bc292aafSAstrid Rost 		break;
12892be17b68SAstrid Rost 	case IIO_PROXIMITY:
12902be17b68SAstrid Rost 		switch (info) {
12917f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
12927f865127SAstrid Rost 			return vcnl4040_read_ps_period(data, val, val2);
12932be17b68SAstrid Rost 		case IIO_EV_INFO_VALUE:
129454667612SMårten Lindahl 			switch (dir) {
129554667612SMårten Lindahl 			case IIO_EV_DIR_RISING:
129654667612SMårten Lindahl 				ret = i2c_smbus_read_word_data(data->client,
129754667612SMårten Lindahl 							       VCNL4040_PS_THDH_LM);
12982be17b68SAstrid Rost 				break;
129954667612SMårten Lindahl 			case IIO_EV_DIR_FALLING:
130054667612SMårten Lindahl 				ret = i2c_smbus_read_word_data(data->client,
130154667612SMårten Lindahl 							       VCNL4040_PS_THDL_LM);
13022be17b68SAstrid Rost 				break;
13032be17b68SAstrid Rost 			default:
13042be17b68SAstrid Rost 				return -EINVAL;
13052be17b68SAstrid Rost 			}
13062be17b68SAstrid Rost 			break;
13072be17b68SAstrid Rost 		default:
13082be17b68SAstrid Rost 			return -EINVAL;
13092be17b68SAstrid Rost 		}
13102be17b68SAstrid Rost 		break;
13112be17b68SAstrid Rost 	default:
13122be17b68SAstrid Rost 		return -EINVAL;
13132be17b68SAstrid Rost 	}
131454667612SMårten Lindahl 	if (ret < 0)
131554667612SMårten Lindahl 		return ret;
131654667612SMårten Lindahl 	*val = ret;
131754667612SMårten Lindahl 	return IIO_VAL_INT;
131854667612SMårten Lindahl }
131954667612SMårten Lindahl 
132054667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev,
132154667612SMårten Lindahl 				const struct iio_chan_spec *chan,
132254667612SMårten Lindahl 				enum iio_event_type type,
132354667612SMårten Lindahl 				enum iio_event_direction dir,
132454667612SMårten Lindahl 				enum iio_event_info info,
132554667612SMårten Lindahl 				int val, int val2)
132654667612SMårten Lindahl {
132754667612SMårten Lindahl 	int ret;
132854667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
132954667612SMårten Lindahl 
13302be17b68SAstrid Rost 	switch (chan->type) {
1331bc292aafSAstrid Rost 	case IIO_LIGHT:
1332bc292aafSAstrid Rost 		switch (info) {
13337f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
13347f865127SAstrid Rost 			return vcnl4040_write_als_period(data, val, val2);
1335bc292aafSAstrid Rost 		case IIO_EV_INFO_VALUE:
1336bc292aafSAstrid Rost 			switch (dir) {
1337bc292aafSAstrid Rost 			case IIO_EV_DIR_RISING:
1338bc292aafSAstrid Rost 				ret = i2c_smbus_write_word_data(data->client,
1339bc292aafSAstrid Rost 								VCNL4040_ALS_THDH_LM,
1340bc292aafSAstrid Rost 								val);
1341bc292aafSAstrid Rost 				break;
1342bc292aafSAstrid Rost 			case IIO_EV_DIR_FALLING:
1343bc292aafSAstrid Rost 				ret = i2c_smbus_write_word_data(data->client,
1344bc292aafSAstrid Rost 								VCNL4040_ALS_THDL_LM,
1345bc292aafSAstrid Rost 								val);
1346bc292aafSAstrid Rost 				break;
1347bc292aafSAstrid Rost 			default:
1348bc292aafSAstrid Rost 				return -EINVAL;
1349bc292aafSAstrid Rost 			}
1350bc292aafSAstrid Rost 			break;
1351bc292aafSAstrid Rost 		default:
1352bc292aafSAstrid Rost 			return -EINVAL;
1353bc292aafSAstrid Rost 		}
1354bc292aafSAstrid Rost 		break;
13552be17b68SAstrid Rost 	case IIO_PROXIMITY:
13562be17b68SAstrid Rost 		switch (info) {
13577f865127SAstrid Rost 		case IIO_EV_INFO_PERIOD:
13587f865127SAstrid Rost 			return vcnl4040_write_ps_period(data, val, val2);
13592be17b68SAstrid Rost 		case IIO_EV_INFO_VALUE:
136054667612SMårten Lindahl 			switch (dir) {
136154667612SMårten Lindahl 			case IIO_EV_DIR_RISING:
136254667612SMårten Lindahl 				ret = i2c_smbus_write_word_data(data->client,
13632be17b68SAstrid Rost 								VCNL4040_PS_THDH_LM,
13642be17b68SAstrid Rost 								val);
13652be17b68SAstrid Rost 				break;
136654667612SMårten Lindahl 			case IIO_EV_DIR_FALLING:
136754667612SMårten Lindahl 				ret = i2c_smbus_write_word_data(data->client,
13682be17b68SAstrid Rost 								VCNL4040_PS_THDL_LM,
13692be17b68SAstrid Rost 								val);
13702be17b68SAstrid Rost 				break;
137154667612SMårten Lindahl 			default:
137254667612SMårten Lindahl 				return -EINVAL;
137354667612SMårten Lindahl 			}
13742be17b68SAstrid Rost 			break;
13752be17b68SAstrid Rost 		default:
13762be17b68SAstrid Rost 			return -EINVAL;
13772be17b68SAstrid Rost 		}
13782be17b68SAstrid Rost 		break;
13792be17b68SAstrid Rost 	default:
13802be17b68SAstrid Rost 		return -EINVAL;
13812be17b68SAstrid Rost 	}
13822be17b68SAstrid Rost 	if (ret < 0)
13832be17b68SAstrid Rost 		return ret;
13842be17b68SAstrid Rost 	return IIO_VAL_INT;
138554667612SMårten Lindahl }
138654667612SMårten Lindahl 
1387d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
1388d35567fcSMathieu Othacehe {
1389d35567fcSMathieu Othacehe 	int ret;
1390d35567fcSMathieu Othacehe 
1391d35567fcSMathieu Othacehe 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL);
1392d35567fcSMathieu Othacehe 	if (ret < 0)
1393d35567fcSMathieu Othacehe 		return false;
1394d35567fcSMathieu Othacehe 
1395d35567fcSMathieu Othacehe 	return !!(ret & VCNL4010_INT_THR_EN);
1396d35567fcSMathieu Othacehe }
1397d35567fcSMathieu Othacehe 
1398d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev,
1399d35567fcSMathieu Othacehe 				      const struct iio_chan_spec *chan,
1400d35567fcSMathieu Othacehe 				      enum iio_event_type type,
1401d35567fcSMathieu Othacehe 				      enum iio_event_direction dir)
1402d35567fcSMathieu Othacehe {
1403d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1404d35567fcSMathieu Othacehe 
1405d35567fcSMathieu Othacehe 	switch (chan->type) {
1406d35567fcSMathieu Othacehe 	case IIO_PROXIMITY:
1407d35567fcSMathieu Othacehe 		return vcnl4010_is_thr_enabled(data);
1408d35567fcSMathieu Othacehe 	default:
1409d35567fcSMathieu Othacehe 		return -EINVAL;
1410d35567fcSMathieu Othacehe 	}
1411d35567fcSMathieu Othacehe }
1412d35567fcSMathieu Othacehe 
1413d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
1414d35567fcSMathieu Othacehe {
1415d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1416d35567fcSMathieu Othacehe 	int ret;
1417d35567fcSMathieu Othacehe 	int icr;
1418d35567fcSMathieu Othacehe 	int command;
1419d35567fcSMathieu Othacehe 
1420d35567fcSMathieu Othacehe 	if (state) {
1421d35567fcSMathieu Othacehe 		ret = iio_device_claim_direct_mode(indio_dev);
1422d35567fcSMathieu Othacehe 		if (ret)
1423d35567fcSMathieu Othacehe 			return ret;
1424d35567fcSMathieu Othacehe 
1425d35567fcSMathieu Othacehe 		/* Enable periodic measurement of proximity data. */
1426d35567fcSMathieu Othacehe 		command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
1427d35567fcSMathieu Othacehe 
1428d35567fcSMathieu Othacehe 		/*
1429d35567fcSMathieu Othacehe 		 * Enable interrupts on threshold, for proximity data by
1430d35567fcSMathieu Othacehe 		 * default.
1431d35567fcSMathieu Othacehe 		 */
1432d35567fcSMathieu Othacehe 		icr = VCNL4010_INT_THR_EN;
1433d35567fcSMathieu Othacehe 	} else {
1434d35567fcSMathieu Othacehe 		if (!vcnl4010_is_thr_enabled(data))
1435d35567fcSMathieu Othacehe 			return 0;
1436d35567fcSMathieu Othacehe 
1437d35567fcSMathieu Othacehe 		command = 0;
1438d35567fcSMathieu Othacehe 		icr = 0;
1439d35567fcSMathieu Othacehe 	}
1440d35567fcSMathieu Othacehe 
1441d35567fcSMathieu Othacehe 	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
1442d35567fcSMathieu Othacehe 					command);
1443d35567fcSMathieu Othacehe 	if (ret < 0)
1444d35567fcSMathieu Othacehe 		goto end;
1445d35567fcSMathieu Othacehe 
1446d35567fcSMathieu Othacehe 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr);
1447d35567fcSMathieu Othacehe 
1448d35567fcSMathieu Othacehe end:
1449d35567fcSMathieu Othacehe 	if (state)
1450d35567fcSMathieu Othacehe 		iio_device_release_direct_mode(indio_dev);
1451d35567fcSMathieu Othacehe 
1452d35567fcSMathieu Othacehe 	return ret;
1453d35567fcSMathieu Othacehe }
1454d35567fcSMathieu Othacehe 
1455d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
1456d35567fcSMathieu Othacehe 				       const struct iio_chan_spec *chan,
1457d35567fcSMathieu Othacehe 				       enum iio_event_type type,
1458d35567fcSMathieu Othacehe 				       enum iio_event_direction dir,
1459d35567fcSMathieu Othacehe 				       int state)
1460d35567fcSMathieu Othacehe {
1461d35567fcSMathieu Othacehe 	switch (chan->type) {
1462d35567fcSMathieu Othacehe 	case IIO_PROXIMITY:
1463d35567fcSMathieu Othacehe 		return vcnl4010_config_threshold(indio_dev, state);
1464d35567fcSMathieu Othacehe 	default:
1465d35567fcSMathieu Othacehe 		return -EINVAL;
1466d35567fcSMathieu Othacehe 	}
1467d35567fcSMathieu Othacehe }
1468d35567fcSMathieu Othacehe 
146954667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
147054667612SMårten Lindahl 				      const struct iio_chan_spec *chan,
147154667612SMårten Lindahl 				      enum iio_event_type type,
147254667612SMårten Lindahl 				      enum iio_event_direction dir)
147354667612SMårten Lindahl {
147454667612SMårten Lindahl 	int ret;
147554667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
147654667612SMårten Lindahl 
14772be17b68SAstrid Rost 	switch (chan->type) {
1478bc292aafSAstrid Rost 	case IIO_LIGHT:
1479bc292aafSAstrid Rost 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
1480bc292aafSAstrid Rost 		if (ret < 0)
1481bc292aafSAstrid Rost 			return ret;
1482bc292aafSAstrid Rost 
1483bc292aafSAstrid Rost 		data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret);
1484bc292aafSAstrid Rost 
1485bc292aafSAstrid Rost 		return data->als_int;
14862be17b68SAstrid Rost 	case IIO_PROXIMITY:
148754667612SMårten Lindahl 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
148854667612SMårten Lindahl 		if (ret < 0)
148954667612SMårten Lindahl 			return ret;
149054667612SMårten Lindahl 
149154667612SMårten Lindahl 		data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
149254667612SMårten Lindahl 
149354667612SMårten Lindahl 		return (dir == IIO_EV_DIR_RISING) ?
149454667612SMårten Lindahl 			FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
149554667612SMårten Lindahl 			FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
14962be17b68SAstrid Rost 	default:
14972be17b68SAstrid Rost 		return -EINVAL;
14982be17b68SAstrid Rost 	}
149954667612SMårten Lindahl }
150054667612SMårten Lindahl 
150154667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
150254667612SMårten Lindahl 				       const struct iio_chan_spec *chan,
150354667612SMårten Lindahl 				       enum iio_event_type type,
150454667612SMårten Lindahl 				       enum iio_event_direction dir, int state)
150554667612SMårten Lindahl {
15062be17b68SAstrid Rost 	int ret = -EINVAL;
150754667612SMårten Lindahl 	u16 val, mask;
150854667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
150954667612SMårten Lindahl 
151054667612SMårten Lindahl 	mutex_lock(&data->vcnl4000_lock);
151154667612SMårten Lindahl 
15122be17b68SAstrid Rost 	switch (chan->type) {
1513bc292aafSAstrid Rost 	case IIO_LIGHT:
1514bc292aafSAstrid Rost 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
1515bc292aafSAstrid Rost 		if (ret < 0)
1516bc292aafSAstrid Rost 			goto out;
1517bc292aafSAstrid Rost 
1518bc292aafSAstrid Rost 		mask = VCNL4040_ALS_CONF_INT_EN;
1519bc292aafSAstrid Rost 		if (state)
1520bc292aafSAstrid Rost 			val = (ret | mask);
1521bc292aafSAstrid Rost 		else
1522bc292aafSAstrid Rost 			val = (ret & ~mask);
1523bc292aafSAstrid Rost 
1524bc292aafSAstrid Rost 		data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
1525bc292aafSAstrid Rost 		ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
1526bc292aafSAstrid Rost 						val);
1527bc292aafSAstrid Rost 		break;
15282be17b68SAstrid Rost 	case IIO_PROXIMITY:
152954667612SMårten Lindahl 		ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
153054667612SMårten Lindahl 		if (ret < 0)
153154667612SMårten Lindahl 			goto out;
153254667612SMårten Lindahl 
153354667612SMårten Lindahl 		if (dir == IIO_EV_DIR_RISING)
153454667612SMårten Lindahl 			mask = VCNL4040_PS_IF_AWAY;
153554667612SMårten Lindahl 		else
153654667612SMårten Lindahl 			mask = VCNL4040_PS_IF_CLOSE;
153754667612SMårten Lindahl 
153854667612SMårten Lindahl 		val = state ? (ret | mask) : (ret & ~mask);
153954667612SMårten Lindahl 
154054667612SMårten Lindahl 		data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
15412be17b68SAstrid Rost 		ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
15422be17b68SAstrid Rost 						val);
15432be17b68SAstrid Rost 		break;
15442be17b68SAstrid Rost 	default:
15452be17b68SAstrid Rost 		break;
15462be17b68SAstrid Rost 	}
154754667612SMårten Lindahl 
154854667612SMårten Lindahl out:
154954667612SMårten Lindahl 	mutex_unlock(&data->vcnl4000_lock);
155054667612SMårten Lindahl 
155154667612SMårten Lindahl 	return ret;
155254667612SMårten Lindahl }
155354667612SMårten Lindahl 
155454667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
155554667612SMårten Lindahl {
155654667612SMårten Lindahl 	struct iio_dev *indio_dev = p;
155754667612SMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
155854667612SMårten Lindahl 	int ret;
155954667612SMårten Lindahl 
1560854965b7SAstrid Rost 	ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
156154667612SMårten Lindahl 	if (ret < 0)
156254667612SMårten Lindahl 		return IRQ_HANDLED;
156354667612SMårten Lindahl 
156454667612SMårten Lindahl 	if (ret & VCNL4040_PS_IF_CLOSE) {
156554667612SMårten Lindahl 		iio_push_event(indio_dev,
156654667612SMårten Lindahl 			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
156754667612SMårten Lindahl 						    IIO_EV_TYPE_THRESH,
156854667612SMårten Lindahl 						    IIO_EV_DIR_RISING),
156954667612SMårten Lindahl 			       iio_get_time_ns(indio_dev));
157054667612SMårten Lindahl 	}
157154667612SMårten Lindahl 
157254667612SMårten Lindahl 	if (ret & VCNL4040_PS_IF_AWAY) {
157354667612SMårten Lindahl 		iio_push_event(indio_dev,
157454667612SMårten Lindahl 			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
157554667612SMårten Lindahl 						    IIO_EV_TYPE_THRESH,
157654667612SMårten Lindahl 						    IIO_EV_DIR_FALLING),
157754667612SMårten Lindahl 			       iio_get_time_ns(indio_dev));
157854667612SMårten Lindahl 	}
157954667612SMårten Lindahl 
1580bc292aafSAstrid Rost 	if (ret & VCNL4040_ALS_FALLING) {
1581bc292aafSAstrid Rost 		iio_push_event(indio_dev,
1582bc292aafSAstrid Rost 			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
1583bc292aafSAstrid Rost 						    IIO_EV_TYPE_THRESH,
1584bc292aafSAstrid Rost 						    IIO_EV_DIR_FALLING),
1585bc292aafSAstrid Rost 			       iio_get_time_ns(indio_dev));
1586bc292aafSAstrid Rost 	}
1587bc292aafSAstrid Rost 
1588bc292aafSAstrid Rost 	if (ret & VCNL4040_ALS_RISING) {
1589bc292aafSAstrid Rost 		iio_push_event(indio_dev,
1590bc292aafSAstrid Rost 			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
1591bc292aafSAstrid Rost 						    IIO_EV_TYPE_THRESH,
1592bc292aafSAstrid Rost 						    IIO_EV_DIR_RISING),
1593bc292aafSAstrid Rost 			       iio_get_time_ns(indio_dev));
1594bc292aafSAstrid Rost 	}
1595bc292aafSAstrid Rost 
159654667612SMårten Lindahl 	return IRQ_HANDLED;
159754667612SMårten Lindahl }
159854667612SMårten Lindahl 
1599d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
1600d35567fcSMathieu Othacehe 					uintptr_t priv,
1601d35567fcSMathieu Othacehe 					const struct iio_chan_spec *chan,
1602d35567fcSMathieu Othacehe 					char *buf)
1603d35567fcSMathieu Othacehe {
1604d35567fcSMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
1605d35567fcSMathieu Othacehe 
1606d35567fcSMathieu Othacehe 	return sprintf(buf, "%u\n", data->near_level);
1607d35567fcSMathieu Othacehe }
1608d35567fcSMathieu Othacehe 
16093a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
16103a52d32aSMårten Lindahl {
16113a52d32aSMårten Lindahl 	struct iio_dev *indio_dev = p;
16123a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
16133a52d32aSMårten Lindahl 	unsigned long isr;
16143a52d32aSMårten Lindahl 	int ret;
16153a52d32aSMårten Lindahl 
16163a52d32aSMårten Lindahl 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
16173a52d32aSMårten Lindahl 	if (ret < 0)
16183a52d32aSMårten Lindahl 		goto end;
16193a52d32aSMårten Lindahl 
16203a52d32aSMårten Lindahl 	isr = ret;
16213a52d32aSMårten Lindahl 
16223a52d32aSMårten Lindahl 	if (isr & VCNL4010_INT_THR) {
16233a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_THR_LOW, &isr)) {
16243a52d32aSMårten Lindahl 			iio_push_event(indio_dev,
16253a52d32aSMårten Lindahl 				       IIO_UNMOD_EVENT_CODE(
16263a52d32aSMårten Lindahl 					       IIO_PROXIMITY,
16273a52d32aSMårten Lindahl 					       1,
16283a52d32aSMårten Lindahl 					       IIO_EV_TYPE_THRESH,
16293a52d32aSMårten Lindahl 					       IIO_EV_DIR_FALLING),
16303a52d32aSMårten Lindahl 				       iio_get_time_ns(indio_dev));
16313a52d32aSMårten Lindahl 		}
16323a52d32aSMårten Lindahl 
16333a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) {
16343a52d32aSMårten Lindahl 			iio_push_event(indio_dev,
16353a52d32aSMårten Lindahl 				       IIO_UNMOD_EVENT_CODE(
16363a52d32aSMårten Lindahl 					       IIO_PROXIMITY,
16373a52d32aSMårten Lindahl 					       1,
16383a52d32aSMårten Lindahl 					       IIO_EV_TYPE_THRESH,
16393a52d32aSMårten Lindahl 					       IIO_EV_DIR_RISING),
16403a52d32aSMårten Lindahl 				       iio_get_time_ns(indio_dev));
16413a52d32aSMårten Lindahl 		}
16423a52d32aSMårten Lindahl 
16433a52d32aSMårten Lindahl 		i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
16443a52d32aSMårten Lindahl 					  isr & VCNL4010_INT_THR);
16453a52d32aSMårten Lindahl 	}
16463a52d32aSMårten Lindahl 
16473a52d32aSMårten Lindahl 	if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
1648f700e55eSMehdi Djait 		iio_trigger_poll_nested(indio_dev->trig);
16493a52d32aSMårten Lindahl 
16503a52d32aSMårten Lindahl end:
16513a52d32aSMårten Lindahl 	return IRQ_HANDLED;
16523a52d32aSMårten Lindahl }
16533a52d32aSMårten Lindahl 
16543a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p)
16553a52d32aSMårten Lindahl {
16563a52d32aSMårten Lindahl 	struct iio_poll_func *pf = p;
16573a52d32aSMårten Lindahl 	struct iio_dev *indio_dev = pf->indio_dev;
16583a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
16593a52d32aSMårten Lindahl 	const unsigned long *active_scan_mask = indio_dev->active_scan_mask;
16603a52d32aSMårten Lindahl 	u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */
16613a52d32aSMårten Lindahl 	bool data_read = false;
16623a52d32aSMårten Lindahl 	unsigned long isr;
16633a52d32aSMårten Lindahl 	int val = 0;
16643a52d32aSMårten Lindahl 	int ret;
16653a52d32aSMårten Lindahl 
16663a52d32aSMårten Lindahl 	ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
16673a52d32aSMårten Lindahl 	if (ret < 0)
16683a52d32aSMårten Lindahl 		goto end;
16693a52d32aSMårten Lindahl 
16703a52d32aSMårten Lindahl 	isr = ret;
16713a52d32aSMårten Lindahl 
16723a52d32aSMårten Lindahl 	if (test_bit(0, active_scan_mask)) {
16733a52d32aSMårten Lindahl 		if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) {
16743a52d32aSMårten Lindahl 			ret = vcnl4000_read_data(data,
16753a52d32aSMårten Lindahl 						 VCNL4000_PS_RESULT_HI,
16763a52d32aSMårten Lindahl 						 &val);
16773a52d32aSMårten Lindahl 			if (ret < 0)
16783a52d32aSMårten Lindahl 				goto end;
16793a52d32aSMårten Lindahl 
16803a52d32aSMårten Lindahl 			buffer[0] = val;
16813a52d32aSMårten Lindahl 			data_read = true;
16823a52d32aSMårten Lindahl 		}
16833a52d32aSMårten Lindahl 	}
16843a52d32aSMårten Lindahl 
16853a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
16863a52d32aSMårten Lindahl 					isr & VCNL4010_INT_DRDY);
16873a52d32aSMårten Lindahl 	if (ret < 0)
16883a52d32aSMårten Lindahl 		goto end;
16893a52d32aSMårten Lindahl 
16903a52d32aSMårten Lindahl 	if (!data_read)
16913a52d32aSMårten Lindahl 		goto end;
16923a52d32aSMårten Lindahl 
16933a52d32aSMårten Lindahl 	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
16943a52d32aSMårten Lindahl 					   iio_get_time_ns(indio_dev));
16953a52d32aSMårten Lindahl 
16963a52d32aSMårten Lindahl end:
16973a52d32aSMårten Lindahl 	iio_trigger_notify_done(indio_dev->trig);
16983a52d32aSMårten Lindahl 	return IRQ_HANDLED;
16993a52d32aSMårten Lindahl }
17003a52d32aSMårten Lindahl 
17013a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
17023a52d32aSMårten Lindahl {
17033a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
17043a52d32aSMårten Lindahl 	int ret;
17053a52d32aSMårten Lindahl 	int cmd;
17063a52d32aSMårten Lindahl 
17073a52d32aSMårten Lindahl 	/* Do not enable the buffer if we are already capturing events. */
17083a52d32aSMårten Lindahl 	if (vcnl4010_is_in_periodic_mode(data))
17093a52d32aSMårten Lindahl 		return -EBUSY;
17103a52d32aSMårten Lindahl 
17113a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
17123a52d32aSMårten Lindahl 					VCNL4010_INT_PROX_EN);
17133a52d32aSMårten Lindahl 	if (ret < 0)
17143a52d32aSMårten Lindahl 		return ret;
17153a52d32aSMårten Lindahl 
17163a52d32aSMårten Lindahl 	cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
17173a52d32aSMårten Lindahl 	return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd);
17183a52d32aSMårten Lindahl }
17193a52d32aSMårten Lindahl 
17203a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
17213a52d32aSMårten Lindahl {
17223a52d32aSMårten Lindahl 	struct vcnl4000_data *data = iio_priv(indio_dev);
17233a52d32aSMårten Lindahl 	int ret;
17243a52d32aSMårten Lindahl 
17253a52d32aSMårten Lindahl 	ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
17263a52d32aSMårten Lindahl 	if (ret < 0)
17273a52d32aSMårten Lindahl 		return ret;
17283a52d32aSMårten Lindahl 
17293a52d32aSMårten Lindahl 	return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
17303a52d32aSMårten Lindahl }
17313a52d32aSMårten Lindahl 
17323a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
17333a52d32aSMårten Lindahl 	.postenable = &vcnl4010_buffer_postenable,
17343a52d32aSMårten Lindahl 	.predisable = &vcnl4010_buffer_predisable,
17353a52d32aSMårten Lindahl };
17363a52d32aSMårten Lindahl 
1737d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
1738d35567fcSMathieu Othacehe 	{
1739d35567fcSMathieu Othacehe 		.name = "nearlevel",
1740d35567fcSMathieu Othacehe 		.shared = IIO_SEPARATE,
1741d35567fcSMathieu Othacehe 		.read = vcnl4000_read_near_level,
1742d35567fcSMathieu Othacehe 	},
1743d35567fcSMathieu Othacehe 	{ /* sentinel */ }
1744d35567fcSMathieu Othacehe };
1745d35567fcSMathieu Othacehe 
1746d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = {
1747d35567fcSMathieu Othacehe 	{
1748d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1749d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_RISING,
1750d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
1751d35567fcSMathieu Othacehe 	}, {
1752d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1753d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_FALLING,
1754d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
1755d35567fcSMathieu Othacehe 	}, {
1756d35567fcSMathieu Othacehe 		.type = IIO_EV_TYPE_THRESH,
1757d35567fcSMathieu Othacehe 		.dir = IIO_EV_DIR_EITHER,
1758d35567fcSMathieu Othacehe 		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
1759d35567fcSMathieu Othacehe 	}
1760d35567fcSMathieu Othacehe };
1761d35567fcSMathieu Othacehe 
17627f865127SAstrid Rost static const struct iio_event_spec vcnl4040_als_event_spec[] = {
17637f865127SAstrid Rost 	{
17647f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
17657f865127SAstrid Rost 		.dir = IIO_EV_DIR_RISING,
17667f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
17677f865127SAstrid Rost 	}, {
17687f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
17697f865127SAstrid Rost 		.dir = IIO_EV_DIR_FALLING,
17707f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_VALUE),
17717f865127SAstrid Rost 	}, {
17727f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
17737f865127SAstrid Rost 		.dir = IIO_EV_DIR_EITHER,
17747f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD),
17757f865127SAstrid Rost 	},
17767f865127SAstrid Rost };
17777f865127SAstrid Rost 
177854667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = {
177954667612SMårten Lindahl 	{
178054667612SMårten Lindahl 		.type = IIO_EV_TYPE_THRESH,
178154667612SMårten Lindahl 		.dir = IIO_EV_DIR_RISING,
178254667612SMårten Lindahl 		.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
178354667612SMårten Lindahl 	}, {
178454667612SMårten Lindahl 		.type = IIO_EV_TYPE_THRESH,
178554667612SMårten Lindahl 		.dir = IIO_EV_DIR_FALLING,
178654667612SMårten Lindahl 		.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
17877f865127SAstrid Rost 	}, {
17887f865127SAstrid Rost 		.type = IIO_EV_TYPE_THRESH,
17897f865127SAstrid Rost 		.dir = IIO_EV_DIR_EITHER,
17907f865127SAstrid Rost 		.mask_separate = BIT(IIO_EV_INFO_PERIOD),
179154667612SMårten Lindahl 	},
179254667612SMårten Lindahl };
179354667612SMårten Lindahl 
1794d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = {
1795d35567fcSMathieu Othacehe 	{
1796d35567fcSMathieu Othacehe 		.type = IIO_LIGHT,
1797d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1798d35567fcSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SCALE),
1799d35567fcSMathieu Othacehe 	}, {
1800d35567fcSMathieu Othacehe 		.type = IIO_PROXIMITY,
1801d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
1802d35567fcSMathieu Othacehe 		.ext_info = vcnl4000_ext_info,
1803d35567fcSMathieu Othacehe 	}
1804d35567fcSMathieu Othacehe };
1805d35567fcSMathieu Othacehe 
1806d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = {
1807d35567fcSMathieu Othacehe 	{
1808d35567fcSMathieu Othacehe 		.type = IIO_LIGHT,
18098fe78d52SMathieu Othacehe 		.scan_index = -1,
1810d35567fcSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1811d35567fcSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SCALE),
1812d35567fcSMathieu Othacehe 	}, {
1813d35567fcSMathieu Othacehe 		.type = IIO_PROXIMITY,
18148fe78d52SMathieu Othacehe 		.scan_index = 0,
1815f6889c1bSMathieu Othacehe 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1816f6889c1bSMathieu Othacehe 			BIT(IIO_CHAN_INFO_SAMP_FREQ),
1817f6889c1bSMathieu Othacehe 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
1818d35567fcSMathieu Othacehe 		.event_spec = vcnl4000_event_spec,
1819d35567fcSMathieu Othacehe 		.num_event_specs = ARRAY_SIZE(vcnl4000_event_spec),
1820d35567fcSMathieu Othacehe 		.ext_info = vcnl4000_ext_info,
18218fe78d52SMathieu Othacehe 		.scan_type = {
18228fe78d52SMathieu Othacehe 			.sign = 'u',
18238fe78d52SMathieu Othacehe 			.realbits = 16,
18248fe78d52SMathieu Othacehe 			.storagebits = 16,
18258fe78d52SMathieu Othacehe 			.endianness = IIO_CPU,
1826d35567fcSMathieu Othacehe 		},
18278fe78d52SMathieu Othacehe 	},
18288fe78d52SMathieu Othacehe 	IIO_CHAN_SOFT_TIMESTAMP(1),
1829d35567fcSMathieu Othacehe };
1830d35567fcSMathieu Othacehe 
183185e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = {
183285e2c6a2SMårten Lindahl 	{
183385e2c6a2SMårten Lindahl 		.type = IIO_LIGHT,
183485e2c6a2SMårten Lindahl 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1835fea2c97dSAstrid Rost 			BIT(IIO_CHAN_INFO_SCALE) |
1836fea2c97dSAstrid Rost 			BIT(IIO_CHAN_INFO_INT_TIME),
1837fea2c97dSAstrid Rost 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
18387f865127SAstrid Rost 		.event_spec = vcnl4040_als_event_spec,
18397f865127SAstrid Rost 		.num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec),
184085e2c6a2SMårten Lindahl 	}, {
184185e2c6a2SMårten Lindahl 		.type = IIO_PROXIMITY,
184285e2c6a2SMårten Lindahl 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1843add98466SAstrid Rost 			BIT(IIO_CHAN_INFO_INT_TIME) |
1844bb33e751SAstrid Rost 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
1845bb33e751SAstrid Rost 			BIT(IIO_CHAN_INFO_CALIBBIAS),
1846add98466SAstrid Rost 		.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
1847bb33e751SAstrid Rost 			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
1848bb33e751SAstrid Rost 			BIT(IIO_CHAN_INFO_CALIBBIAS),
184985e2c6a2SMårten Lindahl 		.ext_info = vcnl4000_ext_info,
185054667612SMårten Lindahl 		.event_spec = vcnl4040_event_spec,
185154667612SMårten Lindahl 		.num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
185285e2c6a2SMårten Lindahl 	}
185385e2c6a2SMårten Lindahl };
185485e2c6a2SMårten Lindahl 
185562a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = {
185662a1efb9SPeter Meerwald 	.read_raw = vcnl4000_read_raw,
185762a1efb9SPeter Meerwald };
185862a1efb9SPeter Meerwald 
1859d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = {
1860d35567fcSMathieu Othacehe 	.read_raw = vcnl4010_read_raw,
1861f6889c1bSMathieu Othacehe 	.read_avail = vcnl4010_read_avail,
1862f6889c1bSMathieu Othacehe 	.write_raw = vcnl4010_write_raw,
1863d35567fcSMathieu Othacehe 	.read_event_value = vcnl4010_read_event,
1864d35567fcSMathieu Othacehe 	.write_event_value = vcnl4010_write_event,
1865d35567fcSMathieu Othacehe 	.read_event_config = vcnl4010_read_event_config,
1866d35567fcSMathieu Othacehe 	.write_event_config = vcnl4010_write_event_config,
1867d35567fcSMathieu Othacehe };
1868d35567fcSMathieu Othacehe 
186985e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = {
187085e2c6a2SMårten Lindahl 	.read_raw = vcnl4000_read_raw,
187185e2c6a2SMårten Lindahl 	.write_raw = vcnl4040_write_raw,
187254667612SMårten Lindahl 	.read_event_value = vcnl4040_read_event,
187354667612SMårten Lindahl 	.write_event_value = vcnl4040_write_event,
187454667612SMårten Lindahl 	.read_event_config = vcnl4040_read_event_config,
187554667612SMårten Lindahl 	.write_event_config = vcnl4040_write_event_config,
187685e2c6a2SMårten Lindahl 	.read_avail = vcnl4040_read_avail,
187785e2c6a2SMårten Lindahl };
187885e2c6a2SMårten Lindahl 
1879d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
1880d35567fcSMathieu Othacehe 	[VCNL4000] = {
1881d35567fcSMathieu Othacehe 		.prod = "VCNL4000",
1882d35567fcSMathieu Othacehe 		.init = vcnl4000_init,
1883d35567fcSMathieu Othacehe 		.measure_light = vcnl4000_measure_light,
1884d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4000_measure_proximity,
1885d35567fcSMathieu Othacehe 		.set_power_state = vcnl4000_set_power_state,
1886d35567fcSMathieu Othacehe 		.channels = vcnl4000_channels,
1887d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4000_channels),
1888d35567fcSMathieu Othacehe 		.info = &vcnl4000_info,
1889d35567fcSMathieu Othacehe 	},
1890d35567fcSMathieu Othacehe 	[VCNL4010] = {
1891d35567fcSMathieu Othacehe 		.prod = "VCNL4010/4020",
1892d35567fcSMathieu Othacehe 		.init = vcnl4000_init,
1893d35567fcSMathieu Othacehe 		.measure_light = vcnl4000_measure_light,
1894d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4000_measure_proximity,
1895d35567fcSMathieu Othacehe 		.set_power_state = vcnl4000_set_power_state,
1896d35567fcSMathieu Othacehe 		.channels = vcnl4010_channels,
1897d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4010_channels),
1898d35567fcSMathieu Othacehe 		.info = &vcnl4010_info,
1899bfb6cfeeSMårten Lindahl 		.irq_thread = vcnl4010_irq_thread,
1900bfb6cfeeSMårten Lindahl 		.trig_buffer_func = vcnl4010_trigger_handler,
1901bfb6cfeeSMårten Lindahl 		.buffer_setup_ops = &vcnl4010_buffer_ops,
1902d35567fcSMathieu Othacehe 	},
1903d35567fcSMathieu Othacehe 	[VCNL4040] = {
1904d35567fcSMathieu Othacehe 		.prod = "VCNL4040",
1905d35567fcSMathieu Othacehe 		.init = vcnl4200_init,
1906d35567fcSMathieu Othacehe 		.measure_light = vcnl4200_measure_light,
1907d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4200_measure_proximity,
1908d35567fcSMathieu Othacehe 		.set_power_state = vcnl4200_set_power_state,
190985e2c6a2SMårten Lindahl 		.channels = vcnl4040_channels,
191085e2c6a2SMårten Lindahl 		.num_channels = ARRAY_SIZE(vcnl4040_channels),
191185e2c6a2SMårten Lindahl 		.info = &vcnl4040_info,
191254667612SMårten Lindahl 		.irq_thread = vcnl4040_irq_thread,
1913854965b7SAstrid Rost 		.int_reg = VCNL4040_INT_FLAGS,
1914e55c96daSAstrid Rost 		.ps_it_times = &vcnl4040_ps_it_times,
1915e55c96daSAstrid Rost 		.num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times),
1916fea2c97dSAstrid Rost 		.als_it_times = &vcnl4040_als_it_times,
1917fea2c97dSAstrid Rost 		.num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times),
1918fea2c97dSAstrid Rost 		.ulux_step = 100000,
1919d35567fcSMathieu Othacehe 	},
1920d35567fcSMathieu Othacehe 	[VCNL4200] = {
1921d35567fcSMathieu Othacehe 		.prod = "VCNL4200",
1922d35567fcSMathieu Othacehe 		.init = vcnl4200_init,
1923d35567fcSMathieu Othacehe 		.measure_light = vcnl4200_measure_light,
1924d35567fcSMathieu Othacehe 		.measure_proximity = vcnl4200_measure_proximity,
1925d35567fcSMathieu Othacehe 		.set_power_state = vcnl4200_set_power_state,
1926854965b7SAstrid Rost 		.channels = vcnl4040_channels,
1927d35567fcSMathieu Othacehe 		.num_channels = ARRAY_SIZE(vcnl4000_channels),
1928854965b7SAstrid Rost 		.info = &vcnl4040_info,
1929854965b7SAstrid Rost 		.irq_thread = vcnl4040_irq_thread,
1930854965b7SAstrid Rost 		.int_reg = VCNL4200_INT_FLAGS,
1931e55c96daSAstrid Rost 		.ps_it_times = &vcnl4200_ps_it_times,
1932e55c96daSAstrid Rost 		.num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times),
1933fea2c97dSAstrid Rost 		.als_it_times = &vcnl4200_als_it_times,
1934fea2c97dSAstrid Rost 		.num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times),
1935fea2c97dSAstrid Rost 		.ulux_step = 24000,
1936d35567fcSMathieu Othacehe 	},
1937d35567fcSMathieu Othacehe };
1938d35567fcSMathieu Othacehe 
19398fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = {
19408fe78d52SMathieu Othacehe 	.validate_device = iio_trigger_validate_own_device,
19418fe78d52SMathieu Othacehe };
19428fe78d52SMathieu Othacehe 
19438fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev)
19448fe78d52SMathieu Othacehe {
19458fe78d52SMathieu Othacehe 	struct vcnl4000_data *data = iio_priv(indio_dev);
19468fe78d52SMathieu Othacehe 	struct i2c_client *client = data->client;
19478fe78d52SMathieu Othacehe 	struct iio_trigger *trigger;
19488fe78d52SMathieu Othacehe 
19498fe78d52SMathieu Othacehe 	trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
195015ea2878SJonathan Cameron 					 indio_dev->name,
195115ea2878SJonathan Cameron 					 iio_device_id(indio_dev));
19528fe78d52SMathieu Othacehe 	if (!trigger)
19538fe78d52SMathieu Othacehe 		return -ENOMEM;
19548fe78d52SMathieu Othacehe 
19558fe78d52SMathieu Othacehe 	trigger->ops = &vcnl4010_trigger_ops;
19568fe78d52SMathieu Othacehe 	iio_trigger_set_drvdata(trigger, indio_dev);
19578fe78d52SMathieu Othacehe 
19588fe78d52SMathieu Othacehe 	return devm_iio_trigger_register(&client->dev, trigger);
19598fe78d52SMathieu Othacehe }
19608fe78d52SMathieu Othacehe 
1961e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client)
196262a1efb9SPeter Meerwald {
1963e61295e0SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
196462a1efb9SPeter Meerwald 	struct vcnl4000_data *data;
196562a1efb9SPeter Meerwald 	struct iio_dev *indio_dev;
19661ebc787aSTomas Novotny 	int ret;
196762a1efb9SPeter Meerwald 
19682669d723SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
196962a1efb9SPeter Meerwald 	if (!indio_dev)
197062a1efb9SPeter Meerwald 		return -ENOMEM;
197162a1efb9SPeter Meerwald 
197262a1efb9SPeter Meerwald 	data = iio_priv(indio_dev);
197362a1efb9SPeter Meerwald 	i2c_set_clientdata(client, indio_dev);
197462a1efb9SPeter Meerwald 	data->client = client;
19751ebc787aSTomas Novotny 	data->id = id->driver_data;
19761ebc787aSTomas Novotny 	data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
197762a1efb9SPeter Meerwald 
197842ec40b0SMårten Lindahl 	mutex_init(&data->vcnl4000_lock);
197942ec40b0SMårten Lindahl 
19801ebc787aSTomas Novotny 	ret = data->chip_spec->init(data);
198162a1efb9SPeter Meerwald 	if (ret < 0)
19822669d723SPeter Meerwald 		return ret;
198362a1efb9SPeter Meerwald 
1984d978bfddSPeter Meerwald-Stadler 	dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
19851ebc787aSTomas Novotny 		data->chip_spec->prod, data->rev);
198662a1efb9SPeter Meerwald 
1987f5a98e1fSGuido Günther 	if (device_property_read_u32(&client->dev, "proximity-near-level",
1988f5a98e1fSGuido Günther 				     &data->near_level))
1989f5a98e1fSGuido Günther 		data->near_level = 0;
1990f5a98e1fSGuido Günther 
1991d35567fcSMathieu Othacehe 	indio_dev->info = data->chip_spec->info;
1992d35567fcSMathieu Othacehe 	indio_dev->channels = data->chip_spec->channels;
1993d35567fcSMathieu Othacehe 	indio_dev->num_channels = data->chip_spec->num_channels;
199462a1efb9SPeter Meerwald 	indio_dev->name = VCNL4000_DRV_NAME;
199562a1efb9SPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
199662a1efb9SPeter Meerwald 
1997bfb6cfeeSMårten Lindahl 	if (data->chip_spec->trig_buffer_func &&
1998bfb6cfeeSMårten Lindahl 	    data->chip_spec->buffer_setup_ops) {
19998fe78d52SMathieu Othacehe 		ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
20008fe78d52SMathieu Othacehe 						      NULL,
2001bfb6cfeeSMårten Lindahl 						      data->chip_spec->trig_buffer_func,
2002bfb6cfeeSMårten Lindahl 						      data->chip_spec->buffer_setup_ops);
20038fe78d52SMathieu Othacehe 		if (ret < 0) {
20048fe78d52SMathieu Othacehe 			dev_err(&client->dev,
20058fe78d52SMathieu Othacehe 				"unable to setup iio triggered buffer\n");
20068fe78d52SMathieu Othacehe 			return ret;
20078fe78d52SMathieu Othacehe 		}
2008bfb6cfeeSMårten Lindahl 	}
20098fe78d52SMathieu Othacehe 
2010bfb6cfeeSMårten Lindahl 	if (client->irq && data->chip_spec->irq_thread) {
2011d35567fcSMathieu Othacehe 		ret = devm_request_threaded_irq(&client->dev, client->irq,
2012bfb6cfeeSMårten Lindahl 						NULL, data->chip_spec->irq_thread,
2013d35567fcSMathieu Othacehe 						IRQF_TRIGGER_FALLING |
2014d35567fcSMathieu Othacehe 						IRQF_ONESHOT,
2015bfb6cfeeSMårten Lindahl 						"vcnl4000_irq",
2016d35567fcSMathieu Othacehe 						indio_dev);
2017d35567fcSMathieu Othacehe 		if (ret < 0) {
2018d35567fcSMathieu Othacehe 			dev_err(&client->dev, "irq request failed\n");
2019d35567fcSMathieu Othacehe 			return ret;
2020d35567fcSMathieu Othacehe 		}
20218fe78d52SMathieu Othacehe 
20228fe78d52SMathieu Othacehe 		ret = vcnl4010_probe_trigger(indio_dev);
20238fe78d52SMathieu Othacehe 		if (ret < 0)
20248fe78d52SMathieu Othacehe 			return ret;
2025d35567fcSMathieu Othacehe 	}
2026d35567fcSMathieu Othacehe 
20275e00708dSGuido Günther 	ret = pm_runtime_set_active(&client->dev);
20285e00708dSGuido Günther 	if (ret < 0)
20295e00708dSGuido Günther 		goto fail_poweroff;
20305e00708dSGuido Günther 
20315e00708dSGuido Günther 	ret = iio_device_register(indio_dev);
20325e00708dSGuido Günther 	if (ret < 0)
20335e00708dSGuido Günther 		goto fail_poweroff;
20345e00708dSGuido Günther 
20355e00708dSGuido Günther 	pm_runtime_enable(&client->dev);
20365e00708dSGuido Günther 	pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
20375e00708dSGuido Günther 	pm_runtime_use_autosuspend(&client->dev);
20385e00708dSGuido Günther 
20395e00708dSGuido Günther 	return 0;
20405e00708dSGuido Günther fail_poweroff:
20415e00708dSGuido Günther 	data->chip_spec->set_power_state(data, false);
20425e00708dSGuido Günther 	return ret;
204362a1efb9SPeter Meerwald }
204462a1efb9SPeter Meerwald 
2045ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = {
2046ebd457d5SAngus Ainslie (Purism) 	{
2047ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4000",
20481436a78cSMarco Felsch 		.data = (void *)VCNL4000,
2049ebd457d5SAngus Ainslie (Purism) 	},
2050ebd457d5SAngus Ainslie (Purism) 	{
2051ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4010",
20521436a78cSMarco Felsch 		.data = (void *)VCNL4010,
2053ebd457d5SAngus Ainslie (Purism) 	},
2054ebd457d5SAngus Ainslie (Purism) 	{
20551436a78cSMarco Felsch 		.compatible = "vishay,vcnl4020",
20561436a78cSMarco Felsch 		.data = (void *)VCNL4010,
2057ebd457d5SAngus Ainslie (Purism) 	},
2058ebd457d5SAngus Ainslie (Purism) 	{
20597fd1c260SMarco Felsch 		.compatible = "vishay,vcnl4040",
20607fd1c260SMarco Felsch 		.data = (void *)VCNL4040,
20617fd1c260SMarco Felsch 	},
20627fd1c260SMarco Felsch 	{
2063ebd457d5SAngus Ainslie (Purism) 		.compatible = "vishay,vcnl4200",
20641436a78cSMarco Felsch 		.data = (void *)VCNL4200,
2065ebd457d5SAngus Ainslie (Purism) 	},
2066ebd457d5SAngus Ainslie (Purism) 	{},
2067ebd457d5SAngus Ainslie (Purism) };
2068ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
2069ebd457d5SAngus Ainslie (Purism) 
2070ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client)
20715e00708dSGuido Günther {
20725e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
20735e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
2074ab91da2fSUwe Kleine-König 	int ret;
20755e00708dSGuido Günther 
20765e00708dSGuido Günther 	pm_runtime_dont_use_autosuspend(&client->dev);
20775e00708dSGuido Günther 	pm_runtime_disable(&client->dev);
20785e00708dSGuido Günther 	iio_device_unregister(indio_dev);
20795e00708dSGuido Günther 	pm_runtime_set_suspended(&client->dev);
20805e00708dSGuido Günther 
2081ab91da2fSUwe Kleine-König 	ret = data->chip_spec->set_power_state(data, false);
2082ab91da2fSUwe Kleine-König 	if (ret)
2083ab91da2fSUwe Kleine-König 		dev_warn(&client->dev, "Failed to power down (%pe)\n",
2084ab91da2fSUwe Kleine-König 			 ERR_PTR(ret));
20855e00708dSGuido Günther }
20865e00708dSGuido Günther 
2087cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev)
20885e00708dSGuido Günther {
20895e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
20905e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
20915e00708dSGuido Günther 
20925e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, false);
20935e00708dSGuido Günther }
20945e00708dSGuido Günther 
2095cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev)
20965e00708dSGuido Günther {
20975e00708dSGuido Günther 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
20985e00708dSGuido Günther 	struct vcnl4000_data *data = iio_priv(indio_dev);
20995e00708dSGuido Günther 
21005e00708dSGuido Günther 	return data->chip_spec->set_power_state(data, true);
21015e00708dSGuido Günther }
21025e00708dSGuido Günther 
2103cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend,
2104cd4d10b1SJonathan Cameron 				 vcnl4000_runtime_resume, NULL);
21055e00708dSGuido Günther 
210662a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = {
210762a1efb9SPeter Meerwald 	.driver = {
210862a1efb9SPeter Meerwald 		.name   = VCNL4000_DRV_NAME,
2109cd4d10b1SJonathan Cameron 		.pm	= pm_ptr(&vcnl4000_pm_ops),
2110ebd457d5SAngus Ainslie (Purism) 		.of_match_table = vcnl_4000_of_match,
211162a1efb9SPeter Meerwald 	},
21127cf15f42SUwe Kleine-König 	.probe = vcnl4000_probe,
211362a1efb9SPeter Meerwald 	.id_table = vcnl4000_id,
21145e00708dSGuido Günther 	.remove	= vcnl4000_remove,
211562a1efb9SPeter Meerwald };
211662a1efb9SPeter Meerwald 
211762a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver);
211862a1efb9SPeter Meerwald 
211962a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
21208fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
212162a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
212262a1efb9SPeter Meerwald MODULE_LICENSE("GPL");
2123