aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorPeter Meerwald <pmeerw@pmeerw.net>2012-06-19 03:58:54 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-20 19:12:46 -0400
commit62a1efb9f868690d68b11ffb22dc598e547aa184 (patch)
tree815c2703e9023497fab6ea46f24a155534484f5c /drivers/iio
parent069f101fa463351f528773d73b74e9b606b3f66a (diff)
iio: add vcnl4000 combined ALS and proximity sensor
minimal driver, no IR current control and proximity/event handling yet v5: * checkpatch warnings * increase msleep() to 20 ms when waiting for data ready as measurement/conversion can take up to 100 ms, 1 ms is too short v4 (address comments by Jonathan Cameron) * remove SENSORS_ prefix in Kconfig * change from IIO_INTENSITY to IIO_LIGHT * move from staging v3 (address comments by Shubhrajyoti Datta) * cleanup Kconfig entry * call I2C read/write functions directly v2 (address comments by Lars-Peter Clausen and Jonathan Cameron) * unify code for reading PS and AL data into parameterized _measure() function * limit wait for data to become ready within 20 tries * drop IIO_LIGHT channel, add SCALE to IIO_INTENSITY * drop extra string arguments used for logging purpose only Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Acked-by: Jonathan Cameron <jic23@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/light/Kconfig11
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/vcnl4000.c217
3 files changed, 229 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index db5618e7d90b..f3ea90d735b8 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -19,4 +19,15 @@ config SENSORS_LM3533
19 light zone through sysfs. A threshold event can be generated on zone 19 light zone through sysfs. A threshold event can be generated on zone
20 changes. The ALS-control output values can be set per zone for the 20 changes. The ALS-control output values can be set per zone for the
21 three current output channels. 21 three current output channels.
22
23config VCNL4000
24 tristate "VCNL4000 combined ALS and proximity sensor"
25 depends on I2C
26 help
27 Say Y here if you want to build a driver for the Vishay VCNL4000
28 combined ambient light and proximity sensor.
29
30 To compile this driver as a module, choose M here: the
31 module will be called vcnl4000.
32
22endmenu 33endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index c1c23a024cd2..06fa4d3f33ec 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -3,3 +3,4 @@
3# 3#
4 4
5obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o 5obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
6obj-$(CONFIG_VCNL4000) += vcnl4000.o
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
new file mode 100644
index 000000000000..e49cb9784a6f
--- /dev/null
+++ b/drivers/iio/light/vcnl4000.c
@@ -0,0 +1,217 @@
1/*
2 * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
3 * proximity sensor
4 *
5 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
6 *
7 * This file is subject to the terms and conditions of version 2 of
8 * the GNU General Public License. See the file COPYING in the main
9 * directory of this archive for more details.
10 *
11 * IIO driver for VCNL4000 (7-bit I2C slave address 0x13)
12 *
13 * TODO:
14 * allow to adjust IR current
15 * proximity threshold and event handling
16 */
17
18#include <linux/module.h>
19#include <linux/i2c.h>
20#include <linux/err.h>
21#include <linux/delay.h>
22
23#include <linux/iio/iio.h>
24#include <linux/iio/sysfs.h>
25
26#define VCNL4000_DRV_NAME "vcnl4000"
27
28#define VCNL4000_COMMAND 0x80 /* Command register */
29#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
30#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
31#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
32#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
33#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
34#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
35#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
36#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
37#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
38
39/* Bit masks for COMMAND register */
40#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
41#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
42#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
43#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
44
45struct vcnl4000_data {
46 struct i2c_client *client;
47};
48
49static const struct i2c_device_id vcnl4000_id[] = {
50 { "vcnl4000", 0 },
51 { }
52};
53MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
54
55static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
56 u8 rdy_mask, u8 data_reg, int *val)
57{
58 int tries = 20;
59 u16 buf;
60 int ret;
61
62 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
63 req_mask);
64 if (ret < 0)
65 return ret;
66
67 /* wait for data to become ready */
68 while (tries--) {
69 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
70 if (ret < 0)
71 return ret;
72 if (ret & rdy_mask)
73 break;
74 msleep(20); /* measurement takes up to 100 ms */
75 }
76
77 if (tries < 0) {
78 dev_err(&data->client->dev,
79 "vcnl4000_measure() failed, data not ready\n");
80 return -EIO;
81 }
82
83 ret = i2c_smbus_read_i2c_block_data(data->client,
84 data_reg, sizeof(buf), (u8 *) &buf);
85 if (ret < 0)
86 return ret;
87
88 *val = be16_to_cpu(buf);
89
90 return 0;
91}
92
93static const struct iio_chan_spec vcnl4000_channels[] = {
94 {
95 .type = IIO_LIGHT,
96 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
97 IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
98 }, {
99 .type = IIO_PROXIMITY,
100 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
101 }
102};
103
104static int vcnl4000_read_raw(struct iio_dev *indio_dev,
105 struct iio_chan_spec const *chan,
106 int *val, int *val2, long mask)
107{
108 int ret = -EINVAL;
109 struct vcnl4000_data *data = iio_priv(indio_dev);
110
111 switch (mask) {
112 case IIO_CHAN_INFO_RAW:
113 switch (chan->type) {
114 case IIO_LIGHT:
115 ret = vcnl4000_measure(data,
116 VCNL4000_AL_OD, VCNL4000_AL_RDY,
117 VCNL4000_AL_RESULT_HI, val);
118 if (ret < 0)
119 return ret;
120 ret = IIO_VAL_INT;
121 break;
122 case IIO_PROXIMITY:
123 ret = vcnl4000_measure(data,
124 VCNL4000_PS_OD, VCNL4000_PS_RDY,
125 VCNL4000_PS_RESULT_HI, val);
126 if (ret < 0)
127 return ret;
128 ret = IIO_VAL_INT;
129 break;
130 default:
131 break;
132 }
133 break;
134 case IIO_CHAN_INFO_SCALE:
135 if (chan->type == IIO_LIGHT) {
136 *val = 0;
137 *val2 = 250000;
138 ret = IIO_VAL_INT_PLUS_MICRO;
139 }
140 break;
141 default:
142 break;
143 }
144
145 return ret;
146}
147
148static const struct iio_info vcnl4000_info = {
149 .read_raw = vcnl4000_read_raw,
150 .driver_module = THIS_MODULE,
151};
152
153static int __devinit vcnl4000_probe(struct i2c_client *client,
154 const struct i2c_device_id *id)
155{
156 struct vcnl4000_data *data;
157 struct iio_dev *indio_dev;
158 int ret;
159
160 indio_dev = iio_device_alloc(sizeof(*data));
161 if (!indio_dev)
162 return -ENOMEM;
163
164 data = iio_priv(indio_dev);
165 i2c_set_clientdata(client, indio_dev);
166 data->client = client;
167
168 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
169 if (ret < 0)
170 goto error_free_dev;
171
172 dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
173 ret >> 4, ret & 0xf);
174
175 indio_dev->dev.parent = &client->dev;
176 indio_dev->info = &vcnl4000_info;
177 indio_dev->channels = vcnl4000_channels;
178 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
179 indio_dev->name = VCNL4000_DRV_NAME;
180 indio_dev->modes = INDIO_DIRECT_MODE;
181
182 ret = iio_device_register(indio_dev);
183 if (ret < 0)
184 goto error_free_dev;
185
186 return 0;
187
188error_free_dev:
189 iio_device_free(indio_dev);
190 return ret;
191}
192
193static int __devexit vcnl4000_remove(struct i2c_client *client)
194{
195 struct iio_dev *indio_dev = i2c_get_clientdata(client);
196
197 iio_device_unregister(indio_dev);
198 iio_device_free(indio_dev);
199
200 return 0;
201}
202
203static struct i2c_driver vcnl4000_driver = {
204 .driver = {
205 .name = VCNL4000_DRV_NAME,
206 .owner = THIS_MODULE,
207 },
208 .probe = vcnl4000_probe,
209 .remove = __devexit_p(vcnl4000_remove),
210 .id_table = vcnl4000_id,
211};
212
213module_i2c_driver(vcnl4000_driver);
214
215MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
216MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
217MODULE_LICENSE("GPL");