17b779f57SRishi Gupta // SPDX-License-Identifier: GPL-2.0+
27b779f57SRishi Gupta /*
37b779f57SRishi Gupta * VEML6030 Ambient Light Sensor
47b779f57SRishi Gupta *
57b779f57SRishi Gupta * Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com>
67b779f57SRishi Gupta *
77b779f57SRishi Gupta * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf
87b779f57SRishi Gupta * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf
97b779f57SRishi Gupta */
107b779f57SRishi Gupta
117b779f57SRishi Gupta #include <linux/module.h>
127b779f57SRishi Gupta #include <linux/i2c.h>
137b779f57SRishi Gupta #include <linux/err.h>
147b779f57SRishi Gupta #include <linux/regmap.h>
157b779f57SRishi Gupta #include <linux/interrupt.h>
167b779f57SRishi Gupta #include <linux/pm_runtime.h>
177b779f57SRishi Gupta #include <linux/iio/iio.h>
187b779f57SRishi Gupta #include <linux/iio/sysfs.h>
197b779f57SRishi Gupta #include <linux/iio/events.h>
207b779f57SRishi Gupta
217b779f57SRishi Gupta /* Device registers */
227b779f57SRishi Gupta #define VEML6030_REG_ALS_CONF 0x00
237b779f57SRishi Gupta #define VEML6030_REG_ALS_WH 0x01
247b779f57SRishi Gupta #define VEML6030_REG_ALS_WL 0x02
257b779f57SRishi Gupta #define VEML6030_REG_ALS_PSM 0x03
267b779f57SRishi Gupta #define VEML6030_REG_ALS_DATA 0x04
277b779f57SRishi Gupta #define VEML6030_REG_WH_DATA 0x05
287b779f57SRishi Gupta #define VEML6030_REG_ALS_INT 0x06
297b779f57SRishi Gupta
307b779f57SRishi Gupta /* Bit masks for specific functionality */
317b779f57SRishi Gupta #define VEML6030_ALS_IT GENMASK(9, 6)
327b779f57SRishi Gupta #define VEML6030_PSM GENMASK(2, 1)
337b779f57SRishi Gupta #define VEML6030_ALS_PERS GENMASK(5, 4)
347b779f57SRishi Gupta #define VEML6030_ALS_GAIN GENMASK(12, 11)
357b779f57SRishi Gupta #define VEML6030_PSM_EN BIT(0)
367b779f57SRishi Gupta #define VEML6030_INT_TH_LOW BIT(15)
377b779f57SRishi Gupta #define VEML6030_INT_TH_HIGH BIT(14)
387b779f57SRishi Gupta #define VEML6030_ALS_INT_EN BIT(1)
397b779f57SRishi Gupta #define VEML6030_ALS_SD BIT(0)
407b779f57SRishi Gupta
417b779f57SRishi Gupta /*
427b779f57SRishi Gupta * The resolution depends on both gain and integration time. The
437b779f57SRishi Gupta * cur_resolution stores one of the resolution mentioned in the
447b779f57SRishi Gupta * table during startup and gets updated whenever integration time
457b779f57SRishi Gupta * or gain is changed.
467b779f57SRishi Gupta *
477b779f57SRishi Gupta * Table 'resolution and maximum detection range' in appnote 84367
487b779f57SRishi Gupta * is visualized as a 2D array. The cur_gain stores index of gain
497b779f57SRishi Gupta * in this table (0-3) while the cur_integration_time holds index
507b779f57SRishi Gupta * of integration time (0-5).
517b779f57SRishi Gupta */
527b779f57SRishi Gupta struct veml6030_data {
537b779f57SRishi Gupta struct i2c_client *client;
547b779f57SRishi Gupta struct regmap *regmap;
557b779f57SRishi Gupta int cur_resolution;
567b779f57SRishi Gupta int cur_gain;
577b779f57SRishi Gupta int cur_integration_time;
587b779f57SRishi Gupta };
597b779f57SRishi Gupta
607b779f57SRishi Gupta /* Integration time available in seconds */
617b779f57SRishi Gupta static IIO_CONST_ATTR(in_illuminance_integration_time_available,
627b779f57SRishi Gupta "0.025 0.05 0.1 0.2 0.4 0.8");
637b779f57SRishi Gupta
647b779f57SRishi Gupta /*
657b779f57SRishi Gupta * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is
667b779f57SRishi Gupta * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2.
677b779f57SRishi Gupta */
687b779f57SRishi Gupta static IIO_CONST_ATTR(in_illuminance_scale_available,
697b779f57SRishi Gupta "0.125 0.25 1.0 2.0");
707b779f57SRishi Gupta
717b779f57SRishi Gupta static struct attribute *veml6030_attributes[] = {
727b779f57SRishi Gupta &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
737b779f57SRishi Gupta &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
747b779f57SRishi Gupta NULL
757b779f57SRishi Gupta };
767b779f57SRishi Gupta
777b779f57SRishi Gupta static const struct attribute_group veml6030_attr_group = {
787b779f57SRishi Gupta .attrs = veml6030_attributes,
797b779f57SRishi Gupta };
807b779f57SRishi Gupta
817b779f57SRishi Gupta /*
827b779f57SRishi Gupta * Persistence = 1/2/4/8 x integration time
837b779f57SRishi Gupta * Minimum time for which light readings must stay above configured
847b779f57SRishi Gupta * threshold to assert the interrupt.
857b779f57SRishi Gupta */
867b779f57SRishi Gupta static const char * const period_values[] = {
877b779f57SRishi Gupta "0.1 0.2 0.4 0.8",
887b779f57SRishi Gupta "0.2 0.4 0.8 1.6",
897b779f57SRishi Gupta "0.4 0.8 1.6 3.2",
907b779f57SRishi Gupta "0.8 1.6 3.2 6.4",
917b779f57SRishi Gupta "0.05 0.1 0.2 0.4",
927b779f57SRishi Gupta "0.025 0.050 0.1 0.2"
937b779f57SRishi Gupta };
947b779f57SRishi Gupta
957b779f57SRishi Gupta /*
967b779f57SRishi Gupta * Return list of valid period values in seconds corresponding to
977b779f57SRishi Gupta * the currently active integration time.
987b779f57SRishi Gupta */
in_illuminance_period_available_show(struct device * dev,struct device_attribute * attr,char * buf)997b779f57SRishi Gupta static ssize_t in_illuminance_period_available_show(struct device *dev,
1007b779f57SRishi Gupta struct device_attribute *attr, char *buf)
1017b779f57SRishi Gupta {
1022cbb41abSJavier Carrasco struct veml6030_data *data = iio_priv(dev_to_iio_dev(dev));
1037b779f57SRishi Gupta int ret, reg, x;
1047b779f57SRishi Gupta
1057b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
1067b779f57SRishi Gupta if (ret) {
1077b779f57SRishi Gupta dev_err(&data->client->dev,
1087b779f57SRishi Gupta "can't read als conf register %d\n", ret);
1097b779f57SRishi Gupta return ret;
1107b779f57SRishi Gupta }
1117b779f57SRishi Gupta
1127b779f57SRishi Gupta ret = ((reg >> 6) & 0xF);
1137b779f57SRishi Gupta switch (ret) {
1147b779f57SRishi Gupta case 0:
1157b779f57SRishi Gupta case 1:
1167b779f57SRishi Gupta case 2:
1177b779f57SRishi Gupta case 3:
1187b779f57SRishi Gupta x = ret;
1197b779f57SRishi Gupta break;
1207b779f57SRishi Gupta case 8:
1217b779f57SRishi Gupta x = 4;
1227b779f57SRishi Gupta break;
1237b779f57SRishi Gupta case 12:
1247b779f57SRishi Gupta x = 5;
1257b779f57SRishi Gupta break;
1267b779f57SRishi Gupta default:
1277b779f57SRishi Gupta return -EINVAL;
1287b779f57SRishi Gupta }
1297b779f57SRishi Gupta
130c79859bdSTian Tao return sysfs_emit(buf, "%s\n", period_values[x]);
1317b779f57SRishi Gupta }
1327b779f57SRishi Gupta
1337b779f57SRishi Gupta static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0);
1347b779f57SRishi Gupta
1357b779f57SRishi Gupta static struct attribute *veml6030_event_attributes[] = {
1367b779f57SRishi Gupta &iio_dev_attr_in_illuminance_period_available.dev_attr.attr,
1377b779f57SRishi Gupta NULL
1387b779f57SRishi Gupta };
1397b779f57SRishi Gupta
1407b779f57SRishi Gupta static const struct attribute_group veml6030_event_attr_group = {
1417b779f57SRishi Gupta .attrs = veml6030_event_attributes,
1427b779f57SRishi Gupta };
1437b779f57SRishi Gupta
veml6030_als_pwr_on(struct veml6030_data * data)1447b779f57SRishi Gupta static int veml6030_als_pwr_on(struct veml6030_data *data)
1457b779f57SRishi Gupta {
1467b779f57SRishi Gupta return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
1477b779f57SRishi Gupta VEML6030_ALS_SD, 0);
1487b779f57SRishi Gupta }
1497b779f57SRishi Gupta
veml6030_als_shut_down(struct veml6030_data * data)1507b779f57SRishi Gupta static int veml6030_als_shut_down(struct veml6030_data *data)
1517b779f57SRishi Gupta {
1527b779f57SRishi Gupta return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
1537b779f57SRishi Gupta VEML6030_ALS_SD, 1);
1547b779f57SRishi Gupta }
1557b779f57SRishi Gupta
veml6030_als_shut_down_action(void * data)1567b779f57SRishi Gupta static void veml6030_als_shut_down_action(void *data)
1577b779f57SRishi Gupta {
1587b779f57SRishi Gupta veml6030_als_shut_down(data);
1597b779f57SRishi Gupta }
1607b779f57SRishi Gupta
1617b779f57SRishi Gupta static const struct iio_event_spec veml6030_event_spec[] = {
1627b779f57SRishi Gupta {
1637b779f57SRishi Gupta .type = IIO_EV_TYPE_THRESH,
1647b779f57SRishi Gupta .dir = IIO_EV_DIR_RISING,
1657b779f57SRishi Gupta .mask_separate = BIT(IIO_EV_INFO_VALUE),
1667b779f57SRishi Gupta }, {
1677b779f57SRishi Gupta .type = IIO_EV_TYPE_THRESH,
1687b779f57SRishi Gupta .dir = IIO_EV_DIR_FALLING,
1697b779f57SRishi Gupta .mask_separate = BIT(IIO_EV_INFO_VALUE),
1707b779f57SRishi Gupta }, {
1717b779f57SRishi Gupta .type = IIO_EV_TYPE_THRESH,
1727b779f57SRishi Gupta .dir = IIO_EV_DIR_EITHER,
1737b779f57SRishi Gupta .mask_separate = BIT(IIO_EV_INFO_PERIOD) |
1747b779f57SRishi Gupta BIT(IIO_EV_INFO_ENABLE),
1757b779f57SRishi Gupta },
1767b779f57SRishi Gupta };
1777b779f57SRishi Gupta
1787b779f57SRishi Gupta /* Channel number */
1797b779f57SRishi Gupta enum veml6030_chan {
1807b779f57SRishi Gupta CH_ALS,
1817b779f57SRishi Gupta CH_WHITE,
1827b779f57SRishi Gupta };
1837b779f57SRishi Gupta
1847b779f57SRishi Gupta static const struct iio_chan_spec veml6030_channels[] = {
1857b779f57SRishi Gupta {
1867b779f57SRishi Gupta .type = IIO_LIGHT,
1877b779f57SRishi Gupta .channel = CH_ALS,
1887b779f57SRishi Gupta .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1897b779f57SRishi Gupta BIT(IIO_CHAN_INFO_PROCESSED) |
1907b779f57SRishi Gupta BIT(IIO_CHAN_INFO_INT_TIME) |
1917b779f57SRishi Gupta BIT(IIO_CHAN_INFO_SCALE),
1927b779f57SRishi Gupta .event_spec = veml6030_event_spec,
1937b779f57SRishi Gupta .num_event_specs = ARRAY_SIZE(veml6030_event_spec),
1947b779f57SRishi Gupta },
1957b779f57SRishi Gupta {
1967b779f57SRishi Gupta .type = IIO_INTENSITY,
1977b779f57SRishi Gupta .channel = CH_WHITE,
1987b779f57SRishi Gupta .modified = 1,
1997b779f57SRishi Gupta .channel2 = IIO_MOD_LIGHT_BOTH,
2007b779f57SRishi Gupta .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
2017b779f57SRishi Gupta BIT(IIO_CHAN_INFO_PROCESSED),
2027b779f57SRishi Gupta },
2037b779f57SRishi Gupta };
2047b779f57SRishi Gupta
2057b779f57SRishi Gupta static const struct regmap_config veml6030_regmap_config = {
2067b779f57SRishi Gupta .name = "veml6030_regmap",
2077b779f57SRishi Gupta .reg_bits = 8,
2087b779f57SRishi Gupta .val_bits = 16,
2097b779f57SRishi Gupta .max_register = VEML6030_REG_ALS_INT,
2107b779f57SRishi Gupta .val_format_endian = REGMAP_ENDIAN_LITTLE,
2117b779f57SRishi Gupta };
2127b779f57SRishi Gupta
veml6030_get_intgrn_tm(struct iio_dev * indio_dev,int * val,int * val2)2137b779f57SRishi Gupta static int veml6030_get_intgrn_tm(struct iio_dev *indio_dev,
2147b779f57SRishi Gupta int *val, int *val2)
2157b779f57SRishi Gupta {
2167b779f57SRishi Gupta int ret, reg;
2177b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
2187b779f57SRishi Gupta
2197b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
2207b779f57SRishi Gupta if (ret) {
2217b779f57SRishi Gupta dev_err(&data->client->dev,
2227b779f57SRishi Gupta "can't read als conf register %d\n", ret);
2237b779f57SRishi Gupta return ret;
2247b779f57SRishi Gupta }
2257b779f57SRishi Gupta
2267b779f57SRishi Gupta switch ((reg >> 6) & 0xF) {
2277b779f57SRishi Gupta case 0:
2287b779f57SRishi Gupta *val2 = 100000;
2297b779f57SRishi Gupta break;
2307b779f57SRishi Gupta case 1:
2317b779f57SRishi Gupta *val2 = 200000;
2327b779f57SRishi Gupta break;
2337b779f57SRishi Gupta case 2:
2347b779f57SRishi Gupta *val2 = 400000;
2357b779f57SRishi Gupta break;
2367b779f57SRishi Gupta case 3:
2377b779f57SRishi Gupta *val2 = 800000;
2387b779f57SRishi Gupta break;
2397b779f57SRishi Gupta case 8:
2407b779f57SRishi Gupta *val2 = 50000;
2417b779f57SRishi Gupta break;
2427b779f57SRishi Gupta case 12:
2437b779f57SRishi Gupta *val2 = 25000;
2447b779f57SRishi Gupta break;
2457b779f57SRishi Gupta default:
2467b779f57SRishi Gupta return -EINVAL;
2477b779f57SRishi Gupta }
2487b779f57SRishi Gupta
2497b779f57SRishi Gupta *val = 0;
2507b779f57SRishi Gupta return IIO_VAL_INT_PLUS_MICRO;
2517b779f57SRishi Gupta }
2527b779f57SRishi Gupta
veml6030_set_intgrn_tm(struct iio_dev * indio_dev,int val,int val2)2537b779f57SRishi Gupta static int veml6030_set_intgrn_tm(struct iio_dev *indio_dev,
2547b779f57SRishi Gupta int val, int val2)
2557b779f57SRishi Gupta {
2567b779f57SRishi Gupta int ret, new_int_time, int_idx;
2577b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
2587b779f57SRishi Gupta
2597b779f57SRishi Gupta if (val)
2607b779f57SRishi Gupta return -EINVAL;
2617b779f57SRishi Gupta
2627b779f57SRishi Gupta switch (val2) {
2637b779f57SRishi Gupta case 25000:
2647b779f57SRishi Gupta new_int_time = 0x300;
2657b779f57SRishi Gupta int_idx = 5;
2667b779f57SRishi Gupta break;
2677b779f57SRishi Gupta case 50000:
2687b779f57SRishi Gupta new_int_time = 0x200;
2697b779f57SRishi Gupta int_idx = 4;
2707b779f57SRishi Gupta break;
2717b779f57SRishi Gupta case 100000:
2727b779f57SRishi Gupta new_int_time = 0x00;
2737b779f57SRishi Gupta int_idx = 3;
2747b779f57SRishi Gupta break;
2757b779f57SRishi Gupta case 200000:
2767b779f57SRishi Gupta new_int_time = 0x40;
2777b779f57SRishi Gupta int_idx = 2;
2787b779f57SRishi Gupta break;
2797b779f57SRishi Gupta case 400000:
2807b779f57SRishi Gupta new_int_time = 0x80;
2817b779f57SRishi Gupta int_idx = 1;
2827b779f57SRishi Gupta break;
2837b779f57SRishi Gupta case 800000:
2847b779f57SRishi Gupta new_int_time = 0xC0;
2857b779f57SRishi Gupta int_idx = 0;
2867b779f57SRishi Gupta break;
2877b779f57SRishi Gupta default:
2887b779f57SRishi Gupta return -EINVAL;
2897b779f57SRishi Gupta }
2907b779f57SRishi Gupta
2917b779f57SRishi Gupta ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
2927b779f57SRishi Gupta VEML6030_ALS_IT, new_int_time);
2937b779f57SRishi Gupta if (ret) {
2947b779f57SRishi Gupta dev_err(&data->client->dev,
2957b779f57SRishi Gupta "can't update als integration time %d\n", ret);
2967b779f57SRishi Gupta return ret;
2977b779f57SRishi Gupta }
2987b779f57SRishi Gupta
2997b779f57SRishi Gupta /*
3007b779f57SRishi Gupta * Cache current integration time and update resolution. For every
3017b779f57SRishi Gupta * increase in integration time to next level, resolution is halved
3027b779f57SRishi Gupta * and vice-versa.
3037b779f57SRishi Gupta */
3047b779f57SRishi Gupta if (data->cur_integration_time < int_idx)
3057b779f57SRishi Gupta data->cur_resolution <<= int_idx - data->cur_integration_time;
3067b779f57SRishi Gupta else if (data->cur_integration_time > int_idx)
3077b779f57SRishi Gupta data->cur_resolution >>= data->cur_integration_time - int_idx;
3087b779f57SRishi Gupta
3097b779f57SRishi Gupta data->cur_integration_time = int_idx;
3107b779f57SRishi Gupta
3117b779f57SRishi Gupta return ret;
3127b779f57SRishi Gupta }
3137b779f57SRishi Gupta
veml6030_read_persistence(struct iio_dev * indio_dev,int * val,int * val2)3147b779f57SRishi Gupta static int veml6030_read_persistence(struct iio_dev *indio_dev,
3157b779f57SRishi Gupta int *val, int *val2)
3167b779f57SRishi Gupta {
3177b779f57SRishi Gupta int ret, reg, period, x, y;
3187b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
3197b779f57SRishi Gupta
3207b779f57SRishi Gupta ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
3217b779f57SRishi Gupta if (ret < 0)
3227b779f57SRishi Gupta return ret;
3237b779f57SRishi Gupta
3247b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
3257b779f57SRishi Gupta if (ret) {
3267b779f57SRishi Gupta dev_err(&data->client->dev,
3277b779f57SRishi Gupta "can't read als conf register %d\n", ret);
3287b779f57SRishi Gupta }
3297b779f57SRishi Gupta
3307b779f57SRishi Gupta /* integration time multiplied by 1/2/4/8 */
3317b779f57SRishi Gupta period = y * (1 << ((reg >> 4) & 0x03));
3327b779f57SRishi Gupta
3337b779f57SRishi Gupta *val = period / 1000000;
3347b779f57SRishi Gupta *val2 = period % 1000000;
3357b779f57SRishi Gupta
3367b779f57SRishi Gupta return IIO_VAL_INT_PLUS_MICRO;
3377b779f57SRishi Gupta }
3387b779f57SRishi Gupta
veml6030_write_persistence(struct iio_dev * indio_dev,int val,int val2)3397b779f57SRishi Gupta static int veml6030_write_persistence(struct iio_dev *indio_dev,
3407b779f57SRishi Gupta int val, int val2)
3417b779f57SRishi Gupta {
3427b779f57SRishi Gupta int ret, period, x, y;
3437b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
3447b779f57SRishi Gupta
3457b779f57SRishi Gupta ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
3467b779f57SRishi Gupta if (ret < 0)
3477b779f57SRishi Gupta return ret;
3487b779f57SRishi Gupta
3497b779f57SRishi Gupta if (!val) {
3507b779f57SRishi Gupta period = val2 / y;
3517b779f57SRishi Gupta } else {
3527b779f57SRishi Gupta if ((val == 1) && (val2 == 600000))
3537b779f57SRishi Gupta period = 1600000 / y;
3547b779f57SRishi Gupta else if ((val == 3) && (val2 == 200000))
3557b779f57SRishi Gupta period = 3200000 / y;
3567b779f57SRishi Gupta else if ((val == 6) && (val2 == 400000))
3577b779f57SRishi Gupta period = 6400000 / y;
3587b779f57SRishi Gupta else
3597b779f57SRishi Gupta period = -1;
3607b779f57SRishi Gupta }
3617b779f57SRishi Gupta
3627b779f57SRishi Gupta if (period <= 0 || period > 8 || hweight8(period) != 1)
3637b779f57SRishi Gupta return -EINVAL;
3647b779f57SRishi Gupta
3657b779f57SRishi Gupta ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
3667b779f57SRishi Gupta VEML6030_ALS_PERS, (ffs(period) - 1) << 4);
3677b779f57SRishi Gupta if (ret)
3687b779f57SRishi Gupta dev_err(&data->client->dev,
3697b779f57SRishi Gupta "can't set persistence value %d\n", ret);
3707b779f57SRishi Gupta
3717b779f57SRishi Gupta return ret;
3727b779f57SRishi Gupta }
3737b779f57SRishi Gupta
veml6030_set_als_gain(struct iio_dev * indio_dev,int val,int val2)3747b779f57SRishi Gupta static int veml6030_set_als_gain(struct iio_dev *indio_dev,
3757b779f57SRishi Gupta int val, int val2)
3767b779f57SRishi Gupta {
3777b779f57SRishi Gupta int ret, new_gain, gain_idx;
3787b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
3797b779f57SRishi Gupta
3807b779f57SRishi Gupta if (val == 0 && val2 == 125000) {
3817b779f57SRishi Gupta new_gain = 0x1000; /* 0x02 << 11 */
3827b779f57SRishi Gupta gain_idx = 3;
3837b779f57SRishi Gupta } else if (val == 0 && val2 == 250000) {
3847b779f57SRishi Gupta new_gain = 0x1800;
3857b779f57SRishi Gupta gain_idx = 2;
3867b779f57SRishi Gupta } else if (val == 1 && val2 == 0) {
3877b779f57SRishi Gupta new_gain = 0x00;
3887b779f57SRishi Gupta gain_idx = 1;
3897b779f57SRishi Gupta } else if (val == 2 && val2 == 0) {
3907b779f57SRishi Gupta new_gain = 0x800;
3917b779f57SRishi Gupta gain_idx = 0;
3927b779f57SRishi Gupta } else {
3937b779f57SRishi Gupta return -EINVAL;
3947b779f57SRishi Gupta }
3957b779f57SRishi Gupta
3967b779f57SRishi Gupta ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
3977b779f57SRishi Gupta VEML6030_ALS_GAIN, new_gain);
3987b779f57SRishi Gupta if (ret) {
3997b779f57SRishi Gupta dev_err(&data->client->dev,
4007b779f57SRishi Gupta "can't set als gain %d\n", ret);
4017b779f57SRishi Gupta return ret;
4027b779f57SRishi Gupta }
4037b779f57SRishi Gupta
4047b779f57SRishi Gupta /*
4057b779f57SRishi Gupta * Cache currently set gain & update resolution. For every
4067b779f57SRishi Gupta * increase in the gain to next level, resolution is halved
4077b779f57SRishi Gupta * and vice-versa.
4087b779f57SRishi Gupta */
4097b779f57SRishi Gupta if (data->cur_gain < gain_idx)
4107b779f57SRishi Gupta data->cur_resolution <<= gain_idx - data->cur_gain;
4117b779f57SRishi Gupta else if (data->cur_gain > gain_idx)
4127b779f57SRishi Gupta data->cur_resolution >>= data->cur_gain - gain_idx;
4137b779f57SRishi Gupta
4147b779f57SRishi Gupta data->cur_gain = gain_idx;
4157b779f57SRishi Gupta
4167b779f57SRishi Gupta return ret;
4177b779f57SRishi Gupta }
4187b779f57SRishi Gupta
veml6030_get_als_gain(struct iio_dev * indio_dev,int * val,int * val2)4197b779f57SRishi Gupta static int veml6030_get_als_gain(struct iio_dev *indio_dev,
4207b779f57SRishi Gupta int *val, int *val2)
4217b779f57SRishi Gupta {
4227b779f57SRishi Gupta int ret, reg;
4237b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
4247b779f57SRishi Gupta
4257b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
4267b779f57SRishi Gupta if (ret) {
4277b779f57SRishi Gupta dev_err(&data->client->dev,
4287b779f57SRishi Gupta "can't read als conf register %d\n", ret);
4297b779f57SRishi Gupta return ret;
4307b779f57SRishi Gupta }
4317b779f57SRishi Gupta
4327b779f57SRishi Gupta switch ((reg >> 11) & 0x03) {
4337b779f57SRishi Gupta case 0:
4347b779f57SRishi Gupta *val = 1;
4357b779f57SRishi Gupta *val2 = 0;
4367b779f57SRishi Gupta break;
4377b779f57SRishi Gupta case 1:
4387b779f57SRishi Gupta *val = 2;
4397b779f57SRishi Gupta *val2 = 0;
4407b779f57SRishi Gupta break;
4417b779f57SRishi Gupta case 2:
4427b779f57SRishi Gupta *val = 0;
4437b779f57SRishi Gupta *val2 = 125000;
4447b779f57SRishi Gupta break;
4457b779f57SRishi Gupta case 3:
4467b779f57SRishi Gupta *val = 0;
4477b779f57SRishi Gupta *val2 = 250000;
4487b779f57SRishi Gupta break;
4497b779f57SRishi Gupta default:
4507b779f57SRishi Gupta return -EINVAL;
4517b779f57SRishi Gupta }
4527b779f57SRishi Gupta
4537b779f57SRishi Gupta return IIO_VAL_INT_PLUS_MICRO;
4547b779f57SRishi Gupta }
4557b779f57SRishi Gupta
veml6030_read_thresh(struct iio_dev * indio_dev,int * val,int * val2,int dir)4567b779f57SRishi Gupta static int veml6030_read_thresh(struct iio_dev *indio_dev,
4577b779f57SRishi Gupta int *val, int *val2, int dir)
4587b779f57SRishi Gupta {
4597b779f57SRishi Gupta int ret, reg;
4607b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
4617b779f57SRishi Gupta
4627b779f57SRishi Gupta if (dir == IIO_EV_DIR_RISING)
4637b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_WH, ®);
4647b779f57SRishi Gupta else
4657b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_WL, ®);
4667b779f57SRishi Gupta if (ret) {
4677b779f57SRishi Gupta dev_err(&data->client->dev,
4687b779f57SRishi Gupta "can't read als threshold value %d\n", ret);
4697b779f57SRishi Gupta return ret;
4707b779f57SRishi Gupta }
4717b779f57SRishi Gupta
4727b779f57SRishi Gupta *val = reg & 0xffff;
4737b779f57SRishi Gupta return IIO_VAL_INT;
4747b779f57SRishi Gupta }
4757b779f57SRishi Gupta
veml6030_write_thresh(struct iio_dev * indio_dev,int val,int val2,int dir)4767b779f57SRishi Gupta static int veml6030_write_thresh(struct iio_dev *indio_dev,
4777b779f57SRishi Gupta int val, int val2, int dir)
4787b779f57SRishi Gupta {
4797b779f57SRishi Gupta int ret;
4807b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
4817b779f57SRishi Gupta
4827b779f57SRishi Gupta if (val > 0xFFFF || val < 0 || val2)
4837b779f57SRishi Gupta return -EINVAL;
4847b779f57SRishi Gupta
4857b779f57SRishi Gupta if (dir == IIO_EV_DIR_RISING) {
4867b779f57SRishi Gupta ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, val);
4877b779f57SRishi Gupta if (ret)
4887b779f57SRishi Gupta dev_err(&data->client->dev,
4897b779f57SRishi Gupta "can't set high threshold %d\n", ret);
4907b779f57SRishi Gupta } else {
4917b779f57SRishi Gupta ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, val);
4927b779f57SRishi Gupta if (ret)
4937b779f57SRishi Gupta dev_err(&data->client->dev,
4947b779f57SRishi Gupta "can't set low threshold %d\n", ret);
4957b779f57SRishi Gupta }
4967b779f57SRishi Gupta
4977b779f57SRishi Gupta return ret;
4987b779f57SRishi Gupta }
4997b779f57SRishi Gupta
5007b779f57SRishi Gupta /*
5017b779f57SRishi Gupta * Provide both raw as well as light reading in lux.
5027b779f57SRishi Gupta * light (in lux) = resolution * raw reading
5037b779f57SRishi Gupta */
veml6030_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)5047b779f57SRishi Gupta static int veml6030_read_raw(struct iio_dev *indio_dev,
5057b779f57SRishi Gupta struct iio_chan_spec const *chan, int *val,
5067b779f57SRishi Gupta int *val2, long mask)
5077b779f57SRishi Gupta {
5087b779f57SRishi Gupta int ret, reg;
5097b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
5107b779f57SRishi Gupta struct regmap *regmap = data->regmap;
5117b779f57SRishi Gupta struct device *dev = &data->client->dev;
5127b779f57SRishi Gupta
5137b779f57SRishi Gupta switch (mask) {
5147b779f57SRishi Gupta case IIO_CHAN_INFO_RAW:
5157b779f57SRishi Gupta case IIO_CHAN_INFO_PROCESSED:
5167b779f57SRishi Gupta switch (chan->type) {
5177b779f57SRishi Gupta case IIO_LIGHT:
5187b779f57SRishi Gupta ret = regmap_read(regmap, VEML6030_REG_ALS_DATA, ®);
5197b779f57SRishi Gupta if (ret < 0) {
5207b779f57SRishi Gupta dev_err(dev, "can't read als data %d\n", ret);
5217b779f57SRishi Gupta return ret;
5227b779f57SRishi Gupta }
5237b779f57SRishi Gupta if (mask == IIO_CHAN_INFO_PROCESSED) {
5247b779f57SRishi Gupta *val = (reg * data->cur_resolution) / 10000;
525*1a797936SJavier Carrasco *val2 = (reg * data->cur_resolution) % 10000 * 100;
5267b779f57SRishi Gupta return IIO_VAL_INT_PLUS_MICRO;
5277b779f57SRishi Gupta }
5287b779f57SRishi Gupta *val = reg;
5297b779f57SRishi Gupta return IIO_VAL_INT;
5307b779f57SRishi Gupta case IIO_INTENSITY:
5317b779f57SRishi Gupta ret = regmap_read(regmap, VEML6030_REG_WH_DATA, ®);
5327b779f57SRishi Gupta if (ret < 0) {
5337b779f57SRishi Gupta dev_err(dev, "can't read white data %d\n", ret);
5347b779f57SRishi Gupta return ret;
5357b779f57SRishi Gupta }
5367b779f57SRishi Gupta if (mask == IIO_CHAN_INFO_PROCESSED) {
5377b779f57SRishi Gupta *val = (reg * data->cur_resolution) / 10000;
5387b779f57SRishi Gupta *val2 = (reg * data->cur_resolution) % 10000;
5397b779f57SRishi Gupta return IIO_VAL_INT_PLUS_MICRO;
5407b779f57SRishi Gupta }
5417b779f57SRishi Gupta *val = reg;
5427b779f57SRishi Gupta return IIO_VAL_INT;
5437b779f57SRishi Gupta default:
5447b779f57SRishi Gupta return -EINVAL;
5457b779f57SRishi Gupta }
5467b779f57SRishi Gupta case IIO_CHAN_INFO_INT_TIME:
5477b779f57SRishi Gupta if (chan->type == IIO_LIGHT)
5487b779f57SRishi Gupta return veml6030_get_intgrn_tm(indio_dev, val, val2);
5497b779f57SRishi Gupta return -EINVAL;
5507b779f57SRishi Gupta case IIO_CHAN_INFO_SCALE:
5517b779f57SRishi Gupta if (chan->type == IIO_LIGHT)
5527b779f57SRishi Gupta return veml6030_get_als_gain(indio_dev, val, val2);
5537b779f57SRishi Gupta return -EINVAL;
5547b779f57SRishi Gupta default:
5557b779f57SRishi Gupta return -EINVAL;
5567b779f57SRishi Gupta }
5577b779f57SRishi Gupta }
5587b779f57SRishi Gupta
veml6030_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)5597b779f57SRishi Gupta static int veml6030_write_raw(struct iio_dev *indio_dev,
5607b779f57SRishi Gupta struct iio_chan_spec const *chan,
5617b779f57SRishi Gupta int val, int val2, long mask)
5627b779f57SRishi Gupta {
5637b779f57SRishi Gupta switch (mask) {
5647b779f57SRishi Gupta case IIO_CHAN_INFO_INT_TIME:
5657b779f57SRishi Gupta switch (chan->type) {
5667b779f57SRishi Gupta case IIO_LIGHT:
5677b779f57SRishi Gupta return veml6030_set_intgrn_tm(indio_dev, val, val2);
5687b779f57SRishi Gupta default:
5697b779f57SRishi Gupta return -EINVAL;
5707b779f57SRishi Gupta }
5717b779f57SRishi Gupta case IIO_CHAN_INFO_SCALE:
5727b779f57SRishi Gupta switch (chan->type) {
5737b779f57SRishi Gupta case IIO_LIGHT:
5747b779f57SRishi Gupta return veml6030_set_als_gain(indio_dev, val, val2);
5757b779f57SRishi Gupta default:
5767b779f57SRishi Gupta return -EINVAL;
5777b779f57SRishi Gupta }
5787b779f57SRishi Gupta default:
5797b779f57SRishi Gupta return -EINVAL;
5807b779f57SRishi Gupta }
5817b779f57SRishi Gupta }
5827b779f57SRishi Gupta
veml6030_read_event_val(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)5837b779f57SRishi Gupta static int veml6030_read_event_val(struct iio_dev *indio_dev,
5847b779f57SRishi Gupta const struct iio_chan_spec *chan, enum iio_event_type type,
5857b779f57SRishi Gupta enum iio_event_direction dir, enum iio_event_info info,
5867b779f57SRishi Gupta int *val, int *val2)
5877b779f57SRishi Gupta {
5887b779f57SRishi Gupta switch (info) {
5897b779f57SRishi Gupta case IIO_EV_INFO_VALUE:
5907b779f57SRishi Gupta switch (dir) {
5917b779f57SRishi Gupta case IIO_EV_DIR_RISING:
5927b779f57SRishi Gupta case IIO_EV_DIR_FALLING:
5937b779f57SRishi Gupta return veml6030_read_thresh(indio_dev, val, val2, dir);
5947b779f57SRishi Gupta default:
5957b779f57SRishi Gupta return -EINVAL;
5967b779f57SRishi Gupta }
5977b779f57SRishi Gupta break;
5987b779f57SRishi Gupta case IIO_EV_INFO_PERIOD:
5997b779f57SRishi Gupta return veml6030_read_persistence(indio_dev, val, val2);
6007b779f57SRishi Gupta default:
6017b779f57SRishi Gupta return -EINVAL;
6027b779f57SRishi Gupta }
6037b779f57SRishi Gupta }
6047b779f57SRishi Gupta
veml6030_write_event_val(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)6057b779f57SRishi Gupta static int veml6030_write_event_val(struct iio_dev *indio_dev,
6067b779f57SRishi Gupta const struct iio_chan_spec *chan, enum iio_event_type type,
6077b779f57SRishi Gupta enum iio_event_direction dir, enum iio_event_info info,
6087b779f57SRishi Gupta int val, int val2)
6097b779f57SRishi Gupta {
6107b779f57SRishi Gupta switch (info) {
6117b779f57SRishi Gupta case IIO_EV_INFO_VALUE:
6127b779f57SRishi Gupta return veml6030_write_thresh(indio_dev, val, val2, dir);
6137b779f57SRishi Gupta case IIO_EV_INFO_PERIOD:
6147b779f57SRishi Gupta return veml6030_write_persistence(indio_dev, val, val2);
6157b779f57SRishi Gupta default:
6167b779f57SRishi Gupta return -EINVAL;
6177b779f57SRishi Gupta }
6187b779f57SRishi Gupta }
6197b779f57SRishi Gupta
veml6030_read_interrupt_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir)6207b779f57SRishi Gupta static int veml6030_read_interrupt_config(struct iio_dev *indio_dev,
6217b779f57SRishi Gupta const struct iio_chan_spec *chan, enum iio_event_type type,
6227b779f57SRishi Gupta enum iio_event_direction dir)
6237b779f57SRishi Gupta {
6247b779f57SRishi Gupta int ret, reg;
6257b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
6267b779f57SRishi Gupta
6277b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
6287b779f57SRishi Gupta if (ret) {
6297b779f57SRishi Gupta dev_err(&data->client->dev,
6307b779f57SRishi Gupta "can't read als conf register %d\n", ret);
6317b779f57SRishi Gupta return ret;
6327b779f57SRishi Gupta }
6337b779f57SRishi Gupta
6347b779f57SRishi Gupta if (reg & VEML6030_ALS_INT_EN)
6357b779f57SRishi Gupta return 1;
6367b779f57SRishi Gupta else
6377b779f57SRishi Gupta return 0;
6387b779f57SRishi Gupta }
6397b779f57SRishi Gupta
6407b779f57SRishi Gupta /*
6417b779f57SRishi Gupta * Sensor should not be measuring light when interrupt is configured.
6427b779f57SRishi Gupta * Therefore correct sequence to configure interrupt functionality is:
6437b779f57SRishi Gupta * shut down -> enable/disable interrupt -> power on
6447b779f57SRishi Gupta *
6457b779f57SRishi Gupta * state = 1 enables interrupt, state = 0 disables interrupt
6467b779f57SRishi Gupta */
veml6030_write_interrupt_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,int state)6477b779f57SRishi Gupta static int veml6030_write_interrupt_config(struct iio_dev *indio_dev,
6487b779f57SRishi Gupta const struct iio_chan_spec *chan, enum iio_event_type type,
6497b779f57SRishi Gupta enum iio_event_direction dir, int state)
6507b779f57SRishi Gupta {
6517b779f57SRishi Gupta int ret;
6527b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
6537b779f57SRishi Gupta
6547b779f57SRishi Gupta if (state < 0 || state > 1)
6557b779f57SRishi Gupta return -EINVAL;
6567b779f57SRishi Gupta
6577b779f57SRishi Gupta ret = veml6030_als_shut_down(data);
6587b779f57SRishi Gupta if (ret < 0) {
6597b779f57SRishi Gupta dev_err(&data->client->dev,
6607b779f57SRishi Gupta "can't disable als to configure interrupt %d\n", ret);
6617b779f57SRishi Gupta return ret;
6627b779f57SRishi Gupta }
6637b779f57SRishi Gupta
6647b779f57SRishi Gupta /* enable interrupt + power on */
6657b779f57SRishi Gupta ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
6667b779f57SRishi Gupta VEML6030_ALS_INT_EN | VEML6030_ALS_SD, state << 1);
6677b779f57SRishi Gupta if (ret)
6687b779f57SRishi Gupta dev_err(&data->client->dev,
6697b779f57SRishi Gupta "can't enable interrupt & poweron als %d\n", ret);
6707b779f57SRishi Gupta
6717b779f57SRishi Gupta return ret;
6727b779f57SRishi Gupta }
6737b779f57SRishi Gupta
6747b779f57SRishi Gupta static const struct iio_info veml6030_info = {
6757b779f57SRishi Gupta .read_raw = veml6030_read_raw,
6767b779f57SRishi Gupta .write_raw = veml6030_write_raw,
6777b779f57SRishi Gupta .read_event_value = veml6030_read_event_val,
6787b779f57SRishi Gupta .write_event_value = veml6030_write_event_val,
6797b779f57SRishi Gupta .read_event_config = veml6030_read_interrupt_config,
6807b779f57SRishi Gupta .write_event_config = veml6030_write_interrupt_config,
6817b779f57SRishi Gupta .attrs = &veml6030_attr_group,
6827b779f57SRishi Gupta .event_attrs = &veml6030_event_attr_group,
6837b779f57SRishi Gupta };
6847b779f57SRishi Gupta
6857b779f57SRishi Gupta static const struct iio_info veml6030_info_no_irq = {
6867b779f57SRishi Gupta .read_raw = veml6030_read_raw,
6877b779f57SRishi Gupta .write_raw = veml6030_write_raw,
6887b779f57SRishi Gupta .attrs = &veml6030_attr_group,
6897b779f57SRishi Gupta };
6907b779f57SRishi Gupta
veml6030_event_handler(int irq,void * private)6917b779f57SRishi Gupta static irqreturn_t veml6030_event_handler(int irq, void *private)
6927b779f57SRishi Gupta {
6937b779f57SRishi Gupta int ret, reg, evtdir;
6947b779f57SRishi Gupta struct iio_dev *indio_dev = private;
6957b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
6967b779f57SRishi Gupta
6977b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, ®);
6987b779f57SRishi Gupta if (ret) {
6997b779f57SRishi Gupta dev_err(&data->client->dev,
7007b779f57SRishi Gupta "can't read als interrupt register %d\n", ret);
7017b779f57SRishi Gupta return IRQ_HANDLED;
7027b779f57SRishi Gupta }
7037b779f57SRishi Gupta
7047b779f57SRishi Gupta /* Spurious interrupt handling */
7057b779f57SRishi Gupta if (!(reg & (VEML6030_INT_TH_HIGH | VEML6030_INT_TH_LOW)))
7067b779f57SRishi Gupta return IRQ_NONE;
7077b779f57SRishi Gupta
7087b779f57SRishi Gupta if (reg & VEML6030_INT_TH_HIGH)
7097b779f57SRishi Gupta evtdir = IIO_EV_DIR_RISING;
7107b779f57SRishi Gupta else
7117b779f57SRishi Gupta evtdir = IIO_EV_DIR_FALLING;
7127b779f57SRishi Gupta
7137b779f57SRishi Gupta iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
7147b779f57SRishi Gupta 0, IIO_EV_TYPE_THRESH, evtdir),
7157b779f57SRishi Gupta iio_get_time_ns(indio_dev));
7167b779f57SRishi Gupta
7177b779f57SRishi Gupta return IRQ_HANDLED;
7187b779f57SRishi Gupta }
7197b779f57SRishi Gupta
7207b779f57SRishi Gupta /*
7217b779f57SRishi Gupta * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2,
7227b779f57SRishi Gupta * persistence to 1 x integration time and the threshold
7237b779f57SRishi Gupta * interrupt disabled by default. First shutdown the sensor,
7247b779f57SRishi Gupta * update registers and then power on the sensor.
7257b779f57SRishi Gupta */
veml6030_hw_init(struct iio_dev * indio_dev)7267b779f57SRishi Gupta static int veml6030_hw_init(struct iio_dev *indio_dev)
7277b779f57SRishi Gupta {
7287b779f57SRishi Gupta int ret, val;
7297b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
7307b779f57SRishi Gupta struct i2c_client *client = data->client;
7317b779f57SRishi Gupta
7327b779f57SRishi Gupta ret = veml6030_als_shut_down(data);
7337b779f57SRishi Gupta if (ret) {
7347b779f57SRishi Gupta dev_err(&client->dev, "can't shutdown als %d\n", ret);
7357b779f57SRishi Gupta return ret;
7367b779f57SRishi Gupta }
7377b779f57SRishi Gupta
7387b779f57SRishi Gupta ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 0x1001);
7397b779f57SRishi Gupta if (ret) {
7407b779f57SRishi Gupta dev_err(&client->dev, "can't setup als configs %d\n", ret);
7417b779f57SRishi Gupta return ret;
7427b779f57SRishi Gupta }
7437b779f57SRishi Gupta
7447b779f57SRishi Gupta ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM,
7457b779f57SRishi Gupta VEML6030_PSM | VEML6030_PSM_EN, 0x03);
7467b779f57SRishi Gupta if (ret) {
7477b779f57SRishi Gupta dev_err(&client->dev, "can't setup default PSM %d\n", ret);
7487b779f57SRishi Gupta return ret;
7497b779f57SRishi Gupta }
7507b779f57SRishi Gupta
7517b779f57SRishi Gupta ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF);
7527b779f57SRishi Gupta if (ret) {
7537b779f57SRishi Gupta dev_err(&client->dev, "can't setup high threshold %d\n", ret);
7547b779f57SRishi Gupta return ret;
7557b779f57SRishi Gupta }
7567b779f57SRishi Gupta
7577b779f57SRishi Gupta ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000);
7587b779f57SRishi Gupta if (ret) {
7597b779f57SRishi Gupta dev_err(&client->dev, "can't setup low threshold %d\n", ret);
7607b779f57SRishi Gupta return ret;
7617b779f57SRishi Gupta }
7627b779f57SRishi Gupta
7637b779f57SRishi Gupta ret = veml6030_als_pwr_on(data);
7647b779f57SRishi Gupta if (ret) {
7657b779f57SRishi Gupta dev_err(&client->dev, "can't poweron als %d\n", ret);
7667b779f57SRishi Gupta return ret;
7677b779f57SRishi Gupta }
7687b779f57SRishi Gupta
7697b779f57SRishi Gupta /* Wait 4 ms to let processor & oscillator start correctly */
7707b779f57SRishi Gupta usleep_range(4000, 4002);
7717b779f57SRishi Gupta
7727b779f57SRishi Gupta /* Clear stale interrupt status bits if any during start */
7737b779f57SRishi Gupta ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val);
7747b779f57SRishi Gupta if (ret < 0) {
7757b779f57SRishi Gupta dev_err(&client->dev,
7767b779f57SRishi Gupta "can't clear als interrupt status %d\n", ret);
7777b779f57SRishi Gupta return ret;
7787b779f57SRishi Gupta }
7797b779f57SRishi Gupta
7807b779f57SRishi Gupta /* Cache currently active measurement parameters */
7817b779f57SRishi Gupta data->cur_gain = 3;
7826bd2b164SJavier Carrasco data->cur_resolution = 5376;
7837b779f57SRishi Gupta data->cur_integration_time = 3;
7847b779f57SRishi Gupta
7857b779f57SRishi Gupta return ret;
7867b779f57SRishi Gupta }
7877b779f57SRishi Gupta
veml6030_probe(struct i2c_client * client)788e465524dSUwe Kleine-König static int veml6030_probe(struct i2c_client *client)
7897b779f57SRishi Gupta {
7907b779f57SRishi Gupta int ret;
7917b779f57SRishi Gupta struct veml6030_data *data;
7927b779f57SRishi Gupta struct iio_dev *indio_dev;
7937b779f57SRishi Gupta struct regmap *regmap;
7947b779f57SRishi Gupta
7957b779f57SRishi Gupta if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
7967b779f57SRishi Gupta dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n");
7977b779f57SRishi Gupta return -EOPNOTSUPP;
7987b779f57SRishi Gupta }
7997b779f57SRishi Gupta
8007b779f57SRishi Gupta regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config);
8017b779f57SRishi Gupta if (IS_ERR(regmap)) {
8027b779f57SRishi Gupta dev_err(&client->dev, "can't setup regmap\n");
8037b779f57SRishi Gupta return PTR_ERR(regmap);
8047b779f57SRishi Gupta }
8057b779f57SRishi Gupta
8067b779f57SRishi Gupta indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
8077b779f57SRishi Gupta if (!indio_dev)
8087b779f57SRishi Gupta return -ENOMEM;
8097b779f57SRishi Gupta
8107b779f57SRishi Gupta data = iio_priv(indio_dev);
8117b779f57SRishi Gupta i2c_set_clientdata(client, indio_dev);
8127b779f57SRishi Gupta data->client = client;
8137b779f57SRishi Gupta data->regmap = regmap;
8147b779f57SRishi Gupta
8157b779f57SRishi Gupta indio_dev->name = "veml6030";
8167b779f57SRishi Gupta indio_dev->channels = veml6030_channels;
8177b779f57SRishi Gupta indio_dev->num_channels = ARRAY_SIZE(veml6030_channels);
8187b779f57SRishi Gupta indio_dev->modes = INDIO_DIRECT_MODE;
8197b779f57SRishi Gupta
8207b779f57SRishi Gupta if (client->irq) {
8217b779f57SRishi Gupta ret = devm_request_threaded_irq(&client->dev, client->irq,
8227b779f57SRishi Gupta NULL, veml6030_event_handler,
8237b779f57SRishi Gupta IRQF_TRIGGER_LOW | IRQF_ONESHOT,
8247b779f57SRishi Gupta "veml6030", indio_dev);
8257b779f57SRishi Gupta if (ret < 0) {
8267b779f57SRishi Gupta dev_err(&client->dev,
8277b779f57SRishi Gupta "irq %d request failed\n", client->irq);
8287b779f57SRishi Gupta return ret;
8297b779f57SRishi Gupta }
8307b779f57SRishi Gupta indio_dev->info = &veml6030_info;
8317b779f57SRishi Gupta } else {
8327b779f57SRishi Gupta indio_dev->info = &veml6030_info_no_irq;
8337b779f57SRishi Gupta }
8347b779f57SRishi Gupta
8357b779f57SRishi Gupta ret = veml6030_hw_init(indio_dev);
8367b779f57SRishi Gupta if (ret < 0)
8377b779f57SRishi Gupta return ret;
8387b779f57SRishi Gupta
8397b779f57SRishi Gupta ret = devm_add_action_or_reset(&client->dev,
8407b779f57SRishi Gupta veml6030_als_shut_down_action, data);
8417b779f57SRishi Gupta if (ret < 0)
8427b779f57SRishi Gupta return ret;
8437b779f57SRishi Gupta
8447b779f57SRishi Gupta return devm_iio_device_register(&client->dev, indio_dev);
8457b779f57SRishi Gupta }
8467b779f57SRishi Gupta
veml6030_runtime_suspend(struct device * dev)8471539e05bSJonathan Cameron static int veml6030_runtime_suspend(struct device *dev)
8487b779f57SRishi Gupta {
8497b779f57SRishi Gupta int ret;
8507b779f57SRishi Gupta struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
8517b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
8527b779f57SRishi Gupta
8537b779f57SRishi Gupta ret = veml6030_als_shut_down(data);
8547b779f57SRishi Gupta if (ret < 0)
8557b779f57SRishi Gupta dev_err(&data->client->dev, "can't suspend als %d\n", ret);
8567b779f57SRishi Gupta
8577b779f57SRishi Gupta return ret;
8587b779f57SRishi Gupta }
8597b779f57SRishi Gupta
veml6030_runtime_resume(struct device * dev)8601539e05bSJonathan Cameron static int veml6030_runtime_resume(struct device *dev)
8617b779f57SRishi Gupta {
8627b779f57SRishi Gupta int ret;
8637b779f57SRishi Gupta struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
8647b779f57SRishi Gupta struct veml6030_data *data = iio_priv(indio_dev);
8657b779f57SRishi Gupta
8667b779f57SRishi Gupta ret = veml6030_als_pwr_on(data);
8677b779f57SRishi Gupta if (ret < 0)
8687b779f57SRishi Gupta dev_err(&data->client->dev, "can't resume als %d\n", ret);
8697b779f57SRishi Gupta
8707b779f57SRishi Gupta return ret;
8717b779f57SRishi Gupta }
8727b779f57SRishi Gupta
8731539e05bSJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend,
8741539e05bSJonathan Cameron veml6030_runtime_resume, NULL);
8757b779f57SRishi Gupta
8767b779f57SRishi Gupta static const struct of_device_id veml6030_of_match[] = {
8777b779f57SRishi Gupta { .compatible = "vishay,veml6030" },
8787b779f57SRishi Gupta { }
8797b779f57SRishi Gupta };
8807b779f57SRishi Gupta MODULE_DEVICE_TABLE(of, veml6030_of_match);
8817b779f57SRishi Gupta
8827b779f57SRishi Gupta static const struct i2c_device_id veml6030_id[] = {
8837b779f57SRishi Gupta { "veml6030", 0 },
8847b779f57SRishi Gupta { }
8857b779f57SRishi Gupta };
8867b779f57SRishi Gupta MODULE_DEVICE_TABLE(i2c, veml6030_id);
8877b779f57SRishi Gupta
8887b779f57SRishi Gupta static struct i2c_driver veml6030_driver = {
8897b779f57SRishi Gupta .driver = {
8907b779f57SRishi Gupta .name = "veml6030",
8917b779f57SRishi Gupta .of_match_table = veml6030_of_match,
8921539e05bSJonathan Cameron .pm = pm_ptr(&veml6030_pm_ops),
8937b779f57SRishi Gupta },
8947cf15f42SUwe Kleine-König .probe = veml6030_probe,
8957b779f57SRishi Gupta .id_table = veml6030_id,
8967b779f57SRishi Gupta };
8977b779f57SRishi Gupta module_i2c_driver(veml6030_driver);
8987b779f57SRishi Gupta
8997b779f57SRishi Gupta MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>");
9007b779f57SRishi Gupta MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor");
9017b779f57SRishi Gupta MODULE_LICENSE("GPL v2");
902