xref: /openbmc/linux/drivers/iio/light/veml6030.c (revision 5f8b7d4b2e9604d03ae06f1a2dd5a1f34c33e533)
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
4647b779f57SRishi Gupta 	else
4657b779f57SRishi Gupta 		ret = regmap_read(data->regmap, VEML6030_REG_ALS_WL, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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