]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iio: light: opt3001: add support for TI's opt3002 light sensor
authorEmil Gedenryd <emil.gedenryd@axis.com>
Thu, 3 Oct 2024 12:22:17 +0000 (14:22 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Mon, 28 Oct 2024 20:04:10 +0000 (20:04 +0000)
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>
drivers/iio/light/Kconfig
drivers/iio/light/opt3001.c

index c38b3c603ab405d959dd9bf56fab41cdc1f42bb3..39c0e08a8e069ac668f906a540a91d7f8c32d8ca 100644 (file)
@@ -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.
index 176e54bb48c33b2445ecfd80351b3953fd610ee9..ff7fc0d4b08f9905bd6514d5f1c64b5923ec86cb 100644 (file)
 #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);