summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorTomas Novotny <tomas@novotny.cz>2018-07-25 11:18:21 -0400
committerJonathan Cameron <Jonathan.Cameron@huawei.com>2018-07-29 07:19:28 -0400
commitbe38866fbb97f0ee2b144ab1de9ee2833a76e0fb (patch)
tree5585576943b923f6003ca507569a80743015a2f6 /drivers/iio
parent58bf9ace8c91be9b3e75cb001dbc199fae89d106 (diff)
iio: vcnl4000: add support for VCNL4200
VCNL4200 is an integrated long distance (up to 1500mm) proximity and ambient light sensor. The support is very basic. There is no configuration of proximity and ambient light sensing yet. Only the reading of both measured values is done. The reading of ambient light and proximity values is blocking. If you request a new value too early, the driver waits for new value to be ready. Signed-off-by: Tomas Novotny <tomas@novotny.cz> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/light/Kconfig5
-rw-r--r--drivers/iio/light/vcnl4000.c114
2 files changed, 109 insertions, 10 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index f17f701a9b61..d66ea754ffff 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -450,11 +450,12 @@ config US5182D
450 will be called us5182d. 450 will be called us5182d.
451 451
452config VCNL4000 452config VCNL4000
453 tristate "VCNL4000/4010/4020 combined ALS and proximity sensor" 453 tristate "VCNL4000/4010/4020/4200 combined ALS and proximity sensor"
454 depends on I2C 454 depends on I2C
455 help 455 help
456 Say Y here if you want to build a driver for the Vishay VCNL4000, 456 Say Y here if you want to build a driver for the Vishay VCNL4000,
457 VCNL4010, VCNL4020 combined ambient light and proximity sensor. 457 VCNL4010, VCNL4020, VCNL4200 combined ambient light and proximity
458 sensor.
458 459
459 To compile this driver as a module, choose M here: the 460 To compile this driver as a module, choose M here: the
460 module will be called vcnl4000. 461 module will be called vcnl4000.
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index a0cd1dcbf935..04fd0d4b6f19 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient 2 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient
3 * light and proximity sensor 3 * light and proximity sensor
4 * 4 *
5 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 5 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
@@ -8,13 +8,15 @@
8 * the GNU General Public License. See the file COPYING in the main 8 * the GNU General Public License. See the file COPYING in the main
9 * directory of this archive for more details. 9 * directory of this archive for more details.
10 * 10 *
11 * IIO driver for VCNL4000 (7-bit I2C slave address 0x13) 11 * IIO driver for:
12 * VCNL4000/10/20 (7-bit I2C slave address 0x13)
13 * VCNL4200 (7-bit I2C slave address 0x51)
12 * 14 *
13 * TODO: 15 * TODO:
14 * allow to adjust IR current 16 * allow to adjust IR current
15 * proximity threshold and event handling 17 * proximity threshold and event handling
16 * periodic ALS/proximity measurement (VCNL4010/20) 18 * periodic ALS/proximity measurement (VCNL4010/20)
17 * interrupts (VCNL4010/20) 19 * interrupts (VCNL4010/20, VCNL4200)
18 */ 20 */
19 21
20#include <linux/module.h> 22#include <linux/module.h>
@@ -28,6 +30,7 @@
28#define VCNL4000_DRV_NAME "vcnl4000" 30#define VCNL4000_DRV_NAME "vcnl4000"
29#define VCNL4000_PROD_ID 0x01 31#define VCNL4000_PROD_ID 0x01
30#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 32#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
33#define VCNL4200_PROD_ID 0x58
31 34
32#define VCNL4000_COMMAND 0x80 /* Command register */ 35#define VCNL4000_COMMAND 0x80 /* Command register */
33#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 36#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
@@ -40,6 +43,12 @@
40#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 43#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
41#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 44#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
42 45
46#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
47#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
48#define VCNL4200_PS_DATA 0x08 /* Proximity data */
49#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
50#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
51
43/* Bit masks for COMMAND register */ 52/* Bit masks for COMMAND register */
44#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 53#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
45#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 54#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
@@ -49,6 +58,14 @@
49enum vcnl4000_device_ids { 58enum vcnl4000_device_ids {
50 VCNL4000, 59 VCNL4000,
51 VCNL4010, 60 VCNL4010,
61 VCNL4200,
62};
63
64struct vcnl4200_channel {
65 u8 reg;
66 ktime_t last_measurement;
67 ktime_t sampling_rate;
68 struct mutex lock;
52}; 69};
53 70
54struct vcnl4000_data { 71struct vcnl4000_data {
@@ -57,7 +74,9 @@ struct vcnl4000_data {
57 int rev; 74 int rev;
58 int al_scale; 75 int al_scale;
59 const struct vcnl4000_chip_spec *chip_spec; 76 const struct vcnl4000_chip_spec *chip_spec;
60 struct mutex lock; 77 struct mutex vcnl4000_lock;
78 struct vcnl4200_channel vcnl4200_al;
79 struct vcnl4200_channel vcnl4200_ps;
61}; 80};
62 81
63struct vcnl4000_chip_spec { 82struct vcnl4000_chip_spec {
@@ -71,6 +90,7 @@ static const struct i2c_device_id vcnl4000_id[] = {
71 { "vcnl4000", VCNL4000 }, 90 { "vcnl4000", VCNL4000 },
72 { "vcnl4010", VCNL4010 }, 91 { "vcnl4010", VCNL4010 },
73 { "vcnl4020", VCNL4010 }, 92 { "vcnl4020", VCNL4010 },
93 { "vcnl4200", VCNL4200 },
74 { } 94 { }
75}; 95};
76MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 96MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
@@ -101,6 +121,42 @@ static int vcnl4000_init(struct vcnl4000_data *data)
101 121
102 data->rev = ret & 0xf; 122 data->rev = ret & 0xf;
103 data->al_scale = 250000; 123 data->al_scale = 250000;
124 mutex_init(&data->vcnl4000_lock);
125
126 return 0;
127};
128
129static int vcnl4200_init(struct vcnl4000_data *data)
130{
131 int ret;
132
133 ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
134 if (ret < 0)
135 return ret;
136
137 if ((ret & 0xff) != VCNL4200_PROD_ID)
138 return -ENODEV;
139
140 data->rev = (ret >> 8) & 0xf;
141
142 /* Set defaults and enable both channels */
143 ret = i2c_smbus_write_byte_data(data->client, VCNL4200_AL_CONF, 0x00);
144 if (ret < 0)
145 return ret;
146 ret = i2c_smbus_write_byte_data(data->client, VCNL4200_PS_CONF1, 0x00);
147 if (ret < 0)
148 return ret;
149
150 data->al_scale = 24000;
151 data->vcnl4200_al.reg = VCNL4200_AL_DATA;
152 data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
153 /* Integration time is 50ms, but the experiments show 54ms in total. */
154 data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
155 data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
156 data->vcnl4200_al.last_measurement = ktime_set(0, 0);
157 data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
158 mutex_init(&data->vcnl4200_al.lock);
159 mutex_init(&data->vcnl4200_ps.lock);
104 160
105 return 0; 161 return 0;
106}; 162};
@@ -112,7 +168,7 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
112 __be16 buf; 168 __be16 buf;
113 int ret; 169 int ret;
114 170
115 mutex_lock(&data->lock); 171 mutex_lock(&data->vcnl4000_lock);
116 172
117 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 173 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
118 req_mask); 174 req_mask);
@@ -141,16 +197,43 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
141 if (ret < 0) 197 if (ret < 0)
142 goto fail; 198 goto fail;
143 199
144 mutex_unlock(&data->lock); 200 mutex_unlock(&data->vcnl4000_lock);
145 *val = be16_to_cpu(buf); 201 *val = be16_to_cpu(buf);
146 202
147 return 0; 203 return 0;
148 204
149fail: 205fail:
150 mutex_unlock(&data->lock); 206 mutex_unlock(&data->vcnl4000_lock);
151 return ret; 207 return ret;
152} 208}
153 209
210static int vcnl4200_measure(struct vcnl4000_data *data,
211 struct vcnl4200_channel *chan, int *val)
212{
213 int ret;
214 s64 delta;
215 ktime_t next_measurement;
216
217 mutex_lock(&chan->lock);
218
219 next_measurement = ktime_add(chan->last_measurement,
220 chan->sampling_rate);
221 delta = ktime_us_delta(next_measurement, ktime_get());
222 if (delta > 0)
223 usleep_range(delta, delta + 500);
224 chan->last_measurement = ktime_get();
225
226 mutex_unlock(&chan->lock);
227
228 ret = i2c_smbus_read_word_data(data->client, chan->reg);
229 if (ret < 0)
230 return ret;
231
232 *val = ret;
233
234 return 0;
235}
236
154static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 237static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
155{ 238{
156 return vcnl4000_measure(data, 239 return vcnl4000_measure(data,
@@ -158,6 +241,11 @@ static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
158 VCNL4000_AL_RESULT_HI, val); 241 VCNL4000_AL_RESULT_HI, val);
159} 242}
160 243
244static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
245{
246 return vcnl4200_measure(data, &data->vcnl4200_al, val);
247}
248
161static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 249static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
162{ 250{
163 return vcnl4000_measure(data, 251 return vcnl4000_measure(data,
@@ -165,6 +253,11 @@ static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
165 VCNL4000_PS_RESULT_HI, val); 253 VCNL4000_PS_RESULT_HI, val);
166} 254}
167 255
256static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
257{
258 return vcnl4200_measure(data, &data->vcnl4200_ps, val);
259}
260
168static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 261static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
169 [VCNL4000] = { 262 [VCNL4000] = {
170 .prod = "VCNL4000", 263 .prod = "VCNL4000",
@@ -178,6 +271,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
178 .measure_light = vcnl4000_measure_light, 271 .measure_light = vcnl4000_measure_light,
179 .measure_proximity = vcnl4000_measure_proximity, 272 .measure_proximity = vcnl4000_measure_proximity,
180 }, 273 },
274 [VCNL4200] = {
275 .prod = "VCNL4200",
276 .init = vcnl4200_init,
277 .measure_light = vcnl4200_measure_light,
278 .measure_proximity = vcnl4200_measure_proximity,
279 },
181}; 280};
182 281
183static const struct iio_chan_spec vcnl4000_channels[] = { 282static const struct iio_chan_spec vcnl4000_channels[] = {
@@ -246,7 +345,6 @@ static int vcnl4000_probe(struct i2c_client *client,
246 data->client = client; 345 data->client = client;
247 data->id = id->driver_data; 346 data->id = id->driver_data;
248 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 347 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
249 mutex_init(&data->lock);
250 348
251 ret = data->chip_spec->init(data); 349 ret = data->chip_spec->init(data);
252 if (ret < 0) 350 if (ret < 0)