1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * AL3320A - Dyna Image Ambient Light Sensor
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2014, Intel Corporation.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * TODO: interrupt support, thresholds
10*4882a593Smuzhiyun * When the driver will get support for interrupt handling, then interrupt
11*4882a593Smuzhiyun * will need to be disabled before turning sensor OFF in order to avoid
12*4882a593Smuzhiyun * potential races with the interrupt handling.
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/bitfield.h>
16*4882a593Smuzhiyun #include <linux/i2c.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/of.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <linux/iio/iio.h>
21*4882a593Smuzhiyun #include <linux/iio/sysfs.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define AL3320A_DRV_NAME "al3320a"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define AL3320A_REG_CONFIG 0x00
26*4882a593Smuzhiyun #define AL3320A_REG_STATUS 0x01
27*4882a593Smuzhiyun #define AL3320A_REG_INT 0x02
28*4882a593Smuzhiyun #define AL3320A_REG_WAIT 0x06
29*4882a593Smuzhiyun #define AL3320A_REG_CONFIG_RANGE 0x07
30*4882a593Smuzhiyun #define AL3320A_REG_PERSIST 0x08
31*4882a593Smuzhiyun #define AL3320A_REG_MEAN_TIME 0x09
32*4882a593Smuzhiyun #define AL3320A_REG_ADUMMY 0x0A
33*4882a593Smuzhiyun #define AL3320A_REG_DATA_LOW 0x22
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define AL3320A_REG_LOW_THRESH_LOW 0x30
36*4882a593Smuzhiyun #define AL3320A_REG_LOW_THRESH_HIGH 0x31
37*4882a593Smuzhiyun #define AL3320A_REG_HIGH_THRESH_LOW 0x32
38*4882a593Smuzhiyun #define AL3320A_REG_HIGH_THRESH_HIGH 0x33
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define AL3320A_CONFIG_DISABLE 0x00
41*4882a593Smuzhiyun #define AL3320A_CONFIG_ENABLE 0x01
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun #define AL3320A_GAIN_MASK GENMASK(2, 1)
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* chip params default values */
46*4882a593Smuzhiyun #define AL3320A_DEFAULT_MEAN_TIME 4
47*4882a593Smuzhiyun #define AL3320A_DEFAULT_WAIT_TIME 0 /* no waiting */
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun #define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun enum al3320a_range {
52*4882a593Smuzhiyun AL3320A_RANGE_1, /* 33.28 Klx */
53*4882a593Smuzhiyun AL3320A_RANGE_2, /* 8.32 Klx */
54*4882a593Smuzhiyun AL3320A_RANGE_3, /* 2.08 Klx */
55*4882a593Smuzhiyun AL3320A_RANGE_4 /* 0.65 Klx */
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun static const int al3320a_scales[][2] = {
59*4882a593Smuzhiyun {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun struct al3320a_data {
63*4882a593Smuzhiyun struct i2c_client *client;
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static const struct iio_chan_spec al3320a_channels[] = {
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun .type = IIO_LIGHT,
69*4882a593Smuzhiyun .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
70*4882a593Smuzhiyun BIT(IIO_CHAN_INFO_SCALE),
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun static struct attribute *al3320a_attributes[] = {
77*4882a593Smuzhiyun &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
78*4882a593Smuzhiyun NULL,
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static const struct attribute_group al3320a_attribute_group = {
82*4882a593Smuzhiyun .attrs = al3320a_attributes,
83*4882a593Smuzhiyun };
84*4882a593Smuzhiyun
al3320a_set_pwr(struct i2c_client * client,bool pwr)85*4882a593Smuzhiyun static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
88*4882a593Smuzhiyun return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
al3320a_set_pwr_off(void * _data)91*4882a593Smuzhiyun static void al3320a_set_pwr_off(void *_data)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun struct al3320a_data *data = _data;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun al3320a_set_pwr(data->client, false);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
al3320a_init(struct al3320a_data * data)98*4882a593Smuzhiyun static int al3320a_init(struct al3320a_data *data)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun int ret;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun ret = al3320a_set_pwr(data->client, true);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if (ret < 0)
105*4882a593Smuzhiyun return ret;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
108*4882a593Smuzhiyun FIELD_PREP(AL3320A_GAIN_MASK,
109*4882a593Smuzhiyun AL3320A_RANGE_3));
110*4882a593Smuzhiyun if (ret < 0)
111*4882a593Smuzhiyun return ret;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
114*4882a593Smuzhiyun AL3320A_DEFAULT_MEAN_TIME);
115*4882a593Smuzhiyun if (ret < 0)
116*4882a593Smuzhiyun return ret;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
119*4882a593Smuzhiyun AL3320A_DEFAULT_WAIT_TIME);
120*4882a593Smuzhiyun if (ret < 0)
121*4882a593Smuzhiyun return ret;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun return 0;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
al3320a_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)126*4882a593Smuzhiyun static int al3320a_read_raw(struct iio_dev *indio_dev,
127*4882a593Smuzhiyun struct iio_chan_spec const *chan, int *val,
128*4882a593Smuzhiyun int *val2, long mask)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct al3320a_data *data = iio_priv(indio_dev);
131*4882a593Smuzhiyun int ret;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun switch (mask) {
134*4882a593Smuzhiyun case IIO_CHAN_INFO_RAW:
135*4882a593Smuzhiyun /*
136*4882a593Smuzhiyun * ALS ADC value is stored in two adjacent registers:
137*4882a593Smuzhiyun * - low byte of output is stored at AL3320A_REG_DATA_LOW
138*4882a593Smuzhiyun * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
139*4882a593Smuzhiyun */
140*4882a593Smuzhiyun ret = i2c_smbus_read_word_data(data->client,
141*4882a593Smuzhiyun AL3320A_REG_DATA_LOW);
142*4882a593Smuzhiyun if (ret < 0)
143*4882a593Smuzhiyun return ret;
144*4882a593Smuzhiyun *val = ret;
145*4882a593Smuzhiyun return IIO_VAL_INT;
146*4882a593Smuzhiyun case IIO_CHAN_INFO_SCALE:
147*4882a593Smuzhiyun ret = i2c_smbus_read_byte_data(data->client,
148*4882a593Smuzhiyun AL3320A_REG_CONFIG_RANGE);
149*4882a593Smuzhiyun if (ret < 0)
150*4882a593Smuzhiyun return ret;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
153*4882a593Smuzhiyun *val = al3320a_scales[ret][0];
154*4882a593Smuzhiyun *val2 = al3320a_scales[ret][1];
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return IIO_VAL_INT_PLUS_MICRO;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun return -EINVAL;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
al3320a_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)161*4882a593Smuzhiyun static int al3320a_write_raw(struct iio_dev *indio_dev,
162*4882a593Smuzhiyun struct iio_chan_spec const *chan, int val,
163*4882a593Smuzhiyun int val2, long mask)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun struct al3320a_data *data = iio_priv(indio_dev);
166*4882a593Smuzhiyun int i;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun switch (mask) {
169*4882a593Smuzhiyun case IIO_CHAN_INFO_SCALE:
170*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
171*4882a593Smuzhiyun if (val != al3320a_scales[i][0] ||
172*4882a593Smuzhiyun val2 != al3320a_scales[i][1])
173*4882a593Smuzhiyun continue;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun return i2c_smbus_write_byte_data(data->client,
176*4882a593Smuzhiyun AL3320A_REG_CONFIG_RANGE,
177*4882a593Smuzhiyun FIELD_PREP(AL3320A_GAIN_MASK, i));
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun break;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun return -EINVAL;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun static const struct iio_info al3320a_info = {
185*4882a593Smuzhiyun .read_raw = al3320a_read_raw,
186*4882a593Smuzhiyun .write_raw = al3320a_write_raw,
187*4882a593Smuzhiyun .attrs = &al3320a_attribute_group,
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun
al3320a_probe(struct i2c_client * client,const struct i2c_device_id * id)190*4882a593Smuzhiyun static int al3320a_probe(struct i2c_client *client,
191*4882a593Smuzhiyun const struct i2c_device_id *id)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct al3320a_data *data;
194*4882a593Smuzhiyun struct iio_dev *indio_dev;
195*4882a593Smuzhiyun int ret;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
198*4882a593Smuzhiyun if (!indio_dev)
199*4882a593Smuzhiyun return -ENOMEM;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun data = iio_priv(indio_dev);
202*4882a593Smuzhiyun i2c_set_clientdata(client, indio_dev);
203*4882a593Smuzhiyun data->client = client;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun indio_dev->info = &al3320a_info;
206*4882a593Smuzhiyun indio_dev->name = AL3320A_DRV_NAME;
207*4882a593Smuzhiyun indio_dev->channels = al3320a_channels;
208*4882a593Smuzhiyun indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
209*4882a593Smuzhiyun indio_dev->modes = INDIO_DIRECT_MODE;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun ret = al3320a_init(data);
212*4882a593Smuzhiyun if (ret < 0) {
213*4882a593Smuzhiyun dev_err(&client->dev, "al3320a chip init failed\n");
214*4882a593Smuzhiyun return ret;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun ret = devm_add_action_or_reset(&client->dev,
218*4882a593Smuzhiyun al3320a_set_pwr_off,
219*4882a593Smuzhiyun data);
220*4882a593Smuzhiyun if (ret < 0)
221*4882a593Smuzhiyun return ret;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun return devm_iio_device_register(&client->dev, indio_dev);
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
al3320a_suspend(struct device * dev)226*4882a593Smuzhiyun static int __maybe_unused al3320a_suspend(struct device *dev)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun return al3320a_set_pwr(to_i2c_client(dev), false);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
al3320a_resume(struct device * dev)231*4882a593Smuzhiyun static int __maybe_unused al3320a_resume(struct device *dev)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun return al3320a_set_pwr(to_i2c_client(dev), true);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun static const struct i2c_device_id al3320a_id[] = {
239*4882a593Smuzhiyun {"al3320a", 0},
240*4882a593Smuzhiyun {}
241*4882a593Smuzhiyun };
242*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, al3320a_id);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun static const struct of_device_id al3320a_of_match[] = {
245*4882a593Smuzhiyun { .compatible = "dynaimage,al3320a", },
246*4882a593Smuzhiyun {},
247*4882a593Smuzhiyun };
248*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, al3320a_of_match);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun static struct i2c_driver al3320a_driver = {
251*4882a593Smuzhiyun .driver = {
252*4882a593Smuzhiyun .name = AL3320A_DRV_NAME,
253*4882a593Smuzhiyun .of_match_table = al3320a_of_match,
254*4882a593Smuzhiyun .pm = &al3320a_pm_ops,
255*4882a593Smuzhiyun },
256*4882a593Smuzhiyun .probe = al3320a_probe,
257*4882a593Smuzhiyun .id_table = al3320a_id,
258*4882a593Smuzhiyun };
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun module_i2c_driver(al3320a_driver);
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
263*4882a593Smuzhiyun MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
264*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
265