18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * A iio driver for the light sensor ISL 29018/29023/29035. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * IIO driver for monitoring ambient light intensity in luxi, proximity 68c2ecf20Sopenharmony_ci * sensing and infrared sensing. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2010, NVIDIA Corporation. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/acpi.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define ISL29018_CONV_TIME_MS 100 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define ISL29018_REG_ADD_COMMAND1 0x00 268c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_SHIFT 5 278c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT) 288c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_POWER_DOWN 0 298c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_ALS_ONCE 1 308c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_IR_ONCE 2 318c2ecf20Sopenharmony_ci#define ISL29018_CMD1_OPMODE_PROX_ONCE 3 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define ISL29018_REG_ADD_COMMAND2 0x01 348c2ecf20Sopenharmony_ci#define ISL29018_CMD2_RESOLUTION_SHIFT 2 358c2ecf20Sopenharmony_ci#define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define ISL29018_CMD2_RANGE_SHIFT 0 388c2ecf20Sopenharmony_ci#define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define ISL29018_CMD2_SCHEME_SHIFT 7 418c2ecf20Sopenharmony_ci#define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define ISL29018_REG_ADD_DATA_LSB 0x02 448c2ecf20Sopenharmony_ci#define ISL29018_REG_ADD_DATA_MSB 0x03 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define ISL29018_REG_TEST 0x08 478c2ecf20Sopenharmony_ci#define ISL29018_TEST_SHIFT 0 488c2ecf20Sopenharmony_ci#define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define ISL29035_REG_DEVICE_ID 0x0F 518c2ecf20Sopenharmony_ci#define ISL29035_DEVICE_ID_SHIFT 0x03 528c2ecf20Sopenharmony_ci#define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT) 538c2ecf20Sopenharmony_ci#define ISL29035_DEVICE_ID 0x5 548c2ecf20Sopenharmony_ci#define ISL29035_BOUT_SHIFT 0x07 558c2ecf20Sopenharmony_ci#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cienum isl29018_int_time { 588c2ecf20Sopenharmony_ci ISL29018_INT_TIME_16, 598c2ecf20Sopenharmony_ci ISL29018_INT_TIME_12, 608c2ecf20Sopenharmony_ci ISL29018_INT_TIME_8, 618c2ecf20Sopenharmony_ci ISL29018_INT_TIME_4, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const unsigned int isl29018_int_utimes[3][4] = { 658c2ecf20Sopenharmony_ci {90000, 5630, 351, 21}, 668c2ecf20Sopenharmony_ci {90000, 5600, 352, 22}, 678c2ecf20Sopenharmony_ci {105000, 6500, 410, 25}, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const struct isl29018_scale { 718c2ecf20Sopenharmony_ci unsigned int scale; 728c2ecf20Sopenharmony_ci unsigned int uscale; 738c2ecf20Sopenharmony_ci} isl29018_scales[4][4] = { 748c2ecf20Sopenharmony_ci { {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} }, 758c2ecf20Sopenharmony_ci { {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} }, 768c2ecf20Sopenharmony_ci { {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} }, 778c2ecf20Sopenharmony_ci { {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} } 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct isl29018_chip { 818c2ecf20Sopenharmony_ci struct regmap *regmap; 828c2ecf20Sopenharmony_ci struct mutex lock; 838c2ecf20Sopenharmony_ci int type; 848c2ecf20Sopenharmony_ci unsigned int calibscale; 858c2ecf20Sopenharmony_ci unsigned int ucalibscale; 868c2ecf20Sopenharmony_ci unsigned int int_time; 878c2ecf20Sopenharmony_ci struct isl29018_scale scale; 888c2ecf20Sopenharmony_ci int prox_scheme; 898c2ecf20Sopenharmony_ci bool suspended; 908c2ecf20Sopenharmony_ci struct regulator *vcc_reg; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int isl29018_set_integration_time(struct isl29018_chip *chip, 948c2ecf20Sopenharmony_ci unsigned int utime) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned int i; 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci unsigned int int_time, new_int_time; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) { 1018c2ecf20Sopenharmony_ci if (utime == isl29018_int_utimes[chip->type][i]) { 1028c2ecf20Sopenharmony_ci new_int_time = i; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type])) 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, 1118c2ecf20Sopenharmony_ci ISL29018_CMD2_RESOLUTION_MASK, 1128c2ecf20Sopenharmony_ci i << ISL29018_CMD2_RESOLUTION_SHIFT); 1138c2ecf20Sopenharmony_ci if (ret < 0) 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Keep the same range when integration time changes */ 1178c2ecf20Sopenharmony_ci int_time = chip->int_time; 1188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) { 1198c2ecf20Sopenharmony_ci if (chip->scale.scale == isl29018_scales[int_time][i].scale && 1208c2ecf20Sopenharmony_ci chip->scale.uscale == isl29018_scales[int_time][i].uscale) { 1218c2ecf20Sopenharmony_ci chip->scale = isl29018_scales[new_int_time][i]; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci chip->int_time = new_int_time; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned int i; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci struct isl29018_scale new_scale; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) { 1378c2ecf20Sopenharmony_ci if (scale == isl29018_scales[chip->int_time][i].scale && 1388c2ecf20Sopenharmony_ci uscale == isl29018_scales[chip->int_time][i].uscale) { 1398c2ecf20Sopenharmony_ci new_scale = isl29018_scales[chip->int_time][i]; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time])) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, 1488c2ecf20Sopenharmony_ci ISL29018_CMD2_RANGE_MASK, 1498c2ecf20Sopenharmony_ci i << ISL29018_CMD2_RANGE_SHIFT); 1508c2ecf20Sopenharmony_ci if (ret < 0) 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci chip->scale = new_scale; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int status; 1618c2ecf20Sopenharmony_ci unsigned int lsb; 1628c2ecf20Sopenharmony_ci unsigned int msb; 1638c2ecf20Sopenharmony_ci struct device *dev = regmap_get_device(chip->regmap); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Set mode */ 1668c2ecf20Sopenharmony_ci status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 1678c2ecf20Sopenharmony_ci mode << ISL29018_CMD1_OPMODE_SHIFT); 1688c2ecf20Sopenharmony_ci if (status) { 1698c2ecf20Sopenharmony_ci dev_err(dev, 1708c2ecf20Sopenharmony_ci "Error in setting operating mode err %d\n", status); 1718c2ecf20Sopenharmony_ci return status; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci msleep(ISL29018_CONV_TIME_MS); 1748c2ecf20Sopenharmony_ci status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb); 1758c2ecf20Sopenharmony_ci if (status < 0) { 1768c2ecf20Sopenharmony_ci dev_err(dev, 1778c2ecf20Sopenharmony_ci "Error in reading LSB DATA with err %d\n", status); 1788c2ecf20Sopenharmony_ci return status; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb); 1828c2ecf20Sopenharmony_ci if (status < 0) { 1838c2ecf20Sopenharmony_ci dev_err(dev, 1848c2ecf20Sopenharmony_ci "Error in reading MSB DATA with error %d\n", status); 1858c2ecf20Sopenharmony_ci return status; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci dev_vdbg(dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return (msb << 8) | lsb; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int isl29018_read_lux(struct isl29018_chip *chip, int *lux) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int lux_data; 1958c2ecf20Sopenharmony_ci unsigned int data_x_range; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci lux_data = isl29018_read_sensor_input(chip, 1988c2ecf20Sopenharmony_ci ISL29018_CMD1_OPMODE_ALS_ONCE); 1998c2ecf20Sopenharmony_ci if (lux_data < 0) 2008c2ecf20Sopenharmony_ci return lux_data; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci data_x_range = lux_data * chip->scale.scale + 2038c2ecf20Sopenharmony_ci lux_data * chip->scale.uscale / 1000000; 2048c2ecf20Sopenharmony_ci *lux = data_x_range * chip->calibscale + 2058c2ecf20Sopenharmony_ci data_x_range * chip->ucalibscale / 1000000; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int isl29018_read_ir(struct isl29018_chip *chip, int *ir) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int ir_data; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ir_data = isl29018_read_sensor_input(chip, 2158c2ecf20Sopenharmony_ci ISL29018_CMD1_OPMODE_IR_ONCE); 2168c2ecf20Sopenharmony_ci if (ir_data < 0) 2178c2ecf20Sopenharmony_ci return ir_data; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci *ir = ir_data; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme, 2258c2ecf20Sopenharmony_ci int *near_ir) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int status; 2288c2ecf20Sopenharmony_ci int prox_data = -1; 2298c2ecf20Sopenharmony_ci int ir_data = -1; 2308c2ecf20Sopenharmony_ci struct device *dev = regmap_get_device(chip->regmap); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Do proximity sensing with required scheme */ 2338c2ecf20Sopenharmony_ci status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, 2348c2ecf20Sopenharmony_ci ISL29018_CMD2_SCHEME_MASK, 2358c2ecf20Sopenharmony_ci scheme << ISL29018_CMD2_SCHEME_SHIFT); 2368c2ecf20Sopenharmony_ci if (status) { 2378c2ecf20Sopenharmony_ci dev_err(dev, "Error in setting operating mode\n"); 2388c2ecf20Sopenharmony_ci return status; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci prox_data = isl29018_read_sensor_input(chip, 2428c2ecf20Sopenharmony_ci ISL29018_CMD1_OPMODE_PROX_ONCE); 2438c2ecf20Sopenharmony_ci if (prox_data < 0) 2448c2ecf20Sopenharmony_ci return prox_data; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (scheme == 1) { 2478c2ecf20Sopenharmony_ci *near_ir = prox_data; 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ir_data = isl29018_read_sensor_input(chip, 2528c2ecf20Sopenharmony_ci ISL29018_CMD1_OPMODE_IR_ONCE); 2538c2ecf20Sopenharmony_ci if (ir_data < 0) 2548c2ecf20Sopenharmony_ci return ir_data; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (prox_data >= ir_data) 2578c2ecf20Sopenharmony_ci *near_ir = prox_data - ir_data; 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci *near_ir = 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic ssize_t in_illuminance_scale_available_show 2658c2ecf20Sopenharmony_ci (struct device *dev, struct device_attribute *attr, 2668c2ecf20Sopenharmony_ci char *buf) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2698c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 2708c2ecf20Sopenharmony_ci unsigned int i; 2718c2ecf20Sopenharmony_ci int len = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 2748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) 2758c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%d.%06d ", 2768c2ecf20Sopenharmony_ci isl29018_scales[chip->int_time][i].scale, 2778c2ecf20Sopenharmony_ci isl29018_scales[chip->int_time][i].uscale); 2788c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return len; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic ssize_t in_illuminance_integration_time_available_show 2868c2ecf20Sopenharmony_ci (struct device *dev, struct device_attribute *attr, 2878c2ecf20Sopenharmony_ci char *buf) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 2908c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 2918c2ecf20Sopenharmony_ci unsigned int i; 2928c2ecf20Sopenharmony_ci int len = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) 2958c2ecf20Sopenharmony_ci len += sprintf(buf + len, "0.%06d ", 2968c2ecf20Sopenharmony_ci isl29018_int_utimes[chip->type][i]); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return len; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* 3048c2ecf20Sopenharmony_ci * From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the 3058c2ecf20Sopenharmony_ci * infrared suppression: 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * Proximity Sensing Scheme: Bit 7. This bit programs the function 3088c2ecf20Sopenharmony_ci * of the proximity detection. Logic 0 of this bit, Scheme 0, makes 3098c2ecf20Sopenharmony_ci * full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range 3108c2ecf20Sopenharmony_ci * of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit, 3118c2ecf20Sopenharmony_ci * Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary) 3128c2ecf20Sopenharmony_ci * proximity_less_ambient detection. The range of Scheme 1 3138c2ecf20Sopenharmony_ci * proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended 3148c2ecf20Sopenharmony_ci * for resolutions less than 16. While Scheme 0 has wider dynamic 3158c2ecf20Sopenharmony_ci * range, Scheme 1 proximity detection is less affected by the 3168c2ecf20Sopenharmony_ci * ambient IR noise variation. 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * 0 Sensing IR from LED and ambient 3198c2ecf20Sopenharmony_ci * 1 Sensing IR from LED with ambient IR rejection 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_cistatic ssize_t proximity_on_chip_ambient_infrared_suppression_show 3228c2ecf20Sopenharmony_ci (struct device *dev, struct device_attribute *attr, 3238c2ecf20Sopenharmony_ci char *buf) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3268c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Return the "proximity scheme" i.e. if the chip does on chip 3308c2ecf20Sopenharmony_ci * infrared suppression (1 means perform on chip suppression) 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->prox_scheme); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic ssize_t proximity_on_chip_ambient_infrared_suppression_store 3368c2ecf20Sopenharmony_ci (struct device *dev, struct device_attribute *attr, 3378c2ecf20Sopenharmony_ci const char *buf, size_t count) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3408c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 3418c2ecf20Sopenharmony_ci int val; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, &val)) 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci if (!(val == 0 || val == 1)) 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* 3498c2ecf20Sopenharmony_ci * Get the "proximity scheme" i.e. if the chip does on chip 3508c2ecf20Sopenharmony_ci * infrared suppression (1 means perform on chip suppression) 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 3538c2ecf20Sopenharmony_ci chip->prox_scheme = val; 3548c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return count; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int isl29018_write_raw(struct iio_dev *indio_dev, 3608c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 3618c2ecf20Sopenharmony_ci int val, 3628c2ecf20Sopenharmony_ci int val2, 3638c2ecf20Sopenharmony_ci long mask) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 3668c2ecf20Sopenharmony_ci int ret = -EINVAL; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 3698c2ecf20Sopenharmony_ci if (chip->suspended) { 3708c2ecf20Sopenharmony_ci ret = -EBUSY; 3718c2ecf20Sopenharmony_ci goto write_done; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci switch (mask) { 3748c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 3758c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) { 3768c2ecf20Sopenharmony_ci chip->calibscale = val; 3778c2ecf20Sopenharmony_ci chip->ucalibscale = val2; 3788c2ecf20Sopenharmony_ci ret = 0; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 3828c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT && !val) 3838c2ecf20Sopenharmony_ci ret = isl29018_set_integration_time(chip, val2); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 3868c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) 3878c2ecf20Sopenharmony_ci ret = isl29018_set_scale(chip, val, val2); 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci default: 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciwrite_done: 3948c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int isl29018_read_raw(struct iio_dev *indio_dev, 4008c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4018c2ecf20Sopenharmony_ci int *val, 4028c2ecf20Sopenharmony_ci int *val2, 4038c2ecf20Sopenharmony_ci long mask) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci int ret = -EINVAL; 4068c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(indio_dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 4098c2ecf20Sopenharmony_ci if (chip->suspended) { 4108c2ecf20Sopenharmony_ci ret = -EBUSY; 4118c2ecf20Sopenharmony_ci goto read_done; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci switch (mask) { 4148c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4158c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 4168c2ecf20Sopenharmony_ci switch (chan->type) { 4178c2ecf20Sopenharmony_ci case IIO_LIGHT: 4188c2ecf20Sopenharmony_ci ret = isl29018_read_lux(chip, val); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case IIO_INTENSITY: 4218c2ecf20Sopenharmony_ci ret = isl29018_read_ir(chip, val); 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 4248c2ecf20Sopenharmony_ci ret = isl29018_read_proximity_ir(chip, 4258c2ecf20Sopenharmony_ci chip->prox_scheme, 4268c2ecf20Sopenharmony_ci val); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci if (!ret) 4328c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 4358c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) { 4368c2ecf20Sopenharmony_ci *val = 0; 4378c2ecf20Sopenharmony_ci *val2 = isl29018_int_utimes[chip->type][chip->int_time]; 4388c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 4428c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) { 4438c2ecf20Sopenharmony_ci *val = chip->scale.scale; 4448c2ecf20Sopenharmony_ci *val2 = chip->scale.uscale; 4458c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 4498c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) { 4508c2ecf20Sopenharmony_ci *val = chip->calibscale; 4518c2ecf20Sopenharmony_ci *val2 = chip->ucalibscale; 4528c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciread_done: 4608c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci#define ISL29018_LIGHT_CHANNEL { \ 4668c2ecf20Sopenharmony_ci .type = IIO_LIGHT, \ 4678c2ecf20Sopenharmony_ci .indexed = 1, \ 4688c2ecf20Sopenharmony_ci .channel = 0, \ 4698c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \ 4708c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | \ 4718c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE) | \ 4728c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci#define ISL29018_IR_CHANNEL { \ 4768c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 4778c2ecf20Sopenharmony_ci .modified = 1, \ 4788c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 4798c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_IR, \ 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci#define ISL29018_PROXIMITY_CHANNEL { \ 4838c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, \ 4848c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic const struct iio_chan_spec isl29018_channels[] = { 4888c2ecf20Sopenharmony_ci ISL29018_LIGHT_CHANNEL, 4898c2ecf20Sopenharmony_ci ISL29018_IR_CHANNEL, 4908c2ecf20Sopenharmony_ci ISL29018_PROXIMITY_CHANNEL, 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic const struct iio_chan_spec isl29023_channels[] = { 4948c2ecf20Sopenharmony_ci ISL29018_LIGHT_CHANNEL, 4958c2ecf20Sopenharmony_ci ISL29018_IR_CHANNEL, 4968c2ecf20Sopenharmony_ci}; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0); 4998c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0); 5008c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic struct attribute *isl29018_attributes[] = { 5058c2ecf20Sopenharmony_ci ISL29018_DEV_ATTR(in_illuminance_scale_available), 5068c2ecf20Sopenharmony_ci ISL29018_DEV_ATTR(in_illuminance_integration_time_available), 5078c2ecf20Sopenharmony_ci ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression), 5088c2ecf20Sopenharmony_ci NULL 5098c2ecf20Sopenharmony_ci}; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic struct attribute *isl29023_attributes[] = { 5128c2ecf20Sopenharmony_ci ISL29018_DEV_ATTR(in_illuminance_scale_available), 5138c2ecf20Sopenharmony_ci ISL29018_DEV_ATTR(in_illuminance_integration_time_available), 5148c2ecf20Sopenharmony_ci NULL 5158c2ecf20Sopenharmony_ci}; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct attribute_group isl29018_group = { 5188c2ecf20Sopenharmony_ci .attrs = isl29018_attributes, 5198c2ecf20Sopenharmony_ci}; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic const struct attribute_group isl29023_group = { 5228c2ecf20Sopenharmony_ci .attrs = isl29023_attributes, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cienum { 5268c2ecf20Sopenharmony_ci isl29018, 5278c2ecf20Sopenharmony_ci isl29023, 5288c2ecf20Sopenharmony_ci isl29035, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int isl29018_chip_init(struct isl29018_chip *chip) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci int status; 5348c2ecf20Sopenharmony_ci struct device *dev = regmap_get_device(chip->regmap); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (chip->type == isl29035) { 5378c2ecf20Sopenharmony_ci unsigned int id; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id); 5408c2ecf20Sopenharmony_ci if (status < 0) { 5418c2ecf20Sopenharmony_ci dev_err(dev, 5428c2ecf20Sopenharmony_ci "Error reading ID register with error %d\n", 5438c2ecf20Sopenharmony_ci status); 5448c2ecf20Sopenharmony_ci return status; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (id != ISL29035_DEVICE_ID) 5508c2ecf20Sopenharmony_ci return -ENODEV; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Clear brownout bit */ 5538c2ecf20Sopenharmony_ci status = regmap_update_bits(chip->regmap, 5548c2ecf20Sopenharmony_ci ISL29035_REG_DEVICE_ID, 5558c2ecf20Sopenharmony_ci ISL29035_BOUT_MASK, 0); 5568c2ecf20Sopenharmony_ci if (status < 0) 5578c2ecf20Sopenharmony_ci return status; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* 5618c2ecf20Sopenharmony_ci * Code added per Intersil Application Note 1534: 5628c2ecf20Sopenharmony_ci * When VDD sinks to approximately 1.8V or below, some of 5638c2ecf20Sopenharmony_ci * the part's registers may change their state. When VDD 5648c2ecf20Sopenharmony_ci * recovers to 2.25V (or greater), the part may thus be in an 5658c2ecf20Sopenharmony_ci * unknown mode of operation. The user can return the part to 5668c2ecf20Sopenharmony_ci * a known mode of operation either by (a) setting VDD = 0V for 5678c2ecf20Sopenharmony_ci * 1 second or more and then powering back up with a slew rate 5688c2ecf20Sopenharmony_ci * of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX 5698c2ecf20Sopenharmony_ci * conversions, clear the test registers, and then rewrite all 5708c2ecf20Sopenharmony_ci * registers to the desired values. 5718c2ecf20Sopenharmony_ci * ... 5728c2ecf20Sopenharmony_ci * For ISL29011, ISL29018, ISL29021, ISL29023 5738c2ecf20Sopenharmony_ci * 1. Write 0x00 to register 0x08 (TEST) 5748c2ecf20Sopenharmony_ci * 2. Write 0x00 to register 0x00 (CMD1) 5758c2ecf20Sopenharmony_ci * 3. Rewrite all registers to the desired values 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says 5788c2ecf20Sopenharmony_ci * the same thing EXCEPT the data sheet asks for a 1ms delay after 5798c2ecf20Sopenharmony_ci * writing the CMD1 register. 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_ci status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0); 5828c2ecf20Sopenharmony_ci if (status < 0) { 5838c2ecf20Sopenharmony_ci dev_err(dev, "Failed to clear isl29018 TEST reg.(%d)\n", 5848c2ecf20Sopenharmony_ci status); 5858c2ecf20Sopenharmony_ci return status; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * See Intersil AN1534 comments above. 5908c2ecf20Sopenharmony_ci * "Operating Mode" (COMMAND1) register is reprogrammed when 5918c2ecf20Sopenharmony_ci * data is read from the device. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0); 5948c2ecf20Sopenharmony_ci if (status < 0) { 5958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to clear isl29018 CMD1 reg.(%d)\n", 5968c2ecf20Sopenharmony_ci status); 5978c2ecf20Sopenharmony_ci return status; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci usleep_range(1000, 2000); /* per data sheet, page 10 */ 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Set defaults */ 6038c2ecf20Sopenharmony_ci status = isl29018_set_scale(chip, chip->scale.scale, 6048c2ecf20Sopenharmony_ci chip->scale.uscale); 6058c2ecf20Sopenharmony_ci if (status < 0) { 6068c2ecf20Sopenharmony_ci dev_err(dev, "Init of isl29018 fails\n"); 6078c2ecf20Sopenharmony_ci return status; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci status = isl29018_set_integration_time(chip, 6118c2ecf20Sopenharmony_ci isl29018_int_utimes[chip->type][chip->int_time]); 6128c2ecf20Sopenharmony_ci if (status < 0) 6138c2ecf20Sopenharmony_ci dev_err(dev, "Init of isl29018 fails\n"); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return status; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic const struct iio_info isl29018_info = { 6198c2ecf20Sopenharmony_ci .attrs = &isl29018_group, 6208c2ecf20Sopenharmony_ci .read_raw = isl29018_read_raw, 6218c2ecf20Sopenharmony_ci .write_raw = isl29018_write_raw, 6228c2ecf20Sopenharmony_ci}; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic const struct iio_info isl29023_info = { 6258c2ecf20Sopenharmony_ci .attrs = &isl29023_group, 6268c2ecf20Sopenharmony_ci .read_raw = isl29018_read_raw, 6278c2ecf20Sopenharmony_ci .write_raw = isl29018_write_raw, 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci switch (reg) { 6338c2ecf20Sopenharmony_ci case ISL29018_REG_ADD_DATA_LSB: 6348c2ecf20Sopenharmony_ci case ISL29018_REG_ADD_DATA_MSB: 6358c2ecf20Sopenharmony_ci case ISL29018_REG_ADD_COMMAND1: 6368c2ecf20Sopenharmony_ci case ISL29018_REG_TEST: 6378c2ecf20Sopenharmony_ci case ISL29035_REG_DEVICE_ID: 6388c2ecf20Sopenharmony_ci return true; 6398c2ecf20Sopenharmony_ci default: 6408c2ecf20Sopenharmony_ci return false; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic const struct regmap_config isl29018_regmap_config = { 6458c2ecf20Sopenharmony_ci .reg_bits = 8, 6468c2ecf20Sopenharmony_ci .val_bits = 8, 6478c2ecf20Sopenharmony_ci .volatile_reg = isl29018_is_volatile_reg, 6488c2ecf20Sopenharmony_ci .max_register = ISL29018_REG_TEST, 6498c2ecf20Sopenharmony_ci .num_reg_defaults_raw = ISL29018_REG_TEST + 1, 6508c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic const struct regmap_config isl29035_regmap_config = { 6548c2ecf20Sopenharmony_ci .reg_bits = 8, 6558c2ecf20Sopenharmony_ci .val_bits = 8, 6568c2ecf20Sopenharmony_ci .volatile_reg = isl29018_is_volatile_reg, 6578c2ecf20Sopenharmony_ci .max_register = ISL29035_REG_DEVICE_ID, 6588c2ecf20Sopenharmony_ci .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1, 6598c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 6608c2ecf20Sopenharmony_ci}; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistruct isl29018_chip_info { 6638c2ecf20Sopenharmony_ci const struct iio_chan_spec *channels; 6648c2ecf20Sopenharmony_ci int num_channels; 6658c2ecf20Sopenharmony_ci const struct iio_info *indio_info; 6668c2ecf20Sopenharmony_ci const struct regmap_config *regmap_cfg; 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic const struct isl29018_chip_info isl29018_chip_info_tbl[] = { 6708c2ecf20Sopenharmony_ci [isl29018] = { 6718c2ecf20Sopenharmony_ci .channels = isl29018_channels, 6728c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(isl29018_channels), 6738c2ecf20Sopenharmony_ci .indio_info = &isl29018_info, 6748c2ecf20Sopenharmony_ci .regmap_cfg = &isl29018_regmap_config, 6758c2ecf20Sopenharmony_ci }, 6768c2ecf20Sopenharmony_ci [isl29023] = { 6778c2ecf20Sopenharmony_ci .channels = isl29023_channels, 6788c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(isl29023_channels), 6798c2ecf20Sopenharmony_ci .indio_info = &isl29023_info, 6808c2ecf20Sopenharmony_ci .regmap_cfg = &isl29018_regmap_config, 6818c2ecf20Sopenharmony_ci }, 6828c2ecf20Sopenharmony_ci [isl29035] = { 6838c2ecf20Sopenharmony_ci .channels = isl29023_channels, 6848c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(isl29023_channels), 6858c2ecf20Sopenharmony_ci .indio_info = &isl29023_info, 6868c2ecf20Sopenharmony_ci .regmap_cfg = &isl29035_regmap_config, 6878c2ecf20Sopenharmony_ci }, 6888c2ecf20Sopenharmony_ci}; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic const char *isl29018_match_acpi_device(struct device *dev, int *data) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci const struct acpi_device_id *id; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci id = acpi_match_device(dev->driver->acpi_match_table, dev); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (!id) 6978c2ecf20Sopenharmony_ci return NULL; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci *data = (int)id->driver_data; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return dev_name(dev); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic void isl29018_disable_regulator_action(void *_data) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct isl29018_chip *chip = _data; 7078c2ecf20Sopenharmony_ci int err; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci err = regulator_disable(chip->vcc_reg); 7108c2ecf20Sopenharmony_ci if (err) 7118c2ecf20Sopenharmony_ci pr_err("failed to disable isl29018's VCC regulator!\n"); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic int isl29018_probe(struct i2c_client *client, 7158c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct isl29018_chip *chip; 7188c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 7198c2ecf20Sopenharmony_ci int err; 7208c2ecf20Sopenharmony_ci const char *name = NULL; 7218c2ecf20Sopenharmony_ci int dev_id = 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); 7248c2ecf20Sopenharmony_ci if (!indio_dev) 7258c2ecf20Sopenharmony_ci return -ENOMEM; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci chip = iio_priv(indio_dev); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (id) { 7328c2ecf20Sopenharmony_ci name = id->name; 7338c2ecf20Sopenharmony_ci dev_id = id->driver_data; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (ACPI_HANDLE(&client->dev)) 7378c2ecf20Sopenharmony_ci name = isl29018_match_acpi_device(&client->dev, &dev_id); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci mutex_init(&chip->lock); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci chip->type = dev_id; 7428c2ecf20Sopenharmony_ci chip->calibscale = 1; 7438c2ecf20Sopenharmony_ci chip->ucalibscale = 0; 7448c2ecf20Sopenharmony_ci chip->int_time = ISL29018_INT_TIME_16; 7458c2ecf20Sopenharmony_ci chip->scale = isl29018_scales[chip->int_time][0]; 7468c2ecf20Sopenharmony_ci chip->suspended = false; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci chip->vcc_reg = devm_regulator_get(&client->dev, "vcc"); 7498c2ecf20Sopenharmony_ci if (IS_ERR(chip->vcc_reg)) 7508c2ecf20Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(chip->vcc_reg), 7518c2ecf20Sopenharmony_ci "failed to get VCC regulator!\n"); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci err = regulator_enable(chip->vcc_reg); 7548c2ecf20Sopenharmony_ci if (err) { 7558c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to enable VCC regulator!\n"); 7568c2ecf20Sopenharmony_ci return err; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action, 7608c2ecf20Sopenharmony_ci chip); 7618c2ecf20Sopenharmony_ci if (err) { 7628c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to setup regulator cleanup action!\n"); 7638c2ecf20Sopenharmony_ci return err; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci chip->regmap = devm_regmap_init_i2c(client, 7678c2ecf20Sopenharmony_ci isl29018_chip_info_tbl[dev_id].regmap_cfg); 7688c2ecf20Sopenharmony_ci if (IS_ERR(chip->regmap)) { 7698c2ecf20Sopenharmony_ci err = PTR_ERR(chip->regmap); 7708c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap initialization fails: %d\n", err); 7718c2ecf20Sopenharmony_ci return err; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci err = isl29018_chip_init(chip); 7758c2ecf20Sopenharmony_ci if (err) 7768c2ecf20Sopenharmony_ci return err; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info; 7798c2ecf20Sopenharmony_ci indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels; 7808c2ecf20Sopenharmony_ci indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels; 7818c2ecf20Sopenharmony_ci indio_dev->name = name; 7828c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 7888c2ecf20Sopenharmony_cistatic int isl29018_suspend(struct device *dev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); 7918c2ecf20Sopenharmony_ci int ret; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * Since this driver uses only polling commands, we are by default in 7978c2ecf20Sopenharmony_ci * auto shutdown (ie, power-down) mode. 7988c2ecf20Sopenharmony_ci * So we do not have much to do here. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci chip->suspended = true; 8018c2ecf20Sopenharmony_ci ret = regulator_disable(chip->vcc_reg); 8028c2ecf20Sopenharmony_ci if (ret) 8038c2ecf20Sopenharmony_ci dev_err(dev, "failed to disable VCC regulator\n"); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return ret; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic int isl29018_resume(struct device *dev) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev)); 8138c2ecf20Sopenharmony_ci int err; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci err = regulator_enable(chip->vcc_reg); 8188c2ecf20Sopenharmony_ci if (err) { 8198c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable VCC regulator\n"); 8208c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 8218c2ecf20Sopenharmony_ci return err; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci err = isl29018_chip_init(chip); 8258c2ecf20Sopenharmony_ci if (!err) 8268c2ecf20Sopenharmony_ci chip->suspended = false; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return err; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume); 8348c2ecf20Sopenharmony_ci#define ISL29018_PM_OPS (&isl29018_pm_ops) 8358c2ecf20Sopenharmony_ci#else 8368c2ecf20Sopenharmony_ci#define ISL29018_PM_OPS NULL 8378c2ecf20Sopenharmony_ci#endif 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 8408c2ecf20Sopenharmony_cistatic const struct acpi_device_id isl29018_acpi_match[] = { 8418c2ecf20Sopenharmony_ci {"ISL29018", isl29018}, 8428c2ecf20Sopenharmony_ci {"ISL29023", isl29023}, 8438c2ecf20Sopenharmony_ci {"ISL29035", isl29035}, 8448c2ecf20Sopenharmony_ci {}, 8458c2ecf20Sopenharmony_ci}; 8468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, isl29018_acpi_match); 8478c2ecf20Sopenharmony_ci#endif 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic const struct i2c_device_id isl29018_id[] = { 8508c2ecf20Sopenharmony_ci {"isl29018", isl29018}, 8518c2ecf20Sopenharmony_ci {"isl29023", isl29023}, 8528c2ecf20Sopenharmony_ci {"isl29035", isl29035}, 8538c2ecf20Sopenharmony_ci {} 8548c2ecf20Sopenharmony_ci}; 8558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl29018_id); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic const struct of_device_id isl29018_of_match[] = { 8588c2ecf20Sopenharmony_ci { .compatible = "isil,isl29018", }, 8598c2ecf20Sopenharmony_ci { .compatible = "isil,isl29023", }, 8608c2ecf20Sopenharmony_ci { .compatible = "isil,isl29035", }, 8618c2ecf20Sopenharmony_ci { }, 8628c2ecf20Sopenharmony_ci}; 8638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, isl29018_of_match); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic struct i2c_driver isl29018_driver = { 8668c2ecf20Sopenharmony_ci .driver = { 8678c2ecf20Sopenharmony_ci .name = "isl29018", 8688c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(isl29018_acpi_match), 8698c2ecf20Sopenharmony_ci .pm = ISL29018_PM_OPS, 8708c2ecf20Sopenharmony_ci .of_match_table = isl29018_of_match, 8718c2ecf20Sopenharmony_ci }, 8728c2ecf20Sopenharmony_ci .probe = isl29018_probe, 8738c2ecf20Sopenharmony_ci .id_table = isl29018_id, 8748c2ecf20Sopenharmony_ci}; 8758c2ecf20Sopenharmony_cimodule_i2c_driver(isl29018_driver); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver"); 8788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 879