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>
20c36b5195SDavid Heidelberg #include <linux/of.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);
90*8353a251SChristophe JAILLET if (ret < 0)
91*8353a251SChristophe JAILLET return ret;
92c36b5195SDavid Heidelberg
93*8353a251SChristophe JAILLET ret = devm_add_action_or_reset(&data->client->dev,
94*8353a251SChristophe JAILLET al3010_set_pwr_off,
95*8353a251SChristophe JAILLET data);
96c36b5195SDavid Heidelberg if (ret < 0)
97c36b5195SDavid Heidelberg return ret;
98c36b5195SDavid Heidelberg
99c36b5195SDavid Heidelberg ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
100c36b5195SDavid Heidelberg FIELD_PREP(AL3010_GAIN_MASK,
101c36b5195SDavid Heidelberg AL3XXX_RANGE_3));
102c36b5195SDavid Heidelberg if (ret < 0)
103c36b5195SDavid Heidelberg return ret;
104c36b5195SDavid Heidelberg
105c36b5195SDavid Heidelberg return 0;
106c36b5195SDavid Heidelberg }
107c36b5195SDavid Heidelberg
al3010_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)108c36b5195SDavid Heidelberg static int al3010_read_raw(struct iio_dev *indio_dev,
109c36b5195SDavid Heidelberg struct iio_chan_spec const *chan, int *val,
110c36b5195SDavid Heidelberg int *val2, long mask)
111c36b5195SDavid Heidelberg {
112c36b5195SDavid Heidelberg struct al3010_data *data = iio_priv(indio_dev);
113c36b5195SDavid Heidelberg int ret;
114c36b5195SDavid Heidelberg
115c36b5195SDavid Heidelberg switch (mask) {
116c36b5195SDavid Heidelberg case IIO_CHAN_INFO_RAW:
117c36b5195SDavid Heidelberg /*
118c36b5195SDavid Heidelberg * ALS ADC value is stored in two adjacent registers:
119c36b5195SDavid Heidelberg * - low byte of output is stored at AL3010_REG_DATA_LOW
120c36b5195SDavid Heidelberg * - high byte of output is stored at AL3010_REG_DATA_LOW + 1
121c36b5195SDavid Heidelberg */
122c36b5195SDavid Heidelberg ret = i2c_smbus_read_word_data(data->client,
123c36b5195SDavid Heidelberg AL3010_REG_DATA_LOW);
124c36b5195SDavid Heidelberg if (ret < 0)
125c36b5195SDavid Heidelberg return ret;
126c36b5195SDavid Heidelberg *val = ret;
127c36b5195SDavid Heidelberg return IIO_VAL_INT;
128c36b5195SDavid Heidelberg case IIO_CHAN_INFO_SCALE:
129c36b5195SDavid Heidelberg ret = i2c_smbus_read_byte_data(data->client,
130c36b5195SDavid Heidelberg AL3010_REG_CONFIG);
131c36b5195SDavid Heidelberg if (ret < 0)
132c36b5195SDavid Heidelberg return ret;
133c36b5195SDavid Heidelberg
134c36b5195SDavid Heidelberg ret = FIELD_GET(AL3010_GAIN_MASK, ret);
135c36b5195SDavid Heidelberg *val = al3010_scales[ret][0];
136c36b5195SDavid Heidelberg *val2 = al3010_scales[ret][1];
137c36b5195SDavid Heidelberg
138c36b5195SDavid Heidelberg return IIO_VAL_INT_PLUS_MICRO;
139c36b5195SDavid Heidelberg }
140c36b5195SDavid Heidelberg return -EINVAL;
141c36b5195SDavid Heidelberg }
142c36b5195SDavid Heidelberg
al3010_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)143c36b5195SDavid Heidelberg static int al3010_write_raw(struct iio_dev *indio_dev,
144c36b5195SDavid Heidelberg struct iio_chan_spec const *chan, int val,
145c36b5195SDavid Heidelberg int val2, long mask)
146c36b5195SDavid Heidelberg {
147c36b5195SDavid Heidelberg struct al3010_data *data = iio_priv(indio_dev);
148c36b5195SDavid Heidelberg int i;
149c36b5195SDavid Heidelberg
150c36b5195SDavid Heidelberg switch (mask) {
151c36b5195SDavid Heidelberg case IIO_CHAN_INFO_SCALE:
152c36b5195SDavid Heidelberg for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
153c36b5195SDavid Heidelberg if (val != al3010_scales[i][0] ||
154c36b5195SDavid Heidelberg val2 != al3010_scales[i][1])
155c36b5195SDavid Heidelberg continue;
156c36b5195SDavid Heidelberg
157c36b5195SDavid Heidelberg return i2c_smbus_write_byte_data(data->client,
158c36b5195SDavid Heidelberg AL3010_REG_CONFIG,
159c36b5195SDavid Heidelberg FIELD_PREP(AL3010_GAIN_MASK, i));
160c36b5195SDavid Heidelberg }
161c36b5195SDavid Heidelberg break;
162c36b5195SDavid Heidelberg }
163c36b5195SDavid Heidelberg return -EINVAL;
164c36b5195SDavid Heidelberg }
165c36b5195SDavid Heidelberg
166c36b5195SDavid Heidelberg static const struct iio_info al3010_info = {
167c36b5195SDavid Heidelberg .read_raw = al3010_read_raw,
168c36b5195SDavid Heidelberg .write_raw = al3010_write_raw,
169c36b5195SDavid Heidelberg .attrs = &al3010_attribute_group,
170c36b5195SDavid Heidelberg };
171c36b5195SDavid Heidelberg
al3010_probe(struct i2c_client * client)172ad428de3SUwe Kleine-König static int al3010_probe(struct i2c_client *client)
173c36b5195SDavid Heidelberg {
174c36b5195SDavid Heidelberg struct al3010_data *data;
175c36b5195SDavid Heidelberg struct iio_dev *indio_dev;
176c36b5195SDavid Heidelberg int ret;
177c36b5195SDavid Heidelberg
178c36b5195SDavid Heidelberg indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
179c36b5195SDavid Heidelberg if (!indio_dev)
180c36b5195SDavid Heidelberg return -ENOMEM;
181c36b5195SDavid Heidelberg
182c36b5195SDavid Heidelberg data = iio_priv(indio_dev);
183c36b5195SDavid Heidelberg i2c_set_clientdata(client, indio_dev);
184c36b5195SDavid Heidelberg data->client = client;
185c36b5195SDavid Heidelberg
186c36b5195SDavid Heidelberg indio_dev->info = &al3010_info;
187c36b5195SDavid Heidelberg indio_dev->name = AL3010_DRV_NAME;
188c36b5195SDavid Heidelberg indio_dev->channels = al3010_channels;
189c36b5195SDavid Heidelberg indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
190c36b5195SDavid Heidelberg indio_dev->modes = INDIO_DIRECT_MODE;
191c36b5195SDavid Heidelberg
192c36b5195SDavid Heidelberg ret = al3010_init(data);
193c36b5195SDavid Heidelberg if (ret < 0) {
194c36b5195SDavid Heidelberg dev_err(&client->dev, "al3010 chip init failed\n");
195c36b5195SDavid Heidelberg return ret;
196c36b5195SDavid Heidelberg }
197c36b5195SDavid Heidelberg
198c36b5195SDavid Heidelberg return devm_iio_device_register(&client->dev, indio_dev);
199c36b5195SDavid Heidelberg }
200c36b5195SDavid Heidelberg
al3010_suspend(struct device * dev)201dc064f21SJonathan Cameron static int al3010_suspend(struct device *dev)
202c36b5195SDavid Heidelberg {
203c36b5195SDavid Heidelberg return al3010_set_pwr(to_i2c_client(dev), false);
204c36b5195SDavid Heidelberg }
205c36b5195SDavid Heidelberg
al3010_resume(struct device * dev)206dc064f21SJonathan Cameron static int al3010_resume(struct device *dev)
207c36b5195SDavid Heidelberg {
208c36b5195SDavid Heidelberg return al3010_set_pwr(to_i2c_client(dev), true);
209c36b5195SDavid Heidelberg }
210c36b5195SDavid Heidelberg
211dc064f21SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
212c36b5195SDavid Heidelberg
213c36b5195SDavid Heidelberg static const struct i2c_device_id al3010_id[] = {
214c36b5195SDavid Heidelberg {"al3010", },
215c36b5195SDavid Heidelberg {}
216c36b5195SDavid Heidelberg };
217c36b5195SDavid Heidelberg MODULE_DEVICE_TABLE(i2c, al3010_id);
218c36b5195SDavid Heidelberg
219c36b5195SDavid Heidelberg static const struct of_device_id al3010_of_match[] = {
220c36b5195SDavid Heidelberg { .compatible = "dynaimage,al3010", },
221c36b5195SDavid Heidelberg {},
222c36b5195SDavid Heidelberg };
223c36b5195SDavid Heidelberg MODULE_DEVICE_TABLE(of, al3010_of_match);
224c36b5195SDavid Heidelberg
225c36b5195SDavid Heidelberg static struct i2c_driver al3010_driver = {
226c36b5195SDavid Heidelberg .driver = {
227c36b5195SDavid Heidelberg .name = AL3010_DRV_NAME,
228c36b5195SDavid Heidelberg .of_match_table = al3010_of_match,
229dc064f21SJonathan Cameron .pm = pm_sleep_ptr(&al3010_pm_ops),
230c36b5195SDavid Heidelberg },
2317cf15f42SUwe Kleine-König .probe = al3010_probe,
232c36b5195SDavid Heidelberg .id_table = al3010_id,
233c36b5195SDavid Heidelberg };
234c36b5195SDavid Heidelberg module_i2c_driver(al3010_driver);
235c36b5195SDavid Heidelberg
236c36b5195SDavid Heidelberg MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
237c36b5195SDavid Heidelberg MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
238c36b5195SDavid Heidelberg MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
239c36b5195SDavid Heidelberg MODULE_LICENSE("GPL v2");
240