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. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms and conditions of the GNU General Public License, | |
9 | * version 2, as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/i2c.h> | |
22 | #include <linux/err.h> | |
23 | #include <linux/mutex.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/regmap.h> | |
06458e27 JC |
27 | #include <linux/iio/iio.h> |
28 | #include <linux/iio/sysfs.h> | |
6d59ba2f | 29 | |
3a37f1c2 | 30 | #define ISL29028_CONV_TIME_MS 100 |
6d59ba2f LD |
31 | |
32 | #define ISL29028_REG_CONFIGURE 0x01 | |
33 | ||
3a37f1c2 PMS |
34 | #define ISL29028_CONF_ALS_IR_MODE_ALS 0 |
35 | #define ISL29028_CONF_ALS_IR_MODE_IR BIT(0) | |
36 | #define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0) | |
6d59ba2f | 37 | |
3a37f1c2 PMS |
38 | #define ISL29028_CONF_ALS_RANGE_LOW_LUX 0 |
39 | #define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1) | |
40 | #define ISL29028_CONF_ALS_RANGE_MASK BIT(1) | |
6d59ba2f | 41 | |
3a37f1c2 PMS |
42 | #define ISL29028_CONF_ALS_DIS 0 |
43 | #define ISL29028_CONF_ALS_EN BIT(2) | |
44 | #define ISL29028_CONF_ALS_EN_MASK BIT(2) | |
6d59ba2f | 45 | |
3a37f1c2 PMS |
46 | #define ISL29028_CONF_PROX_SLP_SH 4 |
47 | #define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH) | |
6d59ba2f | 48 | |
3a37f1c2 PMS |
49 | #define ISL29028_CONF_PROX_EN BIT(7) |
50 | #define ISL29028_CONF_PROX_EN_MASK BIT(7) | |
6d59ba2f LD |
51 | |
52 | #define ISL29028_REG_INTERRUPT 0x02 | |
53 | ||
54 | #define ISL29028_REG_PROX_DATA 0x08 | |
55 | #define ISL29028_REG_ALSIR_L 0x09 | |
56 | #define ISL29028_REG_ALSIR_U 0x0A | |
57 | ||
58 | #define ISL29028_REG_TEST1_MODE 0x0E | |
59 | #define ISL29028_REG_TEST2_MODE 0x0F | |
60 | ||
61 | #define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1) | |
62 | ||
bafaa6de PMS |
63 | enum isl29028_als_ir_mode { |
64 | ISL29028_MODE_NONE = 0, | |
65 | ISL29028_MODE_ALS, | |
66 | ISL29028_MODE_IR, | |
6d59ba2f LD |
67 | }; |
68 | ||
69 | struct isl29028_chip { | |
6d59ba2f LD |
70 | struct mutex lock; |
71 | struct regmap *regmap; | |
72 | ||
73 | unsigned int prox_sampling; | |
74 | bool enable_prox; | |
75 | ||
76 | int lux_scale; | |
bafaa6de | 77 | enum isl29028_als_ir_mode als_ir_mode; |
6d59ba2f LD |
78 | }; |
79 | ||
80 | static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, | |
ca52c88c | 81 | unsigned int sampling) |
6d59ba2f LD |
82 | { |
83 | static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0}; | |
84 | int sel; | |
85 | unsigned int period = DIV_ROUND_UP(1000, sampling); | |
86 | ||
87 | for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) { | |
88 | if (period >= prox_period[sel]) | |
89 | break; | |
90 | } | |
91 | return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 PMS |
92 | ISL29028_CONF_PROX_SLP_MASK, |
93 | sel << ISL29028_CONF_PROX_SLP_SH); | |
6d59ba2f LD |
94 | } |
95 | ||
96 | static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) | |
97 | { | |
98 | int ret; | |
99 | int val = 0; | |
100 | ||
101 | if (enable) | |
3a37f1c2 | 102 | val = ISL29028_CONF_PROX_EN; |
6d59ba2f | 103 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 | 104 | ISL29028_CONF_PROX_EN_MASK, val); |
6d59ba2f LD |
105 | if (ret < 0) |
106 | return ret; | |
107 | ||
108 | /* Wait for conversion to be complete for first sample */ | |
109 | mdelay(DIV_ROUND_UP(1000, chip->prox_sampling)); | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) | |
114 | { | |
3a37f1c2 PMS |
115 | int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX : |
116 | ISL29028_CONF_ALS_RANGE_LOW_LUX; | |
6d59ba2f LD |
117 | |
118 | return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 | 119 | ISL29028_CONF_ALS_RANGE_MASK, val); |
6d59ba2f LD |
120 | } |
121 | ||
122 | static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, | |
bafaa6de | 123 | enum isl29028_als_ir_mode mode) |
6d59ba2f LD |
124 | { |
125 | int ret = 0; | |
126 | ||
127 | switch (mode) { | |
bafaa6de | 128 | case ISL29028_MODE_ALS: |
6d59ba2f | 129 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 PMS |
130 | ISL29028_CONF_ALS_IR_MODE_MASK, |
131 | ISL29028_CONF_ALS_IR_MODE_ALS); | |
6d59ba2f LD |
132 | if (ret < 0) |
133 | return ret; | |
134 | ||
135 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 PMS |
136 | ISL29028_CONF_ALS_RANGE_MASK, |
137 | ISL29028_CONF_ALS_RANGE_HIGH_LUX); | |
6d59ba2f LD |
138 | break; |
139 | ||
bafaa6de | 140 | case ISL29028_MODE_IR: |
6d59ba2f | 141 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 PMS |
142 | ISL29028_CONF_ALS_IR_MODE_MASK, |
143 | ISL29028_CONF_ALS_IR_MODE_IR); | |
6d59ba2f LD |
144 | break; |
145 | ||
bafaa6de | 146 | case ISL29028_MODE_NONE: |
6d59ba2f | 147 | return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, |
3a37f1c2 | 148 | ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS); |
6d59ba2f LD |
149 | } |
150 | ||
151 | if (ret < 0) | |
152 | return ret; | |
153 | ||
154 | /* Enable the ALS/IR */ | |
155 | ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, | |
3a37f1c2 PMS |
156 | ISL29028_CONF_ALS_EN_MASK, |
157 | ISL29028_CONF_ALS_EN); | |
6d59ba2f LD |
158 | if (ret < 0) |
159 | return ret; | |
160 | ||
161 | /* Need to wait for conversion time if ALS/IR mode enabled */ | |
3a37f1c2 | 162 | mdelay(ISL29028_CONV_TIME_MS); |
6d59ba2f LD |
163 | return 0; |
164 | } | |
165 | ||
166 | static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) | |
167 | { | |
722fc316 | 168 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
169 | unsigned int lsb; |
170 | unsigned int msb; | |
171 | int ret; | |
172 | ||
173 | ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb); | |
174 | if (ret < 0) { | |
722fc316 | 175 | dev_err(dev, |
6d59ba2f LD |
176 | "Error in reading register ALSIR_L err %d\n", ret); |
177 | return ret; | |
178 | } | |
179 | ||
180 | ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb); | |
181 | if (ret < 0) { | |
722fc316 | 182 | dev_err(dev, |
6d59ba2f LD |
183 | "Error in reading register ALSIR_U err %d\n", ret); |
184 | return ret; | |
185 | } | |
186 | ||
187 | *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF); | |
188 | return 0; | |
189 | } | |
190 | ||
191 | static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) | |
192 | { | |
722fc316 | 193 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
194 | unsigned int data; |
195 | int ret; | |
196 | ||
197 | ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); | |
198 | if (ret < 0) { | |
722fc316 | 199 | dev_err(dev, "Error in reading register %d, error %d\n", |
ca52c88c | 200 | ISL29028_REG_PROX_DATA, ret); |
6d59ba2f LD |
201 | return ret; |
202 | } | |
203 | *prox = data; | |
204 | return 0; | |
205 | } | |
206 | ||
207 | static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data) | |
208 | { | |
209 | int ret; | |
210 | ||
211 | if (!chip->enable_prox) { | |
212 | ret = isl29028_enable_proximity(chip, true); | |
213 | if (ret < 0) | |
214 | return ret; | |
215 | chip->enable_prox = true; | |
216 | } | |
217 | return isl29028_read_proxim(chip, prox_data); | |
218 | } | |
219 | ||
220 | static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) | |
221 | { | |
722fc316 | 222 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
223 | int ret; |
224 | int als_ir_data; | |
225 | ||
bafaa6de PMS |
226 | if (chip->als_ir_mode != ISL29028_MODE_ALS) { |
227 | ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS); | |
6d59ba2f | 228 | if (ret < 0) { |
722fc316 | 229 | dev_err(dev, |
6d59ba2f LD |
230 | "Error in enabling ALS mode err %d\n", ret); |
231 | return ret; | |
232 | } | |
bafaa6de | 233 | chip->als_ir_mode = ISL29028_MODE_ALS; |
6d59ba2f LD |
234 | } |
235 | ||
236 | ret = isl29028_read_als_ir(chip, &als_ir_data); | |
237 | if (ret < 0) | |
238 | return ret; | |
239 | ||
240 | /* | |
241 | * convert als data count to lux. | |
242 | * if lux_scale = 125, lux = count * 0.031 | |
243 | * if lux_scale = 2000, lux = count * 0.49 | |
244 | */ | |
245 | if (chip->lux_scale == 125) | |
246 | als_ir_data = (als_ir_data * 31) / 1000; | |
247 | else | |
248 | als_ir_data = (als_ir_data * 49) / 100; | |
249 | ||
250 | *als_data = als_ir_data; | |
251 | return 0; | |
252 | } | |
253 | ||
254 | static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) | |
255 | { | |
722fc316 | 256 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
257 | int ret; |
258 | ||
bafaa6de PMS |
259 | if (chip->als_ir_mode != ISL29028_MODE_IR) { |
260 | ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR); | |
6d59ba2f | 261 | if (ret < 0) { |
722fc316 | 262 | dev_err(dev, |
6d59ba2f LD |
263 | "Error in enabling IR mode err %d\n", ret); |
264 | return ret; | |
265 | } | |
bafaa6de | 266 | chip->als_ir_mode = ISL29028_MODE_IR; |
6d59ba2f LD |
267 | } |
268 | return isl29028_read_als_ir(chip, ir_data); | |
269 | } | |
270 | ||
271 | /* Channel IO */ | |
272 | static int isl29028_write_raw(struct iio_dev *indio_dev, | |
ca52c88c ERR |
273 | struct iio_chan_spec const *chan, |
274 | int val, int val2, long mask) | |
6d59ba2f LD |
275 | { |
276 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
722fc316 | 277 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
278 | int ret = -EINVAL; |
279 | ||
280 | mutex_lock(&chip->lock); | |
281 | switch (chan->type) { | |
282 | case IIO_PROXIMITY: | |
892cb6dc | 283 | if (mask != IIO_CHAN_INFO_SAMP_FREQ) { |
722fc316 | 284 | dev_err(dev, |
6d59ba2f LD |
285 | "proximity: mask value 0x%08lx not supported\n", |
286 | mask); | |
287 | break; | |
288 | } | |
289 | if (val < 1 || val > 100) { | |
722fc316 | 290 | dev_err(dev, |
6d59ba2f LD |
291 | "Samp_freq %d is not in range[1:100]\n", val); |
292 | break; | |
293 | } | |
294 | ret = isl29028_set_proxim_sampling(chip, val); | |
295 | if (ret < 0) { | |
722fc316 | 296 | dev_err(dev, |
6d59ba2f LD |
297 | "Setting proximity samp_freq fail, err %d\n", |
298 | ret); | |
299 | break; | |
300 | } | |
301 | chip->prox_sampling = val; | |
302 | break; | |
303 | ||
304 | case IIO_LIGHT: | |
892cb6dc | 305 | if (mask != IIO_CHAN_INFO_SCALE) { |
722fc316 | 306 | dev_err(dev, |
6d59ba2f LD |
307 | "light: mask value 0x%08lx not supported\n", |
308 | mask); | |
309 | break; | |
310 | } | |
311 | if ((val != 125) && (val != 2000)) { | |
722fc316 | 312 | dev_err(dev, |
6d59ba2f LD |
313 | "lux scale %d is invalid [125, 2000]\n", val); |
314 | break; | |
315 | } | |
316 | ret = isl29028_set_als_scale(chip, val); | |
317 | if (ret < 0) { | |
722fc316 | 318 | dev_err(dev, |
6d59ba2f LD |
319 | "Setting lux scale fail with error %d\n", ret); |
320 | break; | |
321 | } | |
322 | chip->lux_scale = val; | |
323 | break; | |
324 | ||
325 | default: | |
722fc316 | 326 | dev_err(dev, "Unsupported channel type\n"); |
6d59ba2f LD |
327 | break; |
328 | } | |
329 | mutex_unlock(&chip->lock); | |
330 | return ret; | |
331 | } | |
332 | ||
333 | static int isl29028_read_raw(struct iio_dev *indio_dev, | |
ca52c88c ERR |
334 | struct iio_chan_spec const *chan, |
335 | int *val, int *val2, long mask) | |
6d59ba2f LD |
336 | { |
337 | struct isl29028_chip *chip = iio_priv(indio_dev); | |
722fc316 | 338 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
339 | int ret = -EINVAL; |
340 | ||
341 | mutex_lock(&chip->lock); | |
342 | switch (mask) { | |
a8c0ed75 JC |
343 | case IIO_CHAN_INFO_RAW: |
344 | case IIO_CHAN_INFO_PROCESSED: | |
6d59ba2f LD |
345 | switch (chan->type) { |
346 | case IIO_LIGHT: | |
347 | ret = isl29028_als_get(chip, val); | |
348 | break; | |
349 | case IIO_INTENSITY: | |
350 | ret = isl29028_ir_get(chip, val); | |
351 | break; | |
352 | case IIO_PROXIMITY: | |
353 | ret = isl29028_proxim_get(chip, val); | |
354 | break; | |
355 | default: | |
356 | break; | |
357 | } | |
358 | if (ret < 0) | |
359 | break; | |
360 | ret = IIO_VAL_INT; | |
361 | break; | |
362 | ||
892cb6dc | 363 | case IIO_CHAN_INFO_SAMP_FREQ: |
6d59ba2f LD |
364 | if (chan->type != IIO_PROXIMITY) |
365 | break; | |
366 | *val = chip->prox_sampling; | |
367 | ret = IIO_VAL_INT; | |
368 | break; | |
369 | ||
892cb6dc | 370 | case IIO_CHAN_INFO_SCALE: |
6d59ba2f LD |
371 | if (chan->type != IIO_LIGHT) |
372 | break; | |
373 | *val = chip->lux_scale; | |
374 | ret = IIO_VAL_INT; | |
375 | break; | |
376 | ||
377 | default: | |
722fc316 | 378 | dev_err(dev, "mask value 0x%08lx not supported\n", mask); |
6d59ba2f LD |
379 | break; |
380 | } | |
381 | mutex_unlock(&chip->lock); | |
382 | return ret; | |
383 | } | |
384 | ||
385 | static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, | |
b9b689c1 PMS |
386 | "1 3 5 10 13 20 83 100"); |
387 | static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000"); | |
6d59ba2f LD |
388 | |
389 | #define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) | |
390 | #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) | |
391 | static struct attribute *isl29028_attributes[] = { | |
392 | ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available), | |
393 | ISL29028_CONST_ATTR(in_illuminance_scale_available), | |
394 | NULL, | |
395 | }; | |
396 | ||
397 | static const struct attribute_group isl29108_group = { | |
398 | .attrs = isl29028_attributes, | |
399 | }; | |
400 | ||
401 | static const struct iio_chan_spec isl29028_channels[] = { | |
402 | { | |
403 | .type = IIO_LIGHT, | |
93a9fdff JC |
404 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | |
405 | BIT(IIO_CHAN_INFO_SCALE), | |
6d59ba2f LD |
406 | }, { |
407 | .type = IIO_INTENSITY, | |
93a9fdff | 408 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
6d59ba2f LD |
409 | }, { |
410 | .type = IIO_PROXIMITY, | |
93a9fdff JC |
411 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
412 | BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
6d59ba2f LD |
413 | } |
414 | }; | |
415 | ||
416 | static const struct iio_info isl29028_info = { | |
417 | .attrs = &isl29108_group, | |
418 | .driver_module = THIS_MODULE, | |
df271034 BG |
419 | .read_raw = isl29028_read_raw, |
420 | .write_raw = isl29028_write_raw, | |
6d59ba2f LD |
421 | }; |
422 | ||
423 | static int isl29028_chip_init(struct isl29028_chip *chip) | |
424 | { | |
722fc316 | 425 | struct device *dev = regmap_get_device(chip->regmap); |
6d59ba2f LD |
426 | int ret; |
427 | ||
428 | chip->enable_prox = false; | |
429 | chip->prox_sampling = 20; | |
430 | chip->lux_scale = 2000; | |
bafaa6de | 431 | chip->als_ir_mode = ISL29028_MODE_NONE; |
6d59ba2f LD |
432 | |
433 | ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); | |
434 | if (ret < 0) { | |
722fc316 | 435 | dev_err(dev, "%s(): write to reg %d failed, err = %d\n", |
6d59ba2f LD |
436 | __func__, ISL29028_REG_TEST1_MODE, ret); |
437 | return ret; | |
438 | } | |
439 | ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); | |
440 | if (ret < 0) { | |
722fc316 | 441 | dev_err(dev, "%s(): write to reg %d failed, err = %d\n", |
6d59ba2f LD |
442 | __func__, ISL29028_REG_TEST2_MODE, ret); |
443 | return ret; | |
444 | } | |
445 | ||
446 | ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); | |
447 | if (ret < 0) { | |
722fc316 | 448 | dev_err(dev, "%s(): write to reg %d failed, err = %d\n", |
6d59ba2f LD |
449 | __func__, ISL29028_REG_CONFIGURE, ret); |
450 | return ret; | |
451 | } | |
452 | ||
453 | ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); | |
454 | if (ret < 0) { | |
722fc316 | 455 | dev_err(dev, "setting the proximity, err = %d\n", ret); |
6d59ba2f LD |
456 | return ret; |
457 | } | |
458 | ||
459 | ret = isl29028_set_als_scale(chip, chip->lux_scale); | |
460 | if (ret < 0) | |
722fc316 | 461 | dev_err(dev, "setting als scale failed, err = %d\n", ret); |
6d59ba2f LD |
462 | return ret; |
463 | } | |
464 | ||
f6127704 | 465 | static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) |
6d59ba2f LD |
466 | { |
467 | switch (reg) { | |
468 | case ISL29028_REG_INTERRUPT: | |
469 | case ISL29028_REG_PROX_DATA: | |
470 | case ISL29028_REG_ALSIR_L: | |
471 | case ISL29028_REG_ALSIR_U: | |
472 | return true; | |
473 | default: | |
474 | return false; | |
475 | } | |
476 | } | |
477 | ||
478 | static const struct regmap_config isl29028_regmap_config = { | |
479 | .reg_bits = 8, | |
480 | .val_bits = 8, | |
f6127704 | 481 | .volatile_reg = isl29028_is_volatile_reg, |
6d59ba2f LD |
482 | .max_register = ISL29028_NUM_REGS - 1, |
483 | .num_reg_defaults_raw = ISL29028_NUM_REGS, | |
484 | .cache_type = REGCACHE_RBTREE, | |
485 | }; | |
486 | ||
4ae1c61f | 487 | static int isl29028_probe(struct i2c_client *client, |
ca52c88c | 488 | const struct i2c_device_id *id) |
6d59ba2f LD |
489 | { |
490 | struct isl29028_chip *chip; | |
491 | struct iio_dev *indio_dev; | |
492 | int ret; | |
493 | ||
80cab1da | 494 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); |
6d59ba2f LD |
495 | if (!indio_dev) { |
496 | dev_err(&client->dev, "iio allocation fails\n"); | |
497 | return -ENOMEM; | |
498 | } | |
499 | ||
500 | chip = iio_priv(indio_dev); | |
501 | ||
502 | i2c_set_clientdata(client, indio_dev); | |
6d59ba2f LD |
503 | mutex_init(&chip->lock); |
504 | ||
505 | chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config); | |
506 | if (IS_ERR(chip->regmap)) { | |
507 | ret = PTR_ERR(chip->regmap); | |
722fc316 AS |
508 | dev_err(&client->dev, "regmap initialization failed: %d\n", |
509 | ret); | |
80cab1da | 510 | return ret; |
6d59ba2f LD |
511 | } |
512 | ||
513 | ret = isl29028_chip_init(chip); | |
514 | if (ret < 0) { | |
722fc316 | 515 | dev_err(&client->dev, "chip initialization failed: %d\n", ret); |
80cab1da | 516 | return ret; |
6d59ba2f LD |
517 | } |
518 | ||
519 | indio_dev->info = &isl29028_info; | |
520 | indio_dev->channels = isl29028_channels; | |
521 | indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); | |
522 | indio_dev->name = id->name; | |
523 | indio_dev->dev.parent = &client->dev; | |
524 | indio_dev->modes = INDIO_DIRECT_MODE; | |
86727e30 | 525 | ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); |
6d59ba2f | 526 | if (ret < 0) { |
722fc316 AS |
527 | dev_err(&client->dev, |
528 | "iio registration fails with error %d\n", | |
6d59ba2f | 529 | ret); |
80cab1da | 530 | return ret; |
6d59ba2f LD |
531 | } |
532 | return 0; | |
6d59ba2f LD |
533 | } |
534 | ||
6d59ba2f LD |
535 | static const struct i2c_device_id isl29028_id[] = { |
536 | {"isl29028", 0}, | |
537 | {} | |
538 | }; | |
539 | MODULE_DEVICE_TABLE(i2c, isl29028_id); | |
540 | ||
541 | static const struct of_device_id isl29028_of_match[] = { | |
37e5157d AE |
542 | { .compatible = "isl,isl29028", }, /* for backward compat., don't use */ |
543 | { .compatible = "isil,isl29028", }, | |
6d59ba2f LD |
544 | { }, |
545 | }; | |
546 | MODULE_DEVICE_TABLE(of, isl29028_of_match); | |
547 | ||
548 | static struct i2c_driver isl29028_driver = { | |
6d59ba2f LD |
549 | .driver = { |
550 | .name = "isl29028", | |
6d59ba2f LD |
551 | .of_match_table = isl29028_of_match, |
552 | }, | |
553 | .probe = isl29028_probe, | |
6d59ba2f LD |
554 | .id_table = isl29028_id, |
555 | }; | |
556 | ||
557 | module_i2c_driver(isl29028_driver); | |
558 | ||
559 | MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); | |
560 | MODULE_LICENSE("GPL v2"); | |
561 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); |