diff options
Diffstat (limited to 'drivers/iio/light')
-rw-r--r-- | drivers/iio/light/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/isl29018.c | 2 | ||||
-rw-r--r-- | drivers/iio/light/isl29028.c | 729 | ||||
-rw-r--r-- | drivers/iio/light/rpr0521.c | 307 | ||||
-rw-r--r-- | drivers/iio/light/tsl2583.c | 106 |
6 files changed, 1078 insertions, 77 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 33e755d8d825..2356ed9285df 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -172,6 +172,16 @@ config SENSORS_ISL29018 in lux, proximity infrared sensing and normal infrared sensing. Data from sensor is accessible via sysfs. +config SENSORS_ISL29028 + tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor" + depends on I2C + select REGMAP_I2C + help + Provides driver for the Intersil's ISL29028 device. + This driver supports the sysfs interface to get the ALS, IR intensity, + Proximity value via iio. The ISL29028 provides the concurrent sensing + of ambient light and proximity. + config ISL29125 tristate "Intersil ISL29125 digital color light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 681363c2b298..fa32fa459e2e 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o +obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o obj-$(CONFIG_ISL29125) += isl29125.o obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index 917dd8b43e72..61f5924b472d 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -807,6 +807,7 @@ static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume); #define ISL29018_PM_OPS NULL #endif +#ifdef CONFIG_ACPI static const struct acpi_device_id isl29018_acpi_match[] = { {"ISL29018", isl29018}, {"ISL29023", isl29023}, @@ -814,6 +815,7 @@ static const struct acpi_device_id isl29018_acpi_match[] = { {}, }; MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match); +#endif static const struct i2c_device_id isl29018_id[] = { {"isl29018", isl29018}, diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c new file mode 100644 index 000000000000..3d09c1fc4dad --- /dev/null +++ b/drivers/iio/light/isl29028.c @@ -0,0 +1,729 @@ +/* + * IIO driver for the light sensor ISL29028. + * ISL29028 is Concurrent Ambient Light and Proximity Sensor + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Datasheets: + * - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29028.pdf + * - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29030.pdf + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/pm_runtime.h> + +#define ISL29028_CONV_TIME_MS 100 + +#define ISL29028_REG_CONFIGURE 0x01 + +#define ISL29028_CONF_ALS_IR_MODE_ALS 0 +#define ISL29028_CONF_ALS_IR_MODE_IR BIT(0) +#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0) + +#define ISL29028_CONF_ALS_RANGE_LOW_LUX 0 +#define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1) +#define ISL29028_CONF_ALS_RANGE_MASK BIT(1) + +#define ISL29028_CONF_ALS_DIS 0 +#define ISL29028_CONF_ALS_EN BIT(2) +#define ISL29028_CONF_ALS_EN_MASK BIT(2) + +#define ISL29028_CONF_PROX_SLP_SH 4 +#define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH) + +#define ISL29028_CONF_PROX_EN BIT(7) +#define ISL29028_CONF_PROX_EN_MASK BIT(7) + +#define ISL29028_REG_INTERRUPT 0x02 + +#define ISL29028_REG_PROX_DATA 0x08 +#define ISL29028_REG_ALSIR_L 0x09 +#define ISL29028_REG_ALSIR_U 0x0A + +#define ISL29028_REG_TEST1_MODE 0x0E +#define ISL29028_REG_TEST2_MODE 0x0F + +#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1) + +#define ISL29028_POWER_OFF_DELAY_MS 2000 + +struct isl29028_prox_data { + int sampling_int; + int sampling_fract; + int sleep_time; +}; + +static const struct isl29028_prox_data isl29028_prox_data[] = { + { 1, 250000, 800 }, + { 2, 500000, 400 }, + { 5, 0, 200 }, + { 10, 0, 100 }, + { 13, 300000, 75 }, + { 20, 0, 50 }, + { 80, 0, 13 }, /* + * Note: Data sheet lists 12.5 ms sleep time. + * Round up a half millisecond for msleep(). + */ + { 100, 0, 0 } +}; + +enum isl29028_als_ir_mode { + ISL29028_MODE_NONE = 0, + ISL29028_MODE_ALS, + ISL29028_MODE_IR, +}; + +struct isl29028_chip { + struct mutex lock; + struct regmap *regmap; + int prox_sampling_int; + int prox_sampling_frac; + bool enable_prox; + int lux_scale; + enum isl29028_als_ir_mode als_ir_mode; +}; + +static int isl29028_find_prox_sleep_index(int sampling_int, int sampling_fract) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(isl29028_prox_data); ++i) { + if (isl29028_prox_data[i].sampling_int == sampling_int && + isl29028_prox_data[i].sampling_fract == sampling_fract) + return i; + } + + return -EINVAL; +} + +static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, + int sampling_int, int sampling_fract) +{ + struct device *dev = regmap_get_device(chip->regmap); + int sleep_index, ret; + + sleep_index = isl29028_find_prox_sleep_index(sampling_int, + sampling_fract); + if (sleep_index < 0) + return sleep_index; + + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_PROX_SLP_MASK, + sleep_index << ISL29028_CONF_PROX_SLP_SH); + + if (ret < 0) { + dev_err(dev, "%s(): Error %d setting the proximity sampling\n", + __func__, ret); + return ret; + } + + chip->prox_sampling_int = sampling_int; + chip->prox_sampling_frac = sampling_fract; + + return ret; +} + +static int isl29028_enable_proximity(struct isl29028_chip *chip) +{ + int prox_index, ret; + + ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling_int, + chip->prox_sampling_frac); + if (ret < 0) + return ret; + + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_PROX_EN_MASK, + ISL29028_CONF_PROX_EN); + if (ret < 0) + return ret; + + /* Wait for conversion to be complete for first sample */ + prox_index = isl29028_find_prox_sleep_index(chip->prox_sampling_int, + chip->prox_sampling_frac); + if (prox_index < 0) + return prox_index; + + msleep(isl29028_prox_data[prox_index].sleep_time); + + return 0; +} + +static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) +{ + struct device *dev = regmap_get_device(chip->regmap); + int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX : + ISL29028_CONF_ALS_RANGE_LOW_LUX; + int ret; + + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_RANGE_MASK, val); + if (ret < 0) { + dev_err(dev, "%s(): Error %d setting the ALS scale\n", __func__, + ret); + return ret; + } + + chip->lux_scale = lux_scale; + + return ret; +} + +static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, + enum isl29028_als_ir_mode mode) +{ + int ret; + + if (chip->als_ir_mode == mode) + return 0; + + ret = isl29028_set_als_scale(chip, chip->lux_scale); + if (ret < 0) + return ret; + + switch (mode) { + case ISL29028_MODE_ALS: + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_IR_MODE_MASK, + ISL29028_CONF_ALS_IR_MODE_ALS); + if (ret < 0) + return ret; + + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_RANGE_MASK, + ISL29028_CONF_ALS_RANGE_HIGH_LUX); + break; + case ISL29028_MODE_IR: + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_IR_MODE_MASK, + ISL29028_CONF_ALS_IR_MODE_IR); + break; + case ISL29028_MODE_NONE: + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_EN_MASK, + ISL29028_CONF_ALS_DIS); + } + + if (ret < 0) + return ret; + + /* Enable the ALS/IR */ + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_EN_MASK, + ISL29028_CONF_ALS_EN); + if (ret < 0) + return ret; + + /* Need to wait for conversion time if ALS/IR mode enabled */ + msleep(ISL29028_CONV_TIME_MS); + + chip->als_ir_mode = mode; + + return 0; +} + +static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) +{ + struct device *dev = regmap_get_device(chip->regmap); + unsigned int lsb; + unsigned int msb; + int ret; + + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb); + if (ret < 0) { + dev_err(dev, + "%s(): Error %d reading register ALSIR_L\n", + __func__, ret); + return ret; + } + + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb); + if (ret < 0) { + dev_err(dev, + "%s(): Error %d reading register ALSIR_U\n", + __func__, ret); + return ret; + } + + *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF); + + return 0; +} + +static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) +{ + struct device *dev = regmap_get_device(chip->regmap); + unsigned int data; + int ret; + + if (!chip->enable_prox) { + ret = isl29028_enable_proximity(chip); + if (ret < 0) + return ret; + + chip->enable_prox = true; + } + + ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); + if (ret < 0) { + dev_err(dev, "%s(): Error %d reading register PROX_DATA\n", + __func__, ret); + return ret; + } + + *prox = data; + + return 0; +} + +static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + int als_ir_data; + + ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS); + if (ret < 0) { + dev_err(dev, "%s(): Error %d enabling ALS mode\n", __func__, + ret); + return ret; + } + + ret = isl29028_read_als_ir(chip, &als_ir_data); + if (ret < 0) + return ret; + + /* + * convert als data count to lux. + * if lux_scale = 125, lux = count * 0.031 + * if lux_scale = 2000, lux = count * 0.49 + */ + if (chip->lux_scale == 125) + als_ir_data = (als_ir_data * 31) / 1000; + else + als_ir_data = (als_ir_data * 49) / 100; + + *als_data = als_ir_data; + + return 0; +} + +static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR); + if (ret < 0) { + dev_err(dev, "%s(): Error %d enabling IR mode\n", __func__, + ret); + return ret; + } + + return isl29028_read_als_ir(chip, ir_data); +} + +static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + +/* Channel IO */ +static int isl29028_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct isl29028_chip *chip = iio_priv(indio_dev); + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + ret = isl29028_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; + + mutex_lock(&chip->lock); + + ret = -EINVAL; + switch (chan->type) { + case IIO_PROXIMITY: + if (mask != IIO_CHAN_INFO_SAMP_FREQ) { + dev_err(dev, + "%s(): proximity: Mask value 0x%08lx is not supported\n", + __func__, mask); + break; + } + + if (val < 1 || val > 100) { + dev_err(dev, + "%s(): proximity: Sampling frequency %d is not in the range [1:100]\n", + __func__, val); + break; + } + + ret = isl29028_set_proxim_sampling(chip, val, val2); + break; + case IIO_LIGHT: + if (mask != IIO_CHAN_INFO_SCALE) { + dev_err(dev, + "%s(): light: Mask value 0x%08lx is not supported\n", + __func__, mask); + break; + } + + if (val != 125 && val != 2000) { + dev_err(dev, + "%s(): light: Lux scale %d is not in the set {125, 2000}\n", + __func__, val); + break; + } + + ret = isl29028_set_als_scale(chip, val); + break; + default: + dev_err(dev, "%s(): Unsupported channel type %x\n", + __func__, chan->type); + break; + } + + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + ret = isl29028_set_pm_runtime_busy(chip, false); + if (ret < 0) + return ret; + + return ret; +} + +static int isl29028_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct isl29028_chip *chip = iio_priv(indio_dev); + struct device *dev = regmap_get_device(chip->regmap); + int ret, pm_ret; + + ret = isl29028_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; + + mutex_lock(&chip->lock); + + ret = -EINVAL; + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_LIGHT: + ret = isl29028_als_get(chip, val); + break; + case IIO_INTENSITY: + ret = isl29028_ir_get(chip, val); + break; + case IIO_PROXIMITY: + ret = isl29028_read_proxim(chip, val); + break; + default: + break; + } + + if (ret < 0) + break; + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type != IIO_PROXIMITY) + break; + + *val = chip->prox_sampling_int; + *val2 = chip->prox_sampling_frac; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_LIGHT) + break; + *val = chip->lux_scale; + ret = IIO_VAL_INT; + break; + default: + dev_err(dev, "%s(): mask value 0x%08lx is not supported\n", + __func__, mask); + break; + } + + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + /** + * Preserve the ret variable if the call to + * isl29028_set_pm_runtime_busy() is successful so the reading + * (if applicable) is returned to user space. + */ + pm_ret = isl29028_set_pm_runtime_busy(chip, false); + if (pm_ret < 0) + return pm_ret; + + return ret; +} + +static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, + "1.25 2.5 5 10 13.3 20 80 100"); +static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000"); + +#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) +static struct attribute *isl29028_attributes[] = { + ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available), + ISL29028_CONST_ATTR(in_illuminance_scale_available), + NULL, +}; + +static const struct attribute_group isl29108_group = { + .attrs = isl29028_attributes, +}; + +static const struct iio_chan_spec isl29028_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_INTENSITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + } +}; + +static const struct iio_info isl29028_info = { + .attrs = &isl29108_group, + .driver_module = THIS_MODULE, + .read_raw = isl29028_read_raw, + .write_raw = isl29028_write_raw, +}; + +static int isl29028_clear_configure_reg(struct isl29028_chip *chip) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); + if (ret < 0) + dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n", + __func__, ret); + + chip->als_ir_mode = ISL29028_MODE_NONE; + chip->enable_prox = false; + + return ret; +} + +static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ISL29028_REG_INTERRUPT: + case ISL29028_REG_PROX_DATA: + case ISL29028_REG_ALSIR_L: + case ISL29028_REG_ALSIR_U: + return true; + default: + return false; + } +} + +static const struct regmap_config isl29028_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = isl29028_is_volatile_reg, + .max_register = ISL29028_NUM_REGS - 1, + .num_reg_defaults_raw = ISL29028_NUM_REGS, + .cache_type = REGCACHE_RBTREE, +}; + +static int isl29028_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct isl29028_chip *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + i2c_set_clientdata(client, indio_dev); + mutex_init(&chip->lock); + + chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, "%s: Error %d initializing regmap\n", + __func__, ret); + return ret; + } + + chip->enable_prox = false; + chip->prox_sampling_int = 20; + chip->prox_sampling_frac = 0; + chip->lux_scale = 2000; + + ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); + if (ret < 0) { + dev_err(&client->dev, + "%s(): Error %d writing to TEST1_MODE register\n", + __func__, ret); + return ret; + } + + ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); + if (ret < 0) { + dev_err(&client->dev, + "%s(): Error %d writing to TEST2_MODE register\n", + __func__, ret); + return ret; + } + + ret = isl29028_clear_configure_reg(chip); + if (ret < 0) + return ret; + + indio_dev->info = &isl29028_info; + indio_dev->channels = isl29028_channels; + indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + ISL29028_POWER_OFF_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "%s(): iio registration failed with error %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int isl29028_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct isl29028_chip *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return isl29028_clear_configure_reg(chip); +} + +static int __maybe_unused isl29028_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct isl29028_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->lock); + + ret = isl29028_clear_configure_reg(chip); + + mutex_unlock(&chip->lock); + + return ret; +} + +static int __maybe_unused isl29028_resume(struct device *dev) +{ + /** + * The specific component (ALS/IR or proximity) will enable itself as + * needed the next time that the user requests a reading. This is done + * above in isl29028_set_als_ir_mode() and isl29028_enable_proximity(). + */ + return 0; +} + +static const struct dev_pm_ops isl29028_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(isl29028_suspend, isl29028_resume, NULL) +}; + +static const struct i2c_device_id isl29028_id[] = { + {"isl29028", 0}, + {"isl29030", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, isl29028_id); + +static const struct of_device_id isl29028_of_match[] = { + { .compatible = "isl,isl29028", }, /* for backward compat., don't use */ + { .compatible = "isil,isl29028", }, + { .compatible = "isil,isl29030", }, + { }, +}; +MODULE_DEVICE_TABLE(of, isl29028_of_match); + +static struct i2c_driver isl29028_driver = { + .driver = { + .name = "isl29028", + .pm = &isl29028_pm_ops, + .of_match_table = isl29028_of_match, + }, + .probe = isl29028_probe, + .remove = isl29028_remove, + .id_table = isl29028_id, +}; + +module_i2c_driver(isl29028_driver); + +MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 7de0f397194b..9d0c2e859bb2 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -9,7 +9,7 @@ * * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38). * - * TODO: illuminance channel, PM support, buffer + * TODO: illuminance channel, buffer */ #include <linux/module.h> @@ -30,6 +30,7 @@ #define RPR0521_REG_PXS_DATA 0x44 /* 16-bit, little endian */ #define RPR0521_REG_ALS_DATA0 0x46 /* 16-bit, little endian */ #define RPR0521_REG_ALS_DATA1 0x48 /* 16-bit, little endian */ +#define RPR0521_REG_PS_OFFSET_LSB 0x53 #define RPR0521_REG_ID 0x92 #define RPR0521_MODE_ALS_MASK BIT(7) @@ -77,9 +78,9 @@ static const struct rpr0521_gain rpr0521_pxs_gain[3] = { }; enum rpr0521_channel { + RPR0521_CHAN_PXS, RPR0521_CHAN_ALS_DATA0, RPR0521_CHAN_ALS_DATA1, - RPR0521_CHAN_PXS, }; struct rpr0521_reg_desc { @@ -88,6 +89,10 @@ struct rpr0521_reg_desc { }; static const struct rpr0521_reg_desc rpr0521_data_reg[] = { + [RPR0521_CHAN_PXS] = { + .address = RPR0521_REG_PXS_DATA, + .device_mask = RPR0521_MODE_PXS_MASK, + }, [RPR0521_CHAN_ALS_DATA0] = { .address = RPR0521_REG_ALS_DATA0, .device_mask = RPR0521_MODE_ALS_MASK, @@ -96,10 +101,6 @@ static const struct rpr0521_reg_desc rpr0521_data_reg[] = { .address = RPR0521_REG_ALS_DATA1, .device_mask = RPR0521_MODE_ALS_MASK, }, - [RPR0521_CHAN_PXS] = { - .address = RPR0521_REG_PXS_DATA, - .device_mask = RPR0521_MODE_PXS_MASK, - }, }; static const struct rpr0521_gain_info { @@ -109,6 +110,13 @@ static const struct rpr0521_gain_info { const struct rpr0521_gain *gain; int size; } rpr0521_gain[] = { + [RPR0521_CHAN_PXS] = { + .reg = RPR0521_REG_PXS_CTRL, + .mask = RPR0521_PXS_GAIN_MASK, + .shift = RPR0521_PXS_GAIN_SHIFT, + .gain = rpr0521_pxs_gain, + .size = ARRAY_SIZE(rpr0521_pxs_gain), + }, [RPR0521_CHAN_ALS_DATA0] = { .reg = RPR0521_REG_ALS_CTRL, .mask = RPR0521_ALS_DATA0_GAIN_MASK, @@ -123,13 +131,30 @@ static const struct rpr0521_gain_info { .gain = rpr0521_als_gain, .size = ARRAY_SIZE(rpr0521_als_gain), }, - [RPR0521_CHAN_PXS] = { - .reg = RPR0521_REG_PXS_CTRL, - .mask = RPR0521_PXS_GAIN_MASK, - .shift = RPR0521_PXS_GAIN_SHIFT, - .gain = rpr0521_pxs_gain, - .size = ARRAY_SIZE(rpr0521_pxs_gain), - }, +}; + +struct rpr0521_samp_freq { + int als_hz; + int als_uhz; + int pxs_hz; + int pxs_uhz; +}; + +static const struct rpr0521_samp_freq rpr0521_samp_freq_i[13] = { +/* {ALS, PXS}, W==currently writable option */ + {0, 0, 0, 0}, /* W0000, 0=standby */ + {0, 0, 100, 0}, /* 0001 */ + {0, 0, 25, 0}, /* 0010 */ + {0, 0, 10, 0}, /* 0011 */ + {0, 0, 2, 500000}, /* 0100 */ + {10, 0, 20, 0}, /* 0101 */ + {10, 0, 10, 0}, /* W0110 */ + {10, 0, 2, 500000}, /* 0111 */ + {2, 500000, 20, 0}, /* 1000, measurement 100ms, sleep 300ms */ + {2, 500000, 10, 0}, /* 1001, measurement 100ms, sleep 300ms */ + {2, 500000, 0, 0}, /* 1010, high sensitivity mode */ + {2, 500000, 2, 500000}, /* W1011, high sensitivity mode */ + {20, 0, 20, 0} /* 1100, ALS_data x 0.5, see specification P.18 */ }; struct rpr0521_data { @@ -142,9 +167,11 @@ struct rpr0521_data { bool als_dev_en; bool pxs_dev_en; - /* optimize runtime pm ops - enable device only if needed */ + /* optimize runtime pm ops - enable/disable device only if needed */ bool als_ps_need_en; bool pxs_ps_need_en; + bool als_need_dis; + bool pxs_need_dis; struct regmap *regmap; }; @@ -152,9 +179,16 @@ struct rpr0521_data { static IIO_CONST_ATTR(in_intensity_scale_available, RPR0521_ALS_SCALE_AVAIL); static IIO_CONST_ATTR(in_proximity_scale_available, RPR0521_PXS_SCALE_AVAIL); +/* + * Start with easy freq first, whole table of freq combinations is more + * complicated. + */ +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("2.5 10"); + static struct attribute *rpr0521_attributes[] = { &iio_const_attr_in_intensity_scale_available.dev_attr.attr, &iio_const_attr_in_proximity_scale_available.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, NULL, }; @@ -164,12 +198,21 @@ static const struct attribute_group rpr0521_attribute_group = { static const struct iio_chan_spec rpr0521_channels[] = { { + .type = IIO_PROXIMITY, + .address = RPR0521_CHAN_PXS, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { .type = IIO_INTENSITY, .modified = 1, .address = RPR0521_CHAN_ALS_DATA0, .channel2 = IIO_MOD_LIGHT_BOTH, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_INTENSITY, @@ -178,13 +221,8 @@ static const struct iio_chan_spec rpr0521_channels[] = { .channel2 = IIO_MOD_LIGHT_IR, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, - { - .type = IIO_PROXIMITY, - .address = RPR0521_CHAN_PXS, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), - } }; static int rpr0521_als_enable(struct rpr0521_data *data, u8 status) @@ -197,7 +235,10 @@ static int rpr0521_als_enable(struct rpr0521_data *data, u8 status) if (ret < 0) return ret; - data->als_dev_en = true; + if (status & RPR0521_MODE_ALS_MASK) + data->als_dev_en = true; + else + data->als_dev_en = false; return 0; } @@ -212,7 +253,10 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status) if (ret < 0) return ret; - data->pxs_dev_en = true; + if (status & RPR0521_MODE_PXS_MASK) + data->pxs_dev_en = true; + else + data->pxs_dev_en = false; return 0; } @@ -224,40 +268,32 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status) * @on: state to be set for devices in @device_mask * @device_mask: bitmask specifying for which device we need to update @on state * - * We rely on rpr0521_runtime_resume to enable our @device_mask devices, but - * if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to - * rpr0521_runtime_resume and we want to enable ALS we MUST set ALS enable - * bit of RPR0521_REG_MODE_CTRL here because rpr0521_runtime_resume will not - * be called twice. + * Calls for this function must be balanced so that each ON should have matching + * OFF. Otherwise pm usage_count gets out of sync. */ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, u8 device_mask) { #ifdef CONFIG_PM int ret; - u8 update_mask = 0; if (device_mask & RPR0521_MODE_ALS_MASK) { - if (on && !data->als_ps_need_en && data->pxs_dev_en) - update_mask |= RPR0521_MODE_ALS_MASK; - else - data->als_ps_need_en = on; + data->als_ps_need_en = on; + data->als_need_dis = !on; } if (device_mask & RPR0521_MODE_PXS_MASK) { - if (on && !data->pxs_ps_need_en && data->als_dev_en) - update_mask |= RPR0521_MODE_PXS_MASK; - else - data->pxs_ps_need_en = on; - } - - if (update_mask) { - ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL, - update_mask, update_mask); - if (ret < 0) - return ret; + data->pxs_ps_need_en = on; + data->pxs_need_dis = !on; } + /* + * On: _resume() is called only when we are suspended + * Off: _suspend() is called after delay if _resume() is not + * called before that. + * Note: If either measurement is re-enabled before _suspend(), + * both stay enabled until _suspend(). + */ if (on) { ret = pm_runtime_get_sync(&data->client->dev); } else { @@ -273,6 +309,23 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, return ret; } + + if (on) { + /* If _resume() was not called, enable measurement now. */ + if (data->als_ps_need_en) { + ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); + if (ret) + return ret; + data->als_ps_need_en = false; + } + + if (data->pxs_ps_need_en) { + ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE); + if (ret) + return ret; + data->pxs_ps_need_en = false; + } + } #endif return 0; } @@ -314,6 +367,106 @@ static int rpr0521_set_gain(struct rpr0521_data *data, int chan, idx << rpr0521_gain[chan].shift); } +static int rpr0521_read_samp_freq(struct rpr0521_data *data, + enum iio_chan_type chan_type, + int *val, int *val2) +{ + int reg, ret; + + ret = regmap_read(data->regmap, RPR0521_REG_MODE_CTRL, ®); + if (ret < 0) + return ret; + + reg &= RPR0521_MODE_MEAS_TIME_MASK; + if (reg >= ARRAY_SIZE(rpr0521_samp_freq_i)) + return -EINVAL; + + switch (chan_type) { + case IIO_INTENSITY: + *val = rpr0521_samp_freq_i[reg].als_hz; + *val2 = rpr0521_samp_freq_i[reg].als_uhz; + return 0; + + case IIO_PROXIMITY: + *val = rpr0521_samp_freq_i[reg].pxs_hz; + *val2 = rpr0521_samp_freq_i[reg].pxs_uhz; + return 0; + + default: + return -EINVAL; + } +} + +static int rpr0521_write_samp_freq_common(struct rpr0521_data *data, + enum iio_chan_type chan_type, + int val, int val2) +{ + int i; + + /* + * Ignore channel + * both pxs and als are setup only to same freq because of simplicity + */ + switch (val) { + case 0: + i = 0; + break; + + case 2: + if (val2 != 500000) + return -EINVAL; + + i = 11; + break; + + case 10: + i = 6; + break; + + default: + return -EINVAL; + } + + return regmap_update_bits(data->regmap, + RPR0521_REG_MODE_CTRL, + RPR0521_MODE_MEAS_TIME_MASK, + i); +} + +static int rpr0521_read_ps_offset(struct rpr0521_data *data, int *offset) +{ + int ret; + __le16 buffer; + + ret = regmap_bulk_read(data->regmap, + RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer)); + + if (ret < 0) { + dev_err(&data->client->dev, "Failed to read PS OFFSET register\n"); + return ret; + } + *offset = le16_to_cpu(buffer); + + return ret; +} + +static int rpr0521_write_ps_offset(struct rpr0521_data *data, int offset) +{ + int ret; + __le16 buffer; + + buffer = cpu_to_le16(offset & 0x3ff); + ret = regmap_raw_write(data->regmap, + RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer)); + + if (ret < 0) { + dev_err(&data->client->dev, "Failed to write PS OFFSET register\n"); + return ret; + } + + return ret; +} + static int rpr0521_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -339,7 +492,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, ret = regmap_bulk_read(data->regmap, rpr0521_data_reg[chan->address].address, - &raw_data, 2); + &raw_data, sizeof(raw_data)); if (ret < 0) { rpr0521_set_power_state(data, false, device_mask); mutex_unlock(&data->lock); @@ -354,6 +507,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, *val = le16_to_cpu(raw_data); return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: mutex_lock(&data->lock); ret = rpr0521_get_gain(data, chan->address, val, val2); @@ -362,6 +516,25 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, return ret; return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->lock); + ret = rpr0521_read_samp_freq(data, chan->type, val, val2); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_OFFSET: + mutex_lock(&data->lock); + ret = rpr0521_read_ps_offset(data, val); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + default: return -EINVAL; } @@ -381,6 +554,22 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return ret; + + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->lock); + ret = rpr0521_write_samp_freq_common(data, chan->type, + val, val2); + mutex_unlock(&data->lock); + + return ret; + + case IIO_CHAN_INFO_OFFSET: + mutex_lock(&data->lock); + ret = rpr0521_write_ps_offset(data, val); + mutex_unlock(&data->lock); + + return ret; + default: return -EINVAL; } @@ -419,12 +608,14 @@ static int rpr0521_init(struct rpr0521_data *data) return ret; } +#ifndef CONFIG_PM ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); if (ret < 0) return ret; ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE); if (ret < 0) return ret; +#endif return 0; } @@ -510,13 +701,26 @@ static int rpr0521_probe(struct i2c_client *client, ret = pm_runtime_set_active(&client->dev); if (ret < 0) - return ret; + goto err_poweroff; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - return iio_device_register(indio_dev); + ret = iio_device_register(indio_dev); + if (ret) + goto err_pm_disable; + + return 0; + +err_pm_disable: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); +err_poweroff: + rpr0521_poweroff(data); + + return ret; } static int rpr0521_remove(struct i2c_client *client) @@ -541,9 +745,16 @@ static int rpr0521_runtime_suspend(struct device *dev) struct rpr0521_data *data = iio_priv(indio_dev); int ret; - /* disable channels and sets {als,pxs}_dev_en to false */ mutex_lock(&data->lock); + /* If measurements are enabled, enable them on resume */ + if (!data->als_need_dis) + data->als_ps_need_en = data->als_dev_en; + if (!data->pxs_need_dis) + data->pxs_ps_need_en = data->pxs_dev_en; + + /* disable channels and sets {als,pxs}_dev_en to false */ ret = rpr0521_poweroff(data); + regcache_mark_dirty(data->regmap); mutex_unlock(&data->lock); return ret; @@ -555,6 +766,7 @@ static int rpr0521_runtime_resume(struct device *dev) struct rpr0521_data *data = iio_priv(indio_dev); int ret; + regcache_sync(data->regmap); if (data->als_ps_need_en) { ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE); if (ret < 0) @@ -568,6 +780,7 @@ static int rpr0521_runtime_resume(struct device *dev) return ret; data->pxs_ps_need_en = false; } + msleep(100); //wait for first measurement result return 0; } diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index a78b6025c465..1679181d2bdd 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -3,7 +3,7 @@ * within the TAOS tsl258x family of devices (tsl2580, tsl2581, tsl2583). * * Copyright (c) 2011, TAOS Corporation. - * Copyright (c) 2016 Brian Masney <masneyb@onstation.org> + * Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/pm_runtime.h> /* Device Registers and Masks */ #define TSL2583_CNTRL 0x00 @@ -64,6 +65,8 @@ #define TSL2583_CHIP_ID 0x90 #define TSL2583_CHIP_ID_MASK 0xf0 +#define TSL2583_POWER_OFF_DELAY_MS 2000 + /* Per-device data */ struct tsl2583_als_info { u16 als_ch0; @@ -108,7 +111,6 @@ struct tsl2583_chip { struct tsl2583_settings als_settings; int als_time_scale; int als_saturation; - bool suspended; }; struct gainadj { @@ -460,8 +462,6 @@ static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev) if (ret < 0) return ret; - chip->suspended = false; - return ret; } @@ -513,11 +513,6 @@ static ssize_t in_illuminance_calibrate_store(struct device *dev, mutex_lock(&chip->als_mutex); - if (chip->suspended) { - ret = -EBUSY; - goto done; - } - ret = tsl2583_als_calibrate(indio_dev); if (ret < 0) goto done; @@ -645,20 +640,36 @@ static const struct iio_chan_spec tsl2583_channels[] = { }, }; +static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) +{ + int ret; + + if (on) { + ret = pm_runtime_get_sync(&chip->client->dev); + if (ret < 0) + pm_runtime_put_noidle(&chip->client->dev); + } else { + pm_runtime_mark_last_busy(&chip->client->dev); + ret = pm_runtime_put_autosuspend(&chip->client->dev); + } + + return ret; +} + static int tsl2583_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct tsl2583_chip *chip = iio_priv(indio_dev); - int ret = -EINVAL; + int ret, pm_ret; - mutex_lock(&chip->als_mutex); + ret = tsl2583_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; - if (chip->suspended) { - ret = -EBUSY; - goto read_done; - } + mutex_lock(&chip->als_mutex); + ret = -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->type == IIO_LIGHT) { @@ -719,6 +730,18 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, read_done: mutex_unlock(&chip->als_mutex); + if (ret < 0) + return ret; + + /* + * Preserve the ret variable if the call to + * tsl2583_set_pm_runtime_busy() is successful so the reading + * (if applicable) is returned to user space. + */ + pm_ret = tsl2583_set_pm_runtime_busy(chip, false); + if (pm_ret < 0) + return pm_ret; + return ret; } @@ -727,15 +750,15 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct tsl2583_chip *chip = iio_priv(indio_dev); - int ret = -EINVAL; + int ret; - mutex_lock(&chip->als_mutex); + ret = tsl2583_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; - if (chip->suspended) { - ret = -EBUSY; - goto write_done; - } + mutex_lock(&chip->als_mutex); + ret = -EINVAL; switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: if (chan->type == IIO_LIGHT) { @@ -767,9 +790,15 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, break; } -write_done: mutex_unlock(&chip->als_mutex); + if (ret < 0) + return ret; + + ret = tsl2583_set_pm_runtime_busy(chip, false); + if (ret < 0) + return ret; + return ret; } @@ -803,7 +832,6 @@ static int tsl2583_probe(struct i2c_client *clientp, i2c_set_clientdata(clientp, indio_dev); mutex_init(&chip->als_mutex); - chip->suspended = true; ret = i2c_smbus_read_byte_data(clientp, TSL2583_CMD_REG | TSL2583_CHIPID); @@ -826,6 +854,11 @@ static int tsl2583_probe(struct i2c_client *clientp, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = chip->client->name; + pm_runtime_enable(&clientp->dev); + pm_runtime_set_autosuspend_delay(&clientp->dev, + TSL2583_POWER_OFF_DELAY_MS); + pm_runtime_use_autosuspend(&clientp->dev); + ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); if (ret) { dev_err(&clientp->dev, "%s: iio registration failed\n", @@ -836,16 +869,25 @@ static int tsl2583_probe(struct i2c_client *clientp, /* Load up the V2 defaults (these are hard coded defaults for now) */ tsl2583_defaults(chip); - /* Make sure the chip is on */ - ret = tsl2583_chip_init_and_power_on(indio_dev); - if (ret < 0) - return ret; - dev_info(&clientp->dev, "Light sensor found.\n"); return 0; } +static int tsl2583_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct tsl2583_chip *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF); +} + static int __maybe_unused tsl2583_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -855,7 +897,6 @@ static int __maybe_unused tsl2583_suspend(struct device *dev) mutex_lock(&chip->als_mutex); ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF); - chip->suspended = true; mutex_unlock(&chip->als_mutex); @@ -877,7 +918,11 @@ static int __maybe_unused tsl2583_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(tsl2583_pm_ops, tsl2583_suspend, tsl2583_resume); +static const struct dev_pm_ops tsl2583_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(tsl2583_suspend, tsl2583_resume, NULL) +}; static struct i2c_device_id tsl2583_idtable[] = { { "tsl2580", 0 }, @@ -904,6 +949,7 @@ static struct i2c_driver tsl2583_driver = { }, .id_table = tsl2583_idtable, .probe = tsl2583_probe, + .remove = tsl2583_remove, }; module_i2c_driver(tsl2583_driver); |