root/drivers/iio/light/ltrf216a.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * LTRF216A Ambient Light Sensor
 *
 * Copyright (C) 2022 Collabora, Ltd.
 * Author: Shreeya Patel <shreeya.patel@collabora.com>
 *
 * Copyright (C) 2021 Lite-On Technology Corp (Singapore)
 * Author: Shi Zhigang <Zhigang.Shi@liteon.com>
 *
 * IIO driver for LTRF216A (7-bit I2C slave address 0x53).
 */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>

#include <linux/iio/iio.h>

#include <linux/unaligned.h>

#define LTRF216A_ALS_RESET_MASK         BIT(4)
#define LTRF216A_ALS_DATA_STATUS        BIT(3)
#define LTRF216A_ALS_ENABLE_MASK        BIT(1)
#define LTRF216A_MAIN_CTRL              0x00
#define LTRF216A_ALS_MEAS_RES           0x04
#define LTRF216A_ALS_GAIN               0x05
#define LTRF216A_PART_ID                0x06
#define LTRF216A_MAIN_STATUS            0x07
#define LTRF216A_ALS_CLEAR_DATA_0       0x0a
#define LTRF216A_ALS_CLEAR_DATA_1       0x0b
#define LTRF216A_ALS_CLEAR_DATA_2       0x0c
#define LTRF216A_ALS_DATA_0             0x0d
#define LTRF216A_ALS_DATA_1             0x0e
#define LTRF216A_ALS_DATA_2             0x0f
#define LTRF216A_INT_CFG                0x19
#define LTRF216A_INT_PST                0x1a
#define LTRF216A_ALS_THRES_UP_0         0x21
#define LTRF216A_ALS_THRES_UP_1         0x22
#define LTRF216A_ALS_THRES_UP_2         0x23
#define LTRF216A_ALS_THRES_LOW_0        0x24
#define LTRF216A_ALS_THRES_LOW_1        0x25
#define LTRF216A_ALS_THRES_LOW_2        0x26
#define LTRF216A_ALS_READ_DATA_DELAY_US 20000

static const int ltrf216a_int_time_available[][2] = {
        { 0, 400000 },
        { 0, 200000 },
        { 0, 100000 },
        { 0,  50000 },
        { 0,  25000 },
};

static const int ltrf216a_int_time_reg[][2] = {
        { 400, 0x03 },
        { 200, 0x13 },
        { 100, 0x22 },
        {  50, 0x31 },
        {  25, 0x40 },
};

struct ltr_chip_info {
        /* Chip contains CLEAR_DATA_0/1/2 registers at offset 0xa..0xc */
        bool            has_clear_data;
        /* Lux calculation multiplier for ALS data */
        int             lux_multiplier;
};

/*
 * Window Factor is needed when the device is under Window glass
 * with coated tinted ink. This is to compensate for the light loss
 * due to the lower transmission rate of the window glass and helps
 * in calculating lux.
 */
#define LTRF216A_WIN_FAC        1

struct ltrf216a_data {
        struct regmap *regmap;
        struct i2c_client *client;
        const struct ltr_chip_info *info;
        u32 int_time;
        u16 int_time_fac;
        u8 als_gain_fac;
        /*
         * Protects regmap accesses and makes sure integration time
         * remains constant during the measurement of lux.
         */
        struct mutex lock;
};

static const struct iio_chan_spec ltrf216a_channels[] = {
        {
                .type = IIO_LIGHT,
                .info_mask_separate =
                        BIT(IIO_CHAN_INFO_RAW) |
                        BIT(IIO_CHAN_INFO_PROCESSED) |
                        BIT(IIO_CHAN_INFO_INT_TIME),
                .info_mask_separate_available =
                        BIT(IIO_CHAN_INFO_INT_TIME),
        },
};

static void ltrf216a_reset(struct iio_dev *indio_dev)
{
        struct ltrf216a_data *data = iio_priv(indio_dev);

        /* reset sensor, chip fails to respond to this, so ignore any errors */
        regmap_write(data->regmap, LTRF216A_MAIN_CTRL, LTRF216A_ALS_RESET_MASK);

        /* reset time */
        usleep_range(1000, 2000);
}

static int ltrf216a_enable(struct iio_dev *indio_dev)
{
        struct ltrf216a_data *data = iio_priv(indio_dev);
        struct device *dev = &data->client->dev;
        int ret;

        /* enable sensor */
        ret = regmap_set_bits(data->regmap,
                              LTRF216A_MAIN_CTRL, LTRF216A_ALS_ENABLE_MASK);
        if (ret) {
                dev_err(dev, "failed to enable sensor: %d\n", ret);
                return ret;
        }

        /* sleep for one integration cycle after enabling the device */
        msleep(ltrf216a_int_time_reg[0][0]);

        return 0;
}

static int ltrf216a_disable(struct iio_dev *indio_dev)
{
        struct ltrf216a_data *data = iio_priv(indio_dev);
        struct device *dev = &data->client->dev;
        int ret;

        ret = regmap_write(data->regmap, LTRF216A_MAIN_CTRL, 0);
        if (ret)
                dev_err(dev, "failed to disable sensor: %d\n", ret);

        return ret;
}

static void ltrf216a_cleanup(void *data)
{
        struct iio_dev *indio_dev = data;

        ltrf216a_disable(indio_dev);
}

static int ltrf216a_set_int_time(struct ltrf216a_data *data, int itime)
{
        struct device *dev = &data->client->dev;
        unsigned int i;
        u8 reg_val;
        int ret;

        for (i = 0; i < ARRAY_SIZE(ltrf216a_int_time_available); i++) {
                if (ltrf216a_int_time_available[i][1] == itime)
                        break;
        }
        if (i == ARRAY_SIZE(ltrf216a_int_time_available))
                return -EINVAL;

        reg_val = ltrf216a_int_time_reg[i][1];

        ret = regmap_write(data->regmap, LTRF216A_ALS_MEAS_RES, reg_val);
        if (ret) {
                dev_err(dev, "failed to set integration time: %d\n", ret);
                return ret;
        }

        data->int_time_fac = ltrf216a_int_time_reg[i][0];
        data->int_time = itime;

        return 0;
}

static int ltrf216a_get_int_time(struct ltrf216a_data *data,
                                 int *val, int *val2)
{
        *val = 0;
        *val2 = data->int_time;
        return IIO_VAL_INT_PLUS_MICRO;
}

static int ltrf216a_set_power_state(struct ltrf216a_data *data, bool on)
{
        struct device *dev = &data->client->dev;
        int ret = 0;

        if (on) {
                ret = pm_runtime_resume_and_get(dev);
                if (ret) {
                        dev_err(dev, "failed to resume runtime PM: %d\n", ret);
                        return ret;
                }
        } else {
                pm_runtime_put_autosuspend(dev);
        }

        return ret;
}

static int ltrf216a_read_data(struct ltrf216a_data *data, u8 addr)
{
        struct device *dev = &data->client->dev;
        int ret, val;
        u8 buf[3];

        ret = regmap_read_poll_timeout(data->regmap, LTRF216A_MAIN_STATUS,
                                       val, val & LTRF216A_ALS_DATA_STATUS,
                                       LTRF216A_ALS_READ_DATA_DELAY_US,
                                       LTRF216A_ALS_READ_DATA_DELAY_US * 50);
        if (ret) {
                dev_err(dev, "failed to wait for measurement data: %d\n", ret);
                return ret;
        }

        ret = regmap_bulk_read(data->regmap, addr, buf, sizeof(buf));
        if (ret) {
                dev_err(dev, "failed to read measurement data: %d\n", ret);
                return ret;
        }

        return get_unaligned_le24(&buf[0]);
}

static int ltrf216a_get_lux(struct ltrf216a_data *data)
{
        int ret, greendata;
        u64 lux;

        ret = ltrf216a_set_power_state(data, true);
        if (ret)
                return ret;

        greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0);
        if (greendata < 0)
                return greendata;

        ltrf216a_set_power_state(data, false);

        lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC;

        return lux;
}

static int ltrf216a_read_raw(struct iio_dev *indio_dev,
                             struct iio_chan_spec const *chan, int *val,
                             int *val2, long mask)
{
        struct ltrf216a_data *data = iio_priv(indio_dev);
        int ret;

        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                ret = ltrf216a_set_power_state(data, true);
                if (ret)
                        return ret;
                mutex_lock(&data->lock);
                ret = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0);
                mutex_unlock(&data->lock);
                ltrf216a_set_power_state(data, false);
                if (ret < 0)
                        return ret;
                *val = ret;
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_PROCESSED:
                mutex_lock(&data->lock);
                ret = ltrf216a_get_lux(data);
                mutex_unlock(&data->lock);
                if (ret < 0)
                        return ret;
                *val = ret;
                *val2 = data->als_gain_fac * data->int_time_fac;
                return IIO_VAL_FRACTIONAL;
        case IIO_CHAN_INFO_INT_TIME:
                mutex_lock(&data->lock);
                ret = ltrf216a_get_int_time(data, val, val2);
                mutex_unlock(&data->lock);
                return ret;
        default:
                return -EINVAL;
        }
}

static int ltrf216a_write_raw(struct iio_dev *indio_dev,
                              struct iio_chan_spec const *chan, int val,
                              int val2, long mask)
{
        struct ltrf216a_data *data = iio_priv(indio_dev);
        int ret;

        switch (mask) {
        case IIO_CHAN_INFO_INT_TIME:
                if (val != 0)
                        return -EINVAL;
                mutex_lock(&data->lock);
                ret = ltrf216a_set_int_time(data, val2);
                mutex_unlock(&data->lock);
                return ret;
        default:
                return -EINVAL;
        }
}

static int ltrf216a_read_available(struct iio_dev *indio_dev,
                                   struct iio_chan_spec const *chan,
                                   const int **vals, int *type, int *length,
                                   long mask)
{
        switch (mask) {
        case IIO_CHAN_INFO_INT_TIME:
                *length = ARRAY_SIZE(ltrf216a_int_time_available) * 2;
                *vals = (const int *)ltrf216a_int_time_available;
                *type = IIO_VAL_INT_PLUS_MICRO;
                return IIO_AVAIL_LIST;
        default:
                return -EINVAL;
        }
}

static const struct iio_info ltrf216a_info = {
        .read_raw = ltrf216a_read_raw,
        .write_raw = ltrf216a_write_raw,
        .read_avail = ltrf216a_read_available,
};

static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg)
{
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct ltrf216a_data *data = iio_priv(indio_dev);

        switch (reg) {
        case LTRF216A_MAIN_CTRL:
        case LTRF216A_ALS_MEAS_RES:
        case LTRF216A_ALS_GAIN:
        case LTRF216A_PART_ID:
        case LTRF216A_MAIN_STATUS:
        case LTRF216A_ALS_DATA_0:
        case LTRF216A_ALS_DATA_1:
        case LTRF216A_ALS_DATA_2:
        case LTRF216A_INT_CFG:
        case LTRF216A_INT_PST:
        case LTRF216A_ALS_THRES_UP_0:
        case LTRF216A_ALS_THRES_UP_1:
        case LTRF216A_ALS_THRES_UP_2:
        case LTRF216A_ALS_THRES_LOW_0:
        case LTRF216A_ALS_THRES_LOW_1:
        case LTRF216A_ALS_THRES_LOW_2:
                return true;
        case LTRF216A_ALS_CLEAR_DATA_0:
        case LTRF216A_ALS_CLEAR_DATA_1:
        case LTRF216A_ALS_CLEAR_DATA_2:
                return data->info->has_clear_data;
        default:
                return false;
        }
}

static bool ltrf216a_writable_reg(struct device *dev, unsigned int reg)
{
        switch (reg) {
        case LTRF216A_MAIN_CTRL:
        case LTRF216A_ALS_MEAS_RES:
        case LTRF216A_ALS_GAIN:
        case LTRF216A_INT_CFG:
        case LTRF216A_INT_PST:
        case LTRF216A_ALS_THRES_UP_0:
        case LTRF216A_ALS_THRES_UP_1:
        case LTRF216A_ALS_THRES_UP_2:
        case LTRF216A_ALS_THRES_LOW_0:
        case LTRF216A_ALS_THRES_LOW_1:
        case LTRF216A_ALS_THRES_LOW_2:
                return true;
        default:
                return false;
        }
}

static bool ltrf216a_volatile_reg(struct device *dev, unsigned int reg)
{
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct ltrf216a_data *data = iio_priv(indio_dev);

        switch (reg) {
        case LTRF216A_MAIN_STATUS:
        case LTRF216A_ALS_DATA_0:
        case LTRF216A_ALS_DATA_1:
        case LTRF216A_ALS_DATA_2:
                return true;
        /*
         * If these registers are not present on a chip (like LTR-308),
         * the missing registers are not considered volatile.
         */
        case LTRF216A_ALS_CLEAR_DATA_0:
        case LTRF216A_ALS_CLEAR_DATA_1:
        case LTRF216A_ALS_CLEAR_DATA_2:
                return data->info->has_clear_data;
        default:
                return false;
        }
}

static bool ltrf216a_precious_reg(struct device *dev, unsigned int reg)
{
        return reg == LTRF216A_MAIN_STATUS;
}

static const struct regmap_config ltrf216a_regmap_config = {
        .name = "ltrf216a",
        .reg_bits = 8,
        .val_bits = 8,
        .cache_type = REGCACHE_RBTREE,
        .max_register = LTRF216A_ALS_THRES_LOW_2,
        .readable_reg = ltrf216a_readable_reg,
        .writeable_reg = ltrf216a_writable_reg,
        .volatile_reg = ltrf216a_volatile_reg,
        .precious_reg = ltrf216a_precious_reg,
        .disable_locking = true,
};

static int ltrf216a_probe(struct i2c_client *client)
{
        struct ltrf216a_data *data;
        struct iio_dev *indio_dev;
        int ret;

        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
        if (!indio_dev)
                return -ENOMEM;

        data = iio_priv(indio_dev);

        data->regmap = devm_regmap_init_i2c(client, &ltrf216a_regmap_config);
        if (IS_ERR(data->regmap))
                return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
                                     "regmap initialization failed\n");

        i2c_set_clientdata(client, indio_dev);
        data->client = client;
        data->info = i2c_get_match_data(client);

        mutex_init(&data->lock);

        indio_dev->info = &ltrf216a_info;
        indio_dev->name = "ltrf216a";
        indio_dev->channels = ltrf216a_channels;
        indio_dev->num_channels = ARRAY_SIZE(ltrf216a_channels);
        indio_dev->modes = INDIO_DIRECT_MODE;

        ret = pm_runtime_set_active(&client->dev);
        if (ret)
                return ret;

        /* reset sensor, chip fails to respond to this, so ignore any errors */
        ltrf216a_reset(indio_dev);

        ret = regmap_reinit_cache(data->regmap, &ltrf216a_regmap_config);
        if (ret)
                return dev_err_probe(&client->dev, ret,
                                     "failed to reinit regmap cache\n");

        ret = ltrf216a_enable(indio_dev);
        if (ret)
                return ret;

        ret = devm_add_action_or_reset(&client->dev, ltrf216a_cleanup,
                                       indio_dev);
        if (ret)
                return ret;

        ret = devm_pm_runtime_enable(&client->dev);
        if (ret)
                return dev_err_probe(&client->dev, ret,
                                     "failed to enable runtime PM\n");

        pm_runtime_set_autosuspend_delay(&client->dev, 1000);
        pm_runtime_use_autosuspend(&client->dev);

        data->int_time = 100000;
        data->int_time_fac = 100;
        data->als_gain_fac = 3;

        return devm_iio_device_register(&client->dev, indio_dev);
}

static int ltrf216a_runtime_suspend(struct device *dev)
{
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct ltrf216a_data *data = iio_priv(indio_dev);
        int ret;

        ret = ltrf216a_disable(indio_dev);
        if (ret)
                return ret;

        regcache_cache_only(data->regmap, true);

        return 0;
}

static int ltrf216a_runtime_resume(struct device *dev)
{
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct ltrf216a_data *data = iio_priv(indio_dev);
        int ret;

        regcache_cache_only(data->regmap, false);
        regcache_mark_dirty(data->regmap);
        ret = regcache_sync(data->regmap);
        if (ret)
                goto cache_only;

        ret = ltrf216a_enable(indio_dev);
        if (ret)
                goto cache_only;

        return 0;

cache_only:
        regcache_cache_only(data->regmap, true);

        return ret;
}

static DEFINE_RUNTIME_DEV_PM_OPS(ltrf216a_pm_ops, ltrf216a_runtime_suspend,
                                 ltrf216a_runtime_resume, NULL);

static const struct ltr_chip_info ltr308_chip_info = {
        .has_clear_data         = false,
        .lux_multiplier         = 60,
};

static const struct ltr_chip_info ltrf216a_chip_info = {
        .has_clear_data         = true,
        .lux_multiplier         = 45,
};

static const struct i2c_device_id ltrf216a_id[] = {
        { "ltr308", .driver_data = (kernel_ulong_t)&ltr308_chip_info },
        { "ltrf216a", .driver_data = (kernel_ulong_t)&ltrf216a_chip_info },
        { }
};
MODULE_DEVICE_TABLE(i2c, ltrf216a_id);

static const struct of_device_id ltrf216a_of_match[] = {
        { .compatible = "liteon,ltr308", .data = &ltr308_chip_info },
        { .compatible = "liteon,ltrf216a", .data = &ltrf216a_chip_info },
        /* For Valve's Steamdeck device, an ACPI platform using PRP0001 */
        { .compatible = "ltr,ltrf216a", .data = &ltrf216a_chip_info },
        { }
};
MODULE_DEVICE_TABLE(of, ltrf216a_of_match);

static struct i2c_driver ltrf216a_driver = {
        .driver = {
                .name = "ltrf216a",
                .pm = pm_ptr(&ltrf216a_pm_ops),
                .of_match_table = ltrf216a_of_match,
        },
        .probe = ltrf216a_probe,
        .id_table = ltrf216a_id,
};
module_i2c_driver(ltrf216a_driver);

MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
MODULE_AUTHOR("Shi Zhigang <Zhigang.Shi@liteon.com>");
MODULE_DESCRIPTION("LTRF216A ambient light sensor driver");
MODULE_LICENSE("GPL");