xref: /openbmc/linux/drivers/iio/light/al3010.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
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