Merge tag 'iio-for-v4.2a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 9 May 2015 16:15:50 +0000 (18:15 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 9 May 2015 16:15:50 +0000 (18:15 +0200)
Jonathan writes:

First round of new drivers, functionality and cleanups for the 4.2 cycle

New drivers / device support
* st sensors driver, lsm303dlh magnetometer support.
* ltr501 - support ltr301 and ltr559 chips.

New functionality
* IIO_CHAN_INFO_CALIBEMISSIVITY for thermopile sensors.
* kxcjk1013 - make driver operational with external trigger.
* Add iio targets to the tools Makefile.

Cleanups
* st sensors - more helpful error message if device id wrong or irq request
  fails, explicitly make the Block Data Update optional rather
  than relying on writes to address 0 not doing anything, make interrupt
  support optional (Not always wired, and not all devices actually have
  an interrupt line.)
* kxcjk-1013 white space additions for readability, add the KXCJ9000 ACPI
  id as seen in the wild.
* sx9500 - GPIO reset support, refactor the GPIO interrupt code, add power
  management, optimize power usage by powering down when possible, rename
  the gpio interrupt pin to be more useful, trivial return path simplification,
  trivial formatting fixes.
* isl29018 -  move towards ABI compliance with a view to moving this driver
  out of staging, add some brackets to ensure code works as expected.  Note
  there is no actual bug as the condition being tested is always true
  (with current devices).
* ltr501 - add regmap support to get caching etc for later patches,
  fix a parameter sanity check that always fails (bug introduced
  earlier in this series), ACPI enumeration support,
  interrupt rate control support, interrupt support in general and
  integration time control support, code alignment cleanups.
* mma9553 - a number of little cleanups following a review from Hartmut
  after I'd already applied the original driver patch.
* tmp006 - prefix some defines with TMP006 for consistency.
* tsl4531 - cleanup some wrong prefixes, presumably from copy and paste.
* mlx90614 - check for errors in read values, add power management,
  add emissivity setting, add device tree binding documentation,
  fix a duplicate const warning.
* ti_am335x_adc - refactor the DT parsing into a separate function.

25 files changed:
Documentation/ABI/testing/sysfs-bus-iio
Documentation/devicetree/bindings/iio/st-sensors.txt
Documentation/devicetree/bindings/iio/temperature/mlx90614.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
drivers/iio/accel/kxcjk-1013.c
drivers/iio/accel/mma9551_core.c
drivers/iio/accel/mma9551_core.h
drivers/iio/accel/mma9553.c
drivers/iio/adc/ti_am335x_adc.c
drivers/iio/common/st_sensors/st_sensors_core.c
drivers/iio/common/st_sensors/st_sensors_trigger.c
drivers/iio/industrialio-core.c
drivers/iio/light/Kconfig
drivers/iio/light/ltr501.c
drivers/iio/light/tsl2563.c
drivers/iio/light/tsl4531.c
drivers/iio/magnetometer/st_magn.h
drivers/iio/magnetometer/st_magn_core.c
drivers/iio/magnetometer/st_magn_i2c.c
drivers/iio/proximity/sx9500.c
drivers/iio/temperature/mlx90614.c
drivers/iio/temperature/tmp006.c
drivers/staging/iio/light/isl29018.c
include/linux/iio/iio.h
tools/Makefile

index 3befcb1..866b4ec 100644 (file)
@@ -1364,3 +1364,14 @@ Description:
                hwfifo_watermak_min but not equal to any of the values in this
                list, the driver will chose an appropriate value for the
                hardware fifo watermark level.
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_temp_calibemissivity
+What:          /sys/bus/iio/devices/iio:deviceX/in_tempX_calibemissivity
+What:          /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibemissivity
+What:          /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibemissivity
+KernelVersion: 4.1
+Contact:       linux-iio@vger.kernel.org
+Description:
+               The emissivity ratio of the surface in the field of view of the
+               contactless temperature sensor.  Emissivity varies from 0 to 1,
+               with 1 being the emissivity of a black body.
index d2aaca9..fb5e0c2 100644 (file)
@@ -45,6 +45,7 @@ Gyroscopes:
 - st,lsm330-gyro
 
 Magnetometers:
+- st,lsm303dlh-magn
 - st,lsm303dlhc-magn
 - st,lsm303dlm-magn
 - st,lis3mdl-magn
diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
new file mode 100644 (file)
index 0000000..9be57b0
--- /dev/null
@@ -0,0 +1,24 @@
+* Melexis MLX90614 contactless IR temperature sensor
+
+http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
+
+Required properties:
+
+  - compatible: should be "melexis,mlx90614"
+  - reg: the I2C address of the sensor
+
+Optional properties:
+
+  - wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
+      to hold low in order to wake up the device.  In normal operation, the
+      GPIO is set as input and will not interfere in I2C communication.  There
+      is no need for a GPIO driving the SCL line.  If no GPIO is given, power
+      management is disabled.
+
+Example:
+
+mlx90614@5a {
+       compatible = "melexis,mlx90614";
+       reg = <0x5a>;
+       wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+};
index 8033919..b90ea45 100644 (file)
@@ -117,6 +117,7 @@ lltc        Linear Technology Corporation
 marvell        Marvell Technology Group Ltd.
 maxim  Maxim Integrated Products
 mediatek       MediaTek Inc.
+melexis        Melexis N.V.
 merrii Merrii Technology Co., Ltd.
 micrel Micrel Inc.
 microchip      Microchip Technology Inc.
index 51da369..0d9bd35 100644 (file)
@@ -875,15 +875,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
        return 0;
 }
 
-static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
-                                     struct iio_trigger *trig)
+static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
 {
        struct kxcjk1013_data *data = iio_priv(indio_dev);
 
-       if (data->dready_trig != trig && data->motion_trig != trig)
-               return -EINVAL;
+       return kxcjk1013_set_power_state(data, true);
+}
 
-       return 0;
+static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
+{
+       struct kxcjk1013_data *data = iio_priv(indio_dev);
+
+       return kxcjk1013_set_power_state(data, false);
 }
 
 static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
@@ -935,6 +938,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
        IIO_CHAN_SOFT_TIMESTAMP(3),
 };
 
+static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
+       .preenable              = kxcjk1013_buffer_preenable,
+       .postenable             = iio_triggered_buffer_postenable,
+       .postdisable            = kxcjk1013_buffer_postdisable,
+       .predisable             = iio_triggered_buffer_predisable,
+};
+
 static const struct iio_info kxcjk1013_info = {
        .attrs                  = &kxcjk1013_attrs_group,
        .read_raw               = kxcjk1013_read_raw,
@@ -943,7 +953,6 @@ static const struct iio_info kxcjk1013_info = {
        .write_event_value      = kxcjk1013_write_event,
        .write_event_config     = kxcjk1013_write_event_config,
        .read_event_config      = kxcjk1013_read_event_config,
-       .validate_trigger       = kxcjk1013_validate_trigger,
        .driver_module          = THIS_MODULE,
 };
 
@@ -1147,8 +1156,10 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
        id = acpi_match_device(dev->driver->acpi_match_table, dev);
        if (!id)
                return NULL;
+
        if (strcmp(id->id, "SMO8500") == 0)
                *is_smo8500_device = true;
+
        *chipset = (enum kx_chipset)id->driver_data;
 
        return dev_name(dev);
@@ -1163,6 +1174,7 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
 
        if (!client)
                return -EINVAL;
+
        if (data->is_smo8500_device)
                return -ENOTSUPP;
 
@@ -1276,16 +1288,15 @@ static int kxcjk1013_probe(struct i2c_client *client,
                        data->motion_trig = NULL;
                        goto err_trigger_unregister;
                }
+       }
 
-               ret = iio_triggered_buffer_setup(indio_dev,
-                                               &iio_pollfunc_store_time,
-                                               kxcjk1013_trigger_handler,
-                                               NULL);
-               if (ret < 0) {
-                       dev_err(&client->dev,
-                                       "iio triggered buffer setup failed\n");
-                       goto err_trigger_unregister;
-               }
+       ret = iio_triggered_buffer_setup(indio_dev,
+                                        &iio_pollfunc_store_time,
+                                        kxcjk1013_trigger_handler,
+                                        &kxcjk1013_buffer_setup_ops);
+       if (ret < 0) {
+               dev_err(&client->dev, "iio triggered buffer setup failed\n");
+               goto err_trigger_unregister;
        }
 
        ret = iio_device_register(indio_dev);
@@ -1418,6 +1429,7 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = {
 static const struct acpi_device_id kx_acpi_match[] = {
        {"KXCJ1013", KXCJK1013},
        {"KXCJ1008", KXCJ91008},
+       {"KXCJ9000", KXCJ91008},
        {"KXTJ1009", KXTJ21009},
        {"SMO8500",  KXCJ91008},
        { },
index 7f55a6d..54b3ae6 100644 (file)
@@ -374,7 +374,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
  * @app_id:    Application ID
  * @reg:       Application register
  * @len:       Length of array to read in bytes
- * @val:       Array of words to read
+ * @buf:       Array of words to read
  *
  * Read multiple configuration registers (word-sized registers).
  *
@@ -409,7 +409,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
  * @app_id:    Application ID
  * @reg:       Application register
  * @len:       Length of array to read in bytes
- * @val:       Array of words to read
+ * @buf:       Array of words to read
  *
  * Read multiple status registers (word-sized registers).
  *
@@ -444,7 +444,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
  * @app_id:    Application ID
  * @reg:       Application register
  * @len:       Length of array to write in bytes
- * @val:       Array of words to write
+ * @buf:       Array of words to write
  *
  * Write multiple configuration registers (word-sized registers).
  *
@@ -785,7 +785,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
  */
 int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
 {
-       return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
+       return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
                                         MMA9551_RSC_RESET +
                                         MMA9551_RSC_OFFSET(app_mask),
                                         MMA9551_RSC_VAL(app_mask));
index edaa56b..79939e4 100644 (file)
@@ -22,7 +22,7 @@
 #define MMA9551_APPID_TILT             0x0B
 #define MMA9551_APPID_SLEEP_WAKE       0x12
 #define MMA9551_APPID_PEDOMETER                0x15
-#define MMA9551_APPID_RCS              0x17
+#define MMA9551_APPID_RSC              0x17
 #define MMA9551_APPID_NONE             0xff
 
 /* Reset/Suspend/Clear application app masks */
index 2df1af7..9d649c4 100644 (file)
@@ -62,8 +62,8 @@
 #define MMA9553_MASK_STATUS_STEPCHG            BIT(13)
 #define MMA9553_MASK_STATUS_ACTCHG             BIT(12)
 #define MMA9553_MASK_STATUS_SUSP               BIT(11)
-#define MMA9553_MASK_STATUS_ACTIVITY           (BIT(10) | BIT(9) | BIT(8))
-#define MMA9553_MASK_STATUS_VERSION            0x00FF
+#define MMA9553_MASK_STATUS_ACTIVITY           GENMASK(10, 8)
+#define MMA9553_MASK_STATUS_VERSION            GENMASK(7, 0)
 
 #define MMA9553_REG_STEPCNT                    0x02
 #define MMA9553_REG_DISTANCE                   0x04
 #define MMA9553_DEFAULT_GPIO_PIN       mma9551_gpio6
 #define MMA9553_DEFAULT_GPIO_POLARITY  0
 
-/* Bitnum used for gpio configuration = bit number in high status byte */
-#define STATUS_TO_BITNUM(bit)          (ffs(bit) - 9)
+/* Bitnum used for GPIO configuration = bit number in high status byte */
+#define MMA9553_STATUS_TO_BITNUM(bit)  (ffs(bit) - 9)
+#define MMA9553_MAX_BITNUM             MMA9553_STATUS_TO_BITNUM(BIT(16))
 
 #define MMA9553_DEFAULT_SAMPLE_RATE    30      /* Hz */
 
 /*
  * The internal activity level must be stable for ACTTHD samples before
- * ACTIVITY is updated.The ACTIVITY variable contains the current activity
+ * ACTIVITY is updated. The ACTIVITY variable contains the current activity
  * level and is updated every time a step is detected or once a second
  * if there are no steps.
  */
@@ -353,11 +354,11 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
         * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
         */
        if (activity_enabled && ev_step_detect->enabled)
-               bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
+               bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
        else if (ev_step_detect->enabled)
-               bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
+               bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
        else if (activity_enabled)
-               bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
+               bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
        else                    /* Reset */
                appid = MMA9551_APPID_NONE;
 
@@ -365,9 +366,12 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
                return 0;
 
        /* Save initial values for activity and stepcnt */
-       if (activity_enabled || ev_step_detect->enabled)
-               mma9553_read_activity_stepcnt(data, &data->activity,
-                                             &data->stepcnt);
+       if (activity_enabled || ev_step_detect->enabled) {
+               ret = mma9553_read_activity_stepcnt(data, &data->activity,
+                                                   &data->stepcnt);
+               if (ret < 0)
+                       return ret;
+       }
 
        ret = mma9551_gpio_config(data->client,
                                  MMA9553_DEFAULT_GPIO_PIN,
@@ -398,13 +402,13 @@ static int mma9553_init(struct mma9553_data *data)
                                      sizeof(data->conf), (u16 *) &data->conf);
        if (ret < 0) {
                dev_err(&data->client->dev,
-                       "device is not MMA9553L: failed to read cfg regs\n");
+                       "failed to read configuration registers\n");
                return ret;
        }
 
 
-       /* Reset gpio */
-       data->gpio_bitnum = -1;
+       /* Reset GPIO */
+       data->gpio_bitnum = MMA9553_MAX_BITNUM;
        ret = mma9553_conf_gpio(data);
        if (ret < 0)
                return ret;
@@ -438,6 +442,32 @@ static int mma9553_init(struct mma9553_data *data)
        return mma9551_set_device_state(data->client, true);
 }
 
+static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
+                                   u16 *tmp)
+{
+       bool powered_on;
+       int ret;
+
+       /*
+        * The HW only counts steps and other dependent
+        * parameters (speed, distance, calories, activity)
+        * if power is on (from enabling an event or the
+        * step counter).
+        */
+       powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
+                    data->stepcnt_enabled;
+       if (!powered_on) {
+               dev_err(&data->client->dev, "No channels enabled\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->mutex);
+       ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
+                                      reg, tmp);
+       mutex_unlock(&data->mutex);
+       return ret;
+}
+
 static int mma9553_read_raw(struct iio_dev *indio_dev,
                            struct iio_chan_spec const *chan,
                            int *val, int *val2, long mask)
@@ -446,69 +476,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
        int ret;
        u16 tmp;
        u8 activity;
-       bool powered_on;
 
        switch (mask) {
        case IIO_CHAN_INFO_PROCESSED:
                switch (chan->type) {
                case IIO_STEPS:
-                       /*
-                        * The HW only counts steps and other dependent
-                        * parameters (speed, distance, calories, activity)
-                        * if power is on (from enabling an event or the
-                        * step counter */
-                       powered_on =
-                           mma9553_is_any_event_enabled(data, false, 0) ||
-                           data->stepcnt_enabled;
-                       if (!powered_on) {
-                               dev_err(&data->client->dev,
-                                       "No channels enabled\n");
-                               return -EINVAL;
-                       }
-                       mutex_lock(&data->mutex);
-                       ret = mma9551_read_status_word(data->client,
-                                                      MMA9551_APPID_PEDOMETER,
+                       ret = mma9553_read_status_word(data,
                                                       MMA9553_REG_STEPCNT,
                                                       &tmp);
-                       mutex_unlock(&data->mutex);
                        if (ret < 0)
                                return ret;
                        *val = tmp;
                        return IIO_VAL_INT;
                case IIO_DISTANCE:
-                       powered_on =
-                           mma9553_is_any_event_enabled(data, false, 0) ||
-                           data->stepcnt_enabled;
-                       if (!powered_on) {
-                               dev_err(&data->client->dev,
-                                       "No channels enabled\n");
-                               return -EINVAL;
-                       }
-                       mutex_lock(&data->mutex);
-                       ret = mma9551_read_status_word(data->client,
-                                                      MMA9551_APPID_PEDOMETER,
+                       ret = mma9553_read_status_word(data,
                                                       MMA9553_REG_DISTANCE,
                                                       &tmp);
-                       mutex_unlock(&data->mutex);
                        if (ret < 0)
                                return ret;
                        *val = tmp;
                        return IIO_VAL_INT;
                case IIO_ACTIVITY:
-                       powered_on =
-                           mma9553_is_any_event_enabled(data, false, 0) ||
-                           data->stepcnt_enabled;
-                       if (!powered_on) {
-                               dev_err(&data->client->dev,
-                                       "No channels enabled\n");
-                               return -EINVAL;
-                       }
-                       mutex_lock(&data->mutex);
-                       ret = mma9551_read_status_word(data->client,
-                                                      MMA9551_APPID_PEDOMETER,
+                       ret = mma9553_read_status_word(data,
                                                       MMA9553_REG_STATUS,
                                                       &tmp);
-                       mutex_unlock(&data->mutex);
                        if (ret < 0)
                                return ret;
 
@@ -533,38 +524,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
                case IIO_VELOCITY:      /* m/h */
                        if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
                                return -EINVAL;
-                       powered_on =
-                           mma9553_is_any_event_enabled(data, false, 0) ||
-                           data->stepcnt_enabled;
-                       if (!powered_on) {
-                               dev_err(&data->client->dev,
-                                       "No channels enabled\n");
-                               return -EINVAL;
-                       }
-                       mutex_lock(&data->mutex);
-                       ret = mma9551_read_status_word(data->client,
-                                                      MMA9551_APPID_PEDOMETER,
-                                                      MMA9553_REG_SPEED, &tmp);
-                       mutex_unlock(&data->mutex);
+                       ret = mma9553_read_status_word(data,
+                                                      MMA9553_REG_SPEED,
+                                                      &tmp);
                        if (ret < 0)
                                return ret;
                        *val = tmp;
                        return IIO_VAL_INT;
                case IIO_ENERGY:        /* Cal or kcal */
-                       powered_on =
-                           mma9553_is_any_event_enabled(data, false, 0) ||
-                           data->stepcnt_enabled;
-                       if (!powered_on) {
-                               dev_err(&data->client->dev,
-                                       "No channels enabled\n");
-                               return -EINVAL;
-                       }
-                       mutex_lock(&data->mutex);
-                       ret = mma9551_read_status_word(data->client,
-                                                      MMA9551_APPID_PEDOMETER,
+                       ret = mma9553_read_status_word(data,
                                                       MMA9553_REG_CALORIES,
                                                       &tmp);
-                       mutex_unlock(&data->mutex);
                        if (ret < 0)
                                return ret;
                        *val = tmp;
@@ -791,7 +761,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
 
        mutex_unlock(&data->mutex);
 
-       return ret;
+       return 0;
 
 err_conf_gpio:
        if (state) {
@@ -896,7 +866,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
        gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
        /*
         * HW expects 0 for female and 1 for male,
-        * while iio index is 0 for male and 1 for female
+        * while iio index is 0 for male and 1 for female.
         */
        return !gender;
 }
@@ -943,11 +913,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
        },
 };
 
-static const char * const calibgender_modes[] = { "male", "female" };
+static const char * const mma9553_calibgender_modes[] = { "male", "female" };
 
 static const struct iio_enum mma9553_calibgender_enum = {
-       .items = calibgender_modes,
-       .num_items = ARRAY_SIZE(calibgender_modes),
+       .items = mma9553_calibgender_modes,
+       .num_items = ARRAY_SIZE(mma9553_calibgender_modes),
        .get = mma9553_get_calibgender_mode,
        .set = mma9553_set_calibgender_mode,
 };
@@ -1108,16 +1078,16 @@ static int mma9553_gpio_probe(struct i2c_client *client)
 
        dev = &client->dev;
 
-       /* data ready gpio interrupt pin */
+       /* data ready GPIO interrupt pin */
        gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
        if (IS_ERR(gpio)) {
-               dev_err(dev, "acpi gpio get index failed\n");
+               dev_err(dev, "ACPI GPIO get index failed\n");
                return PTR_ERR(gpio);
        }
 
        ret = gpiod_to_irq(gpio);
 
-       dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+       dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
 
        return ret;
 }
index a0e7161..42e4440 100644 (file)
@@ -395,16 +395,30 @@ static const struct iio_info tiadc_info = {
        .driver_module = THIS_MODULE,
 };
 
+static int tiadc_parse_dt(struct platform_device *pdev,
+                         struct tiadc_device *adc_dev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct property *prop;
+       const __be32 *cur;
+       int channels = 0;
+       u32 val;
+
+       of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+               adc_dev->channel_line[channels] = val;
+               channels++;
+       }
+
+       adc_dev->channels = channels;
+       return 0;
+}
+
 static int tiadc_probe(struct platform_device *pdev)
 {
        struct iio_dev          *indio_dev;
        struct tiadc_device     *adc_dev;
        struct device_node      *node = pdev->dev.of_node;
-       struct property         *prop;
-       const __be32            *cur;
        int                     err;
-       u32                     val;
-       int                     channels = 0;
 
        if (!node) {
                dev_err(&pdev->dev, "Could not find valid DT data.\n");
@@ -420,12 +434,7 @@ static int tiadc_probe(struct platform_device *pdev)
        adc_dev = iio_priv(indio_dev);
 
        adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
-
-       of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
-               adc_dev->channel_line[channels] = val;
-               channels++;
-       }
-       adc_dev->channels = channels;
+       tiadc_parse_dt(pdev, adc_dev);
 
        indio_dev->dev.parent = &pdev->dev;
        indio_dev->name = dev_name(&pdev->dev);
index edd13d2..1255b15 100644 (file)
@@ -245,6 +245,16 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
 {
        struct st_sensor_data *sdata = iio_priv(indio_dev);
 
+       /* Sensor does not support interrupts */
+       if (sdata->sensor_settings->drdy_irq.addr == 0) {
+               if (pdata->drdy_int_pin)
+                       dev_info(&indio_dev->dev,
+                                "DRDY on pin INT%d specified, but sensor "
+                                "does not support interrupts\n",
+                                pdata->drdy_int_pin);
+               return 0;
+       }
+
        switch (pdata->drdy_int_pin) {
        case 1:
                if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
@@ -285,7 +295,7 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
        if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
                pdata->drdy_int_pin = (u8) val;
        else
-               pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
+               pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
 
        return pdata;
 }
@@ -334,11 +344,13 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
                return err;
 
        /* set BDU */
-       err = st_sensors_write_data_with_mask(indio_dev,
+       if (sdata->sensor_settings->bdu.addr) {
+               err = st_sensors_write_data_with_mask(indio_dev,
                                        sdata->sensor_settings->bdu.addr,
                                        sdata->sensor_settings->bdu.mask, true);
-       if (err < 0)
-               return err;
+               if (err < 0)
+                       return err;
+       }
 
        err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
 
@@ -491,7 +503,8 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
                        break;
        }
        if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
-               dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
+               dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
+                       indio_dev->name, wai);
                goto sensor_name_mismatch;
        }
 
index 8d8ca6f..3e90704 100644 (file)
@@ -37,8 +37,10 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
                        IRQF_TRIGGER_RISING,
                        sdata->trig->name,
                        sdata->trig);
-       if (err)
+       if (err) {
+               dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
                goto request_irq_error;
+       }
 
        iio_trigger_set_drvdata(sdata->trig, indio_dev);
        sdata->trig->ops = trigger_ops;
index 4df97f6..7c98bc1 100644 (file)
@@ -128,6 +128,7 @@ static const char * const iio_chan_info_postfix[] = {
        [IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
        [IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
        [IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
+       [IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity",
 };
 
 /**
index 01a1a16..a437bad 100644 (file)
@@ -169,7 +169,8 @@ config LTR501
        select IIO_TRIGGERED_BUFFER
        help
         If you say yes here you get support for the Lite-On LTR-501ALS-01
-        ambient light and proximity sensor.
+        ambient light and proximity sensor. This driver also supports LTR-559
+        ALS/PS or LTR-301 ALS sensors.
 
         This driver can also be built as a module.  If so, the module
          will be called ltr501.
index 78b8783..280eff1 100644 (file)
@@ -9,15 +9,18 @@
  *
  * 7-bit I2C slave address 0x23
  *
- * TODO: interrupt, threshold, measurement rate, IR LED characteristics
+ * TODO: IR LED characteristics
  */
 
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/events.h>
 #include <linux/iio/sysfs.h>
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/buffer.h>
 
 #define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */
 #define LTR501_PS_CONTR 0x81 /* PS operation mode */
+#define LTR501_PS_MEAS_RATE 0x84 /* measurement rate*/
+#define LTR501_ALS_MEAS_RATE 0x85 /* ALS integ time, measurement rate*/
 #define LTR501_PART_ID 0x86
 #define LTR501_MANUFAC_ID 0x87
 #define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */
 #define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */
 #define LTR501_ALS_PS_STATUS 0x8c
 #define LTR501_PS_DATA 0x8d /* 16-bit, little endian */
+#define LTR501_INTR 0x8f /* output mode, polarity, mode */
+#define LTR501_PS_THRESH_UP 0x90 /* 11 bit, ps upper threshold */
+#define LTR501_PS_THRESH_LOW 0x92 /* 11 bit, ps lower threshold */
+#define LTR501_ALS_THRESH_UP 0x97 /* 16 bit, ALS upper threshold */
+#define LTR501_ALS_THRESH_LOW 0x99 /* 16 bit, ALS lower threshold */
+#define LTR501_INTR_PRST 0x9e /* ps thresh, als thresh */
+#define LTR501_MAX_REG 0x9f
 
 #define LTR501_ALS_CONTR_SW_RESET BIT(2)
 #define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2))
 #define LTR501_CONTR_ALS_GAIN_MASK BIT(3)
 #define LTR501_CONTR_ACTIVE BIT(1)
 
+#define LTR501_STATUS_ALS_INTR BIT(3)
 #define LTR501_STATUS_ALS_RDY BIT(2)
+#define LTR501_STATUS_PS_INTR BIT(1)
 #define LTR501_STATUS_PS_RDY BIT(0)
 
 #define LTR501_PS_DATA_MASK 0x7ff
+#define LTR501_PS_THRESH_MASK 0x7ff
+#define LTR501_ALS_THRESH_MASK 0xffff
+
+#define LTR501_ALS_DEF_PERIOD 500000
+#define LTR501_PS_DEF_PERIOD 100000
+
+#define LTR501_REGMAP_NAME "ltr501_regmap"
+
+static const int int_time_mapping[] = {100000, 50000, 200000, 400000};
+
+static const struct reg_field reg_field_it =
+                               REG_FIELD(LTR501_ALS_MEAS_RATE, 3, 4);
+static const struct reg_field reg_field_als_intr =
+                               REG_FIELD(LTR501_INTR, 0, 0);
+static const struct reg_field reg_field_ps_intr =
+                               REG_FIELD(LTR501_INTR, 1, 1);
+static const struct reg_field reg_field_als_rate =
+                               REG_FIELD(LTR501_ALS_MEAS_RATE, 0, 2);
+static const struct reg_field reg_field_ps_rate =
+                               REG_FIELD(LTR501_PS_MEAS_RATE, 0, 3);
+static const struct reg_field reg_field_als_prst =
+                               REG_FIELD(LTR501_INTR_PRST, 0, 3);
+static const struct reg_field reg_field_ps_prst =
+                               REG_FIELD(LTR501_INTR_PRST, 4, 7);
+
+struct ltr501_samp_table {
+       int freq_val;  /* repetition frequency in micro HZ*/
+       int time_val; /* repetition rate in micro seconds */
+};
+
+#define LTR501_RESERVED_GAIN -1
+
+enum {
+       ltr501 = 0,
+       ltr559,
+       ltr301,
+};
+
+struct ltr501_gain {
+       int scale;
+       int uscale;
+};
+
+static struct ltr501_gain ltr501_als_gain_tbl[] = {
+       {1, 0},
+       {0, 5000},
+};
+
+static struct ltr501_gain ltr559_als_gain_tbl[] = {
+       {1, 0},
+       {0, 500000},
+       {0, 250000},
+       {0, 125000},
+       {LTR501_RESERVED_GAIN, LTR501_RESERVED_GAIN},
+       {LTR501_RESERVED_GAIN, LTR501_RESERVED_GAIN},
+       {0, 20000},
+       {0, 10000},
+};
+
+static struct ltr501_gain ltr501_ps_gain_tbl[] = {
+       {1, 0},
+       {0, 250000},
+       {0, 125000},
+       {0, 62500},
+};
+
+static struct ltr501_gain ltr559_ps_gain_tbl[] = {
+       {0, 62500}, /* x16 gain */
+       {0, 31250}, /* x32 gain */
+       {0, 15625}, /* bits X1 are for x64 gain */
+       {0, 15624},
+};
+
+struct ltr501_chip_info {
+       u8 partid;
+       struct ltr501_gain *als_gain;
+       int als_gain_tbl_size;
+       struct ltr501_gain *ps_gain;
+       int ps_gain_tbl_size;
+       u8 als_mode_active;
+       u8 als_gain_mask;
+       u8 als_gain_shift;
+       struct iio_chan_spec const *channels;
+       const int no_channels;
+       const struct iio_info *info;
+       const struct iio_info *info_no_irq;
+};
 
 struct ltr501_data {
        struct i2c_client *client;
        struct mutex lock_als, lock_ps;
+       struct ltr501_chip_info *chip_info;
        u8 als_contr, ps_contr;
+       int als_period, ps_period; /* period in micro seconds */
+       struct regmap *regmap;
+       struct regmap_field *reg_it;
+       struct regmap_field *reg_als_intr;
+       struct regmap_field *reg_ps_intr;
+       struct regmap_field *reg_als_rate;
+       struct regmap_field *reg_ps_rate;
+       struct regmap_field *reg_als_prst;
+       struct regmap_field *reg_ps_prst;
+};
+
+static const struct ltr501_samp_table ltr501_als_samp_table[] = {
+                       {20000000, 50000}, {10000000, 100000},
+                       {5000000, 200000}, {2000000, 500000},
+                       {1000000, 1000000}, {500000, 2000000},
+                       {500000, 2000000}, {500000, 2000000}
 };
 
+static const struct ltr501_samp_table ltr501_ps_samp_table[] = {
+                       {20000000, 50000}, {14285714, 70000},
+                       {10000000, 100000}, {5000000, 200000},
+                       {2000000, 500000}, {1000000, 1000000},
+                       {500000, 2000000}, {500000, 2000000},
+                       {500000, 2000000}
+};
+
+static unsigned int ltr501_match_samp_freq(const struct ltr501_samp_table *tab,
+                                          int len, int val, int val2)
+{
+       int i, freq;
+
+       freq = val * 1000000 + val2;
+
+       for (i = 0; i < len; i++) {
+               if (tab[i].freq_val == freq)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_als_read_samp_freq(struct ltr501_data *data,
+                                    int *val, int *val2)
+{
+       int ret, i;
+
+       ret = regmap_field_read(data->reg_als_rate, &i);
+       if (ret < 0)
+               return ret;
+
+       if (i < 0 || i >= ARRAY_SIZE(ltr501_als_samp_table))
+               return -EINVAL;
+
+       *val = ltr501_als_samp_table[i].freq_val / 1000000;
+       *val2 = ltr501_als_samp_table[i].freq_val % 1000000;
+
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ltr501_ps_read_samp_freq(struct ltr501_data *data,
+                                   int *val, int *val2)
+{
+       int ret, i;
+
+       ret = regmap_field_read(data->reg_ps_rate, &i);
+       if (ret < 0)
+               return ret;
+
+       if (i < 0 || i >= ARRAY_SIZE(ltr501_ps_samp_table))
+               return -EINVAL;
+
+       *val = ltr501_ps_samp_table[i].freq_val / 1000000;
+       *val2 = ltr501_ps_samp_table[i].freq_val % 1000000;
+
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ltr501_als_write_samp_freq(struct ltr501_data *data,
+                                     int val, int val2)
+{
+       int i, ret;
+
+       i = ltr501_match_samp_freq(ltr501_als_samp_table,
+                                  ARRAY_SIZE(ltr501_als_samp_table),
+                                  val, val2);
+
+       if (i < 0)
+               return i;
+
+       mutex_lock(&data->lock_als);
+       ret = regmap_field_write(data->reg_als_rate, i);
+       mutex_unlock(&data->lock_als);
+
+       return ret;
+}
+
+static int ltr501_ps_write_samp_freq(struct ltr501_data *data,
+                                    int val, int val2)
+{
+       int i, ret;
+
+       i = ltr501_match_samp_freq(ltr501_ps_samp_table,
+                                  ARRAY_SIZE(ltr501_ps_samp_table),
+                                  val, val2);
+
+       if (i < 0)
+               return i;
+
+       mutex_lock(&data->lock_ps);
+       ret = regmap_field_write(data->reg_ps_rate, i);
+       mutex_unlock(&data->lock_ps);
+
+       return ret;
+}
+
+static int ltr501_als_read_samp_period(struct ltr501_data *data, int *val)
+{
+       int ret, i;
+
+       ret = regmap_field_read(data->reg_als_rate, &i);
+       if (ret < 0)
+               return ret;
+
+       if (i < 0 || i >= ARRAY_SIZE(ltr501_als_samp_table))
+               return -EINVAL;
+
+       *val = ltr501_als_samp_table[i].time_val;
+
+       return IIO_VAL_INT;
+}
+
+static int ltr501_ps_read_samp_period(struct ltr501_data *data, int *val)
+{
+       int ret, i;
+
+       ret = regmap_field_read(data->reg_ps_rate, &i);
+       if (ret < 0)
+               return ret;
+
+       if (i < 0 || i >= ARRAY_SIZE(ltr501_ps_samp_table))
+               return -EINVAL;
+
+       *val = ltr501_ps_samp_table[i].time_val;
+
+       return IIO_VAL_INT;
+}
+
 static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
 {
        int tries = 100;
-       int ret;
+       int ret, status;
 
        while (tries--) {
-               ret = i2c_smbus_read_byte_data(data->client,
-                       LTR501_ALS_PS_STATUS);
+               ret = regmap_read(data->regmap, LTR501_ALS_PS_STATUS, &status);
                if (ret < 0)
                        return ret;
-               if ((ret & drdy_mask) == drdy_mask)
+               if ((status & drdy_mask) == drdy_mask)
                        return 0;
                msleep(25);
        }
@@ -70,25 +316,221 @@ static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
        return -EIO;
 }
 
+static int ltr501_set_it_time(struct ltr501_data *data, int it)
+{
+       int ret, i, index = -1, status;
+
+       for (i = 0; i < ARRAY_SIZE(int_time_mapping); i++) {
+               if (int_time_mapping[i] == it) {
+                       index = i;
+                       break;
+               }
+       }
+       /* Make sure integ time index is valid */
+       if (index < 0)
+               return -EINVAL;
+
+       ret = regmap_read(data->regmap, LTR501_ALS_CONTR, &status);
+       if (ret < 0)
+               return ret;
+
+       if (status & LTR501_CONTR_ALS_GAIN_MASK) {
+               /*
+                * 200 ms and 400 ms integ time can only be
+                * used in dynamic range 1
+                */
+               if (index > 1)
+                       return -EINVAL;
+       } else
+               /* 50 ms integ time can only be used in dynamic range 2 */
+               if (index == 1)
+                       return -EINVAL;
+
+       return regmap_field_write(data->reg_it, index);
+}
+
+/* read int time in micro seconds */
+static int ltr501_read_it_time(struct ltr501_data *data, int *val, int *val2)
+{
+       int ret, index;
+
+       ret = regmap_field_read(data->reg_it, &index);
+       if (ret < 0)
+               return ret;
+
+       /* Make sure integ time index is valid */
+       if (index < 0 || index >= ARRAY_SIZE(int_time_mapping))
+               return -EINVAL;
+
+       *val2 = int_time_mapping[index];
+       *val = 0;
+
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
 static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2])
 {
-       int ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
+       int ret;
+
+       ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
        if (ret < 0)
                return ret;
        /* always read both ALS channels in given order */
-       return i2c_smbus_read_i2c_block_data(data->client,
-               LTR501_ALS_DATA1, 2 * sizeof(__le16), (u8 *) buf);
+       return regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
+                               buf, 2 * sizeof(__le16));
 }
 
 static int ltr501_read_ps(struct ltr501_data *data)
 {
-       int ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
+       int ret, status;
+
+       ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
+                              &status, 2);
        if (ret < 0)
                return ret;
-       return i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
+
+       return status;
 }
 
-#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \
+static int ltr501_read_intr_prst(struct ltr501_data *data,
+                                enum iio_chan_type type,
+                                int *val2)
+{
+       int ret, samp_period, prst;
+
+       switch (type) {
+       case IIO_INTENSITY:
+               ret = regmap_field_read(data->reg_als_prst, &prst);
+               if (ret < 0)
+                       return ret;
+
+               ret = ltr501_als_read_samp_period(data, &samp_period);
+
+               if (ret < 0)
+                       return ret;
+               *val2 = samp_period * prst;
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_PROXIMITY:
+               ret = regmap_field_read(data->reg_ps_prst, &prst);
+               if (ret < 0)
+                       return ret;
+
+               ret = ltr501_ps_read_samp_period(data, &samp_period);
+
+               if (ret < 0)
+                       return ret;
+
+               *val2 = samp_period * prst;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_write_intr_prst(struct ltr501_data *data,
+                                 enum iio_chan_type type,
+                                 int val, int val2)
+{
+       int ret, samp_period, new_val;
+       unsigned long period;
+
+       if (val < 0 || val2 < 0)
+               return -EINVAL;
+
+       /* period in microseconds */
+       period = ((val * 1000000) + val2);
+
+       switch (type) {
+       case IIO_INTENSITY:
+               ret = ltr501_als_read_samp_period(data, &samp_period);
+               if (ret < 0)
+                       return ret;
+
+               /* period should be atleast equal to sampling period */
+               if (period < samp_period)
+                       return -EINVAL;
+
+               new_val = DIV_ROUND_UP(period, samp_period);
+               if (new_val < 0 || new_val > 0x0f)
+                       return -EINVAL;
+
+               mutex_lock(&data->lock_als);
+               ret = regmap_field_write(data->reg_als_prst, new_val);
+               mutex_unlock(&data->lock_als);
+               if (ret >= 0)
+                       data->als_period = period;
+
+               return ret;
+       case IIO_PROXIMITY:
+               ret = ltr501_ps_read_samp_period(data, &samp_period);
+               if (ret < 0)
+                       return ret;
+
+               /* period should be atleast equal to rate */
+               if (period < samp_period)
+                       return -EINVAL;
+
+               new_val = DIV_ROUND_UP(period, samp_period);
+               if (new_val < 0 || new_val > 0x0f)
+                       return -EINVAL;
+
+               mutex_lock(&data->lock_ps);
+               ret = regmap_field_write(data->reg_ps_prst, new_val);
+               mutex_unlock(&data->lock_ps);
+               if (ret >= 0)
+                       data->ps_period = period;
+
+               return ret;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static const struct iio_event_spec ltr501_als_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+                                BIT(IIO_EV_INFO_PERIOD),
+       },
+
+};
+
+static const struct iio_event_spec ltr501_pxs_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+                                BIT(IIO_EV_INFO_PERIOD),
+       },
+};
+
+#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared, \
+                                _evspec, _evsize) { \
        .type = IIO_INTENSITY, \
        .modified = 1, \
        .address = (_addr), \
@@ -101,13 +543,20 @@ static int ltr501_read_ps(struct ltr501_data *data)
                .realbits = 16, \
                .storagebits = 16, \
                .endianness = IIO_CPU, \
-       } \
+       }, \
+       .event_spec = _evspec,\
+       .num_event_specs = _evsize,\
 }
 
 static const struct iio_chan_spec ltr501_channels[] = {
-       LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0),
+       LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
+                                ltr501_als_event_spec,
+                                ARRAY_SIZE(ltr501_als_event_spec)),
        LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
-               BIT(IIO_CHAN_INFO_SCALE)),
+                                BIT(IIO_CHAN_INFO_SCALE) |
+                                BIT(IIO_CHAN_INFO_INT_TIME) |
+                                BIT(IIO_CHAN_INFO_SAMP_FREQ),
+                                NULL, 0),
        {
                .type = IIO_PROXIMITY,
                .address = LTR501_PS_DATA,
@@ -120,17 +569,27 @@ static const struct iio_chan_spec ltr501_channels[] = {
                        .storagebits = 16,
                        .endianness = IIO_CPU,
                },
+               .event_spec = ltr501_pxs_event_spec,
+               .num_event_specs = ARRAY_SIZE(ltr501_pxs_event_spec),
        },
        IIO_CHAN_SOFT_TIMESTAMP(3),
 };
 
-static const int ltr501_ps_gain[4][2] = {
-       {1, 0}, {0, 250000}, {0, 125000}, {0, 62500}
+static const struct iio_chan_spec ltr301_channels[] = {
+       LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
+                                ltr501_als_event_spec,
+                                ARRAY_SIZE(ltr501_als_event_spec)),
+       LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
+                                BIT(IIO_CHAN_INFO_SCALE) |
+                                BIT(IIO_CHAN_INFO_INT_TIME) |
+                                BIT(IIO_CHAN_INFO_SAMP_FREQ),
+                                NULL, 0),
+       IIO_CHAN_SOFT_TIMESTAMP(2),
 };
 
 static int ltr501_read_raw(struct iio_dev *indio_dev,
-                               struct iio_chan_spec const *chan,
-                               int *val, int *val2, long mask)
+                          struct iio_chan_spec const *chan,
+                          int *val, int *val2, long mask)
 {
        struct ltr501_data *data = iio_priv(indio_dev);
        __le16 buf[2];
@@ -149,7 +608,7 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
                        if (ret < 0)
                                return ret;
                        *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
-                               buf[0] : buf[1]);
+                                          buf[0] : buf[1]);
                        return IIO_VAL_INT;
                case IIO_PROXIMITY:
                        mutex_lock(&data->lock_ps);
@@ -165,45 +624,59 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_SCALE:
                switch (chan->type) {
                case IIO_INTENSITY:
-                       if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) {
-                               *val = 0;
-                               *val2 = 5000;
-                               return IIO_VAL_INT_PLUS_MICRO;
-                       } else {
-                               *val = 1;
-                               *val2 = 0;
-                               return IIO_VAL_INT;
-                       }
+                       i = (data->als_contr & data->chip_info->als_gain_mask)
+                            >> data->chip_info->als_gain_shift;
+                       *val = data->chip_info->als_gain[i].scale;
+                       *val2 = data->chip_info->als_gain[i].uscale;
+                       return IIO_VAL_INT_PLUS_MICRO;
                case IIO_PROXIMITY:
                        i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >>
                                LTR501_CONTR_PS_GAIN_SHIFT;
-                       *val = ltr501_ps_gain[i][0];
-                       *val2 = ltr501_ps_gain[i][1];
+                       *val = data->chip_info->ps_gain[i].scale;
+                       *val2 = data->chip_info->ps_gain[i].uscale;
                        return IIO_VAL_INT_PLUS_MICRO;
                default:
                        return -EINVAL;
                }
+       case IIO_CHAN_INFO_INT_TIME:
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       return ltr501_read_it_time(data, val, val2);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       return ltr501_als_read_samp_freq(data, val, val2);
+               case IIO_PROXIMITY:
+                       return ltr501_ps_read_samp_freq(data, val, val2);
+               default:
+                       return -EINVAL;
+               }
        }
        return -EINVAL;
 }
 
-static int ltr501_get_ps_gain_index(int val, int val2)
+static int ltr501_get_gain_index(struct ltr501_gain *gain, int size,
+                                int val, int val2)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++)
-               if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1])
+       for (i = 0; i < size; i++)
+               if (val == gain[i].scale && val2 == gain[i].uscale)
                        return i;
 
        return -1;
 }
 
 static int ltr501_write_raw(struct iio_dev *indio_dev,
-                              struct iio_chan_spec const *chan,
-                              int val, int val2, long mask)
+                           struct iio_chan_spec const *chan,
+                           int val, int val2, long mask)
 {
        struct ltr501_data *data = iio_priv(indio_dev);
-       int i;
+       int i, ret, freq_val, freq_val2;
+       struct ltr501_chip_info *info = data->chip_info;
 
        if (iio_buffer_enabled(indio_dev))
                return -EBUSY;
@@ -212,35 +685,382 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_SCALE:
                switch (chan->type) {
                case IIO_INTENSITY:
-                       if (val == 0 && val2 == 5000)
-                               data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK;
-                       else if (val == 1 && val2 == 0)
-                               data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK;
-                       else
+                       i = ltr501_get_gain_index(info->als_gain,
+                                                 info->als_gain_tbl_size,
+                                                 val, val2);
+                       if (i < 0)
                                return -EINVAL;
-                       return i2c_smbus_write_byte_data(data->client,
-                               LTR501_ALS_CONTR, data->als_contr);
+
+                       data->als_contr &= ~info->als_gain_mask;
+                       data->als_contr |= i << info->als_gain_shift;
+
+                       return regmap_write(data->regmap, LTR501_ALS_CONTR,
+                                           data->als_contr);
                case IIO_PROXIMITY:
-                       i = ltr501_get_ps_gain_index(val, val2);
+                       i = ltr501_get_gain_index(info->ps_gain,
+                                                 info->ps_gain_tbl_size,
+                                                 val, val2);
                        if (i < 0)
                                return -EINVAL;
                        data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
                        data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
-                       return i2c_smbus_write_byte_data(data->client,
-                               LTR501_PS_CONTR, data->ps_contr);
+
+                       return regmap_write(data->regmap, LTR501_PS_CONTR,
+                                           data->ps_contr);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_INT_TIME:
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       if (val != 0)
+                               return -EINVAL;
+                       mutex_lock(&data->lock_als);
+                       i = ltr501_set_it_time(data, val2);
+                       mutex_unlock(&data->lock_als);
+                       return i;
                default:
                        return -EINVAL;
                }
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       ret = ltr501_als_read_samp_freq(data, &freq_val,
+                                                       &freq_val2);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = ltr501_als_write_samp_freq(data, val, val2);
+                       if (ret < 0)
+                               return ret;
+
+                       /* update persistence count when changing frequency */
+                       ret = ltr501_write_intr_prst(data, chan->type,
+                                                    0, data->als_period);
+
+                       if (ret < 0)
+                               return ltr501_als_write_samp_freq(data,
+                                                                 freq_val,
+                                                                 freq_val2);
+                       return ret;
+               case IIO_PROXIMITY:
+                       ret = ltr501_ps_read_samp_freq(data, &freq_val,
+                                                      &freq_val2);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = ltr501_ps_write_samp_freq(data, val, val2);
+                       if (ret < 0)
+                               return ret;
+
+                       /* update persistence count when changing frequency */
+                       ret = ltr501_write_intr_prst(data, chan->type,
+                                                    0, data->ps_period);
+
+                       if (ret < 0)
+                               return ltr501_ps_write_samp_freq(data,
+                                                                freq_val,
+                                                                freq_val2);
+                       return ret;
+               default:
+                       return -EINVAL;
+               }
+       }
+       return -EINVAL;
+}
+
+static int ltr501_read_thresh(struct iio_dev *indio_dev,
+                             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 ltr501_data *data = iio_priv(indio_dev);
+       int ret, thresh_data;
+
+       switch (chan->type) {
+       case IIO_INTENSITY:
+               switch (dir) {
+               case IIO_EV_DIR_RISING:
+                       ret = regmap_bulk_read(data->regmap,
+                                              LTR501_ALS_THRESH_UP,
+                                              &thresh_data, 2);
+                       if (ret < 0)
+                               return ret;
+                       *val = thresh_data & LTR501_ALS_THRESH_MASK;
+                       return IIO_VAL_INT;
+               case IIO_EV_DIR_FALLING:
+                       ret = regmap_bulk_read(data->regmap,
+                                              LTR501_ALS_THRESH_LOW,
+                                              &thresh_data, 2);
+                       if (ret < 0)
+                               return ret;
+                       *val = thresh_data & LTR501_ALS_THRESH_MASK;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_PROXIMITY:
+               switch (dir) {
+               case IIO_EV_DIR_RISING:
+                       ret = regmap_bulk_read(data->regmap,
+                                              LTR501_PS_THRESH_UP,
+                                              &thresh_data, 2);
+                       if (ret < 0)
+                               return ret;
+                       *val = thresh_data & LTR501_PS_THRESH_MASK;
+                       return IIO_VAL_INT;
+               case IIO_EV_DIR_FALLING:
+                       ret = regmap_bulk_read(data->regmap,
+                                              LTR501_PS_THRESH_LOW,
+                                              &thresh_data, 2);
+                       if (ret < 0)
+                               return ret;
+                       *val = thresh_data & LTR501_PS_THRESH_MASK;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_write_thresh(struct iio_dev *indio_dev,
+                              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 ltr501_data *data = iio_priv(indio_dev);
+       int ret;
+
+       if (val < 0)
+               return -EINVAL;
+
+       switch (chan->type) {
+       case IIO_INTENSITY:
+               if (val > LTR501_ALS_THRESH_MASK)
+                       return -EINVAL;
+               switch (dir) {
+               case IIO_EV_DIR_RISING:
+                       mutex_lock(&data->lock_als);
+                       ret = regmap_bulk_write(data->regmap,
+                                               LTR501_ALS_THRESH_UP,
+                                               &val, 2);
+                       mutex_unlock(&data->lock_als);
+                       return ret;
+               case IIO_EV_DIR_FALLING:
+                       mutex_lock(&data->lock_als);
+                       ret = regmap_bulk_write(data->regmap,
+                                               LTR501_ALS_THRESH_LOW,
+                                               &val, 2);
+                       mutex_unlock(&data->lock_als);
+                       return ret;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_PROXIMITY:
+               switch (dir) {
+               if (val > LTR501_PS_THRESH_MASK)
+                       return -EINVAL;
+               case IIO_EV_DIR_RISING:
+                       mutex_lock(&data->lock_ps);
+                       ret = regmap_bulk_write(data->regmap,
+                                               LTR501_PS_THRESH_UP,
+                                               &val, 2);
+                       mutex_unlock(&data->lock_ps);
+                       return ret;
+               case IIO_EV_DIR_FALLING:
+                       mutex_lock(&data->lock_ps);
+                       ret = regmap_bulk_write(data->regmap,
+                                               LTR501_PS_THRESH_LOW,
+                                               &val, 2);
+                       mutex_unlock(&data->lock_ps);
+                       return ret;
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_read_event(struct iio_dev *indio_dev,
+                            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)
+{
+       int ret;
+
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               return ltr501_read_thresh(indio_dev, chan, type, dir,
+                                         info, val, val2);
+       case IIO_EV_INFO_PERIOD:
+               ret = ltr501_read_intr_prst(iio_priv(indio_dev),
+                                           chan->type, val2);
+               *val = *val2 / 1000000;
+               *val2 = *val2 % 1000000;
+               return ret;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_write_event(struct iio_dev *indio_dev,
+                             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)
+{
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               if (val2 != 0)
+                       return -EINVAL;
+               return ltr501_write_thresh(indio_dev, chan, type, dir,
+                                          info, val, val2);
+       case IIO_EV_INFO_PERIOD:
+               return ltr501_write_intr_prst(iio_priv(indio_dev), chan->type,
+                                             val, val2);
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_read_event_config(struct iio_dev *indio_dev,
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir)
+{
+       struct ltr501_data *data = iio_priv(indio_dev);
+       int ret, status;
+
+       switch (chan->type) {
+       case IIO_INTENSITY:
+               ret = regmap_field_read(data->reg_als_intr, &status);
+               if (ret < 0)
+                       return ret;
+               return status;
+       case IIO_PROXIMITY:
+               ret = regmap_field_read(data->reg_ps_intr, &status);
+               if (ret < 0)
+                       return ret;
+               return status;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ltr501_write_event_config(struct iio_dev *indio_dev,
+                                    const struct iio_chan_spec *chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir, int state)
+{
+       struct ltr501_data *data = iio_priv(indio_dev);
+       int ret;
+
+       /* only 1 and 0 are valid inputs */
+       if (state != 1  && state != 0)
+               return -EINVAL;
+
+       switch (chan->type) {
+       case IIO_INTENSITY:
+               mutex_lock(&data->lock_als);
+               ret = regmap_field_write(data->reg_als_intr, state);
+               mutex_unlock(&data->lock_als);
+               return ret;
+       case IIO_PROXIMITY:
+               mutex_lock(&data->lock_ps);
+               ret = regmap_field_write(data->reg_ps_intr, state);
+               mutex_unlock(&data->lock_ps);
+               return ret;
+       default:
+               return -EINVAL;
        }
+
        return -EINVAL;
 }
 
-static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625");
-static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005");
+static ssize_t ltr501_show_proximity_scale_avail(struct device *dev,
+                                                struct device_attribute *attr,
+                                                char *buf)
+{
+       struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev));
+       struct ltr501_chip_info *info = data->chip_info;
+       ssize_t len = 0;
+       int i;
+
+       for (i = 0; i < info->ps_gain_tbl_size; i++) {
+               if (info->ps_gain[i].scale == LTR501_RESERVED_GAIN)
+                       continue;
+               len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+                                info->ps_gain[i].scale,
+                                info->ps_gain[i].uscale);
+       }
+
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static ssize_t ltr501_show_intensity_scale_avail(struct device *dev,
+                                                struct device_attribute *attr,
+                                                char *buf)
+{
+       struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev));
+       struct ltr501_chip_info *info = data->chip_info;
+       ssize_t len = 0;
+       int i;
+
+       for (i = 0; i < info->als_gain_tbl_size; i++) {
+               if (info->als_gain[i].scale == LTR501_RESERVED_GAIN)
+                       continue;
+               len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+                                info->als_gain[i].scale,
+                                info->als_gain[i].uscale);
+       }
+
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.05 0.1 0.2 0.4");
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("20 10 5 2 1 0.5");
+
+static IIO_DEVICE_ATTR(in_proximity_scale_available, S_IRUGO,
+                      ltr501_show_proximity_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_intensity_scale_available, S_IRUGO,
+                      ltr501_show_intensity_scale_avail, NULL, 0);
 
 static struct attribute *ltr501_attributes[] = {
-       &iio_const_attr_in_proximity_scale_available.dev_attr.attr,
-       &iio_const_attr_in_intensity_scale_available.dev_attr.attr,
+       &iio_dev_attr_in_proximity_scale_available.dev_attr.attr,
+       &iio_dev_attr_in_intensity_scale_available.dev_attr.attr,
+       &iio_const_attr_integration_time_available.dev_attr.attr,
+       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *ltr301_attributes[] = {
+       &iio_dev_attr_in_intensity_scale_available.dev_attr.attr,
+       &iio_const_attr_integration_time_available.dev_attr.attr,
+       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
        NULL
 };
 
@@ -248,20 +1068,98 @@ static const struct attribute_group ltr501_attribute_group = {
        .attrs = ltr501_attributes,
 };
 
+static const struct attribute_group ltr301_attribute_group = {
+       .attrs = ltr301_attributes,
+};
+
+static const struct iio_info ltr501_info_no_irq = {
+       .read_raw = ltr501_read_raw,
+       .write_raw = ltr501_write_raw,
+       .attrs = &ltr501_attribute_group,
+       .driver_module = THIS_MODULE,
+};
+
 static const struct iio_info ltr501_info = {
        .read_raw = ltr501_read_raw,
        .write_raw = ltr501_write_raw,
        .attrs = &ltr501_attribute_group,
+       .read_event_value       = &ltr501_read_event,
+       .write_event_value      = &ltr501_write_event,
+       .read_event_config      = &ltr501_read_event_config,
+       .write_event_config     = &ltr501_write_event_config,
+       .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ltr301_info_no_irq = {
+       .read_raw = ltr501_read_raw,
+       .write_raw = ltr501_write_raw,
+       .attrs = &ltr301_attribute_group,
        .driver_module = THIS_MODULE,
 };
 
-static int ltr501_write_contr(struct i2c_client *client, u8 als_val, u8 ps_val)
+static const struct iio_info ltr301_info = {
+       .read_raw = ltr501_read_raw,
+       .write_raw = ltr501_write_raw,
+       .attrs = &ltr301_attribute_group,
+       .read_event_value       = &ltr501_read_event,
+       .write_event_value      = &ltr501_write_event,
+       .read_event_config      = &ltr501_read_event_config,
+       .write_event_config     = &ltr501_write_event_config,
+       .driver_module = THIS_MODULE,
+};
+
+static struct ltr501_chip_info ltr501_chip_info_tbl[] = {
+       [ltr501] = {
+               .partid = 0x08,
+               .als_gain = ltr501_als_gain_tbl,
+               .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
+               .ps_gain = ltr501_ps_gain_tbl,
+               .ps_gain_tbl_size = ARRAY_SIZE(ltr501_ps_gain_tbl),
+               .als_mode_active = BIT(0) | BIT(1),
+               .als_gain_mask = BIT(3),
+               .als_gain_shift = 3,
+               .info = &ltr501_info,
+               .info_no_irq = &ltr501_info_no_irq,
+               .channels = ltr501_channels,
+               .no_channels = ARRAY_SIZE(ltr501_channels),
+       },
+       [ltr559] = {
+               .partid = 0x09,
+               .als_gain = ltr559_als_gain_tbl,
+               .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl),
+               .ps_gain = ltr559_ps_gain_tbl,
+               .ps_gain_tbl_size = ARRAY_SIZE(ltr559_ps_gain_tbl),
+               .als_mode_active = BIT(1),
+               .als_gain_mask = BIT(2) | BIT(3) | BIT(4),
+               .als_gain_shift = 2,
+               .info = &ltr501_info,
+               .info_no_irq = &ltr501_info_no_irq,
+               .channels = ltr501_channels,
+               .no_channels = ARRAY_SIZE(ltr501_channels),
+       },
+       [ltr301] = {
+               .partid = 0x08,
+               .als_gain = ltr501_als_gain_tbl,
+               .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
+               .als_mode_active = BIT(0) | BIT(1),
+               .als_gain_mask = BIT(3),
+               .als_gain_shift = 3,
+               .info = &ltr301_info,
+               .info_no_irq = &ltr301_info_no_irq,
+               .channels = ltr301_channels,
+               .no_channels = ARRAY_SIZE(ltr301_channels),
+       },
+};
+
+static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val)
 {
-       int ret = i2c_smbus_write_byte_data(client, LTR501_ALS_CONTR, als_val);
+       int ret;
+
+       ret = regmap_write(data->regmap, LTR501_ALS_CONTR, als_val);
        if (ret < 0)
                return ret;
 
-       return i2c_smbus_write_byte_data(client, LTR501_PS_CONTR, ps_val);
+       return regmap_write(data->regmap, LTR501_PS_CONTR, ps_val);
 }
 
 static irqreturn_t ltr501_trigger_handler(int irq, void *p)
@@ -273,13 +1171,13 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
        __le16 als_buf[2];
        u8 mask = 0;
        int j = 0;
-       int ret;
+       int ret, psdata;
 
        memset(buf, 0, sizeof(buf));
 
        /* figure out which data needs to be ready */
        if (test_bit(0, indio_dev->active_scan_mask) ||
-               test_bit(1, indio_dev->active_scan_mask))
+           test_bit(1, indio_dev->active_scan_mask))
                mask |= LTR501_STATUS_ALS_RDY;
        if (test_bit(2, indio_dev->active_scan_mask))
                mask |= LTR501_STATUS_PS_RDY;
@@ -289,8 +1187,8 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
                goto done;
 
        if (mask & LTR501_STATUS_ALS_RDY) {
-               ret = i2c_smbus_read_i2c_block_data(data->client,
-                       LTR501_ALS_DATA1, sizeof(als_buf), (u8 *) als_buf);
+               ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
+                                      (u8 *)als_buf, sizeof(als_buf));
                if (ret < 0)
                        return ret;
                if (test_bit(0, indio_dev->active_scan_mask))
@@ -300,14 +1198,14 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
        }
 
        if (mask & LTR501_STATUS_PS_RDY) {
-               ret = i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
+               ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
+                                      &psdata, 2);
                if (ret < 0)
                        goto done;
-               buf[j++] = ret & LTR501_PS_DATA_MASK;
+               buf[j++] = psdata & LTR501_PS_DATA_MASK;
        }
 
-       iio_push_to_buffers_with_timestamp(indio_dev, buf,
-               iio_get_time_ns());
+       iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
 
 done:
        iio_trigger_notify_done(indio_dev->trig);
@@ -315,67 +1213,225 @@ done:
        return IRQ_HANDLED;
 }
 
+static irqreturn_t ltr501_interrupt_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct ltr501_data *data = iio_priv(indio_dev);
+       int ret, status;
+
+       ret = regmap_read(data->regmap, LTR501_ALS_PS_STATUS, &status);
+       if (ret < 0) {
+               dev_err(&data->client->dev,
+                       "irq read int reg failed\n");
+               return IRQ_HANDLED;
+       }
+
+       if (status & LTR501_STATUS_ALS_INTR)
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_EITHER),
+                              iio_get_time_ns());
+
+       if (status & LTR501_STATUS_PS_INTR)
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_EITHER),
+                              iio_get_time_ns());
+
+       return IRQ_HANDLED;
+}
+
 static int ltr501_init(struct ltr501_data *data)
 {
-       int ret;
+       int ret, status;
 
-       ret = i2c_smbus_read_byte_data(data->client, LTR501_ALS_CONTR);
+       ret = regmap_read(data->regmap, LTR501_ALS_CONTR, &status);
        if (ret < 0)
                return ret;
-       data->als_contr = ret | LTR501_CONTR_ACTIVE;
 
-       ret = i2c_smbus_read_byte_data(data->client, LTR501_PS_CONTR);
+       data->als_contr = ret | data->chip_info->als_mode_active;
+
+       ret = regmap_read(data->regmap, LTR501_PS_CONTR, &status);
        if (ret < 0)
                return ret;
-       data->ps_contr = ret | LTR501_CONTR_ACTIVE;
 
-       return ltr501_write_contr(data->client, data->als_contr,
-               data->ps_contr);
+       data->ps_contr = status | LTR501_CONTR_ACTIVE;
+
+       ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period);
+       if (ret < 0)
+               return ret;
+
+       ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period);
+       if (ret < 0)
+               return ret;
+
+       return ltr501_write_contr(data, data->als_contr, data->ps_contr);
 }
 
+static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case LTR501_ALS_DATA1:
+       case LTR501_ALS_DATA0:
+       case LTR501_ALS_PS_STATUS:
+       case LTR501_PS_DATA:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct regmap_config ltr501_regmap_config = {
+       .name =  LTR501_REGMAP_NAME,
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = LTR501_MAX_REG,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = ltr501_is_volatile_reg,
+};
+
 static int ltr501_powerdown(struct ltr501_data *data)
 {
-       return ltr501_write_contr(data->client,
-                                 data->als_contr & ~LTR501_CONTR_ACTIVE,
+       return ltr501_write_contr(data, data->als_contr &
+                                 ~data->chip_info->als_mode_active,
                                  data->ps_contr & ~LTR501_CONTR_ACTIVE);
 }
 
+static const char *ltr501_match_acpi_device(struct device *dev, int *chip_idx)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return NULL;
+       *chip_idx = id->driver_data;
+       return dev_name(dev);
+}
+
 static int ltr501_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
+                       const struct i2c_device_id *id)
 {
        struct ltr501_data *data;
        struct iio_dev *indio_dev;
-       int ret;
+       struct regmap *regmap;
+       int ret, partid, chip_idx = 0;
+       const char *name = NULL;
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
        if (!indio_dev)
                return -ENOMEM;
 
+       regmap = devm_regmap_init_i2c(client, &ltr501_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&client->dev, "Regmap initialization failed.\n");
+               return PTR_ERR(regmap);
+       }
+
        data = iio_priv(indio_dev);
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
+       data->regmap = regmap;
        mutex_init(&data->lock_als);
        mutex_init(&data->lock_ps);
 
-       ret = i2c_smbus_read_byte_data(data->client, LTR501_PART_ID);
+       data->reg_it = devm_regmap_field_alloc(&client->dev, regmap,
+                                              reg_field_it);
+       if (IS_ERR(data->reg_it)) {
+               dev_err(&client->dev, "Integ time reg field init failed.\n");
+               return PTR_ERR(data->reg_it);
+       }
+
+       data->reg_als_intr = devm_regmap_field_alloc(&client->dev, regmap,
+                                                    reg_field_als_intr);
+       if (IS_ERR(data->reg_als_intr)) {
+               dev_err(&client->dev, "ALS intr mode reg field init failed\n");
+               return PTR_ERR(data->reg_als_intr);
+       }
+
+       data->reg_ps_intr = devm_regmap_field_alloc(&client->dev, regmap,
+                                                   reg_field_ps_intr);
+       if (IS_ERR(data->reg_ps_intr)) {
+               dev_err(&client->dev, "PS intr mode reg field init failed.\n");
+               return PTR_ERR(data->reg_ps_intr);
+       }
+
+       data->reg_als_rate = devm_regmap_field_alloc(&client->dev, regmap,
+                                                    reg_field_als_rate);
+       if (IS_ERR(data->reg_als_rate)) {
+               dev_err(&client->dev, "ALS samp rate field init failed.\n");
+               return PTR_ERR(data->reg_als_rate);
+       }
+
+       data->reg_ps_rate = devm_regmap_field_alloc(&client->dev, regmap,
+                                                   reg_field_ps_rate);
+       if (IS_ERR(data->reg_ps_rate)) {
+               dev_err(&client->dev, "PS samp rate field init failed.\n");
+               return PTR_ERR(data->reg_ps_rate);
+       }
+
+       data->reg_als_prst = devm_regmap_field_alloc(&client->dev, regmap,
+                                                    reg_field_als_prst);
+       if (IS_ERR(data->reg_als_prst)) {
+               dev_err(&client->dev, "ALS prst reg field init failed\n");
+               return PTR_ERR(data->reg_als_prst);
+       }
+
+       data->reg_ps_prst = devm_regmap_field_alloc(&client->dev, regmap,
+                                                   reg_field_ps_prst);
+       if (IS_ERR(data->reg_ps_prst)) {
+               dev_err(&client->dev, "PS prst reg field init failed.\n");
+               return PTR_ERR(data->reg_ps_prst);
+       }
+
+       ret = regmap_read(data->regmap, LTR501_PART_ID, &partid);
        if (ret < 0)
                return ret;
-       if ((ret >> 4) != 0x8)
+
+       if (id) {
+               name = id->name;
+               chip_idx = id->driver_data;
+       } else  if (ACPI_HANDLE(&client->dev)) {
+               name = ltr501_match_acpi_device(&client->dev, &chip_idx);
+       } else {
+               return -ENODEV;
+       }
+
+       data->chip_info = &ltr501_chip_info_tbl[chip_idx];
+
+       if ((partid >> 4) != data->chip_info->partid)
                return -ENODEV;
 
        indio_dev->dev.parent = &client->dev;
-       indio_dev->info = &ltr501_info;
-       indio_dev->channels = ltr501_channels;
-       indio_dev->num_channels = ARRAY_SIZE(ltr501_channels);
-       indio_dev->name = LTR501_DRV_NAME;
+       indio_dev->info = data->chip_info->info;
+       indio_dev->channels = data->chip_info->channels;
+       indio_dev->num_channels = data->chip_info->no_channels;
+       indio_dev->name = name;
        indio_dev->modes = INDIO_DIRECT_MODE;
 
        ret = ltr501_init(data);
        if (ret < 0)
                return ret;
 
+       if (client->irq > 0) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL, ltr501_interrupt_handler,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               "ltr501_thresh_event",
+                                               indio_dev);
+               if (ret) {
+                       dev_err(&client->dev, "request irq (%d) failed\n",
+                               client->irq);
+                       return ret;
+               }
+       } else {
+               indio_dev->info = data->chip_info->info_no_irq;
+       }
+
        ret = iio_triggered_buffer_setup(indio_dev, NULL,
-               ltr501_trigger_handler, NULL);
+                                        ltr501_trigger_handler, NULL);
        if (ret)
                goto powerdown_on_error;
 
@@ -407,24 +1463,34 @@ static int ltr501_remove(struct i2c_client *client)
 static int ltr501_suspend(struct device *dev)
 {
        struct ltr501_data *data = iio_priv(i2c_get_clientdata(
-               to_i2c_client(dev)));
+                                           to_i2c_client(dev)));
        return ltr501_powerdown(data);
 }
 
 static int ltr501_resume(struct device *dev)
 {
        struct ltr501_data *data = iio_priv(i2c_get_clientdata(
-               to_i2c_client(dev)));
+                                           to_i2c_client(dev)));
 
-       return ltr501_write_contr(data->client, data->als_contr,
+       return ltr501_write_contr(data, data->als_contr,
                data->ps_contr);
 }
 #endif
 
 static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
 
+static const struct acpi_device_id ltr_acpi_match[] = {
+       {"LTER0501", ltr501},
+       {"LTER0559", ltr559},
+       {"LTER0301", ltr301},
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, ltr_acpi_match);
+
 static const struct i2c_device_id ltr501_id[] = {
-       { "ltr501", 0 },
+       { "ltr501", ltr501},
+       { "ltr559", ltr559},
+       { "ltr301", ltr301},
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ltr501_id);
@@ -433,6 +1499,7 @@ static struct i2c_driver ltr501_driver = {
        .driver = {
                .name   = LTR501_DRV_NAME,
                .pm     = &ltr501_pm_ops,
+               .acpi_match_table = ACPI_PTR(ltr_acpi_match),
                .owner  = THIS_MODULE,
        },
        .probe  = ltr501_probe,
index 94daa9f..12731d6 100644 (file)
@@ -240,7 +240,7 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
  * convert between normalized values and HW values obtained using given
  * timing and gain settings.
  */
-static int adc_shiftbits(u8 timing)
+static int tsl2563_adc_shiftbits(u8 timing)
 {
        int shift = 0;
 
@@ -263,9 +263,9 @@ static int adc_shiftbits(u8 timing)
 }
 
 /* Convert a HW ADC value to normalized scale. */
-static u32 normalize_adc(u16 adc, u8 timing)
+static u32 tsl2563_normalize_adc(u16 adc, u8 timing)
 {
-       return adc << adc_shiftbits(timing);
+       return adc << tsl2563_adc_shiftbits(timing);
 }
 
 static void tsl2563_wait_adc(struct tsl2563_chip *chip)
@@ -350,8 +350,8 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
                retry = tsl2563_adjust_gainlevel(chip, adc0);
        }
 
-       chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
-       chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+       chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
+       chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
 
        if (!chip->int_enabled)
                schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
@@ -361,13 +361,13 @@ out:
        return ret;
 }
 
-static inline int calib_to_sysfs(u32 calib)
+static inline int tsl2563_calib_to_sysfs(u32 calib)
 {
        return (int) (((calib * CALIB_BASE_SYSFS) +
                       CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
 }
 
-static inline u32 calib_from_sysfs(int value)
+static inline u32 tsl2563_calib_from_sysfs(int value)
 {
        return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
 }
@@ -426,7 +426,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
 };
 
 /* Convert normalized, scaled ADC values to lux. */
-static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+static unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
 {
        const struct tsl2563_lux_coeff *lp = lux_table;
        unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
@@ -442,7 +442,7 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
 }
 
 /* Apply calibration coefficient to ADC count. */
-static u32 calib_adc(u32 adc, u32 calib)
+static u32 tsl2563_calib_adc(u32 adc, u32 calib)
 {
        unsigned long scaled = adc;
 
@@ -463,9 +463,9 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,
        if (mask != IIO_CHAN_INFO_CALIBSCALE)
                return -EINVAL;
        if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
-               chip->calib0 = calib_from_sysfs(val);
+               chip->calib0 = tsl2563_calib_from_sysfs(val);
        else if (chan->channel2 == IIO_MOD_LIGHT_IR)
-               chip->calib1 = calib_from_sysfs(val);
+               chip->calib1 = tsl2563_calib_from_sysfs(val);
        else
                return -EINVAL;
 
@@ -491,11 +491,11 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
                        ret = tsl2563_get_adc(chip);
                        if (ret)
                                goto error_ret;
-                       calib0 = calib_adc(chip->data0, chip->calib0) *
+                       calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
                                chip->cover_comp_gain;
-                       calib1 = calib_adc(chip->data1, chip->calib1) *
+                       calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
                                chip->cover_comp_gain;
-                       *val = adc_to_lux(calib0, calib1);
+                       *val = tsl2563_adc_to_lux(calib0, calib1);
                        ret = IIO_VAL_INT;
                        break;
                case IIO_INTENSITY:
@@ -515,9 +515,9 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
 
        case IIO_CHAN_INFO_CALIBSCALE:
                if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
-                       *val = calib_to_sysfs(chip->calib0);
+                       *val = tsl2563_calib_to_sysfs(chip->calib0);
                else
-                       *val = calib_to_sysfs(chip->calib1);
+                       *val = tsl2563_calib_to_sysfs(chip->calib1);
                ret = IIO_VAL_INT;
                break;
        default:
@@ -750,8 +750,8 @@ static int tsl2563_probe(struct i2c_client *client,
        chip->high_thres = 0xffff;
        chip->gainlevel = tsl2563_gainlevel_table;
        chip->intr = TSL2563_INT_PERSIST(4);
-       chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
-       chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+       chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
+       chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
 
        if (pdata)
                chip->cover_comp_gain = pdata->cover_comp_gain;
index 0763b86..63c26e2 100644 (file)
 
 #define TSL4531_DRV_NAME "tsl4531"
 
-#define TCS3472_COMMAND BIT(7)
+#define TSL4531_COMMAND BIT(7)
 
-#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
-#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
-#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
-#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
+#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
+#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
+#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
+#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
 
 /* operating modes in control register */
 #define TSL4531_MODE_POWERDOWN 0x00
index 7e81d00..287691c 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/iio/common/st_sensors.h>
 
+#define LSM303DLH_MAGN_DEV_NAME                "lsm303dlh_magn"
 #define LSM303DLHC_MAGN_DEV_NAME       "lsm303dlhc_magn"
 #define LSM303DLM_MAGN_DEV_NAME                "lsm303dlm_magn"
 #define LIS3MDL_MAGN_DEV_NAME          "lis3mdl"
index 8ade473..73574d9 100644 (file)
 #define ST_MAGN_FS_AVL_12000MG                 12000
 #define ST_MAGN_FS_AVL_16000MG                 16000
 
+/* CUSTOM VALUES FOR SENSOR 0 */
+#define ST_MAGN_0_ODR_ADDR                     0x00
+#define ST_MAGN_0_ODR_MASK                     0x1c
+#define ST_MAGN_0_ODR_AVL_1HZ_VAL              0x00
+#define ST_MAGN_0_ODR_AVL_2HZ_VAL              0x01
+#define ST_MAGN_0_ODR_AVL_3HZ_VAL              0x02
+#define ST_MAGN_0_ODR_AVL_8HZ_VAL              0x03
+#define ST_MAGN_0_ODR_AVL_15HZ_VAL             0x04
+#define ST_MAGN_0_ODR_AVL_30HZ_VAL             0x05
+#define ST_MAGN_0_ODR_AVL_75HZ_VAL             0x06
+#define ST_MAGN_0_ODR_AVL_220HZ_VAL            0x07
+#define ST_MAGN_0_PW_ADDR                      0x02
+#define ST_MAGN_0_PW_MASK                      0x03
+#define ST_MAGN_0_PW_ON                                0x00
+#define ST_MAGN_0_PW_OFF                       0x03
+#define ST_MAGN_0_FS_ADDR                      0x01
+#define ST_MAGN_0_FS_MASK                      0xe0
+#define ST_MAGN_0_FS_AVL_1300_VAL              0x01
+#define ST_MAGN_0_FS_AVL_1900_VAL              0x02
+#define ST_MAGN_0_FS_AVL_2500_VAL              0x03
+#define ST_MAGN_0_FS_AVL_4000_VAL              0x04
+#define ST_MAGN_0_FS_AVL_4700_VAL              0x05
+#define ST_MAGN_0_FS_AVL_5600_VAL              0x06
+#define ST_MAGN_0_FS_AVL_8100_VAL              0x07
+#define ST_MAGN_0_FS_AVL_1300_GAIN_XY          1100
+#define ST_MAGN_0_FS_AVL_1900_GAIN_XY          855
+#define ST_MAGN_0_FS_AVL_2500_GAIN_XY          670
+#define ST_MAGN_0_FS_AVL_4000_GAIN_XY          450
+#define ST_MAGN_0_FS_AVL_4700_GAIN_XY          400
+#define ST_MAGN_0_FS_AVL_5600_GAIN_XY          330
+#define ST_MAGN_0_FS_AVL_8100_GAIN_XY          230
+#define ST_MAGN_0_FS_AVL_1300_GAIN_Z           980
+#define ST_MAGN_0_FS_AVL_1900_GAIN_Z           760
+#define ST_MAGN_0_FS_AVL_2500_GAIN_Z           600
+#define ST_MAGN_0_FS_AVL_4000_GAIN_Z           400
+#define ST_MAGN_0_FS_AVL_4700_GAIN_Z           355
+#define ST_MAGN_0_FS_AVL_5600_GAIN_Z           295
+#define ST_MAGN_0_FS_AVL_8100_GAIN_Z           205
+#define ST_MAGN_0_MULTIREAD_BIT                        false
+
 /* CUSTOM VALUES FOR SENSOR 1 */
 #define ST_MAGN_1_WAI_EXP                      0x3c
 #define ST_MAGN_1_ODR_ADDR                     0x00
@@ -150,6 +190,82 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
 };
 
 static const struct st_sensor_settings st_magn_sensors_settings[] = {
+       {
+               .wai = 0, /* This sensor has no valid WhoAmI report 0 */
+               .sensors_supported = {
+                       [0] = LSM303DLH_MAGN_DEV_NAME,
+               },
+               .ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+               .odr = {
+                       .addr = ST_MAGN_0_ODR_ADDR,
+                       .mask = ST_MAGN_0_ODR_MASK,
+                       .odr_avl = {
+                               { 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
+                               { 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
+                               { 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
+                               { 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
+                               { 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
+                               { 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
+                               { 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
+                       },
+               },
+               .pw = {
+                       .addr = ST_MAGN_0_PW_ADDR,
+                       .mask = ST_MAGN_0_PW_MASK,
+                       .value_on = ST_MAGN_0_PW_ON,
+                       .value_off = ST_MAGN_0_PW_OFF,
+               },
+               .fs = {
+                       .addr = ST_MAGN_0_FS_ADDR,
+                       .mask = ST_MAGN_0_FS_MASK,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_MAGN_FS_AVL_1300MG,
+                                       .value = ST_MAGN_0_FS_AVL_1300_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
+                               },
+                               [1] = {
+                                       .num = ST_MAGN_FS_AVL_1900MG,
+                                       .value = ST_MAGN_0_FS_AVL_1900_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
+                               },
+                               [2] = {
+                                       .num = ST_MAGN_FS_AVL_2500MG,
+                                       .value = ST_MAGN_0_FS_AVL_2500_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
+                               },
+                               [3] = {
+                                       .num = ST_MAGN_FS_AVL_4000MG,
+                                       .value = ST_MAGN_0_FS_AVL_4000_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
+                               },
+                               [4] = {
+                                       .num = ST_MAGN_FS_AVL_4700MG,
+                                       .value = ST_MAGN_0_FS_AVL_4700_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
+                               },
+                               [5] = {
+                                       .num = ST_MAGN_FS_AVL_5600MG,
+                                       .value = ST_MAGN_0_FS_AVL_5600_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
+                               },
+                               [6] = {
+                                       .num = ST_MAGN_FS_AVL_8100MG,
+                                       .value = ST_MAGN_0_FS_AVL_8100_VAL,
+                                       .gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
+                                       .gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
+                               },
+                       },
+               },
+               .multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
+               .bootime = 2,
+       },
        {
                .wai = ST_MAGN_1_WAI_EXP,
                .sensors_supported = {
index 92e5c15..5311d8a 100644 (file)
 
 #ifdef CONFIG_OF
 static const struct of_device_id st_magn_of_match[] = {
+       {
+               .compatible = "st,lsm303dlh-magn",
+               .data = LSM303DLH_MAGN_DEV_NAME,
+       },
        {
                .compatible = "st,lsm303dlhc-magn",
                .data = LSM303DLHC_MAGN_DEV_NAME,
@@ -71,6 +75,7 @@ static int st_magn_i2c_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id st_magn_id_table[] = {
+       { LSM303DLH_MAGN_DEV_NAME },
        { LSM303DLHC_MAGN_DEV_NAME },
        { LSM303DLM_MAGN_DEV_NAME },
        { LIS3MDL_MAGN_DEV_NAME },
index fa40f6d..2042e37 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/acpi.h>
 #include <linux/gpio/consumer.h>
 #include <linux/regmap.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
@@ -29,7 +31,9 @@
 
 #define SX9500_DRIVER_NAME             "sx9500"
 #define SX9500_IRQ_NAME                        "sx9500_event"
-#define SX9500_GPIO_NAME               "sx9500_gpio"
+
+#define SX9500_GPIO_INT                        "interrupt"
+#define SX9500_GPIO_RESET              "reset"
 
 /* Register definitions. */
 #define SX9500_REG_IRQ_SRC             0x00
@@ -73,6 +77,7 @@
 #define SX9500_CONVDONE_IRQ            BIT(3)
 
 #define SX9500_PROXSTAT_SHIFT          4
+#define SX9500_COMPSTAT_MASK           GENMASK(3, 0)
 
 #define SX9500_NUM_CHANNELS            4
 
@@ -81,6 +86,7 @@ struct sx9500_data {
        struct i2c_client *client;
        struct iio_trigger *trig;
        struct regmap *regmap;
+       struct gpio_desc *gpiod_rst;
        /*
         * Last reading of the proximity status for each channel.  We
         * only send an event to user space when this changes.
@@ -89,6 +95,11 @@ struct sx9500_data {
        bool event_enabled[SX9500_NUM_CHANNELS];
        bool trigger_enabled;
        u16 *buffer;
+       /* Remember enabled channels and sample rate during suspend. */
+       unsigned int suspend_ctrl0;
+       struct completion completion;
+       int data_rdy_users, close_far_users;
+       int channel_users[SX9500_NUM_CHANNELS];
 };
 
 static const struct iio_event_spec sx9500_events[] = {
@@ -139,6 +150,10 @@ static const struct {
        {2, 500000},
 };
 
+static const unsigned int sx9500_scan_period_table[] = {
+       30, 60, 90, 120, 150, 200, 300, 400,
+};
+
 static const struct regmap_range sx9500_writable_reg_ranges[] = {
        regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
        regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
@@ -191,7 +206,67 @@ static const struct regmap_config sx9500_regmap_config = {
        .volatile_table = &sx9500_volatile_regs,
 };
 
-static int sx9500_read_proximity(struct sx9500_data *data,
+static int sx9500_inc_users(struct sx9500_data *data, int *counter,
+                           unsigned int reg, unsigned int bitmask)
+{
+       (*counter)++;
+       if (*counter != 1)
+               /* Bit is already active, nothing to do. */
+               return 0;
+
+       return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
+}
+
+static int sx9500_dec_users(struct sx9500_data *data, int *counter,
+                           unsigned int reg, unsigned int bitmask)
+{
+       (*counter)--;
+       if (*counter != 0)
+               /* There are more users, do not deactivate. */
+               return 0;
+
+       return regmap_update_bits(data->regmap, reg, bitmask, 0);
+}
+
+static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
+{
+       return sx9500_inc_users(data, &data->channel_users[chan],
+                               SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
+{
+       return sx9500_dec_users(data, &data->channel_users[chan],
+                               SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
+{
+       return sx9500_inc_users(data, &data->data_rdy_users,
+                               SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
+{
+       return sx9500_dec_users(data, &data->data_rdy_users,
+                               SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_inc_close_far_users(struct sx9500_data *data)
+{
+       return sx9500_inc_users(data, &data->close_far_users,
+                               SX9500_REG_IRQ_MSK,
+                               SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_dec_close_far_users(struct sx9500_data *data)
+{
+       return sx9500_dec_users(data, &data->close_far_users,
+                               SX9500_REG_IRQ_MSK,
+                               SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_read_prox_data(struct sx9500_data *data,
                                 const struct iio_chan_spec *chan,
                                 int *val)
 {
@@ -211,6 +286,79 @@ static int sx9500_read_proximity(struct sx9500_data *data,
        return IIO_VAL_INT;
 }
 
+/*
+ * If we have no interrupt support, we have to wait for a scan period
+ * after enabling a channel to get a result.
+ */
+static int sx9500_wait_for_sample(struct sx9500_data *data)
+{
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
+       if (ret < 0)
+               return ret;
+
+       val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
+
+       msleep(sx9500_scan_period_table[val]);
+
+       return 0;
+}
+
+static int sx9500_read_proximity(struct sx9500_data *data,
+                                const struct iio_chan_spec *chan,
+                                int *val)
+{
+       int ret;
+
+       mutex_lock(&data->mutex);
+
+       ret = sx9500_inc_chan_users(data, chan->channel);
+       if (ret < 0)
+               goto out;
+
+       ret = sx9500_inc_data_rdy_users(data);
+       if (ret < 0)
+               goto out_dec_chan;
+
+       mutex_unlock(&data->mutex);
+
+       if (data->client->irq > 0)
+               ret = wait_for_completion_interruptible(&data->completion);
+       else
+               ret = sx9500_wait_for_sample(data);
+
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&data->mutex);
+
+       ret = sx9500_read_prox_data(data, chan, val);
+       if (ret < 0)
+               goto out;
+
+       ret = sx9500_dec_chan_users(data, chan->channel);
+       if (ret < 0)
+               goto out;
+
+       ret = sx9500_dec_data_rdy_users(data);
+       if (ret < 0)
+               goto out;
+
+       ret = IIO_VAL_INT;
+
+       goto out;
+
+out_dec_chan:
+       sx9500_dec_chan_users(data, chan->channel);
+out:
+       mutex_unlock(&data->mutex);
+       reinit_completion(&data->completion);
+
+       return ret;
+}
+
 static int sx9500_read_samp_freq(struct sx9500_data *data,
                                 int *val, int *val2)
 {
@@ -236,7 +384,6 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
                           int *val, int *val2, long mask)
 {
        struct sx9500_data *data = iio_priv(indio_dev);
-       int ret;
 
        switch (chan->type) {
        case IIO_PROXIMITY:
@@ -244,10 +391,7 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
                case IIO_CHAN_INFO_RAW:
                        if (iio_buffer_enabled(indio_dev))
                                return -EBUSY;
-                       mutex_lock(&data->mutex);
-                       ret = sx9500_read_proximity(data, chan, val);
-                       mutex_unlock(&data->mutex);
-                       return ret;
+                       return sx9500_read_proximity(data, chan, val);
                case IIO_CHAN_INFO_SAMP_FREQ:
                        return sx9500_read_samp_freq(data, val, val2);
                default:
@@ -318,28 +462,16 @@ static irqreturn_t sx9500_irq_handler(int irq, void *private)
        return IRQ_WAKE_THREAD;
 }
 
-static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+static void sx9500_push_events(struct iio_dev *indio_dev)
 {
-       struct iio_dev *indio_dev = private;
-       struct sx9500_data *data = iio_priv(indio_dev);
        int ret;
        unsigned int val, chan;
-
-       mutex_lock(&data->mutex);
-
-       ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "i2c transfer error in irq\n");
-               goto out;
-       }
-
-       if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
-               goto out;
+       struct sx9500_data *data = iio_priv(indio_dev);
 
        ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
        if (ret < 0) {
                dev_err(&data->client->dev, "i2c transfer error in irq\n");
-               goto out;
+               return;
        }
 
        val >>= SX9500_PROXSTAT_SHIFT;
@@ -354,15 +486,34 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
                        /* No change on this channel. */
                        continue;
 
-               dir = new_prox ? IIO_EV_DIR_FALLING :
-                       IIO_EV_DIR_RISING;
-               ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
-                                         chan,
-                                         IIO_EV_TYPE_THRESH,
-                                         dir);
+               dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
+               ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
+                                         IIO_EV_TYPE_THRESH, dir);
                iio_push_event(indio_dev, ev, iio_get_time_ns());
                data->prox_stat[chan] = new_prox;
        }
+}
+
+static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int ret;
+       unsigned int val;
+
+       mutex_lock(&data->mutex);
+
+       ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "i2c transfer error in irq\n");
+               goto out;
+       }
+
+       if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
+               sx9500_push_events(indio_dev);
+
+       if (val & SX9500_CONVDONE_IRQ)
+               complete_all(&data->completion);
 
 out:
        mutex_unlock(&data->mutex);
@@ -391,9 +542,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
                                     int state)
 {
        struct sx9500_data *data = iio_priv(indio_dev);
-       int ret, i;
-       bool any_active = false;
-       unsigned int irqmask;
+       int ret;
 
        if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
            dir != IIO_EV_DIR_EITHER)
@@ -401,24 +550,32 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
 
        mutex_lock(&data->mutex);
 
-       data->event_enabled[chan->channel] = state;
+       if (state == 1) {
+               ret = sx9500_inc_chan_users(data, chan->channel);
+               if (ret < 0)
+                       goto out_unlock;
+               ret = sx9500_inc_close_far_users(data);
+               if (ret < 0)
+                       goto out_undo_chan;
+       } else {
+               ret = sx9500_dec_chan_users(data, chan->channel);
+               if (ret < 0)
+                       goto out_unlock;
+               ret = sx9500_dec_close_far_users(data);
+               if (ret < 0)
+                       goto out_undo_chan;
+       }
 
-       for (i = 0; i < SX9500_NUM_CHANNELS; i++)
-               if (data->event_enabled[i]) {
-                       any_active = true;
-                       break;
-               }
+       data->event_enabled[chan->channel] = state;
+       goto out_unlock;
 
-       irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
-       if (any_active)
-               ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
-                                        irqmask, irqmask);
+out_undo_chan:
+       if (state == 1)
+               sx9500_dec_chan_users(data, chan->channel);
        else
-               ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
-                                        irqmask, 0);
-
+               sx9500_inc_chan_users(data, chan->channel);
+out_unlock:
        mutex_unlock(&data->mutex);
-
        return ret;
 }
 
@@ -469,12 +626,16 @@ static int sx9500_set_trigger_state(struct iio_trigger *trig,
 
        mutex_lock(&data->mutex);
 
-       ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
-                                SX9500_CONVDONE_IRQ,
-                                state ? SX9500_CONVDONE_IRQ : 0);
-       if (ret == 0)
-               data->trigger_enabled = state;
+       if (state)
+               ret = sx9500_inc_data_rdy_users(data);
+       else
+               ret = sx9500_dec_data_rdy_users(data);
+       if (ret < 0)
+               goto out;
+
+       data->trigger_enabled = state;
 
+out:
        mutex_unlock(&data->mutex);
 
        return ret;
@@ -496,7 +657,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private)
 
        for_each_set_bit(bit, indio_dev->active_scan_mask,
                         indio_dev->masklength) {
-               ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
+               ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
                                            &val);
                if (ret < 0)
                        goto out;
@@ -515,6 +676,62 @@ out:
        return IRQ_HANDLED;
 }
 
+static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
+{
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int ret, i;
+
+       mutex_lock(&data->mutex);
+
+       for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       ret = sx9500_inc_chan_users(data, i);
+                       if (ret)
+                               break;
+               }
+
+       if (ret)
+               for (i = i - 1; i >= 0; i--)
+                       if (test_bit(i, indio_dev->active_scan_mask))
+                               sx9500_dec_chan_users(data, i);
+
+       mutex_unlock(&data->mutex);
+
+       return ret;
+}
+
+static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
+{
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int ret, i;
+
+       iio_triggered_buffer_predisable(indio_dev);
+
+       mutex_lock(&data->mutex);
+
+       for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       ret = sx9500_dec_chan_users(data, i);
+                       if (ret)
+                               break;
+               }
+
+       if (ret)
+               for (i = i - 1; i >= 0; i--)
+                       if (test_bit(i, indio_dev->active_scan_mask))
+                               sx9500_inc_chan_users(data, i);
+
+       mutex_unlock(&data->mutex);
+
+       return ret;
+}
+
+static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
+       .preenable = sx9500_buffer_preenable,
+       .postenable = iio_triggered_buffer_postenable,
+       .predisable = sx9500_buffer_predisable,
+};
+
 struct sx9500_reg_default {
        u8 reg;
        u8 def;
@@ -570,17 +787,57 @@ static const struct sx9500_reg_default sx9500_default_regs[] = {
        },
        {
                .reg = SX9500_REG_PROX_CTRL0,
-               /* Scan period: 30ms, all sensors enabled. */
-               .def = 0x0f,
+               /* Scan period: 30ms, all sensors disabled. */
+               .def = 0x00,
        },
 };
 
+/* Activate all channels and perform an initial compensation. */
+static int sx9500_init_compensation(struct iio_dev *indio_dev)
+{
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int i, ret;
+       unsigned int val;
+
+       ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+                                GENMASK(SX9500_NUM_CHANNELS, 0),
+                                GENMASK(SX9500_NUM_CHANNELS, 0));
+       if (ret < 0)
+               return ret;
+
+       for (i = 10; i >= 0; i--) {
+               usleep_range(10000, 20000);
+               ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
+               if (ret < 0)
+                       goto out;
+               if (!(val & SX9500_COMPSTAT_MASK))
+                       break;
+       }
+
+       if (i < 0) {
+               dev_err(&data->client->dev, "initial compensation timed out");
+               ret = -ETIMEDOUT;
+       }
+
+out:
+       regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+                          GENMASK(SX9500_NUM_CHANNELS, 0), 0);
+       return ret;
+}
+
 static int sx9500_init_device(struct iio_dev *indio_dev)
 {
        struct sx9500_data *data = iio_priv(indio_dev);
        int ret, i;
        unsigned int val;
 
+       if (data->gpiod_rst) {
+               gpiod_set_value_cansleep(data->gpiod_rst, 0);
+               usleep_range(1000, 2000);
+               gpiod_set_value_cansleep(data->gpiod_rst, 1);
+               usleep_range(1000, 2000);
+       }
+
        ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
        if (ret < 0)
                return ret;
@@ -602,33 +859,34 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
                        return ret;
        }
 
-       return 0;
+       return sx9500_init_compensation(indio_dev);
 }
 
-static int sx9500_gpio_probe(struct i2c_client *client,
-                            struct sx9500_data *data)
+static void sx9500_gpio_probe(struct i2c_client *client,
+                             struct sx9500_data *data)
 {
        struct device *dev;
        struct gpio_desc *gpio;
-       int ret;
 
        if (!client)
-               return -EINVAL;
+               return;
 
        dev = &client->dev;
 
-       /* data ready gpio interrupt pin */
-       gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
-       if (IS_ERR(gpio)) {
-               dev_err(dev, "acpi gpio get index failed\n");
-               return PTR_ERR(gpio);
+       if (client->irq <= 0) {
+               gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
+               if (IS_ERR(gpio))
+                       dev_err(dev, "gpio get irq failed\n");
+               else
+                       client->irq = gpiod_to_irq(gpio);
        }
 
-       ret = gpiod_to_irq(gpio);
-
-       dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
-       return ret;
+       data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
+                                              0, GPIOD_OUT_HIGH);
+       if (IS_ERR(data->gpiod_rst)) {
+               dev_warn(dev, "gpio get reset pin failed\n");
+               data->gpiod_rst = NULL;
+       }
 }
 
 static int sx9500_probe(struct i2c_client *client,
@@ -645,14 +903,13 @@ static int sx9500_probe(struct i2c_client *client,
        data = iio_priv(indio_dev);
        data->client = client;
        mutex_init(&data->mutex);
+       init_completion(&data->completion);
        data->trigger_enabled = false;
 
        data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
        if (IS_ERR(data->regmap))
                return PTR_ERR(data->regmap);
 
-       sx9500_init_device(indio_dev);
-
        indio_dev->dev.parent = &client->dev;
        indio_dev->name = SX9500_DRIVER_NAME;
        indio_dev->channels = sx9500_channels;
@@ -661,10 +918,15 @@ static int sx9500_probe(struct i2c_client *client,
        indio_dev->modes = INDIO_DIRECT_MODE;
        i2c_set_clientdata(client, indio_dev);
 
-       if (client->irq <= 0)
-               client->irq = sx9500_gpio_probe(client, data);
+       sx9500_gpio_probe(client, data);
 
-       if (client->irq > 0) {
+       ret = sx9500_init_device(indio_dev);
+       if (ret < 0)
+               return ret;
+
+       if (client->irq <= 0)
+               dev_warn(&client->dev, "no valid irq found\n");
+       else {
                ret = devm_request_threaded_irq(&client->dev, client->irq,
                                sx9500_irq_handler, sx9500_irq_thread_handler,
                                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -687,7 +949,8 @@ static int sx9500_probe(struct i2c_client *client,
        }
 
        ret = iio_triggered_buffer_setup(indio_dev, NULL,
-                                        sx9500_trigger_handler, NULL);
+                                        sx9500_trigger_handler,
+                                        &sx9500_buffer_setup_ops);
        if (ret < 0)
                goto out_trigger_unregister;
 
@@ -720,6 +983,49 @@ static int sx9500_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int sx9500_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&data->mutex);
+       ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
+                         &data->suspend_ctrl0);
+       if (ret < 0)
+               goto out;
+
+       /*
+        * Scan period doesn't matter because when all the sensors are
+        * deactivated the device is in sleep mode.
+        */
+       ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
+
+out:
+       mutex_unlock(&data->mutex);
+       return ret;
+}
+
+static int sx9500_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct sx9500_data *data = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&data->mutex);
+       ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
+                          data->suspend_ctrl0);
+       mutex_unlock(&data->mutex);
+
+       return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops sx9500_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
+};
+
 static const struct acpi_device_id sx9500_acpi_match[] = {
        {"SSX9500", 0},
        { },
@@ -728,7 +1034,7 @@ MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
 
 static const struct i2c_device_id sx9500_id[] = {
        {"sx9500", 0},
-       {}
+       { },
 };
 MODULE_DEVICE_TABLE(i2c, sx9500_id);
 
@@ -736,6 +1042,7 @@ static struct i2c_driver sx9500_driver = {
        .driver = {
                .name   = SX9500_DRIVER_NAME,
                .acpi_match_table = ACPI_PTR(sx9500_acpi_match),
+               .pm = &sx9500_pm_ops,
        },
        .probe          = sx9500_probe,
        .remove         = sx9500_remove,
index a112fc9..b2d3b56 100644 (file)
  *
  * (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
  *
- * TODO: sleep mode, configuration EEPROM
+ * To wake up from sleep mode, the SDA line must be held low while SCL is high
+ * for at least 33ms.  This is achieved with an extra GPIO that can be connected
+ * directly to the SDA line.  In normal operation, the GPIO is set as input and
+ * will not interfere in I2C communication.  While the GPIO is driven low, the
+ * i2c adapter is locked since it cannot be used by other clients.  The SCL line
+ * always has a pull-up so we do not need an extra GPIO to drive it high.  If
+ * the "wakeup" GPIO is not given, power management will be disabled.
+ *
+ * TODO: filter configuration
  */
 
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/iio/iio.h>
 
 #define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
 #define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
 
+#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
+
 struct mlx90614_data {
        struct i2c_client *client;
+       struct mutex lock; /* for EEPROM access only */
+       struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
+       unsigned long ready_timestamp; /* in jiffies */
 };
 
+/*
+ * Erase an address and write word.
+ * The mutex must be locked before calling.
+ */
+static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
+                              u16 value)
+{
+       /*
+        * Note: The mlx90614 requires a PEC on writing but does not send us a
+        * valid PEC on reading.  Hence, we cannot set I2C_CLIENT_PEC in
+        * i2c_client.flags.  As a workaround, we use i2c_smbus_xfer here.
+        */
+       union i2c_smbus_data data;
+       s32 ret;
+
+       dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
+
+       data.word = 0x0000; /* erase command */
+       ret = i2c_smbus_xfer(client->adapter, client->addr,
+                            client->flags | I2C_CLIENT_PEC,
+                            I2C_SMBUS_WRITE, command,
+                            I2C_SMBUS_WORD_DATA, &data);
+       if (ret < 0)
+               return ret;
+
+       msleep(MLX90614_TIMING_EEPROM);
+
+       data.word = value; /* actual write */
+       ret = i2c_smbus_xfer(client->adapter, client->addr,
+                            client->flags | I2C_CLIENT_PEC,
+                            I2C_SMBUS_WRITE, command,
+                            I2C_SMBUS_WORD_DATA, &data);
+
+       msleep(MLX90614_TIMING_EEPROM);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
+ * the last wake-up.  This is normally only needed to get a valid temperature
+ * reading.  EEPROM access does not need such delay.
+ * Return 0 on success, <0 on error.
+ */
+static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+       unsigned long now;
+
+       if (!data->wakeup_gpio)
+               return 0;
+
+       pm_runtime_get_sync(&data->client->dev);
+
+       if (startup) {
+               now = jiffies;
+               if (time_before(now, data->ready_timestamp) &&
+                   msleep_interruptible(jiffies_to_msecs(
+                               data->ready_timestamp - now)) != 0) {
+                       pm_runtime_put_autosuspend(&data->client->dev);
+                       return -EINTR;
+               }
+       }
+
+       return 0;
+}
+
+static void mlx90614_power_put(struct mlx90614_data *data)
+{
+       if (!data->wakeup_gpio)
+               return;
+
+       pm_runtime_mark_last_busy(&data->client->dev);
+       pm_runtime_put_autosuspend(&data->client->dev);
+}
+#else
+static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+       return 0;
+}
+
+static inline void mlx90614_power_put(struct mlx90614_data *data)
+{
+}
+#endif
+
 static int mlx90614_read_raw(struct iio_dev *indio_dev,
                            struct iio_chan_spec const *channel, int *val,
                            int *val2, long mask)
@@ -85,9 +188,19 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
                        return -EINVAL;
                }
 
+               ret = mlx90614_power_get(data, true);
+               if (ret < 0)
+                       return ret;
                ret = i2c_smbus_read_word_data(data->client, cmd);
+               mlx90614_power_put(data);
+
                if (ret < 0)
                        return ret;
+
+               /* MSB is an error flag */
+               if (ret & 0x8000)
+                       return -EIO;
+
                *val = ret;
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_OFFSET:
@@ -97,6 +210,65 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_SCALE:
                *val = 20;
                return IIO_VAL_INT;
+       case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+               mlx90614_power_get(data, false);
+               mutex_lock(&data->lock);
+               ret = i2c_smbus_read_word_data(data->client,
+                                              MLX90614_EMISSIVITY);
+               mutex_unlock(&data->lock);
+               mlx90614_power_put(data);
+
+               if (ret < 0)
+                       return ret;
+
+               if (ret == 65535) {
+                       *val = 1;
+                       *val2 = 0;
+               } else {
+                       *val = 0;
+                       *val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
+               }
+               return IIO_VAL_INT_PLUS_NANO;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int mlx90614_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *channel, int val,
+                            int val2, long mask)
+{
+       struct mlx90614_data *data = iio_priv(indio_dev);
+       s32 ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+               if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
+                       return -EINVAL;
+               val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
+
+               mlx90614_power_get(data, false);
+               mutex_lock(&data->lock);
+               ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
+                                         val);
+               mutex_unlock(&data->lock);
+               mlx90614_power_put(data);
+
+               if (ret < 0)
+                       return ret;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
+                                    struct iio_chan_spec const *channel,
+                                    long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_CALIBEMISSIVITY:
+               return IIO_VAL_INT_PLUS_NANO;
        default:
                return -EINVAL;
        }
@@ -115,7 +287,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
                .type = IIO_TEMP,
                .modified = 1,
                .channel2 = IIO_MOD_TEMP_OBJECT,
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
                    BIT(IIO_CHAN_INFO_SCALE),
        },
@@ -125,7 +298,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
                .modified = 1,
                .channel = 1,
                .channel2 = IIO_MOD_TEMP_OBJECT,
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
                    BIT(IIO_CHAN_INFO_SCALE),
        },
@@ -133,9 +307,103 @@ static const struct iio_chan_spec mlx90614_channels[] = {
 
 static const struct iio_info mlx90614_info = {
        .read_raw = mlx90614_read_raw,
+       .write_raw = mlx90614_write_raw,
+       .write_raw_get_fmt = mlx90614_write_raw_get_fmt,
        .driver_module = THIS_MODULE,
 };
 
+#ifdef CONFIG_PM
+static int mlx90614_sleep(struct mlx90614_data *data)
+{
+       s32 ret;
+
+       if (!data->wakeup_gpio) {
+               dev_dbg(&data->client->dev, "Sleep disabled");
+               return -ENOSYS;
+       }
+
+       dev_dbg(&data->client->dev, "Requesting sleep");
+
+       mutex_lock(&data->lock);
+       ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
+                            data->client->flags | I2C_CLIENT_PEC,
+                            I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
+                            I2C_SMBUS_BYTE, NULL);
+       mutex_unlock(&data->lock);
+
+       return ret;
+}
+
+static int mlx90614_wakeup(struct mlx90614_data *data)
+{
+       if (!data->wakeup_gpio) {
+               dev_dbg(&data->client->dev, "Wake-up disabled");
+               return -ENOSYS;
+       }
+
+       dev_dbg(&data->client->dev, "Requesting wake-up");
+
+       i2c_lock_adapter(data->client->adapter);
+       gpiod_direction_output(data->wakeup_gpio, 0);
+       msleep(MLX90614_TIMING_WAKEUP);
+       gpiod_direction_input(data->wakeup_gpio);
+       i2c_unlock_adapter(data->client->adapter);
+
+       data->ready_timestamp = jiffies +
+                       msecs_to_jiffies(MLX90614_TIMING_STARTUP);
+
+       /*
+        * Quirk: the i2c controller may get confused right after the
+        * wake-up signal has been sent.  As a workaround, do a dummy read.
+        * If the read fails, the controller will probably be reset so that
+        * further reads will work.
+        */
+       i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+
+       return 0;
+}
+
+/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
+static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+       struct gpio_desc *gpio;
+
+       if (!i2c_check_functionality(client->adapter,
+                                               I2C_FUNC_SMBUS_WRITE_BYTE)) {
+               dev_info(&client->dev,
+                        "i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
+               return NULL;
+       }
+
+       gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
+
+       if (IS_ERR(gpio)) {
+               dev_warn(&client->dev,
+                        "gpio acquisition failed with error %ld, sleep disabled",
+                        PTR_ERR(gpio));
+               return NULL;
+       } else if (!gpio) {
+               dev_info(&client->dev,
+                        "wakeup-gpio not found, sleep disabled");
+       }
+
+       return gpio;
+}
+#else
+static inline int mlx90614_sleep(struct mlx90614_data *data)
+{
+       return -ENOSYS;
+}
+static inline int mlx90614_wakeup(struct mlx90614_data *data)
+{
+       return -ENOSYS;
+}
+static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+       return NULL;
+}
+#endif
+
 /* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
 static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
 {
@@ -166,6 +434,10 @@ static int mlx90614_probe(struct i2c_client *client,
        data = iio_priv(indio_dev);
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
+       mutex_init(&data->lock);
+       data->wakeup_gpio = mlx90614_probe_wakeup(client);
+
+       mlx90614_wakeup(data);
 
        indio_dev->dev.parent = &client->dev;
        indio_dev->name = id->name;
@@ -188,12 +460,30 @@ static int mlx90614_probe(struct i2c_client *client,
                return ret;
        }
 
+       if (data->wakeup_gpio) {
+               pm_runtime_set_autosuspend_delay(&client->dev,
+                                                MLX90614_AUTOSLEEP_DELAY);
+               pm_runtime_use_autosuspend(&client->dev);
+               pm_runtime_set_active(&client->dev);
+               pm_runtime_enable(&client->dev);
+       }
+
        return iio_device_register(indio_dev);
 }
 
 static int mlx90614_remove(struct i2c_client *client)
 {
-       iio_device_unregister(i2c_get_clientdata(client));
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct mlx90614_data *data = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+
+       if (data->wakeup_gpio) {
+               pm_runtime_disable(&client->dev);
+               if (!pm_runtime_status_suspended(&client->dev))
+                       mlx90614_sleep(data);
+               pm_runtime_set_suspended(&client->dev);
+       }
 
        return 0;
 }
@@ -204,10 +494,67 @@ static const struct i2c_device_id mlx90614_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, mlx90614_id);
 
+#ifdef CONFIG_PM_SLEEP
+static int mlx90614_pm_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct mlx90614_data *data = iio_priv(indio_dev);
+
+       if (data->wakeup_gpio && pm_runtime_active(dev))
+               return mlx90614_sleep(data);
+
+       return 0;
+}
+
+static int mlx90614_pm_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct mlx90614_data *data = iio_priv(indio_dev);
+       int err;
+
+       if (data->wakeup_gpio) {
+               err = mlx90614_wakeup(data);
+               if (err < 0)
+                       return err;
+
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int mlx90614_pm_runtime_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct mlx90614_data *data = iio_priv(indio_dev);
+
+       return mlx90614_sleep(data);
+}
+
+static int mlx90614_pm_runtime_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct mlx90614_data *data = iio_priv(indio_dev);
+
+       return mlx90614_wakeup(data);
+}
+#endif
+
+static const struct dev_pm_ops mlx90614_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
+       SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
+                          mlx90614_pm_runtime_resume, NULL)
+};
+
 static struct i2c_driver mlx90614_driver = {
        .driver = {
                .name   = "mlx90614",
                .owner  = THIS_MODULE,
+               .pm     = &mlx90614_pm_ops,
        },
        .probe = mlx90614_probe,
        .remove = mlx90614_remove,
index 84a0789..fcc49f8 100644 (file)
@@ -41,8 +41,8 @@
 #define TMP006_CONFIG_CR_MASK 0x0e00
 #define TMP006_CONFIG_CR_SHIFT 9
 
-#define MANUFACTURER_MAGIC 0x5449
-#define DEVICE_MAGIC 0x0067
+#define TMP006_MANUFACTURER_MAGIC 0x5449
+#define TMP006_DEVICE_MAGIC 0x0067
 
 struct tmp006_data {
        struct i2c_client *client;
@@ -191,7 +191,7 @@ static bool tmp006_check_identification(struct i2c_client *client)
        if (did < 0)
                return false;
 
-       return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
+       return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
 }
 
 static int tmp006_probe(struct i2c_client *client,
index a348918..e646c5d 100644 (file)
 #define ISL29035_BOUT_SHIFT            0x07
 #define ISL29035_BOUT_MASK             (0x01 << ISL29035_BOUT_SHIFT)
 
+#define ISL29018_INT_TIME_AVAIL                "0.090000 0.005630 0.000351 0.000021"
+#define ISL29023_INT_TIME_AVAIL                "0.090000 0.005600 0.000352 0.000022"
+#define ISL29035_INT_TIME_AVAIL                "0.105000 0.006500 0.000410 0.000025"
+
+static const char * const int_time_avail[] = {
+       ISL29018_INT_TIME_AVAIL,
+       ISL29023_INT_TIME_AVAIL,
+       ISL29035_INT_TIME_AVAIL,
+};
+
+enum isl29018_int_time {
+       ISL29018_INT_TIME_16,
+       ISL29018_INT_TIME_12,
+       ISL29018_INT_TIME_8,
+       ISL29018_INT_TIME_4,
+};
+
+static const unsigned int isl29018_int_utimes[3][4] = {
+       {90000, 5630, 351, 21},
+       {90000, 5600, 352, 22},
+       {105000, 6500, 410, 25},
+};
+
+static const struct isl29018_scale {
+       unsigned int scale;
+       unsigned int uscale;
+} isl29018_scales[4][4] = {
+       { {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} },
+       { {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} },
+       { {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} },
+       { {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} }
+};
+
 struct isl29018_chip {
        struct device           *dev;
        struct regmap           *regmap;
        struct mutex            lock;
        int                     type;
-       unsigned int            lux_scale;
-       unsigned int            lux_uscale;
-       unsigned int            range;
-       unsigned int            adc_bit;
+       unsigned int            calibscale;
+       unsigned int            ucalibscale;
+       unsigned int            int_time;
+       struct isl29018_scale   scale;
        int                     prox_scheme;
        bool                    suspended;
 };
 
-static int isl29018_set_range(struct isl29018_chip *chip, unsigned long range,
-               unsigned int *new_range)
+static int isl29018_set_integration_time(struct isl29018_chip *chip,
+                                        unsigned int utime)
 {
-       static const unsigned long supp_ranges[] = {1000, 4000, 16000, 64000};
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(supp_ranges); ++i) {
-               if (range <= supp_ranges[i]) {
-                       *new_range = (unsigned int)supp_ranges[i];
+       int i, ret;
+       unsigned int int_time, new_int_time;
+       struct isl29018_scale new_scale;
+
+       for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
+               if (utime == isl29018_int_utimes[chip->type][i]) {
+                       new_int_time = i;
+                       new_scale = isl29018_scales[new_int_time][0];
                        break;
                }
        }
 
-       if (i >= ARRAY_SIZE(supp_ranges))
+       if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
                return -EINVAL;
 
-       return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
-                       COMMANDII_RANGE_MASK, i << COMMANDII_RANGE_SHIFT);
+       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
+                                COMMANDII_RESOLUTION_MASK,
+                                i << COMMANDII_RESOLUTION_SHIFT);
+       if (ret < 0)
+               return ret;
+
+       /* keep the same range when integration time changes */
+       int_time = chip->int_time;
+       for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
+               if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
+                   chip->scale.uscale == isl29018_scales[int_time][i].uscale) {
+                       chip->scale = isl29018_scales[new_int_time][i];
+                       break;
+               }
+       }
+       chip->int_time = new_int_time;
+
+       return 0;
 }
 
-static int isl29018_set_resolution(struct isl29018_chip *chip,
-                       unsigned long adcbit, unsigned int *conf_adc_bit)
+static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
 {
-       static const unsigned long supp_adcbit[] = {16, 12, 8, 4};
-       int i;
+       int i, ret;
+       struct isl29018_scale new_scale;
 
-       for (i = 0; i < ARRAY_SIZE(supp_adcbit); ++i) {
-               if (adcbit >= supp_adcbit[i]) {
-                       *conf_adc_bit = (unsigned int)supp_adcbit[i];
+       for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
+               if (scale == isl29018_scales[chip->int_time][i].scale &&
+                   uscale == isl29018_scales[chip->int_time][i].uscale) {
+                       new_scale = isl29018_scales[chip->int_time][i];
                        break;
                }
        }
 
-       if (i >= ARRAY_SIZE(supp_adcbit))
+       if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
                return -EINVAL;
 
-       return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
-                       COMMANDII_RESOLUTION_MASK,
-                       i << COMMANDII_RESOLUTION_SHIFT);
+       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
+                                COMMANDII_RANGE_MASK,
+                                i << COMMANDII_RANGE_SHIFT);
+       if (ret < 0)
+               return ret;
+
+       chip->scale = new_scale;
+
+       return 0;
 }
 
 static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
@@ -156,22 +213,17 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
 static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
 {
        int lux_data;
-       unsigned int data_x_range, lux_unshifted;
+       unsigned int data_x_range;
 
        lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
 
        if (lux_data < 0)
                return lux_data;
 
-       /* To support fractional scaling, separate the unshifted lux
-        * into two calculations: int scaling and micro-scaling.
-        * lux_uscale ranges from 0-999999, so about 20 bits.  Split
-        * the /1,000,000 in two to reduce the risk of over/underflow.
-        */
-       data_x_range = lux_data * chip->range;
-       lux_unshifted = data_x_range * chip->lux_scale;
-       lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000;
-       *lux = lux_unshifted >> chip->adc_bit;
+       data_x_range = lux_data * chip->scale.scale +
+                      lux_data * chip->scale.uscale / 1000000;
+       *lux = data_x_range * chip->calibscale +
+              data_x_range * chip->ucalibscale / 1000000;
 
        return 0;
 }
@@ -229,86 +281,37 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
        return 0;
 }
 
-/* Sysfs interface */
-/* range */
-static ssize_t show_range(struct device *dev,
+static ssize_t show_scale_available(struct device *dev,
                        struct device_attribute *attr, char *buf)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct isl29018_chip *chip = iio_priv(indio_dev);
+       int i, len = 0;
 
-       return sprintf(buf, "%u\n", chip->range);
-}
-
-static ssize_t store_range(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-       struct isl29018_chip *chip = iio_priv(indio_dev);
-       int status;
-       unsigned long lval;
-       unsigned int new_range;
-
-       if (kstrtoul(buf, 10, &lval))
-               return -EINVAL;
-
-       if (!(lval == 1000UL || lval == 4000UL ||
-                       lval == 16000UL || lval == 64000UL)) {
-               dev_err(dev, "The range is not supported\n");
-               return -EINVAL;
-       }
+       for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
+               len += sprintf(buf + len, "%d.%06d ",
+                              isl29018_scales[chip->int_time][i].scale,
+                              isl29018_scales[chip->int_time][i].uscale);
 
-       mutex_lock(&chip->lock);
-       status = isl29018_set_range(chip, lval, &new_range);
-       if (status < 0) {
-               mutex_unlock(&chip->lock);
-               dev_err(dev,
-                       "Error in setting max range with err %d\n", status);
-               return status;
-       }
-       chip->range = new_range;
-       mutex_unlock(&chip->lock);
+       buf[len - 1] = '\n';
 
-       return count;
+       return len;
 }
 
-/* resolution */
-static ssize_t show_resolution(struct device *dev,
+static ssize_t show_int_time_available(struct device *dev,
                        struct device_attribute *attr, char *buf)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct isl29018_chip *chip = iio_priv(indio_dev);
+       int i, len = 0;
 
-       return sprintf(buf, "%u\n", chip->adc_bit);
-}
+       for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
+               len += sprintf(buf + len, "0.%06d ",
+                              isl29018_int_utimes[chip->type][i]);
 
-static ssize_t store_resolution(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-       struct isl29018_chip *chip = iio_priv(indio_dev);
-       int status;
-       unsigned int val;
-       unsigned int new_adc_bit;
-
-       if (kstrtouint(buf, 10, &val))
-               return -EINVAL;
-       if (!(val == 4 || val == 8 || val == 12 || val == 16)) {
-               dev_err(dev, "The resolution is not supported\n");
-               return -EINVAL;
-       }
-
-       mutex_lock(&chip->lock);
-       status = isl29018_set_resolution(chip, val, &new_adc_bit);
-       if (status < 0) {
-               mutex_unlock(&chip->lock);
-               dev_err(dev, "Error in setting resolution\n");
-               return status;
-       }
-       chip->adc_bit = new_adc_bit;
-       mutex_unlock(&chip->lock);
+       buf[len - 1] = '\n';
 
-       return count;
+       return len;
 }
 
 /* proximity scheme */
@@ -357,11 +360,29 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
        int ret = -EINVAL;
 
        mutex_lock(&chip->lock);
-       if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) {
-               chip->lux_scale = val;
-               /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */
-               chip->lux_uscale = val2;
-               ret = 0;
+       switch (mask) {
+       case IIO_CHAN_INFO_CALIBSCALE:
+               if (chan->type == IIO_LIGHT) {
+                       chip->calibscale = val;
+                       chip->ucalibscale = val2;
+                       ret = 0;
+               }
+               break;
+       case IIO_CHAN_INFO_INT_TIME:
+               if (chan->type == IIO_LIGHT) {
+                       if (val != 0) {
+                               mutex_unlock(&chip->lock);
+                               return -EINVAL;
+                       }
+                       ret = isl29018_set_integration_time(chip, val2);
+               }
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               if (chan->type == IIO_LIGHT)
+                       ret = isl29018_set_scale(chip, val, val2);
+               break;
+       default:
+               break;
        }
        mutex_unlock(&chip->lock);
 
@@ -402,10 +423,24 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
                if (!ret)
                        ret = IIO_VAL_INT;
                break;
+       case IIO_CHAN_INFO_INT_TIME:
+               if (chan->type == IIO_LIGHT) {
+                       *val = 0;
+                       *val2 = isl29018_int_utimes[chip->type][chip->int_time];
+                       ret = IIO_VAL_INT_PLUS_MICRO;
+               }
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               if (chan->type == IIO_LIGHT) {
+                       *val = chip->scale.scale;
+                       *val2 = chip->scale.uscale;
+                       ret = IIO_VAL_INT_PLUS_MICRO;
+               }
+               break;
        case IIO_CHAN_INFO_CALIBSCALE:
                if (chan->type == IIO_LIGHT) {
-                       *val = chip->lux_scale;
-                       *val2 = chip->lux_uscale;
+                       *val = chip->calibscale;
+                       *val2 = chip->ucalibscale;
                        ret = IIO_VAL_INT_PLUS_MICRO;
                }
                break;
@@ -421,7 +456,9 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
        .indexed = 1,                                                   \
        .channel = 0,                                                   \
        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |            \
-       BIT(IIO_CHAN_INFO_CALIBSCALE),                                  \
+       BIT(IIO_CHAN_INFO_CALIBSCALE) |                                 \
+       BIT(IIO_CHAN_INFO_SCALE) |                                      \
+       BIT(IIO_CHAN_INFO_INT_TIME),                                    \
 }
 
 #define ISL29018_IR_CHANNEL {                                          \
@@ -447,32 +484,27 @@ static const struct iio_chan_spec isl29023_channels[] = {
        ISL29018_IR_CHANNEL,
 };
 
-static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0);
-static IIO_CONST_ATTR(range_available, "1000 4000 16000 64000");
-static IIO_CONST_ATTR(adc_resolution_available, "4 8 12 16");
-static IIO_DEVICE_ATTR(adc_resolution, S_IRUGO | S_IWUSR,
-                                       show_resolution, store_resolution, 0);
+static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
+                      show_int_time_available, NULL, 0);
+static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
+                     show_scale_available, NULL, 0);
 static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
                                        S_IRUGO | S_IWUSR,
                                        show_prox_infrared_suppression,
                                        store_prox_infrared_suppression, 0);
 
 #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
-#define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
+
 static struct attribute *isl29018_attributes[] = {
-       ISL29018_DEV_ATTR(range),
-       ISL29018_CONST_ATTR(range_available),
-       ISL29018_DEV_ATTR(adc_resolution),
-       ISL29018_CONST_ATTR(adc_resolution_available),
+       ISL29018_DEV_ATTR(in_illuminance_scale_available),
+       ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
        ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
        NULL
 };
 
 static struct attribute *isl29023_attributes[] = {
-       ISL29018_DEV_ATTR(range),
-       ISL29018_CONST_ATTR(range_available),
-       ISL29018_DEV_ATTR(adc_resolution),
-       ISL29018_CONST_ATTR(adc_resolution_available),
+       ISL29018_DEV_ATTR(in_illuminance_scale_available),
+       ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
        NULL
 };
 
@@ -516,8 +548,6 @@ enum {
 static int isl29018_chip_init(struct isl29018_chip *chip)
 {
        int status;
-       unsigned int new_adc_bit;
-       unsigned int new_range;
 
        if (chip->type == isl29035) {
                status = isl29035_detect(chip);
@@ -566,14 +596,19 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
        usleep_range(1000, 2000);       /* per data sheet, page 10 */
 
        /* set defaults */
-       status = isl29018_set_range(chip, chip->range, &new_range);
+       status = isl29018_set_scale(chip, chip->scale.scale,
+                                   chip->scale.uscale);
        if (status < 0) {
                dev_err(chip->dev, "Init of isl29018 fails\n");
                return status;
        }
 
-       status = isl29018_set_resolution(chip, chip->adc_bit,
-                                               &new_adc_bit);
+       status = isl29018_set_integration_time(chip,
+                       isl29018_int_utimes[chip->type][chip->int_time]);
+       if (status < 0) {
+               dev_err(chip->dev, "Init of isl29018 fails\n");
+               return status;
+       }
 
        return 0;
 }
@@ -701,10 +736,10 @@ static int isl29018_probe(struct i2c_client *client,
        mutex_init(&chip->lock);
 
        chip->type = dev_id;
-       chip->lux_scale = 1;
-       chip->lux_uscale = 0;
-       chip->range = 1000;
-       chip->adc_bit = 16;
+       chip->calibscale = 1;
+       chip->ucalibscale = 0;
+       chip->int_time = ISL29018_INT_TIME_16;
+       chip->scale = isl29018_scales[chip->int_time][0];
        chip->suspended = false;
 
        chip->regmap = devm_regmap_init_i2c(client,
index d86b753..b1e46ae 100644 (file)
@@ -43,6 +43,7 @@ enum iio_chan_info_enum {
        IIO_CHAN_INFO_CALIBWEIGHT,
        IIO_CHAN_INFO_DEBOUNCE_COUNT,
        IIO_CHAN_INFO_DEBOUNCE_TIME,
+       IIO_CHAN_INFO_CALIBEMISSIVITY,
 };
 
 enum iio_shared_by {
index 9a617ad..79463b0 100644 (file)
@@ -8,6 +8,7 @@ help:
        @echo '  cpupower   - a tool for all things x86 CPU power'
        @echo '  firewire   - the userspace part of nosy, an IEEE-1394 traffic sniffer'
        @echo '  hv         - tools used when in Hyper-V clients'
+       @echo '  iio        - IIO tools'
        @echo '  lguest     - a minimal 32-bit x86 hypervisor'
        @echo '  perf       - Linux performance measurement and analysis tool'
        @echo '  selftests  - various kernel selftests'
@@ -41,7 +42,7 @@ acpi: FORCE
 cpupower: FORCE
        $(call descend,power/$@)
 
-cgroup firewire hv guest usb virtio vm net: FORCE
+cgroup firewire hv guest usb virtio vm net iio: FORCE
        $(call descend,$@)
 
 liblockdep: FORCE
@@ -91,7 +92,7 @@ acpi_clean:
 cpupower_clean:
        $(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
+cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
        $(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -114,6 +115,6 @@ tmon_clean:
 
 clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
                perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
-               vm_clean net_clean x86_energy_perf_policy_clean tmon_clean
+               vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean
 
 .PHONY: FORCE