18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VEML6030 Ambient Light Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf 88c2ecf20Sopenharmony_ci * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf 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/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 188c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Device registers */ 228c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_CONF 0x00 238c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_WH 0x01 248c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_WL 0x02 258c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_PSM 0x03 268c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_DATA 0x04 278c2ecf20Sopenharmony_ci#define VEML6030_REG_WH_DATA 0x05 288c2ecf20Sopenharmony_ci#define VEML6030_REG_ALS_INT 0x06 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Bit masks for specific functionality */ 318c2ecf20Sopenharmony_ci#define VEML6030_ALS_IT GENMASK(9, 6) 328c2ecf20Sopenharmony_ci#define VEML6030_PSM GENMASK(2, 1) 338c2ecf20Sopenharmony_ci#define VEML6030_ALS_PERS GENMASK(5, 4) 348c2ecf20Sopenharmony_ci#define VEML6030_ALS_GAIN GENMASK(12, 11) 358c2ecf20Sopenharmony_ci#define VEML6030_PSM_EN BIT(0) 368c2ecf20Sopenharmony_ci#define VEML6030_INT_TH_LOW BIT(15) 378c2ecf20Sopenharmony_ci#define VEML6030_INT_TH_HIGH BIT(14) 388c2ecf20Sopenharmony_ci#define VEML6030_ALS_INT_EN BIT(1) 398c2ecf20Sopenharmony_ci#define VEML6030_ALS_SD BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * The resolution depends on both gain and integration time. The 438c2ecf20Sopenharmony_ci * cur_resolution stores one of the resolution mentioned in the 448c2ecf20Sopenharmony_ci * table during startup and gets updated whenever integration time 458c2ecf20Sopenharmony_ci * or gain is changed. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Table 'resolution and maximum detection range' in appnote 84367 488c2ecf20Sopenharmony_ci * is visualized as a 2D array. The cur_gain stores index of gain 498c2ecf20Sopenharmony_ci * in this table (0-3) while the cur_integration_time holds index 508c2ecf20Sopenharmony_ci * of integration time (0-5). 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistruct veml6030_data { 538c2ecf20Sopenharmony_ci struct i2c_client *client; 548c2ecf20Sopenharmony_ci struct regmap *regmap; 558c2ecf20Sopenharmony_ci int cur_resolution; 568c2ecf20Sopenharmony_ci int cur_gain; 578c2ecf20Sopenharmony_ci int cur_integration_time; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Integration time available in seconds */ 618c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(in_illuminance_integration_time_available, 628c2ecf20Sopenharmony_ci "0.025 0.05 0.1 0.2 0.4 0.8"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is 668c2ecf20Sopenharmony_ci * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(in_illuminance_scale_available, 698c2ecf20Sopenharmony_ci "0.125 0.25 1.0 2.0"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct attribute *veml6030_attributes[] = { 728c2ecf20Sopenharmony_ci &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, 738c2ecf20Sopenharmony_ci &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, 748c2ecf20Sopenharmony_ci NULL 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct attribute_group veml6030_attr_group = { 788c2ecf20Sopenharmony_ci .attrs = veml6030_attributes, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Persistence = 1/2/4/8 x integration time 838c2ecf20Sopenharmony_ci * Minimum time for which light readings must stay above configured 848c2ecf20Sopenharmony_ci * threshold to assert the interrupt. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic const char * const period_values[] = { 878c2ecf20Sopenharmony_ci "0.1 0.2 0.4 0.8", 888c2ecf20Sopenharmony_ci "0.2 0.4 0.8 1.6", 898c2ecf20Sopenharmony_ci "0.4 0.8 1.6 3.2", 908c2ecf20Sopenharmony_ci "0.8 1.6 3.2 6.4", 918c2ecf20Sopenharmony_ci "0.05 0.1 0.2 0.4", 928c2ecf20Sopenharmony_ci "0.025 0.050 0.1 0.2" 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Return list of valid period values in seconds corresponding to 978c2ecf20Sopenharmony_ci * the currently active integration time. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic ssize_t in_illuminance_period_available_show(struct device *dev, 1008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int ret, reg, x; 1038c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 1048c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); 1078c2ecf20Sopenharmony_ci if (ret) { 1088c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 1098c2ecf20Sopenharmony_ci "can't read als conf register %d\n", ret); 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = ((reg >> 6) & 0xF); 1148c2ecf20Sopenharmony_ci switch (ret) { 1158c2ecf20Sopenharmony_ci case 0: 1168c2ecf20Sopenharmony_ci case 1: 1178c2ecf20Sopenharmony_ci case 2: 1188c2ecf20Sopenharmony_ci case 3: 1198c2ecf20Sopenharmony_ci x = ret; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case 8: 1228c2ecf20Sopenharmony_ci x = 4; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 12: 1258c2ecf20Sopenharmony_ci x = 5; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci default: 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", period_values[x]); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct attribute *veml6030_event_attributes[] = { 1378c2ecf20Sopenharmony_ci &iio_dev_attr_in_illuminance_period_available.dev_attr.attr, 1388c2ecf20Sopenharmony_ci NULL 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct attribute_group veml6030_event_attr_group = { 1428c2ecf20Sopenharmony_ci .attrs = veml6030_event_attributes, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int veml6030_als_pwr_on(struct veml6030_data *data) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 1488c2ecf20Sopenharmony_ci VEML6030_ALS_SD, 0); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int veml6030_als_shut_down(struct veml6030_data *data) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 1548c2ecf20Sopenharmony_ci VEML6030_ALS_SD, 1); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void veml6030_als_shut_down_action(void *data) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci veml6030_als_shut_down(data); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic const struct iio_event_spec veml6030_event_spec[] = { 1638c2ecf20Sopenharmony_ci { 1648c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 1658c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 1668c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 1678c2ecf20Sopenharmony_ci }, { 1688c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 1698c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 1708c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 1718c2ecf20Sopenharmony_ci }, { 1728c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 1738c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 1748c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_PERIOD) | 1758c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 1768c2ecf20Sopenharmony_ci }, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* Channel number */ 1808c2ecf20Sopenharmony_cienum veml6030_chan { 1818c2ecf20Sopenharmony_ci CH_ALS, 1828c2ecf20Sopenharmony_ci CH_WHITE, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct iio_chan_spec veml6030_channels[] = { 1868c2ecf20Sopenharmony_ci { 1878c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 1888c2ecf20Sopenharmony_ci .channel = CH_ALS, 1898c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1908c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_PROCESSED) | 1918c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 1928c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1938c2ecf20Sopenharmony_ci .event_spec = veml6030_event_spec, 1948c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(veml6030_event_spec), 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci { 1978c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 1988c2ecf20Sopenharmony_ci .channel = CH_WHITE, 1998c2ecf20Sopenharmony_ci .modified = 1, 2008c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_BOTH, 2018c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 2028c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_PROCESSED), 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic const struct regmap_config veml6030_regmap_config = { 2078c2ecf20Sopenharmony_ci .name = "veml6030_regmap", 2088c2ecf20Sopenharmony_ci .reg_bits = 8, 2098c2ecf20Sopenharmony_ci .val_bits = 16, 2108c2ecf20Sopenharmony_ci .max_register = VEML6030_REG_ALS_INT, 2118c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int veml6030_get_intgrn_tm(struct iio_dev *indio_dev, 2158c2ecf20Sopenharmony_ci int *val, int *val2) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int ret, reg; 2188c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); 2218c2ecf20Sopenharmony_ci if (ret) { 2228c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 2238c2ecf20Sopenharmony_ci "can't read als conf register %d\n", ret); 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci switch ((reg >> 6) & 0xF) { 2288c2ecf20Sopenharmony_ci case 0: 2298c2ecf20Sopenharmony_ci *val2 = 100000; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case 1: 2328c2ecf20Sopenharmony_ci *val2 = 200000; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case 2: 2358c2ecf20Sopenharmony_ci *val2 = 400000; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case 3: 2388c2ecf20Sopenharmony_ci *val2 = 800000; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case 8: 2418c2ecf20Sopenharmony_ci *val2 = 50000; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case 12: 2448c2ecf20Sopenharmony_ci *val2 = 25000; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci *val = 0; 2518c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int veml6030_set_intgrn_tm(struct iio_dev *indio_dev, 2558c2ecf20Sopenharmony_ci int val, int val2) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int ret, new_int_time, int_idx; 2588c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (val) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci switch (val2) { 2648c2ecf20Sopenharmony_ci case 25000: 2658c2ecf20Sopenharmony_ci new_int_time = 0x300; 2668c2ecf20Sopenharmony_ci int_idx = 5; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case 50000: 2698c2ecf20Sopenharmony_ci new_int_time = 0x200; 2708c2ecf20Sopenharmony_ci int_idx = 4; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case 100000: 2738c2ecf20Sopenharmony_ci new_int_time = 0x00; 2748c2ecf20Sopenharmony_ci int_idx = 3; 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci case 200000: 2778c2ecf20Sopenharmony_ci new_int_time = 0x40; 2788c2ecf20Sopenharmony_ci int_idx = 2; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case 400000: 2818c2ecf20Sopenharmony_ci new_int_time = 0x80; 2828c2ecf20Sopenharmony_ci int_idx = 1; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case 800000: 2858c2ecf20Sopenharmony_ci new_int_time = 0xC0; 2868c2ecf20Sopenharmony_ci int_idx = 0; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci default: 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 2938c2ecf20Sopenharmony_ci VEML6030_ALS_IT, new_int_time); 2948c2ecf20Sopenharmony_ci if (ret) { 2958c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 2968c2ecf20Sopenharmony_ci "can't update als integration time %d\n", ret); 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Cache current integration time and update resolution. For every 3028c2ecf20Sopenharmony_ci * increase in integration time to next level, resolution is halved 3038c2ecf20Sopenharmony_ci * and vice-versa. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (data->cur_integration_time < int_idx) 3068c2ecf20Sopenharmony_ci data->cur_resolution <<= int_idx - data->cur_integration_time; 3078c2ecf20Sopenharmony_ci else if (data->cur_integration_time > int_idx) 3088c2ecf20Sopenharmony_ci data->cur_resolution >>= data->cur_integration_time - int_idx; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci data->cur_integration_time = int_idx; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int veml6030_read_persistence(struct iio_dev *indio_dev, 3168c2ecf20Sopenharmony_ci int *val, int *val2) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int ret, reg, period, x, y; 3198c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = veml6030_get_intgrn_tm(indio_dev, &x, &y); 3228c2ecf20Sopenharmony_ci if (ret < 0) 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); 3268c2ecf20Sopenharmony_ci if (ret) { 3278c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 3288c2ecf20Sopenharmony_ci "can't read als conf register %d\n", ret); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* integration time multiplied by 1/2/4/8 */ 3328c2ecf20Sopenharmony_ci period = y * (1 << ((reg >> 4) & 0x03)); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci *val = period / 1000000; 3358c2ecf20Sopenharmony_ci *val2 = period % 1000000; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int veml6030_write_persistence(struct iio_dev *indio_dev, 3418c2ecf20Sopenharmony_ci int val, int val2) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci int ret, period, x, y; 3448c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret = veml6030_get_intgrn_tm(indio_dev, &x, &y); 3478c2ecf20Sopenharmony_ci if (ret < 0) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!val) { 3518c2ecf20Sopenharmony_ci period = val2 / y; 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci if ((val == 1) && (val2 == 600000)) 3548c2ecf20Sopenharmony_ci period = 1600000 / y; 3558c2ecf20Sopenharmony_ci else if ((val == 3) && (val2 == 200000)) 3568c2ecf20Sopenharmony_ci period = 3200000 / y; 3578c2ecf20Sopenharmony_ci else if ((val == 6) && (val2 == 400000)) 3588c2ecf20Sopenharmony_ci period = 6400000 / y; 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci period = -1; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (period <= 0 || period > 8 || hweight8(period) != 1) 3648c2ecf20Sopenharmony_ci return -EINVAL; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 3678c2ecf20Sopenharmony_ci VEML6030_ALS_PERS, (ffs(period) - 1) << 4); 3688c2ecf20Sopenharmony_ci if (ret) 3698c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 3708c2ecf20Sopenharmony_ci "can't set persistence value %d\n", ret); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int veml6030_set_als_gain(struct iio_dev *indio_dev, 3768c2ecf20Sopenharmony_ci int val, int val2) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci int ret, new_gain, gain_idx; 3798c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (val == 0 && val2 == 125000) { 3828c2ecf20Sopenharmony_ci new_gain = 0x1000; /* 0x02 << 11 */ 3838c2ecf20Sopenharmony_ci gain_idx = 3; 3848c2ecf20Sopenharmony_ci } else if (val == 0 && val2 == 250000) { 3858c2ecf20Sopenharmony_ci new_gain = 0x1800; 3868c2ecf20Sopenharmony_ci gain_idx = 2; 3878c2ecf20Sopenharmony_ci } else if (val == 1 && val2 == 0) { 3888c2ecf20Sopenharmony_ci new_gain = 0x00; 3898c2ecf20Sopenharmony_ci gain_idx = 1; 3908c2ecf20Sopenharmony_ci } else if (val == 2 && val2 == 0) { 3918c2ecf20Sopenharmony_ci new_gain = 0x800; 3928c2ecf20Sopenharmony_ci gain_idx = 0; 3938c2ecf20Sopenharmony_ci } else { 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 3988c2ecf20Sopenharmony_ci VEML6030_ALS_GAIN, new_gain); 3998c2ecf20Sopenharmony_ci if (ret) { 4008c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 4018c2ecf20Sopenharmony_ci "can't set als gain %d\n", ret); 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Cache currently set gain & update resolution. For every 4078c2ecf20Sopenharmony_ci * increase in the gain to next level, resolution is halved 4088c2ecf20Sopenharmony_ci * and vice-versa. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci if (data->cur_gain < gain_idx) 4118c2ecf20Sopenharmony_ci data->cur_resolution <<= gain_idx - data->cur_gain; 4128c2ecf20Sopenharmony_ci else if (data->cur_gain > gain_idx) 4138c2ecf20Sopenharmony_ci data->cur_resolution >>= data->cur_gain - gain_idx; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci data->cur_gain = gain_idx; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int veml6030_get_als_gain(struct iio_dev *indio_dev, 4218c2ecf20Sopenharmony_ci int *val, int *val2) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci int ret, reg; 4248c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); 4278c2ecf20Sopenharmony_ci if (ret) { 4288c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 4298c2ecf20Sopenharmony_ci "can't read als conf register %d\n", ret); 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci switch ((reg >> 11) & 0x03) { 4348c2ecf20Sopenharmony_ci case 0: 4358c2ecf20Sopenharmony_ci *val = 1; 4368c2ecf20Sopenharmony_ci *val2 = 0; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case 1: 4398c2ecf20Sopenharmony_ci *val = 2; 4408c2ecf20Sopenharmony_ci *val2 = 0; 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci case 2: 4438c2ecf20Sopenharmony_ci *val = 0; 4448c2ecf20Sopenharmony_ci *val2 = 125000; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case 3: 4478c2ecf20Sopenharmony_ci *val = 0; 4488c2ecf20Sopenharmony_ci *val2 = 250000; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci default: 4518c2ecf20Sopenharmony_ci return -EINVAL; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int veml6030_read_thresh(struct iio_dev *indio_dev, 4588c2ecf20Sopenharmony_ci int *val, int *val2, int dir) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci int ret, reg; 4618c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 4648c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_WH, ®); 4658c2ecf20Sopenharmony_ci else 4668c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_WL, ®); 4678c2ecf20Sopenharmony_ci if (ret) { 4688c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 4698c2ecf20Sopenharmony_ci "can't read als threshold value %d\n", ret); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci *val = reg & 0xffff; 4748c2ecf20Sopenharmony_ci return IIO_VAL_INT; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int veml6030_write_thresh(struct iio_dev *indio_dev, 4788c2ecf20Sopenharmony_ci int val, int val2, int dir) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int ret; 4818c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (val > 0xFFFF || val < 0 || val2) 4848c2ecf20Sopenharmony_ci return -EINVAL; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) { 4878c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, val); 4888c2ecf20Sopenharmony_ci if (ret) 4898c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 4908c2ecf20Sopenharmony_ci "can't set high threshold %d\n", ret); 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, val); 4938c2ecf20Sopenharmony_ci if (ret) 4948c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 4958c2ecf20Sopenharmony_ci "can't set low threshold %d\n", ret); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* 5028c2ecf20Sopenharmony_ci * Provide both raw as well as light reading in lux. 5038c2ecf20Sopenharmony_ci * light (in lux) = resolution * raw reading 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cistatic int veml6030_read_raw(struct iio_dev *indio_dev, 5068c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, int *val, 5078c2ecf20Sopenharmony_ci int *val2, long mask) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci int ret, reg; 5108c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 5118c2ecf20Sopenharmony_ci struct regmap *regmap = data->regmap; 5128c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci switch (mask) { 5158c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 5168c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 5178c2ecf20Sopenharmony_ci switch (chan->type) { 5188c2ecf20Sopenharmony_ci case IIO_LIGHT: 5198c2ecf20Sopenharmony_ci ret = regmap_read(regmap, VEML6030_REG_ALS_DATA, ®); 5208c2ecf20Sopenharmony_ci if (ret < 0) { 5218c2ecf20Sopenharmony_ci dev_err(dev, "can't read als data %d\n", ret); 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci if (mask == IIO_CHAN_INFO_PROCESSED) { 5258c2ecf20Sopenharmony_ci *val = (reg * data->cur_resolution) / 10000; 5268c2ecf20Sopenharmony_ci *val2 = (reg * data->cur_resolution) % 10000; 5278c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci *val = reg; 5308c2ecf20Sopenharmony_ci return IIO_VAL_INT; 5318c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5328c2ecf20Sopenharmony_ci ret = regmap_read(regmap, VEML6030_REG_WH_DATA, ®); 5338c2ecf20Sopenharmony_ci if (ret < 0) { 5348c2ecf20Sopenharmony_ci dev_err(dev, "can't read white data %d\n", ret); 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci if (mask == IIO_CHAN_INFO_PROCESSED) { 5388c2ecf20Sopenharmony_ci *val = (reg * data->cur_resolution) / 10000; 5398c2ecf20Sopenharmony_ci *val2 = (reg * data->cur_resolution) % 10000; 5408c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci *val = reg; 5438c2ecf20Sopenharmony_ci return IIO_VAL_INT; 5448c2ecf20Sopenharmony_ci default: 5458c2ecf20Sopenharmony_ci return -EINVAL; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 5488c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) 5498c2ecf20Sopenharmony_ci return veml6030_get_intgrn_tm(indio_dev, val, val2); 5508c2ecf20Sopenharmony_ci return -EINVAL; 5518c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 5528c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) 5538c2ecf20Sopenharmony_ci return veml6030_get_als_gain(indio_dev, val, val2); 5548c2ecf20Sopenharmony_ci return -EINVAL; 5558c2ecf20Sopenharmony_ci default: 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int veml6030_write_raw(struct iio_dev *indio_dev, 5618c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 5628c2ecf20Sopenharmony_ci int val, int val2, long mask) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci switch (mask) { 5658c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 5668c2ecf20Sopenharmony_ci switch (chan->type) { 5678c2ecf20Sopenharmony_ci case IIO_LIGHT: 5688c2ecf20Sopenharmony_ci return veml6030_set_intgrn_tm(indio_dev, val, val2); 5698c2ecf20Sopenharmony_ci default: 5708c2ecf20Sopenharmony_ci return -EINVAL; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 5738c2ecf20Sopenharmony_ci switch (chan->type) { 5748c2ecf20Sopenharmony_ci case IIO_LIGHT: 5758c2ecf20Sopenharmony_ci return veml6030_set_als_gain(indio_dev, val, val2); 5768c2ecf20Sopenharmony_ci default: 5778c2ecf20Sopenharmony_ci return -EINVAL; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int veml6030_read_event_val(struct iio_dev *indio_dev, 5858c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 5868c2ecf20Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, 5878c2ecf20Sopenharmony_ci int *val, int *val2) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci switch (info) { 5908c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 5918c2ecf20Sopenharmony_ci switch (dir) { 5928c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 5938c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 5948c2ecf20Sopenharmony_ci return veml6030_read_thresh(indio_dev, val, val2, dir); 5958c2ecf20Sopenharmony_ci default: 5968c2ecf20Sopenharmony_ci return -EINVAL; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case IIO_EV_INFO_PERIOD: 6008c2ecf20Sopenharmony_ci return veml6030_read_persistence(indio_dev, val, val2); 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci return -EINVAL; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int veml6030_write_event_val(struct iio_dev *indio_dev, 6078c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 6088c2ecf20Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, 6098c2ecf20Sopenharmony_ci int val, int val2) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci switch (info) { 6128c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 6138c2ecf20Sopenharmony_ci return veml6030_write_thresh(indio_dev, val, val2, dir); 6148c2ecf20Sopenharmony_ci case IIO_EV_INFO_PERIOD: 6158c2ecf20Sopenharmony_ci return veml6030_write_persistence(indio_dev, val, val2); 6168c2ecf20Sopenharmony_ci default: 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int veml6030_read_interrupt_config(struct iio_dev *indio_dev, 6228c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 6238c2ecf20Sopenharmony_ci enum iio_event_direction dir) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci int ret, reg; 6268c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); 6298c2ecf20Sopenharmony_ci if (ret) { 6308c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 6318c2ecf20Sopenharmony_ci "can't read als conf register %d\n", ret); 6328c2ecf20Sopenharmony_ci return ret; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (reg & VEML6030_ALS_INT_EN) 6368c2ecf20Sopenharmony_ci return 1; 6378c2ecf20Sopenharmony_ci else 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci/* 6428c2ecf20Sopenharmony_ci * Sensor should not be measuring light when interrupt is configured. 6438c2ecf20Sopenharmony_ci * Therefore correct sequence to configure interrupt functionality is: 6448c2ecf20Sopenharmony_ci * shut down -> enable/disable interrupt -> power on 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * state = 1 enables interrupt, state = 0 disables interrupt 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_cistatic int veml6030_write_interrupt_config(struct iio_dev *indio_dev, 6498c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 6508c2ecf20Sopenharmony_ci enum iio_event_direction dir, int state) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci int ret; 6538c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (state < 0 || state > 1) 6568c2ecf20Sopenharmony_ci return -EINVAL; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ret = veml6030_als_shut_down(data); 6598c2ecf20Sopenharmony_ci if (ret < 0) { 6608c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 6618c2ecf20Sopenharmony_ci "can't disable als to configure interrupt %d\n", ret); 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* enable interrupt + power on */ 6668c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 6678c2ecf20Sopenharmony_ci VEML6030_ALS_INT_EN | VEML6030_ALS_SD, state << 1); 6688c2ecf20Sopenharmony_ci if (ret) 6698c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 6708c2ecf20Sopenharmony_ci "can't enable interrupt & poweron als %d\n", ret); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return ret; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic const struct iio_info veml6030_info = { 6768c2ecf20Sopenharmony_ci .read_raw = veml6030_read_raw, 6778c2ecf20Sopenharmony_ci .write_raw = veml6030_write_raw, 6788c2ecf20Sopenharmony_ci .read_event_value = veml6030_read_event_val, 6798c2ecf20Sopenharmony_ci .write_event_value = veml6030_write_event_val, 6808c2ecf20Sopenharmony_ci .read_event_config = veml6030_read_interrupt_config, 6818c2ecf20Sopenharmony_ci .write_event_config = veml6030_write_interrupt_config, 6828c2ecf20Sopenharmony_ci .attrs = &veml6030_attr_group, 6838c2ecf20Sopenharmony_ci .event_attrs = &veml6030_event_attr_group, 6848c2ecf20Sopenharmony_ci}; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic const struct iio_info veml6030_info_no_irq = { 6878c2ecf20Sopenharmony_ci .read_raw = veml6030_read_raw, 6888c2ecf20Sopenharmony_ci .write_raw = veml6030_write_raw, 6898c2ecf20Sopenharmony_ci .attrs = &veml6030_attr_group, 6908c2ecf20Sopenharmony_ci}; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic irqreturn_t veml6030_event_handler(int irq, void *private) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci int ret, reg, evtdir; 6958c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = private; 6968c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, ®); 6998c2ecf20Sopenharmony_ci if (ret) { 7008c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 7018c2ecf20Sopenharmony_ci "can't read als interrupt register %d\n", ret); 7028c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Spurious interrupt handling */ 7068c2ecf20Sopenharmony_ci if (!(reg & (VEML6030_INT_TH_HIGH | VEML6030_INT_TH_LOW))) 7078c2ecf20Sopenharmony_ci return IRQ_NONE; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (reg & VEML6030_INT_TH_HIGH) 7108c2ecf20Sopenharmony_ci evtdir = IIO_EV_DIR_RISING; 7118c2ecf20Sopenharmony_ci else 7128c2ecf20Sopenharmony_ci evtdir = IIO_EV_DIR_FALLING; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 7158c2ecf20Sopenharmony_ci 0, IIO_EV_TYPE_THRESH, evtdir), 7168c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* 7228c2ecf20Sopenharmony_ci * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2, 7238c2ecf20Sopenharmony_ci * persistence to 1 x integration time and the threshold 7248c2ecf20Sopenharmony_ci * interrupt disabled by default. First shutdown the sensor, 7258c2ecf20Sopenharmony_ci * update registers and then power on the sensor. 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_cistatic int veml6030_hw_init(struct iio_dev *indio_dev) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci int ret, val; 7308c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 7318c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = veml6030_als_shut_down(data); 7348c2ecf20Sopenharmony_ci if (ret) { 7358c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't shutdown als %d\n", ret); 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 0x1001); 7408c2ecf20Sopenharmony_ci if (ret) { 7418c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't setup als configs %d\n", ret); 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM, 7468c2ecf20Sopenharmony_ci VEML6030_PSM | VEML6030_PSM_EN, 0x03); 7478c2ecf20Sopenharmony_ci if (ret) { 7488c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't setup default PSM %d\n", ret); 7498c2ecf20Sopenharmony_ci return ret; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF); 7538c2ecf20Sopenharmony_ci if (ret) { 7548c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't setup high threshold %d\n", ret); 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000); 7598c2ecf20Sopenharmony_ci if (ret) { 7608c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't setup low threshold %d\n", ret); 7618c2ecf20Sopenharmony_ci return ret; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci ret = veml6030_als_pwr_on(data); 7658c2ecf20Sopenharmony_ci if (ret) { 7668c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't poweron als %d\n", ret); 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* Wait 4 ms to let processor & oscillator start correctly */ 7718c2ecf20Sopenharmony_ci usleep_range(4000, 4002); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Clear stale interrupt status bits if any during start */ 7748c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val); 7758c2ecf20Sopenharmony_ci if (ret < 0) { 7768c2ecf20Sopenharmony_ci dev_err(&client->dev, 7778c2ecf20Sopenharmony_ci "can't clear als interrupt status %d\n", ret); 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Cache currently active measurement parameters */ 7828c2ecf20Sopenharmony_ci data->cur_gain = 3; 7838c2ecf20Sopenharmony_ci data->cur_resolution = 4608; 7848c2ecf20Sopenharmony_ci data->cur_integration_time = 3; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int veml6030_probe(struct i2c_client *client, 7908c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci int ret; 7938c2ecf20Sopenharmony_ci struct veml6030_data *data; 7948c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 7958c2ecf20Sopenharmony_ci struct regmap *regmap; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 7988c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n"); 7998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config); 8038c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 8048c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't setup regmap\n"); 8058c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 8098c2ecf20Sopenharmony_ci if (!indio_dev) 8108c2ecf20Sopenharmony_ci return -ENOMEM; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 8138c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 8148c2ecf20Sopenharmony_ci data->client = client; 8158c2ecf20Sopenharmony_ci data->regmap = regmap; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci indio_dev->name = "veml6030"; 8188c2ecf20Sopenharmony_ci indio_dev->channels = veml6030_channels; 8198c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(veml6030_channels); 8208c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (client->irq) { 8238c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 8248c2ecf20Sopenharmony_ci NULL, veml6030_event_handler, 8258c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 8268c2ecf20Sopenharmony_ci "veml6030", indio_dev); 8278c2ecf20Sopenharmony_ci if (ret < 0) { 8288c2ecf20Sopenharmony_ci dev_err(&client->dev, 8298c2ecf20Sopenharmony_ci "irq %d request failed\n", client->irq); 8308c2ecf20Sopenharmony_ci return ret; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci indio_dev->info = &veml6030_info; 8338c2ecf20Sopenharmony_ci } else { 8348c2ecf20Sopenharmony_ci indio_dev->info = &veml6030_info_no_irq; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ret = veml6030_hw_init(indio_dev); 8388c2ecf20Sopenharmony_ci if (ret < 0) 8398c2ecf20Sopenharmony_ci return ret; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(&client->dev, 8428c2ecf20Sopenharmony_ci veml6030_als_shut_down_action, data); 8438c2ecf20Sopenharmony_ci if (ret < 0) 8448c2ecf20Sopenharmony_ci return ret; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int __maybe_unused veml6030_runtime_suspend(struct device *dev) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci int ret; 8528c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 8538c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci ret = veml6030_als_shut_down(data); 8568c2ecf20Sopenharmony_ci if (ret < 0) 8578c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "can't suspend als %d\n", ret); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return ret; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int __maybe_unused veml6030_runtime_resume(struct device *dev) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci int ret; 8658c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 8668c2ecf20Sopenharmony_ci struct veml6030_data *data = iio_priv(indio_dev); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci ret = veml6030_als_pwr_on(data); 8698c2ecf20Sopenharmony_ci if (ret < 0) 8708c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "can't resume als %d\n", ret); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic const struct dev_pm_ops veml6030_pm_ops = { 8768c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 8778c2ecf20Sopenharmony_ci pm_runtime_force_resume) 8788c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(veml6030_runtime_suspend, 8798c2ecf20Sopenharmony_ci veml6030_runtime_resume, NULL) 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic const struct of_device_id veml6030_of_match[] = { 8838c2ecf20Sopenharmony_ci { .compatible = "vishay,veml6030" }, 8848c2ecf20Sopenharmony_ci { } 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, veml6030_of_match); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic const struct i2c_device_id veml6030_id[] = { 8898c2ecf20Sopenharmony_ci { "veml6030", 0 }, 8908c2ecf20Sopenharmony_ci { } 8918c2ecf20Sopenharmony_ci}; 8928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, veml6030_id); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic struct i2c_driver veml6030_driver = { 8958c2ecf20Sopenharmony_ci .driver = { 8968c2ecf20Sopenharmony_ci .name = "veml6030", 8978c2ecf20Sopenharmony_ci .of_match_table = veml6030_of_match, 8988c2ecf20Sopenharmony_ci .pm = &veml6030_pm_ops, 8998c2ecf20Sopenharmony_ci }, 9008c2ecf20Sopenharmony_ci .probe = veml6030_probe, 9018c2ecf20Sopenharmony_ci .id_table = veml6030_id, 9028c2ecf20Sopenharmony_ci}; 9038c2ecf20Sopenharmony_cimodule_i2c_driver(veml6030_driver); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>"); 9068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VEML6030 Ambient Light Sensor"); 9078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 908