]>
Commit | Line | Data |
---|---|---|
6d59ba2f LD |
1 | /* |
2 | * IIO driver for the light sensor ISL29028. | |
3 | * ISL29028 is Concurrent Ambient Light and Proximity Sensor | |
4 | * | |
5 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
bcbbd0fa | 6 | * Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org> |
6d59ba2f LD |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms and conditions of the GNU General Public License, | |
10 | * version 2, as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
e530cc84 SR |
19 | * |
20 | * Datasheets: | |
21 | * - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29028.pdf | |
22 | * - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29030.pdf | |
6d59ba2f LD |
23 | */ |
24 | ||
25 | #include <linux/module.h> | |
26 | #include <linux/i2c.h> | |
27 | #include <linux/err.h> | |
28 | #include <linux/mutex.h> | |
29 | #include <linux/delay.h> | |
30 | #include <linux/slab.h> | |
31 | #include <linux/regmap.h> | |
06458e27 JC |
32 | #include <linux/iio/iio.h> |
33 | #include <linux/iio/sysfs.h> | |
2db5054a | 34 | #include <linux/pm_runtime.h> |
6d59ba2f | 35 | |
29e90cd7 | 36 | #define ISL29028_CONV_TIME_MS 100 |
6d59ba2f | 37 | |
29e90cd7 | 38 | #define ISL29028_REG_CONFIGURE 0x01 |
6d59ba2f | 39 | |
29e90cd7 BM |
40 | #define ISL29028_CONF_ALS_IR_MODE_ALS 0 |
41 | #define ISL29028_CONF_ALS_IR_MODE_IR BIT(0) | |
42 | #define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0) | |
6d59ba2f | 43 | |
29e90cd7 | 44 | #define ISL29028_CONF_ALS_RANGE_LOW_LUX 0 |
3a37f1c2 | 45 | #define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1) |
29e90cd7 | 46 | #define ISL29028_CONF_ALS_RANGE_MASK BIT(1) |
6d59ba2f | 47 | |
29e90cd7 BM |
48 | #define ISL29028_CONF_ALS_DIS 0 |
49 | #define ISL29028_CONF_ALS_EN BIT(2) | |
50 | #define ISL29028_CONF_ALS_EN_MASK BIT(2) | |
6d59ba2f | 51 | |
29e90cd7 BM |
52 | #define ISL29028_CONF_PROX_SLP_SH 4 |
53 | #define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH) | |
6d59ba2f | 54 | |
29e90cd7 BM |
55 | #define ISL29028_CONF_PROX_EN BIT(7) |
56 | #define ISL29028_CONF_PROX_EN_MASK BIT(7) | |
6d59ba2f | 57 | |
29e90cd7 | 58 | #define ISL29028_REG_INTERRUPT 0x02 |
6d59ba2f | 59 | |
29e90cd7 BM |
60 | #define ISL29028_REG_PROX_DATA 0x08 |
61 | #define ISL29028_REG_ALSIR_L 0x09 | |
62 | #define ISL29028_REG_ALSIR_U 0x0A | |
6d59ba2f | 63 | |
29e90cd7 BM |
64 | #define ISL29028_REG_TEST1_MODE 0x0E |
65 | #define ISL29028_REG_TEST2_MODE 0x0F | |
6d59ba2f | 66 | |
29e90cd7 | 67 | #define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1) |
6d59ba2f | 68 | |
2db5054a BM |
69 | #define ISL29028_POWER_OFF_DELAY_MS 2000 |
70 | ||
e4ff6c1b BM |
71 | struct isl29028_prox_data { |
72 | int sampling_int; | |
73 | int sampling_fract; | |
74 | int sleep_time; | |
75 | }; | |
76 | ||
77 | static const struct isl29028_prox_data isl29028_prox_data[] = { | |
78 | { 1, 250000, 800 }, | |
79 | { 2, 500000, 400 }, | |
80 | { 5, 0, 200 }, | |
81 | { 10, 0, 100 }, | |
82 | { 13, 300000, 75 }, | |
83 | { 20, 0, 50 }, | |
84 | { 80, 0, 13 }, /* | |
85 | * Note: Data sheet lists 12.5 ms sleep time. | |
86 | * Round up a half millisecond for msleep(). | |
87 | */ | |
88 | { 100, 0, 0 } | |
89 | }; | |
45e2852b | 90 | |
bafaa6de PMS |
91 | enum isl29028_als_ir_mode { |
92 | ISL29028_MODE_NONE = 0, | |
93 | ISL29028_MODE_ALS, | |
94 | ISL29028_MODE_IR, | |
6d59ba2f LD |
95 | }; |
96 | ||
97 | struct isl29028_chip { | |
6db2fa0a BM |
98 | struct mutex lock; |
99 | struct regmap *regmap; | |
e4ff6c1b BM |
100 | int prox_sampling_int; |
101 | int prox_sampling_frac; | |
6db2fa0a | 102 | bool enable_prox; |
6db2fa0a | 103 | int lux_scale; |
bafaa6de | 104 | enum isl29028_als_ir_mode als_ir_mode; |
6d59ba2f LD |
105 | }; |
106 | ||
e4ff6c1b | 107 | static int isl29028_find_prox_sleep_index(int sampling_int, int sampling_fract) |
6d59ba2f | 108 | { |
45e2852b | 109 | int i; |
6d59ba2f | 110 | |
e4ff6c1b BM |
111 | for (i = 0; i < ARRAY_SIZE(isl29028_prox_data); ++i) { |
112 | if (isl29028_prox_data[i].sampling_int == sampling_int && | |
113 | isl29028_prox_data[i].sampling_fract == sampling_fract) | |
114 | return i; | |
6d59ba2f | 115 | } |
b1dda969 | 116 | |
e4ff6c1b | 117 | return -EINVAL; |
45e2852b BM |
118 | } |
119 | ||
120 | static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, | |
e4ff6c1b | 121 | int sampling_int, int sampling_fract) |
45e2852b BM |
122 | { |
123 | struct device *dev = regmap_get_device(chip->regmap); | |
124 | int sleep_index, ret; | |
125 | ||
e4ff6c1b BM |
126 | sleep_index = isl29028_find_prox_sleep_index(sampling_int, |
127 | sampling_fract); | |
128 | if (sleep_index < 0) | |
129 | return sleep_index; | |
130 | ||
ae1e5238 BM |
131 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
132 | ISL29028_CONF_PROX_SLP_MASK, | |
45e2852b | 133 | sleep_index << ISL29028_CONF_PROX_SLP_SH); |
ae1e5238 BM |
134 | |
135 | if (ret < 0) { | |
136 | dev_err(dev, "%s(): Error %d setting the proximity sampling\n", | |
137 | __func__, ret); | |
138 | return ret; | |
139 | } | |
140 | ||
e4ff6c1b BM |
141 | chip->prox_sampling_int = sampling_int; |
142 | chip->prox_sampling_frac = sampling_fract; | |
ae1e5238 BM |
143 | |
144 | return ret; | |
6d59ba2f LD |
145 | } |
146 | ||
67663424 | 147 | static int isl29028_enable_proximity(struct isl29028_chip *chip) |
6d59ba2f | 148 | { |
e4ff6c1b | 149 | int prox_index, ret; |
6d59ba2f | 150 | |
e4ff6c1b BM |
151 | ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling_int, |
152 | chip->prox_sampling_frac); | |
84a76694 BM |
153 | if (ret < 0) |
154 | return ret; | |
155 | ||
6d59ba2f | 156 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
67663424 BM |
157 | ISL29028_CONF_PROX_EN_MASK, |
158 | ISL29028_CONF_PROX_EN); | |
6d59ba2f LD |
159 | if (ret < 0) |
160 | return ret; | |
161 | ||
162 | /* Wait for conversion to be complete for first sample */ | |
e4ff6c1b BM |
163 | prox_index = isl29028_find_prox_sleep_index(chip->prox_sampling_int, |
164 | chip->prox_sampling_frac); | |
165 | if (prox_index < 0) | |
166 | return prox_index; | |
167 | ||
168 | msleep(isl29028_prox_data[prox_index].sleep_time); | |
b1dda969 | 169 | |
6d59ba2f LD |
170 | return 0; |
171 | } | |
172 | ||
173 | static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) | |
174 | { | |
bcf5104e | 175 | struct device *dev = regmap_get_device(chip->regmap); |
3a37f1c2 PMS |
176 | int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX : |
177 | ISL29028_CONF_ALS_RANGE_LOW_LUX; | |
bcf5104e BM |
178 | int ret; |
179 | ||
180 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
181 | ISL29028_CONF_ALS_RANGE_MASK, val); | |
182 | if (ret < 0) { | |
183 | dev_err(dev, "%s(): Error %d setting the ALS scale\n", __func__, | |
184 | ret); | |
185 | return ret; | |
186 | } | |
187 | ||
188 | chip->lux_scale = lux_scale; | |
6d59ba2f | 189 | |
bcf5104e | 190 | return ret; |
6d59ba2f LD |
191 | } |
192 | ||
193 | static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, | |
bafaa6de | 194 | enum isl29028_als_ir_mode mode) |
6d59ba2f | 195 | { |
0fac96ed | 196 | int ret; |
6d59ba2f | 197 | |
f40531d1 BM |
198 | if (chip->als_ir_mode == mode) |
199 | return 0; | |
200 | ||
0fac96ed BM |
201 | ret = isl29028_set_als_scale(chip, chip->lux_scale); |
202 | if (ret < 0) | |
203 | return ret; | |
204 | ||
6d59ba2f | 205 | switch (mode) { |
bafaa6de | 206 | case ISL29028_MODE_ALS: |
6d59ba2f | 207 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 PMS |
208 | ISL29028_CONF_ALS_IR_MODE_MASK, |
209 | ISL29028_CONF_ALS_IR_MODE_ALS); | |
6d59ba2f LD |
210 | if (ret < 0) |
211 | return ret; | |
212 | ||
213 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 PMS |
214 | ISL29028_CONF_ALS_RANGE_MASK, |
215 | ISL29028_CONF_ALS_RANGE_HIGH_LUX); | |
6d59ba2f | 216 | break; |
bafaa6de | 217 | case ISL29028_MODE_IR: |
6d59ba2f | 218 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 PMS |
219 | ISL29028_CONF_ALS_IR_MODE_MASK, |
220 | ISL29028_CONF_ALS_IR_MODE_IR); | |
6d59ba2f | 221 | break; |
bafaa6de | 222 | case ISL29028_MODE_NONE: |
6d59ba2f | 223 | return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
c4f60925 BM |
224 | ISL29028_CONF_ALS_EN_MASK, |
225 | ISL29028_CONF_ALS_DIS); | |
6d59ba2f LD |
226 | } |
227 | ||
228 | if (ret < 0) | |
229 | return ret; | |
230 | ||
231 | /* Enable the ALS/IR */ | |
232 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 PMS |
233 | ISL29028_CONF_ALS_EN_MASK, |
234 | ISL29028_CONF_ALS_EN); | |
6d59ba2f LD |
235 | if (ret < 0) |
236 | return ret; | |
237 | ||
238 | /* Need to wait for conversion time if ALS/IR mode enabled */ | |
55bf851b | 239 | msleep(ISL29028_CONV_TIME_MS); |
f40531d1 BM |
240 | |
241 | chip->als_ir_mode = mode; | |
242 | ||
6d59ba2f LD |
243 | return 0; |
244 | } | |
245 | ||
246 | static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) | |
247 | { | |
722fc316 | 248 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
249 | unsigned int lsb; |
250 | unsigned int msb; | |
251 | int ret; | |
252 | ||
253 | ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb); | |
254 | if (ret < 0) { | |
722fc316 | 255 | dev_err(dev, |
578e02a7 BM |
256 | "%s(): Error %d reading register ALSIR_L\n", |
257 | __func__, ret); | |
6d59ba2f LD |
258 | return ret; |
259 | } | |
260 | ||
261 | ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb); | |
262 | if (ret < 0) { | |
722fc316 | 263 | dev_err(dev, |
578e02a7 BM |
264 | "%s(): Error %d reading register ALSIR_U\n", |
265 | __func__, ret); | |
6d59ba2f LD |
266 | return ret; |
267 | } | |
268 | ||
269 | *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF); | |
b1dda969 | 270 | |
6d59ba2f LD |
271 | return 0; |
272 | } | |
273 | ||
274 | static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) | |
275 | { | |
722fc316 | 276 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
277 | unsigned int data; |
278 | int ret; | |
279 | ||
b44b1f31 | 280 | if (!chip->enable_prox) { |
67663424 | 281 | ret = isl29028_enable_proximity(chip); |
b44b1f31 BM |
282 | if (ret < 0) |
283 | return ret; | |
b1dda969 | 284 | |
b44b1f31 BM |
285 | chip->enable_prox = true; |
286 | } | |
287 | ||
6d59ba2f LD |
288 | ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); |
289 | if (ret < 0) { | |
578e02a7 BM |
290 | dev_err(dev, "%s(): Error %d reading register PROX_DATA\n", |
291 | __func__, ret); | |
6d59ba2f LD |
292 | return ret; |
293 | } | |
b1dda969 | 294 | |
6d59ba2f | 295 | *prox = data; |
b1dda969 | 296 | |
6d59ba2f LD |
297 | return 0; |
298 | } | |
299 | ||
6d59ba2f LD |
300 | static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) |
301 | { | |
722fc316 | 302 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
303 | int ret; |
304 | int als_ir_data; | |
305 | ||
f40531d1 BM |
306 | ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS); |
307 | if (ret < 0) { | |
578e02a7 BM |
308 | dev_err(dev, "%s(): Error %d enabling ALS mode\n", __func__, |
309 | ret); | |
f40531d1 | 310 | return ret; |
6d59ba2f LD |
311 | } |
312 | ||
313 | ret = isl29028_read_als_ir(chip, &als_ir_data); | |
314 | if (ret < 0) | |
315 | return ret; | |
316 | ||
317 | /* | |
318 | * convert als data count to lux. | |
319 | * if lux_scale = 125, lux = count * 0.031 | |
320 | * if lux_scale = 2000, lux = count * 0.49 | |
321 | */ | |
322 | if (chip->lux_scale == 125) | |
323 | als_ir_data = (als_ir_data * 31) / 1000; | |
324 | else | |
325 | als_ir_data = (als_ir_data * 49) / 100; | |
326 | ||
327 | *als_data = als_ir_data; | |
b1dda969 | 328 | |
6d59ba2f LD |
329 | return 0; |
330 | } | |
331 | ||
332 | static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) | |
333 | { | |
722fc316 | 334 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
335 | int ret; |
336 | ||
f40531d1 BM |
337 | ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR); |
338 | if (ret < 0) { | |
578e02a7 BM |
339 | dev_err(dev, "%s(): Error %d enabling IR mode\n", __func__, |
340 | ret); | |
f40531d1 | 341 | return ret; |
6d59ba2f | 342 | } |
b1dda969 | 343 | |
6d59ba2f LD |
344 | return isl29028_read_als_ir(chip, ir_data); |
345 | } | |
346 | ||
2db5054a BM |
347 | static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) |
348 | { | |
349 | struct device *dev = regmap_get_device(chip->regmap); | |
350 | int ret; | |
351 | ||
352 | if (on) { | |
353 | ret = pm_runtime_get_sync(dev); | |
354 | if (ret < 0) | |
355 | pm_runtime_put_noidle(dev); | |
356 | } else { | |
357 | pm_runtime_mark_last_busy(dev); | |
358 | ret = pm_runtime_put_autosuspend(dev); | |
359 | } | |
360 | ||
361 | return ret; | |
362 | } | |
363 | ||
6d59ba2f LD |
364 | /* Channel IO */ |
365 | static int isl29028_write_raw(struct iio_dev *indio_dev, | |
ca52c88c ERR |
366 | struct iio_chan_spec const *chan, |
367 | int val, int val2, long mask) | |
6d59ba2f LD |
368 | { |
369 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
722fc316 | 370 | struct device *dev = regmap_get_device(chip->regmap); |
2db5054a BM |
371 | int ret; |
372 | ||
373 | ret = isl29028_set_pm_runtime_busy(chip, true); | |
374 | if (ret < 0) | |
375 | return ret; | |
6d59ba2f LD |
376 | |
377 | mutex_lock(&chip->lock); | |
2db5054a BM |
378 | |
379 | ret = -EINVAL; | |
6d59ba2f LD |
380 | switch (chan->type) { |
381 | case IIO_PROXIMITY: | |
892cb6dc | 382 | if (mask != IIO_CHAN_INFO_SAMP_FREQ) { |
722fc316 | 383 | dev_err(dev, |
578e02a7 BM |
384 | "%s(): proximity: Mask value 0x%08lx is not supported\n", |
385 | __func__, mask); | |
6d59ba2f LD |
386 | break; |
387 | } | |
b1dda969 | 388 | |
6d59ba2f | 389 | if (val < 1 || val > 100) { |
722fc316 | 390 | dev_err(dev, |
578e02a7 BM |
391 | "%s(): proximity: Sampling frequency %d is not in the range [1:100]\n", |
392 | __func__, val); | |
6d59ba2f LD |
393 | break; |
394 | } | |
b1dda969 | 395 | |
e4ff6c1b | 396 | ret = isl29028_set_proxim_sampling(chip, val, val2); |
6d59ba2f | 397 | break; |
6d59ba2f | 398 | case IIO_LIGHT: |
892cb6dc | 399 | if (mask != IIO_CHAN_INFO_SCALE) { |
722fc316 | 400 | dev_err(dev, |
578e02a7 BM |
401 | "%s(): light: Mask value 0x%08lx is not supported\n", |
402 | __func__, mask); | |
6d59ba2f LD |
403 | break; |
404 | } | |
b1dda969 | 405 | |
71a0a643 | 406 | if (val != 125 && val != 2000) { |
722fc316 | 407 | dev_err(dev, |
578e02a7 BM |
408 | "%s(): light: Lux scale %d is not in the set {125, 2000}\n", |
409 | __func__, val); | |
6d59ba2f LD |
410 | break; |
411 | } | |
b1dda969 | 412 | |
6d59ba2f | 413 | ret = isl29028_set_als_scale(chip, val); |
6d59ba2f | 414 | break; |
6d59ba2f | 415 | default: |
578e02a7 BM |
416 | dev_err(dev, "%s(): Unsupported channel type %x\n", |
417 | __func__, chan->type); | |
6d59ba2f LD |
418 | break; |
419 | } | |
b1dda969 | 420 | |
6d59ba2f | 421 | mutex_unlock(&chip->lock); |
b1dda969 | 422 | |
2db5054a BM |
423 | if (ret < 0) |
424 | return ret; | |
425 | ||
426 | ret = isl29028_set_pm_runtime_busy(chip, false); | |
427 | if (ret < 0) | |
428 | return ret; | |
429 | ||
6d59ba2f LD |
430 | return ret; |
431 | } | |
432 | ||
433 | static int isl29028_read_raw(struct iio_dev *indio_dev, | |
ca52c88c ERR |
434 | struct iio_chan_spec const *chan, |
435 | int *val, int *val2, long mask) | |
6d59ba2f LD |
436 | { |
437 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
722fc316 | 438 | struct device *dev = regmap_get_device(chip->regmap); |
2db5054a BM |
439 | int ret, pm_ret; |
440 | ||
441 | ret = isl29028_set_pm_runtime_busy(chip, true); | |
442 | if (ret < 0) | |
443 | return ret; | |
6d59ba2f LD |
444 | |
445 | mutex_lock(&chip->lock); | |
2db5054a BM |
446 | |
447 | ret = -EINVAL; | |
6d59ba2f | 448 | switch (mask) { |
a8c0ed75 JC |
449 | case IIO_CHAN_INFO_RAW: |
450 | case IIO_CHAN_INFO_PROCESSED: | |
6d59ba2f LD |
451 | switch (chan->type) { |
452 | case IIO_LIGHT: | |
453 | ret = isl29028_als_get(chip, val); | |
454 | break; | |
455 | case IIO_INTENSITY: | |
456 | ret = isl29028_ir_get(chip, val); | |
457 | break; | |
458 | case IIO_PROXIMITY: | |
b44b1f31 | 459 | ret = isl29028_read_proxim(chip, val); |
6d59ba2f LD |
460 | break; |
461 | default: | |
462 | break; | |
463 | } | |
b1dda969 | 464 | |
6d59ba2f LD |
465 | if (ret < 0) |
466 | break; | |
b1dda969 | 467 | |
6d59ba2f LD |
468 | ret = IIO_VAL_INT; |
469 | break; | |
892cb6dc | 470 | case IIO_CHAN_INFO_SAMP_FREQ: |
6d59ba2f LD |
471 | if (chan->type != IIO_PROXIMITY) |
472 | break; | |
b1dda969 | 473 | |
e4ff6c1b BM |
474 | *val = chip->prox_sampling_int; |
475 | *val2 = chip->prox_sampling_frac; | |
6d59ba2f LD |
476 | ret = IIO_VAL_INT; |
477 | break; | |
892cb6dc | 478 | case IIO_CHAN_INFO_SCALE: |
6d59ba2f LD |
479 | if (chan->type != IIO_LIGHT) |
480 | break; | |
481 | *val = chip->lux_scale; | |
482 | ret = IIO_VAL_INT; | |
483 | break; | |
6d59ba2f | 484 | default: |
578e02a7 BM |
485 | dev_err(dev, "%s(): mask value 0x%08lx is not supported\n", |
486 | __func__, mask); | |
6d59ba2f LD |
487 | break; |
488 | } | |
b1dda969 | 489 | |
6d59ba2f | 490 | mutex_unlock(&chip->lock); |
b1dda969 | 491 | |
2db5054a BM |
492 | if (ret < 0) |
493 | return ret; | |
494 | ||
495 | /** | |
496 | * Preserve the ret variable if the call to | |
497 | * isl29028_set_pm_runtime_busy() is successful so the reading | |
498 | * (if applicable) is returned to user space. | |
499 | */ | |
500 | pm_ret = isl29028_set_pm_runtime_busy(chip, false); | |
501 | if (pm_ret < 0) | |
502 | return pm_ret; | |
503 | ||
6d59ba2f LD |
504 | return ret; |
505 | } | |
506 | ||
507 | static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, | |
e4ff6c1b | 508 | "1.25 2.5 5 10 13.3 20 80 100"); |
b9b689c1 | 509 | static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000"); |
6d59ba2f | 510 | |
6d59ba2f LD |
511 | #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) |
512 | static struct attribute *isl29028_attributes[] = { | |
513 | ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available), | |
514 | ISL29028_CONST_ATTR(in_illuminance_scale_available), | |
515 | NULL, | |
516 | }; | |
517 | ||
518 | static const struct attribute_group isl29108_group = { | |
519 | .attrs = isl29028_attributes, | |
520 | }; | |
521 | ||
522 | static const struct iio_chan_spec isl29028_channels[] = { | |
523 | { | |
524 | .type = IIO_LIGHT, | |
93a9fdff JC |
525 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | |
526 | BIT(IIO_CHAN_INFO_SCALE), | |
6d59ba2f LD |
527 | }, { |
528 | .type = IIO_INTENSITY, | |
93a9fdff | 529 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
6d59ba2f LD |
530 | }, { |
531 | .type = IIO_PROXIMITY, | |
93a9fdff JC |
532 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
533 | BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
6d59ba2f LD |
534 | } |
535 | }; | |
536 | ||
537 | static const struct iio_info isl29028_info = { | |
538 | .attrs = &isl29108_group, | |
df271034 BG |
539 | .read_raw = isl29028_read_raw, |
540 | .write_raw = isl29028_write_raw, | |
6d59ba2f LD |
541 | }; |
542 | ||
2db5054a | 543 | static int isl29028_clear_configure_reg(struct isl29028_chip *chip) |
6d59ba2f | 544 | { |
722fc316 | 545 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
546 | int ret; |
547 | ||
6d59ba2f | 548 | ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); |
2db5054a | 549 | if (ret < 0) |
578e02a7 BM |
550 | dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n", |
551 | __func__, ret); | |
2db5054a BM |
552 | |
553 | chip->als_ir_mode = ISL29028_MODE_NONE; | |
554 | chip->enable_prox = false; | |
6d59ba2f | 555 | |
0fac96ed | 556 | return ret; |
6d59ba2f LD |
557 | } |
558 | ||
f6127704 | 559 | static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) |
6d59ba2f LD |
560 | { |
561 | switch (reg) { | |
562 | case ISL29028_REG_INTERRUPT: | |
563 | case ISL29028_REG_PROX_DATA: | |
564 | case ISL29028_REG_ALSIR_L: | |
565 | case ISL29028_REG_ALSIR_U: | |
566 | return true; | |
567 | default: | |
568 | return false; | |
569 | } | |
570 | } | |
571 | ||
572 | static const struct regmap_config isl29028_regmap_config = { | |
573 | .reg_bits = 8, | |
574 | .val_bits = 8, | |
f6127704 | 575 | .volatile_reg = isl29028_is_volatile_reg, |
6d59ba2f LD |
576 | .max_register = ISL29028_NUM_REGS - 1, |
577 | .num_reg_defaults_raw = ISL29028_NUM_REGS, | |
578 | .cache_type = REGCACHE_RBTREE, | |
579 | }; | |
580 | ||
4ae1c61f | 581 | static int isl29028_probe(struct i2c_client *client, |
ca52c88c | 582 | const struct i2c_device_id *id) |
6d59ba2f LD |
583 | { |
584 | struct isl29028_chip *chip; | |
585 | struct iio_dev *indio_dev; | |
586 | int ret; | |
587 | ||
80cab1da | 588 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); |
8c939402 | 589 | if (!indio_dev) |
6d59ba2f | 590 | return -ENOMEM; |
6d59ba2f LD |
591 | |
592 | chip = iio_priv(indio_dev); | |
593 | ||
594 | i2c_set_clientdata(client, indio_dev); | |
6d59ba2f LD |
595 | mutex_init(&chip->lock); |
596 | ||
597 | chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config); | |
598 | if (IS_ERR(chip->regmap)) { | |
599 | ret = PTR_ERR(chip->regmap); | |
578e02a7 BM |
600 | dev_err(&client->dev, "%s: Error %d initializing regmap\n", |
601 | __func__, ret); | |
80cab1da | 602 | return ret; |
6d59ba2f LD |
603 | } |
604 | ||
58f64366 | 605 | chip->enable_prox = false; |
e4ff6c1b BM |
606 | chip->prox_sampling_int = 20; |
607 | chip->prox_sampling_frac = 0; | |
58f64366 | 608 | chip->lux_scale = 2000; |
58f64366 BM |
609 | |
610 | ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); | |
611 | if (ret < 0) { | |
612 | dev_err(&client->dev, | |
578e02a7 BM |
613 | "%s(): Error %d writing to TEST1_MODE register\n", |
614 | __func__, ret); | |
58f64366 BM |
615 | return ret; |
616 | } | |
b1dda969 | 617 | |
58f64366 BM |
618 | ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); |
619 | if (ret < 0) { | |
620 | dev_err(&client->dev, | |
578e02a7 BM |
621 | "%s(): Error %d writing to TEST2_MODE register\n", |
622 | __func__, ret); | |
58f64366 BM |
623 | return ret; |
624 | } | |
625 | ||
2db5054a | 626 | ret = isl29028_clear_configure_reg(chip); |
65ac716b | 627 | if (ret < 0) |
80cab1da | 628 | return ret; |
6d59ba2f LD |
629 | |
630 | indio_dev->info = &isl29028_info; | |
631 | indio_dev->channels = isl29028_channels; | |
632 | indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); | |
633 | indio_dev->name = id->name; | |
634 | indio_dev->dev.parent = &client->dev; | |
635 | indio_dev->modes = INDIO_DIRECT_MODE; | |
b1dda969 | 636 | |
2db5054a BM |
637 | pm_runtime_enable(&client->dev); |
638 | pm_runtime_set_autosuspend_delay(&client->dev, | |
639 | ISL29028_POWER_OFF_DELAY_MS); | |
640 | pm_runtime_use_autosuspend(&client->dev); | |
641 | ||
86727e30 | 642 | ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); |
6d59ba2f | 643 | if (ret < 0) { |
722fc316 | 644 | dev_err(&client->dev, |
578e02a7 BM |
645 | "%s(): iio registration failed with error %d\n", |
646 | __func__, ret); | |
80cab1da | 647 | return ret; |
6d59ba2f | 648 | } |
b1dda969 | 649 | |
6d59ba2f | 650 | return 0; |
6d59ba2f LD |
651 | } |
652 | ||
2db5054a BM |
653 | static int isl29028_remove(struct i2c_client *client) |
654 | { | |
655 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
656 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
657 | ||
658 | iio_device_unregister(indio_dev); | |
659 | ||
660 | pm_runtime_disable(&client->dev); | |
661 | pm_runtime_set_suspended(&client->dev); | |
662 | pm_runtime_put_noidle(&client->dev); | |
663 | ||
664 | return isl29028_clear_configure_reg(chip); | |
665 | } | |
666 | ||
667 | static int __maybe_unused isl29028_suspend(struct device *dev) | |
668 | { | |
669 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | |
670 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
671 | int ret; | |
672 | ||
673 | mutex_lock(&chip->lock); | |
674 | ||
675 | ret = isl29028_clear_configure_reg(chip); | |
676 | ||
677 | mutex_unlock(&chip->lock); | |
678 | ||
679 | return ret; | |
680 | } | |
681 | ||
682 | static int __maybe_unused isl29028_resume(struct device *dev) | |
683 | { | |
684 | /** | |
685 | * The specific component (ALS/IR or proximity) will enable itself as | |
686 | * needed the next time that the user requests a reading. This is done | |
687 | * above in isl29028_set_als_ir_mode() and isl29028_enable_proximity(). | |
688 | */ | |
689 | return 0; | |
690 | } | |
691 | ||
692 | static const struct dev_pm_ops isl29028_pm_ops = { | |
0b59c388 BM |
693 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
694 | pm_runtime_force_resume) | |
2db5054a BM |
695 | SET_RUNTIME_PM_OPS(isl29028_suspend, isl29028_resume, NULL) |
696 | }; | |
697 | ||
6d59ba2f LD |
698 | static const struct i2c_device_id isl29028_id[] = { |
699 | {"isl29028", 0}, | |
e530cc84 | 700 | {"isl29030", 0}, |
6d59ba2f LD |
701 | {} |
702 | }; | |
703 | MODULE_DEVICE_TABLE(i2c, isl29028_id); | |
704 | ||
705 | static const struct of_device_id isl29028_of_match[] = { | |
37e5157d AE |
706 | { .compatible = "isl,isl29028", }, /* for backward compat., don't use */ |
707 | { .compatible = "isil,isl29028", }, | |
e530cc84 | 708 | { .compatible = "isil,isl29030", }, |
6d59ba2f LD |
709 | { }, |
710 | }; | |
711 | MODULE_DEVICE_TABLE(of, isl29028_of_match); | |
712 | ||
713 | static struct i2c_driver isl29028_driver = { | |
6d59ba2f LD |
714 | .driver = { |
715 | .name = "isl29028", | |
2db5054a | 716 | .pm = &isl29028_pm_ops, |
6d59ba2f LD |
717 | .of_match_table = isl29028_of_match, |
718 | }, | |
719 | .probe = isl29028_probe, | |
2db5054a | 720 | .remove = isl29028_remove, |
6d59ba2f LD |
721 | .id_table = isl29028_id, |
722 | }; | |
723 | ||
724 | module_i2c_driver(isl29028_driver); | |
725 | ||
726 | MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); | |
727 | MODULE_LICENSE("GPL v2"); | |
728 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); |