1 /* 2 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient 3 * light and proximity sensor 4 * 5 * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 6 * Copyright 2019 Pursim SPC 7 * 8 * This file is subject to the terms and conditions of version 2 of 9 * the GNU General Public License. See the file COPYING in the main 10 * directory of this archive for more details. 11 * 12 * IIO driver for: 13 * VCNL4000/10/20 (7-bit I2C slave address 0x13) 14 * VCNL4040 (7-bit I2C slave address 0x60) 15 * VCNL4200 (7-bit I2C slave address 0x51) 16 * 17 * TODO: 18 * allow to adjust IR current 19 * proximity threshold and event handling 20 * periodic ALS/proximity measurement (VCNL4010/20) 21 * interrupts (VCNL4010/20/40, VCNL4200) 22 */ 23 24 #include <linux/module.h> 25 #include <linux/i2c.h> 26 #include <linux/err.h> 27 #include <linux/delay.h> 28 29 #include <linux/iio/iio.h> 30 #include <linux/iio/sysfs.h> 31 32 #define VCNL4000_DRV_NAME "vcnl4000" 33 #define VCNL4000_PROD_ID 0x01 34 #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 35 #define VCNL4040_PROD_ID 0x86 36 #define VCNL4200_PROD_ID 0x58 37 38 #define VCNL4000_COMMAND 0x80 /* Command register */ 39 #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 40 #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 41 #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 42 #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 43 #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 44 #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 45 #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 46 #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 47 #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 48 49 #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 50 #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 51 #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 52 #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 53 #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 54 55 #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 56 57 /* Bit masks for COMMAND register */ 58 #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 59 #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 60 #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 61 #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 62 63 enum vcnl4000_device_ids { 64 VCNL4000, 65 VCNL4010, 66 VCNL4040, 67 VCNL4200, 68 }; 69 70 struct vcnl4200_channel { 71 u8 reg; 72 ktime_t last_measurement; 73 ktime_t sampling_rate; 74 struct mutex lock; 75 }; 76 77 struct vcnl4000_data { 78 struct i2c_client *client; 79 enum vcnl4000_device_ids id; 80 int rev; 81 int al_scale; 82 const struct vcnl4000_chip_spec *chip_spec; 83 struct mutex vcnl4000_lock; 84 struct vcnl4200_channel vcnl4200_al; 85 struct vcnl4200_channel vcnl4200_ps; 86 }; 87 88 struct vcnl4000_chip_spec { 89 const char *prod; 90 int (*init)(struct vcnl4000_data *data); 91 int (*measure_light)(struct vcnl4000_data *data, int *val); 92 int (*measure_proximity)(struct vcnl4000_data *data, int *val); 93 }; 94 95 static const struct i2c_device_id vcnl4000_id[] = { 96 { "vcnl4000", VCNL4000 }, 97 { "vcnl4010", VCNL4010 }, 98 { "vcnl4020", VCNL4010 }, 99 { "vcnl4040", VCNL4040 }, 100 { "vcnl4200", VCNL4200 }, 101 { } 102 }; 103 MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 104 105 static int vcnl4000_init(struct vcnl4000_data *data) 106 { 107 int ret, prod_id; 108 109 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 110 if (ret < 0) 111 return ret; 112 113 prod_id = ret >> 4; 114 switch (prod_id) { 115 case VCNL4000_PROD_ID: 116 if (data->id != VCNL4000) 117 dev_warn(&data->client->dev, 118 "wrong device id, use vcnl4000"); 119 break; 120 case VCNL4010_PROD_ID: 121 if (data->id != VCNL4010) 122 dev_warn(&data->client->dev, 123 "wrong device id, use vcnl4010/4020"); 124 break; 125 default: 126 return -ENODEV; 127 } 128 129 data->rev = ret & 0xf; 130 data->al_scale = 250000; 131 mutex_init(&data->vcnl4000_lock); 132 133 return 0; 134 }; 135 136 static int vcnl4200_init(struct vcnl4000_data *data) 137 { 138 int ret, id; 139 140 ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 141 if (ret < 0) 142 return ret; 143 144 id = ret & 0xff; 145 146 if (id != VCNL4200_PROD_ID) { 147 ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 148 if (ret < 0) 149 return ret; 150 151 id = ret & 0xff; 152 153 if (id != VCNL4040_PROD_ID) 154 return -ENODEV; 155 } 156 157 dev_dbg(&data->client->dev, "device id 0x%x", id); 158 159 data->rev = (ret >> 8) & 0xf; 160 161 /* Set defaults and enable both channels */ 162 ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0); 163 if (ret < 0) 164 return ret; 165 ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0); 166 if (ret < 0) 167 return ret; 168 169 data->al_scale = 24000; 170 data->vcnl4200_al.reg = VCNL4200_AL_DATA; 171 data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 172 switch (id) { 173 case VCNL4200_PROD_ID: 174 /* Integration time is 50ms, but the experiments */ 175 /* show 54ms in total. */ 176 data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000); 177 data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000); 178 break; 179 case VCNL4040_PROD_ID: 180 /* Integration time is 80ms, add 10ms. */ 181 data->vcnl4200_al.sampling_rate = ktime_set(0, 100000 * 1000); 182 data->vcnl4200_ps.sampling_rate = ktime_set(0, 100000 * 1000); 183 break; 184 } 185 data->vcnl4200_al.last_measurement = ktime_set(0, 0); 186 data->vcnl4200_ps.last_measurement = ktime_set(0, 0); 187 mutex_init(&data->vcnl4200_al.lock); 188 mutex_init(&data->vcnl4200_ps.lock); 189 190 return 0; 191 }; 192 193 static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 194 u8 rdy_mask, u8 data_reg, int *val) 195 { 196 int tries = 20; 197 __be16 buf; 198 int ret; 199 200 mutex_lock(&data->vcnl4000_lock); 201 202 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 203 req_mask); 204 if (ret < 0) 205 goto fail; 206 207 /* wait for data to become ready */ 208 while (tries--) { 209 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 210 if (ret < 0) 211 goto fail; 212 if (ret & rdy_mask) 213 break; 214 msleep(20); /* measurement takes up to 100 ms */ 215 } 216 217 if (tries < 0) { 218 dev_err(&data->client->dev, 219 "vcnl4000_measure() failed, data not ready\n"); 220 ret = -EIO; 221 goto fail; 222 } 223 224 ret = i2c_smbus_read_i2c_block_data(data->client, 225 data_reg, sizeof(buf), (u8 *) &buf); 226 if (ret < 0) 227 goto fail; 228 229 mutex_unlock(&data->vcnl4000_lock); 230 *val = be16_to_cpu(buf); 231 232 return 0; 233 234 fail: 235 mutex_unlock(&data->vcnl4000_lock); 236 return ret; 237 } 238 239 static int vcnl4200_measure(struct vcnl4000_data *data, 240 struct vcnl4200_channel *chan, int *val) 241 { 242 int ret; 243 s64 delta; 244 ktime_t next_measurement; 245 246 mutex_lock(&chan->lock); 247 248 next_measurement = ktime_add(chan->last_measurement, 249 chan->sampling_rate); 250 delta = ktime_us_delta(next_measurement, ktime_get()); 251 if (delta > 0) 252 usleep_range(delta, delta + 500); 253 chan->last_measurement = ktime_get(); 254 255 mutex_unlock(&chan->lock); 256 257 ret = i2c_smbus_read_word_data(data->client, chan->reg); 258 if (ret < 0) 259 return ret; 260 261 *val = ret; 262 263 return 0; 264 } 265 266 static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 267 { 268 return vcnl4000_measure(data, 269 VCNL4000_AL_OD, VCNL4000_AL_RDY, 270 VCNL4000_AL_RESULT_HI, val); 271 } 272 273 static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 274 { 275 return vcnl4200_measure(data, &data->vcnl4200_al, val); 276 } 277 278 static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 279 { 280 return vcnl4000_measure(data, 281 VCNL4000_PS_OD, VCNL4000_PS_RDY, 282 VCNL4000_PS_RESULT_HI, val); 283 } 284 285 static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 286 { 287 return vcnl4200_measure(data, &data->vcnl4200_ps, val); 288 } 289 290 static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 291 [VCNL4000] = { 292 .prod = "VCNL4000", 293 .init = vcnl4000_init, 294 .measure_light = vcnl4000_measure_light, 295 .measure_proximity = vcnl4000_measure_proximity, 296 }, 297 [VCNL4010] = { 298 .prod = "VCNL4010/4020", 299 .init = vcnl4000_init, 300 .measure_light = vcnl4000_measure_light, 301 .measure_proximity = vcnl4000_measure_proximity, 302 }, 303 [VCNL4040] = { 304 .prod = "VCNL4040", 305 .init = vcnl4200_init, 306 .measure_light = vcnl4200_measure_light, 307 .measure_proximity = vcnl4200_measure_proximity, 308 }, 309 [VCNL4200] = { 310 .prod = "VCNL4200", 311 .init = vcnl4200_init, 312 .measure_light = vcnl4200_measure_light, 313 .measure_proximity = vcnl4200_measure_proximity, 314 }, 315 }; 316 317 static const struct iio_chan_spec vcnl4000_channels[] = { 318 { 319 .type = IIO_LIGHT, 320 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 321 BIT(IIO_CHAN_INFO_SCALE), 322 }, { 323 .type = IIO_PROXIMITY, 324 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 325 } 326 }; 327 328 static int vcnl4000_read_raw(struct iio_dev *indio_dev, 329 struct iio_chan_spec const *chan, 330 int *val, int *val2, long mask) 331 { 332 int ret; 333 struct vcnl4000_data *data = iio_priv(indio_dev); 334 335 switch (mask) { 336 case IIO_CHAN_INFO_RAW: 337 switch (chan->type) { 338 case IIO_LIGHT: 339 ret = data->chip_spec->measure_light(data, val); 340 if (ret < 0) 341 return ret; 342 return IIO_VAL_INT; 343 case IIO_PROXIMITY: 344 ret = data->chip_spec->measure_proximity(data, val); 345 if (ret < 0) 346 return ret; 347 return IIO_VAL_INT; 348 default: 349 return -EINVAL; 350 } 351 case IIO_CHAN_INFO_SCALE: 352 if (chan->type != IIO_LIGHT) 353 return -EINVAL; 354 355 *val = 0; 356 *val2 = data->al_scale; 357 return IIO_VAL_INT_PLUS_MICRO; 358 default: 359 return -EINVAL; 360 } 361 } 362 363 static const struct iio_info vcnl4000_info = { 364 .read_raw = vcnl4000_read_raw, 365 }; 366 367 static int vcnl4000_probe(struct i2c_client *client, 368 const struct i2c_device_id *id) 369 { 370 struct vcnl4000_data *data; 371 struct iio_dev *indio_dev; 372 int ret; 373 374 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 375 if (!indio_dev) 376 return -ENOMEM; 377 378 data = iio_priv(indio_dev); 379 i2c_set_clientdata(client, indio_dev); 380 data->client = client; 381 data->id = id->driver_data; 382 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 383 384 ret = data->chip_spec->init(data); 385 if (ret < 0) 386 return ret; 387 388 dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 389 data->chip_spec->prod, data->rev); 390 391 indio_dev->dev.parent = &client->dev; 392 indio_dev->info = &vcnl4000_info; 393 indio_dev->channels = vcnl4000_channels; 394 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 395 indio_dev->name = VCNL4000_DRV_NAME; 396 indio_dev->modes = INDIO_DIRECT_MODE; 397 398 return devm_iio_device_register(&client->dev, indio_dev); 399 } 400 401 static const struct of_device_id vcnl_4000_of_match[] = { 402 { 403 .compatible = "vishay,vcnl4000", 404 .data = "VCNL4000", 405 }, 406 { 407 .compatible = "vishay,vcnl4010", 408 .data = "VCNL4010", 409 }, 410 { 411 .compatible = "vishay,vcnl4010", 412 .data = "VCNL4020", 413 }, 414 { 415 .compatible = "vishay,vcnl4200", 416 .data = "VCNL4200", 417 }, 418 {}, 419 }; 420 MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 421 422 static struct i2c_driver vcnl4000_driver = { 423 .driver = { 424 .name = VCNL4000_DRV_NAME, 425 .of_match_table = vcnl_4000_of_match, 426 }, 427 .probe = vcnl4000_probe, 428 .id_table = vcnl4000_id, 429 }; 430 431 module_i2c_driver(vcnl4000_driver); 432 433 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 434 MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 435 MODULE_LICENSE("GPL"); 436