diff options
author | Peter Meerwald <pmeerw@pmeerw.net> | 2012-06-19 03:58:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-06-20 19:12:46 -0400 |
commit | 62a1efb9f868690d68b11ffb22dc598e547aa184 (patch) | |
tree | 815c2703e9023497fab6ea46f24a155534484f5c /drivers/iio | |
parent | 069f101fa463351f528773d73b74e9b606b3f66a (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/Kconfig | 11 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/vcnl4000.c | 217 |
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 | |||
23 | config 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 | |||
22 | endmenu | 33 | endmenu |
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 | ||
5 | obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o | 5 | obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o |
6 | obj-$(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 | |||
45 | struct vcnl4000_data { | ||
46 | struct i2c_client *client; | ||
47 | }; | ||
48 | |||
49 | static const struct i2c_device_id vcnl4000_id[] = { | ||
50 | { "vcnl4000", 0 }, | ||
51 | { } | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(i2c, vcnl4000_id); | ||
54 | |||
55 | static 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 | |||
93 | static 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 | |||
104 | static 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 | |||
148 | static const struct iio_info vcnl4000_info = { | ||
149 | .read_raw = vcnl4000_read_raw, | ||
150 | .driver_module = THIS_MODULE, | ||
151 | }; | ||
152 | |||
153 | static 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 | |||
188 | error_free_dev: | ||
189 | iio_device_free(indio_dev); | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | static 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 | |||
203 | static 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 | |||
213 | module_i2c_driver(vcnl4000_driver); | ||
214 | |||
215 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | ||
216 | MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); | ||
217 | MODULE_LICENSE("GPL"); | ||