1c36b5195SDavid Heidelberg // SPDX-License-Identifier: GPL-2.0-only
2c36b5195SDavid Heidelberg /*
3c36b5195SDavid Heidelberg * AL3010 - Dyna Image Ambient Light Sensor
4c36b5195SDavid Heidelberg *
5c36b5195SDavid Heidelberg * Copyright (c) 2014, Intel Corporation.
6c36b5195SDavid Heidelberg * Copyright (c) 2016, Dyna-Image Corp.
7c36b5195SDavid Heidelberg * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko
8c36b5195SDavid Heidelberg *
9c36b5195SDavid Heidelberg * IIO driver for AL3010 (7-bit I2C slave address 0x1C).
10c36b5195SDavid Heidelberg *
11c36b5195SDavid Heidelberg * TODO: interrupt support, thresholds
12c36b5195SDavid Heidelberg * When the driver will get support for interrupt handling, then interrupt
13c36b5195SDavid Heidelberg * will need to be disabled before turning sensor OFF in order to avoid
14c36b5195SDavid Heidelberg * potential races with the interrupt handling.
15c36b5195SDavid Heidelberg */
16c36b5195SDavid Heidelberg
17c36b5195SDavid Heidelberg #include <linux/bitfield.h>
18c36b5195SDavid Heidelberg #include <linux/i2c.h>
19c36b5195SDavid Heidelberg #include <linux/module.h>
20*a5d8684fSJonathan Cameron #include <linux/mod_devicetable.h>
21c36b5195SDavid Heidelberg
22c36b5195SDavid Heidelberg #include <linux/iio/iio.h>
23c36b5195SDavid Heidelberg #include <linux/iio/sysfs.h>
24c36b5195SDavid Heidelberg
25c36b5195SDavid Heidelberg #define AL3010_DRV_NAME "al3010"
26c36b5195SDavid Heidelberg
27c36b5195SDavid Heidelberg #define AL3010_REG_SYSTEM 0x00
28c36b5195SDavid Heidelberg #define AL3010_REG_DATA_LOW 0x0c
29c36b5195SDavid Heidelberg #define AL3010_REG_CONFIG 0x10
30c36b5195SDavid Heidelberg
31c36b5195SDavid Heidelberg #define AL3010_CONFIG_DISABLE 0x00
32c36b5195SDavid Heidelberg #define AL3010_CONFIG_ENABLE 0x01
33c36b5195SDavid Heidelberg
34c36b5195SDavid Heidelberg #define AL3010_GAIN_MASK GENMASK(6,4)
35c36b5195SDavid Heidelberg
36c36b5195SDavid Heidelberg #define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018"
37c36b5195SDavid Heidelberg
38c36b5195SDavid Heidelberg enum al3xxxx_range {
39c36b5195SDavid Heidelberg AL3XXX_RANGE_1, /* 77806 lx */
40c36b5195SDavid Heidelberg AL3XXX_RANGE_2, /* 19542 lx */
41c36b5195SDavid Heidelberg AL3XXX_RANGE_3, /* 4863 lx */
42c36b5195SDavid Heidelberg AL3XXX_RANGE_4 /* 1216 lx */
43c36b5195SDavid Heidelberg };
44c36b5195SDavid Heidelberg
45c36b5195SDavid Heidelberg static const int al3010_scales[][2] = {
46c36b5195SDavid Heidelberg {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
47c36b5195SDavid Heidelberg };
48c36b5195SDavid Heidelberg
49c36b5195SDavid Heidelberg struct al3010_data {
50c36b5195SDavid Heidelberg struct i2c_client *client;
51c36b5195SDavid Heidelberg };
52c36b5195SDavid Heidelberg
53c36b5195SDavid Heidelberg static const struct iio_chan_spec al3010_channels[] = {
54c36b5195SDavid Heidelberg {
55c36b5195SDavid Heidelberg .type = IIO_LIGHT,
56c36b5195SDavid Heidelberg .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
57c36b5195SDavid Heidelberg BIT(IIO_CHAN_INFO_SCALE),
58c36b5195SDavid Heidelberg }
59c36b5195SDavid Heidelberg };
60c36b5195SDavid Heidelberg
61c36b5195SDavid Heidelberg static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE);
62c36b5195SDavid Heidelberg
63c36b5195SDavid Heidelberg static struct attribute *al3010_attributes[] = {
64c36b5195SDavid Heidelberg &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
65c36b5195SDavid Heidelberg NULL,
66c36b5195SDavid Heidelberg };
67c36b5195SDavid Heidelberg
68c36b5195SDavid Heidelberg static const struct attribute_group al3010_attribute_group = {
69c36b5195SDavid Heidelberg .attrs = al3010_attributes,
70c36b5195SDavid Heidelberg };
71c36b5195SDavid Heidelberg
al3010_set_pwr(struct i2c_client * client,bool pwr)72c36b5195SDavid Heidelberg static int al3010_set_pwr(struct i2c_client *client, bool pwr)
73c36b5195SDavid Heidelberg {
74c36b5195SDavid Heidelberg u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE;
75c36b5195SDavid Heidelberg return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val);
76c36b5195SDavid Heidelberg }
77c36b5195SDavid Heidelberg
al3010_set_pwr_off(void * _data)78c36b5195SDavid Heidelberg static void al3010_set_pwr_off(void *_data)
79c36b5195SDavid Heidelberg {
80c36b5195SDavid Heidelberg struct al3010_data *data = _data;
81c36b5195SDavid Heidelberg
82c36b5195SDavid Heidelberg al3010_set_pwr(data->client, false);
83c36b5195SDavid Heidelberg }
84c36b5195SDavid Heidelberg
al3010_init(struct al3010_data * data)85c36b5195SDavid Heidelberg static int al3010_init(struct al3010_data *data)
86c36b5195SDavid Heidelberg {
87c36b5195SDavid Heidelberg int ret;
88c36b5195SDavid Heidelberg
89c36b5195SDavid Heidelberg ret = al3010_set_pwr(data->client, true);
90c36b5195SDavid Heidelberg
91c36b5195SDavid Heidelberg if (ret < 0)
92c36b5195SDavid Heidelberg return ret;
93c36b5195SDavid Heidelberg
94c36b5195SDavid Heidelberg ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
95c36b5195SDavid Heidelberg FIELD_PREP(AL3010_GAIN_MASK,
96c36b5195SDavid Heidelberg AL3XXX_RANGE_3));
97c36b5195SDavid Heidelberg if (ret < 0)
98c36b5195SDavid Heidelberg return ret;
99c36b5195SDavid Heidelberg
100c36b5195SDavid Heidelberg return 0;
101c36b5195SDavid Heidelberg }
102c36b5195SDavid Heidelberg
al3010_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)103c36b5195SDavid Heidelberg static int al3010_read_raw(struct iio_dev *indio_dev,
104c36b5195SDavid Heidelberg struct iio_chan_spec const *chan, int *val,
105c36b5195SDavid Heidelberg int *val2, long mask)
106c36b5195SDavid Heidelberg {
107c36b5195SDavid Heidelberg struct al3010_data *data = iio_priv(indio_dev);
108c36b5195SDavid Heidelberg int ret;
109c36b5195SDavid Heidelberg
110c36b5195SDavid Heidelberg switch (mask) {
111c36b5195SDavid Heidelberg case IIO_CHAN_INFO_RAW:
112c36b5195SDavid Heidelberg /*
113c36b5195SDavid Heidelberg * ALS ADC value is stored in two adjacent registers:
114c36b5195SDavid Heidelberg * - low byte of output is stored at AL3010_REG_DATA_LOW
115c36b5195SDavid Heidelberg * - high byte of output is stored at AL3010_REG_DATA_LOW + 1
116c36b5195SDavid Heidelberg */
117c36b5195SDavid Heidelberg ret = i2c_smbus_read_word_data(data->client,
118c36b5195SDavid Heidelberg AL3010_REG_DATA_LOW);
119c36b5195SDavid Heidelberg if (ret < 0)
120c36b5195SDavid Heidelberg return ret;
121c36b5195SDavid Heidelberg *val = ret;
122c36b5195SDavid Heidelberg return IIO_VAL_INT;
123c36b5195SDavid Heidelberg case IIO_CHAN_INFO_SCALE:
124c36b5195SDavid Heidelberg ret = i2c_smbus_read_byte_data(data->client,
125c36b5195SDavid Heidelberg AL3010_REG_CONFIG);
126c36b5195SDavid Heidelberg if (ret < 0)
127c36b5195SDavid Heidelberg return ret;
128c36b5195SDavid Heidelberg
129c36b5195SDavid Heidelberg ret = FIELD_GET(AL3010_GAIN_MASK, ret);
130c36b5195SDavid Heidelberg *val = al3010_scales[ret][0];
131c36b5195SDavid Heidelberg *val2 = al3010_scales[ret][1];
132c36b5195SDavid Heidelberg
133c36b5195SDavid Heidelberg return IIO_VAL_INT_PLUS_MICRO;
134c36b5195SDavid Heidelberg }
135c36b5195SDavid Heidelberg return -EINVAL;
136c36b5195SDavid Heidelberg }
137c36b5195SDavid Heidelberg
al3010_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)138c36b5195SDavid Heidelberg static int al3010_write_raw(struct iio_dev *indio_dev,
139c36b5195SDavid Heidelberg struct iio_chan_spec const *chan, int val,
140c36b5195SDavid Heidelberg int val2, long mask)
141c36b5195SDavid Heidelberg {
142c36b5195SDavid Heidelberg struct al3010_data *data = iio_priv(indio_dev);
143c36b5195SDavid Heidelberg int i;
144c36b5195SDavid Heidelberg
145c36b5195SDavid Heidelberg switch (mask) {
146c36b5195SDavid Heidelberg case IIO_CHAN_INFO_SCALE:
147c36b5195SDavid Heidelberg for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
148c36b5195SDavid Heidelberg if (val != al3010_scales[i][0] ||
149c36b5195SDavid Heidelberg val2 != al3010_scales[i][1])
150c36b5195SDavid Heidelberg continue;
151c36b5195SDavid Heidelberg
152c36b5195SDavid Heidelberg return i2c_smbus_write_byte_data(data->client,
153c36b5195SDavid Heidelberg AL3010_REG_CONFIG,
154c36b5195SDavid Heidelberg FIELD_PREP(AL3010_GAIN_MASK, i));
155c36b5195SDavid Heidelberg }
156c36b5195SDavid Heidelberg break;
157c36b5195SDavid Heidelberg }
158c36b5195SDavid Heidelberg return -EINVAL;
159c36b5195SDavid Heidelberg }
160c36b5195SDavid Heidelberg
161c36b5195SDavid Heidelberg static const struct iio_info al3010_info = {
162c36b5195SDavid Heidelberg .read_raw = al3010_read_raw,
163c36b5195SDavid Heidelberg .write_raw = al3010_write_raw,
164c36b5195SDavid Heidelberg .attrs = &al3010_attribute_group,
165c36b5195SDavid Heidelberg };
166c36b5195SDavid Heidelberg
al3010_probe(struct i2c_client * client)167ad428de3SUwe Kleine-König static int al3010_probe(struct i2c_client *client)
168c36b5195SDavid Heidelberg {
169c36b5195SDavid Heidelberg struct al3010_data *data;
170c36b5195SDavid Heidelberg struct iio_dev *indio_dev;
171c36b5195SDavid Heidelberg int ret;
172c36b5195SDavid Heidelberg
173c36b5195SDavid Heidelberg indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
174c36b5195SDavid Heidelberg if (!indio_dev)
175c36b5195SDavid Heidelberg return -ENOMEM;
176c36b5195SDavid Heidelberg
177c36b5195SDavid Heidelberg data = iio_priv(indio_dev);
178c36b5195SDavid Heidelberg i2c_set_clientdata(client, indio_dev);
179c36b5195SDavid Heidelberg data->client = client;
180c36b5195SDavid Heidelberg
181c36b5195SDavid Heidelberg indio_dev->info = &al3010_info;
182c36b5195SDavid Heidelberg indio_dev->name = AL3010_DRV_NAME;
183c36b5195SDavid Heidelberg indio_dev->channels = al3010_channels;
184c36b5195SDavid Heidelberg indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
185c36b5195SDavid Heidelberg indio_dev->modes = INDIO_DIRECT_MODE;
186c36b5195SDavid Heidelberg
187c36b5195SDavid Heidelberg ret = al3010_init(data);
188c36b5195SDavid Heidelberg if (ret < 0) {
189c36b5195SDavid Heidelberg dev_err(&client->dev, "al3010 chip init failed\n");
190c36b5195SDavid Heidelberg return ret;
191c36b5195SDavid Heidelberg }
192c36b5195SDavid Heidelberg
193c36b5195SDavid Heidelberg ret = devm_add_action_or_reset(&client->dev,
194c36b5195SDavid Heidelberg al3010_set_pwr_off,
195c36b5195SDavid Heidelberg data);
196c36b5195SDavid Heidelberg if (ret < 0)
197c36b5195SDavid Heidelberg return ret;
198c36b5195SDavid Heidelberg
199c36b5195SDavid Heidelberg return devm_iio_device_register(&client->dev, indio_dev);
200c36b5195SDavid Heidelberg }
201c36b5195SDavid Heidelberg
al3010_suspend(struct device * dev)202dc064f21SJonathan Cameron static int al3010_suspend(struct device *dev)
203c36b5195SDavid Heidelberg {
204c36b5195SDavid Heidelberg return al3010_set_pwr(to_i2c_client(dev), false);
205c36b5195SDavid Heidelberg }
206c36b5195SDavid Heidelberg
al3010_resume(struct device * dev)207dc064f21SJonathan Cameron static int al3010_resume(struct device *dev)
208c36b5195SDavid Heidelberg {
209c36b5195SDavid Heidelberg return al3010_set_pwr(to_i2c_client(dev), true);
210c36b5195SDavid Heidelberg }
211c36b5195SDavid Heidelberg
212dc064f21SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
213c36b5195SDavid Heidelberg
214c36b5195SDavid Heidelberg static const struct i2c_device_id al3010_id[] = {
215c36b5195SDavid Heidelberg {"al3010", },
216c36b5195SDavid Heidelberg {}
217c36b5195SDavid Heidelberg };
218c36b5195SDavid Heidelberg MODULE_DEVICE_TABLE(i2c, al3010_id);
219c36b5195SDavid Heidelberg
220c36b5195SDavid Heidelberg static const struct of_device_id al3010_of_match[] = {
221c36b5195SDavid Heidelberg { .compatible = "dynaimage,al3010", },
222c36b5195SDavid Heidelberg {},
223c36b5195SDavid Heidelberg };
224c36b5195SDavid Heidelberg MODULE_DEVICE_TABLE(of, al3010_of_match);
225c36b5195SDavid Heidelberg
226c36b5195SDavid Heidelberg static struct i2c_driver al3010_driver = {
227c36b5195SDavid Heidelberg .driver = {
228c36b5195SDavid Heidelberg .name = AL3010_DRV_NAME,
229c36b5195SDavid Heidelberg .of_match_table = al3010_of_match,
230dc064f21SJonathan Cameron .pm = pm_sleep_ptr(&al3010_pm_ops),
231c36b5195SDavid Heidelberg },
2327cf15f42SUwe Kleine-König .probe = al3010_probe,
233c36b5195SDavid Heidelberg .id_table = al3010_id,
234c36b5195SDavid Heidelberg };
235c36b5195SDavid Heidelberg module_i2c_driver(al3010_driver);
236c36b5195SDavid Heidelberg
237c36b5195SDavid Heidelberg MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
238c36b5195SDavid Heidelberg MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
239c36b5195SDavid Heidelberg MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
240c36b5195SDavid Heidelberg MODULE_LICENSE("GPL v2");
241