1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x60
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2018, DENX Software Engineering GmbH
6*4882a593Smuzhiyun * Author: Parthiban Nallathambi <pn@denx.de>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * TODO: Proximity
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun #include <linux/bitops.h>
11*4882a593Smuzhiyun #include <linux/i2c.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/pm_runtime.h>
14*4882a593Smuzhiyun #include <linux/regmap.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/iio/buffer.h>
17*4882a593Smuzhiyun #include <linux/iio/events.h>
18*4882a593Smuzhiyun #include <linux/iio/iio.h>
19*4882a593Smuzhiyun #include <linux/iio/sysfs.h>
20*4882a593Smuzhiyun #include <linux/iio/trigger.h>
21*4882a593Smuzhiyun #include <linux/iio/trigger_consumer.h>
22*4882a593Smuzhiyun #include <linux/iio/triggered_buffer.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define VCNL4035_DRV_NAME "vcnl4035"
25*4882a593Smuzhiyun #define VCNL4035_IRQ_NAME "vcnl4035_event"
26*4882a593Smuzhiyun #define VCNL4035_REGMAP_NAME "vcnl4035_regmap"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* Device registers */
29*4882a593Smuzhiyun #define VCNL4035_ALS_CONF 0x00
30*4882a593Smuzhiyun #define VCNL4035_ALS_THDH 0x01
31*4882a593Smuzhiyun #define VCNL4035_ALS_THDL 0x02
32*4882a593Smuzhiyun #define VCNL4035_ALS_DATA 0x0B
33*4882a593Smuzhiyun #define VCNL4035_WHITE_DATA 0x0C
34*4882a593Smuzhiyun #define VCNL4035_INT_FLAG 0x0D
35*4882a593Smuzhiyun #define VCNL4035_DEV_ID 0x0E
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* Register masks */
38*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_MASK BIT(0)
39*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8)
40*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_INT_MASK BIT(1)
41*4882a593Smuzhiyun #define VCNL4035_ALS_IT_MASK GENMASK(7, 5)
42*4882a593Smuzhiyun #define VCNL4035_ALS_PERS_MASK GENMASK(3, 2)
43*4882a593Smuzhiyun #define VCNL4035_INT_ALS_IF_H_MASK BIT(12)
44*4882a593Smuzhiyun #define VCNL4035_INT_ALS_IF_L_MASK BIT(13)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* Default values */
47*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_ENABLE BIT(0)
48*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_DISABLE 0x00
49*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_INT_ENABLE BIT(1)
50*4882a593Smuzhiyun #define VCNL4035_MODE_ALS_INT_DISABLE 0
51*4882a593Smuzhiyun #define VCNL4035_DEV_ID_VAL 0x80
52*4882a593Smuzhiyun #define VCNL4035_ALS_IT_DEFAULT 0x01
53*4882a593Smuzhiyun #define VCNL4035_ALS_PERS_DEFAULT 0x00
54*4882a593Smuzhiyun #define VCNL4035_ALS_THDH_DEFAULT 5000
55*4882a593Smuzhiyun #define VCNL4035_ALS_THDL_DEFAULT 100
56*4882a593Smuzhiyun #define VCNL4035_SLEEP_DELAY_MS 2000
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun struct vcnl4035_data {
59*4882a593Smuzhiyun struct i2c_client *client;
60*4882a593Smuzhiyun struct regmap *regmap;
61*4882a593Smuzhiyun unsigned int als_it_val;
62*4882a593Smuzhiyun unsigned int als_persistence;
63*4882a593Smuzhiyun unsigned int als_thresh_low;
64*4882a593Smuzhiyun unsigned int als_thresh_high;
65*4882a593Smuzhiyun struct iio_trigger *drdy_trigger0;
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun
vcnl4035_is_triggered(struct vcnl4035_data * data)68*4882a593Smuzhiyun static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun int ret;
71*4882a593Smuzhiyun int reg;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, ®);
74*4882a593Smuzhiyun if (ret < 0)
75*4882a593Smuzhiyun return false;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun return !!(reg &
78*4882a593Smuzhiyun (VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK));
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
vcnl4035_drdy_irq_thread(int irq,void * private)81*4882a593Smuzhiyun static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct iio_dev *indio_dev = private;
84*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun if (vcnl4035_is_triggered(data)) {
87*4882a593Smuzhiyun iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
88*4882a593Smuzhiyun 0,
89*4882a593Smuzhiyun IIO_EV_TYPE_THRESH,
90*4882a593Smuzhiyun IIO_EV_DIR_EITHER),
91*4882a593Smuzhiyun iio_get_time_ns(indio_dev));
92*4882a593Smuzhiyun iio_trigger_poll_chained(data->drdy_trigger0);
93*4882a593Smuzhiyun return IRQ_HANDLED;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return IRQ_NONE;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun /* Triggered buffer */
vcnl4035_trigger_consumer_handler(int irq,void * p)100*4882a593Smuzhiyun static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct iio_poll_func *pf = p;
103*4882a593Smuzhiyun struct iio_dev *indio_dev = pf->indio_dev;
104*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
105*4882a593Smuzhiyun /* Ensure naturally aligned timestamp */
106*4882a593Smuzhiyun u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8);
107*4882a593Smuzhiyun int ret;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);
110*4882a593Smuzhiyun if (ret < 0) {
111*4882a593Smuzhiyun dev_err(&data->client->dev,
112*4882a593Smuzhiyun "Trigger consumer can't read from sensor.\n");
113*4882a593Smuzhiyun goto fail_read;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun iio_push_to_buffers_with_timestamp(indio_dev, buffer,
116*4882a593Smuzhiyun iio_get_time_ns(indio_dev));
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun fail_read:
119*4882a593Smuzhiyun iio_trigger_notify_done(indio_dev->trig);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return IRQ_HANDLED;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
vcnl4035_als_drdy_set_state(struct iio_trigger * trigger,bool enable_drdy)124*4882a593Smuzhiyun static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger,
125*4882a593Smuzhiyun bool enable_drdy)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
128*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
129*4882a593Smuzhiyun int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE :
130*4882a593Smuzhiyun VCNL4035_MODE_ALS_INT_DISABLE;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
133*4882a593Smuzhiyun VCNL4035_MODE_ALS_INT_MASK,
134*4882a593Smuzhiyun val);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun static const struct iio_trigger_ops vcnl4035_trigger_ops = {
138*4882a593Smuzhiyun .validate_device = iio_trigger_validate_own_device,
139*4882a593Smuzhiyun .set_trigger_state = vcnl4035_als_drdy_set_state,
140*4882a593Smuzhiyun };
141*4882a593Smuzhiyun
vcnl4035_set_pm_runtime_state(struct vcnl4035_data * data,bool on)142*4882a593Smuzhiyun static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun int ret;
145*4882a593Smuzhiyun struct device *dev = &data->client->dev;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (on) {
148*4882a593Smuzhiyun ret = pm_runtime_get_sync(dev);
149*4882a593Smuzhiyun if (ret < 0)
150*4882a593Smuzhiyun pm_runtime_put_noidle(dev);
151*4882a593Smuzhiyun } else {
152*4882a593Smuzhiyun pm_runtime_mark_last_busy(dev);
153*4882a593Smuzhiyun ret = pm_runtime_put_autosuspend(dev);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return ret;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun /*
160*4882a593Smuzhiyun * Device IT INT Time (ms) Scale (lux/step)
161*4882a593Smuzhiyun * 000 50 0.064
162*4882a593Smuzhiyun * 001 100 0.032
163*4882a593Smuzhiyun * 010 200 0.016
164*4882a593Smuzhiyun * 100 400 0.008
165*4882a593Smuzhiyun * 101 - 111 800 0.004
166*4882a593Smuzhiyun * Values are proportional, so ALS INT is selected for input due to
167*4882a593Smuzhiyun * simplicity reason. Integration time value and scaling is
168*4882a593Smuzhiyun * calculated based on device INT value
169*4882a593Smuzhiyun *
170*4882a593Smuzhiyun * Raw value needs to be scaled using ALS steps
171*4882a593Smuzhiyun */
vcnl4035_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)172*4882a593Smuzhiyun static int vcnl4035_read_raw(struct iio_dev *indio_dev,
173*4882a593Smuzhiyun struct iio_chan_spec const *chan, int *val,
174*4882a593Smuzhiyun int *val2, long mask)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
177*4882a593Smuzhiyun int ret;
178*4882a593Smuzhiyun int raw_data;
179*4882a593Smuzhiyun unsigned int reg;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun switch (mask) {
182*4882a593Smuzhiyun case IIO_CHAN_INFO_RAW:
183*4882a593Smuzhiyun ret = vcnl4035_set_pm_runtime_state(data, true);
184*4882a593Smuzhiyun if (ret < 0)
185*4882a593Smuzhiyun return ret;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun ret = iio_device_claim_direct_mode(indio_dev);
188*4882a593Smuzhiyun if (!ret) {
189*4882a593Smuzhiyun if (chan->channel)
190*4882a593Smuzhiyun reg = VCNL4035_ALS_DATA;
191*4882a593Smuzhiyun else
192*4882a593Smuzhiyun reg = VCNL4035_WHITE_DATA;
193*4882a593Smuzhiyun ret = regmap_read(data->regmap, reg, &raw_data);
194*4882a593Smuzhiyun iio_device_release_direct_mode(indio_dev);
195*4882a593Smuzhiyun if (!ret) {
196*4882a593Smuzhiyun *val = raw_data;
197*4882a593Smuzhiyun ret = IIO_VAL_INT;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun vcnl4035_set_pm_runtime_state(data, false);
201*4882a593Smuzhiyun return ret;
202*4882a593Smuzhiyun case IIO_CHAN_INFO_INT_TIME:
203*4882a593Smuzhiyun *val = 50;
204*4882a593Smuzhiyun if (data->als_it_val)
205*4882a593Smuzhiyun *val = data->als_it_val * 100;
206*4882a593Smuzhiyun return IIO_VAL_INT;
207*4882a593Smuzhiyun case IIO_CHAN_INFO_SCALE:
208*4882a593Smuzhiyun *val = 64;
209*4882a593Smuzhiyun if (!data->als_it_val)
210*4882a593Smuzhiyun *val2 = 1000;
211*4882a593Smuzhiyun else
212*4882a593Smuzhiyun *val2 = data->als_it_val * 2 * 1000;
213*4882a593Smuzhiyun return IIO_VAL_FRACTIONAL;
214*4882a593Smuzhiyun default:
215*4882a593Smuzhiyun return -EINVAL;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
vcnl4035_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)219*4882a593Smuzhiyun static int vcnl4035_write_raw(struct iio_dev *indio_dev,
220*4882a593Smuzhiyun struct iio_chan_spec const *chan,
221*4882a593Smuzhiyun int val, int val2, long mask)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun int ret;
224*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun switch (mask) {
227*4882a593Smuzhiyun case IIO_CHAN_INFO_INT_TIME:
228*4882a593Smuzhiyun if (val <= 0 || val > 800)
229*4882a593Smuzhiyun return -EINVAL;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun ret = vcnl4035_set_pm_runtime_state(data, true);
232*4882a593Smuzhiyun if (ret < 0)
233*4882a593Smuzhiyun return ret;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
236*4882a593Smuzhiyun VCNL4035_ALS_IT_MASK,
237*4882a593Smuzhiyun val / 100);
238*4882a593Smuzhiyun if (!ret)
239*4882a593Smuzhiyun data->als_it_val = val / 100;
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun vcnl4035_set_pm_runtime_state(data, false);
242*4882a593Smuzhiyun return ret;
243*4882a593Smuzhiyun default:
244*4882a593Smuzhiyun return -EINVAL;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun /* No direct ABI for persistence and threshold, so eventing */
vcnl4035_read_thresh(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)249*4882a593Smuzhiyun static int vcnl4035_read_thresh(struct iio_dev *indio_dev,
250*4882a593Smuzhiyun const struct iio_chan_spec *chan, enum iio_event_type type,
251*4882a593Smuzhiyun enum iio_event_direction dir, enum iio_event_info info,
252*4882a593Smuzhiyun int *val, int *val2)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun switch (info) {
257*4882a593Smuzhiyun case IIO_EV_INFO_VALUE:
258*4882a593Smuzhiyun switch (dir) {
259*4882a593Smuzhiyun case IIO_EV_DIR_RISING:
260*4882a593Smuzhiyun *val = data->als_thresh_high;
261*4882a593Smuzhiyun return IIO_VAL_INT;
262*4882a593Smuzhiyun case IIO_EV_DIR_FALLING:
263*4882a593Smuzhiyun *val = data->als_thresh_low;
264*4882a593Smuzhiyun return IIO_VAL_INT;
265*4882a593Smuzhiyun default:
266*4882a593Smuzhiyun return -EINVAL;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun break;
269*4882a593Smuzhiyun case IIO_EV_INFO_PERIOD:
270*4882a593Smuzhiyun *val = data->als_persistence;
271*4882a593Smuzhiyun return IIO_VAL_INT;
272*4882a593Smuzhiyun default:
273*4882a593Smuzhiyun return -EINVAL;
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
vcnl4035_write_thresh(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)278*4882a593Smuzhiyun static int vcnl4035_write_thresh(struct iio_dev *indio_dev,
279*4882a593Smuzhiyun const struct iio_chan_spec *chan, enum iio_event_type type,
280*4882a593Smuzhiyun enum iio_event_direction dir, enum iio_event_info info, int val,
281*4882a593Smuzhiyun int val2)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
284*4882a593Smuzhiyun int ret;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun switch (info) {
287*4882a593Smuzhiyun case IIO_EV_INFO_VALUE:
288*4882a593Smuzhiyun /* 16 bit threshold range 0 - 65535 */
289*4882a593Smuzhiyun if (val < 0 || val > 65535)
290*4882a593Smuzhiyun return -EINVAL;
291*4882a593Smuzhiyun if (dir == IIO_EV_DIR_RISING) {
292*4882a593Smuzhiyun if (val < data->als_thresh_low)
293*4882a593Smuzhiyun return -EINVAL;
294*4882a593Smuzhiyun ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
295*4882a593Smuzhiyun val);
296*4882a593Smuzhiyun if (ret)
297*4882a593Smuzhiyun return ret;
298*4882a593Smuzhiyun data->als_thresh_high = val;
299*4882a593Smuzhiyun } else {
300*4882a593Smuzhiyun if (val > data->als_thresh_high)
301*4882a593Smuzhiyun return -EINVAL;
302*4882a593Smuzhiyun ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
303*4882a593Smuzhiyun val);
304*4882a593Smuzhiyun if (ret)
305*4882a593Smuzhiyun return ret;
306*4882a593Smuzhiyun data->als_thresh_low = val;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun return ret;
309*4882a593Smuzhiyun case IIO_EV_INFO_PERIOD:
310*4882a593Smuzhiyun /* allow only 1 2 4 8 as persistence value */
311*4882a593Smuzhiyun if (val < 0 || val > 8 || hweight8(val) != 1)
312*4882a593Smuzhiyun return -EINVAL;
313*4882a593Smuzhiyun ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
314*4882a593Smuzhiyun VCNL4035_ALS_PERS_MASK, val);
315*4882a593Smuzhiyun if (!ret)
316*4882a593Smuzhiyun data->als_persistence = val;
317*4882a593Smuzhiyun return ret;
318*4882a593Smuzhiyun default:
319*4882a593Smuzhiyun return -EINVAL;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800");
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun static struct attribute *vcnl4035_attributes[] = {
326*4882a593Smuzhiyun &iio_const_attr_integration_time_available.dev_attr.attr,
327*4882a593Smuzhiyun NULL,
328*4882a593Smuzhiyun };
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun static const struct attribute_group vcnl4035_attribute_group = {
331*4882a593Smuzhiyun .attrs = vcnl4035_attributes,
332*4882a593Smuzhiyun };
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun static const struct iio_info vcnl4035_info = {
335*4882a593Smuzhiyun .read_raw = vcnl4035_read_raw,
336*4882a593Smuzhiyun .write_raw = vcnl4035_write_raw,
337*4882a593Smuzhiyun .read_event_value = vcnl4035_read_thresh,
338*4882a593Smuzhiyun .write_event_value = vcnl4035_write_thresh,
339*4882a593Smuzhiyun .attrs = &vcnl4035_attribute_group,
340*4882a593Smuzhiyun };
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun static const struct iio_event_spec vcnl4035_event_spec[] = {
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun .type = IIO_EV_TYPE_THRESH,
345*4882a593Smuzhiyun .dir = IIO_EV_DIR_RISING,
346*4882a593Smuzhiyun .mask_separate = BIT(IIO_EV_INFO_VALUE),
347*4882a593Smuzhiyun }, {
348*4882a593Smuzhiyun .type = IIO_EV_TYPE_THRESH,
349*4882a593Smuzhiyun .dir = IIO_EV_DIR_FALLING,
350*4882a593Smuzhiyun .mask_separate = BIT(IIO_EV_INFO_VALUE),
351*4882a593Smuzhiyun }, {
352*4882a593Smuzhiyun .type = IIO_EV_TYPE_THRESH,
353*4882a593Smuzhiyun .dir = IIO_EV_DIR_EITHER,
354*4882a593Smuzhiyun .mask_separate = BIT(IIO_EV_INFO_PERIOD),
355*4882a593Smuzhiyun },
356*4882a593Smuzhiyun };
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun enum vcnl4035_scan_index_order {
359*4882a593Smuzhiyun VCNL4035_CHAN_INDEX_LIGHT,
360*4882a593Smuzhiyun VCNL4035_CHAN_INDEX_WHITE_LED,
361*4882a593Smuzhiyun };
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
364*4882a593Smuzhiyun .validate_scan_mask = &iio_validate_scan_mask_onehot,
365*4882a593Smuzhiyun };
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun static const struct iio_chan_spec vcnl4035_channels[] = {
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun .type = IIO_LIGHT,
370*4882a593Smuzhiyun .channel = 0,
371*4882a593Smuzhiyun .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
372*4882a593Smuzhiyun BIT(IIO_CHAN_INFO_INT_TIME) |
373*4882a593Smuzhiyun BIT(IIO_CHAN_INFO_SCALE),
374*4882a593Smuzhiyun .event_spec = vcnl4035_event_spec,
375*4882a593Smuzhiyun .num_event_specs = ARRAY_SIZE(vcnl4035_event_spec),
376*4882a593Smuzhiyun .scan_index = VCNL4035_CHAN_INDEX_LIGHT,
377*4882a593Smuzhiyun .scan_type = {
378*4882a593Smuzhiyun .sign = 'u',
379*4882a593Smuzhiyun .realbits = 16,
380*4882a593Smuzhiyun .storagebits = 16,
381*4882a593Smuzhiyun .endianness = IIO_LE,
382*4882a593Smuzhiyun },
383*4882a593Smuzhiyun },
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun .type = IIO_INTENSITY,
386*4882a593Smuzhiyun .channel = 1,
387*4882a593Smuzhiyun .modified = 1,
388*4882a593Smuzhiyun .channel2 = IIO_MOD_LIGHT_BOTH,
389*4882a593Smuzhiyun .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
390*4882a593Smuzhiyun .scan_index = VCNL4035_CHAN_INDEX_WHITE_LED,
391*4882a593Smuzhiyun .scan_type = {
392*4882a593Smuzhiyun .sign = 'u',
393*4882a593Smuzhiyun .realbits = 16,
394*4882a593Smuzhiyun .storagebits = 16,
395*4882a593Smuzhiyun .endianness = IIO_LE,
396*4882a593Smuzhiyun },
397*4882a593Smuzhiyun },
398*4882a593Smuzhiyun };
399*4882a593Smuzhiyun
vcnl4035_set_als_power_state(struct vcnl4035_data * data,u8 status)400*4882a593Smuzhiyun static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
403*4882a593Smuzhiyun VCNL4035_MODE_ALS_MASK,
404*4882a593Smuzhiyun status);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
vcnl4035_init(struct vcnl4035_data * data)407*4882a593Smuzhiyun static int vcnl4035_init(struct vcnl4035_data *data)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun int ret;
410*4882a593Smuzhiyun int id;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id);
413*4882a593Smuzhiyun if (ret < 0) {
414*4882a593Smuzhiyun dev_err(&data->client->dev, "Failed to read DEV_ID register\n");
415*4882a593Smuzhiyun return ret;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun if (id != VCNL4035_DEV_ID_VAL) {
419*4882a593Smuzhiyun dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",
420*4882a593Smuzhiyun id, VCNL4035_DEV_ID_VAL);
421*4882a593Smuzhiyun return -ENODEV;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
425*4882a593Smuzhiyun if (ret < 0)
426*4882a593Smuzhiyun return ret;
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun /* ALS white channel enable */
429*4882a593Smuzhiyun ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
430*4882a593Smuzhiyun VCNL4035_MODE_ALS_WHITE_CHAN,
431*4882a593Smuzhiyun 1);
432*4882a593Smuzhiyun if (ret) {
433*4882a593Smuzhiyun dev_err(&data->client->dev, "set white channel enable %d\n",
434*4882a593Smuzhiyun ret);
435*4882a593Smuzhiyun return ret;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /* set default integration time - 100 ms for ALS */
439*4882a593Smuzhiyun ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
440*4882a593Smuzhiyun VCNL4035_ALS_IT_MASK,
441*4882a593Smuzhiyun VCNL4035_ALS_IT_DEFAULT);
442*4882a593Smuzhiyun if (ret) {
443*4882a593Smuzhiyun dev_err(&data->client->dev, "set default ALS IT returned %d\n",
444*4882a593Smuzhiyun ret);
445*4882a593Smuzhiyun return ret;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun data->als_it_val = VCNL4035_ALS_IT_DEFAULT;
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun /* set default persistence time - 1 for ALS */
450*4882a593Smuzhiyun ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
451*4882a593Smuzhiyun VCNL4035_ALS_PERS_MASK,
452*4882a593Smuzhiyun VCNL4035_ALS_PERS_DEFAULT);
453*4882a593Smuzhiyun if (ret) {
454*4882a593Smuzhiyun dev_err(&data->client->dev, "set default PERS returned %d\n",
455*4882a593Smuzhiyun ret);
456*4882a593Smuzhiyun return ret;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun data->als_persistence = VCNL4035_ALS_PERS_DEFAULT;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun /* set default HIGH threshold for ALS */
461*4882a593Smuzhiyun ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
462*4882a593Smuzhiyun VCNL4035_ALS_THDH_DEFAULT);
463*4882a593Smuzhiyun if (ret) {
464*4882a593Smuzhiyun dev_err(&data->client->dev, "set default THDH returned %d\n",
465*4882a593Smuzhiyun ret);
466*4882a593Smuzhiyun return ret;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun /* set default LOW threshold for ALS */
471*4882a593Smuzhiyun ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
472*4882a593Smuzhiyun VCNL4035_ALS_THDL_DEFAULT);
473*4882a593Smuzhiyun if (ret) {
474*4882a593Smuzhiyun dev_err(&data->client->dev, "set default THDL returned %d\n",
475*4882a593Smuzhiyun ret);
476*4882a593Smuzhiyun return ret;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT;
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun return 0;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun
vcnl4035_is_volatile_reg(struct device * dev,unsigned int reg)483*4882a593Smuzhiyun static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg)
484*4882a593Smuzhiyun {
485*4882a593Smuzhiyun switch (reg) {
486*4882a593Smuzhiyun case VCNL4035_ALS_CONF:
487*4882a593Smuzhiyun case VCNL4035_DEV_ID:
488*4882a593Smuzhiyun return false;
489*4882a593Smuzhiyun default:
490*4882a593Smuzhiyun return true;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun static const struct regmap_config vcnl4035_regmap_config = {
495*4882a593Smuzhiyun .name = VCNL4035_REGMAP_NAME,
496*4882a593Smuzhiyun .reg_bits = 8,
497*4882a593Smuzhiyun .val_bits = 16,
498*4882a593Smuzhiyun .max_register = VCNL4035_DEV_ID,
499*4882a593Smuzhiyun .cache_type = REGCACHE_RBTREE,
500*4882a593Smuzhiyun .volatile_reg = vcnl4035_is_volatile_reg,
501*4882a593Smuzhiyun .val_format_endian = REGMAP_ENDIAN_LITTLE,
502*4882a593Smuzhiyun };
503*4882a593Smuzhiyun
vcnl4035_probe_trigger(struct iio_dev * indio_dev)504*4882a593Smuzhiyun static int vcnl4035_probe_trigger(struct iio_dev *indio_dev)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun int ret;
507*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun data->drdy_trigger0 = devm_iio_trigger_alloc(
510*4882a593Smuzhiyun indio_dev->dev.parent,
511*4882a593Smuzhiyun "%s-dev%d", indio_dev->name, indio_dev->id);
512*4882a593Smuzhiyun if (!data->drdy_trigger0)
513*4882a593Smuzhiyun return -ENOMEM;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
516*4882a593Smuzhiyun data->drdy_trigger0->ops = &vcnl4035_trigger_ops;
517*4882a593Smuzhiyun iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
518*4882a593Smuzhiyun ret = devm_iio_trigger_register(indio_dev->dev.parent,
519*4882a593Smuzhiyun data->drdy_trigger0);
520*4882a593Smuzhiyun if (ret) {
521*4882a593Smuzhiyun dev_err(&data->client->dev, "iio trigger register failed\n");
522*4882a593Smuzhiyun return ret;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun /* Trigger setup */
526*4882a593Smuzhiyun ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
527*4882a593Smuzhiyun NULL, vcnl4035_trigger_consumer_handler,
528*4882a593Smuzhiyun &iio_triggered_buffer_setup_ops);
529*4882a593Smuzhiyun if (ret < 0) {
530*4882a593Smuzhiyun dev_err(&data->client->dev, "iio triggered buffer setup failed\n");
531*4882a593Smuzhiyun return ret;
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun /* IRQ to trigger mapping */
535*4882a593Smuzhiyun ret = devm_request_threaded_irq(&data->client->dev, data->client->irq,
536*4882a593Smuzhiyun NULL, vcnl4035_drdy_irq_thread,
537*4882a593Smuzhiyun IRQF_TRIGGER_LOW | IRQF_ONESHOT,
538*4882a593Smuzhiyun VCNL4035_IRQ_NAME, indio_dev);
539*4882a593Smuzhiyun if (ret < 0)
540*4882a593Smuzhiyun dev_err(&data->client->dev, "request irq %d for trigger0 failed\n",
541*4882a593Smuzhiyun data->client->irq);
542*4882a593Smuzhiyun return ret;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun
vcnl4035_probe(struct i2c_client * client,const struct i2c_device_id * id)545*4882a593Smuzhiyun static int vcnl4035_probe(struct i2c_client *client,
546*4882a593Smuzhiyun const struct i2c_device_id *id)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun struct vcnl4035_data *data;
549*4882a593Smuzhiyun struct iio_dev *indio_dev;
550*4882a593Smuzhiyun struct regmap *regmap;
551*4882a593Smuzhiyun int ret;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
554*4882a593Smuzhiyun if (!indio_dev)
555*4882a593Smuzhiyun return -ENOMEM;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config);
558*4882a593Smuzhiyun if (IS_ERR(regmap)) {
559*4882a593Smuzhiyun dev_err(&client->dev, "regmap_init failed!\n");
560*4882a593Smuzhiyun return PTR_ERR(regmap);
561*4882a593Smuzhiyun }
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun data = iio_priv(indio_dev);
564*4882a593Smuzhiyun i2c_set_clientdata(client, indio_dev);
565*4882a593Smuzhiyun data->client = client;
566*4882a593Smuzhiyun data->regmap = regmap;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun indio_dev->info = &vcnl4035_info;
569*4882a593Smuzhiyun indio_dev->name = VCNL4035_DRV_NAME;
570*4882a593Smuzhiyun indio_dev->channels = vcnl4035_channels;
571*4882a593Smuzhiyun indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels);
572*4882a593Smuzhiyun indio_dev->modes = INDIO_DIRECT_MODE;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun ret = vcnl4035_init(data);
575*4882a593Smuzhiyun if (ret < 0) {
576*4882a593Smuzhiyun dev_err(&client->dev, "vcnl4035 chip init failed\n");
577*4882a593Smuzhiyun return ret;
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun if (client->irq > 0) {
581*4882a593Smuzhiyun ret = vcnl4035_probe_trigger(indio_dev);
582*4882a593Smuzhiyun if (ret < 0) {
583*4882a593Smuzhiyun dev_err(&client->dev, "vcnl4035 unable init trigger\n");
584*4882a593Smuzhiyun goto fail_poweroff;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun ret = pm_runtime_set_active(&client->dev);
589*4882a593Smuzhiyun if (ret < 0)
590*4882a593Smuzhiyun goto fail_poweroff;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun ret = iio_device_register(indio_dev);
593*4882a593Smuzhiyun if (ret < 0)
594*4882a593Smuzhiyun goto fail_poweroff;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun pm_runtime_enable(&client->dev);
597*4882a593Smuzhiyun pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS);
598*4882a593Smuzhiyun pm_runtime_use_autosuspend(&client->dev);
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun return 0;
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun fail_poweroff:
603*4882a593Smuzhiyun vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
604*4882a593Smuzhiyun return ret;
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun
vcnl4035_remove(struct i2c_client * client)607*4882a593Smuzhiyun static int vcnl4035_remove(struct i2c_client *client)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun struct iio_dev *indio_dev = i2c_get_clientdata(client);
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun pm_runtime_dont_use_autosuspend(&client->dev);
612*4882a593Smuzhiyun pm_runtime_disable(&client->dev);
613*4882a593Smuzhiyun iio_device_unregister(indio_dev);
614*4882a593Smuzhiyun pm_runtime_set_suspended(&client->dev);
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun return vcnl4035_set_als_power_state(iio_priv(indio_dev),
617*4882a593Smuzhiyun VCNL4035_MODE_ALS_DISABLE);
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun
vcnl4035_runtime_suspend(struct device * dev)620*4882a593Smuzhiyun static int __maybe_unused vcnl4035_runtime_suspend(struct device *dev)
621*4882a593Smuzhiyun {
622*4882a593Smuzhiyun struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
623*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
624*4882a593Smuzhiyun int ret;
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
627*4882a593Smuzhiyun regcache_mark_dirty(data->regmap);
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun return ret;
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun
vcnl4035_runtime_resume(struct device * dev)632*4882a593Smuzhiyun static int __maybe_unused vcnl4035_runtime_resume(struct device *dev)
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
635*4882a593Smuzhiyun struct vcnl4035_data *data = iio_priv(indio_dev);
636*4882a593Smuzhiyun int ret;
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun regcache_sync(data->regmap);
639*4882a593Smuzhiyun ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
640*4882a593Smuzhiyun if (ret < 0)
641*4882a593Smuzhiyun return ret;
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun /* wait for 1 ALS integration cycle */
644*4882a593Smuzhiyun msleep(data->als_it_val * 100);
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun return 0;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun static const struct dev_pm_ops vcnl4035_pm_ops = {
650*4882a593Smuzhiyun SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
651*4882a593Smuzhiyun pm_runtime_force_resume)
652*4882a593Smuzhiyun SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend,
653*4882a593Smuzhiyun vcnl4035_runtime_resume, NULL)
654*4882a593Smuzhiyun };
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun static const struct of_device_id vcnl4035_of_match[] = {
657*4882a593Smuzhiyun { .compatible = "vishay,vcnl4035", },
658*4882a593Smuzhiyun { }
659*4882a593Smuzhiyun };
660*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, vcnl4035_of_match);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun static struct i2c_driver vcnl4035_driver = {
663*4882a593Smuzhiyun .driver = {
664*4882a593Smuzhiyun .name = VCNL4035_DRV_NAME,
665*4882a593Smuzhiyun .pm = &vcnl4035_pm_ops,
666*4882a593Smuzhiyun .of_match_table = vcnl4035_of_match,
667*4882a593Smuzhiyun },
668*4882a593Smuzhiyun .probe = vcnl4035_probe,
669*4882a593Smuzhiyun .remove = vcnl4035_remove,
670*4882a593Smuzhiyun };
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun module_i2c_driver(vcnl4035_driver);
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>");
675*4882a593Smuzhiyun MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver");
676*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
677