File Index
Files and directories
Sign in
FILE

zopt2201.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/light/zopt2201.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
  4 *
  5 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
  6 *
  7 * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
  8 * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
  9 *
 10 * TODO: interrupt support, ALS/UVB raw mode
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/i2c.h>
 15#include <linux/mutex.h>
 16#include <linux/err.h>
 17#include <linux/delay.h>
 18
 19#include <linux/iio/iio.h>
 20#include <linux/iio/sysfs.h>
 21
 22#include <linux/unaligned.h>
 23
 24#define ZOPT2201_DRV_NAME "zopt2201"
 25
 26/* Registers */
 27#define ZOPT2201_MAIN_CTRL		0x00
 28#define ZOPT2201_LS_MEAS_RATE		0x04
 29#define ZOPT2201_LS_GAIN		0x05
 30#define ZOPT2201_PART_ID		0x06
 31#define ZOPT2201_MAIN_STATUS		0x07
 32#define ZOPT2201_ALS_DATA		0x0d /* LSB first, 13 to 20 bits */
 33#define ZOPT2201_UVB_DATA		0x10 /* LSB first, 13 to 20 bits */
 34#define ZOPT2201_UV_COMP_DATA		0x13 /* LSB first, 13 to 20 bits */
 35#define ZOPT2201_COMP_DATA		0x16 /* LSB first, 13 to 20 bits */
 36#define ZOPT2201_INT_CFG		0x19
 37#define ZOPT2201_INT_PST		0x1a
 38
 39#define ZOPT2201_MAIN_CTRL_LS_MODE	BIT(3) /* 0 .. ALS, 1 .. UV B */
 40#define ZOPT2201_MAIN_CTRL_LS_EN	BIT(1)
 41
 42/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
 43#define ZOPT2201_MEAS_RES_20BIT		0 /* takes 400 ms */
 44#define ZOPT2201_MEAS_RES_19BIT		1 /* takes 200 ms */
 45#define ZOPT2201_MEAS_RES_18BIT		2 /* takes 100 ms, default */
 46#define ZOPT2201_MEAS_RES_17BIT		3 /* takes 50 ms */
 47#define ZOPT2201_MEAS_RES_16BIT		4 /* takes 25 ms */
 48#define ZOPT2201_MEAS_RES_13BIT		5 /* takes 3.125 ms */
 49#define ZOPT2201_MEAS_RES_SHIFT		4
 50
 51/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
 52#define ZOPT2201_MEAS_FREQ_25MS		0
 53#define ZOPT2201_MEAS_FREQ_50MS		1
 54#define ZOPT2201_MEAS_FREQ_100MS	2 /* default */
 55#define ZOPT2201_MEAS_FREQ_200MS	3
 56#define ZOPT2201_MEAS_FREQ_500MS	4
 57#define ZOPT2201_MEAS_FREQ_1000MS	5
 58#define ZOPT2201_MEAS_FREQ_2000MS	6
 59
 60/* Values for ZOPT2201_LS_GAIN */
 61#define ZOPT2201_LS_GAIN_1		0
 62#define ZOPT2201_LS_GAIN_3		1
 63#define ZOPT2201_LS_GAIN_6		2
 64#define ZOPT2201_LS_GAIN_9		3
 65#define ZOPT2201_LS_GAIN_18		4
 66
 67/* Values for ZOPT2201_MAIN_STATUS */
 68#define ZOPT2201_MAIN_STATUS_POWERON	BIT(5)
 69#define ZOPT2201_MAIN_STATUS_INT	BIT(4)
 70#define ZOPT2201_MAIN_STATUS_DRDY	BIT(3)
 71
 72#define ZOPT2201_PART_NUMBER		0xb2
 73
 74struct zopt2201_data {
 75	struct i2c_client *client;
 76	struct mutex lock;
 77	u8 gain;
 78	u8 res;
 79	u8 rate;
 80};
 81
 82static const struct {
 83	unsigned int gain; /* gain factor */
 84	unsigned int scale; /* micro lux per count */
 85} zopt2201_gain_als[] = {
 86	{  1, 19200000 },
 87	{  3,  6400000 },
 88	{  6,  3200000 },
 89	{  9,  2133333 },
 90	{ 18,  1066666 },
 91};
 92
 93static const struct {
 94	unsigned int gain; /* gain factor */
 95	unsigned int scale; /* micro W/m2 per count */
 96} zopt2201_gain_uvb[] = {
 97	{  1, 460800 },
 98	{  3, 153600 },
 99	{  6,  76800 },
100	{  9,  51200 },
101	{ 18,  25600 },
102};
103
104static const struct {
105	unsigned int bits; /* sensor resolution in bits */
106	unsigned long us; /* measurement time in micro seconds */
107} zopt2201_resolution[] = {
108	{ 20, 400000 },
109	{ 19, 200000 },
110	{ 18, 100000 },
111	{ 17,  50000 },
112	{ 16,  25000 },
113	{ 13,   3125 },
114};
115
116struct zopt2201_scale {
117	unsigned int scale, uscale; /* scale factor as integer + micro */
118	u8 gain; /* gain register value */
119	u8 res; /* resolution register value */
120};
121
122static const struct zopt2201_scale zopt2201_scale_als[] = {
123	{ 19, 200000, 0, 5 },
124	{  6, 400000, 1, 5 },
125	{  3, 200000, 2, 5 },
126	{  2, 400000, 0, 4 },
127	{  2, 133333, 3, 5 },
128	{  1, 200000, 0, 3 },
129	{  1,  66666, 4, 5 },
130	{  0, 800000, 1, 4 },
131	{  0, 600000, 0, 2 },
132	{  0, 400000, 2, 4 },
133	{  0, 300000, 0, 1 },
134	{  0, 266666, 3, 4 },
135	{  0, 200000, 2, 3 },
136	{  0, 150000, 0, 0 },
137	{  0, 133333, 4, 4 },
138	{  0, 100000, 2, 2 },
139	{  0,  66666, 4, 3 },
140	{  0,  50000, 2, 1 },
141	{  0,  33333, 4, 2 },
142	{  0,  25000, 2, 0 },
143	{  0,  16666, 4, 1 },
144	{  0,   8333, 4, 0 },
145};
146
147static const struct zopt2201_scale zopt2201_scale_uvb[] = {
148	{ 0, 460800, 0, 5 },
149	{ 0, 153600, 1, 5 },
150	{ 0,  76800, 2, 5 },
151	{ 0,  57600, 0, 4 },
152	{ 0,  51200, 3, 5 },
153	{ 0,  28800, 0, 3 },
154	{ 0,  25600, 4, 5 },
155	{ 0,  19200, 1, 4 },
156	{ 0,  14400, 0, 2 },
157	{ 0,   9600, 2, 4 },
158	{ 0,   7200, 0, 1 },
159	{ 0,   6400, 3, 4 },
160	{ 0,   4800, 2, 3 },
161	{ 0,   3600, 0, 0 },
162	{ 0,   3200, 4, 4 },
163	{ 0,   2400, 2, 2 },
164	{ 0,   1600, 4, 3 },
165	{ 0,   1200, 2, 1 },
166	{ 0,    800, 4, 2 },
167	{ 0,    600, 2, 0 },
168	{ 0,    400, 4, 1 },
169	{ 0,    200, 4, 0 },
170};
171
172static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
173{
174	u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
175
176	if (uvb_mode)
177		out |= ZOPT2201_MAIN_CTRL_LS_MODE;
178
179	return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
180}
181
182static int zopt2201_read(struct zopt2201_data *data, u8 reg)
183{
184	struct i2c_client *client = data->client;
185	int tries = 10;
186	u8 buf[3];
187	int ret;
188
189	mutex_lock(&data->lock);
190	ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
191	if (ret < 0)
192		goto fail;
193
194	while (tries--) {
195		unsigned long t = zopt2201_resolution[data->res].us;
196
197		if (t <= 20000)
198			usleep_range(t, t + 1000);
199		else
200			msleep(t / 1000);
201		ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
202		if (ret < 0)
203			goto fail;
204		if (ret & ZOPT2201_MAIN_STATUS_DRDY)
205			break;
206	}
207
208	if (tries < 0) {
209		ret = -ETIMEDOUT;
210		goto fail;
211	}
212
213	ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
214	if (ret < 0)
215		goto fail;
216
217	ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
218	if (ret < 0)
219		goto fail;
220	mutex_unlock(&data->lock);
221
222	return get_unaligned_le24(&buf[0]);
223
224fail:
225	mutex_unlock(&data->lock);
226	return ret;
227}
228
229static const struct iio_chan_spec zopt2201_channels[] = {
230	{
231		.type = IIO_LIGHT,
232		.address = ZOPT2201_ALS_DATA,
233		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
234				      BIT(IIO_CHAN_INFO_SCALE),
235		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
236	},
237	{
238		.type = IIO_INTENSITY,
239		.modified = 1,
240		.channel2 = IIO_MOD_LIGHT_UV,
241		.address = ZOPT2201_UVB_DATA,
242		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
243				      BIT(IIO_CHAN_INFO_SCALE),
244		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
245	},
246	{
247		.type = IIO_UVINDEX,
248		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
249	},
250};
251
252static int zopt2201_read_raw(struct iio_dev *indio_dev,
253				struct iio_chan_spec const *chan,
254				int *val, int *val2, long mask)
255{
256	struct zopt2201_data *data = iio_priv(indio_dev);
257	u64 tmp;
258	int ret;
259
260	switch (mask) {
261	case IIO_CHAN_INFO_RAW:
262		ret = zopt2201_read(data, chan->address);
263		if (ret < 0)
264			return ret;
265		*val = ret;
266		return IIO_VAL_INT;
267	case IIO_CHAN_INFO_PROCESSED:
268		ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
269		if (ret < 0)
270			return ret;
271		*val = ret * 18 *
272			(1 << (20 - zopt2201_resolution[data->res].bits)) /
273			zopt2201_gain_uvb[data->gain].gain;
274		return IIO_VAL_INT;
275	case IIO_CHAN_INFO_SCALE:
276		switch (chan->address) {
277		case ZOPT2201_ALS_DATA:
278			*val = zopt2201_gain_als[data->gain].scale;
279			break;
280		case ZOPT2201_UVB_DATA:
281			*val = zopt2201_gain_uvb[data->gain].scale;
282			break;
283		default:
284			return -EINVAL;
285		}
286
287		*val2 = 1000000;
288		*val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
289		tmp = div_s64(*val * 1000000ULL, *val2);
290		*val = div_s64_rem(tmp, 1000000, val2);
291
292		return IIO_VAL_INT_PLUS_MICRO;
293	case IIO_CHAN_INFO_INT_TIME:
294		*val = 0;
295		*val2 = zopt2201_resolution[data->res].us;
296		return IIO_VAL_INT_PLUS_MICRO;
297	default:
298		return -EINVAL;
299	}
300}
301
302static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
303{
304	int ret;
305
306	ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
307					(res << ZOPT2201_MEAS_RES_SHIFT) |
308					data->rate);
309	if (ret < 0)
310		return ret;
311
312	data->res = res;
313
314	return 0;
315}
316
317static int zopt2201_write_resolution(struct zopt2201_data *data,
318				     int val, int val2)
319{
320	int i, ret;
321
322	if (val != 0)
323		return -EINVAL;
324
325	for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
326		if (val2 == zopt2201_resolution[i].us) {
327			mutex_lock(&data->lock);
328			ret = zopt2201_set_resolution(data, i);
329			mutex_unlock(&data->lock);
330			return ret;
331		}
332
333	return -EINVAL;
334}
335
336static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
337{
338	int ret;
339
340	ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
341	if (ret < 0)
342		return ret;
343
344	data->gain = gain;
345
346	return 0;
347}
348
349static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx,
350				       const struct zopt2201_scale *zopt2201_scale_array)
351{
352	int ret;
353
354	mutex_lock(&data->lock);
355	ret = zopt2201_set_resolution(data, zopt2201_scale_array[idx].res);
356	if (ret < 0)
357		goto unlock;
358
359	ret = zopt2201_set_gain(data, zopt2201_scale_array[idx].gain);
360
361unlock:
362	mutex_unlock(&data->lock);
363	return ret;
364}
365
366static int zopt2201_write_scale_als(struct zopt2201_data *data,
367				     int val, int val2)
368{
369	int i;
370
371	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
372		if (val == zopt2201_scale_als[i].scale &&
373		    val2 == zopt2201_scale_als[i].uscale)
374			return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_als);
375
376	return -EINVAL;
377}
378
379static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
380				     int val, int val2)
381{
382	int i;
383
384	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
385		if (val == zopt2201_scale_uvb[i].scale &&
386		    val2 == zopt2201_scale_uvb[i].uscale)
387			return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_uvb);
388
389	return -EINVAL;
390}
391
392static int zopt2201_write_raw(struct iio_dev *indio_dev,
393			      struct iio_chan_spec const *chan,
394			      int val, int val2, long mask)
395{
396	struct zopt2201_data *data = iio_priv(indio_dev);
397
398	switch (mask) {
399	case IIO_CHAN_INFO_INT_TIME:
400		return zopt2201_write_resolution(data, val, val2);
401	case IIO_CHAN_INFO_SCALE:
402		switch (chan->address) {
403		case ZOPT2201_ALS_DATA:
404			return zopt2201_write_scale_als(data, val, val2);
405		case ZOPT2201_UVB_DATA:
406			return zopt2201_write_scale_uvb(data, val, val2);
407		default:
408			return -EINVAL;
409		}
410	}
411
412	return -EINVAL;
413}
414
415static ssize_t zopt2201_show_int_time_available(struct device *dev,
416						struct device_attribute *attr,
417						char *buf)
418{
419	size_t len = 0;
420	int i;
421
422	for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
423		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
424				 zopt2201_resolution[i].us);
425	buf[len - 1] = '\n';
426
427	return len;
428}
429
430static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
431
432static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
433					     struct device_attribute *attr,
434					     char *buf)
435{
436	ssize_t len = 0;
437	int i;
438
439	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
440		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
441				 zopt2201_scale_als[i].scale,
442				 zopt2201_scale_als[i].uscale);
443	buf[len - 1] = '\n';
444
445	return len;
446}
447
448static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
449					     struct device_attribute *attr,
450					     char *buf)
451{
452	ssize_t len = 0;
453	int i;
454
455	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
456		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
457				 zopt2201_scale_uvb[i].scale,
458				 zopt2201_scale_uvb[i].uscale);
459	buf[len - 1] = '\n';
460
461	return len;
462}
463
464static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
465		       zopt2201_show_als_scale_avail, NULL, 0);
466static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
467		       zopt2201_show_uvb_scale_avail, NULL, 0);
468
469static struct attribute *zopt2201_attributes[] = {
470	&iio_dev_attr_integration_time_available.dev_attr.attr,
471	&iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
472	&iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
473	NULL
474};
475
476static const struct attribute_group zopt2201_attribute_group = {
477	.attrs = zopt2201_attributes,
478};
479
480static const struct iio_info zopt2201_info = {
481	.read_raw = zopt2201_read_raw,
482	.write_raw = zopt2201_write_raw,
483	.attrs = &zopt2201_attribute_group,
484};
485
486static int zopt2201_probe(struct i2c_client *client)
487{
488	struct zopt2201_data *data;
489	struct iio_dev *indio_dev;
490	int ret;
491
492	if (!i2c_check_functionality(client->adapter,
493				     I2C_FUNC_SMBUS_READ_I2C_BLOCK))
494		return -EOPNOTSUPP;
495
496	ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
497	if (ret < 0)
498		return ret;
499	if (ret != ZOPT2201_PART_NUMBER)
500		return -ENODEV;
501
502	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
503	if (!indio_dev)
504		return -ENOMEM;
505
506	data = iio_priv(indio_dev);
507	i2c_set_clientdata(client, indio_dev);
508	data->client = client;
509	mutex_init(&data->lock);
510
511	indio_dev->info = &zopt2201_info;
512	indio_dev->channels = zopt2201_channels;
513	indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
514	indio_dev->name = ZOPT2201_DRV_NAME;
515	indio_dev->modes = INDIO_DIRECT_MODE;
516
517	data->rate = ZOPT2201_MEAS_FREQ_100MS;
518	ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
519	if (ret < 0)
520		return ret;
521
522	ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
523	if (ret < 0)
524		return ret;
525
526	return devm_iio_device_register(&client->dev, indio_dev);
527}
528
529static const struct i2c_device_id zopt2201_id[] = {
530	{ "zopt2201" },
531	{ }
532};
533MODULE_DEVICE_TABLE(i2c, zopt2201_id);
534
535static struct i2c_driver zopt2201_driver = {
536	.driver = {
537		.name   = ZOPT2201_DRV_NAME,
538	},
539	.probe = zopt2201_probe,
540	.id_table = zopt2201_id,
541};
542
543module_i2c_driver(zopt2201_driver);
544
545MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
546MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
547MODULE_LICENSE("GPL");
~/ Read-only file preview.