Commit | Line | Data |
---|---|---|
36edc939 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
62a1efb9 | 2 | /* |
5a441aad | 3 | * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient |
d978bfdd | 4 | * light and proximity sensor |
62a1efb9 PM |
5 | * |
6 | * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> | |
5a441aad | 7 | * Copyright 2019 Pursim SPC |
62a1efb9 | 8 | * |
be38866f TN |
9 | * IIO driver for: |
10 | * VCNL4000/10/20 (7-bit I2C slave address 0x13) | |
5a441aad | 11 | * VCNL4040 (7-bit I2C slave address 0x60) |
be38866f | 12 | * VCNL4200 (7-bit I2C slave address 0x51) |
62a1efb9 PM |
13 | * |
14 | * TODO: | |
15 | * allow to adjust IR current | |
16 | * proximity threshold and event handling | |
d978bfdd | 17 | * periodic ALS/proximity measurement (VCNL4010/20) |
5a441aad | 18 | * interrupts (VCNL4010/20/40, VCNL4200) |
62a1efb9 PM |
19 | */ |
20 | ||
21 | #include <linux/module.h> | |
22 | #include <linux/i2c.h> | |
23 | #include <linux/err.h> | |
24 | #include <linux/delay.h> | |
5e00708d | 25 | #include <linux/pm_runtime.h> |
62a1efb9 PM |
26 | |
27 | #include <linux/iio/iio.h> | |
28 | #include <linux/iio/sysfs.h> | |
29 | ||
30 | #define VCNL4000_DRV_NAME "vcnl4000" | |
1ebc787a TN |
31 | #define VCNL4000_PROD_ID 0x01 |
32 | #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ | |
5a441aad | 33 | #define VCNL4040_PROD_ID 0x86 |
be38866f | 34 | #define VCNL4200_PROD_ID 0x58 |
62a1efb9 PM |
35 | |
36 | #define VCNL4000_COMMAND 0x80 /* Command register */ | |
37 | #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ | |
38 | #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ | |
39 | #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ | |
40 | #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ | |
41 | #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ | |
42 | #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ | |
43 | #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ | |
44 | #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ | |
45 | #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ | |
46 | ||
be38866f TN |
47 | #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ |
48 | #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ | |
49 | #define VCNL4200_PS_DATA 0x08 /* Proximity data */ | |
50 | #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ | |
51 | #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ | |
52 | ||
5a441aad AAP |
53 | #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ |
54 | ||
62a1efb9 | 55 | /* Bit masks for COMMAND register */ |
ff6a5259 PMS |
56 | #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ |
57 | #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ | |
58 | #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ | |
59 | #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ | |
62a1efb9 | 60 | |
5e00708d GG |
61 | #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ |
62 | ||
1ebc787a TN |
63 | enum vcnl4000_device_ids { |
64 | VCNL4000, | |
50c50b97 | 65 | VCNL4010, |
5a441aad | 66 | VCNL4040, |
be38866f TN |
67 | VCNL4200, |
68 | }; | |
69 | ||
70 | struct vcnl4200_channel { | |
71 | u8 reg; | |
72 | ktime_t last_measurement; | |
73 | ktime_t sampling_rate; | |
74 | struct mutex lock; | |
1ebc787a TN |
75 | }; |
76 | ||
62a1efb9 PM |
77 | struct vcnl4000_data { |
78 | struct i2c_client *client; | |
1ebc787a TN |
79 | enum vcnl4000_device_ids id; |
80 | int rev; | |
81 | int al_scale; | |
82 | const struct vcnl4000_chip_spec *chip_spec; | |
be38866f TN |
83 | struct mutex vcnl4000_lock; |
84 | struct vcnl4200_channel vcnl4200_al; | |
85 | struct vcnl4200_channel vcnl4200_ps; | |
62a1efb9 PM |
86 | }; |
87 | ||
1ebc787a TN |
88 | struct vcnl4000_chip_spec { |
89 | const char *prod; | |
90 | int (*init)(struct vcnl4000_data *data); | |
91 | int (*measure_light)(struct vcnl4000_data *data, int *val); | |
92 | int (*measure_proximity)(struct vcnl4000_data *data, int *val); | |
5e00708d | 93 | int (*set_power_state)(struct vcnl4000_data *data, bool on); |
1ebc787a TN |
94 | }; |
95 | ||
62a1efb9 | 96 | static const struct i2c_device_id vcnl4000_id[] = { |
1ebc787a | 97 | { "vcnl4000", VCNL4000 }, |
50c50b97 TN |
98 | { "vcnl4010", VCNL4010 }, |
99 | { "vcnl4020", VCNL4010 }, | |
5a441aad | 100 | { "vcnl4040", VCNL4040 }, |
be38866f | 101 | { "vcnl4200", VCNL4200 }, |
62a1efb9 PM |
102 | { } |
103 | }; | |
104 | MODULE_DEVICE_TABLE(i2c, vcnl4000_id); | |
105 | ||
5e00708d GG |
106 | static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) |
107 | { | |
108 | /* no suspend op */ | |
109 | return 0; | |
110 | } | |
111 | ||
1ebc787a TN |
112 | static int vcnl4000_init(struct vcnl4000_data *data) |
113 | { | |
114 | int ret, prod_id; | |
115 | ||
116 | ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); | |
117 | if (ret < 0) | |
118 | return ret; | |
119 | ||
120 | prod_id = ret >> 4; | |
58bf9ace TN |
121 | switch (prod_id) { |
122 | case VCNL4000_PROD_ID: | |
123 | if (data->id != VCNL4000) | |
124 | dev_warn(&data->client->dev, | |
125 | "wrong device id, use vcnl4000"); | |
126 | break; | |
127 | case VCNL4010_PROD_ID: | |
128 | if (data->id != VCNL4010) | |
129 | dev_warn(&data->client->dev, | |
130 | "wrong device id, use vcnl4010/4020"); | |
131 | break; | |
132 | default: | |
1ebc787a | 133 | return -ENODEV; |
58bf9ace | 134 | } |
1ebc787a TN |
135 | |
136 | data->rev = ret & 0xf; | |
137 | data->al_scale = 250000; | |
be38866f TN |
138 | mutex_init(&data->vcnl4000_lock); |
139 | ||
5e00708d | 140 | return data->chip_spec->set_power_state(data, true); |
be38866f TN |
141 | }; |
142 | ||
5e00708d GG |
143 | static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) |
144 | { | |
145 | u16 val = on ? 0 /* power on */ : 1 /* shut down */; | |
146 | int ret; | |
147 | ||
148 | ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); | |
149 | if (ret < 0) | |
150 | return ret; | |
151 | ||
152 | ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); | |
153 | if (ret < 0) | |
154 | return ret; | |
155 | ||
156 | if (on) { | |
157 | /* Wait at least one integration cycle before fetching data */ | |
158 | data->vcnl4200_al.last_measurement = ktime_get(); | |
159 | data->vcnl4200_ps.last_measurement = ktime_get(); | |
160 | } | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
be38866f TN |
165 | static int vcnl4200_init(struct vcnl4000_data *data) |
166 | { | |
5a441aad | 167 | int ret, id; |
be38866f TN |
168 | |
169 | ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); | |
170 | if (ret < 0) | |
171 | return ret; | |
172 | ||
5a441aad AAP |
173 | id = ret & 0xff; |
174 | ||
175 | if (id != VCNL4200_PROD_ID) { | |
176 | ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); | |
177 | if (ret < 0) | |
178 | return ret; | |
179 | ||
180 | id = ret & 0xff; | |
181 | ||
182 | if (id != VCNL4040_PROD_ID) | |
183 | return -ENODEV; | |
184 | } | |
185 | ||
186 | dev_dbg(&data->client->dev, "device id 0x%x", id); | |
be38866f TN |
187 | |
188 | data->rev = (ret >> 8) & 0xf; | |
189 | ||
be38866f TN |
190 | data->vcnl4200_al.reg = VCNL4200_AL_DATA; |
191 | data->vcnl4200_ps.reg = VCNL4200_PS_DATA; | |
5a441aad AAP |
192 | switch (id) { |
193 | case VCNL4200_PROD_ID: | |
b42aa97e TN |
194 | /* Default wait time is 50ms, add 20% tolerance. */ |
195 | data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); | |
196 | /* Default wait time is 4.8ms, add 20% tolerance. */ | |
197 | data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); | |
bc80573e | 198 | data->al_scale = 24000; |
5a441aad AAP |
199 | break; |
200 | case VCNL4040_PROD_ID: | |
2ca5a879 TN |
201 | /* Default wait time is 80ms, add 20% tolerance. */ |
202 | data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); | |
203 | /* Default wait time is 5ms, add 20% tolerance. */ | |
204 | data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); | |
bc80573e | 205 | data->al_scale = 120000; |
5a441aad AAP |
206 | break; |
207 | } | |
be38866f TN |
208 | mutex_init(&data->vcnl4200_al.lock); |
209 | mutex_init(&data->vcnl4200_ps.lock); | |
1ebc787a | 210 | |
5e00708d GG |
211 | ret = data->chip_spec->set_power_state(data, true); |
212 | if (ret < 0) | |
213 | return ret; | |
214 | ||
1ebc787a TN |
215 | return 0; |
216 | }; | |
217 | ||
62a1efb9 PM |
218 | static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, |
219 | u8 rdy_mask, u8 data_reg, int *val) | |
220 | { | |
221 | int tries = 20; | |
91f197e0 | 222 | __be16 buf; |
62a1efb9 PM |
223 | int ret; |
224 | ||
be38866f | 225 | mutex_lock(&data->vcnl4000_lock); |
ff34ed6d | 226 | |
62a1efb9 PM |
227 | ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, |
228 | req_mask); | |
229 | if (ret < 0) | |
ff34ed6d | 230 | goto fail; |
62a1efb9 PM |
231 | |
232 | /* wait for data to become ready */ | |
233 | while (tries--) { | |
234 | ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); | |
235 | if (ret < 0) | |
ff34ed6d | 236 | goto fail; |
62a1efb9 PM |
237 | if (ret & rdy_mask) |
238 | break; | |
239 | msleep(20); /* measurement takes up to 100 ms */ | |
240 | } | |
241 | ||
242 | if (tries < 0) { | |
243 | dev_err(&data->client->dev, | |
244 | "vcnl4000_measure() failed, data not ready\n"); | |
ff34ed6d PMS |
245 | ret = -EIO; |
246 | goto fail; | |
62a1efb9 PM |
247 | } |
248 | ||
249 | ret = i2c_smbus_read_i2c_block_data(data->client, | |
250 | data_reg, sizeof(buf), (u8 *) &buf); | |
251 | if (ret < 0) | |
ff34ed6d | 252 | goto fail; |
62a1efb9 | 253 | |
be38866f | 254 | mutex_unlock(&data->vcnl4000_lock); |
62a1efb9 PM |
255 | *val = be16_to_cpu(buf); |
256 | ||
257 | return 0; | |
ff34ed6d PMS |
258 | |
259 | fail: | |
be38866f | 260 | mutex_unlock(&data->vcnl4000_lock); |
ff34ed6d | 261 | return ret; |
62a1efb9 PM |
262 | } |
263 | ||
be38866f TN |
264 | static int vcnl4200_measure(struct vcnl4000_data *data, |
265 | struct vcnl4200_channel *chan, int *val) | |
266 | { | |
267 | int ret; | |
268 | s64 delta; | |
269 | ktime_t next_measurement; | |
270 | ||
271 | mutex_lock(&chan->lock); | |
272 | ||
273 | next_measurement = ktime_add(chan->last_measurement, | |
274 | chan->sampling_rate); | |
275 | delta = ktime_us_delta(next_measurement, ktime_get()); | |
276 | if (delta > 0) | |
277 | usleep_range(delta, delta + 500); | |
278 | chan->last_measurement = ktime_get(); | |
279 | ||
280 | mutex_unlock(&chan->lock); | |
281 | ||
282 | ret = i2c_smbus_read_word_data(data->client, chan->reg); | |
283 | if (ret < 0) | |
284 | return ret; | |
285 | ||
286 | *val = ret; | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
1ebc787a TN |
291 | static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) |
292 | { | |
293 | return vcnl4000_measure(data, | |
294 | VCNL4000_AL_OD, VCNL4000_AL_RDY, | |
295 | VCNL4000_AL_RESULT_HI, val); | |
296 | } | |
297 | ||
be38866f TN |
298 | static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) |
299 | { | |
300 | return vcnl4200_measure(data, &data->vcnl4200_al, val); | |
301 | } | |
302 | ||
1ebc787a TN |
303 | static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) |
304 | { | |
305 | return vcnl4000_measure(data, | |
306 | VCNL4000_PS_OD, VCNL4000_PS_RDY, | |
307 | VCNL4000_PS_RESULT_HI, val); | |
308 | } | |
309 | ||
be38866f TN |
310 | static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) |
311 | { | |
312 | return vcnl4200_measure(data, &data->vcnl4200_ps, val); | |
313 | } | |
314 | ||
1ebc787a TN |
315 | static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { |
316 | [VCNL4000] = { | |
317 | .prod = "VCNL4000", | |
318 | .init = vcnl4000_init, | |
319 | .measure_light = vcnl4000_measure_light, | |
320 | .measure_proximity = vcnl4000_measure_proximity, | |
5e00708d | 321 | .set_power_state = vcnl4000_set_power_state, |
1ebc787a | 322 | }, |
50c50b97 TN |
323 | [VCNL4010] = { |
324 | .prod = "VCNL4010/4020", | |
325 | .init = vcnl4000_init, | |
326 | .measure_light = vcnl4000_measure_light, | |
327 | .measure_proximity = vcnl4000_measure_proximity, | |
5e00708d | 328 | .set_power_state = vcnl4000_set_power_state, |
50c50b97 | 329 | }, |
5a441aad AAP |
330 | [VCNL4040] = { |
331 | .prod = "VCNL4040", | |
332 | .init = vcnl4200_init, | |
333 | .measure_light = vcnl4200_measure_light, | |
334 | .measure_proximity = vcnl4200_measure_proximity, | |
5e00708d | 335 | .set_power_state = vcnl4200_set_power_state, |
5a441aad | 336 | }, |
be38866f TN |
337 | [VCNL4200] = { |
338 | .prod = "VCNL4200", | |
339 | .init = vcnl4200_init, | |
340 | .measure_light = vcnl4200_measure_light, | |
341 | .measure_proximity = vcnl4200_measure_proximity, | |
5e00708d | 342 | .set_power_state = vcnl4200_set_power_state, |
be38866f | 343 | }, |
1ebc787a TN |
344 | }; |
345 | ||
62a1efb9 PM |
346 | static const struct iio_chan_spec vcnl4000_channels[] = { |
347 | { | |
348 | .type = IIO_LIGHT, | |
bb7c5940 JC |
349 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
350 | BIT(IIO_CHAN_INFO_SCALE), | |
62a1efb9 PM |
351 | }, { |
352 | .type = IIO_PROXIMITY, | |
bb7c5940 | 353 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
62a1efb9 PM |
354 | } |
355 | }; | |
356 | ||
5e00708d GG |
357 | static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) |
358 | { | |
359 | struct device *dev = &data->client->dev; | |
360 | int ret; | |
361 | ||
362 | if (on) { | |
363 | ret = pm_runtime_get_sync(dev); | |
364 | if (ret < 0) | |
365 | pm_runtime_put_noidle(dev); | |
366 | } else { | |
367 | pm_runtime_mark_last_busy(dev); | |
368 | ret = pm_runtime_put_autosuspend(dev); | |
369 | } | |
370 | ||
371 | return ret; | |
372 | } | |
373 | ||
62a1efb9 PM |
374 | static int vcnl4000_read_raw(struct iio_dev *indio_dev, |
375 | struct iio_chan_spec const *chan, | |
376 | int *val, int *val2, long mask) | |
377 | { | |
5d693139 | 378 | int ret; |
62a1efb9 PM |
379 | struct vcnl4000_data *data = iio_priv(indio_dev); |
380 | ||
381 | switch (mask) { | |
382 | case IIO_CHAN_INFO_RAW: | |
5e00708d GG |
383 | ret = vcnl4000_set_pm_runtime_state(data, true); |
384 | if (ret < 0) | |
385 | return ret; | |
386 | ||
62a1efb9 PM |
387 | switch (chan->type) { |
388 | case IIO_LIGHT: | |
1ebc787a | 389 | ret = data->chip_spec->measure_light(data, val); |
4a818643 GG |
390 | if (!ret) |
391 | ret = IIO_VAL_INT; | |
392 | break; | |
62a1efb9 | 393 | case IIO_PROXIMITY: |
1ebc787a | 394 | ret = data->chip_spec->measure_proximity(data, val); |
4a818643 GG |
395 | if (!ret) |
396 | ret = IIO_VAL_INT; | |
397 | break; | |
62a1efb9 | 398 | default: |
4a818643 | 399 | ret = -EINVAL; |
62a1efb9 | 400 | } |
5e00708d | 401 | vcnl4000_set_pm_runtime_state(data, false); |
4a818643 | 402 | return ret; |
62a1efb9 | 403 | case IIO_CHAN_INFO_SCALE: |
5d693139 PMS |
404 | if (chan->type != IIO_LIGHT) |
405 | return -EINVAL; | |
406 | ||
407 | *val = 0; | |
1ebc787a | 408 | *val2 = data->al_scale; |
5d693139 | 409 | return IIO_VAL_INT_PLUS_MICRO; |
62a1efb9 | 410 | default: |
5d693139 | 411 | return -EINVAL; |
62a1efb9 | 412 | } |
62a1efb9 PM |
413 | } |
414 | ||
415 | static const struct iio_info vcnl4000_info = { | |
416 | .read_raw = vcnl4000_read_raw, | |
62a1efb9 PM |
417 | }; |
418 | ||
fc52692c GKH |
419 | static int vcnl4000_probe(struct i2c_client *client, |
420 | const struct i2c_device_id *id) | |
62a1efb9 PM |
421 | { |
422 | struct vcnl4000_data *data; | |
423 | struct iio_dev *indio_dev; | |
1ebc787a | 424 | int ret; |
62a1efb9 | 425 | |
2669d723 | 426 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); |
62a1efb9 PM |
427 | if (!indio_dev) |
428 | return -ENOMEM; | |
429 | ||
430 | data = iio_priv(indio_dev); | |
431 | i2c_set_clientdata(client, indio_dev); | |
432 | data->client = client; | |
1ebc787a TN |
433 | data->id = id->driver_data; |
434 | data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; | |
62a1efb9 | 435 | |
1ebc787a | 436 | ret = data->chip_spec->init(data); |
62a1efb9 | 437 | if (ret < 0) |
2669d723 | 438 | return ret; |
62a1efb9 | 439 | |
d978bfdd | 440 | dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", |
1ebc787a | 441 | data->chip_spec->prod, data->rev); |
62a1efb9 PM |
442 | |
443 | indio_dev->dev.parent = &client->dev; | |
444 | indio_dev->info = &vcnl4000_info; | |
445 | indio_dev->channels = vcnl4000_channels; | |
446 | indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); | |
447 | indio_dev->name = VCNL4000_DRV_NAME; | |
448 | indio_dev->modes = INDIO_DIRECT_MODE; | |
449 | ||
5e00708d GG |
450 | ret = pm_runtime_set_active(&client->dev); |
451 | if (ret < 0) | |
452 | goto fail_poweroff; | |
453 | ||
454 | ret = iio_device_register(indio_dev); | |
455 | if (ret < 0) | |
456 | goto fail_poweroff; | |
457 | ||
458 | pm_runtime_enable(&client->dev); | |
459 | pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); | |
460 | pm_runtime_use_autosuspend(&client->dev); | |
461 | ||
462 | return 0; | |
463 | fail_poweroff: | |
464 | data->chip_spec->set_power_state(data, false); | |
465 | return ret; | |
62a1efb9 PM |
466 | } |
467 | ||
ebd457d5 AAP |
468 | static const struct of_device_id vcnl_4000_of_match[] = { |
469 | { | |
470 | .compatible = "vishay,vcnl4000", | |
1436a78c | 471 | .data = (void *)VCNL4000, |
ebd457d5 AAP |
472 | }, |
473 | { | |
474 | .compatible = "vishay,vcnl4010", | |
1436a78c | 475 | .data = (void *)VCNL4010, |
ebd457d5 AAP |
476 | }, |
477 | { | |
1436a78c MF |
478 | .compatible = "vishay,vcnl4020", |
479 | .data = (void *)VCNL4010, | |
ebd457d5 | 480 | }, |
7fd1c260 MF |
481 | { |
482 | .compatible = "vishay,vcnl4040", | |
483 | .data = (void *)VCNL4040, | |
484 | }, | |
ebd457d5 AAP |
485 | { |
486 | .compatible = "vishay,vcnl4200", | |
1436a78c | 487 | .data = (void *)VCNL4200, |
ebd457d5 AAP |
488 | }, |
489 | {}, | |
490 | }; | |
491 | MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); | |
492 | ||
5e00708d GG |
493 | static int vcnl4000_remove(struct i2c_client *client) |
494 | { | |
495 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
496 | struct vcnl4000_data *data = iio_priv(indio_dev); | |
497 | ||
498 | pm_runtime_dont_use_autosuspend(&client->dev); | |
499 | pm_runtime_disable(&client->dev); | |
500 | iio_device_unregister(indio_dev); | |
501 | pm_runtime_set_suspended(&client->dev); | |
502 | ||
503 | return data->chip_spec->set_power_state(data, false); | |
504 | } | |
505 | ||
506 | static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) | |
507 | { | |
508 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | |
509 | struct vcnl4000_data *data = iio_priv(indio_dev); | |
510 | ||
511 | return data->chip_spec->set_power_state(data, false); | |
512 | } | |
513 | ||
514 | static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) | |
515 | { | |
516 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | |
517 | struct vcnl4000_data *data = iio_priv(indio_dev); | |
518 | ||
519 | return data->chip_spec->set_power_state(data, true); | |
520 | } | |
521 | ||
522 | static const struct dev_pm_ops vcnl4000_pm_ops = { | |
523 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
524 | pm_runtime_force_resume) | |
525 | SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, | |
526 | vcnl4000_runtime_resume, NULL) | |
527 | }; | |
528 | ||
62a1efb9 PM |
529 | static struct i2c_driver vcnl4000_driver = { |
530 | .driver = { | |
531 | .name = VCNL4000_DRV_NAME, | |
5e00708d | 532 | .pm = &vcnl4000_pm_ops, |
ebd457d5 | 533 | .of_match_table = vcnl_4000_of_match, |
62a1efb9 PM |
534 | }, |
535 | .probe = vcnl4000_probe, | |
62a1efb9 | 536 | .id_table = vcnl4000_id, |
5e00708d | 537 | .remove = vcnl4000_remove, |
62a1efb9 PM |
538 | }; |
539 | ||
540 | module_i2c_driver(vcnl4000_driver); | |
541 | ||
542 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | |
543 | MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); | |
544 | MODULE_LICENSE("GPL"); |