1 /* 2 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient 3 * light and 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: 12 * VCNL4000/10/20 (7-bit I2C slave address 0x13) 13 * VCNL4200 (7-bit I2C slave address 0x51) 14 * 15 * TODO: 16 * allow to adjust IR current 17 * proximity threshold and event handling 18 * periodic ALS/proximity measurement (VCNL4010/20) 19 * interrupts (VCNL4010/20, VCNL4200) 20 */ 21 22 #include <linux/module.h> 23 #include <linux/i2c.h> 24 #include <linux/err.h> 25 #include <linux/delay.h> 26 27 #include <linux/iio/iio.h> 28 #include <linux/iio/sysfs.h> 29 30 #define VCNL4000_DRV_NAME "vcnl4000" 31 #define VCNL4000_PROD_ID 0x01 32 #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 33 #define VCNL4200_PROD_ID 0x58 34 35 #define VCNL4000_COMMAND 0x80 /* Command register */ 36 #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 37 #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 38 #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 39 #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 40 #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 41 #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 42 #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 43 #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 44 #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 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 52 /* Bit masks for COMMAND register */ 53 #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 54 #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 55 #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 56 #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 57 58 enum vcnl4000_device_ids { 59 VCNL4000, 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; 69 }; 70 71 struct vcnl4000_data { 72 struct i2c_client *client; 73 enum vcnl4000_device_ids id; 74 int rev; 75 int al_scale; 76 const struct vcnl4000_chip_spec *chip_spec; 77 struct mutex vcnl4000_lock; 78 struct vcnl4200_channel vcnl4200_al; 79 struct vcnl4200_channel vcnl4200_ps; 80 }; 81 82 struct vcnl4000_chip_spec { 83 const char *prod; 84 int (*init)(struct vcnl4000_data *data); 85 int (*measure_light)(struct vcnl4000_data *data, int *val); 86 int (*measure_proximity)(struct vcnl4000_data *data, int *val); 87 }; 88 89 static const struct i2c_device_id vcnl4000_id[] = { 90 { "vcnl4000", VCNL4000 }, 91 { "vcnl4010", VCNL4010 }, 92 { "vcnl4020", VCNL4010 }, 93 { "vcnl4200", VCNL4200 }, 94 { } 95 }; 96 MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 97 98 static int vcnl4000_init(struct vcnl4000_data *data) 99 { 100 int ret, prod_id; 101 102 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 103 if (ret < 0) 104 return ret; 105 106 prod_id = ret >> 4; 107 switch (prod_id) { 108 case VCNL4000_PROD_ID: 109 if (data->id != VCNL4000) 110 dev_warn(&data->client->dev, 111 "wrong device id, use vcnl4000"); 112 break; 113 case VCNL4010_PROD_ID: 114 if (data->id != VCNL4010) 115 dev_warn(&data->client->dev, 116 "wrong device id, use vcnl4010/4020"); 117 break; 118 default: 119 return -ENODEV; 120 } 121 122 data->rev = ret & 0xf; 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); 160 161 return 0; 162 }; 163 164 static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 165 u8 rdy_mask, u8 data_reg, int *val) 166 { 167 int tries = 20; 168 __be16 buf; 169 int ret; 170 171 mutex_lock(&data->vcnl4000_lock); 172 173 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 174 req_mask); 175 if (ret < 0) 176 goto fail; 177 178 /* wait for data to become ready */ 179 while (tries--) { 180 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 181 if (ret < 0) 182 goto fail; 183 if (ret & rdy_mask) 184 break; 185 msleep(20); /* measurement takes up to 100 ms */ 186 } 187 188 if (tries < 0) { 189 dev_err(&data->client->dev, 190 "vcnl4000_measure() failed, data not ready\n"); 191 ret = -EIO; 192 goto fail; 193 } 194 195 ret = i2c_smbus_read_i2c_block_data(data->client, 196 data_reg, sizeof(buf), (u8 *) &buf); 197 if (ret < 0) 198 goto fail; 199 200 mutex_unlock(&data->vcnl4000_lock); 201 *val = be16_to_cpu(buf); 202 203 return 0; 204 205 fail: 206 mutex_unlock(&data->vcnl4000_lock); 207 return ret; 208 } 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 237 static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 238 { 239 return vcnl4000_measure(data, 240 VCNL4000_AL_OD, VCNL4000_AL_RDY, 241 VCNL4000_AL_RESULT_HI, val); 242 } 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 249 static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 250 { 251 return vcnl4000_measure(data, 252 VCNL4000_PS_OD, VCNL4000_PS_RDY, 253 VCNL4000_PS_RESULT_HI, val); 254 } 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 261 static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 262 [VCNL4000] = { 263 .prod = "VCNL4000", 264 .init = vcnl4000_init, 265 .measure_light = vcnl4000_measure_light, 266 .measure_proximity = vcnl4000_measure_proximity, 267 }, 268 [VCNL4010] = { 269 .prod = "VCNL4010/4020", 270 .init = vcnl4000_init, 271 .measure_light = vcnl4000_measure_light, 272 .measure_proximity = vcnl4000_measure_proximity, 273 }, 274 [VCNL4200] = { 275 .prod = "VCNL4200", 276 .init = vcnl4200_init, 277 .measure_light = vcnl4200_measure_light, 278 .measure_proximity = vcnl4200_measure_proximity, 279 }, 280 }; 281 282 static const struct iio_chan_spec vcnl4000_channels[] = { 283 { 284 .type = IIO_LIGHT, 285 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 286 BIT(IIO_CHAN_INFO_SCALE), 287 }, { 288 .type = IIO_PROXIMITY, 289 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 290 } 291 }; 292 293 static int vcnl4000_read_raw(struct iio_dev *indio_dev, 294 struct iio_chan_spec const *chan, 295 int *val, int *val2, long mask) 296 { 297 int ret; 298 struct vcnl4000_data *data = iio_priv(indio_dev); 299 300 switch (mask) { 301 case IIO_CHAN_INFO_RAW: 302 switch (chan->type) { 303 case IIO_LIGHT: 304 ret = data->chip_spec->measure_light(data, val); 305 if (ret < 0) 306 return ret; 307 return IIO_VAL_INT; 308 case IIO_PROXIMITY: 309 ret = data->chip_spec->measure_proximity(data, val); 310 if (ret < 0) 311 return ret; 312 return IIO_VAL_INT; 313 default: 314 return -EINVAL; 315 } 316 case IIO_CHAN_INFO_SCALE: 317 if (chan->type != IIO_LIGHT) 318 return -EINVAL; 319 320 *val = 0; 321 *val2 = data->al_scale; 322 return IIO_VAL_INT_PLUS_MICRO; 323 default: 324 return -EINVAL; 325 } 326 } 327 328 static const struct iio_info vcnl4000_info = { 329 .read_raw = vcnl4000_read_raw, 330 }; 331 332 static int vcnl4000_probe(struct i2c_client *client, 333 const struct i2c_device_id *id) 334 { 335 struct vcnl4000_data *data; 336 struct iio_dev *indio_dev; 337 int ret; 338 339 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 340 if (!indio_dev) 341 return -ENOMEM; 342 343 data = iio_priv(indio_dev); 344 i2c_set_clientdata(client, indio_dev); 345 data->client = client; 346 data->id = id->driver_data; 347 data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 348 349 ret = data->chip_spec->init(data); 350 if (ret < 0) 351 return ret; 352 353 dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 354 data->chip_spec->prod, data->rev); 355 356 indio_dev->dev.parent = &client->dev; 357 indio_dev->info = &vcnl4000_info; 358 indio_dev->channels = vcnl4000_channels; 359 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 360 indio_dev->name = VCNL4000_DRV_NAME; 361 indio_dev->modes = INDIO_DIRECT_MODE; 362 363 return devm_iio_device_register(&client->dev, indio_dev); 364 } 365 366 static struct i2c_driver vcnl4000_driver = { 367 .driver = { 368 .name = VCNL4000_DRV_NAME, 369 }, 370 .probe = vcnl4000_probe, 371 .id_table = vcnl4000_id, 372 }; 373 374 module_i2c_driver(vcnl4000_driver); 375 376 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 377 MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 378 MODULE_LICENSE("GPL"); 379