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