xref: /linux/drivers/iio/light/ltr390.c (revision 7ec462100ef9142344ddbf86f2c3008b97acddbe)
18b0d4c40SAnshul Dalal // SPDX-License-Identifier: GPL-2.0-or-later
28b0d4c40SAnshul Dalal /*
38b0d4c40SAnshul Dalal  * IIO driver for Lite-On LTR390 ALS and UV sensor
48b0d4c40SAnshul Dalal  * (7-bit I2C slave address 0x53)
58b0d4c40SAnshul Dalal  *
68b0d4c40SAnshul Dalal  * Based on the work of:
78b0d4c40SAnshul Dalal  *   Shreeya Patel and Shi Zhigang (LTRF216 Driver)
88b0d4c40SAnshul Dalal  *
98b0d4c40SAnshul Dalal  * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
108b0d4c40SAnshul Dalal  *
118b0d4c40SAnshul Dalal  * Datasheet:
128b0d4c40SAnshul Dalal  *   https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
138b0d4c40SAnshul Dalal  *
148b0d4c40SAnshul Dalal  * TODO:
158b0d4c40SAnshul Dalal  *   - Support for configurable gain and resolution
168b0d4c40SAnshul Dalal  *   - Sensor suspend/resume support
178b0d4c40SAnshul Dalal  *   - Add support for reading the ALS
188b0d4c40SAnshul Dalal  *   - Interrupt support
198b0d4c40SAnshul Dalal  */
208b0d4c40SAnshul Dalal 
218b0d4c40SAnshul Dalal #include <linux/i2c.h>
228b0d4c40SAnshul Dalal #include <linux/math.h>
238b0d4c40SAnshul Dalal #include <linux/module.h>
248b0d4c40SAnshul Dalal #include <linux/mutex.h>
258b0d4c40SAnshul Dalal #include <linux/regmap.h>
2613fad260SAbhash Jha #include <linux/bitfield.h>
278b0d4c40SAnshul Dalal 
288b0d4c40SAnshul Dalal #include <linux/iio/iio.h>
298b0d4c40SAnshul Dalal 
30*5f60d5f6SAl Viro #include <linux/unaligned.h>
318b0d4c40SAnshul Dalal 
328b0d4c40SAnshul Dalal #define LTR390_MAIN_CTRL		0x00
3313fad260SAbhash Jha #define LTR390_ALS_UVS_MEAS_RATE	0x04
3413fad260SAbhash Jha #define LTR390_ALS_UVS_GAIN		0x05
358b0d4c40SAnshul Dalal #define LTR390_PART_ID			0x06
3613fad260SAbhash Jha #define LTR390_ALS_DATA			0x0D
378b0d4c40SAnshul Dalal #define LTR390_UVS_DATA			0x10
3813fad260SAbhash Jha #define LTR390_INT_CFG			0x19
3913fad260SAbhash Jha 
4013fad260SAbhash Jha #define LTR390_PART_NUMBER_ID		0xb
4113fad260SAbhash Jha #define LTR390_ALS_UVS_GAIN_MASK	0x07
4213fad260SAbhash Jha #define LTR390_ALS_UVS_INT_TIME_MASK	0x70
4313fad260SAbhash Jha #define LTR390_ALS_UVS_INT_TIME(x)	FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x))
448b0d4c40SAnshul Dalal 
458b0d4c40SAnshul Dalal #define LTR390_SW_RESET	      BIT(4)
468b0d4c40SAnshul Dalal #define LTR390_UVS_MODE	      BIT(3)
478b0d4c40SAnshul Dalal #define LTR390_SENSOR_ENABLE  BIT(1)
488b0d4c40SAnshul Dalal 
494bd7e5ceSAbhash Jha #define LTR390_FRACTIONAL_PRECISION 100
504bd7e5ceSAbhash Jha 
518b0d4c40SAnshul Dalal /*
528b0d4c40SAnshul Dalal  * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
538b0d4c40SAnshul Dalal  * the sensor are equal to 1 UV Index [Datasheet Page#8].
548b0d4c40SAnshul Dalal  *
558b0d4c40SAnshul Dalal  * For the default resolution of 18-bit (integration time: 100ms) and default
568b0d4c40SAnshul Dalal  * gain of 3x, the counts/uvi are calculated as follows:
578b0d4c40SAnshul Dalal  * 2300 / ((3/18) * (100/400)) = 95.83
588b0d4c40SAnshul Dalal  */
598b0d4c40SAnshul Dalal #define LTR390_COUNTS_PER_UVI 96
608b0d4c40SAnshul Dalal 
618b0d4c40SAnshul Dalal /*
628b0d4c40SAnshul Dalal  * Window Factor is needed when the device is under Window glass with coated
638b0d4c40SAnshul Dalal  * tinted ink. This is to compensate for the light loss due to the lower
648b0d4c40SAnshul Dalal  * transmission rate of the window glass and helps * in calculating lux.
658b0d4c40SAnshul Dalal  */
668b0d4c40SAnshul Dalal #define LTR390_WINDOW_FACTOR 1
678b0d4c40SAnshul Dalal 
6814e0d914SAbhash Jha enum ltr390_mode {
6914e0d914SAbhash Jha 	LTR390_SET_ALS_MODE,
7014e0d914SAbhash Jha 	LTR390_SET_UVS_MODE,
7114e0d914SAbhash Jha };
7214e0d914SAbhash Jha 
738b0d4c40SAnshul Dalal struct ltr390_data {
748b0d4c40SAnshul Dalal 	struct regmap *regmap;
758b0d4c40SAnshul Dalal 	struct i2c_client *client;
768b0d4c40SAnshul Dalal 	/* Protects device from simulataneous reads */
778b0d4c40SAnshul Dalal 	struct mutex lock;
7814e0d914SAbhash Jha 	enum ltr390_mode mode;
7913fad260SAbhash Jha 	int gain;
8013fad260SAbhash Jha 	int int_time_us;
818b0d4c40SAnshul Dalal };
828b0d4c40SAnshul Dalal 
838b0d4c40SAnshul Dalal static const struct regmap_config ltr390_regmap_config = {
848b0d4c40SAnshul Dalal 	.name = "ltr390",
858b0d4c40SAnshul Dalal 	.reg_bits = 8,
868b0d4c40SAnshul Dalal 	.reg_stride = 1,
878b0d4c40SAnshul Dalal 	.val_bits = 8,
888b0d4c40SAnshul Dalal };
898b0d4c40SAnshul Dalal 
ltr390_register_read(struct ltr390_data * data,u8 register_address)908b0d4c40SAnshul Dalal static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
918b0d4c40SAnshul Dalal {
928b0d4c40SAnshul Dalal 	struct device *dev = &data->client->dev;
938b0d4c40SAnshul Dalal 	int ret;
948b0d4c40SAnshul Dalal 	u8 recieve_buffer[3];
958b0d4c40SAnshul Dalal 
968b0d4c40SAnshul Dalal 	ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
978b0d4c40SAnshul Dalal 			       sizeof(recieve_buffer));
988b0d4c40SAnshul Dalal 	if (ret) {
998b0d4c40SAnshul Dalal 		dev_err(dev, "failed to read measurement data");
1008b0d4c40SAnshul Dalal 		return ret;
1018b0d4c40SAnshul Dalal 	}
1028b0d4c40SAnshul Dalal 
1038b0d4c40SAnshul Dalal 	return get_unaligned_le24(recieve_buffer);
1048b0d4c40SAnshul Dalal }
1058b0d4c40SAnshul Dalal 
ltr390_set_mode(struct ltr390_data * data,enum ltr390_mode mode)10614e0d914SAbhash Jha static int ltr390_set_mode(struct ltr390_data *data, enum ltr390_mode mode)
10714e0d914SAbhash Jha {
10814e0d914SAbhash Jha 	int ret;
10914e0d914SAbhash Jha 
11014e0d914SAbhash Jha 	if (data->mode == mode)
11114e0d914SAbhash Jha 		return 0;
11214e0d914SAbhash Jha 
11314e0d914SAbhash Jha 	switch (mode) {
11414e0d914SAbhash Jha 	case LTR390_SET_ALS_MODE:
11514e0d914SAbhash Jha 		ret = regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
11614e0d914SAbhash Jha 		break;
11714e0d914SAbhash Jha 
11814e0d914SAbhash Jha 	case LTR390_SET_UVS_MODE:
11914e0d914SAbhash Jha 		ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
12014e0d914SAbhash Jha 		break;
12114e0d914SAbhash Jha 	}
12214e0d914SAbhash Jha 
12314e0d914SAbhash Jha 	if (ret)
12414e0d914SAbhash Jha 		return ret;
12514e0d914SAbhash Jha 
12614e0d914SAbhash Jha 	data->mode = mode;
12714e0d914SAbhash Jha 	return 0;
12814e0d914SAbhash Jha }
12914e0d914SAbhash Jha 
ltr390_counts_per_uvi(struct ltr390_data * data)1304bd7e5ceSAbhash Jha static int ltr390_counts_per_uvi(struct ltr390_data *data)
1314bd7e5ceSAbhash Jha {
1324bd7e5ceSAbhash Jha 	const int orig_gain = 18;
1334bd7e5ceSAbhash Jha 	const int orig_int_time = 400;
1344bd7e5ceSAbhash Jha 
1354bd7e5ceSAbhash Jha 	return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time);
1364bd7e5ceSAbhash Jha }
1374bd7e5ceSAbhash Jha 
ltr390_read_raw(struct iio_dev * iio_device,struct iio_chan_spec const * chan,int * val,int * val2,long mask)1388b0d4c40SAnshul Dalal static int ltr390_read_raw(struct iio_dev *iio_device,
1398b0d4c40SAnshul Dalal 			   struct iio_chan_spec const *chan, int *val,
1408b0d4c40SAnshul Dalal 			   int *val2, long mask)
1418b0d4c40SAnshul Dalal {
1428b0d4c40SAnshul Dalal 	int ret;
1438b0d4c40SAnshul Dalal 	struct ltr390_data *data = iio_priv(iio_device);
1448b0d4c40SAnshul Dalal 
14513fad260SAbhash Jha 	guard(mutex)(&data->lock);
1468b0d4c40SAnshul Dalal 	switch (mask) {
1478b0d4c40SAnshul Dalal 	case IIO_CHAN_INFO_RAW:
14814e0d914SAbhash Jha 		switch (chan->type) {
14914e0d914SAbhash Jha 		case IIO_UVINDEX:
15014e0d914SAbhash Jha 			ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
15114e0d914SAbhash Jha 			if (ret < 0)
15214e0d914SAbhash Jha 				return ret;
15314e0d914SAbhash Jha 
1548b0d4c40SAnshul Dalal 			ret = ltr390_register_read(data, LTR390_UVS_DATA);
1558b0d4c40SAnshul Dalal 			if (ret < 0)
1568b0d4c40SAnshul Dalal 				return ret;
15714e0d914SAbhash Jha 			break;
15814e0d914SAbhash Jha 
15914e0d914SAbhash Jha 		case IIO_LIGHT:
16014e0d914SAbhash Jha 			ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
16114e0d914SAbhash Jha 			if (ret < 0)
16214e0d914SAbhash Jha 				return ret;
16314e0d914SAbhash Jha 
16414e0d914SAbhash Jha 			ret = ltr390_register_read(data, LTR390_ALS_DATA);
16514e0d914SAbhash Jha 			if (ret < 0)
16614e0d914SAbhash Jha 				return ret;
16714e0d914SAbhash Jha 			break;
16814e0d914SAbhash Jha 
16914e0d914SAbhash Jha 		default:
17014e0d914SAbhash Jha 			return -EINVAL;
17114e0d914SAbhash Jha 		}
1728b0d4c40SAnshul Dalal 		*val = ret;
1738b0d4c40SAnshul Dalal 		return IIO_VAL_INT;
1748b0d4c40SAnshul Dalal 	case IIO_CHAN_INFO_SCALE:
17514e0d914SAbhash Jha 		switch (chan->type) {
17614e0d914SAbhash Jha 		case IIO_UVINDEX:
1774bd7e5ceSAbhash Jha 			*val = LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION;
1784bd7e5ceSAbhash Jha 			*val2 = ltr390_counts_per_uvi(data);
1798b0d4c40SAnshul Dalal 			return IIO_VAL_FRACTIONAL;
18013fad260SAbhash Jha 
18114e0d914SAbhash Jha 		case IIO_LIGHT:
18214e0d914SAbhash Jha 			*val = LTR390_WINDOW_FACTOR * 6 * 100;
18314e0d914SAbhash Jha 			*val2 = data->gain * data->int_time_us;
18414e0d914SAbhash Jha 			return IIO_VAL_FRACTIONAL;
18514e0d914SAbhash Jha 
18614e0d914SAbhash Jha 		default:
18714e0d914SAbhash Jha 			return -EINVAL;
18814e0d914SAbhash Jha 		}
18914e0d914SAbhash Jha 
19013fad260SAbhash Jha 	case IIO_CHAN_INFO_INT_TIME:
19113fad260SAbhash Jha 		*val = data->int_time_us;
19213fad260SAbhash Jha 		return IIO_VAL_INT;
19313fad260SAbhash Jha 
19413fad260SAbhash Jha 	default:
19513fad260SAbhash Jha 		return -EINVAL;
19613fad260SAbhash Jha 	}
19713fad260SAbhash Jha }
19813fad260SAbhash Jha 
19913fad260SAbhash Jha /* integration time in us */
20013fad260SAbhash Jha static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 };
20113fad260SAbhash Jha static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 };
20213fad260SAbhash Jha 
20314e0d914SAbhash Jha static const struct iio_chan_spec ltr390_channels[] = {
20414e0d914SAbhash Jha 	/* UV sensor */
20514e0d914SAbhash Jha 	{
20613fad260SAbhash Jha 		.type = IIO_UVINDEX,
20714e0d914SAbhash Jha 		.scan_index = 0,
20813fad260SAbhash Jha 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
20913fad260SAbhash Jha 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
21013fad260SAbhash Jha 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
21114e0d914SAbhash Jha 	},
21214e0d914SAbhash Jha 	/* ALS sensor */
21314e0d914SAbhash Jha 	{
21414e0d914SAbhash Jha 		.type = IIO_LIGHT,
21514e0d914SAbhash Jha 		.scan_index = 1,
21614e0d914SAbhash Jha 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
21714e0d914SAbhash Jha 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
21814e0d914SAbhash Jha 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
21914e0d914SAbhash Jha 	},
22013fad260SAbhash Jha };
22113fad260SAbhash Jha 
ltr390_set_gain(struct ltr390_data * data,int val)22213fad260SAbhash Jha static int ltr390_set_gain(struct ltr390_data *data, int val)
22313fad260SAbhash Jha {
22413fad260SAbhash Jha 	int ret, idx;
22513fad260SAbhash Jha 
22613fad260SAbhash Jha 	for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
22713fad260SAbhash Jha 		if (ltr390_gain_map[idx] != val)
22813fad260SAbhash Jha 			continue;
22913fad260SAbhash Jha 
23013fad260SAbhash Jha 		guard(mutex)(&data->lock);
23113fad260SAbhash Jha 		ret = regmap_update_bits(data->regmap,
23213fad260SAbhash Jha 					LTR390_ALS_UVS_GAIN,
23313fad260SAbhash Jha 					LTR390_ALS_UVS_GAIN_MASK, idx);
23413fad260SAbhash Jha 		if (ret)
23513fad260SAbhash Jha 			return ret;
23613fad260SAbhash Jha 
23713fad260SAbhash Jha 		data->gain = ltr390_gain_map[idx];
23813fad260SAbhash Jha 		return 0;
23913fad260SAbhash Jha 	}
24013fad260SAbhash Jha 
24113fad260SAbhash Jha 	return -EINVAL;
24213fad260SAbhash Jha }
24313fad260SAbhash Jha 
ltr390_set_int_time(struct ltr390_data * data,int val)24413fad260SAbhash Jha static int ltr390_set_int_time(struct ltr390_data *data, int val)
24513fad260SAbhash Jha {
24613fad260SAbhash Jha 	int ret, idx;
24713fad260SAbhash Jha 
24813fad260SAbhash Jha 	for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
24913fad260SAbhash Jha 		if (ltr390_int_time_map_us[idx] != val)
25013fad260SAbhash Jha 			continue;
25113fad260SAbhash Jha 
25213fad260SAbhash Jha 		guard(mutex)(&data->lock);
25313fad260SAbhash Jha 		ret = regmap_update_bits(data->regmap,
25413fad260SAbhash Jha 					LTR390_ALS_UVS_MEAS_RATE,
25513fad260SAbhash Jha 					LTR390_ALS_UVS_INT_TIME_MASK,
25613fad260SAbhash Jha 					LTR390_ALS_UVS_INT_TIME(idx));
25713fad260SAbhash Jha 		if (ret)
25813fad260SAbhash Jha 			return ret;
25913fad260SAbhash Jha 
26013fad260SAbhash Jha 		data->int_time_us = ltr390_int_time_map_us[idx];
26113fad260SAbhash Jha 		return 0;
26213fad260SAbhash Jha 	}
26313fad260SAbhash Jha 
26413fad260SAbhash Jha 	return -EINVAL;
26513fad260SAbhash Jha }
26613fad260SAbhash Jha 
ltr390_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)26713fad260SAbhash Jha static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
26813fad260SAbhash Jha 				const int **vals, int *type, int *length, long mask)
26913fad260SAbhash Jha {
27013fad260SAbhash Jha 	switch (mask) {
27113fad260SAbhash Jha 	case IIO_CHAN_INFO_SCALE:
27213fad260SAbhash Jha 		*length = ARRAY_SIZE(ltr390_gain_map);
27313fad260SAbhash Jha 		*type = IIO_VAL_INT;
27413fad260SAbhash Jha 		*vals = ltr390_gain_map;
27513fad260SAbhash Jha 		return IIO_AVAIL_LIST;
27613fad260SAbhash Jha 	case IIO_CHAN_INFO_INT_TIME:
27713fad260SAbhash Jha 		*length = ARRAY_SIZE(ltr390_int_time_map_us);
27813fad260SAbhash Jha 		*type = IIO_VAL_INT;
27913fad260SAbhash Jha 		*vals = ltr390_int_time_map_us;
28013fad260SAbhash Jha 		return IIO_AVAIL_LIST;
28113fad260SAbhash Jha 	default:
28213fad260SAbhash Jha 		return -EINVAL;
28313fad260SAbhash Jha 	}
28413fad260SAbhash Jha }
28513fad260SAbhash Jha 
ltr390_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)28613fad260SAbhash Jha static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
28713fad260SAbhash Jha 				int val, int val2, long mask)
28813fad260SAbhash Jha {
28913fad260SAbhash Jha 	struct ltr390_data *data = iio_priv(indio_dev);
29013fad260SAbhash Jha 
29113fad260SAbhash Jha 	switch (mask) {
29213fad260SAbhash Jha 	case IIO_CHAN_INFO_SCALE:
29313fad260SAbhash Jha 		if (val2 != 0)
29413fad260SAbhash Jha 			return -EINVAL;
29513fad260SAbhash Jha 
29613fad260SAbhash Jha 		return ltr390_set_gain(data, val);
29713fad260SAbhash Jha 
29813fad260SAbhash Jha 	case IIO_CHAN_INFO_INT_TIME:
29913fad260SAbhash Jha 		if (val2 != 0)
30013fad260SAbhash Jha 			return -EINVAL;
30113fad260SAbhash Jha 
30213fad260SAbhash Jha 		return ltr390_set_int_time(data, val);
30313fad260SAbhash Jha 
3048b0d4c40SAnshul Dalal 	default:
3058b0d4c40SAnshul Dalal 		return -EINVAL;
3068b0d4c40SAnshul Dalal 	}
3078b0d4c40SAnshul Dalal }
3088b0d4c40SAnshul Dalal 
3098b0d4c40SAnshul Dalal static const struct iio_info ltr390_info = {
3108b0d4c40SAnshul Dalal 	.read_raw = ltr390_read_raw,
31113fad260SAbhash Jha 	.write_raw = ltr390_write_raw,
31213fad260SAbhash Jha 	.read_avail = ltr390_read_avail,
3138b0d4c40SAnshul Dalal };
3148b0d4c40SAnshul Dalal 
ltr390_probe(struct i2c_client * client)3158b0d4c40SAnshul Dalal static int ltr390_probe(struct i2c_client *client)
3168b0d4c40SAnshul Dalal {
3178b0d4c40SAnshul Dalal 	struct ltr390_data *data;
3188b0d4c40SAnshul Dalal 	struct iio_dev *indio_dev;
3198b0d4c40SAnshul Dalal 	struct device *dev;
3208b0d4c40SAnshul Dalal 	int ret, part_number;
3218b0d4c40SAnshul Dalal 
3228b0d4c40SAnshul Dalal 	dev = &client->dev;
3238b0d4c40SAnshul Dalal 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
3248b0d4c40SAnshul Dalal 	if (!indio_dev)
3258b0d4c40SAnshul Dalal 		return -ENOMEM;
3268b0d4c40SAnshul Dalal 
3278b0d4c40SAnshul Dalal 	data = iio_priv(indio_dev);
3288b0d4c40SAnshul Dalal 
3298b0d4c40SAnshul Dalal 	data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
3308b0d4c40SAnshul Dalal 	if (IS_ERR(data->regmap))
3318b0d4c40SAnshul Dalal 		return dev_err_probe(dev, PTR_ERR(data->regmap),
3328b0d4c40SAnshul Dalal 				     "regmap initialization failed\n");
3338b0d4c40SAnshul Dalal 
3348b0d4c40SAnshul Dalal 	data->client = client;
33513fad260SAbhash Jha 	/* default value of integration time from pg: 15 of the datasheet */
33613fad260SAbhash Jha 	data->int_time_us = 100000;
33713fad260SAbhash Jha 	/* default value of gain from pg: 16 of the datasheet */
33813fad260SAbhash Jha 	data->gain = 3;
33914e0d914SAbhash Jha 	/* default mode for ltr390 is ALS mode */
34014e0d914SAbhash Jha 	data->mode = LTR390_SET_ALS_MODE;
34113fad260SAbhash Jha 
3428b0d4c40SAnshul Dalal 	mutex_init(&data->lock);
3438b0d4c40SAnshul Dalal 
3448b0d4c40SAnshul Dalal 	indio_dev->info = &ltr390_info;
34514e0d914SAbhash Jha 	indio_dev->channels = ltr390_channels;
34614e0d914SAbhash Jha 	indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
3478b0d4c40SAnshul Dalal 	indio_dev->name = "ltr390";
3488b0d4c40SAnshul Dalal 
3498b0d4c40SAnshul Dalal 	ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
3508b0d4c40SAnshul Dalal 	if (ret)
3518b0d4c40SAnshul Dalal 		return dev_err_probe(dev, ret,
3528b0d4c40SAnshul Dalal 				     "failed to get sensor's part id\n");
3538b0d4c40SAnshul Dalal 	/* Lower 4 bits of `part_number` change with hardware revisions */
3548b0d4c40SAnshul Dalal 	if (part_number >> 4 != LTR390_PART_NUMBER_ID)
3558b0d4c40SAnshul Dalal 		dev_info(dev, "received invalid product id: 0x%x", part_number);
3568b0d4c40SAnshul Dalal 	dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
3578b0d4c40SAnshul Dalal 
3588b0d4c40SAnshul Dalal 	/* reset sensor, chip fails to respond to this, so ignore any errors */
3598b0d4c40SAnshul Dalal 	regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
3608b0d4c40SAnshul Dalal 
3618b0d4c40SAnshul Dalal 	/* Wait for the registers to reset before proceeding */
3628b0d4c40SAnshul Dalal 	usleep_range(1000, 2000);
3638b0d4c40SAnshul Dalal 
36414e0d914SAbhash Jha 	ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
3658b0d4c40SAnshul Dalal 	if (ret)
3668b0d4c40SAnshul Dalal 		return dev_err_probe(dev, ret, "failed to enable the sensor\n");
3678b0d4c40SAnshul Dalal 
3688b0d4c40SAnshul Dalal 	return devm_iio_device_register(dev, indio_dev);
3698b0d4c40SAnshul Dalal }
3708b0d4c40SAnshul Dalal 
3718b0d4c40SAnshul Dalal static const struct i2c_device_id ltr390_id[] = {
3728b0d4c40SAnshul Dalal 	{ "ltr390" },
3738b0d4c40SAnshul Dalal 	{ /* Sentinel */ }
3748b0d4c40SAnshul Dalal };
3758b0d4c40SAnshul Dalal MODULE_DEVICE_TABLE(i2c, ltr390_id);
3768b0d4c40SAnshul Dalal 
3778b0d4c40SAnshul Dalal static const struct of_device_id ltr390_of_table[] = {
3788b0d4c40SAnshul Dalal 	{ .compatible = "liteon,ltr390" },
3798b0d4c40SAnshul Dalal 	{ /* Sentinel */ }
3808b0d4c40SAnshul Dalal };
3818b0d4c40SAnshul Dalal MODULE_DEVICE_TABLE(of, ltr390_of_table);
3828b0d4c40SAnshul Dalal 
3838b0d4c40SAnshul Dalal static struct i2c_driver ltr390_driver = {
3848b0d4c40SAnshul Dalal 	.driver = {
3858b0d4c40SAnshul Dalal 		.name = "ltr390",
3868b0d4c40SAnshul Dalal 		.of_match_table = ltr390_of_table,
3878b0d4c40SAnshul Dalal 	},
3888b0d4c40SAnshul Dalal 	.probe = ltr390_probe,
3898b0d4c40SAnshul Dalal 	.id_table = ltr390_id,
3908b0d4c40SAnshul Dalal };
3918b0d4c40SAnshul Dalal module_i2c_driver(ltr390_driver);
3928b0d4c40SAnshul Dalal 
3938b0d4c40SAnshul Dalal MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
3948b0d4c40SAnshul Dalal MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
3958b0d4c40SAnshul Dalal MODULE_LICENSE("GPL");
396