xref: /linux/drivers/iio/light/ltr390.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * IIO driver for Lite-On LTR390 ALS and UV sensor
4  * (7-bit I2C slave address 0x53)
5  *
6  * Based on the work of:
7  *   Shreeya Patel and Shi Zhigang (LTRF216 Driver)
8  *
9  * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
10  *
11  * Datasheet:
12  *   https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
13  *
14  * TODO:
15  *   - Support for configurable gain and resolution
16  *   - Sensor suspend/resume support
17  *   - Add support for reading the ALS
18  *   - Interrupt support
19  */
20 
21 #include <linux/i2c.h>
22 #include <linux/math.h>
23 #include <linux/module.h>
24 #include <linux/mutex.h>
25 #include <linux/regmap.h>
26 
27 #include <linux/iio/iio.h>
28 
29 #include <asm/unaligned.h>
30 
31 #define LTR390_MAIN_CTRL      0x00
32 #define LTR390_PART_ID	      0x06
33 #define LTR390_UVS_DATA	      0x10
34 
35 #define LTR390_SW_RESET	      BIT(4)
36 #define LTR390_UVS_MODE	      BIT(3)
37 #define LTR390_SENSOR_ENABLE  BIT(1)
38 
39 #define LTR390_PART_NUMBER_ID 0xb
40 
41 /*
42  * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
43  * the sensor are equal to 1 UV Index [Datasheet Page#8].
44  *
45  * For the default resolution of 18-bit (integration time: 100ms) and default
46  * gain of 3x, the counts/uvi are calculated as follows:
47  * 2300 / ((3/18) * (100/400)) = 95.83
48  */
49 #define LTR390_COUNTS_PER_UVI 96
50 
51 /*
52  * Window Factor is needed when the device is under Window glass with coated
53  * tinted ink. This is to compensate for the light loss due to the lower
54  * transmission rate of the window glass and helps * in calculating lux.
55  */
56 #define LTR390_WINDOW_FACTOR 1
57 
58 struct ltr390_data {
59 	struct regmap *regmap;
60 	struct i2c_client *client;
61 	/* Protects device from simulataneous reads */
62 	struct mutex lock;
63 };
64 
65 static const struct regmap_config ltr390_regmap_config = {
66 	.name = "ltr390",
67 	.reg_bits = 8,
68 	.reg_stride = 1,
69 	.val_bits = 8,
70 };
71 
72 static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
73 {
74 	struct device *dev = &data->client->dev;
75 	int ret;
76 	u8 recieve_buffer[3];
77 
78 	guard(mutex)(&data->lock);
79 
80 	ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
81 			       sizeof(recieve_buffer));
82 	if (ret) {
83 		dev_err(dev, "failed to read measurement data");
84 		return ret;
85 	}
86 
87 	return get_unaligned_le24(recieve_buffer);
88 }
89 
90 static int ltr390_read_raw(struct iio_dev *iio_device,
91 			   struct iio_chan_spec const *chan, int *val,
92 			   int *val2, long mask)
93 {
94 	int ret;
95 	struct ltr390_data *data = iio_priv(iio_device);
96 
97 	switch (mask) {
98 	case IIO_CHAN_INFO_RAW:
99 		ret = ltr390_register_read(data, LTR390_UVS_DATA);
100 		if (ret < 0)
101 			return ret;
102 		*val = ret;
103 		return IIO_VAL_INT;
104 	case IIO_CHAN_INFO_SCALE:
105 		*val = LTR390_WINDOW_FACTOR;
106 		*val2 = LTR390_COUNTS_PER_UVI;
107 		return IIO_VAL_FRACTIONAL;
108 	default:
109 		return -EINVAL;
110 	}
111 }
112 
113 static const struct iio_info ltr390_info = {
114 	.read_raw = ltr390_read_raw,
115 };
116 
117 static const struct iio_chan_spec ltr390_channel = {
118 	.type = IIO_UVINDEX,
119 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
120 };
121 
122 static int ltr390_probe(struct i2c_client *client)
123 {
124 	struct ltr390_data *data;
125 	struct iio_dev *indio_dev;
126 	struct device *dev;
127 	int ret, part_number;
128 
129 	dev = &client->dev;
130 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
131 	if (!indio_dev)
132 		return -ENOMEM;
133 
134 	data = iio_priv(indio_dev);
135 
136 	data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
137 	if (IS_ERR(data->regmap))
138 		return dev_err_probe(dev, PTR_ERR(data->regmap),
139 				     "regmap initialization failed\n");
140 
141 	data->client = client;
142 	mutex_init(&data->lock);
143 
144 	indio_dev->info = &ltr390_info;
145 	indio_dev->channels = &ltr390_channel;
146 	indio_dev->num_channels = 1;
147 	indio_dev->name = "ltr390";
148 
149 	ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
150 	if (ret)
151 		return dev_err_probe(dev, ret,
152 				     "failed to get sensor's part id\n");
153 	/* Lower 4 bits of `part_number` change with hardware revisions */
154 	if (part_number >> 4 != LTR390_PART_NUMBER_ID)
155 		dev_info(dev, "received invalid product id: 0x%x", part_number);
156 	dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
157 
158 	/* reset sensor, chip fails to respond to this, so ignore any errors */
159 	regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
160 
161 	/* Wait for the registers to reset before proceeding */
162 	usleep_range(1000, 2000);
163 
164 	ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
165 			      LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
166 	if (ret)
167 		return dev_err_probe(dev, ret, "failed to enable the sensor\n");
168 
169 	return devm_iio_device_register(dev, indio_dev);
170 }
171 
172 static const struct i2c_device_id ltr390_id[] = {
173 	{ "ltr390" },
174 	{ /* Sentinel */ }
175 };
176 MODULE_DEVICE_TABLE(i2c, ltr390_id);
177 
178 static const struct of_device_id ltr390_of_table[] = {
179 	{ .compatible = "liteon,ltr390" },
180 	{ /* Sentinel */ }
181 };
182 MODULE_DEVICE_TABLE(of, ltr390_of_table);
183 
184 static struct i2c_driver ltr390_driver = {
185 	.driver = {
186 		.name = "ltr390",
187 		.of_match_table = ltr390_of_table,
188 	},
189 	.probe = ltr390_probe,
190 	.id_table = ltr390_id,
191 };
192 module_i2c_driver(ltr390_driver);
193 
194 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
195 MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
196 MODULE_LICENSE("GPL");
197