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