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