162a1efb9SPeter Meerwald /* 2d978bfddSPeter Meerwald-Stadler * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient 3d978bfddSPeter Meerwald-Stadler * light and proximity sensor 462a1efb9SPeter Meerwald * 562a1efb9SPeter Meerwald * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 662a1efb9SPeter Meerwald * 762a1efb9SPeter Meerwald * This file is subject to the terms and conditions of version 2 of 862a1efb9SPeter Meerwald * the GNU General Public License. See the file COPYING in the main 962a1efb9SPeter Meerwald * directory of this archive for more details. 1062a1efb9SPeter Meerwald * 1162a1efb9SPeter Meerwald * IIO driver for VCNL4000 (7-bit I2C slave address 0x13) 1262a1efb9SPeter Meerwald * 1362a1efb9SPeter Meerwald * TODO: 1462a1efb9SPeter Meerwald * allow to adjust IR current 1562a1efb9SPeter Meerwald * proximity threshold and event handling 16d978bfddSPeter Meerwald-Stadler * periodic ALS/proximity measurement (VCNL4010/20) 17d978bfddSPeter Meerwald-Stadler * interrupts (VCNL4010/20) 1862a1efb9SPeter Meerwald */ 1962a1efb9SPeter Meerwald 2062a1efb9SPeter Meerwald #include <linux/module.h> 2162a1efb9SPeter Meerwald #include <linux/i2c.h> 2262a1efb9SPeter Meerwald #include <linux/err.h> 2362a1efb9SPeter Meerwald #include <linux/delay.h> 2462a1efb9SPeter Meerwald 2562a1efb9SPeter Meerwald #include <linux/iio/iio.h> 2662a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 2762a1efb9SPeter Meerwald 2862a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 29d978bfddSPeter Meerwald-Stadler #define VCNL4000_ID 0x01 30d978bfddSPeter Meerwald-Stadler #define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */ 3162a1efb9SPeter Meerwald 3262a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 3362a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 3462a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 3562a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 3662a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 3762a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 3862a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 3962a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 4062a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 4162a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 4262a1efb9SPeter Meerwald 4362a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 44ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 45ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 46ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 47ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 4862a1efb9SPeter Meerwald 4962a1efb9SPeter Meerwald struct vcnl4000_data { 5062a1efb9SPeter Meerwald struct i2c_client *client; 5162a1efb9SPeter Meerwald }; 5262a1efb9SPeter Meerwald 5362a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 5462a1efb9SPeter Meerwald { "vcnl4000", 0 }, 5562a1efb9SPeter Meerwald { } 5662a1efb9SPeter Meerwald }; 5762a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 5862a1efb9SPeter Meerwald 5962a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 6062a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 6162a1efb9SPeter Meerwald { 6262a1efb9SPeter Meerwald int tries = 20; 6391f197e0SLars-Peter Clausen __be16 buf; 6462a1efb9SPeter Meerwald int ret; 6562a1efb9SPeter Meerwald 6662a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 6762a1efb9SPeter Meerwald req_mask); 6862a1efb9SPeter Meerwald if (ret < 0) 6962a1efb9SPeter Meerwald return ret; 7062a1efb9SPeter Meerwald 7162a1efb9SPeter Meerwald /* wait for data to become ready */ 7262a1efb9SPeter Meerwald while (tries--) { 7362a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 7462a1efb9SPeter Meerwald if (ret < 0) 7562a1efb9SPeter Meerwald return ret; 7662a1efb9SPeter Meerwald if (ret & rdy_mask) 7762a1efb9SPeter Meerwald break; 7862a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 7962a1efb9SPeter Meerwald } 8062a1efb9SPeter Meerwald 8162a1efb9SPeter Meerwald if (tries < 0) { 8262a1efb9SPeter Meerwald dev_err(&data->client->dev, 8362a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 8462a1efb9SPeter Meerwald return -EIO; 8562a1efb9SPeter Meerwald } 8662a1efb9SPeter Meerwald 8762a1efb9SPeter Meerwald ret = i2c_smbus_read_i2c_block_data(data->client, 8862a1efb9SPeter Meerwald data_reg, sizeof(buf), (u8 *) &buf); 8962a1efb9SPeter Meerwald if (ret < 0) 9062a1efb9SPeter Meerwald return ret; 9162a1efb9SPeter Meerwald 9262a1efb9SPeter Meerwald *val = be16_to_cpu(buf); 9362a1efb9SPeter Meerwald 9462a1efb9SPeter Meerwald return 0; 9562a1efb9SPeter Meerwald } 9662a1efb9SPeter Meerwald 9762a1efb9SPeter Meerwald static const struct iio_chan_spec vcnl4000_channels[] = { 9862a1efb9SPeter Meerwald { 9962a1efb9SPeter Meerwald .type = IIO_LIGHT, 100bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 101bb7c5940SJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), 10262a1efb9SPeter Meerwald }, { 10362a1efb9SPeter Meerwald .type = IIO_PROXIMITY, 104bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 10562a1efb9SPeter Meerwald } 10662a1efb9SPeter Meerwald }; 10762a1efb9SPeter Meerwald 10862a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 10962a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 11062a1efb9SPeter Meerwald int *val, int *val2, long mask) 11162a1efb9SPeter Meerwald { 112*5d693139SPeter Meerwald-Stadler int ret; 11362a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 11462a1efb9SPeter Meerwald 11562a1efb9SPeter Meerwald switch (mask) { 11662a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 11762a1efb9SPeter Meerwald switch (chan->type) { 11862a1efb9SPeter Meerwald case IIO_LIGHT: 11962a1efb9SPeter Meerwald ret = vcnl4000_measure(data, 12062a1efb9SPeter Meerwald VCNL4000_AL_OD, VCNL4000_AL_RDY, 12162a1efb9SPeter Meerwald VCNL4000_AL_RESULT_HI, val); 12262a1efb9SPeter Meerwald if (ret < 0) 12362a1efb9SPeter Meerwald return ret; 124*5d693139SPeter Meerwald-Stadler return IIO_VAL_INT; 12562a1efb9SPeter Meerwald case IIO_PROXIMITY: 12662a1efb9SPeter Meerwald ret = vcnl4000_measure(data, 12762a1efb9SPeter Meerwald VCNL4000_PS_OD, VCNL4000_PS_RDY, 12862a1efb9SPeter Meerwald VCNL4000_PS_RESULT_HI, val); 12962a1efb9SPeter Meerwald if (ret < 0) 13062a1efb9SPeter Meerwald return ret; 131*5d693139SPeter Meerwald-Stadler return IIO_VAL_INT; 13262a1efb9SPeter Meerwald default: 133*5d693139SPeter Meerwald-Stadler return -EINVAL; 13462a1efb9SPeter Meerwald } 13562a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 136*5d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 137*5d693139SPeter Meerwald-Stadler return -EINVAL; 138*5d693139SPeter Meerwald-Stadler 13962a1efb9SPeter Meerwald *val = 0; 14062a1efb9SPeter Meerwald *val2 = 250000; 141*5d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 14262a1efb9SPeter Meerwald default: 143*5d693139SPeter Meerwald-Stadler return -EINVAL; 14462a1efb9SPeter Meerwald } 14562a1efb9SPeter Meerwald } 14662a1efb9SPeter Meerwald 14762a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 14862a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 14962a1efb9SPeter Meerwald .driver_module = THIS_MODULE, 15062a1efb9SPeter Meerwald }; 15162a1efb9SPeter Meerwald 152fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 15362a1efb9SPeter Meerwald const struct i2c_device_id *id) 15462a1efb9SPeter Meerwald { 15562a1efb9SPeter Meerwald struct vcnl4000_data *data; 15662a1efb9SPeter Meerwald struct iio_dev *indio_dev; 157d978bfddSPeter Meerwald-Stadler int ret, prod_id; 15862a1efb9SPeter Meerwald 1592669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 16062a1efb9SPeter Meerwald if (!indio_dev) 16162a1efb9SPeter Meerwald return -ENOMEM; 16262a1efb9SPeter Meerwald 16362a1efb9SPeter Meerwald data = iio_priv(indio_dev); 16462a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 16562a1efb9SPeter Meerwald data->client = client; 16662a1efb9SPeter Meerwald 16762a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 16862a1efb9SPeter Meerwald if (ret < 0) 1692669d723SPeter Meerwald return ret; 17062a1efb9SPeter Meerwald 171d978bfddSPeter Meerwald-Stadler prod_id = ret >> 4; 172d978bfddSPeter Meerwald-Stadler if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID) 173d978bfddSPeter Meerwald-Stadler return -ENODEV; 174d978bfddSPeter Meerwald-Stadler 175d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 176d978bfddSPeter Meerwald-Stadler (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000", 177d978bfddSPeter Meerwald-Stadler ret & 0xf); 17862a1efb9SPeter Meerwald 17962a1efb9SPeter Meerwald indio_dev->dev.parent = &client->dev; 18062a1efb9SPeter Meerwald indio_dev->info = &vcnl4000_info; 18162a1efb9SPeter Meerwald indio_dev->channels = vcnl4000_channels; 18262a1efb9SPeter Meerwald indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 18362a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 18462a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 18562a1efb9SPeter Meerwald 186691dab42SSachin Kamat return devm_iio_device_register(&client->dev, indio_dev); 18762a1efb9SPeter Meerwald } 18862a1efb9SPeter Meerwald 18962a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 19062a1efb9SPeter Meerwald .driver = { 19162a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 19262a1efb9SPeter Meerwald }, 19362a1efb9SPeter Meerwald .probe = vcnl4000_probe, 19462a1efb9SPeter Meerwald .id_table = vcnl4000_id, 19562a1efb9SPeter Meerwald }; 19662a1efb9SPeter Meerwald 19762a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 19862a1efb9SPeter Meerwald 19962a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 20062a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 20162a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 202