Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Shreeya Patel | 2240 | 100.00% | 2 | 100.00% |
Total | 2240 | 2 |
// 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 <asm/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 }, }; /* * 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; 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_mark_last_busy(dev); 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, div; 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 * 45 * LTRF216A_WIN_FAC * 100; div = data->als_gain_fac * data->int_time_fac * 100; return div_u64(lux, div); } 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; return IIO_VAL_INT; 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) { 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_CLEAR_DATA_0: case LTRF216A_ALS_CLEAR_DATA_1: case LTRF216A_ALS_CLEAR_DATA_2: 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; 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) { switch (reg) { case LTRF216A_MAIN_STATUS: case LTRF216A_ALS_CLEAR_DATA_0: case LTRF216A_ALS_CLEAR_DATA_1: case LTRF216A_ALS_CLEAR_DATA_2: case LTRF216A_ALS_DATA_0: case LTRF216A_ALS_DATA_1: case LTRF216A_ALS_DATA_2: return true; 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, <rf216a_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; mutex_init(&data->lock); indio_dev->info = <rf216a_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, <rf216a_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 i2c_device_id ltrf216a_id[] = { { "ltrf216a" }, {} }; MODULE_DEVICE_TABLE(i2c, ltrf216a_id); static const struct of_device_id ltrf216a_of_match[] = { { .compatible = "liteon,ltrf216a" }, { .compatible = "ltr,ltrf216a" }, {} }; MODULE_DEVICE_TABLE(of, ltrf216a_of_match); static struct i2c_driver ltrf216a_driver = { .driver = { .name = "ltrf216a", .pm = pm_ptr(<rf216a_pm_ops), .of_match_table = ltrf216a_of_match, }, .probe_new = 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1