diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-08-12 12:43:41 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-08-12 12:43:41 -0700 |
commit | 1c46ae0af6df0bbde66c5e868563be57f18a27b4 (patch) | |
tree | b5dbe61f8d05f9c67aff0080ea87945342d58c7d /drivers/iio/light | |
parent | a73e99cb67e7438e5ab0c524ae63a8a27616c839 (diff) | |
parent | 41d903c00051d8f31c98a8136edbac67e6f8688f (diff) | |
download | linux-1c46ae0af6df0bbde66c5e868563be57f18a27b4.tar.bz2 |
Merge tag 'iio-for-4.3b-2' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
Second set of new device support, features and cleanup for the 4.3 cycle.
Take 2 also includes a fix set that was too late for the 4.2 cycle.
As we had a lot of tools and docs work in this set, I have broken those
out into their own categories in this description.
Fixes from the pull request '4th set of IIO fixes for the 4.2 cycle'.
* Poll functions for both event chardev and the buffer one were returning
negative error codes (via a positive value).
* A recent change to lsiio adding some error handling that was wrong and
stopped the tool working.
* bmg160 was missing some dependencies in Kconfig
* berlin2-adc had a misshandled register (wrote a value rather than a bitmap)
New device support
* TI opt3001 light sensor
* TXC PA12 ALS and proximity sensor.
* mcp3301 ADC support (in mcp320x driver)
* ST lsm303agr accelerometer and magnetometer drivers (plus some st-sensors
common support to allow different WHOAMI register addresses, devices with
fixed scale and allow interrupt equiped magnetometers).
* ADIS16305, ADIS16367, ADIS16445IMUs (in the adis16400 driver)
* ADIS16266 gyro (in the adis16260 driver)
* ADIS16137 gyro (in the adis16136 driver)
New functionality
* mmc35240 DT bindings.
* Inverse unit conversion macros to aid handing of values written to sysfs
attributes.
Core cleanup
* Forward declaration of struct iio_trigger to avoid a compile warning.
Driver cleanup / fixes
* mxs-lradc
- Clarify which parts are supported.
- Fix spelling erorrs.
- Missing/extra includes
- reorder includes
- add datasheet name listings for all usable channels (to allow them
to be bound by name from consumer drivers)
* acpi-als - add some function prefixes as per general iio style.
* bmc150_magn - replace a magic value with the existing define.
* vf610 - determine possible sample frequencies taking into account the
electrical characteristics (defining a minimum sample time)
* dht11
- whitespace
- additional docs
- avoid mulitple assignments in one line
- Use the new funciton ktime_get_resolution_ns to cleanup a nasty trick
previously used for timing.
* Fix all drivers that consider 0 a valid IRQ for historical reasons.
* Export I2C module alias info where previously missing (to allow autoprobing)
* Export OF module alias info where previously missing.
* mmc35240 - switch some variables into arrays to improve readability.
* mlx90614 - define some magic numbers for readability.
* bmc150_magn
- expand area locked by a mutex to cover all the use of the
data->buffer.
- use descriptive naming for a mask instead of a magic value.
* berin2-adc
- pass up an error code rather that a generic error
- constify the iio_chan_spec
- some other little tidy ups.
* stk8312
- fix a dependency on triggered buffers in kconfig
- add a check for invalid attribute values
- improve error handling by returning error codes where possible and
return immediately where relevant
- rework macro defs to use GENMASK etc
- change some variable types to reduce unnecessary casting
- clean up code style
- drop a local buffer copy for bulk reads and use the one in data->buffer
instead.
* adis16400 - the adis16448 gyroscope scale was wrong.
* adis16480 - some more wrong scales for various parts.
* adis16300 - has an undocumented product id and serial number registers so
use them.
* iio_simple_dummy - fix some wrong code indentation.
* bmc150-accel - use the chip ID to detect the chip present rather than
verifying the expected part was there. This was in response to a wrong
ACPI entry on the WinBook TW100.
* mma8452
- fix _get_hp_filter_index
- drop a double include
- pass up an error code rather than rewriting it
- range check input values to attribute writes
- register defs tidy up using GENMASK and reordering them to be easier to
follow.
- various coding style cleanups
- put the Kconfig entry in the write place (alphabetically).
Tools related
* Tools cleanup - drop an explicity NULL comparison, some unnecessary braces,
use the ARRAY_SIZE macro, send error messages to stderr instead of dropping
them in the middle of normal output.
* Fix tools to allow that scale and offset attributes are optional.
* More tools fixes including allowing true 32bit data (previously an overflow
prevented more than 31bits)
* Drop a stray header guard that ended up in a c file.
* Make calc_digits static as it isn't exported or in the header.
* Set ci_array pointer to NULL after free as a protection against non safe
usage of the tools core code. Also convert a double pointer to a single
one as the extra level of indirection was unnecessary.
Docs
* DocBook introduction by Daniel Baluta. Glad we are beginning to
draw together some more introductory docs to suplement the various
tools / examples.
* Drop bytes_per_datum sysfs attribute docs as it no longer exists.
* A whole load of missing / fixing of kernel-doc for the core of IIO.
* Document the trigger name sysfs attribute in the ABI docs.
* Minor typos in the ABI docs related to power down modes.
Diffstat (limited to 'drivers/iio/light')
-rw-r--r-- | drivers/iio/light/Kconfig | 21 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/light/acpi-als.c | 18 | ||||
-rw-r--r-- | drivers/iio/light/cm32181.c | 1 | ||||
-rw-r--r-- | drivers/iio/light/cm3232.c | 1 | ||||
-rw-r--r-- | drivers/iio/light/cm36651.c | 1 | ||||
-rw-r--r-- | drivers/iio/light/gp2ap020a00f.c | 1 | ||||
-rw-r--r-- | drivers/iio/light/opt3001.c | 804 | ||||
-rw-r--r-- | drivers/iio/light/pa12203001.c | 483 | ||||
-rw-r--r-- | drivers/iio/light/stk3310.c | 1 |
10 files changed, 1324 insertions, 9 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index b37e18d07630..7ed859a700c4 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -210,6 +210,27 @@ config LTR501 This driver can also be built as a module. If so, the module will be called ltr501. +config OPT3001 + tristate "Texas Instruments OPT3001 Light Sensor" + depends on I2C + help + If you say Y or M here, you get support for Texas Instruments + OPT3001 Ambient Light Sensor. + + If built as a dynamically linked module, it will be called + opt3001. + +config PA12203001 + tristate "TXC PA12203001 light and proximity sensor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the TXC PA12203001 + ambient light and proximity sensor. + + This driver can also be built as a module. If so, the module + will be called pa12203001. + config STK3310 tristate "STK3310 ALS and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index adf97237db72..91c74c014b6f 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -19,6 +19,8 @@ obj-$(CONFIG_ISL29125) += isl29125.o obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_LTR501) += ltr501.o +obj-$(CONFIG_OPT3001) += opt3001.o +obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_STK3310) += stk3310.o diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 1dafa0756bfa..60537ec0c923 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -65,20 +65,20 @@ static const struct iio_chan_spec acpi_als_channels[] = { * to acpi_als_channels[], the evt_buffer below will grow * automatically. */ -#define EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels) -#define EVT_BUFFER_SIZE \ - (sizeof(s64) + (EVT_NR_SOURCES * sizeof(s32))) +#define ACPI_ALS_EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels) +#define ACPI_ALS_EVT_BUFFER_SIZE \ + (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32))) struct acpi_als { struct acpi_device *device; struct mutex lock; - s32 evt_buffer[EVT_BUFFER_SIZE]; + s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE]; }; /* * All types of properties the ACPI0008 block can report. The ALI, ALC, ALT - * and ALP can all be handled by als_read_value() below, while the ALR is + * and ALP can all be handled by acpi_als_read_value() below, while the ALR is * special. * * The _ALR property returns tables that can be used to fine-tune the values @@ -93,7 +93,7 @@ struct acpi_als { #define ACPI_ALS_POLLING "_ALP" #define ACPI_ALS_TABLES "_ALR" -static int als_read_value(struct acpi_als *als, char *prop, s32 *val) +static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val) { unsigned long long temp_val; acpi_status status; @@ -122,11 +122,11 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) mutex_lock(&als->lock); - memset(buffer, 0, EVT_BUFFER_SIZE); + memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE); switch (event) { case ACPI_ALS_NOTIFY_ILLUMINANCE: - ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); + ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); if (ret < 0) goto out; *buffer++ = val; @@ -159,7 +159,7 @@ static int acpi_als_read_raw(struct iio_dev *indio_dev, if (chan->type != IIO_LIGHT) return -EINVAL; - ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val); + ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val); if (ret < 0) return ret; diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 1c0de2f8885d..d6fd0dace74f 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -353,6 +353,7 @@ static const struct of_device_id cm32181_of_match[] = { { .compatible = "capella,cm32181" }, { } }; +MODULE_DEVICE_TABLE(of, cm32181_of_match); static struct i2c_driver cm32181_driver = { .driver = { diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index 1b508c65877c..fe89b6823217 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -417,6 +417,7 @@ static const struct of_device_id cm3232_of_match[] = { {.compatible = "capella,cm3232"}, {} }; +MODULE_DEVICE_TABLE(of, cm3232_of_match); static struct i2c_driver cm3232_driver = { .driver = { diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index 2a39e141e90c..c8d7b5ea7e78 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -731,6 +731,7 @@ static const struct of_device_id cm36651_of_match[] = { { .compatible = "capella,cm36651" }, { } }; +MODULE_DEVICE_TABLE(of, cm36651_of_match); static struct i2c_driver cm36651_driver = { .driver = { diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 0334a814b5eb..6d41086f7c64 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1634,6 +1634,7 @@ static const struct of_device_id gp2ap020a00f_of_match[] = { { .compatible = "sharp,gp2ap020a00f" }, { } }; +MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match); #endif static struct i2c_driver gp2ap020a00f_driver = { diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c new file mode 100644 index 000000000000..923aa6aef0ed --- /dev/null +++ b/drivers/iio/light/opt3001.c @@ -0,0 +1,804 @@ +/** + * opt3001.c - Texas Instruments OPT3001 Light Sensor + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Andreas Dannenberg <dannenberg@ti.com> + * Based on previous work from: Felipe Balbi <balbi@ti.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 of the License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define OPT3001_RESULT 0x00 +#define OPT3001_CONFIGURATION 0x01 +#define OPT3001_LOW_LIMIT 0x02 +#define OPT3001_HIGH_LIMIT 0x03 +#define OPT3001_MANUFACTURER_ID 0x7e +#define OPT3001_DEVICE_ID 0x7f + +#define OPT3001_CONFIGURATION_RN_MASK (0xf << 12) +#define OPT3001_CONFIGURATION_RN_AUTO (0xc << 12) + +#define OPT3001_CONFIGURATION_CT BIT(11) + +#define OPT3001_CONFIGURATION_M_MASK (3 << 9) +#define OPT3001_CONFIGURATION_M_SHUTDOWN (0 << 9) +#define OPT3001_CONFIGURATION_M_SINGLE (1 << 9) +#define OPT3001_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */ + +#define OPT3001_CONFIGURATION_OVF BIT(8) +#define OPT3001_CONFIGURATION_CRF BIT(7) +#define OPT3001_CONFIGURATION_FH BIT(6) +#define OPT3001_CONFIGURATION_FL BIT(5) +#define OPT3001_CONFIGURATION_L BIT(4) +#define OPT3001_CONFIGURATION_POL BIT(3) +#define OPT3001_CONFIGURATION_ME BIT(2) + +#define OPT3001_CONFIGURATION_FC_MASK (3 << 0) + +/* The end-of-conversion enable is located in the low-limit register */ +#define OPT3001_LOW_LIMIT_EOC_ENABLE 0xc000 + +#define OPT3001_REG_EXPONENT(n) ((n) >> 12) +#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff) + +/* + * Time to wait for conversion result to be ready. The device datasheet + * worst-case max value is 880ms. Add some slack to be on the safe side. + */ +#define OPT3001_RESULT_READY_TIMEOUT msecs_to_jiffies(1000) + +struct opt3001 { + struct i2c_client *client; + struct device *dev; + + struct mutex lock; + u16 ok_to_ignore_lock:1; + u16 result_ready:1; + wait_queue_head_t result_ready_queue; + u16 result; + + u32 int_time; + u32 mode; + + u16 high_thresh_mantissa; + u16 low_thresh_mantissa; + + u8 high_thresh_exp; + u8 low_thresh_exp; +}; + +struct opt3001_scale { + int val; + int val2; +}; + +static const struct opt3001_scale opt3001_scales[] = { + { + .val = 40, + .val2 = 950000, + }, + { + .val = 81, + .val2 = 900000, + }, + { + .val = 163, + .val2 = 800000, + }, + { + .val = 327, + .val2 = 600000, + }, + { + .val = 655, + .val2 = 200000, + }, + { + .val = 1310, + .val2 = 400000, + }, + { + .val = 2620, + .val2 = 800000, + }, + { + .val = 5241, + .val2 = 600000, + }, + { + .val = 10483, + .val2 = 200000, + }, + { + .val = 20966, + .val2 = 400000, + }, + { + .val = 83865, + .val2 = 600000, + }, +}; + +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]; + + /* + * Combine the integer and micro parts for comparison + * purposes. Use milli lux precision to avoid 32-bit integer + * overflows. + */ + if ((val * 1000 + val2 / 1000) <= + (scale->val * 1000 + scale->val2 / 1000)) { + *exponent = i; + return 0; + } + } + + return -EINVAL; +} + +static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, + u16 mantissa, int *val, int *val2) +{ + int lux; + + lux = 10 * (mantissa << exponent); + *val = lux / 1000; + *val2 = (lux - (*val * 1000)) * 1000; +} + +static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode) +{ + *reg &= ~OPT3001_CONFIGURATION_M_MASK; + *reg |= mode; + opt->mode = mode; +} + +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.8"); + +static struct attribute *opt3001_attributes[] = { + &iio_const_attr_integration_time_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group opt3001_attribute_group = { + .attrs = opt3001_attributes, +}; + +static const struct iio_event_spec opt3001_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec opt3001_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + 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_lux(struct opt3001 *opt, int *val, int *val2) +{ + int ret; + u16 mantissa; + u16 reg; + u8 exponent; + u16 value; + + /* + * Enable the end-of-conversion interrupt mechanism. Note that doing + * so will overwrite the low-level limit value however we will restore + * this value later on. + */ + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + /* Reset data-ready indicator flag (will be set in the IRQ routine) */ + opt->result_ready = false; + + /* Allow IRQ to access the device despite lock being set */ + opt->ok_to_ignore_lock = true; + + /* Configure for single-conversion mode and start a new conversion */ + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + goto err; + } + + reg = ret; + opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); + + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, + reg); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + goto err; + } + + /* Wait for the IRQ to indicate the conversion is complete */ + ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, + OPT3001_RESULT_READY_TIMEOUT); + +err: + /* Disallow IRQ to access the device while lock is active */ + opt->ok_to_ignore_lock = false; + + if (ret == 0) + return -ETIMEDOUT; + else if (ret < 0) + return ret; + + /* + * Disable the end-of-conversion interrupt mechanism by restoring the + * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note + * that selectively clearing those enable bits would affect the actual + * limit value due to bit-overlap and therefore can't be done. + */ + value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT, + value); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + exponent = OPT3001_REG_EXPONENT(opt->result); + mantissa = OPT3001_REG_MANTISSA(opt->result); + + opt3001_to_iio_ret(opt, exponent, mantissa, val, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2) +{ + *val = 0; + *val2 = opt->int_time; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int opt3001_set_int_time(struct opt3001 *opt, int time) +{ + int ret; + u16 reg; + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; + } + + reg = ret; + + switch (time) { + case 100000: + reg &= ~OPT3001_CONFIGURATION_CT; + opt->int_time = 100000; + break; + case 800000: + reg |= OPT3001_CONFIGURATION_CT; + opt->int_time = 800000; + break; + default: + return -EINVAL; + } + + return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, + reg); +} + +static int opt3001_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct opt3001 *opt = iio_priv(iio); + int ret; + + if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) + return -EBUSY; + + if (chan->type != IIO_LIGHT) + return -EINVAL; + + mutex_lock(&opt->lock); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = opt3001_get_lux(opt, val, val2); + break; + case IIO_CHAN_INFO_INT_TIME: + ret = opt3001_get_int_time(opt, val, val2); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&opt->lock); + + return ret; +} + +static int opt3001_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct opt3001 *opt = iio_priv(iio); + int ret; + + if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) + return -EBUSY; + + if (chan->type != IIO_LIGHT) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_INT_TIME) + return -EINVAL; + + if (val != 0) + return -EINVAL; + + mutex_lock(&opt->lock); + ret = opt3001_set_int_time(opt, val2); + mutex_unlock(&opt->lock); + + return ret; +} + +static int opt3001_read_event_value(struct iio_dev *iio, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2) +{ + struct opt3001 *opt = iio_priv(iio); + int ret = IIO_VAL_INT_PLUS_MICRO; + + mutex_lock(&opt->lock); + + switch (dir) { + case IIO_EV_DIR_RISING: + opt3001_to_iio_ret(opt, opt->high_thresh_exp, + opt->high_thresh_mantissa, val, val2); + break; + case IIO_EV_DIR_FALLING: + opt3001_to_iio_ret(opt, opt->low_thresh_exp, + opt->low_thresh_mantissa, val, val2); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&opt->lock); + + return ret; +} + +static int opt3001_write_event_value(struct iio_dev *iio, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2) +{ + struct opt3001 *opt = iio_priv(iio); + int ret; + + u16 mantissa; + u16 value; + u16 reg; + + u8 exponent; + + if (val < 0) + return -EINVAL; + + mutex_lock(&opt->lock); + + ret = opt3001_find_scale(opt, val, val2, &exponent); + if (ret < 0) { + dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2); + goto err; + } + + mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent; + value = (exponent << 12) | mantissa; + + switch (dir) { + case IIO_EV_DIR_RISING: + reg = OPT3001_HIGH_LIMIT; + opt->high_thresh_mantissa = mantissa; + opt->high_thresh_exp = exponent; + break; + case IIO_EV_DIR_FALLING: + reg = OPT3001_LOW_LIMIT; + opt->low_thresh_mantissa = mantissa; + opt->low_thresh_exp = exponent; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = i2c_smbus_write_word_swapped(opt->client, reg, value); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", reg); + goto err; + } + +err: + mutex_unlock(&opt->lock); + + return ret; +} + +static int opt3001_read_event_config(struct iio_dev *iio, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct opt3001 *opt = iio_priv(iio); + + return opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS; +} + +static int opt3001_write_event_config(struct iio_dev *iio, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct opt3001 *opt = iio_priv(iio); + int ret; + u16 mode; + u16 reg; + + if (state && opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) + return 0; + + if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN) + return 0; + + mutex_lock(&opt->lock); + + mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS + : OPT3001_CONFIGURATION_M_SHUTDOWN; + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + goto err; + } + + reg = ret; + opt3001_set_mode(opt, ®, mode); + + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, + reg); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + goto err; + } + +err: + mutex_unlock(&opt->lock); + + return ret; +} + +static const struct iio_info opt3001_info = { + .driver_module = THIS_MODULE, + .attrs = &opt3001_attribute_group, + .read_raw = opt3001_read_raw, + .write_raw = opt3001_write_raw, + .read_event_value = opt3001_read_event_value, + .write_event_value = opt3001_write_event_value, + .read_event_config = opt3001_read_event_config, + .write_event_config = opt3001_write_event_config, +}; + +static int opt3001_read_id(struct opt3001 *opt) +{ + char manufacturer[2]; + u16 device_id; + int ret; + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_MANUFACTURER_ID); + return ret; + } + + manufacturer[0] = ret >> 8; + manufacturer[1] = ret & 0xff; + + 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); + return ret; + } + + device_id = ret; + + dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0], + manufacturer[1], device_id); + + return 0; +} + +static int opt3001_configure(struct opt3001 *opt) +{ + int ret; + u16 reg; + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; + } + + reg = ret; + + /* Enable automatic full-scale setting mode */ + reg &= ~OPT3001_CONFIGURATION_RN_MASK; + reg |= OPT3001_CONFIGURATION_RN_AUTO; + + /* Reflect status of the device's integration time setting */ + if (reg & OPT3001_CONFIGURATION_CT) + opt->int_time = 800000; + else + opt->int_time = 100000; + + /* Ensure device is in shutdown initially */ + opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); + + /* Configure for latched window-style comparison operation */ + reg |= OPT3001_CONFIGURATION_L; + reg &= ~OPT3001_CONFIGURATION_POL; + reg &= ~OPT3001_CONFIGURATION_ME; + reg &= ~OPT3001_CONFIGURATION_FC_MASK; + + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, + reg); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + return ret; + } + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); + opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_HIGH_LIMIT); + return ret; + } + + opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret); + opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret); + + return 0; +} + +static irqreturn_t opt3001_irq(int irq, void *_iio) +{ + struct iio_dev *iio = _iio; + struct opt3001 *opt = iio_priv(iio); + int ret; + + if (!opt->ok_to_ignore_lock) + mutex_lock(&opt->lock); + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + goto out; + } + + if ((ret & OPT3001_CONFIGURATION_M_MASK) == + OPT3001_CONFIGURATION_M_CONTINUOUS) { + if (ret & OPT3001_CONFIGURATION_FH) + iio_push_event(iio, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + if (ret & OPT3001_CONFIGURATION_FL) + iio_push_event(iio, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } else if (ret & OPT3001_CONFIGURATION_CRF) { + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_RESULT); + goto out; + } + opt->result = ret; + opt->result_ready = true; + wake_up(&opt->result_ready_queue); + } + +out: + if (!opt->ok_to_ignore_lock) + mutex_unlock(&opt->lock); + + return IRQ_HANDLED; +} + +static int opt3001_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + + struct iio_dev *iio; + struct opt3001 *opt; + int irq = client->irq; + int ret; + + iio = devm_iio_device_alloc(dev, sizeof(*opt)); + if (!iio) + return -ENOMEM; + + opt = iio_priv(iio); + opt->client = client; + opt->dev = dev; + + 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; + + ret = opt3001_configure(opt); + if (ret) + return ret; + + iio->name = client->name; + iio->channels = opt3001_channels; + iio->num_channels = ARRAY_SIZE(opt3001_channels); + iio->dev.parent = dev; + iio->modes = INDIO_DIRECT_MODE; + iio->info = &opt3001_info; + + ret = devm_iio_device_register(dev, iio); + if (ret) { + dev_err(dev, "failed to register IIO device\n"); + return ret; + } + + ret = request_threaded_irq(irq, NULL, opt3001_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); + if (ret) { + dev_err(dev, "failed to request IRQ #%d\n", irq); + return ret; + } + + return 0; +} + +static int opt3001_remove(struct i2c_client *client) +{ + struct iio_dev *iio = i2c_get_clientdata(client); + struct opt3001 *opt = iio_priv(iio); + int ret; + u16 reg; + + free_irq(client->irq, iio); + + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + return ret; + } + + reg = ret; + opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); + + ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, + reg); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_CONFIGURATION); + return ret; + } + + return 0; +} + +static const struct i2c_device_id opt3001_id[] = { + { "opt3001", 0 }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(i2c, opt3001_id); + +static const struct of_device_id opt3001_of_match[] = { + { .compatible = "ti,opt3001" }, + { } +}; + +static struct i2c_driver opt3001_driver = { + .probe = opt3001_probe, + .remove = opt3001_remove, + .id_table = opt3001_id, + + .driver = { + .name = "opt3001", + .of_match_table = of_match_ptr(opt3001_of_match), + .owner = THIS_MODULE, + }, +}; + +module_i2c_driver(opt3001_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_DESCRIPTION("Texas Instruments OPT3001 Light Sensor Driver"); diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c new file mode 100644 index 000000000000..45f7bde02bbf --- /dev/null +++ b/drivers/iio/light/pa12203001.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Driver for TXC PA12203001 Proximity and Ambient Light Sensor. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * To do: Interrupt support. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#define PA12203001_DRIVER_NAME "pa12203001" + +#define PA12203001_REG_CFG0 0x00 +#define PA12203001_REG_CFG1 0x01 +#define PA12203001_REG_CFG2 0x02 +#define PA12203001_REG_CFG3 0x03 + +#define PA12203001_REG_ADL 0x0b +#define PA12203001_REG_PDH 0x0e + +#define PA12203001_REG_POFS 0x10 +#define PA12203001_REG_PSET 0x11 + +#define PA12203001_ALS_EN_MASK BIT(0) +#define PA12203001_PX_EN_MASK BIT(1) +#define PA12203001_PX_NORMAL_MODE_MASK GENMASK(7, 6) +#define PA12203001_AFSR_MASK GENMASK(5, 4) +#define PA12203001_AFSR_SHIFT 4 + +#define PA12203001_PSCAN 0x03 + +/* als range 31000, ps, als disabled */ +#define PA12203001_REG_CFG0_DEFAULT 0x30 + +/* led current: 100 mA */ +#define PA12203001_REG_CFG1_DEFAULT 0x20 + +/* ps mode: normal, interrupts not active */ +#define PA12203001_REG_CFG2_DEFAULT 0xcc + +#define PA12203001_REG_CFG3_DEFAULT 0x00 + +#define PA12203001_SLEEP_DELAY_MS 3000 + +#define PA12203001_CHIP_ENABLE 0xff +#define PA12203001_CHIP_DISABLE 0x00 + +/* available scales: corresponding to [500, 4000, 7000, 31000] lux */ +static const int pa12203001_scales[] = { 7629, 61036, 106813, 473029}; + +struct pa12203001_data { + struct i2c_client *client; + + /* protect device states */ + struct mutex lock; + + bool als_enabled; + bool px_enabled; + bool als_needs_enable; + bool px_needs_enable; + + struct regmap *map; +}; + +static const struct { + u8 reg; + u8 val; +} regvals[] = { + {PA12203001_REG_CFG0, PA12203001_REG_CFG0_DEFAULT}, + {PA12203001_REG_CFG1, PA12203001_REG_CFG1_DEFAULT}, + {PA12203001_REG_CFG2, PA12203001_REG_CFG2_DEFAULT}, + {PA12203001_REG_CFG3, PA12203001_REG_CFG3_DEFAULT}, + {PA12203001_REG_PSET, PA12203001_PSCAN}, +}; + +static IIO_CONST_ATTR(in_illuminance_scale_available, + "0.007629 0.061036 0.106813 0.473029"); + +static struct attribute *pa12203001_attrs[] = { + &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group pa12203001_attr_group = { + .attrs = pa12203001_attrs, +}; + +static const struct iio_chan_spec pa12203001_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +static const struct regmap_range pa12203001_volatile_regs_ranges[] = { + regmap_reg_range(PA12203001_REG_ADL, PA12203001_REG_ADL + 1), + regmap_reg_range(PA12203001_REG_PDH, PA12203001_REG_PDH), +}; + +static const struct regmap_access_table pa12203001_volatile_regs = { + .yes_ranges = pa12203001_volatile_regs_ranges, + .n_yes_ranges = ARRAY_SIZE(pa12203001_volatile_regs_ranges), +}; + +static const struct regmap_config pa12203001_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PA12203001_REG_PSET, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &pa12203001_volatile_regs, +}; + +static inline int pa12203001_als_enable(struct pa12203001_data *data, u8 enable) +{ + int ret; + + ret = regmap_update_bits(data->map, PA12203001_REG_CFG0, + PA12203001_ALS_EN_MASK, enable); + if (ret < 0) + return ret; + + data->als_enabled = !!enable; + + return 0; +} + +static inline int pa12203001_px_enable(struct pa12203001_data *data, u8 enable) +{ + int ret; + + ret = regmap_update_bits(data->map, PA12203001_REG_CFG0, + PA12203001_PX_EN_MASK, enable); + if (ret < 0) + return ret; + + data->px_enabled = !!enable; + + return 0; +} + +static int pa12203001_set_power_state(struct pa12203001_data *data, bool on, + u8 mask) +{ +#ifdef CONFIG_PM + int ret; + + if (on && (mask & PA12203001_ALS_EN_MASK)) { + mutex_lock(&data->lock); + if (data->px_enabled) { + ret = pa12203001_als_enable(data, + PA12203001_ALS_EN_MASK); + if (ret < 0) + goto err; + } else { + data->als_needs_enable = true; + } + mutex_unlock(&data->lock); + } + + if (on && (mask & PA12203001_PX_EN_MASK)) { + mutex_lock(&data->lock); + if (data->als_enabled) { + ret = pa12203001_px_enable(data, PA12203001_PX_EN_MASK); + if (ret < 0) + goto err; + } else { + data->px_needs_enable = true; + } + mutex_unlock(&data->lock); + } + + if (on) { + ret = pm_runtime_get_sync(&data->client->dev); + if (ret < 0) + pm_runtime_put_noidle(&data->client->dev); + + } else { + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); + } + + return ret; + +err: + mutex_unlock(&data->lock); + return ret; + +#endif + return 0; +} + +static int pa12203001_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct pa12203001_data *data = iio_priv(indio_dev); + int ret; + u8 dev_mask; + unsigned int reg_byte; + __le16 reg_word; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + dev_mask = PA12203001_ALS_EN_MASK; + ret = pa12203001_set_power_state(data, true, dev_mask); + if (ret < 0) + return ret; + /* + * ALS ADC value is stored in registers + * PA12203001_REG_ADL and in PA12203001_REG_ADL + 1. + */ + ret = regmap_bulk_read(data->map, PA12203001_REG_ADL, + ®_word, 2); + if (ret < 0) + goto reg_err; + + *val = le16_to_cpu(reg_word); + ret = pa12203001_set_power_state(data, false, dev_mask); + if (ret < 0) + return ret; + break; + case IIO_PROXIMITY: + dev_mask = PA12203001_PX_EN_MASK; + ret = pa12203001_set_power_state(data, true, dev_mask); + if (ret < 0) + return ret; + ret = regmap_read(data->map, PA12203001_REG_PDH, + ®_byte); + if (ret < 0) + goto reg_err; + + *val = reg_byte; + ret = pa12203001_set_power_state(data, false, dev_mask); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(data->map, PA12203001_REG_CFG0, ®_byte); + if (ret < 0) + return ret; + *val = 0; + reg_byte = (reg_byte & PA12203001_AFSR_MASK); + *val2 = pa12203001_scales[reg_byte >> 4]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + +reg_err: + pa12203001_set_power_state(data, false, dev_mask); + return ret; +} + +static int pa12203001_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct pa12203001_data *data = iio_priv(indio_dev); + int i, ret, new_val; + unsigned int reg_byte; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(data->map, PA12203001_REG_CFG0, ®_byte); + if (val != 0 || ret < 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(pa12203001_scales); i++) { + if (val2 == pa12203001_scales[i]) { + new_val = i << PA12203001_AFSR_SHIFT; + return regmap_update_bits(data->map, + PA12203001_REG_CFG0, + PA12203001_AFSR_MASK, + new_val); + } + } + break; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info pa12203001_info = { + .driver_module = THIS_MODULE, + .read_raw = pa12203001_read_raw, + .write_raw = pa12203001_write_raw, + .attrs = &pa12203001_attr_group, +}; + +static int pa12203001_init(struct iio_dev *indio_dev) +{ + struct pa12203001_data *data = iio_priv(indio_dev); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(regvals); i++) { + ret = regmap_write(data->map, regvals[i].reg, regvals[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int pa12203001_power_chip(struct iio_dev *indio_dev, u8 state) +{ + struct pa12203001_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = pa12203001_als_enable(data, state); + if (ret < 0) + goto out; + + ret = pa12203001_px_enable(data, state); + +out: + mutex_unlock(&data->lock); + return ret; +} + +static int pa12203001_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pa12203001_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, + sizeof(struct pa12203001_data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + data->map = devm_regmap_init_i2c(client, &pa12203001_regmap_config); + if (IS_ERR(data->map)) + return PTR_ERR(data->map); + + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &pa12203001_info; + indio_dev->name = PA12203001_DRIVER_NAME; + indio_dev->channels = pa12203001_channels; + indio_dev->num_channels = ARRAY_SIZE(pa12203001_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = pa12203001_init(indio_dev); + if (ret < 0) + return ret; + + ret = pa12203001_power_chip(indio_dev, PA12203001_CHIP_ENABLE); + if (ret < 0) + return ret; + + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) { + pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE); + return ret; + } + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + PA12203001_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return iio_device_register(indio_dev); +} + +static int pa12203001_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE); +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM) +static int pa12203001_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + + return pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE); +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int pa12203001_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + + return pa12203001_power_chip(indio_dev, PA12203001_CHIP_ENABLE); +} +#endif + +#ifdef CONFIG_PM +static int pa12203001_runtime_resume(struct device *dev) +{ + struct pa12203001_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + mutex_lock(&data->lock); + if (data->als_needs_enable) { + pa12203001_als_enable(data, PA12203001_ALS_EN_MASK); + data->als_needs_enable = false; + } + if (data->px_needs_enable) { + pa12203001_px_enable(data, PA12203001_PX_EN_MASK); + data->px_needs_enable = false; + } + mutex_unlock(&data->lock); + + return 0; +} +#endif + +static const struct dev_pm_ops pa12203001_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pa12203001_suspend, pa12203001_resume) + SET_RUNTIME_PM_OPS(pa12203001_suspend, pa12203001_runtime_resume, NULL) +}; + +static const struct acpi_device_id pa12203001_acpi_match[] = { + { "TXCPA122", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match); + +static const struct i2c_device_id pa12203001_id[] = { + {"txcpa122", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pa12203001_id); + +static struct i2c_driver pa12203001_driver = { + .driver = { + .name = PA12203001_DRIVER_NAME, + .pm = &pa12203001_pm_ops, + .acpi_match_table = ACPI_PTR(pa12203001_acpi_match), + }, + .probe = pa12203001_probe, + .remove = pa12203001_remove, + .id_table = pa12203001_id, + +}; +module_i2c_driver(pa12203001_driver); + +MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>"); +MODULE_DESCRIPTION("Driver for TXC PA12203001 Proximity and Light Sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 11a027adc204..f101bb5bddc7 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -676,6 +676,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = { {"STK3311", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); static const struct acpi_device_id stk3310_acpi_id[] = { {"STK3310", 0}, |