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