1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * IIO driver for the light sensor ISL76682.
4 * ISL76682 is Ambient Light Sensor
5 *
6 * Copyright (c) 2023 Marek Vasut <marex@denx.de>
7 */
8
9#include <linux/array_size.h>
10#include <linux/bits.h>
11#include <linux/cleanup.h>
12#include <linux/delay.h>
13#include <linux/err.h>
14#include <linux/i2c.h>
15#include <linux/module.h>
16#include <linux/mutex.h>
17#include <linux/regmap.h>
18#include <linux/types.h>
19
20#include <linux/iio/iio.h>
21
22#define ISL76682_REG_COMMAND 0x00
23
24#define ISL76682_COMMAND_EN BIT(7)
25#define ISL76682_COMMAND_MODE_CONTINUOUS BIT(6)
26#define ISL76682_COMMAND_LIGHT_IR BIT(5)
27
28#define ISL76682_COMMAND_RANGE_LUX_1K 0x0
29#define ISL76682_COMMAND_RANGE_LUX_4K 0x1
30#define ISL76682_COMMAND_RANGE_LUX_16K 0x2
31#define ISL76682_COMMAND_RANGE_LUX_64K 0x3
32#define ISL76682_COMMAND_RANGE_LUX_MASK GENMASK(1, 0)
33
34#define ISL76682_REG_ALSIR_L 0x01
35
36#define ISL76682_REG_ALSIR_U 0x02
37
38#define ISL76682_NUM_REGS (ISL76682_REG_ALSIR_U + 1)
39
40#define ISL76682_CONV_TIME_MS 100
41#define ISL76682_INT_TIME_US 90000
42
43#define ISL76682_ADC_MAX (BIT(16) - 1)
44
45struct isl76682_chip {
46 /*
47 * Lock to synchronize access to device command register
48 * and the content of range variable below.
49 */
50 struct mutex lock;
51 struct regmap *regmap;
52 u8 range;
53 u8 command;
54};
55
56struct isl76682_range {
57 u8 range;
58 u32 als;
59 u32 ir;
60};
61
62static struct isl76682_range isl76682_range_table[] = {
63 { ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 },
64 { ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 },
65 { ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 },
66 { ISL76682_COMMAND_RANGE_LUX_64K, 960000, 673000 }
67};
68
69static int isl76682_get(struct isl76682_chip *chip, bool mode_ir, int *data)
70{
71 u8 command;
72 int ret;
73
74 command = ISL76682_COMMAND_EN | ISL76682_COMMAND_MODE_CONTINUOUS |
75 chip->range;
76
77 if (mode_ir)
78 command |= ISL76682_COMMAND_LIGHT_IR;
79
80 if (command != chip->command) {
81 ret = regmap_write(map: chip->regmap, ISL76682_REG_COMMAND, val: command);
82 if (ret)
83 return ret;
84
85 /* Need to wait for conversion time if ALS/IR mode enabled */
86 msleep(ISL76682_CONV_TIME_MS);
87
88 chip->command = command;
89 }
90
91 ret = regmap_bulk_read(map: chip->regmap, ISL76682_REG_ALSIR_L, val: data, val_count: 2);
92 *data &= ISL76682_ADC_MAX;
93 return ret;
94}
95
96static int isl76682_write_raw(struct iio_dev *indio_dev,
97 struct iio_chan_spec const *chan,
98 int val, int val2, long mask)
99{
100 struct isl76682_chip *chip = iio_priv(indio_dev);
101 int i;
102
103 if (mask != IIO_CHAN_INFO_SCALE)
104 return -EINVAL;
105
106 if (val != 0)
107 return -EINVAL;
108
109 for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {
110 if (chan->type == IIO_LIGHT && val2 != isl76682_range_table[i].als)
111 continue;
112 if (chan->type == IIO_INTENSITY && val2 != isl76682_range_table[i].ir)
113 continue;
114
115 scoped_guard(mutex, &chip->lock)
116 chip->range = isl76682_range_table[i].range;
117 return 0;
118 }
119
120 return -EINVAL;
121}
122
123static int isl76682_read_raw(struct iio_dev *indio_dev,
124 struct iio_chan_spec const *chan,
125 int *val, int *val2, long mask)
126{
127 struct isl76682_chip *chip = iio_priv(indio_dev);
128 int ret;
129 int i;
130
131 guard(mutex)(T: &chip->lock);
132
133 switch (mask) {
134 case IIO_CHAN_INFO_RAW:
135 switch (chan->type) {
136 case IIO_LIGHT:
137 ret = isl76682_get(chip, mode_ir: false, data: val);
138 return (ret < 0) ? ret : IIO_VAL_INT;
139 case IIO_INTENSITY:
140 ret = isl76682_get(chip, mode_ir: true, data: val);
141 return (ret < 0) ? ret : IIO_VAL_INT;
142 default:
143 return -EINVAL;
144 }
145 case IIO_CHAN_INFO_SCALE:
146 for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {
147 if (chip->range != isl76682_range_table[i].range)
148 continue;
149
150 *val = 0;
151 switch (chan->type) {
152 case IIO_LIGHT:
153 *val2 = isl76682_range_table[i].als;
154 return IIO_VAL_INT_PLUS_MICRO;
155 case IIO_INTENSITY:
156 *val2 = isl76682_range_table[i].ir;
157 return IIO_VAL_INT_PLUS_MICRO;
158 default:
159 return -EINVAL;
160 }
161 }
162 return -EINVAL;
163 case IIO_CHAN_INFO_INT_TIME:
164 *val = 0;
165 *val2 = ISL76682_INT_TIME_US;
166 return IIO_VAL_INT_PLUS_MICRO;
167 default:
168 return -EINVAL;
169 }
170}
171
172static int illuminance_scale_available[] = {
173 0, 15000,
174 0, 60000,
175 0, 240000,
176 0, 960000,
177};
178
179static int intensity_scale_available[] = {
180 0, 10500,
181 0, 42000,
182 0, 168000,
183 0, 673000,
184};
185
186static int isl76682_read_avail(struct iio_dev *indio_dev,
187 struct iio_chan_spec const *chan,
188 const int **vals, int *type,
189 int *length, long mask)
190{
191 switch (mask) {
192 case IIO_CHAN_INFO_SCALE:
193 switch (chan->type) {
194 case IIO_LIGHT:
195 *vals = illuminance_scale_available;
196 *length = ARRAY_SIZE(illuminance_scale_available);
197 *type = IIO_VAL_INT_PLUS_MICRO;
198 return IIO_AVAIL_LIST;
199 case IIO_INTENSITY:
200 *vals = intensity_scale_available;
201 *length = ARRAY_SIZE(intensity_scale_available);
202 *type = IIO_VAL_INT_PLUS_MICRO;
203 return IIO_AVAIL_LIST;
204 default:
205 return -EINVAL;
206 }
207 default:
208 return -EINVAL;
209 }
210}
211
212static const struct iio_chan_spec isl76682_channels[] = {
213 {
214 .type = IIO_LIGHT,
215 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
216 BIT(IIO_CHAN_INFO_SCALE),
217 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
218 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
219 }, {
220 .type = IIO_INTENSITY,
221 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
222 BIT(IIO_CHAN_INFO_SCALE),
223 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
224 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
225 }
226};
227
228static const struct iio_info isl76682_info = {
229 .read_avail = isl76682_read_avail,
230 .read_raw = isl76682_read_raw,
231 .write_raw = isl76682_write_raw,
232};
233
234static int isl76682_clear_configure_reg(struct isl76682_chip *chip)
235{
236 struct device *dev = regmap_get_device(map: chip->regmap);
237 int ret;
238
239 ret = regmap_write(map: chip->regmap, ISL76682_REG_COMMAND, val: 0x0);
240 if (ret < 0)
241 dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret);
242
243 /*
244 * In the success case, the command register was zeroed out.
245 *
246 * In the error case, we do not know in which state the command
247 * register is, so we assume it is zeroed out, so that it would
248 * be reprogrammed at the next data read out, and at that time
249 * we hope it would be reprogrammed successfully. That is very
250 * much a best effort approach.
251 */
252 chip->command = 0;
253
254 return ret;
255}
256
257static void isl76682_reset_action(void *chip)
258{
259 isl76682_clear_configure_reg(chip);
260}
261
262static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg)
263{
264 switch (reg) {
265 case ISL76682_REG_ALSIR_L:
266 case ISL76682_REG_ALSIR_U:
267 return true;
268 default:
269 return false;
270 }
271}
272
273static const struct regmap_config isl76682_regmap_config = {
274 .reg_bits = 8,
275 .val_bits = 8,
276 .volatile_reg = isl76682_is_volatile_reg,
277 .max_register = ISL76682_NUM_REGS - 1,
278 .num_reg_defaults_raw = ISL76682_NUM_REGS,
279 .cache_type = REGCACHE_FLAT,
280};
281
282static int isl76682_probe(struct i2c_client *client)
283{
284 struct device *dev = &client->dev;
285 struct isl76682_chip *chip;
286 struct iio_dev *indio_dev;
287 int ret;
288
289 indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*chip));
290 if (!indio_dev)
291 return -ENOMEM;
292
293 chip = iio_priv(indio_dev);
294
295 mutex_init(&chip->lock);
296
297 chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config);
298 ret = PTR_ERR_OR_ZERO(ptr: chip->regmap);
299 if (ret)
300 return dev_err_probe(dev, err: ret, fmt: "Error initializing regmap\n");
301
302 chip->range = ISL76682_COMMAND_RANGE_LUX_1K;
303
304 ret = isl76682_clear_configure_reg(chip);
305 if (ret < 0)
306 return ret;
307
308 ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip);
309 if (ret)
310 return ret;
311
312 indio_dev->info = &isl76682_info;
313 indio_dev->channels = isl76682_channels;
314 indio_dev->num_channels = ARRAY_SIZE(isl76682_channels);
315 indio_dev->name = "isl76682";
316 indio_dev->modes = INDIO_DIRECT_MODE;
317
318 return devm_iio_device_register(dev, indio_dev);
319}
320
321static const struct i2c_device_id isl76682_id[] = {
322 { "isl76682" },
323 { }
324};
325MODULE_DEVICE_TABLE(i2c, isl76682_id);
326
327static const struct of_device_id isl76682_of_match[] = {
328 { .compatible = "isil,isl76682" },
329 { }
330};
331MODULE_DEVICE_TABLE(of, isl76682_of_match);
332
333static struct i2c_driver isl76682_driver = {
334 .driver = {
335 .name = "isl76682",
336 .of_match_table = isl76682_of_match,
337 },
338 .probe = isl76682_probe,
339 .id_table = isl76682_id,
340};
341module_i2c_driver(isl76682_driver);
342
343MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver");
344MODULE_LICENSE("GPL");
345MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
346

source code of linux/drivers/iio/light/isl76682.c