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
3
* light and proximity sensor
4
4
*
5
5
* Copyright 2012 Peter Meerwald <[email protected] >
8
8
* the GNU General Public License. See the file COPYING in the main
9
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
15
* TODO:
14
16
* allow to adjust IR current
15
17
* proximity threshold and event handling
16
18
* periodic ALS/proximity measurement (VCNL4010/20)
17
- * interrupts (VCNL4010/20)
19
+ * interrupts (VCNL4010/20, VCNL4200 )
18
20
*/
19
21
20
22
#include <linux/module.h>
28
30
#define VCNL4000_DRV_NAME "vcnl4000"
29
31
#define VCNL4000_PROD_ID 0x01
30
32
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
33
+ #define VCNL4200_PROD_ID 0x58
31
34
32
35
#define VCNL4000_COMMAND 0x80 /* Command register */
33
36
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
40
43
#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
41
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
52
/* Bit masks for COMMAND register */
44
53
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
45
54
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
49
58
enum vcnl4000_device_ids {
50
59
VCNL4000 ,
51
60
VCNL4010 ,
61
+ VCNL4200 ,
62
+ };
63
+
64
+ struct vcnl4200_channel {
65
+ u8 reg ;
66
+ ktime_t last_measurement ;
67
+ ktime_t sampling_rate ;
68
+ struct mutex lock ;
52
69
};
53
70
54
71
struct vcnl4000_data {
@@ -57,7 +74,9 @@ struct vcnl4000_data {
57
74
int rev ;
58
75
int al_scale ;
59
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
63
82
struct vcnl4000_chip_spec {
@@ -71,6 +90,7 @@ static const struct i2c_device_id vcnl4000_id[] = {
71
90
{ "vcnl4000" , VCNL4000 },
72
91
{ "vcnl4010" , VCNL4010 },
73
92
{ "vcnl4020" , VCNL4010 },
93
+ { "vcnl4200" , VCNL4200 },
74
94
{ }
75
95
};
76
96
MODULE_DEVICE_TABLE (i2c , vcnl4000_id );
@@ -101,6 +121,42 @@ static int vcnl4000_init(struct vcnl4000_data *data)
101
121
102
122
data -> rev = ret & 0xf ;
103
123
data -> al_scale = 250000 ;
124
+ mutex_init (& data -> vcnl4000_lock );
125
+
126
+ return 0 ;
127
+ };
128
+
129
+ static 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
161
return 0 ;
106
162
};
@@ -112,7 +168,7 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
112
168
__be16 buf ;
113
169
int ret ;
114
170
115
- mutex_lock (& data -> lock );
171
+ mutex_lock (& data -> vcnl4000_lock );
116
172
117
173
ret = i2c_smbus_write_byte_data (data -> client , VCNL4000_COMMAND ,
118
174
req_mask );
@@ -141,30 +197,67 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
141
197
if (ret < 0 )
142
198
goto fail ;
143
199
144
- mutex_unlock (& data -> lock );
200
+ mutex_unlock (& data -> vcnl4000_lock );
145
201
* val = be16_to_cpu (buf );
146
202
147
203
return 0 ;
148
204
149
205
fail :
150
- mutex_unlock (& data -> lock );
206
+ mutex_unlock (& data -> vcnl4000_lock );
151
207
return ret ;
152
208
}
153
209
210
+ static 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
+
154
237
static int vcnl4000_measure_light (struct vcnl4000_data * data , int * val )
155
238
{
156
239
return vcnl4000_measure (data ,
157
240
VCNL4000_AL_OD , VCNL4000_AL_RDY ,
158
241
VCNL4000_AL_RESULT_HI , val );
159
242
}
160
243
244
+ static int vcnl4200_measure_light (struct vcnl4000_data * data , int * val )
245
+ {
246
+ return vcnl4200_measure (data , & data -> vcnl4200_al , val );
247
+ }
248
+
161
249
static int vcnl4000_measure_proximity (struct vcnl4000_data * data , int * val )
162
250
{
163
251
return vcnl4000_measure (data ,
164
252
VCNL4000_PS_OD , VCNL4000_PS_RDY ,
165
253
VCNL4000_PS_RESULT_HI , val );
166
254
}
167
255
256
+ static int vcnl4200_measure_proximity (struct vcnl4000_data * data , int * val )
257
+ {
258
+ return vcnl4200_measure (data , & data -> vcnl4200_ps , val );
259
+ }
260
+
168
261
static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg [] = {
169
262
[VCNL4000 ] = {
170
263
.prod = "VCNL4000" ,
@@ -178,6 +271,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
178
271
.measure_light = vcnl4000_measure_light ,
179
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
183
282
static const struct iio_chan_spec vcnl4000_channels [] = {
@@ -246,7 +345,6 @@ static int vcnl4000_probe(struct i2c_client *client,
246
345
data -> client = client ;
247
346
data -> id = id -> driver_data ;
248
347
data -> chip_spec = & vcnl4000_chip_spec_cfg [data -> id ];
249
- mutex_init (& data -> lock );
250
348
251
349
ret = data -> chip_spec -> init (data );
252
350
if (ret < 0 )
0 commit comments