mirror of https://gitee.com/openkylin/linux.git
iio: vcnl4000: make the driver extendable
There are similar chips in the vcnl4xxx family. The initialization and communication is a bit different for members of the family, so this patch makes the driver extendable for different chips. There is no functional change. Signed-off-by: Tomas Novotny <tomas@novotny.cz> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
parent
cebc4585f6
commit
1ebc787ae5
|
@ -26,8 +26,8 @@
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
|
|
||||||
#define VCNL4000_DRV_NAME "vcnl4000"
|
#define VCNL4000_DRV_NAME "vcnl4000"
|
||||||
#define VCNL4000_ID 0x01
|
#define VCNL4000_PROD_ID 0x01
|
||||||
#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
|
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
|
||||||
|
|
||||||
#define VCNL4000_COMMAND 0x80 /* Command register */
|
#define VCNL4000_COMMAND 0x80 /* Command register */
|
||||||
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
|
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
|
||||||
|
@ -46,17 +46,50 @@
|
||||||
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
||||||
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
||||||
|
|
||||||
|
enum vcnl4000_device_ids {
|
||||||
|
VCNL4000,
|
||||||
|
};
|
||||||
|
|
||||||
struct vcnl4000_data {
|
struct vcnl4000_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
enum vcnl4000_device_ids id;
|
||||||
|
int rev;
|
||||||
|
int al_scale;
|
||||||
|
const struct vcnl4000_chip_spec *chip_spec;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vcnl4000_chip_spec {
|
||||||
|
const char *prod;
|
||||||
|
int (*init)(struct vcnl4000_data *data);
|
||||||
|
int (*measure_light)(struct vcnl4000_data *data, int *val);
|
||||||
|
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
|
||||||
|
};
|
||||||
|
|
||||||
static const struct i2c_device_id vcnl4000_id[] = {
|
static const struct i2c_device_id vcnl4000_id[] = {
|
||||||
{ "vcnl4000", 0 },
|
{ "vcnl4000", VCNL4000 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
|
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
|
||||||
|
|
||||||
|
static int vcnl4000_init(struct vcnl4000_data *data)
|
||||||
|
{
|
||||||
|
int ret, prod_id;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
prod_id = ret >> 4;
|
||||||
|
if (prod_id != VCNL4010_PROD_ID && prod_id != VCNL4000_PROD_ID)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
data->rev = ret & 0xf;
|
||||||
|
data->al_scale = 250000;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
||||||
u8 rdy_mask, u8 data_reg, int *val)
|
u8 rdy_mask, u8 data_reg, int *val)
|
||||||
{
|
{
|
||||||
|
@ -103,6 +136,29 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
|
||||||
|
{
|
||||||
|
return vcnl4000_measure(data,
|
||||||
|
VCNL4000_AL_OD, VCNL4000_AL_RDY,
|
||||||
|
VCNL4000_AL_RESULT_HI, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
|
||||||
|
{
|
||||||
|
return vcnl4000_measure(data,
|
||||||
|
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
||||||
|
VCNL4000_PS_RESULT_HI, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
|
||||||
|
[VCNL4000] = {
|
||||||
|
.prod = "VCNL4000",
|
||||||
|
.init = vcnl4000_init,
|
||||||
|
.measure_light = vcnl4000_measure_light,
|
||||||
|
.measure_proximity = vcnl4000_measure_proximity,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_chan_spec vcnl4000_channels[] = {
|
static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||||
{
|
{
|
||||||
.type = IIO_LIGHT,
|
.type = IIO_LIGHT,
|
||||||
|
@ -125,16 +181,12 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_LIGHT:
|
case IIO_LIGHT:
|
||||||
ret = vcnl4000_measure(data,
|
ret = data->chip_spec->measure_light(data, val);
|
||||||
VCNL4000_AL_OD, VCNL4000_AL_RDY,
|
|
||||||
VCNL4000_AL_RESULT_HI, val);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_PROXIMITY:
|
case IIO_PROXIMITY:
|
||||||
ret = vcnl4000_measure(data,
|
ret = data->chip_spec->measure_proximity(data, val);
|
||||||
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
|
||||||
VCNL4000_PS_RESULT_HI, val);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
|
@ -146,7 +198,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
*val = 0;
|
*val = 0;
|
||||||
*val2 = 250000;
|
*val2 = data->al_scale;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -162,7 +214,7 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||||
{
|
{
|
||||||
struct vcnl4000_data *data;
|
struct vcnl4000_data *data;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
int ret, prod_id;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
|
@ -171,19 +223,16 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||||
data = iio_priv(indio_dev);
|
data = iio_priv(indio_dev);
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
data->id = id->driver_data;
|
||||||
|
data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
|
||||||
mutex_init(&data->lock);
|
mutex_init(&data->lock);
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
ret = data->chip_spec->init(data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
prod_id = ret >> 4;
|
|
||||||
if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
|
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
|
||||||
(prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
|
data->chip_spec->prod, data->rev);
|
||||||
ret & 0xf);
|
|
||||||
|
|
||||||
indio_dev->dev.parent = &client->dev;
|
indio_dev->dev.parent = &client->dev;
|
||||||
indio_dev->info = &vcnl4000_info;
|
indio_dev->info = &vcnl4000_info;
|
||||||
|
|
Loading…
Reference in New Issue