1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * isl29501.c: ISL29501 Time of Flight sensor driver. |
4 | * |
5 | * Copyright (C) 2018 |
6 | * Author: Mathieu Othacehe <m.othacehe@gmail.com> |
7 | * |
8 | * 7-bit I2C slave address: 0x57 |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/err.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/iio/iio.h> |
17 | #include <linux/iio/sysfs.h> |
18 | |
19 | #include <linux/iio/trigger_consumer.h> |
20 | #include <linux/iio/buffer.h> |
21 | #include <linux/iio/triggered_buffer.h> |
22 | |
23 | /* Control, setting and status registers */ |
24 | #define ISL29501_DEVICE_ID 0x00 |
25 | #define ISL29501_ID 0x0A |
26 | |
27 | /* Sampling control registers */ |
28 | #define ISL29501_INTEGRATION_PERIOD 0x10 |
29 | #define ISL29501_SAMPLE_PERIOD 0x11 |
30 | |
31 | /* Closed loop calibration registers */ |
32 | #define ISL29501_CROSSTALK_I_MSB 0x24 |
33 | #define ISL29501_CROSSTALK_I_LSB 0x25 |
34 | #define ISL29501_CROSSTALK_I_EXPONENT 0x26 |
35 | #define ISL29501_CROSSTALK_Q_MSB 0x27 |
36 | #define ISL29501_CROSSTALK_Q_LSB 0x28 |
37 | #define ISL29501_CROSSTALK_Q_EXPONENT 0x29 |
38 | #define ISL29501_CROSSTALK_GAIN_MSB 0x2A |
39 | #define ISL29501_CROSSTALK_GAIN_LSB 0x2B |
40 | #define ISL29501_MAGNITUDE_REF_EXP 0x2C |
41 | #define ISL29501_MAGNITUDE_REF_MSB 0x2D |
42 | #define ISL29501_MAGNITUDE_REF_LSB 0x2E |
43 | #define ISL29501_PHASE_OFFSET_MSB 0x2F |
44 | #define ISL29501_PHASE_OFFSET_LSB 0x30 |
45 | |
46 | /* Analog control registers */ |
47 | #define ISL29501_DRIVER_RANGE 0x90 |
48 | #define ISL29501_EMITTER_DAC 0x91 |
49 | |
50 | #define ISL29501_COMMAND_REGISTER 0xB0 |
51 | |
52 | /* Commands */ |
53 | #define ISL29501_EMUL_SAMPLE_START_PIN 0x49 |
54 | #define ISL29501_RESET_ALL_REGISTERS 0xD7 |
55 | #define ISL29501_RESET_INT_SM 0xD1 |
56 | |
57 | /* Ambiant light and temperature corrections */ |
58 | #define ISL29501_TEMP_REFERENCE 0x31 |
59 | #define ISL29501_PHASE_EXPONENT 0x33 |
60 | #define ISL29501_TEMP_COEFF_A 0x34 |
61 | #define ISL29501_TEMP_COEFF_B 0x39 |
62 | #define ISL29501_AMBIANT_COEFF_A 0x36 |
63 | #define ISL29501_AMBIANT_COEFF_B 0x3B |
64 | |
65 | /* Data output registers */ |
66 | #define ISL29501_DISTANCE_MSB_DATA 0xD1 |
67 | #define ISL29501_DISTANCE_LSB_DATA 0xD2 |
68 | #define ISL29501_PRECISION_MSB 0xD3 |
69 | #define ISL29501_PRECISION_LSB 0xD4 |
70 | #define ISL29501_MAGNITUDE_EXPONENT 0xD5 |
71 | #define ISL29501_MAGNITUDE_MSB 0xD6 |
72 | #define ISL29501_MAGNITUDE_LSB 0xD7 |
73 | #define ISL29501_PHASE_MSB 0xD8 |
74 | #define ISL29501_PHASE_LSB 0xD9 |
75 | #define ISL29501_I_RAW_EXPONENT 0xDA |
76 | #define ISL29501_I_RAW_MSB 0xDB |
77 | #define ISL29501_I_RAW_LSB 0xDC |
78 | #define ISL29501_Q_RAW_EXPONENT 0xDD |
79 | #define ISL29501_Q_RAW_MSB 0xDE |
80 | #define ISL29501_Q_RAW_LSB 0xDF |
81 | #define ISL29501_DIE_TEMPERATURE 0xE2 |
82 | #define ISL29501_AMBIENT_LIGHT 0xE3 |
83 | #define ISL29501_GAIN_MSB 0xE6 |
84 | #define ISL29501_GAIN_LSB 0xE7 |
85 | |
86 | #define ISL29501_MAX_EXP_VAL 15 |
87 | |
88 | #define ISL29501_INT_TIME_AVAILABLE \ |
89 | "0.00007 0.00014 0.00028 0.00057 0.00114 " \ |
90 | "0.00228 0.00455 0.00910 0.01820 0.03640 " \ |
91 | "0.07281 0.14561" |
92 | |
93 | #define ISL29501_CURRENT_SCALE_AVAILABLE \ |
94 | "0.0039 0.0078 0.0118 0.0157 0.0196 " \ |
95 | "0.0235 0.0275 0.0314 0.0352 0.0392 " \ |
96 | "0.0431 0.0471 0.0510 0.0549 0.0588" |
97 | |
98 | enum isl29501_correction_coeff { |
99 | COEFF_TEMP_A, |
100 | COEFF_TEMP_B, |
101 | COEFF_LIGHT_A, |
102 | COEFF_LIGHT_B, |
103 | COEFF_MAX, |
104 | }; |
105 | |
106 | struct isl29501_private { |
107 | struct i2c_client *client; |
108 | struct mutex lock; |
109 | /* Exact representation of correction coefficients. */ |
110 | unsigned int shadow_coeffs[COEFF_MAX]; |
111 | }; |
112 | |
113 | enum isl29501_register_name { |
114 | REG_DISTANCE, |
115 | REG_PHASE, |
116 | REG_TEMPERATURE, |
117 | REG_AMBIENT_LIGHT, |
118 | REG_GAIN, |
119 | REG_GAIN_BIAS, |
120 | REG_PHASE_EXP, |
121 | REG_CALIB_PHASE_TEMP_A, |
122 | REG_CALIB_PHASE_TEMP_B, |
123 | REG_CALIB_PHASE_LIGHT_A, |
124 | REG_CALIB_PHASE_LIGHT_B, |
125 | REG_DISTANCE_BIAS, |
126 | REG_TEMPERATURE_BIAS, |
127 | REG_INT_TIME, |
128 | REG_SAMPLE_TIME, |
129 | REG_DRIVER_RANGE, |
130 | REG_EMITTER_DAC, |
131 | }; |
132 | |
133 | struct isl29501_register_desc { |
134 | u8 msb; |
135 | u8 lsb; |
136 | }; |
137 | |
138 | static const struct isl29501_register_desc isl29501_registers[] = { |
139 | [REG_DISTANCE] = { |
140 | .msb = ISL29501_DISTANCE_MSB_DATA, |
141 | .lsb = ISL29501_DISTANCE_LSB_DATA, |
142 | }, |
143 | [REG_PHASE] = { |
144 | .msb = ISL29501_PHASE_MSB, |
145 | .lsb = ISL29501_PHASE_LSB, |
146 | }, |
147 | [REG_TEMPERATURE] = { |
148 | .lsb = ISL29501_DIE_TEMPERATURE, |
149 | }, |
150 | [REG_AMBIENT_LIGHT] = { |
151 | .lsb = ISL29501_AMBIENT_LIGHT, |
152 | }, |
153 | [REG_GAIN] = { |
154 | .msb = ISL29501_GAIN_MSB, |
155 | .lsb = ISL29501_GAIN_LSB, |
156 | }, |
157 | [REG_GAIN_BIAS] = { |
158 | .msb = ISL29501_CROSSTALK_GAIN_MSB, |
159 | .lsb = ISL29501_CROSSTALK_GAIN_LSB, |
160 | }, |
161 | [REG_PHASE_EXP] = { |
162 | .lsb = ISL29501_PHASE_EXPONENT, |
163 | }, |
164 | [REG_CALIB_PHASE_TEMP_A] = { |
165 | .lsb = ISL29501_TEMP_COEFF_A, |
166 | }, |
167 | [REG_CALIB_PHASE_TEMP_B] = { |
168 | .lsb = ISL29501_TEMP_COEFF_B, |
169 | }, |
170 | [REG_CALIB_PHASE_LIGHT_A] = { |
171 | .lsb = ISL29501_AMBIANT_COEFF_A, |
172 | }, |
173 | [REG_CALIB_PHASE_LIGHT_B] = { |
174 | .lsb = ISL29501_AMBIANT_COEFF_B, |
175 | }, |
176 | [REG_DISTANCE_BIAS] = { |
177 | .msb = ISL29501_PHASE_OFFSET_MSB, |
178 | .lsb = ISL29501_PHASE_OFFSET_LSB, |
179 | }, |
180 | [REG_TEMPERATURE_BIAS] = { |
181 | .lsb = ISL29501_TEMP_REFERENCE, |
182 | }, |
183 | [REG_INT_TIME] = { |
184 | .lsb = ISL29501_INTEGRATION_PERIOD, |
185 | }, |
186 | [REG_SAMPLE_TIME] = { |
187 | .lsb = ISL29501_SAMPLE_PERIOD, |
188 | }, |
189 | [REG_DRIVER_RANGE] = { |
190 | .lsb = ISL29501_DRIVER_RANGE, |
191 | }, |
192 | [REG_EMITTER_DAC] = { |
193 | .lsb = ISL29501_EMITTER_DAC, |
194 | }, |
195 | }; |
196 | |
197 | static int isl29501_register_read(struct isl29501_private *isl29501, |
198 | enum isl29501_register_name name, |
199 | u32 *val) |
200 | { |
201 | const struct isl29501_register_desc *reg = &isl29501_registers[name]; |
202 | u8 msb = 0, lsb = 0; |
203 | s32 ret; |
204 | |
205 | mutex_lock(&isl29501->lock); |
206 | if (reg->msb) { |
207 | ret = i2c_smbus_read_byte_data(client: isl29501->client, command: reg->msb); |
208 | if (ret < 0) |
209 | goto err; |
210 | msb = ret; |
211 | } |
212 | |
213 | if (reg->lsb) { |
214 | ret = i2c_smbus_read_byte_data(client: isl29501->client, command: reg->lsb); |
215 | if (ret < 0) |
216 | goto err; |
217 | lsb = ret; |
218 | } |
219 | mutex_unlock(lock: &isl29501->lock); |
220 | |
221 | *val = (msb << 8) + lsb; |
222 | |
223 | return 0; |
224 | err: |
225 | mutex_unlock(lock: &isl29501->lock); |
226 | |
227 | return ret; |
228 | } |
229 | |
230 | static u32 isl29501_register_write(struct isl29501_private *isl29501, |
231 | enum isl29501_register_name name, |
232 | u32 value) |
233 | { |
234 | const struct isl29501_register_desc *reg = &isl29501_registers[name]; |
235 | int ret; |
236 | |
237 | if (!reg->msb && value > U8_MAX) |
238 | return -ERANGE; |
239 | |
240 | if (value > U16_MAX) |
241 | return -ERANGE; |
242 | |
243 | mutex_lock(&isl29501->lock); |
244 | if (reg->msb) { |
245 | ret = i2c_smbus_write_byte_data(client: isl29501->client, |
246 | command: reg->msb, value: value >> 8); |
247 | if (ret < 0) |
248 | goto err; |
249 | } |
250 | |
251 | ret = i2c_smbus_write_byte_data(client: isl29501->client, command: reg->lsb, value); |
252 | |
253 | err: |
254 | mutex_unlock(lock: &isl29501->lock); |
255 | return ret; |
256 | } |
257 | |
258 | static ssize_t isl29501_read_ext(struct iio_dev *indio_dev, |
259 | uintptr_t private, |
260 | const struct iio_chan_spec *chan, |
261 | char *buf) |
262 | { |
263 | struct isl29501_private *isl29501 = iio_priv(indio_dev); |
264 | enum isl29501_register_name reg = private; |
265 | int ret; |
266 | u32 value, gain, coeff, exp; |
267 | |
268 | switch (reg) { |
269 | case REG_GAIN: |
270 | case REG_GAIN_BIAS: |
271 | ret = isl29501_register_read(isl29501, name: reg, val: &gain); |
272 | if (ret < 0) |
273 | return ret; |
274 | |
275 | value = gain; |
276 | break; |
277 | case REG_CALIB_PHASE_TEMP_A: |
278 | case REG_CALIB_PHASE_TEMP_B: |
279 | case REG_CALIB_PHASE_LIGHT_A: |
280 | case REG_CALIB_PHASE_LIGHT_B: |
281 | ret = isl29501_register_read(isl29501, name: REG_PHASE_EXP, val: &exp); |
282 | if (ret < 0) |
283 | return ret; |
284 | |
285 | ret = isl29501_register_read(isl29501, name: reg, val: &coeff); |
286 | if (ret < 0) |
287 | return ret; |
288 | |
289 | value = coeff << exp; |
290 | break; |
291 | default: |
292 | return -EINVAL; |
293 | } |
294 | |
295 | return sprintf(buf, fmt: "%u\n" , value); |
296 | } |
297 | |
298 | static int isl29501_set_shadow_coeff(struct isl29501_private *isl29501, |
299 | enum isl29501_register_name reg, |
300 | unsigned int val) |
301 | { |
302 | enum isl29501_correction_coeff coeff; |
303 | |
304 | switch (reg) { |
305 | case REG_CALIB_PHASE_TEMP_A: |
306 | coeff = COEFF_TEMP_A; |
307 | break; |
308 | case REG_CALIB_PHASE_TEMP_B: |
309 | coeff = COEFF_TEMP_B; |
310 | break; |
311 | case REG_CALIB_PHASE_LIGHT_A: |
312 | coeff = COEFF_LIGHT_A; |
313 | break; |
314 | case REG_CALIB_PHASE_LIGHT_B: |
315 | coeff = COEFF_LIGHT_B; |
316 | break; |
317 | default: |
318 | return -EINVAL; |
319 | } |
320 | isl29501->shadow_coeffs[coeff] = val; |
321 | |
322 | return 0; |
323 | } |
324 | |
325 | static int isl29501_write_coeff(struct isl29501_private *isl29501, |
326 | enum isl29501_correction_coeff coeff, |
327 | int val) |
328 | { |
329 | enum isl29501_register_name reg; |
330 | |
331 | switch (coeff) { |
332 | case COEFF_TEMP_A: |
333 | reg = REG_CALIB_PHASE_TEMP_A; |
334 | break; |
335 | case COEFF_TEMP_B: |
336 | reg = REG_CALIB_PHASE_TEMP_B; |
337 | break; |
338 | case COEFF_LIGHT_A: |
339 | reg = REG_CALIB_PHASE_LIGHT_A; |
340 | break; |
341 | case COEFF_LIGHT_B: |
342 | reg = REG_CALIB_PHASE_LIGHT_B; |
343 | break; |
344 | default: |
345 | return -EINVAL; |
346 | } |
347 | |
348 | return isl29501_register_write(isl29501, name: reg, value: val); |
349 | } |
350 | |
351 | static unsigned int isl29501_find_corr_exp(unsigned int val, |
352 | unsigned int max_exp, |
353 | unsigned int max_mantissa) |
354 | { |
355 | unsigned int exp = 1; |
356 | |
357 | /* |
358 | * Correction coefficients are represented under |
359 | * mantissa * 2^exponent form, where mantissa and exponent |
360 | * are stored in two separate registers of the sensor. |
361 | * |
362 | * Compute and return the lowest exponent such as: |
363 | * mantissa = value / 2^exponent |
364 | * |
365 | * where mantissa < max_mantissa. |
366 | */ |
367 | if (val <= max_mantissa) |
368 | return 0; |
369 | |
370 | while ((val >> exp) > max_mantissa) { |
371 | exp++; |
372 | |
373 | if (exp > max_exp) |
374 | return max_exp; |
375 | } |
376 | |
377 | return exp; |
378 | } |
379 | |
380 | static ssize_t isl29501_write_ext(struct iio_dev *indio_dev, |
381 | uintptr_t private, |
382 | const struct iio_chan_spec *chan, |
383 | const char *buf, size_t len) |
384 | { |
385 | struct isl29501_private *isl29501 = iio_priv(indio_dev); |
386 | enum isl29501_register_name reg = private; |
387 | unsigned int val; |
388 | int max_exp = 0; |
389 | int ret; |
390 | int i; |
391 | |
392 | ret = kstrtouint(s: buf, base: 10, res: &val); |
393 | if (ret) |
394 | return ret; |
395 | |
396 | switch (reg) { |
397 | case REG_GAIN_BIAS: |
398 | if (val > U16_MAX) |
399 | return -ERANGE; |
400 | |
401 | ret = isl29501_register_write(isl29501, name: reg, value: val); |
402 | if (ret < 0) |
403 | return ret; |
404 | |
405 | break; |
406 | case REG_CALIB_PHASE_TEMP_A: |
407 | case REG_CALIB_PHASE_TEMP_B: |
408 | case REG_CALIB_PHASE_LIGHT_A: |
409 | case REG_CALIB_PHASE_LIGHT_B: |
410 | |
411 | if (val > (U8_MAX << ISL29501_MAX_EXP_VAL)) |
412 | return -ERANGE; |
413 | |
414 | /* Store the correction coefficient under its exact form. */ |
415 | ret = isl29501_set_shadow_coeff(isl29501, reg, val); |
416 | if (ret < 0) |
417 | return ret; |
418 | |
419 | /* |
420 | * Find the highest exponent needed to represent |
421 | * correction coefficients. |
422 | */ |
423 | for (i = 0; i < COEFF_MAX; i++) { |
424 | int corr; |
425 | int corr_exp; |
426 | |
427 | corr = isl29501->shadow_coeffs[i]; |
428 | corr_exp = isl29501_find_corr_exp(val: corr, |
429 | ISL29501_MAX_EXP_VAL, |
430 | U8_MAX / 2); |
431 | dev_dbg(&isl29501->client->dev, |
432 | "found exp of corr(%d) = %d\n" , corr, corr_exp); |
433 | |
434 | max_exp = max(max_exp, corr_exp); |
435 | } |
436 | |
437 | /* |
438 | * Represent every correction coefficient under |
439 | * mantissa * 2^max_exponent form and force the |
440 | * writing of those coefficients on the sensor. |
441 | */ |
442 | for (i = 0; i < COEFF_MAX; i++) { |
443 | int corr; |
444 | int mantissa; |
445 | |
446 | corr = isl29501->shadow_coeffs[i]; |
447 | if (!corr) |
448 | continue; |
449 | |
450 | mantissa = corr >> max_exp; |
451 | |
452 | ret = isl29501_write_coeff(isl29501, coeff: i, val: mantissa); |
453 | if (ret < 0) |
454 | return ret; |
455 | } |
456 | |
457 | ret = isl29501_register_write(isl29501, name: REG_PHASE_EXP, value: max_exp); |
458 | if (ret < 0) |
459 | return ret; |
460 | |
461 | break; |
462 | default: |
463 | return -EINVAL; |
464 | } |
465 | |
466 | return len; |
467 | } |
468 | |
469 | #define _ISL29501_EXT_INFO(_name, _ident) { \ |
470 | .name = _name, \ |
471 | .read = isl29501_read_ext, \ |
472 | .write = isl29501_write_ext, \ |
473 | .private = _ident, \ |
474 | .shared = IIO_SEPARATE, \ |
475 | } |
476 | |
477 | static const struct iio_chan_spec_ext_info isl29501_ext_info[] = { |
478 | _ISL29501_EXT_INFO("agc_gain" , REG_GAIN), |
479 | _ISL29501_EXT_INFO("agc_gain_bias" , REG_GAIN_BIAS), |
480 | _ISL29501_EXT_INFO("calib_phase_temp_a" , REG_CALIB_PHASE_TEMP_A), |
481 | _ISL29501_EXT_INFO("calib_phase_temp_b" , REG_CALIB_PHASE_TEMP_B), |
482 | _ISL29501_EXT_INFO("calib_phase_light_a" , REG_CALIB_PHASE_LIGHT_A), |
483 | _ISL29501_EXT_INFO("calib_phase_light_b" , REG_CALIB_PHASE_LIGHT_B), |
484 | { }, |
485 | }; |
486 | |
487 | #define ISL29501_DISTANCE_SCAN_INDEX 0 |
488 | #define ISL29501_TIMESTAMP_SCAN_INDEX 1 |
489 | |
490 | static const struct iio_chan_spec isl29501_channels[] = { |
491 | { |
492 | .type = IIO_PROXIMITY, |
493 | .scan_index = ISL29501_DISTANCE_SCAN_INDEX, |
494 | .info_mask_separate = |
495 | BIT(IIO_CHAN_INFO_RAW) | |
496 | BIT(IIO_CHAN_INFO_SCALE) | |
497 | BIT(IIO_CHAN_INFO_CALIBBIAS), |
498 | .scan_type = { |
499 | .sign = 'u', |
500 | .realbits = 16, |
501 | .storagebits = 16, |
502 | .endianness = IIO_CPU, |
503 | }, |
504 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | |
505 | BIT(IIO_CHAN_INFO_SAMP_FREQ), |
506 | .ext_info = isl29501_ext_info, |
507 | }, |
508 | { |
509 | .type = IIO_PHASE, |
510 | .scan_index = -1, |
511 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
512 | BIT(IIO_CHAN_INFO_SCALE), |
513 | }, |
514 | { |
515 | .type = IIO_CURRENT, |
516 | .scan_index = -1, |
517 | .output = 1, |
518 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
519 | BIT(IIO_CHAN_INFO_SCALE), |
520 | }, |
521 | { |
522 | .type = IIO_TEMP, |
523 | .scan_index = -1, |
524 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
525 | BIT(IIO_CHAN_INFO_SCALE) | |
526 | BIT(IIO_CHAN_INFO_CALIBBIAS), |
527 | }, |
528 | { |
529 | .type = IIO_INTENSITY, |
530 | .scan_index = -1, |
531 | .modified = 1, |
532 | .channel2 = IIO_MOD_LIGHT_CLEAR, |
533 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
534 | BIT(IIO_CHAN_INFO_SCALE), |
535 | }, |
536 | IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX), |
537 | }; |
538 | |
539 | static int isl29501_reset_registers(struct isl29501_private *isl29501) |
540 | { |
541 | int ret; |
542 | |
543 | ret = i2c_smbus_write_byte_data(client: isl29501->client, |
544 | ISL29501_COMMAND_REGISTER, |
545 | ISL29501_RESET_ALL_REGISTERS); |
546 | if (ret < 0) { |
547 | dev_err(&isl29501->client->dev, |
548 | "cannot reset registers %d\n" , ret); |
549 | return ret; |
550 | } |
551 | |
552 | ret = i2c_smbus_write_byte_data(client: isl29501->client, |
553 | ISL29501_COMMAND_REGISTER, |
554 | ISL29501_RESET_INT_SM); |
555 | if (ret < 0) |
556 | dev_err(&isl29501->client->dev, |
557 | "cannot reset state machine %d\n" , ret); |
558 | |
559 | return ret; |
560 | } |
561 | |
562 | static int isl29501_begin_acquisition(struct isl29501_private *isl29501) |
563 | { |
564 | int ret; |
565 | |
566 | ret = i2c_smbus_write_byte_data(client: isl29501->client, |
567 | ISL29501_COMMAND_REGISTER, |
568 | ISL29501_EMUL_SAMPLE_START_PIN); |
569 | if (ret < 0) |
570 | dev_err(&isl29501->client->dev, |
571 | "cannot begin acquisition %d\n" , ret); |
572 | |
573 | return ret; |
574 | } |
575 | |
576 | static IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE); |
577 | static IIO_CONST_ATTR(out_current_scale_available, |
578 | ISL29501_CURRENT_SCALE_AVAILABLE); |
579 | |
580 | static struct attribute *isl29501_attributes[] = { |
581 | &iio_const_attr_integration_time_available.dev_attr.attr, |
582 | &iio_const_attr_out_current_scale_available.dev_attr.attr, |
583 | NULL |
584 | }; |
585 | |
586 | static const struct attribute_group isl29501_attribute_group = { |
587 | .attrs = isl29501_attributes, |
588 | }; |
589 | |
590 | static const int isl29501_current_scale_table[][2] = { |
591 | {0, 3900}, {0, 7800}, {0, 11800}, {0, 15700}, |
592 | {0, 19600}, {0, 23500}, {0, 27500}, {0, 31400}, |
593 | {0, 35200}, {0, 39200}, {0, 43100}, {0, 47100}, |
594 | {0, 51000}, {0, 54900}, {0, 58800}, |
595 | }; |
596 | |
597 | static const int isl29501_int_time[][2] = { |
598 | {0, 70}, /* 0.07 ms */ |
599 | {0, 140}, /* 0.14 ms */ |
600 | {0, 280}, /* 0.28 ms */ |
601 | {0, 570}, /* 0.57 ms */ |
602 | {0, 1140}, /* 1.14 ms */ |
603 | {0, 2280}, /* 2.28 ms */ |
604 | {0, 4550}, /* 4.55 ms */ |
605 | {0, 9100}, /* 9.11 ms */ |
606 | {0, 18200}, /* 18.2 ms */ |
607 | {0, 36400}, /* 36.4 ms */ |
608 | {0, 72810}, /* 72.81 ms */ |
609 | {0, 145610} /* 145.28 ms */ |
610 | }; |
611 | |
612 | static int isl29501_get_raw(struct isl29501_private *isl29501, |
613 | const struct iio_chan_spec *chan, |
614 | int *raw) |
615 | { |
616 | int ret; |
617 | |
618 | switch (chan->type) { |
619 | case IIO_PROXIMITY: |
620 | ret = isl29501_register_read(isl29501, name: REG_DISTANCE, val: raw); |
621 | if (ret < 0) |
622 | return ret; |
623 | |
624 | return IIO_VAL_INT; |
625 | case IIO_INTENSITY: |
626 | ret = isl29501_register_read(isl29501, |
627 | name: REG_AMBIENT_LIGHT, |
628 | val: raw); |
629 | if (ret < 0) |
630 | return ret; |
631 | |
632 | return IIO_VAL_INT; |
633 | case IIO_PHASE: |
634 | ret = isl29501_register_read(isl29501, name: REG_PHASE, val: raw); |
635 | if (ret < 0) |
636 | return ret; |
637 | |
638 | return IIO_VAL_INT; |
639 | case IIO_CURRENT: |
640 | ret = isl29501_register_read(isl29501, name: REG_EMITTER_DAC, val: raw); |
641 | if (ret < 0) |
642 | return ret; |
643 | |
644 | return IIO_VAL_INT; |
645 | case IIO_TEMP: |
646 | ret = isl29501_register_read(isl29501, name: REG_TEMPERATURE, val: raw); |
647 | if (ret < 0) |
648 | return ret; |
649 | |
650 | return IIO_VAL_INT; |
651 | default: |
652 | return -EINVAL; |
653 | } |
654 | } |
655 | |
656 | static int isl29501_get_scale(struct isl29501_private *isl29501, |
657 | const struct iio_chan_spec *chan, |
658 | int *val, int *val2) |
659 | { |
660 | int ret; |
661 | u32 current_scale; |
662 | |
663 | switch (chan->type) { |
664 | case IIO_PROXIMITY: |
665 | /* distance = raw_distance * 33.31 / 65536 (m) */ |
666 | *val = 3331; |
667 | *val2 = 6553600; |
668 | |
669 | return IIO_VAL_FRACTIONAL; |
670 | case IIO_PHASE: |
671 | /* phase = raw_phase * 2pi / 65536 (rad) */ |
672 | *val = 0; |
673 | *val2 = 95874; |
674 | |
675 | return IIO_VAL_INT_PLUS_NANO; |
676 | case IIO_INTENSITY: |
677 | /* light = raw_light * 35 / 10000 (mA) */ |
678 | *val = 35; |
679 | *val2 = 10000; |
680 | |
681 | return IIO_VAL_FRACTIONAL; |
682 | case IIO_CURRENT: |
683 | ret = isl29501_register_read(isl29501, |
684 | name: REG_DRIVER_RANGE, |
685 | val: ¤t_scale); |
686 | if (ret < 0) |
687 | return ret; |
688 | |
689 | if (current_scale > ARRAY_SIZE(isl29501_current_scale_table)) |
690 | return -EINVAL; |
691 | |
692 | if (!current_scale) { |
693 | *val = 0; |
694 | *val2 = 0; |
695 | return IIO_VAL_INT; |
696 | } |
697 | |
698 | *val = isl29501_current_scale_table[current_scale - 1][0]; |
699 | *val2 = isl29501_current_scale_table[current_scale - 1][1]; |
700 | |
701 | return IIO_VAL_INT_PLUS_MICRO; |
702 | case IIO_TEMP: |
703 | /* temperature = raw_temperature * 125 / 100000 (milli °C) */ |
704 | *val = 125; |
705 | *val2 = 100000; |
706 | |
707 | return IIO_VAL_FRACTIONAL; |
708 | default: |
709 | return -EINVAL; |
710 | } |
711 | } |
712 | |
713 | static int isl29501_get_calibbias(struct isl29501_private *isl29501, |
714 | const struct iio_chan_spec *chan, |
715 | int *bias) |
716 | { |
717 | switch (chan->type) { |
718 | case IIO_PROXIMITY: |
719 | return isl29501_register_read(isl29501, |
720 | name: REG_DISTANCE_BIAS, |
721 | val: bias); |
722 | case IIO_TEMP: |
723 | return isl29501_register_read(isl29501, |
724 | name: REG_TEMPERATURE_BIAS, |
725 | val: bias); |
726 | default: |
727 | return -EINVAL; |
728 | } |
729 | } |
730 | |
731 | static int isl29501_get_inttime(struct isl29501_private *isl29501, |
732 | int *val, int *val2) |
733 | { |
734 | int ret; |
735 | u32 inttime; |
736 | |
737 | ret = isl29501_register_read(isl29501, name: REG_INT_TIME, val: &inttime); |
738 | if (ret < 0) |
739 | return ret; |
740 | |
741 | if (inttime >= ARRAY_SIZE(isl29501_int_time)) |
742 | return -EINVAL; |
743 | |
744 | *val = isl29501_int_time[inttime][0]; |
745 | *val2 = isl29501_int_time[inttime][1]; |
746 | |
747 | return IIO_VAL_INT_PLUS_MICRO; |
748 | } |
749 | |
750 | static int isl29501_get_freq(struct isl29501_private *isl29501, |
751 | int *val, int *val2) |
752 | { |
753 | int ret; |
754 | int sample_time; |
755 | unsigned long long freq; |
756 | u32 temp; |
757 | |
758 | ret = isl29501_register_read(isl29501, name: REG_SAMPLE_TIME, val: &sample_time); |
759 | if (ret < 0) |
760 | return ret; |
761 | |
762 | /* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ |
763 | freq = 1000000ULL * 1000000ULL; |
764 | |
765 | do_div(freq, 450 * (sample_time + 1)); |
766 | |
767 | temp = do_div(freq, 1000000); |
768 | *val = freq; |
769 | *val2 = temp; |
770 | |
771 | return IIO_VAL_INT_PLUS_MICRO; |
772 | } |
773 | |
774 | static int isl29501_read_raw(struct iio_dev *indio_dev, |
775 | struct iio_chan_spec const *chan, int *val, |
776 | int *val2, long mask) |
777 | { |
778 | struct isl29501_private *isl29501 = iio_priv(indio_dev); |
779 | |
780 | switch (mask) { |
781 | case IIO_CHAN_INFO_RAW: |
782 | return isl29501_get_raw(isl29501, chan, raw: val); |
783 | case IIO_CHAN_INFO_SCALE: |
784 | return isl29501_get_scale(isl29501, chan, val, val2); |
785 | case IIO_CHAN_INFO_INT_TIME: |
786 | return isl29501_get_inttime(isl29501, val, val2); |
787 | case IIO_CHAN_INFO_SAMP_FREQ: |
788 | return isl29501_get_freq(isl29501, val, val2); |
789 | case IIO_CHAN_INFO_CALIBBIAS: |
790 | return isl29501_get_calibbias(isl29501, chan, bias: val); |
791 | default: |
792 | return -EINVAL; |
793 | } |
794 | } |
795 | |
796 | static int isl29501_set_raw(struct isl29501_private *isl29501, |
797 | const struct iio_chan_spec *chan, |
798 | int raw) |
799 | { |
800 | switch (chan->type) { |
801 | case IIO_CURRENT: |
802 | return isl29501_register_write(isl29501, name: REG_EMITTER_DAC, value: raw); |
803 | default: |
804 | return -EINVAL; |
805 | } |
806 | } |
807 | |
808 | static int isl29501_set_inttime(struct isl29501_private *isl29501, |
809 | int val, int val2) |
810 | { |
811 | int i; |
812 | |
813 | for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) { |
814 | if (isl29501_int_time[i][0] == val && |
815 | isl29501_int_time[i][1] == val2) { |
816 | return isl29501_register_write(isl29501, |
817 | name: REG_INT_TIME, |
818 | value: i); |
819 | } |
820 | } |
821 | |
822 | return -EINVAL; |
823 | } |
824 | |
825 | static int isl29501_set_scale(struct isl29501_private *isl29501, |
826 | const struct iio_chan_spec *chan, |
827 | int val, int val2) |
828 | { |
829 | int i; |
830 | |
831 | if (chan->type != IIO_CURRENT) |
832 | return -EINVAL; |
833 | |
834 | for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) { |
835 | if (isl29501_current_scale_table[i][0] == val && |
836 | isl29501_current_scale_table[i][1] == val2) { |
837 | return isl29501_register_write(isl29501, |
838 | name: REG_DRIVER_RANGE, |
839 | value: i + 1); |
840 | } |
841 | } |
842 | |
843 | return -EINVAL; |
844 | } |
845 | |
846 | static int isl29501_set_calibbias(struct isl29501_private *isl29501, |
847 | const struct iio_chan_spec *chan, |
848 | int bias) |
849 | { |
850 | switch (chan->type) { |
851 | case IIO_PROXIMITY: |
852 | return isl29501_register_write(isl29501, |
853 | name: REG_DISTANCE_BIAS, |
854 | value: bias); |
855 | case IIO_TEMP: |
856 | return isl29501_register_write(isl29501, |
857 | name: REG_TEMPERATURE_BIAS, |
858 | value: bias); |
859 | default: |
860 | return -EINVAL; |
861 | } |
862 | } |
863 | |
864 | static int isl29501_set_freq(struct isl29501_private *isl29501, |
865 | int val, int val2) |
866 | { |
867 | int freq; |
868 | unsigned long long sample_time; |
869 | |
870 | /* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ |
871 | freq = val * 1000000 + val2 % 1000000; |
872 | sample_time = 2222ULL * 1000000ULL; |
873 | do_div(sample_time, freq); |
874 | |
875 | sample_time -= 1; |
876 | |
877 | if (sample_time > 255) |
878 | return -ERANGE; |
879 | |
880 | return isl29501_register_write(isl29501, name: REG_SAMPLE_TIME, value: sample_time); |
881 | } |
882 | |
883 | static int isl29501_write_raw(struct iio_dev *indio_dev, |
884 | struct iio_chan_spec const *chan, |
885 | int val, int val2, long mask) |
886 | { |
887 | struct isl29501_private *isl29501 = iio_priv(indio_dev); |
888 | |
889 | switch (mask) { |
890 | case IIO_CHAN_INFO_RAW: |
891 | return isl29501_set_raw(isl29501, chan, raw: val); |
892 | case IIO_CHAN_INFO_INT_TIME: |
893 | return isl29501_set_inttime(isl29501, val, val2); |
894 | case IIO_CHAN_INFO_SAMP_FREQ: |
895 | return isl29501_set_freq(isl29501, val, val2); |
896 | case IIO_CHAN_INFO_SCALE: |
897 | return isl29501_set_scale(isl29501, chan, val, val2); |
898 | case IIO_CHAN_INFO_CALIBBIAS: |
899 | return isl29501_set_calibbias(isl29501, chan, bias: val); |
900 | default: |
901 | return -EINVAL; |
902 | } |
903 | } |
904 | |
905 | static const struct iio_info isl29501_info = { |
906 | .read_raw = &isl29501_read_raw, |
907 | .write_raw = &isl29501_write_raw, |
908 | .attrs = &isl29501_attribute_group, |
909 | }; |
910 | |
911 | static int isl29501_init_chip(struct isl29501_private *isl29501) |
912 | { |
913 | int ret; |
914 | |
915 | ret = i2c_smbus_read_byte_data(client: isl29501->client, ISL29501_DEVICE_ID); |
916 | if (ret < 0) { |
917 | dev_err(&isl29501->client->dev, "Error reading device id\n" ); |
918 | return ret; |
919 | } |
920 | |
921 | if (ret != ISL29501_ID) { |
922 | dev_err(&isl29501->client->dev, |
923 | "Wrong chip id, got %x expected %x\n" , |
924 | ret, ISL29501_DEVICE_ID); |
925 | return -ENODEV; |
926 | } |
927 | |
928 | ret = isl29501_reset_registers(isl29501); |
929 | if (ret < 0) |
930 | return ret; |
931 | |
932 | return isl29501_begin_acquisition(isl29501); |
933 | } |
934 | |
935 | static irqreturn_t isl29501_trigger_handler(int irq, void *p) |
936 | { |
937 | struct iio_poll_func *pf = p; |
938 | struct iio_dev *indio_dev = pf->indio_dev; |
939 | struct isl29501_private *isl29501 = iio_priv(indio_dev); |
940 | const unsigned long *active_mask = indio_dev->active_scan_mask; |
941 | u32 buffer[4] __aligned(8) = {}; /* 1x16-bit + naturally aligned ts */ |
942 | |
943 | if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask)) |
944 | isl29501_register_read(isl29501, name: REG_DISTANCE, val: buffer); |
945 | |
946 | iio_push_to_buffers_with_timestamp(indio_dev, data: buffer, timestamp: pf->timestamp); |
947 | iio_trigger_notify_done(trig: indio_dev->trig); |
948 | |
949 | return IRQ_HANDLED; |
950 | } |
951 | |
952 | static int isl29501_probe(struct i2c_client *client) |
953 | { |
954 | struct iio_dev *indio_dev; |
955 | struct isl29501_private *isl29501; |
956 | int ret; |
957 | |
958 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*isl29501)); |
959 | if (!indio_dev) |
960 | return -ENOMEM; |
961 | |
962 | isl29501 = iio_priv(indio_dev); |
963 | |
964 | i2c_set_clientdata(client, data: indio_dev); |
965 | isl29501->client = client; |
966 | |
967 | mutex_init(&isl29501->lock); |
968 | |
969 | ret = isl29501_init_chip(isl29501); |
970 | if (ret < 0) |
971 | return ret; |
972 | |
973 | indio_dev->modes = INDIO_DIRECT_MODE; |
974 | indio_dev->channels = isl29501_channels; |
975 | indio_dev->num_channels = ARRAY_SIZE(isl29501_channels); |
976 | indio_dev->name = client->name; |
977 | indio_dev->info = &isl29501_info; |
978 | |
979 | ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, |
980 | iio_pollfunc_store_time, |
981 | isl29501_trigger_handler, |
982 | NULL); |
983 | if (ret < 0) { |
984 | dev_err(&client->dev, "unable to setup iio triggered buffer\n" ); |
985 | return ret; |
986 | } |
987 | |
988 | return devm_iio_device_register(&client->dev, indio_dev); |
989 | } |
990 | |
991 | static const struct i2c_device_id isl29501_id[] = { |
992 | {"isl29501" , 0}, |
993 | {} |
994 | }; |
995 | |
996 | MODULE_DEVICE_TABLE(i2c, isl29501_id); |
997 | |
998 | #if defined(CONFIG_OF) |
999 | static const struct of_device_id isl29501_i2c_matches[] = { |
1000 | { .compatible = "renesas,isl29501" }, |
1001 | { } |
1002 | }; |
1003 | MODULE_DEVICE_TABLE(of, isl29501_i2c_matches); |
1004 | #endif |
1005 | |
1006 | static struct i2c_driver isl29501_driver = { |
1007 | .driver = { |
1008 | .name = "isl29501" , |
1009 | }, |
1010 | .id_table = isl29501_id, |
1011 | .probe = isl29501_probe, |
1012 | }; |
1013 | module_i2c_driver(isl29501_driver); |
1014 | |
1015 | MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>" ); |
1016 | MODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver" ); |
1017 | MODULE_LICENSE("GPL v2" ); |
1018 | |