iio: light: opt3001: add support for TI's opt3002 light sensor
TI's opt3002 light sensor shares most properties with the opt3001 model, with the exception of supporting a wider spectrum range. Add support for TI's opt3002 by extending the TI opt3001 driver. Datasheet: https://www.ti.com/product/OPT3002 Signed-off-by: Emil Gedenryd <emil.gedenryd@axis.com> Link: https://patch.msgid.link/20241003-add_opt3002-v4-2-c550dc4591b4@axis.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
committed by
Jonathan Cameron
parent
0d7fd2d6aa
commit
fc6fa04ef3
@@ -476,7 +476,7 @@ config OPT3001
|
||||
depends on I2C
|
||||
help
|
||||
If you say Y or M here, you get support for Texas Instruments
|
||||
OPT3001 Ambient Light Sensor.
|
||||
OPT3001 Ambient Light Sensor, OPT3002 Light-to-Digital Sensor.
|
||||
|
||||
If built as a dynamically linked module, it will be called
|
||||
opt3001.
|
||||
|
||||
@@ -70,6 +70,35 @@
|
||||
#define OPT3001_RESULT_READY_SHORT 150
|
||||
#define OPT3001_RESULT_READY_LONG 1000
|
||||
|
||||
struct opt3001_scale {
|
||||
int val;
|
||||
int val2;
|
||||
};
|
||||
|
||||
struct opt3001_chip_info {
|
||||
const struct iio_chan_spec (*channels)[2];
|
||||
enum iio_chan_type chan_type;
|
||||
int num_channels;
|
||||
|
||||
const struct opt3001_scale (*scales)[12];
|
||||
/*
|
||||
* Factor as specified by conversion equation in datasheet.
|
||||
* eg. 0.01 (scaled to integer 10) for opt3001.
|
||||
*/
|
||||
int factor_whole;
|
||||
/*
|
||||
* Factor to compensate for potentially scaled factor_whole.
|
||||
*/
|
||||
int factor_integer;
|
||||
/*
|
||||
* Factor used to align decimal part of proccessed value to six decimal
|
||||
* places.
|
||||
*/
|
||||
int factor_decimal;
|
||||
|
||||
bool has_id;
|
||||
};
|
||||
|
||||
struct opt3001 {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
@@ -79,6 +108,7 @@ struct opt3001 {
|
||||
bool result_ready;
|
||||
wait_queue_head_t result_ready_queue;
|
||||
u16 result;
|
||||
const struct opt3001_chip_info *chip_info;
|
||||
|
||||
u32 int_time;
|
||||
u32 mode;
|
||||
@@ -92,11 +122,6 @@ struct opt3001 {
|
||||
bool use_irq;
|
||||
};
|
||||
|
||||
struct opt3001_scale {
|
||||
int val;
|
||||
int val2;
|
||||
};
|
||||
|
||||
static const struct opt3001_scale opt3001_scales[] = {
|
||||
{
|
||||
.val = 40,
|
||||
@@ -148,21 +173,68 @@ static const struct opt3001_scale opt3001_scales[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct opt3001_scale opt3002_scales[] = {
|
||||
{
|
||||
.val = 4914,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 9828,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 19656,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 39312,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 78624,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 157248,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 314496,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 628992,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 1257984,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 2515968,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 5031936,
|
||||
.val2 = 0,
|
||||
},
|
||||
{
|
||||
.val = 10063872,
|
||||
.val2 = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static int opt3001_find_scale(const struct opt3001 *opt, int val,
|
||||
int val2, u8 *exponent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(opt3001_scales); i++) {
|
||||
const struct opt3001_scale *scale = &opt3001_scales[i];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) {
|
||||
const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i];
|
||||
/*
|
||||
* Combine the integer and micro parts for comparison
|
||||
* purposes. Use milli lux precision to avoid 32-bit integer
|
||||
* overflows.
|
||||
* Compare the integer and micro parts to determine value scale.
|
||||
*/
|
||||
if ((val * 1000 + val2 / 1000) <=
|
||||
(scale->val * 1000 + scale->val2 / 1000)) {
|
||||
if (val < scale->val ||
|
||||
(val == scale->val && val2 <= scale->val2)) {
|
||||
*exponent = i;
|
||||
return 0;
|
||||
}
|
||||
@@ -174,11 +246,14 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val,
|
||||
static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent,
|
||||
u16 mantissa, int *val, int *val2)
|
||||
{
|
||||
int lux;
|
||||
int ret;
|
||||
int whole = opt->chip_info->factor_whole;
|
||||
int integer = opt->chip_info->factor_integer;
|
||||
int decimal = opt->chip_info->factor_decimal;
|
||||
|
||||
lux = 10 * (mantissa << exponent);
|
||||
*val = lux / 1000;
|
||||
*val2 = (lux - (*val * 1000)) * 1000;
|
||||
ret = whole * (mantissa << exponent);
|
||||
*val = ret / integer;
|
||||
*val2 = (ret - (*val * integer)) * decimal;
|
||||
}
|
||||
|
||||
static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode)
|
||||
@@ -225,7 +300,18 @@ static const struct iio_chan_spec opt3001_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
|
||||
static const struct iio_chan_spec opt3002_channels[] = {
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.event_spec = opt3001_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(opt3001_event_spec),
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
u16 mantissa;
|
||||
@@ -397,14 +483,15 @@ static int opt3001_read_raw(struct iio_dev *iio,
|
||||
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
|
||||
return -EBUSY;
|
||||
|
||||
if (chan->type != IIO_LIGHT)
|
||||
if (chan->type != opt->chip_info->chan_type)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&opt->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = opt3001_get_lux(opt, val, val2);
|
||||
ret = opt3001_get_processed(opt, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
ret = opt3001_get_int_time(opt, val, val2);
|
||||
@@ -428,7 +515,7 @@ static int opt3001_write_raw(struct iio_dev *iio,
|
||||
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
|
||||
return -EBUSY;
|
||||
|
||||
if (chan->type != IIO_LIGHT)
|
||||
if (chan->type != opt->chip_info->chan_type)
|
||||
return -EINVAL;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_INT_TIME)
|
||||
@@ -479,6 +566,9 @@ static int opt3001_write_event_value(struct iio_dev *iio,
|
||||
{
|
||||
struct opt3001 *opt = iio_priv(iio);
|
||||
int ret;
|
||||
int whole;
|
||||
int integer;
|
||||
int decimal;
|
||||
|
||||
u16 mantissa;
|
||||
u16 value;
|
||||
@@ -497,7 +587,12 @@ static int opt3001_write_event_value(struct iio_dev *iio,
|
||||
goto err;
|
||||
}
|
||||
|
||||
mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
|
||||
whole = opt->chip_info->factor_whole;
|
||||
integer = opt->chip_info->factor_integer;
|
||||
decimal = opt->chip_info->factor_decimal;
|
||||
|
||||
mantissa = (((val * integer) + (val2 / decimal)) / whole) >> exponent;
|
||||
|
||||
value = (exponent << 12) | mantissa;
|
||||
|
||||
switch (dir) {
|
||||
@@ -610,7 +705,7 @@ static int opt3001_read_id(struct opt3001 *opt)
|
||||
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to read register %02x\n",
|
||||
OPT3001_DEVICE_ID);
|
||||
OPT3001_DEVICE_ID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -692,6 +787,7 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
|
||||
struct opt3001 *opt = iio_priv(iio);
|
||||
int ret;
|
||||
bool wake_result_ready_queue = false;
|
||||
enum iio_chan_type chan_type = opt->chip_info->chan_type;
|
||||
|
||||
if (!opt->ok_to_ignore_lock)
|
||||
mutex_lock(&opt->lock);
|
||||
@@ -707,13 +803,13 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
|
||||
OPT3001_CONFIGURATION_M_CONTINUOUS) {
|
||||
if (ret & OPT3001_CONFIGURATION_FH)
|
||||
iio_push_event(iio,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_UNMOD_EVENT_CODE(chan_type, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns(iio));
|
||||
if (ret & OPT3001_CONFIGURATION_FL)
|
||||
iio_push_event(iio,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_UNMOD_EVENT_CODE(chan_type, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns(iio));
|
||||
@@ -755,22 +851,25 @@ static int opt3001_probe(struct i2c_client *client)
|
||||
opt = iio_priv(iio);
|
||||
opt->client = client;
|
||||
opt->dev = dev;
|
||||
opt->chip_info = i2c_get_match_data(client);
|
||||
|
||||
mutex_init(&opt->lock);
|
||||
init_waitqueue_head(&opt->result_ready_queue);
|
||||
i2c_set_clientdata(client, iio);
|
||||
|
||||
ret = opt3001_read_id(opt);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (opt->chip_info->has_id) {
|
||||
ret = opt3001_read_id(opt);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = opt3001_configure(opt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iio->name = client->name;
|
||||
iio->channels = opt3001_channels;
|
||||
iio->num_channels = ARRAY_SIZE(opt3001_channels);
|
||||
iio->channels = *opt->chip_info->channels;
|
||||
iio->num_channels = opt->chip_info->num_channels;
|
||||
iio->modes = INDIO_DIRECT_MODE;
|
||||
iio->info = &opt3001_info;
|
||||
|
||||
@@ -825,14 +924,38 @@ static void opt3001_remove(struct i2c_client *client)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct opt3001_chip_info opt3001_chip_information = {
|
||||
.channels = &opt3001_channels,
|
||||
.chan_type = IIO_LIGHT,
|
||||
.num_channels = ARRAY_SIZE(opt3001_channels),
|
||||
.scales = &opt3001_scales,
|
||||
.factor_whole = 10,
|
||||
.factor_integer = 1000,
|
||||
.factor_decimal = 1000,
|
||||
.has_id = true,
|
||||
};
|
||||
|
||||
static const struct opt3001_chip_info opt3002_chip_information = {
|
||||
.channels = &opt3002_channels,
|
||||
.chan_type = IIO_INTENSITY,
|
||||
.num_channels = ARRAY_SIZE(opt3002_channels),
|
||||
.scales = &opt3002_scales,
|
||||
.factor_whole = 12,
|
||||
.factor_integer = 10,
|
||||
.factor_decimal = 100000,
|
||||
.has_id = false,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id opt3001_id[] = {
|
||||
{ "opt3001" },
|
||||
{ "opt3001", (kernel_ulong_t)&opt3001_chip_information },
|
||||
{ "opt3002", (kernel_ulong_t)&opt3002_chip_information },
|
||||
{ } /* Terminating Entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, opt3001_id);
|
||||
|
||||
static const struct of_device_id opt3001_of_match[] = {
|
||||
{ .compatible = "ti,opt3001" },
|
||||
{ .compatible = "ti,opt3001", .data = &opt3001_chip_information },
|
||||
{ .compatible = "ti,opt3002", .data = &opt3002_chip_information },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opt3001_of_match);
|
||||
|
||||
Reference in New Issue
Block a user