162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald/* 262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and 362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * proximity sensor 462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * 562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * 762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * This file is subject to the terms and conditions of version 2 of 862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * the GNU General Public License. See the file COPYING in the main 962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * directory of this archive for more details. 1062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * 1162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * IIO driver for VCNL4000 (7-bit I2C slave address 0x13) 1262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * 1362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * TODO: 1462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * allow to adjust IR current 1562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald * proximity threshold and event handling 1662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald */ 1762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 1862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/module.h> 1962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/i2c.h> 2062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/err.h> 2162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/delay.h> 2262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 2362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/iio/iio.h> 2462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#include <linux/iio/sysfs.h> 2562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 2662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_DRV_NAME "vcnl4000" 2762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 2862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_COMMAND 0x80 /* Command register */ 2962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 3062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 3162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 3262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 3362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 3462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 3562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 3662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 3762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 3862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 3962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald/* Bit masks for COMMAND register */ 4062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */ 4162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */ 4262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */ 4362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */ 4462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 4562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstruct vcnl4000_data { 4662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald struct i2c_client *client; 4762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald}; 4862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 4962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic const struct i2c_device_id vcnl4000_id[] = { 5062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald { "vcnl4000", 0 }, 5162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald { } 5262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald}; 5362a1efb9f868690d68b11ffb22dc598e547aa184Peter MeerwaldMODULE_DEVICE_TABLE(i2c, vcnl4000_id); 5462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 5562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 5662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald u8 rdy_mask, u8 data_reg, int *val) 5762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald{ 5862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald int tries = 20; 5991f197e0c0ceda88c442f7adce6a84e5faa53586Lars-Peter Clausen __be16 buf; 6062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald int ret; 6162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 6262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 6362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald req_mask); 6462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 6562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 6662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 6762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald /* wait for data to become ready */ 6862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald while (tries--) { 6962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 7062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 7162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 7262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret & rdy_mask) 7362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 7462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald msleep(20); /* measurement takes up to 100 ms */ 7562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 7662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 7762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (tries < 0) { 7862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald dev_err(&data->client->dev, 7962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald "vcnl4000_measure() failed, data not ready\n"); 8062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return -EIO; 8162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 8262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 8362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = i2c_smbus_read_i2c_block_data(data->client, 8462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald data_reg, sizeof(buf), (u8 *) &buf); 8562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 8662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 8762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 8862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald *val = be16_to_cpu(buf); 8962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 9062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return 0; 9162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald} 9262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 9362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic const struct iio_chan_spec vcnl4000_channels[] = { 9462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald { 9562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .type = IIO_LIGHT, 96bb7c5940248ac53fdb6c3684bbfc20f8a26f1acdJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 97bb7c5940248ac53fdb6c3684bbfc20f8a26f1acdJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), 9862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald }, { 9962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .type = IIO_PROXIMITY, 100bb7c5940248ac53fdb6c3684bbfc20f8a26f1acdJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 10162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 10262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald}; 10362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 10462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic int vcnl4000_read_raw(struct iio_dev *indio_dev, 10562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald struct iio_chan_spec const *chan, 10662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald int *val, int *val2, long mask) 10762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald{ 10862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald int ret = -EINVAL; 10962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 11062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 11162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald switch (mask) { 11262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald case IIO_CHAN_INFO_RAW: 11362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald switch (chan->type) { 11462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald case IIO_LIGHT: 11562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = vcnl4000_measure(data, 11662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald VCNL4000_AL_OD, VCNL4000_AL_RDY, 11762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald VCNL4000_AL_RESULT_HI, val); 11862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 11962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 12062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = IIO_VAL_INT; 12162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 12262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald case IIO_PROXIMITY: 12362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = vcnl4000_measure(data, 12462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald VCNL4000_PS_OD, VCNL4000_PS_RDY, 12562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald VCNL4000_PS_RESULT_HI, val); 12662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 12762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 12862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = IIO_VAL_INT; 12962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 13062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald default: 13162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 13262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 13362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 13462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald case IIO_CHAN_INFO_SCALE: 13562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (chan->type == IIO_LIGHT) { 13662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald *val = 0; 13762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald *val2 = 250000; 13862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = IIO_VAL_INT_PLUS_MICRO; 13962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 14062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 14162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald default: 14262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald break; 14362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald } 14462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 14562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return ret; 14662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald} 14762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 14862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic const struct iio_info vcnl4000_info = { 14962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .read_raw = vcnl4000_read_raw, 15062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .driver_module = THIS_MODULE, 15162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald}; 15262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 153fc52692c49969ec72595766929b9f54ac402da34Greg Kroah-Hartmanstatic int vcnl4000_probe(struct i2c_client *client, 154fc52692c49969ec72595766929b9f54ac402da34Greg Kroah-Hartman const struct i2c_device_id *id) 15562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald{ 15662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald struct vcnl4000_data *data; 15762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald struct iio_dev *indio_dev; 15862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald int ret; 15962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 1602669d723ac6a01857273848a090c747f044a62d3Peter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 16162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (!indio_dev) 16262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald return -ENOMEM; 16362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 16462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald data = iio_priv(indio_dev); 16562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald i2c_set_clientdata(client, indio_dev); 16662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald data->client = client; 16762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 16862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 16962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald if (ret < 0) 1702669d723ac6a01857273848a090c747f044a62d3Peter Meerwald return ret; 17162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 17262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n", 17362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald ret >> 4, ret & 0xf); 17462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 17562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->dev.parent = &client->dev; 17662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->info = &vcnl4000_info; 17762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->channels = vcnl4000_channels; 17862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 17962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 18062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 18162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 182691dab4291f5df64e08d49529e3eafc481811555Sachin Kamat return devm_iio_device_register(&client->dev, indio_dev); 18362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald} 18462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 18562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldstatic struct i2c_driver vcnl4000_driver = { 18662a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .driver = { 18762a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .name = VCNL4000_DRV_NAME, 18862a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .owner = THIS_MODULE, 18962a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald }, 19062a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .probe = vcnl4000_probe, 19162a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald .id_table = vcnl4000_id, 19262a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald}; 19362a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 19462a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwaldmodule_i2c_driver(vcnl4000_driver); 19562a1efb9f868690d68b11ffb22dc598e547aa184Peter Meerwald 19662a1efb9f868690d68b11ffb22dc598e547aa184Peter MeerwaldMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 19762a1efb9f868690d68b11ffb22dc598e547aa184Peter MeerwaldMODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 19862a1efb9f868690d68b11ffb22dc598e547aa184Peter MeerwaldMODULE_LICENSE("GPL"); 199