aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/light
diff options
context:
space:
mode:
authorPeter Meerwald <pmeerw@pmeerw.net>2014-05-07 08:38:00 -0400
committerJonathan Cameron <jic23@kernel.org>2014-07-07 04:06:50 -0400
commit6c25539cbc460f7f594e30ac6db88d5e61e8baff (patch)
tree484010d1be4151afd2faad183e8afa36c5f73a6e /drivers/iio/light
parenta034234277d2f51104deadd559dd9584ba00a8cc (diff)
iio: Add Intersil isl29125 digital color light sensor driver
datasheet: http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29125.pdf Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/light')
-rw-r--r--drivers/iio/light/Kconfig12
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/isl29125.c347
3 files changed, 360 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index c89740d4748f..7d83dca6d080 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -62,6 +62,18 @@ config GP2AP020A00F
62 To compile this driver as a module, choose M here: the 62 To compile this driver as a module, choose M here: the
63 module will be called gp2ap020a00f. 63 module will be called gp2ap020a00f.
64 64
65config ISL29125
66 tristate "Intersil ISL29125 digital color light sensor"
67 depends on I2C
68 select IIO_BUFFER
69 select IIO_TRIGGERED_BUFFER
70 help
71 Say Y here if you want to build a driver for the Intersil ISL29125
72 RGB light sensor for I2C.
73
74 To compile this driver as a module, choose M here: the module will be
75 called isl29125.
76
65config HID_SENSOR_ALS 77config HID_SENSOR_ALS
66 depends on HID_SENSOR_HUB 78 depends on HID_SENSOR_HUB
67 select IIO_BUFFER 79 select IIO_BUFFER
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 3eb36e5151fa..f3d1857b2083 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CM36651) += cm36651.o
10obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o 10obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
11obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o 11obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
12obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o 12obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
13obj-$(CONFIG_ISL29125) += isl29125.o
13obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o 14obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
14obj-$(CONFIG_LTR501) += ltr501.o 15obj-$(CONFIG_LTR501) += ltr501.o
15obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o 16obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c
new file mode 100644
index 000000000000..c82f4a6f8464
--- /dev/null
+++ b/drivers/iio/light/isl29125.c
@@ -0,0 +1,347 @@
1/*
2 * isl29125.c - Support for Intersil ISL29125 RGB light sensor
3 *
4 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
5 *
6 * This file is subject to the terms and conditions of version 2 of
7 * the GNU General Public License. See the file COPYING in the main
8 * directory of this archive for more details.
9 *
10 * RGB light sensor with 16-bit channels for red, green, blue);
11 * 7-bit I2C slave address 0x44
12 *
13 * TODO: interrupt support, IR compensation, thresholds, 12bit
14 */
15
16#include <linux/module.h>
17#include <linux/i2c.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20
21#include <linux/iio/iio.h>
22#include <linux/iio/sysfs.h>
23#include <linux/iio/trigger_consumer.h>
24#include <linux/iio/buffer.h>
25#include <linux/iio/triggered_buffer.h>
26
27#define ISL29125_DRV_NAME "isl29125"
28
29#define ISL29125_DEVICE_ID 0x00
30#define ISL29125_CONF1 0x01
31#define ISL29125_CONF2 0x02
32#define ISL29125_CONF3 0x03
33#define ISL29125_STATUS 0x08
34#define ISL29125_GREEN_DATA 0x09
35#define ISL29125_RED_DATA 0x0b
36#define ISL29125_BLUE_DATA 0x0d
37
38#define ISL29125_ID 0x7d
39
40#define ISL29125_MODE_MASK GENMASK(2, 0)
41#define ISL29125_MODE_PD 0x0
42#define ISL29125_MODE_G 0x1
43#define ISL29125_MODE_R 0x2
44#define ISL29125_MODE_B 0x3
45#define ISL29125_MODE_RGB 0x5
46
47#define ISL29125_MODE_RANGE BIT(3)
48
49#define ISL29125_STATUS_CONV BIT(1)
50
51struct isl29125_data {
52 struct i2c_client *client;
53 struct mutex lock;
54 u8 conf1;
55 u16 buffer[8]; /* 3x 16-bit, padding, 8 bytes timestamp */
56};
57
58#define ISL29125_CHANNEL(_color, _si) { \
59 .type = IIO_INTENSITY, \
60 .modified = 1, \
61 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
62 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
63 .channel2 = IIO_MOD_LIGHT_##_color, \
64 .scan_index = _si, \
65 .scan_type = { \
66 .sign = 'u', \
67 .realbits = 16, \
68 .storagebits = 16, \
69 .endianness = IIO_CPU, \
70 }, \
71}
72
73static const struct iio_chan_spec isl29125_channels[] = {
74 ISL29125_CHANNEL(GREEN, 0),
75 ISL29125_CHANNEL(RED, 1),
76 ISL29125_CHANNEL(BLUE, 2),
77 IIO_CHAN_SOFT_TIMESTAMP(3),
78};
79
80static const struct {
81 u8 mode, data;
82} isl29125_regs[] = {
83 {ISL29125_MODE_G, ISL29125_GREEN_DATA},
84 {ISL29125_MODE_R, ISL29125_RED_DATA},
85 {ISL29125_MODE_B, ISL29125_BLUE_DATA},
86};
87
88static int isl29125_read_data(struct isl29125_data *data, int si)
89{
90 int tries = 5;
91 int ret;
92
93 ret = i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
94 data->conf1 | isl29125_regs[si].mode);
95 if (ret < 0)
96 return ret;
97
98 msleep(101);
99
100 while (tries--) {
101 ret = i2c_smbus_read_byte_data(data->client, ISL29125_STATUS);
102 if (ret < 0)
103 goto fail;
104 if (ret & ISL29125_STATUS_CONV)
105 break;
106 msleep(20);
107 }
108
109 if (tries < 0) {
110 dev_err(&data->client->dev, "data not ready\n");
111 ret = -EIO;
112 goto fail;
113 }
114
115 ret = i2c_smbus_read_word_data(data->client, isl29125_regs[si].data);
116
117fail:
118 i2c_smbus_write_byte_data(data->client, ISL29125_CONF1, data->conf1);
119 return ret;
120}
121
122static int isl29125_read_raw(struct iio_dev *indio_dev,
123 struct iio_chan_spec const *chan,
124 int *val, int *val2, long mask)
125{
126 struct isl29125_data *data = iio_priv(indio_dev);
127 int ret;
128
129 switch (mask) {
130 case IIO_CHAN_INFO_RAW:
131 if (iio_buffer_enabled(indio_dev))
132 return -EBUSY;
133 mutex_lock(&data->lock);
134 ret = isl29125_read_data(data, chan->scan_index);
135 mutex_unlock(&data->lock);
136 if (ret < 0)
137 return ret;
138 *val = ret;
139 return IIO_VAL_INT;
140 case IIO_CHAN_INFO_SCALE:
141 *val = 0;
142 if (data->conf1 & ISL29125_MODE_RANGE)
143 *val2 = 152590; /* 10k lux full range */
144 else
145 *val2 = 5722; /* 375 lux full range */
146 return IIO_VAL_INT_PLUS_MICRO;
147 }
148 return -EINVAL;
149}
150
151static int isl29125_write_raw(struct iio_dev *indio_dev,
152 struct iio_chan_spec const *chan,
153 int val, int val2, long mask)
154{
155 struct isl29125_data *data = iio_priv(indio_dev);
156
157 switch (mask) {
158 case IIO_CHAN_INFO_SCALE:
159 if (val != 0)
160 return -EINVAL;
161 if (val2 == 152590)
162 data->conf1 |= ISL29125_MODE_RANGE;
163 else if (val2 == 5722)
164 data->conf1 &= ~ISL29125_MODE_RANGE;
165 else
166 return -EINVAL;
167 return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
168 data->conf1);
169 default:
170 return -EINVAL;
171 }
172}
173
174static irqreturn_t isl29125_trigger_handler(int irq, void *p)
175{
176 struct iio_poll_func *pf = p;
177 struct iio_dev *indio_dev = pf->indio_dev;
178 struct isl29125_data *data = iio_priv(indio_dev);
179 int i, j = 0;
180
181 for_each_set_bit(i, indio_dev->active_scan_mask,
182 indio_dev->masklength) {
183 int ret = i2c_smbus_read_word_data(data->client,
184 isl29125_regs[i].data);
185 if (ret < 0)
186 goto done;
187
188 data->buffer[j++] = ret;
189 }
190
191 iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
192 iio_get_time_ns());
193
194done:
195 iio_trigger_notify_done(indio_dev->trig);
196
197 return IRQ_HANDLED;
198}
199
200static const struct iio_info isl29125_info = {
201 .read_raw = isl29125_read_raw,
202 .write_raw = isl29125_write_raw,
203 .driver_module = THIS_MODULE,
204};
205
206static int isl29125_buffer_preenable(struct iio_dev *indio_dev)
207{
208 struct isl29125_data *data = iio_priv(indio_dev);
209
210 data->conf1 |= ISL29125_MODE_RGB;
211 return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
212 data->conf1);
213}
214
215static int isl29125_buffer_predisable(struct iio_dev *indio_dev)
216{
217 struct isl29125_data *data = iio_priv(indio_dev);
218 int ret;
219
220 ret = iio_triggered_buffer_predisable(indio_dev);
221 if (ret < 0)
222 return ret;
223
224 data->conf1 &= ~ISL29125_MODE_MASK;
225 data->conf1 |= ISL29125_MODE_PD;
226 return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
227 data->conf1);
228}
229
230static const struct iio_buffer_setup_ops isl29125_buffer_setup_ops = {
231 .preenable = isl29125_buffer_preenable,
232 .postenable = &iio_triggered_buffer_postenable,
233 .predisable = isl29125_buffer_predisable,
234};
235
236static int isl29125_probe(struct i2c_client *client,
237 const struct i2c_device_id *id)
238{
239 struct isl29125_data *data;
240 struct iio_dev *indio_dev;
241 int ret;
242
243 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
244 if (indio_dev == NULL)
245 return -ENOMEM;
246
247 data = iio_priv(indio_dev);
248 i2c_set_clientdata(client, indio_dev);
249 data->client = client;
250 mutex_init(&data->lock);
251
252 indio_dev->dev.parent = &client->dev;
253 indio_dev->info = &isl29125_info;
254 indio_dev->name = ISL29125_DRV_NAME;
255 indio_dev->channels = isl29125_channels;
256 indio_dev->num_channels = ARRAY_SIZE(isl29125_channels);
257 indio_dev->modes = INDIO_DIRECT_MODE;
258
259 ret = i2c_smbus_read_byte_data(data->client, ISL29125_DEVICE_ID);
260 if (ret < 0)
261 return ret;
262 if (ret != ISL29125_ID)
263 return -ENODEV;
264
265 data->conf1 = ISL29125_MODE_PD | ISL29125_MODE_RANGE;
266 ret = i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
267 data->conf1);
268 if (ret < 0)
269 return ret;
270
271 ret = i2c_smbus_write_byte_data(data->client, ISL29125_STATUS, 0);
272 if (ret < 0)
273 return ret;
274
275 ret = iio_triggered_buffer_setup(indio_dev, NULL,
276 isl29125_trigger_handler, &isl29125_buffer_setup_ops);
277 if (ret < 0)
278 return ret;
279
280 ret = iio_device_register(indio_dev);
281 if (ret < 0)
282 goto buffer_cleanup;
283
284 return 0;
285
286buffer_cleanup:
287 iio_triggered_buffer_cleanup(indio_dev);
288 return ret;
289}
290
291static int isl29125_powerdown(struct isl29125_data *data)
292{
293 return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
294 (data->conf1 & ~ISL29125_MODE_MASK) | ISL29125_MODE_PD);
295}
296
297static int isl29125_remove(struct i2c_client *client)
298{
299 struct iio_dev *indio_dev = i2c_get_clientdata(client);
300
301 iio_device_unregister(indio_dev);
302 iio_triggered_buffer_cleanup(indio_dev);
303 isl29125_powerdown(iio_priv(indio_dev));
304
305 return 0;
306}
307
308#ifdef CONFIG_PM_SLEEP
309static int isl29125_suspend(struct device *dev)
310{
311 struct isl29125_data *data = iio_priv(i2c_get_clientdata(
312 to_i2c_client(dev)));
313 return isl29125_powerdown(data);
314}
315
316static int isl29125_resume(struct device *dev)
317{
318 struct isl29125_data *data = iio_priv(i2c_get_clientdata(
319 to_i2c_client(dev)));
320 return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
321 data->conf1);
322}
323#endif
324
325static SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, isl29125_resume);
326
327static const struct i2c_device_id isl29125_id[] = {
328 { "isl29125", 0 },
329 { }
330};
331MODULE_DEVICE_TABLE(i2c, isl29125_id);
332
333static struct i2c_driver isl29125_driver = {
334 .driver = {
335 .name = ISL29125_DRV_NAME,
336 .pm = &isl29125_pm_ops,
337 .owner = THIS_MODULE,
338 },
339 .probe = isl29125_probe,
340 .remove = isl29125_remove,
341 .id_table = isl29125_id,
342};
343module_i2c_driver(isl29125_driver);
344
345MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
346MODULE_DESCRIPTION("ISL29125 RGB light sensor driver");
347MODULE_LICENSE("GPL");