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