aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJonathan Cameron <jic23@cam.ac.uk>2009-08-18 13:06:21 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-09-15 15:02:24 -0400
commit78632b609a71ff2f598b114026fca03747f3a8c8 (patch)
tree50d62b336e51e66841116ff6328c169b168a1d50 /drivers
parentd1325cf45077bd27db3271dfc320c40bf65a8a07 (diff)
Staging: IIO: tsl2561 digital light sensor core support
This is a pretty minimalist example of an IIO driver. Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/iio/Kconfig1
-rw-r--r--drivers/staging/iio/Makefile3
-rw-r--r--drivers/staging/iio/light/Kconfig13
-rw-r--r--drivers/staging/iio/light/Makefile5
-rw-r--r--drivers/staging/iio/light/light.h12
-rw-r--r--drivers/staging/iio/light/tsl2561.c276
6 files changed, 309 insertions, 1 deletions
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index d7b3107823f6..5824f9ecf222 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -12,5 +12,6 @@ menuconfig IIO
12if IIO 12if IIO
13 13
14source "drivers/staging/iio/adc/Kconfig" 14source "drivers/staging/iio/adc/Kconfig"
15source "drivers/staging/iio/light/Kconfig"
15 16
16endif # IIO 17endif # IIO
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 9d11f640d20b..4e78f9845b57 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -5,4 +5,5 @@
5obj-$(CONFIG_IIO) += industrialio.o 5obj-$(CONFIG_IIO) += industrialio.o
6industrialio-y := industrialio-core.o 6industrialio-y := industrialio-core.o
7 7
8obj-y += adc/ \ No newline at end of file 8obj-y += adc/
9obj-y += light/ \ No newline at end of file
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
new file mode 100644
index 000000000000..12af0c46fe22
--- /dev/null
+++ b/drivers/staging/iio/light/Kconfig
@@ -0,0 +1,13 @@
1#
2# Light sensors
3#
4comment "Light sensors"
5
6config TSL2561
7 tristate "TAOS TSL2561 light-to-digital convertor"
8 depends on I2C
9 help
10 Say yes bere to build support for the TAOS light to digital
11 convertor. This chip has two light sensors. One is broadband
12 including infrared whilst the other measures only infrared.
13 Provides direct access via sysfs.
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
new file mode 100644
index 000000000000..ccff15167609
--- /dev/null
+++ b/drivers/staging/iio/light/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for industrial I/O Light sensors
3#
4
5obj-$(CONFIG_TSL2561) += tsl2561.o
diff --git a/drivers/staging/iio/light/light.h b/drivers/staging/iio/light/light.h
new file mode 100644
index 000000000000..f00f827689c2
--- /dev/null
+++ b/drivers/staging/iio/light/light.h
@@ -0,0 +1,12 @@
1#include "../sysfs.h"
2
3/* Light to digital sensor attributes */
4
5#define IIO_DEV_ATTR_LIGHT_INFRARED(_num, _show, _addr) \
6 IIO_DEVICE_ATTR(light_infrared##_num, S_IRUGO, _show, NULL, _addr)
7
8#define IIO_DEV_ATTR_LIGHT_BROAD(_num, _show, _addr) \
9 IIO_DEVICE_ATTR(light_broadspectrum##_num, S_IRUGO, _show, NULL, _addr)
10
11#define IIO_DEV_ATTR_LIGHT_VISIBLE(_num, _show, _addr) \
12 IIO_DEVICE_ATTR(light_visible##_num, S_IRUGO, _show, NULL, _addr)
diff --git a/drivers/staging/iio/light/tsl2561.c b/drivers/staging/iio/light/tsl2561.c
new file mode 100644
index 000000000000..ea8a5efc19bc
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2561.c
@@ -0,0 +1,276 @@
1/*
2 * tsl2561.c - Linux kernel modules for light to digital convertor
3 *
4 * Copyright (C) 2008-2009 Jonathan Cameron <jic23@cam.ac.uk>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Some portions based upon the tsl2550 driver.
21 *
22 * This driver could probably be adapted easily to talk to the tsl2560 (smbus)
23 *
24 * Needs some work to support the events this can generate.
25 * Todo: Implement interrupt handling. Currently a hardware bug means
26 * this isn't available on my test board.
27 */
28
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/i2c.h>
32#include "../iio.h"
33#include "../sysfs.h"
34#include "light.h"
35
36#define TSL2561_CONTROL_REGISTER 0x00
37#define TSL2561_TIMING_REGISTER 0x01
38#define TSL2561_THRESHLOW_LOW_REGISTER 0x02
39#define TSL2561_THRESHLOW_HIGH_REGISTER 0x03
40#define TSL2561_THRESHHIGH_LOW_REGISTER 0x04
41#define TSL2561_THRESHHIGH_HIGH_REGISTER 0x05
42#define TSL2561_INT_CONTROL_REGISTER 0x06
43
44#define TSL2561_INT_REG_INT_OFF 0x00
45#define TSL2561_INT_REG_INT_LEVEL 0x08
46#define TSL2561_INT_REG_INT_SMBUS 0x10
47#define TSL2561_INT_REG_INT_TEST 0x18
48
49#define TSL2561_ID_REGISTER 0x0A
50
51#define TSL2561_DATA_0_LOW 0x0C
52#define TSL2561_DATA_1_LOW 0x0E
53
54/* Control Register Values */
55#define TSL2561_CONT_REG_PWR_ON 0x03
56#define TSL2561_CONT_REG_PWR_OFF 0x00
57
58/**
59 * struct tsl2561_state - device specific state
60 * @indio_dev: the industrialio I/O info structure
61 * @client: i2c client
62 * @command_buf: single command buffer used for all operations
63 * @command_buf_lock: ensure unique access to command_buf
64 */
65struct tsl2561_state {
66 struct iio_dev *indio_dev;
67 struct i2c_client *client;
68 struct tsl2561_command *command_buf;
69 struct mutex command_buf_lock;
70};
71
72/**
73 * struct tsl2561_command - command byte for smbus
74 * @address: register address
75 * @block: is this a block r/w
76 * @word: is this a word r/w
77 * @clear: set to 1 to clear pending interrupt
78 * @cmd: select the command register - always 1.
79 */
80struct tsl2561_command {
81 unsigned int address:4;
82 unsigned int block:1;
83 unsigned int word:1;
84 unsigned int clear:1;
85 unsigned int cmd:1;
86};
87
88static inline void tsl2561_init_command_buf(struct tsl2561_command *buf)
89{
90 buf->address = 0;
91 buf->block = 0;
92 buf->word = 0;
93 buf->clear = 0;
94 buf->cmd = 1;
95}
96
97static ssize_t tsl2561_read_val(struct device *dev,
98 struct device_attribute *attr,
99 char *buf)
100{
101 int ret = 0, data;
102 ssize_t len = 0;
103 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
104 struct iio_dev *indio_dev = dev_get_drvdata(dev);
105 struct tsl2561_state *st = indio_dev->dev_data;
106
107 mutex_lock(&st->command_buf_lock);
108 st->command_buf->cmd = 1;
109 st->command_buf->word = 1;
110 st->command_buf->address = this_attr->address;
111
112 data = i2c_smbus_read_word_data(st->client, *(char *)(st->command_buf));
113 if (data < 0) {
114 ret = data;
115 goto error_ret;
116 }
117 len = sprintf(buf, "%u\n", data);
118
119error_ret:
120 mutex_unlock(&st->command_buf_lock);
121
122 return ret ? ret : len;
123}
124
125static IIO_DEV_ATTR_LIGHT_INFRARED(0, tsl2561_read_val, TSL2561_DATA_0_LOW);
126static IIO_DEV_ATTR_LIGHT_BROAD(0, tsl2561_read_val, TSL2561_DATA_1_LOW);
127
128static struct attribute *tsl2561_attributes[] = {
129 &iio_dev_attr_light_infrared0.dev_attr.attr,
130 &iio_dev_attr_light_broadspectrum0.dev_attr.attr,
131 NULL,
132};
133
134static const struct attribute_group tsl2561_attribute_group = {
135 .attrs = tsl2561_attributes,
136};
137
138static int tsl2561_initialize(struct tsl2561_state *st)
139{
140 int err;
141
142 mutex_lock(&st->command_buf_lock);
143 st->command_buf->word = 0;
144 st->command_buf->block = 0;
145 st->command_buf->address = TSL2561_CONTROL_REGISTER;
146 err = i2c_smbus_write_byte_data(st->client, *(char *)(st->command_buf),
147 TSL2561_CONT_REG_PWR_ON);
148 if (err)
149 goto error_ret;
150
151 st->command_buf->address = TSL2561_INT_CONTROL_REGISTER;
152 err = i2c_smbus_write_byte_data(st->client, *(char *)(st->command_buf),
153 TSL2561_INT_REG_INT_TEST);
154
155error_ret:
156 mutex_unlock(&st->command_buf_lock);
157
158 return err;
159}
160
161static int tsl2561_powerdown(struct i2c_client *client)
162{
163 int err;
164 struct tsl2561_command Command = {
165 .cmd = 1,
166 .clear = 0,
167 .word = 0,
168 .block = 0,
169 .address = TSL2561_CONTROL_REGISTER,
170 };
171
172 err = i2c_smbus_write_byte_data(client, *(char *)(&Command),
173 TSL2561_CONT_REG_PWR_OFF);
174 return (err < 0) ? err : 0;
175}
176static int __devinit tsl2561_probe(struct i2c_client *client,
177 const struct i2c_device_id *id)
178{
179 int ret = 0, regdone = 0;
180 struct tsl2561_state *st = kzalloc(sizeof(*st), GFP_KERNEL);
181
182 if (st == NULL) {
183 ret = -ENOMEM;
184 goto error_ret;
185 }
186 i2c_set_clientdata(client, st);
187 st->client = client;
188 mutex_init(&st->command_buf_lock);
189
190 st->command_buf = kmalloc(sizeof(*st->command_buf), GFP_KERNEL);
191 if (st->command_buf == NULL) {
192 ret = -ENOMEM;
193 goto error_free_state;
194 }
195 tsl2561_init_command_buf(st->command_buf);
196
197 st->indio_dev = iio_allocate_device();
198 if (st->indio_dev == NULL) {
199 ret = -ENOMEM;
200 goto error_free_command_buf;
201 }
202 st->indio_dev->attrs = &tsl2561_attribute_group;
203 st->indio_dev->dev.parent = &client->dev;
204 st->indio_dev->dev_data = (void *)(st);
205 st->indio_dev->driver_module = THIS_MODULE;
206 st->indio_dev->modes = INDIO_DIRECT_MODE;
207 ret = iio_device_register(st->indio_dev);
208 if (ret)
209 goto error_free_iiodev;
210 regdone = 1;
211 /* Intialize the chip */
212 ret = tsl2561_initialize(st);
213 if (ret)
214 goto error_unregister_iiodev;
215
216 return 0;
217error_unregister_iiodev:
218error_free_iiodev:
219 if (regdone)
220 iio_device_unregister(st->indio_dev);
221 else
222 iio_free_device(st->indio_dev);
223error_free_command_buf:
224 kfree(st->command_buf);
225error_free_state:
226 kfree(st);
227error_ret:
228 return ret;
229
230}
231
232static int __devexit tsl2561_remove(struct i2c_client *client)
233{
234 struct tsl2561_state *st = i2c_get_clientdata(client);
235
236 iio_device_unregister(st->indio_dev);
237 kfree(st);
238
239 return tsl2561_powerdown(client);
240}
241
242static unsigned short normal_i2c[] = { 0x29, 0x39, 0x49, I2C_CLIENT_END };
243
244I2C_CLIENT_INSMOD;
245
246static const struct i2c_device_id tsl2561_id[] = {
247 { "tsl2561", 0 },
248 { }
249};
250MODULE_DEVICE_TABLE(i2c, tsl2561_id);
251
252
253static struct i2c_driver tsl2561_driver = {
254 .driver = {
255 .name = "tsl2561",
256 },
257 .probe = tsl2561_probe,
258 .remove = __devexit_p(tsl2561_remove),
259 .id_table = tsl2561_id,
260};
261
262static __init int tsl2561_init(void)
263{
264 return i2c_add_driver(&tsl2561_driver);
265}
266module_init(tsl2561_init);
267
268static __exit void tsl2561_exit(void)
269{
270 i2c_del_driver(&tsl2561_driver);
271}
272module_exit(tsl2561_exit);
273
274MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
275MODULE_DESCRIPTION("TSL2561 light sensor driver");
276MODULE_LICENSE("GPL");