11c287992SMathieu Othacehe // SPDX-License-Identifier: GPL-2.0 21c287992SMathieu Othacehe /* 31c287992SMathieu Othacehe * isl29501.c: ISL29501 Time of Flight sensor driver. 41c287992SMathieu Othacehe * 51c287992SMathieu Othacehe * Copyright (C) 2018 61c287992SMathieu Othacehe * Author: Mathieu Othacehe <m.othacehe@gmail.com> 71c287992SMathieu Othacehe * 81c287992SMathieu Othacehe * 7-bit I2C slave address: 0x57 91c287992SMathieu Othacehe */ 101c287992SMathieu Othacehe 111c287992SMathieu Othacehe #include <linux/kernel.h> 121c287992SMathieu Othacehe #include <linux/module.h> 131c287992SMathieu Othacehe #include <linux/i2c.h> 141c287992SMathieu Othacehe #include <linux/err.h> 151c287992SMathieu Othacehe #include <linux/of_device.h> 161c287992SMathieu Othacehe #include <linux/iio/iio.h> 171c287992SMathieu Othacehe #include <linux/iio/sysfs.h> 181c287992SMathieu Othacehe 191c287992SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 201c287992SMathieu Othacehe #include <linux/iio/buffer.h> 211c287992SMathieu Othacehe #include <linux/iio/triggered_buffer.h> 221c287992SMathieu Othacehe 231c287992SMathieu Othacehe /* Control, setting and status registers */ 241c287992SMathieu Othacehe #define ISL29501_DEVICE_ID 0x00 251c287992SMathieu Othacehe #define ISL29501_ID 0x0A 261c287992SMathieu Othacehe 271c287992SMathieu Othacehe /* Sampling control registers */ 281c287992SMathieu Othacehe #define ISL29501_INTEGRATION_PERIOD 0x10 291c287992SMathieu Othacehe #define ISL29501_SAMPLE_PERIOD 0x11 301c287992SMathieu Othacehe 311c287992SMathieu Othacehe /* Closed loop calibration registers */ 321c287992SMathieu Othacehe #define ISL29501_CROSSTALK_I_MSB 0x24 331c287992SMathieu Othacehe #define ISL29501_CROSSTALK_I_LSB 0x25 341c287992SMathieu Othacehe #define ISL29501_CROSSTALK_I_EXPONENT 0x26 351c287992SMathieu Othacehe #define ISL29501_CROSSTALK_Q_MSB 0x27 361c287992SMathieu Othacehe #define ISL29501_CROSSTALK_Q_LSB 0x28 371c287992SMathieu Othacehe #define ISL29501_CROSSTALK_Q_EXPONENT 0x29 381c287992SMathieu Othacehe #define ISL29501_CROSSTALK_GAIN_MSB 0x2A 391c287992SMathieu Othacehe #define ISL29501_CROSSTALK_GAIN_LSB 0x2B 401c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_REF_EXP 0x2C 411c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_REF_MSB 0x2D 421c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_REF_LSB 0x2E 431c287992SMathieu Othacehe #define ISL29501_PHASE_OFFSET_MSB 0x2F 441c287992SMathieu Othacehe #define ISL29501_PHASE_OFFSET_LSB 0x30 451c287992SMathieu Othacehe 461c287992SMathieu Othacehe /* Analog control registers */ 471c287992SMathieu Othacehe #define ISL29501_DRIVER_RANGE 0x90 481c287992SMathieu Othacehe #define ISL29501_EMITTER_DAC 0x91 491c287992SMathieu Othacehe 501c287992SMathieu Othacehe #define ISL29501_COMMAND_REGISTER 0xB0 511c287992SMathieu Othacehe 521c287992SMathieu Othacehe /* Commands */ 531c287992SMathieu Othacehe #define ISL29501_EMUL_SAMPLE_START_PIN 0x49 541c287992SMathieu Othacehe #define ISL29501_RESET_ALL_REGISTERS 0xD7 551c287992SMathieu Othacehe #define ISL29501_RESET_INT_SM 0xD1 561c287992SMathieu Othacehe 571c287992SMathieu Othacehe /* Ambiant light and temperature corrections */ 581c287992SMathieu Othacehe #define ISL29501_TEMP_REFERENCE 0x31 591c287992SMathieu Othacehe #define ISL29501_PHASE_EXPONENT 0x33 601c287992SMathieu Othacehe #define ISL29501_TEMP_COEFF_A 0x34 611c287992SMathieu Othacehe #define ISL29501_TEMP_COEFF_B 0x39 621c287992SMathieu Othacehe #define ISL29501_AMBIANT_COEFF_A 0x36 631c287992SMathieu Othacehe #define ISL29501_AMBIANT_COEFF_B 0x3B 641c287992SMathieu Othacehe 651c287992SMathieu Othacehe /* Data output registers */ 661c287992SMathieu Othacehe #define ISL29501_DISTANCE_MSB_DATA 0xD1 671c287992SMathieu Othacehe #define ISL29501_DISTANCE_LSB_DATA 0xD2 681c287992SMathieu Othacehe #define ISL29501_PRECISION_MSB 0xD3 691c287992SMathieu Othacehe #define ISL29501_PRECISION_LSB 0xD4 701c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_EXPONENT 0xD5 711c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_MSB 0xD6 721c287992SMathieu Othacehe #define ISL29501_MAGNITUDE_LSB 0xD7 731c287992SMathieu Othacehe #define ISL29501_PHASE_MSB 0xD8 741c287992SMathieu Othacehe #define ISL29501_PHASE_LSB 0xD9 751c287992SMathieu Othacehe #define ISL29501_I_RAW_EXPONENT 0xDA 761c287992SMathieu Othacehe #define ISL29501_I_RAW_MSB 0xDB 771c287992SMathieu Othacehe #define ISL29501_I_RAW_LSB 0xDC 781c287992SMathieu Othacehe #define ISL29501_Q_RAW_EXPONENT 0xDD 791c287992SMathieu Othacehe #define ISL29501_Q_RAW_MSB 0xDE 801c287992SMathieu Othacehe #define ISL29501_Q_RAW_LSB 0xDF 811c287992SMathieu Othacehe #define ISL29501_DIE_TEMPERATURE 0xE2 821c287992SMathieu Othacehe #define ISL29501_AMBIENT_LIGHT 0xE3 831c287992SMathieu Othacehe #define ISL29501_GAIN_MSB 0xE6 841c287992SMathieu Othacehe #define ISL29501_GAIN_LSB 0xE7 851c287992SMathieu Othacehe 861c287992SMathieu Othacehe #define ISL29501_MAX_EXP_VAL 15 871c287992SMathieu Othacehe 881c287992SMathieu Othacehe #define ISL29501_INT_TIME_AVAILABLE \ 891c287992SMathieu Othacehe "0.00007 0.00014 0.00028 0.00057 0.00114 " \ 901c287992SMathieu Othacehe "0.00228 0.00455 0.00910 0.01820 0.03640 " \ 911c287992SMathieu Othacehe "0.07281 0.14561" 921c287992SMathieu Othacehe 931c287992SMathieu Othacehe #define ISL29501_CURRENT_SCALE_AVAILABLE \ 941c287992SMathieu Othacehe "0.0039 0.0078 0.0118 0.0157 0.0196 " \ 951c287992SMathieu Othacehe "0.0235 0.0275 0.0314 0.0352 0.0392 " \ 961c287992SMathieu Othacehe "0.0431 0.0471 0.0510 0.0549 0.0588" 971c287992SMathieu Othacehe 981c287992SMathieu Othacehe enum isl29501_correction_coeff { 991c287992SMathieu Othacehe COEFF_TEMP_A, 1001c287992SMathieu Othacehe COEFF_TEMP_B, 1011c287992SMathieu Othacehe COEFF_LIGHT_A, 1021c287992SMathieu Othacehe COEFF_LIGHT_B, 1031c287992SMathieu Othacehe COEFF_MAX, 1041c287992SMathieu Othacehe }; 1051c287992SMathieu Othacehe 1061c287992SMathieu Othacehe struct isl29501_private { 1071c287992SMathieu Othacehe struct i2c_client *client; 1081c287992SMathieu Othacehe struct mutex lock; 1091c287992SMathieu Othacehe /* Exact representation of correction coefficients. */ 1101c287992SMathieu Othacehe unsigned int shadow_coeffs[COEFF_MAX]; 1111c287992SMathieu Othacehe }; 1121c287992SMathieu Othacehe 1131c287992SMathieu Othacehe enum isl29501_register_name { 1141c287992SMathieu Othacehe REG_DISTANCE, 1151c287992SMathieu Othacehe REG_PHASE, 1161c287992SMathieu Othacehe REG_TEMPERATURE, 1171c287992SMathieu Othacehe REG_AMBIENT_LIGHT, 1181c287992SMathieu Othacehe REG_GAIN, 1191c287992SMathieu Othacehe REG_GAIN_BIAS, 1201c287992SMathieu Othacehe REG_PHASE_EXP, 1211c287992SMathieu Othacehe REG_CALIB_PHASE_TEMP_A, 1221c287992SMathieu Othacehe REG_CALIB_PHASE_TEMP_B, 1231c287992SMathieu Othacehe REG_CALIB_PHASE_LIGHT_A, 1241c287992SMathieu Othacehe REG_CALIB_PHASE_LIGHT_B, 1251c287992SMathieu Othacehe REG_DISTANCE_BIAS, 1261c287992SMathieu Othacehe REG_TEMPERATURE_BIAS, 1271c287992SMathieu Othacehe REG_INT_TIME, 1281c287992SMathieu Othacehe REG_SAMPLE_TIME, 1291c287992SMathieu Othacehe REG_DRIVER_RANGE, 1301c287992SMathieu Othacehe REG_EMITTER_DAC, 1311c287992SMathieu Othacehe }; 1321c287992SMathieu Othacehe 1331c287992SMathieu Othacehe struct isl29501_register_desc { 1341c287992SMathieu Othacehe u8 msb; 1351c287992SMathieu Othacehe u8 lsb; 1361c287992SMathieu Othacehe }; 1371c287992SMathieu Othacehe 1381c287992SMathieu Othacehe static const struct isl29501_register_desc isl29501_registers[] = { 1391c287992SMathieu Othacehe [REG_DISTANCE] = { 1401c287992SMathieu Othacehe .msb = ISL29501_DISTANCE_MSB_DATA, 1411c287992SMathieu Othacehe .lsb = ISL29501_DISTANCE_LSB_DATA, 1421c287992SMathieu Othacehe }, 1431c287992SMathieu Othacehe [REG_PHASE] = { 1441c287992SMathieu Othacehe .msb = ISL29501_PHASE_MSB, 1451c287992SMathieu Othacehe .lsb = ISL29501_PHASE_LSB, 1461c287992SMathieu Othacehe }, 1471c287992SMathieu Othacehe [REG_TEMPERATURE] = { 1481c287992SMathieu Othacehe .lsb = ISL29501_DIE_TEMPERATURE, 1491c287992SMathieu Othacehe }, 1501c287992SMathieu Othacehe [REG_AMBIENT_LIGHT] = { 1511c287992SMathieu Othacehe .lsb = ISL29501_AMBIENT_LIGHT, 1521c287992SMathieu Othacehe }, 1531c287992SMathieu Othacehe [REG_GAIN] = { 1541c287992SMathieu Othacehe .msb = ISL29501_GAIN_MSB, 1551c287992SMathieu Othacehe .lsb = ISL29501_GAIN_LSB, 1561c287992SMathieu Othacehe }, 1571c287992SMathieu Othacehe [REG_GAIN_BIAS] = { 1581c287992SMathieu Othacehe .msb = ISL29501_CROSSTALK_GAIN_MSB, 1591c287992SMathieu Othacehe .lsb = ISL29501_CROSSTALK_GAIN_LSB, 1601c287992SMathieu Othacehe }, 1611c287992SMathieu Othacehe [REG_PHASE_EXP] = { 1621c287992SMathieu Othacehe .lsb = ISL29501_PHASE_EXPONENT, 1631c287992SMathieu Othacehe }, 1641c287992SMathieu Othacehe [REG_CALIB_PHASE_TEMP_A] = { 1651c287992SMathieu Othacehe .lsb = ISL29501_TEMP_COEFF_A, 1661c287992SMathieu Othacehe }, 1671c287992SMathieu Othacehe [REG_CALIB_PHASE_TEMP_B] = { 1681c287992SMathieu Othacehe .lsb = ISL29501_TEMP_COEFF_B, 1691c287992SMathieu Othacehe }, 1701c287992SMathieu Othacehe [REG_CALIB_PHASE_LIGHT_A] = { 1711c287992SMathieu Othacehe .lsb = ISL29501_AMBIANT_COEFF_A, 1721c287992SMathieu Othacehe }, 1731c287992SMathieu Othacehe [REG_CALIB_PHASE_LIGHT_B] = { 1741c287992SMathieu Othacehe .lsb = ISL29501_AMBIANT_COEFF_B, 1751c287992SMathieu Othacehe }, 1761c287992SMathieu Othacehe [REG_DISTANCE_BIAS] = { 1771c287992SMathieu Othacehe .msb = ISL29501_PHASE_OFFSET_MSB, 1781c287992SMathieu Othacehe .lsb = ISL29501_PHASE_OFFSET_LSB, 1791c287992SMathieu Othacehe }, 1801c287992SMathieu Othacehe [REG_TEMPERATURE_BIAS] = { 1811c287992SMathieu Othacehe .lsb = ISL29501_TEMP_REFERENCE, 1821c287992SMathieu Othacehe }, 1831c287992SMathieu Othacehe [REG_INT_TIME] = { 1841c287992SMathieu Othacehe .lsb = ISL29501_INTEGRATION_PERIOD, 1851c287992SMathieu Othacehe }, 1861c287992SMathieu Othacehe [REG_SAMPLE_TIME] = { 1871c287992SMathieu Othacehe .lsb = ISL29501_SAMPLE_PERIOD, 1881c287992SMathieu Othacehe }, 1891c287992SMathieu Othacehe [REG_DRIVER_RANGE] = { 1901c287992SMathieu Othacehe .lsb = ISL29501_DRIVER_RANGE, 1911c287992SMathieu Othacehe }, 1921c287992SMathieu Othacehe [REG_EMITTER_DAC] = { 1931c287992SMathieu Othacehe .lsb = ISL29501_EMITTER_DAC, 1941c287992SMathieu Othacehe }, 1951c287992SMathieu Othacehe }; 1961c287992SMathieu Othacehe 1971c287992SMathieu Othacehe static int isl29501_register_read(struct isl29501_private *isl29501, 1981c287992SMathieu Othacehe enum isl29501_register_name name, 1991c287992SMathieu Othacehe u32 *val) 2001c287992SMathieu Othacehe { 2011c287992SMathieu Othacehe const struct isl29501_register_desc *reg = &isl29501_registers[name]; 2021c287992SMathieu Othacehe u8 msb = 0, lsb = 0; 2031c287992SMathieu Othacehe s32 ret; 2041c287992SMathieu Othacehe 2051c287992SMathieu Othacehe mutex_lock(&isl29501->lock); 2061c287992SMathieu Othacehe if (reg->msb) { 2071c287992SMathieu Othacehe ret = i2c_smbus_read_byte_data(isl29501->client, reg->msb); 2081c287992SMathieu Othacehe if (ret < 0) 2091c287992SMathieu Othacehe goto err; 2101c287992SMathieu Othacehe msb = ret; 2111c287992SMathieu Othacehe } 2121c287992SMathieu Othacehe 2131c287992SMathieu Othacehe if (reg->lsb) { 2141c287992SMathieu Othacehe ret = i2c_smbus_read_byte_data(isl29501->client, reg->lsb); 2151c287992SMathieu Othacehe if (ret < 0) 2161c287992SMathieu Othacehe goto err; 2171c287992SMathieu Othacehe lsb = ret; 2181c287992SMathieu Othacehe } 2191c287992SMathieu Othacehe mutex_unlock(&isl29501->lock); 2201c287992SMathieu Othacehe 2211c287992SMathieu Othacehe *val = (msb << 8) + lsb; 2221c287992SMathieu Othacehe 2231c287992SMathieu Othacehe return 0; 2241c287992SMathieu Othacehe err: 2251c287992SMathieu Othacehe mutex_unlock(&isl29501->lock); 2261c287992SMathieu Othacehe 2271c287992SMathieu Othacehe return ret; 2281c287992SMathieu Othacehe } 2291c287992SMathieu Othacehe 2301c287992SMathieu Othacehe static u32 isl29501_register_write(struct isl29501_private *isl29501, 2311c287992SMathieu Othacehe enum isl29501_register_name name, 2321c287992SMathieu Othacehe u32 value) 2331c287992SMathieu Othacehe { 2341c287992SMathieu Othacehe const struct isl29501_register_desc *reg = &isl29501_registers[name]; 2351c287992SMathieu Othacehe int ret; 2361c287992SMathieu Othacehe 2371c287992SMathieu Othacehe if (!reg->msb && value > U8_MAX) 2381c287992SMathieu Othacehe return -ERANGE; 2391c287992SMathieu Othacehe 2401c287992SMathieu Othacehe if (value > U16_MAX) 2411c287992SMathieu Othacehe return -ERANGE; 2421c287992SMathieu Othacehe 2431c287992SMathieu Othacehe mutex_lock(&isl29501->lock); 2441c287992SMathieu Othacehe if (reg->msb) { 2451c287992SMathieu Othacehe ret = i2c_smbus_write_byte_data(isl29501->client, 24624493cceSGeert Uytterhoeven reg->msb, value >> 8); 2471c287992SMathieu Othacehe if (ret < 0) 2481c287992SMathieu Othacehe goto err; 2491c287992SMathieu Othacehe } 2501c287992SMathieu Othacehe 25124493cceSGeert Uytterhoeven ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value); 2521c287992SMathieu Othacehe 2531c287992SMathieu Othacehe err: 2541c287992SMathieu Othacehe mutex_unlock(&isl29501->lock); 2551c287992SMathieu Othacehe return ret; 2561c287992SMathieu Othacehe } 2571c287992SMathieu Othacehe 2581c287992SMathieu Othacehe static ssize_t isl29501_read_ext(struct iio_dev *indio_dev, 2591c287992SMathieu Othacehe uintptr_t private, 2601c287992SMathieu Othacehe const struct iio_chan_spec *chan, 2611c287992SMathieu Othacehe char *buf) 2621c287992SMathieu Othacehe { 2631c287992SMathieu Othacehe struct isl29501_private *isl29501 = iio_priv(indio_dev); 2641c287992SMathieu Othacehe enum isl29501_register_name reg = private; 2651c287992SMathieu Othacehe int ret; 2661c287992SMathieu Othacehe u32 value, gain, coeff, exp; 2671c287992SMathieu Othacehe 2681c287992SMathieu Othacehe switch (reg) { 2691c287992SMathieu Othacehe case REG_GAIN: 2701c287992SMathieu Othacehe case REG_GAIN_BIAS: 2711c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, reg, &gain); 2721c287992SMathieu Othacehe if (ret < 0) 2731c287992SMathieu Othacehe return ret; 2741c287992SMathieu Othacehe 2751c287992SMathieu Othacehe value = gain; 2761c287992SMathieu Othacehe break; 2771c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_A: 2781c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_B: 2791c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_A: 2801c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_B: 2811c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_PHASE_EXP, &exp); 2821c287992SMathieu Othacehe if (ret < 0) 2831c287992SMathieu Othacehe return ret; 2841c287992SMathieu Othacehe 2851c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, reg, &coeff); 2861c287992SMathieu Othacehe if (ret < 0) 2871c287992SMathieu Othacehe return ret; 2881c287992SMathieu Othacehe 2891c287992SMathieu Othacehe value = coeff << exp; 2901c287992SMathieu Othacehe break; 2911c287992SMathieu Othacehe default: 2921c287992SMathieu Othacehe return -EINVAL; 2931c287992SMathieu Othacehe } 2941c287992SMathieu Othacehe 2951c287992SMathieu Othacehe return sprintf(buf, "%u\n", value); 2961c287992SMathieu Othacehe } 2971c287992SMathieu Othacehe 2981c287992SMathieu Othacehe static int isl29501_set_shadow_coeff(struct isl29501_private *isl29501, 2991c287992SMathieu Othacehe enum isl29501_register_name reg, 3001c287992SMathieu Othacehe unsigned int val) 3011c287992SMathieu Othacehe { 3021c287992SMathieu Othacehe enum isl29501_correction_coeff coeff; 3031c287992SMathieu Othacehe 3041c287992SMathieu Othacehe switch (reg) { 3051c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_A: 3061c287992SMathieu Othacehe coeff = COEFF_TEMP_A; 3071c287992SMathieu Othacehe break; 3081c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_B: 3091c287992SMathieu Othacehe coeff = COEFF_TEMP_B; 3101c287992SMathieu Othacehe break; 3111c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_A: 3121c287992SMathieu Othacehe coeff = COEFF_LIGHT_A; 3131c287992SMathieu Othacehe break; 3141c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_B: 3151c287992SMathieu Othacehe coeff = COEFF_LIGHT_B; 3161c287992SMathieu Othacehe break; 3171c287992SMathieu Othacehe default: 3181c287992SMathieu Othacehe return -EINVAL; 3191c287992SMathieu Othacehe } 3201c287992SMathieu Othacehe isl29501->shadow_coeffs[coeff] = val; 3211c287992SMathieu Othacehe 3221c287992SMathieu Othacehe return 0; 3231c287992SMathieu Othacehe } 3241c287992SMathieu Othacehe 3251c287992SMathieu Othacehe static int isl29501_write_coeff(struct isl29501_private *isl29501, 3261c287992SMathieu Othacehe enum isl29501_correction_coeff coeff, 3271c287992SMathieu Othacehe int val) 3281c287992SMathieu Othacehe { 3291c287992SMathieu Othacehe enum isl29501_register_name reg; 3301c287992SMathieu Othacehe 3311c287992SMathieu Othacehe switch (coeff) { 3321c287992SMathieu Othacehe case COEFF_TEMP_A: 3331c287992SMathieu Othacehe reg = REG_CALIB_PHASE_TEMP_A; 3341c287992SMathieu Othacehe break; 3351c287992SMathieu Othacehe case COEFF_TEMP_B: 3361c287992SMathieu Othacehe reg = REG_CALIB_PHASE_TEMP_B; 3371c287992SMathieu Othacehe break; 3381c287992SMathieu Othacehe case COEFF_LIGHT_A: 3391c287992SMathieu Othacehe reg = REG_CALIB_PHASE_LIGHT_A; 3401c287992SMathieu Othacehe break; 3411c287992SMathieu Othacehe case COEFF_LIGHT_B: 3421c287992SMathieu Othacehe reg = REG_CALIB_PHASE_LIGHT_B; 3431c287992SMathieu Othacehe break; 3441c287992SMathieu Othacehe default: 3451c287992SMathieu Othacehe return -EINVAL; 3461c287992SMathieu Othacehe } 3471c287992SMathieu Othacehe 3481c287992SMathieu Othacehe return isl29501_register_write(isl29501, reg, val); 3491c287992SMathieu Othacehe } 3501c287992SMathieu Othacehe 3511c287992SMathieu Othacehe static unsigned int isl29501_find_corr_exp(unsigned int val, 3521c287992SMathieu Othacehe unsigned int max_exp, 3531c287992SMathieu Othacehe unsigned int max_mantissa) 3541c287992SMathieu Othacehe { 3551c287992SMathieu Othacehe unsigned int exp = 1; 3561c287992SMathieu Othacehe 3571c287992SMathieu Othacehe /* 3581c287992SMathieu Othacehe * Correction coefficients are represented under 3591c287992SMathieu Othacehe * mantissa * 2^exponent form, where mantissa and exponent 3601c287992SMathieu Othacehe * are stored in two separate registers of the sensor. 3611c287992SMathieu Othacehe * 3621c287992SMathieu Othacehe * Compute and return the lowest exponent such as: 3631c287992SMathieu Othacehe * mantissa = value / 2^exponent 3641c287992SMathieu Othacehe * 3651c287992SMathieu Othacehe * where mantissa < max_mantissa. 3661c287992SMathieu Othacehe */ 3671c287992SMathieu Othacehe if (val <= max_mantissa) 3681c287992SMathieu Othacehe return 0; 3691c287992SMathieu Othacehe 3701c287992SMathieu Othacehe while ((val >> exp) > max_mantissa) { 3711c287992SMathieu Othacehe exp++; 3721c287992SMathieu Othacehe 3731c287992SMathieu Othacehe if (exp > max_exp) 3741c287992SMathieu Othacehe return max_exp; 3751c287992SMathieu Othacehe } 3761c287992SMathieu Othacehe 3771c287992SMathieu Othacehe return exp; 3781c287992SMathieu Othacehe } 3791c287992SMathieu Othacehe 3801c287992SMathieu Othacehe static ssize_t isl29501_write_ext(struct iio_dev *indio_dev, 3811c287992SMathieu Othacehe uintptr_t private, 3821c287992SMathieu Othacehe const struct iio_chan_spec *chan, 3831c287992SMathieu Othacehe const char *buf, size_t len) 3841c287992SMathieu Othacehe { 3851c287992SMathieu Othacehe struct isl29501_private *isl29501 = iio_priv(indio_dev); 3861c287992SMathieu Othacehe enum isl29501_register_name reg = private; 3871c287992SMathieu Othacehe unsigned int val; 3881c287992SMathieu Othacehe int max_exp = 0; 3891c287992SMathieu Othacehe int ret; 3901c287992SMathieu Othacehe int i; 3911c287992SMathieu Othacehe 3921c287992SMathieu Othacehe ret = kstrtouint(buf, 10, &val); 3931c287992SMathieu Othacehe if (ret) 3941c287992SMathieu Othacehe return ret; 3951c287992SMathieu Othacehe 3961c287992SMathieu Othacehe switch (reg) { 3971c287992SMathieu Othacehe case REG_GAIN_BIAS: 3981c287992SMathieu Othacehe if (val > U16_MAX) 3991c287992SMathieu Othacehe return -ERANGE; 4001c287992SMathieu Othacehe 4011c287992SMathieu Othacehe ret = isl29501_register_write(isl29501, reg, val); 4021c287992SMathieu Othacehe if (ret < 0) 4031c287992SMathieu Othacehe return ret; 4041c287992SMathieu Othacehe 4051c287992SMathieu Othacehe break; 4061c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_A: 4071c287992SMathieu Othacehe case REG_CALIB_PHASE_TEMP_B: 4081c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_A: 4091c287992SMathieu Othacehe case REG_CALIB_PHASE_LIGHT_B: 4101c287992SMathieu Othacehe 4111c287992SMathieu Othacehe if (val > (U8_MAX << ISL29501_MAX_EXP_VAL)) 4121c287992SMathieu Othacehe return -ERANGE; 4131c287992SMathieu Othacehe 4141c287992SMathieu Othacehe /* Store the correction coefficient under its exact form. */ 4151c287992SMathieu Othacehe ret = isl29501_set_shadow_coeff(isl29501, reg, val); 4161c287992SMathieu Othacehe if (ret < 0) 4171c287992SMathieu Othacehe return ret; 4181c287992SMathieu Othacehe 4191c287992SMathieu Othacehe /* 4201c287992SMathieu Othacehe * Find the highest exponent needed to represent 4211c287992SMathieu Othacehe * correction coefficients. 4221c287992SMathieu Othacehe */ 4231c287992SMathieu Othacehe for (i = 0; i < COEFF_MAX; i++) { 4241c287992SMathieu Othacehe int corr; 4251c287992SMathieu Othacehe int corr_exp; 4261c287992SMathieu Othacehe 4271c287992SMathieu Othacehe corr = isl29501->shadow_coeffs[i]; 4281c287992SMathieu Othacehe corr_exp = isl29501_find_corr_exp(corr, 4291c287992SMathieu Othacehe ISL29501_MAX_EXP_VAL, 4301c287992SMathieu Othacehe U8_MAX / 2); 4311c287992SMathieu Othacehe dev_dbg(&isl29501->client->dev, 4321c287992SMathieu Othacehe "found exp of corr(%d) = %d\n", corr, corr_exp); 4331c287992SMathieu Othacehe 4341c287992SMathieu Othacehe max_exp = max(max_exp, corr_exp); 4351c287992SMathieu Othacehe } 4361c287992SMathieu Othacehe 4371c287992SMathieu Othacehe /* 4381c287992SMathieu Othacehe * Represent every correction coefficient under 4391c287992SMathieu Othacehe * mantissa * 2^max_exponent form and force the 4401c287992SMathieu Othacehe * writing of those coefficients on the sensor. 4411c287992SMathieu Othacehe */ 4421c287992SMathieu Othacehe for (i = 0; i < COEFF_MAX; i++) { 4431c287992SMathieu Othacehe int corr; 4441c287992SMathieu Othacehe int mantissa; 4451c287992SMathieu Othacehe 4461c287992SMathieu Othacehe corr = isl29501->shadow_coeffs[i]; 4471c287992SMathieu Othacehe if (!corr) 4481c287992SMathieu Othacehe continue; 4491c287992SMathieu Othacehe 4501c287992SMathieu Othacehe mantissa = corr >> max_exp; 4511c287992SMathieu Othacehe 4521c287992SMathieu Othacehe ret = isl29501_write_coeff(isl29501, i, mantissa); 4531c287992SMathieu Othacehe if (ret < 0) 4541c287992SMathieu Othacehe return ret; 4551c287992SMathieu Othacehe } 4561c287992SMathieu Othacehe 4571c287992SMathieu Othacehe ret = isl29501_register_write(isl29501, REG_PHASE_EXP, max_exp); 4581c287992SMathieu Othacehe if (ret < 0) 4591c287992SMathieu Othacehe return ret; 4601c287992SMathieu Othacehe 4611c287992SMathieu Othacehe break; 4621c287992SMathieu Othacehe default: 4631c287992SMathieu Othacehe return -EINVAL; 4641c287992SMathieu Othacehe } 4651c287992SMathieu Othacehe 4661c287992SMathieu Othacehe return len; 4671c287992SMathieu Othacehe } 4681c287992SMathieu Othacehe 4691c287992SMathieu Othacehe #define _ISL29501_EXT_INFO(_name, _ident) { \ 4701c287992SMathieu Othacehe .name = _name, \ 4711c287992SMathieu Othacehe .read = isl29501_read_ext, \ 4721c287992SMathieu Othacehe .write = isl29501_write_ext, \ 4731c287992SMathieu Othacehe .private = _ident, \ 4741c287992SMathieu Othacehe .shared = IIO_SEPARATE, \ 4751c287992SMathieu Othacehe } 4761c287992SMathieu Othacehe 4771c287992SMathieu Othacehe static const struct iio_chan_spec_ext_info isl29501_ext_info[] = { 4781c287992SMathieu Othacehe _ISL29501_EXT_INFO("agc_gain", REG_GAIN), 4791c287992SMathieu Othacehe _ISL29501_EXT_INFO("agc_gain_bias", REG_GAIN_BIAS), 4801c287992SMathieu Othacehe _ISL29501_EXT_INFO("calib_phase_temp_a", REG_CALIB_PHASE_TEMP_A), 4811c287992SMathieu Othacehe _ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B), 4821c287992SMathieu Othacehe _ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A), 4831c287992SMathieu Othacehe _ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B), 4841c287992SMathieu Othacehe { }, 4851c287992SMathieu Othacehe }; 4861c287992SMathieu Othacehe 4871c287992SMathieu Othacehe #define ISL29501_DISTANCE_SCAN_INDEX 0 4881c287992SMathieu Othacehe #define ISL29501_TIMESTAMP_SCAN_INDEX 1 4891c287992SMathieu Othacehe 4901c287992SMathieu Othacehe static const struct iio_chan_spec isl29501_channels[] = { 4911c287992SMathieu Othacehe { 4921c287992SMathieu Othacehe .type = IIO_PROXIMITY, 4931c287992SMathieu Othacehe .scan_index = ISL29501_DISTANCE_SCAN_INDEX, 4941c287992SMathieu Othacehe .info_mask_separate = 4951c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_RAW) | 4961c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE) | 4971c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_CALIBBIAS), 4981c287992SMathieu Othacehe .scan_type = { 4991c287992SMathieu Othacehe .sign = 'u', 5001c287992SMathieu Othacehe .realbits = 16, 5011c287992SMathieu Othacehe .storagebits = 16, 5021c287992SMathieu Othacehe .endianness = IIO_CPU, 5031c287992SMathieu Othacehe }, 5041c287992SMathieu Othacehe .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | 5051c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 5061c287992SMathieu Othacehe .ext_info = isl29501_ext_info, 5071c287992SMathieu Othacehe }, 5081c287992SMathieu Othacehe { 5091c287992SMathieu Othacehe .type = IIO_PHASE, 5101c287992SMathieu Othacehe .scan_index = -1, 5111c287992SMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 5121c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 5131c287992SMathieu Othacehe }, 5141c287992SMathieu Othacehe { 5151c287992SMathieu Othacehe .type = IIO_CURRENT, 5161c287992SMathieu Othacehe .scan_index = -1, 5171c287992SMathieu Othacehe .output = 1, 5181c287992SMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 5191c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 5201c287992SMathieu Othacehe }, 5211c287992SMathieu Othacehe { 5221c287992SMathieu Othacehe .type = IIO_TEMP, 5231c287992SMathieu Othacehe .scan_index = -1, 5241c287992SMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 5251c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE) | 5261c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_CALIBBIAS), 5271c287992SMathieu Othacehe }, 5281c287992SMathieu Othacehe { 5291c287992SMathieu Othacehe .type = IIO_INTENSITY, 5301c287992SMathieu Othacehe .scan_index = -1, 5311c287992SMathieu Othacehe .modified = 1, 5321c287992SMathieu Othacehe .channel2 = IIO_MOD_LIGHT_CLEAR, 5331c287992SMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 5341c287992SMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 5351c287992SMathieu Othacehe }, 5361c287992SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX), 5371c287992SMathieu Othacehe }; 5381c287992SMathieu Othacehe 5391c287992SMathieu Othacehe static int isl29501_reset_registers(struct isl29501_private *isl29501) 5401c287992SMathieu Othacehe { 5411c287992SMathieu Othacehe int ret; 5421c287992SMathieu Othacehe 5431c287992SMathieu Othacehe ret = i2c_smbus_write_byte_data(isl29501->client, 5441c287992SMathieu Othacehe ISL29501_COMMAND_REGISTER, 5451c287992SMathieu Othacehe ISL29501_RESET_ALL_REGISTERS); 5461c287992SMathieu Othacehe if (ret < 0) { 5471c287992SMathieu Othacehe dev_err(&isl29501->client->dev, 5481c287992SMathieu Othacehe "cannot reset registers %d\n", ret); 5491c287992SMathieu Othacehe return ret; 5501c287992SMathieu Othacehe } 5511c287992SMathieu Othacehe 5521c287992SMathieu Othacehe ret = i2c_smbus_write_byte_data(isl29501->client, 5531c287992SMathieu Othacehe ISL29501_COMMAND_REGISTER, 5541c287992SMathieu Othacehe ISL29501_RESET_INT_SM); 5551c287992SMathieu Othacehe if (ret < 0) 5561c287992SMathieu Othacehe dev_err(&isl29501->client->dev, 5571c287992SMathieu Othacehe "cannot reset state machine %d\n", ret); 5581c287992SMathieu Othacehe 5591c287992SMathieu Othacehe return ret; 5601c287992SMathieu Othacehe } 5611c287992SMathieu Othacehe 5621c287992SMathieu Othacehe static int isl29501_begin_acquisition(struct isl29501_private *isl29501) 5631c287992SMathieu Othacehe { 5641c287992SMathieu Othacehe int ret; 5651c287992SMathieu Othacehe 5661c287992SMathieu Othacehe ret = i2c_smbus_write_byte_data(isl29501->client, 5671c287992SMathieu Othacehe ISL29501_COMMAND_REGISTER, 5681c287992SMathieu Othacehe ISL29501_EMUL_SAMPLE_START_PIN); 5691c287992SMathieu Othacehe if (ret < 0) 5701c287992SMathieu Othacehe dev_err(&isl29501->client->dev, 5711c287992SMathieu Othacehe "cannot begin acquisition %d\n", ret); 5721c287992SMathieu Othacehe 5731c287992SMathieu Othacehe return ret; 5741c287992SMathieu Othacehe } 5751c287992SMathieu Othacehe 5761c287992SMathieu Othacehe static IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE); 5771c287992SMathieu Othacehe static IIO_CONST_ATTR(out_current_scale_available, 5781c287992SMathieu Othacehe ISL29501_CURRENT_SCALE_AVAILABLE); 5791c287992SMathieu Othacehe 5801c287992SMathieu Othacehe static struct attribute *isl29501_attributes[] = { 5811c287992SMathieu Othacehe &iio_const_attr_integration_time_available.dev_attr.attr, 5821c287992SMathieu Othacehe &iio_const_attr_out_current_scale_available.dev_attr.attr, 5831c287992SMathieu Othacehe NULL 5841c287992SMathieu Othacehe }; 5851c287992SMathieu Othacehe 5861c287992SMathieu Othacehe static const struct attribute_group isl29501_attribute_group = { 5871c287992SMathieu Othacehe .attrs = isl29501_attributes, 5881c287992SMathieu Othacehe }; 5891c287992SMathieu Othacehe 5901c287992SMathieu Othacehe static const int isl29501_current_scale_table[][2] = { 5911c287992SMathieu Othacehe {0, 3900}, {0, 7800}, {0, 11800}, {0, 15700}, 5921c287992SMathieu Othacehe {0, 19600}, {0, 23500}, {0, 27500}, {0, 31400}, 5931c287992SMathieu Othacehe {0, 35200}, {0, 39200}, {0, 43100}, {0, 47100}, 5941c287992SMathieu Othacehe {0, 51000}, {0, 54900}, {0, 58800}, 5951c287992SMathieu Othacehe }; 5961c287992SMathieu Othacehe 5971c287992SMathieu Othacehe static const int isl29501_int_time[][2] = { 5981c287992SMathieu Othacehe {0, 70}, /* 0.07 ms */ 5991c287992SMathieu Othacehe {0, 140}, /* 0.14 ms */ 6001c287992SMathieu Othacehe {0, 280}, /* 0.28 ms */ 6011c287992SMathieu Othacehe {0, 570}, /* 0.57 ms */ 6021c287992SMathieu Othacehe {0, 1140}, /* 1.14 ms */ 6031c287992SMathieu Othacehe {0, 2280}, /* 2.28 ms */ 6041c287992SMathieu Othacehe {0, 4550}, /* 4.55 ms */ 6051c287992SMathieu Othacehe {0, 9100}, /* 9.11 ms */ 6061c287992SMathieu Othacehe {0, 18200}, /* 18.2 ms */ 6071c287992SMathieu Othacehe {0, 36400}, /* 36.4 ms */ 6081c287992SMathieu Othacehe {0, 72810}, /* 72.81 ms */ 6091c287992SMathieu Othacehe {0, 145610} /* 145.28 ms */ 6101c287992SMathieu Othacehe }; 6111c287992SMathieu Othacehe 6121c287992SMathieu Othacehe static int isl29501_get_raw(struct isl29501_private *isl29501, 6131c287992SMathieu Othacehe const struct iio_chan_spec *chan, 6141c287992SMathieu Othacehe int *raw) 6151c287992SMathieu Othacehe { 6161c287992SMathieu Othacehe int ret; 6171c287992SMathieu Othacehe 6181c287992SMathieu Othacehe switch (chan->type) { 6191c287992SMathieu Othacehe case IIO_PROXIMITY: 6201c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_DISTANCE, raw); 6211c287992SMathieu Othacehe if (ret < 0) 6221c287992SMathieu Othacehe return ret; 6231c287992SMathieu Othacehe 6241c287992SMathieu Othacehe return IIO_VAL_INT; 6251c287992SMathieu Othacehe case IIO_INTENSITY: 6261c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, 6271c287992SMathieu Othacehe REG_AMBIENT_LIGHT, 6281c287992SMathieu Othacehe raw); 6291c287992SMathieu Othacehe if (ret < 0) 6301c287992SMathieu Othacehe return ret; 6311c287992SMathieu Othacehe 6321c287992SMathieu Othacehe return IIO_VAL_INT; 6331c287992SMathieu Othacehe case IIO_PHASE: 6341c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_PHASE, raw); 6351c287992SMathieu Othacehe if (ret < 0) 6361c287992SMathieu Othacehe return ret; 6371c287992SMathieu Othacehe 6381c287992SMathieu Othacehe return IIO_VAL_INT; 6391c287992SMathieu Othacehe case IIO_CURRENT: 6401c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_EMITTER_DAC, raw); 6411c287992SMathieu Othacehe if (ret < 0) 6421c287992SMathieu Othacehe return ret; 6431c287992SMathieu Othacehe 6441c287992SMathieu Othacehe return IIO_VAL_INT; 6451c287992SMathieu Othacehe case IIO_TEMP: 6461c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_TEMPERATURE, raw); 6471c287992SMathieu Othacehe if (ret < 0) 6481c287992SMathieu Othacehe return ret; 6491c287992SMathieu Othacehe 6501c287992SMathieu Othacehe return IIO_VAL_INT; 6511c287992SMathieu Othacehe default: 6521c287992SMathieu Othacehe return -EINVAL; 6531c287992SMathieu Othacehe } 6541c287992SMathieu Othacehe } 6551c287992SMathieu Othacehe 6561c287992SMathieu Othacehe static int isl29501_get_scale(struct isl29501_private *isl29501, 6571c287992SMathieu Othacehe const struct iio_chan_spec *chan, 6581c287992SMathieu Othacehe int *val, int *val2) 6591c287992SMathieu Othacehe { 6601c287992SMathieu Othacehe int ret; 6611c287992SMathieu Othacehe u32 current_scale; 6621c287992SMathieu Othacehe 6631c287992SMathieu Othacehe switch (chan->type) { 6641c287992SMathieu Othacehe case IIO_PROXIMITY: 6651c287992SMathieu Othacehe /* distance = raw_distance * 33.31 / 65536 (m) */ 6661c287992SMathieu Othacehe *val = 3331; 6671c287992SMathieu Othacehe *val2 = 6553600; 6681c287992SMathieu Othacehe 6691c287992SMathieu Othacehe return IIO_VAL_FRACTIONAL; 6701c287992SMathieu Othacehe case IIO_PHASE: 6711c287992SMathieu Othacehe /* phase = raw_phase * 2pi / 65536 (rad) */ 6721c287992SMathieu Othacehe *val = 0; 6731c287992SMathieu Othacehe *val2 = 95874; 6741c287992SMathieu Othacehe 6751c287992SMathieu Othacehe return IIO_VAL_INT_PLUS_NANO; 6761c287992SMathieu Othacehe case IIO_INTENSITY: 6771c287992SMathieu Othacehe /* light = raw_light * 35 / 10000 (mA) */ 6781c287992SMathieu Othacehe *val = 35; 6791c287992SMathieu Othacehe *val2 = 10000; 6801c287992SMathieu Othacehe 6811c287992SMathieu Othacehe return IIO_VAL_FRACTIONAL; 6821c287992SMathieu Othacehe case IIO_CURRENT: 6831c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, 6841c287992SMathieu Othacehe REG_DRIVER_RANGE, 6851c287992SMathieu Othacehe ¤t_scale); 6861c287992SMathieu Othacehe if (ret < 0) 6871c287992SMathieu Othacehe return ret; 6881c287992SMathieu Othacehe 6891c287992SMathieu Othacehe if (current_scale > ARRAY_SIZE(isl29501_current_scale_table)) 6901c287992SMathieu Othacehe return -EINVAL; 6911c287992SMathieu Othacehe 6921c287992SMathieu Othacehe if (!current_scale) { 6931c287992SMathieu Othacehe *val = 0; 6941c287992SMathieu Othacehe *val2 = 0; 6951c287992SMathieu Othacehe return IIO_VAL_INT; 6961c287992SMathieu Othacehe } 6971c287992SMathieu Othacehe 6981c287992SMathieu Othacehe *val = isl29501_current_scale_table[current_scale - 1][0]; 6991c287992SMathieu Othacehe *val2 = isl29501_current_scale_table[current_scale - 1][1]; 7001c287992SMathieu Othacehe 7011c287992SMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 7021c287992SMathieu Othacehe case IIO_TEMP: 7031c287992SMathieu Othacehe /* temperature = raw_temperature * 125 / 100000 (milli °C) */ 7041c287992SMathieu Othacehe *val = 125; 7051c287992SMathieu Othacehe *val2 = 100000; 7061c287992SMathieu Othacehe 7071c287992SMathieu Othacehe return IIO_VAL_FRACTIONAL; 7081c287992SMathieu Othacehe default: 7091c287992SMathieu Othacehe return -EINVAL; 7101c287992SMathieu Othacehe } 7111c287992SMathieu Othacehe } 7121c287992SMathieu Othacehe 7131c287992SMathieu Othacehe static int isl29501_get_calibbias(struct isl29501_private *isl29501, 7141c287992SMathieu Othacehe const struct iio_chan_spec *chan, 7151c287992SMathieu Othacehe int *bias) 7161c287992SMathieu Othacehe { 7171c287992SMathieu Othacehe switch (chan->type) { 7181c287992SMathieu Othacehe case IIO_PROXIMITY: 7191c287992SMathieu Othacehe return isl29501_register_read(isl29501, 7201c287992SMathieu Othacehe REG_DISTANCE_BIAS, 7211c287992SMathieu Othacehe bias); 7221c287992SMathieu Othacehe case IIO_TEMP: 7231c287992SMathieu Othacehe return isl29501_register_read(isl29501, 7241c287992SMathieu Othacehe REG_TEMPERATURE_BIAS, 7251c287992SMathieu Othacehe bias); 7261c287992SMathieu Othacehe default: 7271c287992SMathieu Othacehe return -EINVAL; 7281c287992SMathieu Othacehe } 7291c287992SMathieu Othacehe } 7301c287992SMathieu Othacehe 7311c287992SMathieu Othacehe static int isl29501_get_inttime(struct isl29501_private *isl29501, 7321c287992SMathieu Othacehe int *val, int *val2) 7331c287992SMathieu Othacehe { 7341c287992SMathieu Othacehe int ret; 7351c287992SMathieu Othacehe u32 inttime; 7361c287992SMathieu Othacehe 7371c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_INT_TIME, &inttime); 7381c287992SMathieu Othacehe if (ret < 0) 7391c287992SMathieu Othacehe return ret; 7401c287992SMathieu Othacehe 7411c287992SMathieu Othacehe if (inttime >= ARRAY_SIZE(isl29501_int_time)) 7421c287992SMathieu Othacehe return -EINVAL; 7431c287992SMathieu Othacehe 7441c287992SMathieu Othacehe *val = isl29501_int_time[inttime][0]; 7451c287992SMathieu Othacehe *val2 = isl29501_int_time[inttime][1]; 7461c287992SMathieu Othacehe 7471c287992SMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 7481c287992SMathieu Othacehe } 7491c287992SMathieu Othacehe 7501c287992SMathieu Othacehe static int isl29501_get_freq(struct isl29501_private *isl29501, 7511c287992SMathieu Othacehe int *val, int *val2) 7521c287992SMathieu Othacehe { 7531c287992SMathieu Othacehe int ret; 7541c287992SMathieu Othacehe int sample_time; 7551c287992SMathieu Othacehe unsigned long long freq; 7561c287992SMathieu Othacehe u32 temp; 7571c287992SMathieu Othacehe 7581c287992SMathieu Othacehe ret = isl29501_register_read(isl29501, REG_SAMPLE_TIME, &sample_time); 7591c287992SMathieu Othacehe if (ret < 0) 7601c287992SMathieu Othacehe return ret; 7611c287992SMathieu Othacehe 7621c287992SMathieu Othacehe /* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ 7631c287992SMathieu Othacehe freq = 1000000ULL * 1000000ULL; 7641c287992SMathieu Othacehe 7651c287992SMathieu Othacehe do_div(freq, 450 * (sample_time + 1)); 7661c287992SMathieu Othacehe 7671c287992SMathieu Othacehe temp = do_div(freq, 1000000); 7681c287992SMathieu Othacehe *val = freq; 7691c287992SMathieu Othacehe *val2 = temp; 7701c287992SMathieu Othacehe 7711c287992SMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 7721c287992SMathieu Othacehe } 7731c287992SMathieu Othacehe 7741c287992SMathieu Othacehe static int isl29501_read_raw(struct iio_dev *indio_dev, 7751c287992SMathieu Othacehe struct iio_chan_spec const *chan, int *val, 7761c287992SMathieu Othacehe int *val2, long mask) 7771c287992SMathieu Othacehe { 7781c287992SMathieu Othacehe struct isl29501_private *isl29501 = iio_priv(indio_dev); 7791c287992SMathieu Othacehe 7801c287992SMathieu Othacehe switch (mask) { 7811c287992SMathieu Othacehe case IIO_CHAN_INFO_RAW: 7821c287992SMathieu Othacehe return isl29501_get_raw(isl29501, chan, val); 7831c287992SMathieu Othacehe case IIO_CHAN_INFO_SCALE: 7841c287992SMathieu Othacehe return isl29501_get_scale(isl29501, chan, val, val2); 7851c287992SMathieu Othacehe case IIO_CHAN_INFO_INT_TIME: 7861c287992SMathieu Othacehe return isl29501_get_inttime(isl29501, val, val2); 7871c287992SMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 7881c287992SMathieu Othacehe return isl29501_get_freq(isl29501, val, val2); 7891c287992SMathieu Othacehe case IIO_CHAN_INFO_CALIBBIAS: 7901c287992SMathieu Othacehe return isl29501_get_calibbias(isl29501, chan, val); 7911c287992SMathieu Othacehe default: 7921c287992SMathieu Othacehe return -EINVAL; 7931c287992SMathieu Othacehe } 7941c287992SMathieu Othacehe } 7951c287992SMathieu Othacehe 7961c287992SMathieu Othacehe static int isl29501_set_raw(struct isl29501_private *isl29501, 7971c287992SMathieu Othacehe const struct iio_chan_spec *chan, 7981c287992SMathieu Othacehe int raw) 7991c287992SMathieu Othacehe { 8001c287992SMathieu Othacehe switch (chan->type) { 8011c287992SMathieu Othacehe case IIO_CURRENT: 8021c287992SMathieu Othacehe return isl29501_register_write(isl29501, REG_EMITTER_DAC, raw); 8031c287992SMathieu Othacehe default: 8041c287992SMathieu Othacehe return -EINVAL; 8051c287992SMathieu Othacehe } 8061c287992SMathieu Othacehe } 8071c287992SMathieu Othacehe 8081c287992SMathieu Othacehe static int isl29501_set_inttime(struct isl29501_private *isl29501, 8091c287992SMathieu Othacehe int val, int val2) 8101c287992SMathieu Othacehe { 8111c287992SMathieu Othacehe int i; 8121c287992SMathieu Othacehe 8131c287992SMathieu Othacehe for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) { 8141c287992SMathieu Othacehe if (isl29501_int_time[i][0] == val && 8151c287992SMathieu Othacehe isl29501_int_time[i][1] == val2) { 8161c287992SMathieu Othacehe return isl29501_register_write(isl29501, 8171c287992SMathieu Othacehe REG_INT_TIME, 8181c287992SMathieu Othacehe i); 8191c287992SMathieu Othacehe } 8201c287992SMathieu Othacehe } 8211c287992SMathieu Othacehe 8221c287992SMathieu Othacehe return -EINVAL; 8231c287992SMathieu Othacehe } 8241c287992SMathieu Othacehe 8251c287992SMathieu Othacehe static int isl29501_set_scale(struct isl29501_private *isl29501, 8261c287992SMathieu Othacehe const struct iio_chan_spec *chan, 8271c287992SMathieu Othacehe int val, int val2) 8281c287992SMathieu Othacehe { 8291c287992SMathieu Othacehe int i; 8301c287992SMathieu Othacehe 8311c287992SMathieu Othacehe if (chan->type != IIO_CURRENT) 8321c287992SMathieu Othacehe return -EINVAL; 8331c287992SMathieu Othacehe 8341c287992SMathieu Othacehe for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) { 8351c287992SMathieu Othacehe if (isl29501_current_scale_table[i][0] == val && 8361c287992SMathieu Othacehe isl29501_current_scale_table[i][1] == val2) { 8371c287992SMathieu Othacehe return isl29501_register_write(isl29501, 8381c287992SMathieu Othacehe REG_DRIVER_RANGE, 8391c287992SMathieu Othacehe i + 1); 8401c287992SMathieu Othacehe } 8411c287992SMathieu Othacehe } 8421c287992SMathieu Othacehe 8431c287992SMathieu Othacehe return -EINVAL; 8441c287992SMathieu Othacehe } 8451c287992SMathieu Othacehe 8461c287992SMathieu Othacehe static int isl29501_set_calibbias(struct isl29501_private *isl29501, 8471c287992SMathieu Othacehe const struct iio_chan_spec *chan, 8481c287992SMathieu Othacehe int bias) 8491c287992SMathieu Othacehe { 8501c287992SMathieu Othacehe switch (chan->type) { 8511c287992SMathieu Othacehe case IIO_PROXIMITY: 8521c287992SMathieu Othacehe return isl29501_register_write(isl29501, 8531c287992SMathieu Othacehe REG_DISTANCE_BIAS, 8541c287992SMathieu Othacehe bias); 8551c287992SMathieu Othacehe case IIO_TEMP: 8561c287992SMathieu Othacehe return isl29501_register_write(isl29501, 8571c287992SMathieu Othacehe REG_TEMPERATURE_BIAS, 8581c287992SMathieu Othacehe bias); 8591c287992SMathieu Othacehe default: 8601c287992SMathieu Othacehe return -EINVAL; 8611c287992SMathieu Othacehe } 8621c287992SMathieu Othacehe } 8631c287992SMathieu Othacehe 8641c287992SMathieu Othacehe static int isl29501_set_freq(struct isl29501_private *isl29501, 8651c287992SMathieu Othacehe int val, int val2) 8661c287992SMathieu Othacehe { 8671c287992SMathieu Othacehe int freq; 8681c287992SMathieu Othacehe unsigned long long sample_time; 8691c287992SMathieu Othacehe 8701c287992SMathieu Othacehe /* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ 8711c287992SMathieu Othacehe freq = val * 1000000 + val2 % 1000000; 8721c287992SMathieu Othacehe sample_time = 2222ULL * 1000000ULL; 8731c287992SMathieu Othacehe do_div(sample_time, freq); 8741c287992SMathieu Othacehe 8751c287992SMathieu Othacehe sample_time -= 1; 8761c287992SMathieu Othacehe 8771c287992SMathieu Othacehe if (sample_time > 255) 8781c287992SMathieu Othacehe return -ERANGE; 8791c287992SMathieu Othacehe 8801c287992SMathieu Othacehe return isl29501_register_write(isl29501, REG_SAMPLE_TIME, sample_time); 8811c287992SMathieu Othacehe } 8821c287992SMathieu Othacehe 8831c287992SMathieu Othacehe static int isl29501_write_raw(struct iio_dev *indio_dev, 8841c287992SMathieu Othacehe struct iio_chan_spec const *chan, 8851c287992SMathieu Othacehe int val, int val2, long mask) 8861c287992SMathieu Othacehe { 8871c287992SMathieu Othacehe struct isl29501_private *isl29501 = iio_priv(indio_dev); 8881c287992SMathieu Othacehe 8891c287992SMathieu Othacehe switch (mask) { 8901c287992SMathieu Othacehe case IIO_CHAN_INFO_RAW: 8911c287992SMathieu Othacehe return isl29501_set_raw(isl29501, chan, val); 8921c287992SMathieu Othacehe case IIO_CHAN_INFO_INT_TIME: 8931c287992SMathieu Othacehe return isl29501_set_inttime(isl29501, val, val2); 8941c287992SMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 8951c287992SMathieu Othacehe return isl29501_set_freq(isl29501, val, val2); 8961c287992SMathieu Othacehe case IIO_CHAN_INFO_SCALE: 8971c287992SMathieu Othacehe return isl29501_set_scale(isl29501, chan, val, val2); 8981c287992SMathieu Othacehe case IIO_CHAN_INFO_CALIBBIAS: 8991c287992SMathieu Othacehe return isl29501_set_calibbias(isl29501, chan, val); 9001c287992SMathieu Othacehe default: 9011c287992SMathieu Othacehe return -EINVAL; 9021c287992SMathieu Othacehe } 9031c287992SMathieu Othacehe } 9041c287992SMathieu Othacehe 9051c287992SMathieu Othacehe static const struct iio_info isl29501_info = { 9061c287992SMathieu Othacehe .read_raw = &isl29501_read_raw, 9071c287992SMathieu Othacehe .write_raw = &isl29501_write_raw, 9081c287992SMathieu Othacehe .attrs = &isl29501_attribute_group, 9091c287992SMathieu Othacehe }; 9101c287992SMathieu Othacehe 9111c287992SMathieu Othacehe static int isl29501_init_chip(struct isl29501_private *isl29501) 9121c287992SMathieu Othacehe { 9131c287992SMathieu Othacehe int ret; 9141c287992SMathieu Othacehe 9151c287992SMathieu Othacehe ret = i2c_smbus_read_byte_data(isl29501->client, ISL29501_DEVICE_ID); 9161c287992SMathieu Othacehe if (ret < 0) { 9171c287992SMathieu Othacehe dev_err(&isl29501->client->dev, "Error reading device id\n"); 9181c287992SMathieu Othacehe return ret; 9191c287992SMathieu Othacehe } 9201c287992SMathieu Othacehe 9211c287992SMathieu Othacehe if (ret != ISL29501_ID) { 9221c287992SMathieu Othacehe dev_err(&isl29501->client->dev, 9231c287992SMathieu Othacehe "Wrong chip id, got %x expected %x\n", 9241c287992SMathieu Othacehe ret, ISL29501_DEVICE_ID); 9251c287992SMathieu Othacehe return -ENODEV; 9261c287992SMathieu Othacehe } 9271c287992SMathieu Othacehe 9281c287992SMathieu Othacehe ret = isl29501_reset_registers(isl29501); 9291c287992SMathieu Othacehe if (ret < 0) 9301c287992SMathieu Othacehe return ret; 9311c287992SMathieu Othacehe 9321c287992SMathieu Othacehe return isl29501_begin_acquisition(isl29501); 9331c287992SMathieu Othacehe } 9341c287992SMathieu Othacehe 9351c287992SMathieu Othacehe static irqreturn_t isl29501_trigger_handler(int irq, void *p) 9361c287992SMathieu Othacehe { 9371c287992SMathieu Othacehe struct iio_poll_func *pf = p; 9381c287992SMathieu Othacehe struct iio_dev *indio_dev = pf->indio_dev; 9391c287992SMathieu Othacehe struct isl29501_private *isl29501 = iio_priv(indio_dev); 9401c287992SMathieu Othacehe const unsigned long *active_mask = indio_dev->active_scan_mask; 94192babc99SJonathan Cameron u32 buffer[4] __aligned(8) = {}; /* 1x16-bit + naturally aligned ts */ 9421c287992SMathieu Othacehe 9431c287992SMathieu Othacehe if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask)) 9441c287992SMathieu Othacehe isl29501_register_read(isl29501, REG_DISTANCE, buffer); 9451c287992SMathieu Othacehe 9461c287992SMathieu Othacehe iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); 9471c287992SMathieu Othacehe iio_trigger_notify_done(indio_dev->trig); 9481c287992SMathieu Othacehe 9491c287992SMathieu Othacehe return IRQ_HANDLED; 9501c287992SMathieu Othacehe } 9511c287992SMathieu Othacehe 952*9d6f774dSUwe Kleine-König static int isl29501_probe(struct i2c_client *client) 9531c287992SMathieu Othacehe { 9541c287992SMathieu Othacehe struct iio_dev *indio_dev; 9551c287992SMathieu Othacehe struct isl29501_private *isl29501; 9561c287992SMathieu Othacehe int ret; 9571c287992SMathieu Othacehe 9581c287992SMathieu Othacehe indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*isl29501)); 9591c287992SMathieu Othacehe if (!indio_dev) 9601c287992SMathieu Othacehe return -ENOMEM; 9611c287992SMathieu Othacehe 9621c287992SMathieu Othacehe isl29501 = iio_priv(indio_dev); 9631c287992SMathieu Othacehe 9641c287992SMathieu Othacehe i2c_set_clientdata(client, indio_dev); 9651c287992SMathieu Othacehe isl29501->client = client; 9661c287992SMathieu Othacehe 9671c287992SMathieu Othacehe mutex_init(&isl29501->lock); 9681c287992SMathieu Othacehe 9691c287992SMathieu Othacehe ret = isl29501_init_chip(isl29501); 9701c287992SMathieu Othacehe if (ret < 0) 9711c287992SMathieu Othacehe return ret; 9721c287992SMathieu Othacehe 9731c287992SMathieu Othacehe indio_dev->modes = INDIO_DIRECT_MODE; 9741c287992SMathieu Othacehe indio_dev->channels = isl29501_channels; 9751c287992SMathieu Othacehe indio_dev->num_channels = ARRAY_SIZE(isl29501_channels); 9761c287992SMathieu Othacehe indio_dev->name = client->name; 9771c287992SMathieu Othacehe indio_dev->info = &isl29501_info; 9781c287992SMathieu Othacehe 9791c287992SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 9801c287992SMathieu Othacehe iio_pollfunc_store_time, 9811c287992SMathieu Othacehe isl29501_trigger_handler, 9821c287992SMathieu Othacehe NULL); 9831c287992SMathieu Othacehe if (ret < 0) { 9841c287992SMathieu Othacehe dev_err(&client->dev, "unable to setup iio triggered buffer\n"); 9851c287992SMathieu Othacehe return ret; 9861c287992SMathieu Othacehe } 9871c287992SMathieu Othacehe 9881c287992SMathieu Othacehe return devm_iio_device_register(&client->dev, indio_dev); 9891c287992SMathieu Othacehe } 9901c287992SMathieu Othacehe 9911c287992SMathieu Othacehe static const struct i2c_device_id isl29501_id[] = { 9921c287992SMathieu Othacehe {"isl29501", 0}, 9931c287992SMathieu Othacehe {} 9941c287992SMathieu Othacehe }; 9951c287992SMathieu Othacehe 9961c287992SMathieu Othacehe MODULE_DEVICE_TABLE(i2c, isl29501_id); 9971c287992SMathieu Othacehe 9981c287992SMathieu Othacehe #if defined(CONFIG_OF) 9991c287992SMathieu Othacehe static const struct of_device_id isl29501_i2c_matches[] = { 10001c287992SMathieu Othacehe { .compatible = "renesas,isl29501" }, 10011c287992SMathieu Othacehe { } 10021c287992SMathieu Othacehe }; 10031c287992SMathieu Othacehe MODULE_DEVICE_TABLE(of, isl29501_i2c_matches); 10041c287992SMathieu Othacehe #endif 10051c287992SMathieu Othacehe 10061c287992SMathieu Othacehe static struct i2c_driver isl29501_driver = { 10071c287992SMathieu Othacehe .driver = { 10081c287992SMathieu Othacehe .name = "isl29501", 10091c287992SMathieu Othacehe }, 10101c287992SMathieu Othacehe .id_table = isl29501_id, 1011*9d6f774dSUwe Kleine-König .probe_new = isl29501_probe, 10121c287992SMathieu Othacehe }; 10131c287992SMathieu Othacehe module_i2c_driver(isl29501_driver); 10141c287992SMathieu Othacehe 10151c287992SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 10161c287992SMathieu Othacehe MODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver"); 10171c287992SMathieu Othacehe MODULE_LICENSE("GPL v2"); 1018