xref: /openbmc/linux/drivers/iio/light/adjd_s311.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bbdb822cSPeter Meerwald /*
3bbdb822cSPeter Meerwald  * adjd_s311.c - Support for ADJD-S311-CR999 digital color sensor
4bbdb822cSPeter Meerwald  *
5bbdb822cSPeter Meerwald  * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
6bbdb822cSPeter Meerwald  *
7bbdb822cSPeter Meerwald  * driver for ADJD-S311-CR999 digital color sensor (10-bit channels for
8bbdb822cSPeter Meerwald  * red, green, blue, clear); 7-bit I2C slave address 0x74
9bbdb822cSPeter Meerwald  *
10bbdb822cSPeter Meerwald  * limitations: no calibration, no offset mode, no sleep mode
11bbdb822cSPeter Meerwald  */
12bbdb822cSPeter Meerwald 
13bbdb822cSPeter Meerwald #include <linux/module.h>
14bbdb822cSPeter Meerwald #include <linux/interrupt.h>
15bbdb822cSPeter Meerwald #include <linux/i2c.h>
16bbdb822cSPeter Meerwald #include <linux/slab.h>
17bbdb822cSPeter Meerwald #include <linux/delay.h>
18bbdb822cSPeter Meerwald #include <linux/bitmap.h>
19bbdb822cSPeter Meerwald #include <linux/err.h>
20bbdb822cSPeter Meerwald #include <linux/irq.h>
21bbdb822cSPeter Meerwald 
22bbdb822cSPeter Meerwald #include <linux/iio/iio.h>
23bbdb822cSPeter Meerwald #include <linux/iio/sysfs.h>
24bbdb822cSPeter Meerwald #include <linux/iio/trigger_consumer.h>
25bbdb822cSPeter Meerwald #include <linux/iio/buffer.h>
26bbdb822cSPeter Meerwald #include <linux/iio/triggered_buffer.h>
27bbdb822cSPeter Meerwald 
28bbdb822cSPeter Meerwald #define ADJD_S311_DRV_NAME "adjd_s311"
29bbdb822cSPeter Meerwald 
30bbdb822cSPeter Meerwald #define ADJD_S311_CTRL		0x00
31bbdb822cSPeter Meerwald #define ADJD_S311_CONFIG	0x01
32bbdb822cSPeter Meerwald #define ADJD_S311_CAP_RED	0x06
33bbdb822cSPeter Meerwald #define ADJD_S311_CAP_GREEN	0x07
34bbdb822cSPeter Meerwald #define ADJD_S311_CAP_BLUE	0x08
35bbdb822cSPeter Meerwald #define ADJD_S311_CAP_CLEAR	0x09
364c730292SPeter Meerwald #define ADJD_S311_INT_RED	0x0a
374c730292SPeter Meerwald #define ADJD_S311_INT_GREEN	0x0c
384c730292SPeter Meerwald #define ADJD_S311_INT_BLUE	0x0e
394c730292SPeter Meerwald #define ADJD_S311_INT_CLEAR	0x10
404c730292SPeter Meerwald #define ADJD_S311_DATA_RED	0x40
414c730292SPeter Meerwald #define ADJD_S311_DATA_GREEN	0x42
424c730292SPeter Meerwald #define ADJD_S311_DATA_BLUE	0x44
434c730292SPeter Meerwald #define ADJD_S311_DATA_CLEAR	0x46
44bbdb822cSPeter Meerwald #define ADJD_S311_OFFSET_RED	0x48
45bbdb822cSPeter Meerwald #define ADJD_S311_OFFSET_GREEN	0x49
46bbdb822cSPeter Meerwald #define ADJD_S311_OFFSET_BLUE	0x4a
47bbdb822cSPeter Meerwald #define ADJD_S311_OFFSET_CLEAR	0x4b
48bbdb822cSPeter Meerwald 
49bbdb822cSPeter Meerwald #define ADJD_S311_CTRL_GOFS	0x02
50bbdb822cSPeter Meerwald #define ADJD_S311_CTRL_GSSR	0x01
51bbdb822cSPeter Meerwald #define ADJD_S311_CAP_MASK	0x0f
52bbdb822cSPeter Meerwald #define ADJD_S311_INT_MASK	0x0fff
53bbdb822cSPeter Meerwald #define ADJD_S311_DATA_MASK	0x03ff
54bbdb822cSPeter Meerwald 
55bbdb822cSPeter Meerwald struct adjd_s311_data {
56bbdb822cSPeter Meerwald 	struct i2c_client *client;
572427a7e9SAlexandru Ardelean 	struct {
582427a7e9SAlexandru Ardelean 		s16 chans[4];
592427a7e9SAlexandru Ardelean 		s64 ts __aligned(8);
602427a7e9SAlexandru Ardelean 	} scan;
61bbdb822cSPeter Meerwald };
62bbdb822cSPeter Meerwald 
63bbdb822cSPeter Meerwald enum adjd_s311_channel_idx {
64bbdb822cSPeter Meerwald 	IDX_RED, IDX_GREEN, IDX_BLUE, IDX_CLEAR
65bbdb822cSPeter Meerwald };
66bbdb822cSPeter Meerwald 
674c730292SPeter Meerwald #define ADJD_S311_DATA_REG(chan) (ADJD_S311_DATA_RED + (chan) * 2)
684c730292SPeter Meerwald #define ADJD_S311_INT_REG(chan) (ADJD_S311_INT_RED + (chan) * 2)
69bbdb822cSPeter Meerwald #define ADJD_S311_CAP_REG(chan) (ADJD_S311_CAP_RED + (chan))
70bbdb822cSPeter Meerwald 
adjd_s311_req_data(struct iio_dev * indio_dev)71bbdb822cSPeter Meerwald static int adjd_s311_req_data(struct iio_dev *indio_dev)
72bbdb822cSPeter Meerwald {
73bbdb822cSPeter Meerwald 	struct adjd_s311_data *data = iio_priv(indio_dev);
74bbdb822cSPeter Meerwald 	int tries = 10;
75bbdb822cSPeter Meerwald 
76bbdb822cSPeter Meerwald 	int ret = i2c_smbus_write_byte_data(data->client, ADJD_S311_CTRL,
77bbdb822cSPeter Meerwald 		ADJD_S311_CTRL_GSSR);
78bbdb822cSPeter Meerwald 	if (ret < 0)
79bbdb822cSPeter Meerwald 		return ret;
80bbdb822cSPeter Meerwald 
81bbdb822cSPeter Meerwald 	while (tries--) {
82bbdb822cSPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client, ADJD_S311_CTRL);
83bbdb822cSPeter Meerwald 		if (ret < 0)
84bbdb822cSPeter Meerwald 			return ret;
85bbdb822cSPeter Meerwald 		if (!(ret & ADJD_S311_CTRL_GSSR))
86bbdb822cSPeter Meerwald 			break;
87bbdb822cSPeter Meerwald 		msleep(20);
88bbdb822cSPeter Meerwald 	}
89bbdb822cSPeter Meerwald 
90bbdb822cSPeter Meerwald 	if (tries < 0) {
91bbdb822cSPeter Meerwald 		dev_err(&data->client->dev,
92bbdb822cSPeter Meerwald 			"adjd_s311_req_data() failed, data not ready\n");
93bbdb822cSPeter Meerwald 		return -EIO;
94bbdb822cSPeter Meerwald 	}
95bbdb822cSPeter Meerwald 
96bbdb822cSPeter Meerwald 	return 0;
97bbdb822cSPeter Meerwald }
98bbdb822cSPeter Meerwald 
adjd_s311_read_data(struct iio_dev * indio_dev,u8 reg,int * val)99bbdb822cSPeter Meerwald static int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val)
100bbdb822cSPeter Meerwald {
101bbdb822cSPeter Meerwald 	struct adjd_s311_data *data = iio_priv(indio_dev);
102bbdb822cSPeter Meerwald 
103bbdb822cSPeter Meerwald 	int ret = adjd_s311_req_data(indio_dev);
104bbdb822cSPeter Meerwald 	if (ret < 0)
105bbdb822cSPeter Meerwald 		return ret;
106bbdb822cSPeter Meerwald 
107bbdb822cSPeter Meerwald 	ret = i2c_smbus_read_word_data(data->client, reg);
108bbdb822cSPeter Meerwald 	if (ret < 0)
109bbdb822cSPeter Meerwald 		return ret;
110bbdb822cSPeter Meerwald 
111bbdb822cSPeter Meerwald 	*val = ret & ADJD_S311_DATA_MASK;
112bbdb822cSPeter Meerwald 
113bbdb822cSPeter Meerwald 	return 0;
114bbdb822cSPeter Meerwald }
115bbdb822cSPeter Meerwald 
adjd_s311_trigger_handler(int irq,void * p)116bbdb822cSPeter Meerwald static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
117bbdb822cSPeter Meerwald {
118bbdb822cSPeter Meerwald 	struct iio_poll_func *pf = p;
119bbdb822cSPeter Meerwald 	struct iio_dev *indio_dev = pf->indio_dev;
120bbdb822cSPeter Meerwald 	struct adjd_s311_data *data = iio_priv(indio_dev);
121bc2b7dabSGregor Boirie 	s64 time_ns = iio_get_time_ns(indio_dev);
122bbdb822cSPeter Meerwald 	int i, j = 0;
123bbdb822cSPeter Meerwald 
124bbdb822cSPeter Meerwald 	int ret = adjd_s311_req_data(indio_dev);
125bbdb822cSPeter Meerwald 	if (ret < 0)
126bbdb822cSPeter Meerwald 		goto done;
127bbdb822cSPeter Meerwald 
128bbdb822cSPeter Meerwald 	for_each_set_bit(i, indio_dev->active_scan_mask,
129bbdb822cSPeter Meerwald 		indio_dev->masklength) {
130bbdb822cSPeter Meerwald 		ret = i2c_smbus_read_word_data(data->client,
131bbdb822cSPeter Meerwald 			ADJD_S311_DATA_REG(i));
132bbdb822cSPeter Meerwald 		if (ret < 0)
133bbdb822cSPeter Meerwald 			goto done;
134bbdb822cSPeter Meerwald 
1352427a7e9SAlexandru Ardelean 		data->scan.chans[j++] = ret & ADJD_S311_DATA_MASK;
136bbdb822cSPeter Meerwald 	}
137bbdb822cSPeter Meerwald 
1382427a7e9SAlexandru Ardelean 	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns);
139bbdb822cSPeter Meerwald 
140bbdb822cSPeter Meerwald done:
141bbdb822cSPeter Meerwald 	iio_trigger_notify_done(indio_dev->trig);
142bbdb822cSPeter Meerwald 
143bbdb822cSPeter Meerwald 	return IRQ_HANDLED;
144bbdb822cSPeter Meerwald }
145bbdb822cSPeter Meerwald 
146bbdb822cSPeter Meerwald #define ADJD_S311_CHANNEL(_color, _scan_idx) { \
147bbdb822cSPeter Meerwald 	.type = IIO_INTENSITY, \
148bbdb822cSPeter Meerwald 	.modified = 1, \
149bbdb822cSPeter Meerwald 	.address = (IDX_##_color), \
1500112f521SJonathan Cameron 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
151caeac374SPeter Meerwald 		BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
152caeac374SPeter Meerwald 		BIT(IIO_CHAN_INFO_INT_TIME), \
153bbdb822cSPeter Meerwald 	.channel2 = (IIO_MOD_LIGHT_##_color), \
154bbdb822cSPeter Meerwald 	.scan_index = (_scan_idx), \
15580ac4b8aSJonathan Cameron 	.scan_type = { \
15680ac4b8aSJonathan Cameron 		.sign = 'u', \
15780ac4b8aSJonathan Cameron 		.realbits = 10, \
15880ac4b8aSJonathan Cameron 		.storagebits = 16, \
15980ac4b8aSJonathan Cameron 		.endianness = IIO_CPU, \
16080ac4b8aSJonathan Cameron 	}, \
161bbdb822cSPeter Meerwald }
162bbdb822cSPeter Meerwald 
163bbdb822cSPeter Meerwald static const struct iio_chan_spec adjd_s311_channels[] = {
164bbdb822cSPeter Meerwald 	ADJD_S311_CHANNEL(RED, 0),
165bbdb822cSPeter Meerwald 	ADJD_S311_CHANNEL(GREEN, 1),
166bbdb822cSPeter Meerwald 	ADJD_S311_CHANNEL(BLUE, 2),
167bbdb822cSPeter Meerwald 	ADJD_S311_CHANNEL(CLEAR, 3),
168bbdb822cSPeter Meerwald 	IIO_CHAN_SOFT_TIMESTAMP(4),
169bbdb822cSPeter Meerwald };
170bbdb822cSPeter Meerwald 
adjd_s311_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)171bbdb822cSPeter Meerwald static int adjd_s311_read_raw(struct iio_dev *indio_dev,
172bbdb822cSPeter Meerwald 			   struct iio_chan_spec const *chan,
173bbdb822cSPeter Meerwald 			   int *val, int *val2, long mask)
174bbdb822cSPeter Meerwald {
175bbdb822cSPeter Meerwald 	struct adjd_s311_data *data = iio_priv(indio_dev);
176bbdb822cSPeter Meerwald 	int ret;
177bbdb822cSPeter Meerwald 
178bbdb822cSPeter Meerwald 	switch (mask) {
179bbdb822cSPeter Meerwald 	case IIO_CHAN_INFO_RAW:
18071f42642SPeter Meerwald 		ret = adjd_s311_read_data(indio_dev,
18171f42642SPeter Meerwald 			ADJD_S311_DATA_REG(chan->address), val);
182bbdb822cSPeter Meerwald 		if (ret < 0)
183bbdb822cSPeter Meerwald 			return ret;
184bbdb822cSPeter Meerwald 		return IIO_VAL_INT;
185bbdb822cSPeter Meerwald 	case IIO_CHAN_INFO_HARDWAREGAIN:
186bbdb822cSPeter Meerwald 		ret = i2c_smbus_read_byte_data(data->client,
187bbdb822cSPeter Meerwald 			ADJD_S311_CAP_REG(chan->address));
188bbdb822cSPeter Meerwald 		if (ret < 0)
189bbdb822cSPeter Meerwald 			return ret;
190bbdb822cSPeter Meerwald 		*val = ret & ADJD_S311_CAP_MASK;
191bbdb822cSPeter Meerwald 		return IIO_VAL_INT;
192caeac374SPeter Meerwald 	case IIO_CHAN_INFO_INT_TIME:
193caeac374SPeter Meerwald 		ret = i2c_smbus_read_word_data(data->client,
194caeac374SPeter Meerwald 			ADJD_S311_INT_REG(chan->address));
195caeac374SPeter Meerwald 		if (ret < 0)
196caeac374SPeter Meerwald 			return ret;
197caeac374SPeter Meerwald 		*val = 0;
198caeac374SPeter Meerwald 		/*
199caeac374SPeter Meerwald 		 * not documented, based on measurement:
200caeac374SPeter Meerwald 		 * 4095 LSBs correspond to roughly 4 ms
201caeac374SPeter Meerwald 		 */
202caeac374SPeter Meerwald 		*val2 = ret & ADJD_S311_INT_MASK;
203caeac374SPeter Meerwald 		return IIO_VAL_INT_PLUS_MICRO;
204bbdb822cSPeter Meerwald 	}
205bbdb822cSPeter Meerwald 	return -EINVAL;
206bbdb822cSPeter Meerwald }
207bbdb822cSPeter Meerwald 
adjd_s311_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)208bbdb822cSPeter Meerwald static int adjd_s311_write_raw(struct iio_dev *indio_dev,
209bbdb822cSPeter Meerwald 			       struct iio_chan_spec const *chan,
210bbdb822cSPeter Meerwald 			       int val, int val2, long mask)
211bbdb822cSPeter Meerwald {
212bbdb822cSPeter Meerwald 	struct adjd_s311_data *data = iio_priv(indio_dev);
213bbdb822cSPeter Meerwald 
214bbdb822cSPeter Meerwald 	switch (mask) {
215bbdb822cSPeter Meerwald 	case IIO_CHAN_INFO_HARDWAREGAIN:
216bbdb822cSPeter Meerwald 		if (val < 0 || val > ADJD_S311_CAP_MASK)
217bbdb822cSPeter Meerwald 			return -EINVAL;
218bbdb822cSPeter Meerwald 
219caeac374SPeter Meerwald 		return i2c_smbus_write_byte_data(data->client,
220bbdb822cSPeter Meerwald 			ADJD_S311_CAP_REG(chan->address), val);
221caeac374SPeter Meerwald 	case IIO_CHAN_INFO_INT_TIME:
222caeac374SPeter Meerwald 		if (val != 0 || val2 < 0 || val2 > ADJD_S311_INT_MASK)
223caeac374SPeter Meerwald 			return -EINVAL;
224caeac374SPeter Meerwald 
225caeac374SPeter Meerwald 		return i2c_smbus_write_word_data(data->client,
226caeac374SPeter Meerwald 			ADJD_S311_INT_REG(chan->address), val2);
227bbdb822cSPeter Meerwald 	}
228bbdb822cSPeter Meerwald 	return -EINVAL;
229bbdb822cSPeter Meerwald }
230bbdb822cSPeter Meerwald 
231bbdb822cSPeter Meerwald static const struct iio_info adjd_s311_info = {
232bbdb822cSPeter Meerwald 	.read_raw = adjd_s311_read_raw,
233bbdb822cSPeter Meerwald 	.write_raw = adjd_s311_write_raw,
234bbdb822cSPeter Meerwald };
235bbdb822cSPeter Meerwald 
adjd_s311_probe(struct i2c_client * client)23639c7d963SUwe Kleine-König static int adjd_s311_probe(struct i2c_client *client)
237bbdb822cSPeter Meerwald {
238bbdb822cSPeter Meerwald 	struct adjd_s311_data *data;
239bbdb822cSPeter Meerwald 	struct iio_dev *indio_dev;
240bbdb822cSPeter Meerwald 	int err;
241bbdb822cSPeter Meerwald 
242ccf12c33SPeter Meerwald 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
243ccf12c33SPeter Meerwald 	if (indio_dev == NULL)
244ccf12c33SPeter Meerwald 		return -ENOMEM;
245ccf12c33SPeter Meerwald 
246bbdb822cSPeter Meerwald 	data = iio_priv(indio_dev);
247bbdb822cSPeter Meerwald 	data->client = client;
248bbdb822cSPeter Meerwald 
249bbdb822cSPeter Meerwald 	indio_dev->info = &adjd_s311_info;
250bbdb822cSPeter Meerwald 	indio_dev->name = ADJD_S311_DRV_NAME;
251bbdb822cSPeter Meerwald 	indio_dev->channels = adjd_s311_channels;
252bbdb822cSPeter Meerwald 	indio_dev->num_channels = ARRAY_SIZE(adjd_s311_channels);
253bbdb822cSPeter Meerwald 	indio_dev->modes = INDIO_DIRECT_MODE;
254bbdb822cSPeter Meerwald 
255bb761e72SAlexandru Ardelean 	err = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
256bbdb822cSPeter Meerwald 					      adjd_s311_trigger_handler, NULL);
257bbdb822cSPeter Meerwald 	if (err < 0)
258ccf12c33SPeter Meerwald 		return err;
259bbdb822cSPeter Meerwald 
260bb761e72SAlexandru Ardelean 	return devm_iio_device_register(&client->dev, indio_dev);
261bbdb822cSPeter Meerwald }
262bbdb822cSPeter Meerwald 
263bbdb822cSPeter Meerwald static const struct i2c_device_id adjd_s311_id[] = {
264bbdb822cSPeter Meerwald 	{ "adjd_s311", 0 },
265bbdb822cSPeter Meerwald 	{ }
266bbdb822cSPeter Meerwald };
267bbdb822cSPeter Meerwald MODULE_DEVICE_TABLE(i2c, adjd_s311_id);
268bbdb822cSPeter Meerwald 
269bbdb822cSPeter Meerwald static struct i2c_driver adjd_s311_driver = {
270bbdb822cSPeter Meerwald 	.driver = {
271bbdb822cSPeter Meerwald 		.name	= ADJD_S311_DRV_NAME,
272bbdb822cSPeter Meerwald 	},
273*7cf15f42SUwe Kleine-König 	.probe		= adjd_s311_probe,
274bbdb822cSPeter Meerwald 	.id_table	= adjd_s311_id,
275bbdb822cSPeter Meerwald };
276bbdb822cSPeter Meerwald module_i2c_driver(adjd_s311_driver);
277bbdb822cSPeter Meerwald 
278bbdb822cSPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
279bbdb822cSPeter Meerwald MODULE_DESCRIPTION("ADJD-S311 color sensor");
280bbdb822cSPeter Meerwald MODULE_LICENSE("GPL");
281