1 /* 2 * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 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 VCNL4000 (7-bit I2C slave address 0x13) 12 * 13 * TODO: 14 * allow to adjust IR current 15 * proximity threshold and event handling 16 * periodic ALS/proximity measurement (VCNL4010/20) 17 * interrupts (VCNL4010/20) 18 */ 19 20 #include <linux/module.h> 21 #include <linux/i2c.h> 22 #include <linux/err.h> 23 #include <linux/delay.h> 24 25 #include <linux/iio/iio.h> 26 #include <linux/iio/sysfs.h> 27 28 #define VCNL4000_DRV_NAME "vcnl4000" 29 #define VCNL4000_ID 0x01 30 #define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */ 31 32 #define VCNL4000_COMMAND 0x80 /* Command register */ 33 #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 34 #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 35 #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 36 #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 37 #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 38 #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 39 #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 40 #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 41 #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 42 43 /* Bit masks for COMMAND register */ 44 #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 45 #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 46 #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 47 #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 48 49 struct vcnl4000_data { 50 struct i2c_client *client; 51 struct mutex lock; 52 }; 53 54 static const struct i2c_device_id vcnl4000_id[] = { 55 { "vcnl4000", 0 }, 56 { } 57 }; 58 MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 59 60 static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 61 u8 rdy_mask, u8 data_reg, int *val) 62 { 63 int tries = 20; 64 __be16 buf; 65 int ret; 66 67 mutex_lock(&data->lock); 68 69 ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 70 req_mask); 71 if (ret < 0) 72 goto fail; 73 74 /* wait for data to become ready */ 75 while (tries--) { 76 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 77 if (ret < 0) 78 goto fail; 79 if (ret & rdy_mask) 80 break; 81 msleep(20); /* measurement takes up to 100 ms */ 82 } 83 84 if (tries < 0) { 85 dev_err(&data->client->dev, 86 "vcnl4000_measure() failed, data not ready\n"); 87 ret = -EIO; 88 goto fail; 89 } 90 91 ret = i2c_smbus_read_i2c_block_data(data->client, 92 data_reg, sizeof(buf), (u8 *) &buf); 93 if (ret < 0) 94 goto fail; 95 96 mutex_unlock(&data->lock); 97 *val = be16_to_cpu(buf); 98 99 return 0; 100 101 fail: 102 mutex_unlock(&data->lock); 103 return ret; 104 } 105 106 static const struct iio_chan_spec vcnl4000_channels[] = { 107 { 108 .type = IIO_LIGHT, 109 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 110 BIT(IIO_CHAN_INFO_SCALE), 111 }, { 112 .type = IIO_PROXIMITY, 113 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 114 } 115 }; 116 117 static int vcnl4000_read_raw(struct iio_dev *indio_dev, 118 struct iio_chan_spec const *chan, 119 int *val, int *val2, long mask) 120 { 121 int ret; 122 struct vcnl4000_data *data = iio_priv(indio_dev); 123 124 switch (mask) { 125 case IIO_CHAN_INFO_RAW: 126 switch (chan->type) { 127 case IIO_LIGHT: 128 ret = vcnl4000_measure(data, 129 VCNL4000_AL_OD, VCNL4000_AL_RDY, 130 VCNL4000_AL_RESULT_HI, val); 131 if (ret < 0) 132 return ret; 133 return IIO_VAL_INT; 134 case IIO_PROXIMITY: 135 ret = vcnl4000_measure(data, 136 VCNL4000_PS_OD, VCNL4000_PS_RDY, 137 VCNL4000_PS_RESULT_HI, val); 138 if (ret < 0) 139 return ret; 140 return IIO_VAL_INT; 141 default: 142 return -EINVAL; 143 } 144 case IIO_CHAN_INFO_SCALE: 145 if (chan->type != IIO_LIGHT) 146 return -EINVAL; 147 148 *val = 0; 149 *val2 = 250000; 150 return IIO_VAL_INT_PLUS_MICRO; 151 default: 152 return -EINVAL; 153 } 154 } 155 156 static const struct iio_info vcnl4000_info = { 157 .read_raw = vcnl4000_read_raw, 158 .driver_module = THIS_MODULE, 159 }; 160 161 static int vcnl4000_probe(struct i2c_client *client, 162 const struct i2c_device_id *id) 163 { 164 struct vcnl4000_data *data; 165 struct iio_dev *indio_dev; 166 int ret, prod_id; 167 168 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 169 if (!indio_dev) 170 return -ENOMEM; 171 172 data = iio_priv(indio_dev); 173 i2c_set_clientdata(client, indio_dev); 174 data->client = client; 175 mutex_init(&data->lock); 176 177 ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 178 if (ret < 0) 179 return ret; 180 181 prod_id = ret >> 4; 182 if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID) 183 return -ENODEV; 184 185 dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 186 (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000", 187 ret & 0xf); 188 189 indio_dev->dev.parent = &client->dev; 190 indio_dev->info = &vcnl4000_info; 191 indio_dev->channels = vcnl4000_channels; 192 indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 193 indio_dev->name = VCNL4000_DRV_NAME; 194 indio_dev->modes = INDIO_DIRECT_MODE; 195 196 return devm_iio_device_register(&client->dev, indio_dev); 197 } 198 199 static struct i2c_driver vcnl4000_driver = { 200 .driver = { 201 .name = VCNL4000_DRV_NAME, 202 }, 203 .probe = vcnl4000_probe, 204 .id_table = vcnl4000_id, 205 }; 206 207 module_i2c_driver(vcnl4000_driver); 208 209 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 210 MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 211 MODULE_LICENSE("GPL"); 212