Merge tag 'iio-for-4.11b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Jan 2017 08:23:23 +0000 (09:23 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Jan 2017 08:23:23 +0000 (09:23 +0100)
Jonathan writes:

Second round of IIO new device support, cleanups and features for the 4.11 cycle

New device support:
* lsm6dsx imu
  - new driver and bindings.
* max11100 adc
  - new driver and bindings.
* tlc4541
  - new driver
* tmp007 thermopile
  - new driver.

Core
* in kernel interfaces
  - pass through raw values if no scaling provided and a processed value is
    requested.
* trigger
  - close a race condition in acquiring trigger reference.
  - constify device_type structures.
  - rework the viio_trigger_alloc function to be much neater and easier to
  read.
  - free trigger resources correctly on some error paths. Avoids putting a
  module we don't have.

Documentation
* ABI
  - specify a unit for proximity measurements.

Cleanups and features
* ads1015
  - constify iio_info structure.
* ads7950 cleanups following merge in previous pull
  - Add device tree bindings
  - Drop the ti prefix from the module name in common with other drivers.
  - Change regulator name to vref to match datasheet and other drivers.
* ak8974
  - remove a redundant zero timeout check.
* bmi160
  - use variable names for sizeof instead of types.
* cm3605
  - mark PM functions as __maybe_unused to avoid a build warning.
* isl29028 (on it's way towards moving out of staging).
  - alignment fixes and newline improvements.
  - combine proxim_get and read_proxim for simpler code.
  - drop unused ISL29028_DEV_ATTR macro
  - move some error logging into functions to cut out repitition.
  - make error messages more consistent.
  - tidy up some brackets.
  - drop the enable flag that nothing uses.
  - only set proximity rate and ALS scale when relevant channel type is enabled.
  - runtime pm support.
* lsm6dsx
  - fix wrong values for gyro sensitivitiy.
* mag3110
  - claim direct mode during sysfs reads to avoid a race condition.
* max1363
  - export OF device table IDs as module aliases.
* max30100
  - use msleep for long uncritical delays.
* mcp4531
  - export OF device table as module aliases.
* ms5611
  - claim direct mode during sysfs reads to avoid a race condition.
* opt3001
  - export OF device table as module aliases.
* sx9500
  - claim direct mode during oversampling changes to avoid a race condition.

39 files changed:
Documentation/ABI/testing/sysfs-bus-iio
Documentation/devicetree/bindings/iio/adc/max11100.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/temperature/tmp007.txt [new file with mode: 0644]
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/max11100.c [new file with mode: 0644]
drivers/iio/adc/max1363.c
drivers/iio/adc/ti-ads1015.c
drivers/iio/adc/ti-ads7950.c
drivers/iio/adc/ti-tlc4541.c [new file with mode: 0644]
drivers/iio/health/max30100.c
drivers/iio/imu/Kconfig
drivers/iio/imu/Makefile
drivers/iio/imu/bmi160/bmi160_core.c
drivers/iio/imu/st_lsm6dsx/Kconfig [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/Makefile [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c [new file with mode: 0644]
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c [new file with mode: 0644]
drivers/iio/industrialio-trigger.c
drivers/iio/inkern.c
drivers/iio/light/cm3605.c
drivers/iio/light/opt3001.c
drivers/iio/magnetometer/ak8974.c
drivers/iio/magnetometer/mag3110.c
drivers/iio/potentiometer/mcp4531.c
drivers/iio/pressure/ms5611_core.c
drivers/iio/proximity/sx9500.c
drivers/iio/temperature/Kconfig
drivers/iio/temperature/Makefile
drivers/iio/temperature/tmp007.c [new file with mode: 0644]
drivers/iio/trigger/iio-trig-interrupt.c
drivers/iio/trigger/iio-trig-sysfs.c
drivers/staging/iio/light/isl29028.c
drivers/staging/iio/trigger/iio-trig-bfin-timer.c

index 8ec362bd5948bf57a4732b3738246dfc24378e88..530809ccfacf336710a40cf501401ccae0bb6ae4 100644 (file)
@@ -1255,7 +1255,8 @@ Description:
                reflectivity of infrared or ultrasound emitted.
                Often these sensors are unit less and as such conversion
                to SI units is not possible. Higher proximity measurements
-               indicate closer objects, and vice versa.
+               indicate closer objects, and vice versa. Units after
+               application of scale and offset are meters.
 
 What:          /sys/.../iio:deviceX/in_illuminance_input
 What:          /sys/.../iio:deviceX/in_illuminance_raw
diff --git a/Documentation/devicetree/bindings/iio/adc/max11100.txt b/Documentation/devicetree/bindings/iio/adc/max11100.txt
new file mode 100644 (file)
index 0000000..b7f7177
--- /dev/null
@@ -0,0 +1,18 @@
+* Maxim max11100 Analog to Digital Converter (ADC)
+
+Required properties:
+  - compatible: Should be "maxim,max11100"
+  - reg: the adc unit address
+  - vref-supply: phandle to the regulator that provides reference voltage
+
+Optional properties:
+  - spi-max-frequency: SPI maximum frequency
+
+Example:
+
+max11100: adc@0 {
+        compatible = "maxim,max11100";
+        reg = <0>;
+        vref-supply = <&adc0_vref>;
+        spi-max-frequency = <240000>;
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt
new file mode 100644 (file)
index 0000000..e77a6f7
--- /dev/null
@@ -0,0 +1,23 @@
+* Texas Instruments ADS7950 family of A/DC chips
+
+Required properties:
+ - compatible: Must be one of "ti,ads7950", "ti,ads7951", "ti,ads7952",
+   "ti,ads7953", "ti,ads7954", "ti,ads7955", "ti,ads7956", "ti,ads7957",
+   "ti,ads7958", "ti,ads7959", "ti,ads7960", or "ti,ads7961"
+ - reg: SPI chip select number for the device
+ - #io-channel-cells: Must be 1 as per ../iio-bindings.txt
+ - vref-supply: phandle to a regulator node that supplies the 2.5V or 5V
+   reference voltage
+
+Recommended properties:
+ - spi-max-frequency: Definition as per
+               Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+adc@0 {
+       compatible = "ti,ads7957";
+       reg = <0>;
+       #io-channel-cells = <1>;
+       vref-supply = <&refin_supply>;
+       spi-max-frequency = <10000000>;
+};
diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
new file mode 100644 (file)
index 0000000..ed3cdac
--- /dev/null
@@ -0,0 +1,24 @@
+* ST_LSM6DSx driver for STM 6-axis (acc + gyro) imu Mems sensors
+
+Required properties:
+- compatible: must be one of:
+  "st,lsm6ds3"
+  "st,lsm6dsm"
+- reg: i2c address of the sensor / spi cs line
+
+Optional properties:
+- interrupt-parent: should be the phandle for the interrupt controller
+- interrupts: interrupt mapping for IRQ. It should be configured with
+  flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt
+  client node bindings.
+
+Example:
+
+lsm6dsm@6b {
+       compatible = "st,lsm6dsm";
+       reg = <0x6b>;
+       interrupt-parent = <&gpio0>;
+       interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+};
diff --git a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
new file mode 100644 (file)
index 0000000..3b8f41f
--- /dev/null
@@ -0,0 +1,27 @@
+* TI TMP007 - IR thermopile sensor with integrated math engine
+
+Link to datasheet: http://www.ti.com/lit/ds/symlink/tmp007.pdf
+
+Required properties:
+
+  - compatible: should be "ti,tmp007"
+  - reg: the I2C address of the sensor (changeable via ADR pins)
+               ------------------------------
+               |ADR1 | ADR0 | Device Address|
+               ------------------------------
+                  0      0        0x40
+                  0      1        0x41
+                  0     SDA       0x42
+                  0     SCL       0x43
+                  1      0        0x44
+                  1      1        0x45
+                  1     SDA       0x46
+                  1     SCL       0x47
+
+Example:
+
+tmp007@40 {
+        compatible = "ti,tmp007";
+        reg = <0x40>;
+};
+
index 8e7764c3cc84063c5290c5945aaa2d2519408c62..1a73e03defac134cee6ac89fdcb8969fe6f6cbce 100644 (file)
@@ -326,6 +326,15 @@ config MAX1027
          To compile this driver as a module, choose M here: the module will be
          called max1027.
 
+config MAX11100
+       tristate "Maxim max11100 ADC driver"
+       depends on SPI_MASTER
+       help
+         Say yes here to build support for Maxim max11100 SPI ADC
+
+         To compile this driver as a module, choose M here: the module will be
+         called max11100.
+
 config MAX1363
        tristate "Maxim max1363 ADC driver"
        depends on I2C
@@ -603,6 +612,18 @@ config TI_AM335X_ADC
          To compile this driver as a module, choose M here: the module will be
          called ti_am335x_adc.
 
+config TI_TLC4541
+       tristate "Texas Instruments TLC4541 ADC driver"
+       depends on SPI
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         Say yes here to build support for Texas Instruments TLC4541 / TLC3541
+         ADC chips.
+
+         This driver can also be built as a module. If so, the module will be
+         called ti-tlc4541.
+
 config TWL4030_MADC
        tristate "TWL4030 MADC (Monitoring A/D Converter)"
        depends on TWL4030_CORE
index 260d083a2b3fb7c7853ca55700c79e5bd07b80b8..9475fd572e5bc3b8521eecf19ac5697fe935d9af 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
 obj-$(CONFIG_LTC2485) += ltc2485.o
 obj-$(CONFIG_MAX1027) += max1027.o
+obj-$(CONFIG_MAX11100) += max11100.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_MCP320X) += mcp320x.o
 obj-$(CONFIG_MCP3422) += mcp3422.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
 obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
 obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
 obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
 obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
 obj-$(CONFIG_VF610_ADC) += vf610_adc.o
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
new file mode 100644 (file)
index 0000000..a088cf9
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * iio/adc/max11100.c
+ * Maxim max11100 ADC Driver with IIO interface
+ *
+ * Copyright (C) 2016-17 Renesas Electronics Corporation
+ * Copyright (C) 2016-17 Jacopo Mondi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+/*
+ * LSB is the ADC single digital step
+ * 1 LSB = (vref_mv / 2 ^ 16)
+ *
+ * LSB is used to calculate analog voltage value
+ * from the number of ADC steps count
+ *
+ * Ain = (count * LSB)
+ */
+#define MAX11100_LSB_DIV               (1 << 16)
+
+struct max11100_state {
+       struct regulator *vref_reg;
+       struct spi_device *spi;
+
+       /*
+        * DMA (thus cache coherency maintenance) requires the
+        * transfer buffers to live in their own cache lines.
+        */
+       u8 buffer[3] ____cacheline_aligned;
+};
+
+static struct iio_chan_spec max11100_channels[] = {
+       { /* [0] */
+               .type = IIO_VOLTAGE,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                                     BIT(IIO_CHAN_INFO_SCALE),
+       },
+};
+
+static int max11100_read_single(struct iio_dev *indio_dev, int *val)
+{
+       int ret;
+       struct max11100_state *state = iio_priv(indio_dev);
+
+       ret = spi_read(state->spi, state->buffer, sizeof(state->buffer));
+       if (ret) {
+               dev_err(&indio_dev->dev, "SPI transfer failed\n");
+               return ret;
+       }
+
+       /* the first 8 bits sent out from ADC must be 0s */
+       if (state->buffer[0]) {
+               dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n");
+               return -EINVAL;
+       }
+
+       *val = (state->buffer[1] << 8) | state->buffer[2];
+
+       return 0;
+}
+
+static int max11100_read_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int *val, int *val2, long info)
+{
+       int ret, vref_uv;
+       struct max11100_state *state = iio_priv(indio_dev);
+
+       switch (info) {
+       case IIO_CHAN_INFO_RAW:
+               ret = max11100_read_single(indio_dev, val);
+               if (ret)
+                       return ret;
+
+               return IIO_VAL_INT;
+
+       case IIO_CHAN_INFO_SCALE:
+               vref_uv = regulator_get_voltage(state->vref_reg);
+               if (vref_uv < 0)
+                       /* dummy regulator "get_voltage" returns -EINVAL */
+                       return -EINVAL;
+
+               *val =  vref_uv / 1000;
+               *val2 = MAX11100_LSB_DIV;
+               return IIO_VAL_FRACTIONAL;
+       }
+
+       return -EINVAL;
+}
+
+static const struct iio_info max11100_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = max11100_read_raw,
+};
+
+static int max11100_probe(struct spi_device *spi)
+{
+       int ret;
+       struct iio_dev *indio_dev;
+       struct max11100_state *state;
+
+       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, indio_dev);
+
+       state = iio_priv(indio_dev);
+       state->spi = spi;
+
+       indio_dev->dev.parent = &spi->dev;
+       indio_dev->dev.of_node = spi->dev.of_node;
+       indio_dev->name = "max11100";
+       indio_dev->info = &max11100_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = max11100_channels,
+       indio_dev->num_channels = ARRAY_SIZE(max11100_channels),
+
+       state->vref_reg = devm_regulator_get(&spi->dev, "vref");
+       if (IS_ERR(state->vref_reg))
+               return PTR_ERR(state->vref_reg);
+
+       ret = regulator_enable(state->vref_reg);
+       if (ret)
+               return ret;
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto disable_regulator;
+
+       return 0;
+
+disable_regulator:
+       regulator_disable(state->vref_reg);
+
+       return ret;
+}
+
+static int max11100_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+       struct max11100_state *state = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       regulator_disable(state->vref_reg);
+
+       return 0;
+}
+
+static const struct of_device_id max11100_ids[] = {
+       {.compatible = "maxim,max11100"},
+       { },
+};
+MODULE_DEVICE_TABLE(of, max11100_ids);
+
+static struct spi_driver max11100_driver = {
+       .driver = {
+               .name   = "max11100",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(max11100_ids),
+       },
+       .probe          = max11100_probe,
+       .remove         = max11100_remove,
+};
+
+module_spi_driver(max11100_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
+MODULE_DESCRIPTION("Maxim max11100 ADC Driver");
+MODULE_LICENSE("GPL v2");
index 841a13c9b6ea03cdae08f4e3f46d85fd89253124..c6c12feb4a088cfc2d1c3d0ed015bdba72b413f9 100644 (file)
@@ -1567,6 +1567,7 @@ static const struct of_device_id max1363_of_match[] = {
        MAX1363_COMPATIBLE("maxim,max11647", max11647),
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, max1363_of_match);
 #endif
 
 static int max1363_probe(struct i2c_client *client,
index cde6f130a99ab83ef6f2d5eb7d19116f389a870c..422b314f5a3f02df4360a98d0d711d620ebabc32 100644 (file)
@@ -472,14 +472,14 @@ static const struct attribute_group ads1115_attribute_group = {
        .attrs = ads1115_attributes,
 };
 
-static struct iio_info ads1015_info = {
+static const struct iio_info ads1015_info = {
        .driver_module  = THIS_MODULE,
        .read_raw       = ads1015_read_raw,
        .write_raw      = ads1015_write_raw,
        .attrs          = &ads1015_attribute_group,
 };
 
-static struct iio_info ads1115_info = {
+static const struct iio_info ads1115_info = {
        .driver_module  = THIS_MODULE,
        .read_raw       = ads1015_read_raw,
        .write_raw      = ads1015_write_raw,
index 0330361b8ed51b00fac85098ee57e3bb45205e7e..16a06633332cc594fa9fe02aaa92568be477a7b3 100644 (file)
@@ -411,15 +411,15 @@ static int ti_ads7950_probe(struct spi_device *spi)
        spi_message_init_with_transfers(&st->scan_single_msg,
                                        st->scan_single_xfer, 3);
 
-       st->reg = devm_regulator_get(&spi->dev, "refin");
+       st->reg = devm_regulator_get(&spi->dev, "vref");
        if (IS_ERR(st->reg)) {
-               dev_err(&spi->dev, "Failed get get regulator \"refin\"\n");
+               dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
                return PTR_ERR(st->reg);
        }
 
        ret = regulator_enable(st->reg);
        if (ret) {
-               dev_err(&spi->dev, "Failed to enable regulator \"refin\"\n");
+               dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
                return ret;
        }
 
@@ -459,25 +459,25 @@ static int ti_ads7950_remove(struct spi_device *spi)
 }
 
 static const struct spi_device_id ti_ads7950_id[] = {
-       {"ti-ads7950", TI_ADS7950},
-       {"ti-ads7951", TI_ADS7951},
-       {"ti-ads7952", TI_ADS7952},
-       {"ti-ads7953", TI_ADS7953},
-       {"ti-ads7954", TI_ADS7954},
-       {"ti-ads7955", TI_ADS7955},
-       {"ti-ads7956", TI_ADS7956},
-       {"ti-ads7957", TI_ADS7957},
-       {"ti-ads7958", TI_ADS7958},
-       {"ti-ads7959", TI_ADS7959},
-       {"ti-ads7960", TI_ADS7960},
-       {"ti-ads7961", TI_ADS7961},
+       { "ads7950", TI_ADS7950 },
+       { "ads7951", TI_ADS7951 },
+       { "ads7952", TI_ADS7952 },
+       { "ads7953", TI_ADS7953 },
+       { "ads7954", TI_ADS7954 },
+       { "ads7955", TI_ADS7955 },
+       { "ads7956", TI_ADS7956 },
+       { "ads7957", TI_ADS7957 },
+       { "ads7958", TI_ADS7958 },
+       { "ads7959", TI_ADS7959 },
+       { "ads7960", TI_ADS7960 },
+       { "ads7961", TI_ADS7961 },
        { }
 };
 MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
 
 static struct spi_driver ti_ads7950_driver = {
        .driver = {
-               .name   = "ti-ads7950",
+               .name   = "ads7950",
        },
        .probe          = ti_ads7950_probe,
        .remove         = ti_ads7950_remove,
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
new file mode 100644 (file)
index 0000000..78d91a0
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * TI tlc4541 ADC Driver
+ *
+ * Copyright (C) 2017 Phil Reid
+ *
+ * Datasheets can be found here:
+ * http://www.ti.com/lit/gpn/tlc3541
+ * http://www.ti.com/lit/gpn/tlc4541
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The tlc4541 requires 24 clock cycles to start a transfer.
+ * Conversion then takes 2.94us to complete before data is ready
+ * Data is returned MSB first.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+
+struct tlc4541_state {
+       struct spi_device               *spi;
+       struct regulator                *reg;
+       struct spi_transfer             scan_single_xfer[3];
+       struct spi_message              scan_single_msg;
+
+       /*
+        * DMA (thus cache coherency maintenance) requires the
+        * transfer buffers to live in their own cache lines.
+        * 2 bytes data + 6 bytes padding + 8 bytes timestamp when
+        * call iio_push_to_buffers_with_timestamp.
+        */
+       __be16                          rx_buf[8] ____cacheline_aligned;
+};
+
+struct tlc4541_chip_info {
+       const struct iio_chan_spec *channels;
+       unsigned int num_channels;
+};
+
+enum tlc4541_id {
+       TLC3541,
+       TLC4541,
+};
+
+#define TLC4541_V_CHAN(bits, bitshift) {                              \
+               .type = IIO_VOLTAGE,                                  \
+               .info_mask_separate       = BIT(IIO_CHAN_INFO_RAW),   \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+               .scan_type = {                                        \
+                       .sign = 'u',                                  \
+                       .realbits = (bits),                           \
+                       .storagebits = 16,                            \
+                       .shift = (bitshift),                          \
+                       .endianness = IIO_BE,                         \
+               },                                                    \
+       }
+
+#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
+const struct iio_chan_spec name ## _channels[] = { \
+       TLC4541_V_CHAN(bits, bitshift), \
+       IIO_CHAN_SOFT_TIMESTAMP(1), \
+}
+
+static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
+static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);
+
+static const struct tlc4541_chip_info tlc4541_chip_info[] = {
+       [TLC3541] = {
+               .channels = tlc3541_channels,
+               .num_channels = ARRAY_SIZE(tlc3541_channels),
+       },
+       [TLC4541] = {
+               .channels = tlc4541_channels,
+               .num_channels = ARRAY_SIZE(tlc4541_channels),
+       },
+};
+
+static irqreturn_t tlc4541_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct tlc4541_state *st = iio_priv(indio_dev);
+       int ret;
+
+       ret = spi_sync(st->spi, &st->scan_single_msg);
+       if (ret < 0)
+               goto done;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+                                          iio_get_time_ns(indio_dev));
+
+done:
+       iio_trigger_notify_done(indio_dev->trig);
+       return IRQ_HANDLED;
+}
+
+static int tlc4541_get_range(struct tlc4541_state *st)
+{
+       int vref;
+
+       vref = regulator_get_voltage(st->reg);
+       if (vref < 0)
+               return vref;
+
+       vref /= 1000;
+
+       return vref;
+}
+
+static int tlc4541_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val,
+                           int *val2,
+                           long m)
+{
+       int ret = 0;
+       struct tlc4541_state *st = iio_priv(indio_dev);
+
+       switch (m) {
+       case IIO_CHAN_INFO_RAW:
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
+               ret = spi_sync(st->spi, &st->scan_single_msg);
+               iio_device_release_direct_mode(indio_dev);
+               if (ret < 0)
+                       return ret;
+               *val = be16_to_cpu(st->rx_buf[0]);
+               *val = *val >> chan->scan_type.shift;
+               *val &= GENMASK(chan->scan_type.realbits - 1, 0);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               ret = tlc4541_get_range(st);
+               if (ret < 0)
+                       return ret;
+               *val = ret;
+               *val2 = chan->scan_type.realbits;
+               return IIO_VAL_FRACTIONAL_LOG2;
+       }
+       return -EINVAL;
+}
+
+static const struct iio_info tlc4541_info = {
+       .read_raw = &tlc4541_read_raw,
+       .driver_module = THIS_MODULE,
+};
+
+static int tlc4541_probe(struct spi_device *spi)
+{
+       struct tlc4541_state *st;
+       struct iio_dev *indio_dev;
+       const struct tlc4541_chip_info *info;
+       int ret;
+       int8_t device_init = 0;
+
+       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+       if (indio_dev == NULL)
+               return -ENOMEM;
+
+       st = iio_priv(indio_dev);
+
+       spi_set_drvdata(spi, indio_dev);
+
+       st->spi = spi;
+
+       info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data];
+
+       indio_dev->name = spi_get_device_id(spi)->name;
+       indio_dev->dev.parent = &spi->dev;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = info->channels;
+       indio_dev->num_channels = info->num_channels;
+       indio_dev->info = &tlc4541_info;
+
+       /* perform reset */
+       spi_write(spi, &device_init, 1);
+
+       /* Setup default message */
+       st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
+       st->scan_single_xfer[0].len = 3;
+       st->scan_single_xfer[1].delay_usecs = 3;
+       st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
+       st->scan_single_xfer[2].len = 2;
+
+       spi_message_init_with_transfers(&st->scan_single_msg,
+                                       st->scan_single_xfer, 3);
+
+       st->reg = devm_regulator_get(&spi->dev, "vref");
+       if (IS_ERR(st->reg))
+               return PTR_ERR(st->reg);
+
+       ret = regulator_enable(st->reg);
+       if (ret)
+               return ret;
+
+       ret = iio_triggered_buffer_setup(indio_dev, NULL,
+                       &tlc4541_trigger_handler, NULL);
+       if (ret)
+               goto error_disable_reg;
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto error_cleanup_buffer;
+
+       return 0;
+
+error_cleanup_buffer:
+       iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+       regulator_disable(st->reg);
+
+       return ret;
+}
+
+static int tlc4541_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+       struct tlc4541_state *st = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+       regulator_disable(st->reg);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id tlc4541_dt_ids[] = {
+       { .compatible = "ti,tlc3541", },
+       { .compatible = "ti,tlc4541", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
+#endif
+
+static const struct spi_device_id tlc4541_id[] = {
+       {"tlc3541", TLC3541},
+       {"tlc4541", TLC4541},
+       {}
+};
+MODULE_DEVICE_TABLE(spi, tlc4541_id);
+
+static struct spi_driver tlc4541_driver = {
+       .driver = {
+               .name   = "tlc4541",
+               .of_match_table = of_match_ptr(tlc4541_dt_ids),
+       },
+       .probe          = tlc4541_probe,
+       .remove         = tlc4541_remove,
+       .id_table       = tlc4541_id,
+};
+module_spi_driver(tlc4541_driver);
+
+MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>");
+MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC");
+MODULE_LICENSE("GPL v2");
index 90ab8a2d2846f8a8591ee6b1615dce2c984020ef..9648c69ea1a2062b9297d8fd8c6701d88a08c3e4 100644 (file)
@@ -378,7 +378,7 @@ static int max30100_get_temp(struct max30100_data *data, int *val)
        if (ret)
                return ret;
 
-       usleep_range(35000, 50000);
+       msleep(35);
 
        return max30100_read_temp(data, val);
 }
index 1f1ad41ef881faa468b8a290b441dcf980dd6519..156630a216960bba56d8e16fc208d80345fd15b2 100644 (file)
@@ -39,6 +39,7 @@ config KMX61
          be called kmx61.
 
 source "drivers/iio/imu/inv_mpu6050/Kconfig"
+source "drivers/iio/imu/st_lsm6dsx/Kconfig"
 
 endmenu
 
index c71bcd30dc38ae487f895d288df56d775c846341..8b563c3323b572926217b54094a1b23a3f815857 100644 (file)
@@ -17,3 +17,5 @@ obj-y += bmi160/
 obj-y += inv_mpu6050/
 
 obj-$(CONFIG_KMX61) += kmx61.o
+
+obj-y += st_lsm6dsx/
index c9e319bff58b314f626797f9dbf36339eb76be04..cfd225ed1c8da57b7a7726ff32aca80e8cb70e1a 100644 (file)
@@ -325,9 +325,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
        __le16 sample;
        enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
 
-       reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
+       reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
 
-       ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
+       ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
        if (ret < 0)
                return ret;
 
@@ -392,8 +392,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
 
        for_each_set_bit(i, indio_dev->active_scan_mask,
                         indio_dev->masklength) {
-               ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
-                                      &sample, sizeof(__le16));
+               ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
+                                      &sample, sizeof(sample));
                if (ret < 0)
                        goto done;
                buf[j++] = sample;
diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
new file mode 100644 (file)
index 0000000..935d4cd
--- /dev/null
@@ -0,0 +1,22 @@
+
+config IIO_ST_LSM6DSX
+       tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
+       depends on (I2C || SPI)
+       select IIO_BUFFER
+       select IIO_KFIFO_BUF
+       select IIO_ST_LSM6DSX_I2C if (I2C)
+       select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
+       help
+         Say yes here to build support for STMicroelectronics LSM6DSx imu
+         sensor. Supported devices: lsm6ds3, lsm6dsm
+
+         To compile this driver as a module, choose M here: the module
+         will be called st_lsm6dsx.
+
+config IIO_ST_LSM6DSX_I2C
+       tristate
+       depends on IIO_ST_LSM6DSX
+
+config IIO_ST_LSM6DSX_SPI
+       tristate
+       depends on IIO_ST_LSM6DSX
diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
new file mode 100644 (file)
index 0000000..35919fe
--- /dev/null
@@ -0,0 +1,5 @@
+st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
+
+obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
+obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
+obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
new file mode 100644 (file)
index 0000000..69deafe
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * STMicroelectronics st_lsm6dsx sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_LSM6DSX_H
+#define ST_LSM6DSX_H
+
+#include <linux/device.h>
+
+#define ST_LSM6DS3_DEV_NAME    "lsm6ds3"
+#define ST_LSM6DSM_DEV_NAME    "lsm6dsm"
+
+enum st_lsm6dsx_hw_id {
+       ST_LSM6DS3_ID,
+       ST_LSM6DSM_ID,
+};
+
+#define ST_LSM6DSX_CHAN_SIZE           2
+#define ST_LSM6DSX_SAMPLE_SIZE         6
+#define ST_LSM6DSX_SAMPLE_DEPTH                (ST_LSM6DSX_SAMPLE_SIZE / \
+                                        ST_LSM6DSX_CHAN_SIZE)
+
+#if defined(CONFIG_SPI_MASTER)
+#define ST_LSM6DSX_RX_MAX_LENGTH       256
+#define ST_LSM6DSX_TX_MAX_LENGTH       8
+
+struct st_lsm6dsx_transfer_buffer {
+       u8 rx_buf[ST_LSM6DSX_RX_MAX_LENGTH];
+       u8 tx_buf[ST_LSM6DSX_TX_MAX_LENGTH] ____cacheline_aligned;
+};
+#endif /* CONFIG_SPI_MASTER */
+
+struct st_lsm6dsx_transfer_function {
+       int (*read)(struct device *dev, u8 addr, int len, u8 *data);
+       int (*write)(struct device *dev, u8 addr, int len, u8 *data);
+};
+
+struct st_lsm6dsx_reg {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_lsm6dsx_settings {
+       u8 wai;
+       u16 max_fifo_size;
+       enum st_lsm6dsx_hw_id id;
+};
+
+enum st_lsm6dsx_sensor_id {
+       ST_LSM6DSX_ID_ACC,
+       ST_LSM6DSX_ID_GYRO,
+       ST_LSM6DSX_ID_MAX,
+};
+
+enum st_lsm6dsx_fifo_mode {
+       ST_LSM6DSX_FIFO_BYPASS = 0x0,
+       ST_LSM6DSX_FIFO_CONT = 0x6,
+};
+
+/**
+ * struct st_lsm6dsx_sensor - ST IMU sensor instance
+ * @id: Sensor identifier.
+ * @hw: Pointer to instance of struct st_lsm6dsx_hw.
+ * @gain: Configured sensor sensitivity.
+ * @odr: Output data rate of the sensor [Hz].
+ * @watermark: Sensor watermark level.
+ * @sip: Number of samples in a given pattern.
+ * @decimator: FIFO decimation factor.
+ * @decimator_mask: Sensor mask for decimation register.
+ * @delta_ts: Delta time between two consecutive interrupts.
+ * @ts: Latest timestamp from the interrupt handler.
+ */
+struct st_lsm6dsx_sensor {
+       enum st_lsm6dsx_sensor_id id;
+       struct st_lsm6dsx_hw *hw;
+
+       u32 gain;
+       u16 odr;
+
+       u16 watermark;
+       u8 sip;
+       u8 decimator;
+       u8 decimator_mask;
+
+       s64 delta_ts;
+       s64 ts;
+};
+
+/**
+ * struct st_lsm6dsx_hw - ST IMU MEMS hw instance
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @irq: Device interrupt line (I2C or SPI).
+ * @lock: Mutex to protect read and write operations.
+ * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
+ * @fifo_mode: FIFO operating mode supported by the device.
+ * @enable_mask: Enabled sensor bitmask.
+ * @sip: Total number of samples (acc/gyro) in a given pattern.
+ * @iio_devs: Pointers to acc/gyro iio_dev instances.
+ * @settings: Pointer to the specific sensor settings in use.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers used by SPI I/O operations.
+ */
+struct st_lsm6dsx_hw {
+       struct device *dev;
+       int irq;
+
+       struct mutex lock;
+       struct mutex fifo_lock;
+
+       enum st_lsm6dsx_fifo_mode fifo_mode;
+       u8 enable_mask;
+       u8 sip;
+
+       struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX];
+
+       const struct st_lsm6dsx_settings *settings;
+
+       const struct st_lsm6dsx_transfer_function *tf;
+#if defined(CONFIG_SPI_MASTER)
+       struct st_lsm6dsx_transfer_buffer tb;
+#endif /* CONFIG_SPI_MASTER */
+};
+
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
+                    const struct st_lsm6dsx_transfer_function *tf_ops);
+int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
+int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
+                              u8 val);
+int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
+                               u16 watermark);
+
+#endif /* ST_LSM6DSX_H */
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
new file mode 100644 (file)
index 0000000..78532ce
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * STMicroelectronics st_lsm6dsx FIFO buffer library driver
+ *
+ * LSM6DS3/LSM6DSM: The FIFO buffer can be configured to store data
+ * from gyroscope and accelerometer. Samples are queued without any tag
+ * according to a specific pattern based on 'FIFO data sets' (6 bytes each):
+ *  - 1st data set is reserved for gyroscope data
+ *  - 2nd data set is reserved for accelerometer data
+ * The FIFO pattern changes depending on the ODRs and decimation factors
+ * assigned to the FIFO data sets. The first sequence of data stored in FIFO
+ * buffer contains the data of all the enabled FIFO data sets
+ * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
+ * value of the decimation factor and ODR set for each FIFO data set.
+ * FIFO supported modes:
+ *  - BYPASS: FIFO disabled
+ *  - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
+ *    restarts from the beginning and the oldest sample is overwritten
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_REG_FIFO_THL_ADDR           0x06
+#define ST_LSM6DSX_REG_FIFO_THH_ADDR           0x07
+#define ST_LSM6DSX_FIFO_TH_MASK                        GENMASK(11, 0)
+#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR       0x08
+#define ST_LSM6DSX_REG_FIFO_MODE_ADDR          0x0a
+#define ST_LSM6DSX_FIFO_MODE_MASK              GENMASK(2, 0)
+#define ST_LSM6DSX_FIFO_ODR_MASK               GENMASK(6, 3)
+#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR         0x3a
+#define ST_LSM6DSX_FIFO_DIFF_MASK              GENMASK(11, 0)
+#define ST_LSM6DSX_FIFO_EMPTY_MASK             BIT(12)
+#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR          0x3e
+
+#define ST_LSM6DSX_MAX_FIFO_ODR_VAL            0x08
+
+struct st_lsm6dsx_decimator_entry {
+       u8 decimator;
+       u8 val;
+};
+
+static const
+struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
+       {  0, 0x0 },
+       {  1, 0x1 },
+       {  2, 0x2 },
+       {  3, 0x3 },
+       {  4, 0x4 },
+       {  8, 0x5 },
+       { 16, 0x6 },
+       { 32, 0x7 },
+};
+
+static int st_lsm6dsx_get_decimator_val(u8 val)
+{
+       const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table);
+       int i;
+
+       for (i = 0; i < max_size; i++)
+               if (st_lsm6dsx_decimator_table[i].decimator == val)
+                       break;
+
+       return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
+}
+
+static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
+                                      u16 *max_odr, u16 *min_odr)
+{
+       struct st_lsm6dsx_sensor *sensor;
+       int i;
+
+       *max_odr = 0, *min_odr = ~0;
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               sensor = iio_priv(hw->iio_devs[i]);
+
+               if (!(hw->enable_mask & BIT(sensor->id)))
+                       continue;
+
+               *max_odr = max_t(u16, *max_odr, sensor->odr);
+               *min_odr = min_t(u16, *min_odr, sensor->odr);
+       }
+}
+
+static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
+{
+       struct st_lsm6dsx_sensor *sensor;
+       u16 max_odr, min_odr, sip = 0;
+       int err, i;
+       u8 data;
+
+       st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               sensor = iio_priv(hw->iio_devs[i]);
+
+               /* update fifo decimators and sample in pattern */
+               if (hw->enable_mask & BIT(sensor->id)) {
+                       sensor->sip = sensor->odr / min_odr;
+                       sensor->decimator = max_odr / sensor->odr;
+                       data = st_lsm6dsx_get_decimator_val(sensor->decimator);
+               } else {
+                       sensor->sip = 0;
+                       sensor->decimator = 0;
+                       data = 0;
+               }
+
+               err = st_lsm6dsx_write_with_mask(hw,
+                                       ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR,
+                                       sensor->decimator_mask, data);
+               if (err < 0)
+                       return err;
+
+               sip += sensor->sip;
+       }
+       hw->sip = sip;
+
+       return 0;
+}
+
+static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
+                                   enum st_lsm6dsx_fifo_mode fifo_mode)
+{
+       u8 data;
+       int err;
+
+       switch (fifo_mode) {
+       case ST_LSM6DSX_FIFO_BYPASS:
+               data = fifo_mode;
+               break;
+       case ST_LSM6DSX_FIFO_CONT:
+               data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL <<
+                       __ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+                           sizeof(data), &data);
+       if (err < 0)
+               return err;
+
+       hw->fifo_mode = fifo_mode;
+
+       return 0;
+}
+
+int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
+{
+       u16 fifo_watermark = ~0, cur_watermark, sip = 0;
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+       struct st_lsm6dsx_sensor *cur_sensor;
+       __le16 wdata;
+       int i, err;
+       u8 data;
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               cur_sensor = iio_priv(hw->iio_devs[i]);
+
+               if (!(hw->enable_mask & BIT(cur_sensor->id)))
+                       continue;
+
+               cur_watermark = (cur_sensor == sensor) ? watermark
+                                                      : cur_sensor->watermark;
+
+               fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
+               sip += cur_sensor->sip;
+       }
+
+       if (!sip)
+               return 0;
+
+       fifo_watermark = max_t(u16, fifo_watermark, sip);
+       fifo_watermark = (fifo_watermark / sip) * sip;
+       fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH;
+
+       mutex_lock(&hw->lock);
+
+       err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR,
+                          sizeof(data), &data);
+       if (err < 0)
+               goto out;
+
+       fifo_watermark = ((data & ~ST_LSM6DSX_FIFO_TH_MASK) << 8) |
+                         (fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK);
+
+       wdata = cpu_to_le16(fifo_watermark);
+       err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR,
+                           sizeof(wdata), (u8 *)&wdata);
+out:
+       mutex_unlock(&hw->lock);
+
+       return err < 0 ? err : 0;
+}
+
+/**
+ * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine
+ * @hw: Pointer to instance of struct st_lsm6dsx_hw.
+ *
+ * Read samples from the hw FIFO and push them to IIO buffers.
+ *
+ * Return: Number of bytes read from the FIFO
+ */
+static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
+{
+       u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
+       int err, acc_sip, gyro_sip, read_len, samples, offset;
+       struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
+       s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
+       u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
+       u8 buff[pattern_len];
+       __le16 fifo_status;
+
+       err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR,
+                          sizeof(fifo_status), (u8 *)&fifo_status);
+       if (err < 0)
+               return err;
+
+       if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
+               return 0;
+
+       fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) *
+                  ST_LSM6DSX_CHAN_SIZE;
+       samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
+       fifo_len = (fifo_len / pattern_len) * pattern_len;
+
+       /*
+        * compute delta timestamp between two consecutive samples
+        * in order to estimate queueing time of data generated
+        * by the sensor
+        */
+       acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+       acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
+       acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
+                              samples);
+
+       gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
+       gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
+       gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
+                               samples);
+
+       for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
+               err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
+                                  sizeof(buff), buff);
+               if (err < 0)
+                       return err;
+
+               /*
+                * Data are written to the FIFO with a specific pattern
+                * depending on the configured ODRs. The first sequence of data
+                * stored in FIFO contains the data of all enabled sensors
+                * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
+                * depending on the value of the decimation factor set for each
+                * sensor.
+                *
+                * Supposing the FIFO is storing data from gyroscope and
+                * accelerometer at different ODRs:
+                *   - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
+                * Since the gyroscope ODR is twice the accelerometer one, the
+                * following pattern is repeated every 9 samples:
+                *   - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
+                */
+               gyro_sip = gyro_sensor->sip;
+               acc_sip = acc_sensor->sip;
+               offset = 0;
+
+               while (acc_sip > 0 || gyro_sip > 0) {
+                       if (gyro_sip-- > 0) {
+                               memcpy(iio_buff, &buff[offset],
+                                      ST_LSM6DSX_SAMPLE_SIZE);
+                               iio_push_to_buffers_with_timestamp(
+                                       hw->iio_devs[ST_LSM6DSX_ID_GYRO],
+                                       iio_buff, gyro_ts);
+                               offset += ST_LSM6DSX_SAMPLE_SIZE;
+                               gyro_ts += gyro_delta_ts;
+                       }
+
+                       if (acc_sip-- > 0) {
+                               memcpy(iio_buff, &buff[offset],
+                                      ST_LSM6DSX_SAMPLE_SIZE);
+                               iio_push_to_buffers_with_timestamp(
+                                       hw->iio_devs[ST_LSM6DSX_ID_ACC],
+                                       iio_buff, acc_ts);
+                               offset += ST_LSM6DSX_SAMPLE_SIZE;
+                               acc_ts += acc_delta_ts;
+                       }
+               }
+       }
+
+       return read_len;
+}
+
+static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
+{
+       int err;
+
+       mutex_lock(&hw->fifo_lock);
+
+       st_lsm6dsx_read_fifo(hw);
+       err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
+
+       mutex_unlock(&hw->fifo_lock);
+
+       return err;
+}
+
+static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+       int err;
+
+       if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) {
+               err = st_lsm6dsx_flush_fifo(hw);
+               if (err < 0)
+                       return err;
+       }
+
+       if (enable) {
+               err = st_lsm6dsx_sensor_enable(sensor);
+               if (err < 0)
+                       return err;
+       } else {
+               err = st_lsm6dsx_sensor_disable(sensor);
+               if (err < 0)
+                       return err;
+       }
+
+       err = st_lsm6dsx_update_decimators(hw);
+       if (err < 0)
+               return err;
+
+       err = st_lsm6dsx_update_watermark(sensor, sensor->watermark);
+       if (err < 0)
+               return err;
+
+       if (hw->enable_mask) {
+               err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
+               if (err < 0)
+                       return err;
+
+               /*
+                * store enable buffer timestamp as reference to compute
+                * first delta timestamp
+                */
+               sensor->ts = iio_get_time_ns(iio_dev);
+       }
+
+       return 0;
+}
+
+static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
+{
+       struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
+       struct st_lsm6dsx_sensor *sensor;
+       int i;
+
+       if (!hw->sip)
+               return IRQ_NONE;
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               sensor = iio_priv(hw->iio_devs[i]);
+
+               if (sensor->sip > 0) {
+                       s64 timestamp;
+
+                       timestamp = iio_get_time_ns(hw->iio_devs[i]);
+                       sensor->delta_ts = timestamp - sensor->ts;
+                       sensor->ts = timestamp;
+               }
+       }
+
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
+{
+       struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
+       int count;
+
+       mutex_lock(&hw->fifo_lock);
+       count = st_lsm6dsx_read_fifo(hw);
+       mutex_unlock(&hw->fifo_lock);
+
+       return !count ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
+{
+       return st_lsm6dsx_update_fifo(iio_dev, true);
+}
+
+static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
+{
+       return st_lsm6dsx_update_fifo(iio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
+       .preenable = st_lsm6dsx_buffer_preenable,
+       .postdisable = st_lsm6dsx_buffer_postdisable,
+};
+
+int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
+{
+       struct iio_buffer *buffer;
+       unsigned long irq_type;
+       int i, err;
+
+       irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
+
+       switch (irq_type) {
+       case IRQF_TRIGGER_HIGH:
+       case IRQF_TRIGGER_RISING:
+               break;
+       default:
+               dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
+               return -EINVAL;
+       }
+
+       err = devm_request_threaded_irq(hw->dev, hw->irq,
+                                       st_lsm6dsx_handler_irq,
+                                       st_lsm6dsx_handler_thread,
+                                       irq_type | IRQF_ONESHOT,
+                                       "lsm6dsx", hw);
+       if (err) {
+               dev_err(hw->dev, "failed to request trigger irq %d\n",
+                       hw->irq);
+               return err;
+       }
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               buffer = devm_iio_kfifo_allocate(hw->dev);
+               if (!buffer)
+                       return -ENOMEM;
+
+               iio_device_attach_buffer(hw->iio_devs[i], buffer);
+               hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
+               hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
+       }
+
+       return 0;
+}
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
new file mode 100644 (file)
index 0000000..f869dfa
--- /dev/null
@@ -0,0 +1,673 @@
+/*
+ * STMicroelectronics st_lsm6dsx sensor driver
+ *
+ * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer
+ * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial
+ * interface standard output.
+ * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale
+ * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of
+ * +-125/+-245/+-500/+-1000/+-2000 dps
+ * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
+ * allowing dynamic batching of sensor data.
+ *
+ * Supported sensors:
+ * - LSM6DS3:
+ *   - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
+ *   - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ *   - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
+ *   - FIFO size: 8KB
+ *
+ * - LSM6DSM:
+ *   - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
+ *   - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ *   - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
+ *   - FIFO size: 4KB
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_REG_ACC_DEC_MASK            GENMASK(2, 0)
+#define ST_LSM6DSX_REG_GYRO_DEC_MASK           GENMASK(5, 3)
+#define ST_LSM6DSX_REG_INT1_ADDR               0x0d
+#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK       BIT(3)
+#define ST_LSM6DSX_REG_WHOAMI_ADDR             0x0f
+#define ST_LSM6DSX_REG_RESET_ADDR              0x12
+#define ST_LSM6DSX_REG_RESET_MASK              BIT(0)
+#define ST_LSM6DSX_REG_BDU_ADDR                        0x12
+#define ST_LSM6DSX_REG_BDU_MASK                        BIT(6)
+#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR       0x13
+#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK       BIT(5)
+#define ST_LSM6DSX_REG_ROUNDING_ADDR           0x16
+#define ST_LSM6DSX_REG_ROUNDING_MASK           BIT(2)
+#define ST_LSM6DSX_REG_LIR_ADDR                        0x58
+#define ST_LSM6DSX_REG_LIR_MASK                        BIT(0)
+
+#define ST_LSM6DSX_REG_ACC_ODR_ADDR            0x10
+#define ST_LSM6DSX_REG_ACC_ODR_MASK            GENMASK(7, 4)
+#define ST_LSM6DSX_REG_ACC_FS_ADDR             0x10
+#define ST_LSM6DSX_REG_ACC_FS_MASK             GENMASK(3, 2)
+#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR                0x28
+#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR                0x2a
+#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR                0x2c
+
+#define ST_LSM6DSX_REG_GYRO_ODR_ADDR           0x11
+#define ST_LSM6DSX_REG_GYRO_ODR_MASK           GENMASK(7, 4)
+#define ST_LSM6DSX_REG_GYRO_FS_ADDR            0x11
+#define ST_LSM6DSX_REG_GYRO_FS_MASK            GENMASK(3, 2)
+#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR       0x22
+#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR       0x24
+#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR       0x26
+
+#define ST_LSM6DS3_WHOAMI                      0x69
+#define ST_LSM6DSM_WHOAMI                      0x6a
+
+#define ST_LSM6DS3_MAX_FIFO_SIZE               8192
+#define ST_LSM6DSM_MAX_FIFO_SIZE               4096
+
+#define ST_LSM6DSX_ACC_FS_2G_GAIN              IIO_G_TO_M_S_2(61)
+#define ST_LSM6DSX_ACC_FS_4G_GAIN              IIO_G_TO_M_S_2(122)
+#define ST_LSM6DSX_ACC_FS_8G_GAIN              IIO_G_TO_M_S_2(244)
+#define ST_LSM6DSX_ACC_FS_16G_GAIN             IIO_G_TO_M_S_2(488)
+
+#define ST_LSM6DSX_GYRO_FS_245_GAIN            IIO_DEGREE_TO_RAD(8750)
+#define ST_LSM6DSX_GYRO_FS_500_GAIN            IIO_DEGREE_TO_RAD(17500)
+#define ST_LSM6DSX_GYRO_FS_1000_GAIN           IIO_DEGREE_TO_RAD(35000)
+#define ST_LSM6DSX_GYRO_FS_2000_GAIN           IIO_DEGREE_TO_RAD(70000)
+
+struct st_lsm6dsx_odr {
+       u16 hz;
+       u8 val;
+};
+
+#define ST_LSM6DSX_ODR_LIST_SIZE       6
+struct st_lsm6dsx_odr_table_entry {
+       struct st_lsm6dsx_reg reg;
+       struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
+};
+
+static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
+       [ST_LSM6DSX_ID_ACC] = {
+               .reg = {
+                       .addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
+                       .mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
+               },
+               .odr_avl[0] = {  13, 0x01 },
+               .odr_avl[1] = {  26, 0x02 },
+               .odr_avl[2] = {  52, 0x03 },
+               .odr_avl[3] = { 104, 0x04 },
+               .odr_avl[4] = { 208, 0x05 },
+               .odr_avl[5] = { 416, 0x06 },
+       },
+       [ST_LSM6DSX_ID_GYRO] = {
+               .reg = {
+                       .addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
+                       .mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
+               },
+               .odr_avl[0] = {  13, 0x01 },
+               .odr_avl[1] = {  26, 0x02 },
+               .odr_avl[2] = {  52, 0x03 },
+               .odr_avl[3] = { 104, 0x04 },
+               .odr_avl[4] = { 208, 0x05 },
+               .odr_avl[5] = { 416, 0x06 },
+       }
+};
+
+struct st_lsm6dsx_fs {
+       u32 gain;
+       u8 val;
+};
+
+#define ST_LSM6DSX_FS_LIST_SIZE                4
+struct st_lsm6dsx_fs_table_entry {
+       struct st_lsm6dsx_reg reg;
+       struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
+};
+
+static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
+       [ST_LSM6DSX_ID_ACC] = {
+               .reg = {
+                       .addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
+                       .mask = ST_LSM6DSX_REG_ACC_FS_MASK,
+               },
+               .fs_avl[0] = {  ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
+               .fs_avl[1] = {  ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
+               .fs_avl[2] = {  ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
+               .fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
+       },
+       [ST_LSM6DSX_ID_GYRO] = {
+               .reg = {
+                       .addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
+                       .mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
+               },
+               .fs_avl[0] = {  ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
+               .fs_avl[1] = {  ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
+               .fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
+               .fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
+       }
+};
+
+static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
+       {
+               .wai = ST_LSM6DS3_WHOAMI,
+               .max_fifo_size = ST_LSM6DS3_MAX_FIFO_SIZE,
+               .id = ST_LSM6DS3_ID,
+       },
+       {
+               .wai = ST_LSM6DSM_WHOAMI,
+               .max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE,
+               .id = ST_LSM6DSM_ID,
+       },
+};
+
+#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)             \
+{                                                                      \
+       .type = chan_type,                                              \
+       .address = addr,                                                \
+       .modified = 1,                                                  \
+       .channel2 = mod,                                                \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |                  \
+                             BIT(IIO_CHAN_INFO_SCALE),                 \
+       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),        \
+       .scan_index = scan_idx,                                         \
+       .scan_type = {                                                  \
+               .sign = 's',                                            \
+               .realbits = 16,                                         \
+               .storagebits = 16,                                      \
+               .endianness = IIO_LE,                                   \
+       },                                                              \
+}
+
+static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
+       ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
+                          IIO_MOD_X, 0),
+       ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
+                          IIO_MOD_Y, 1),
+       ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
+                          IIO_MOD_Z, 2),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
+       ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
+                          IIO_MOD_X, 0),
+       ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
+                          IIO_MOD_Y, 1),
+       ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
+                          IIO_MOD_Z, 2),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
+                              u8 val)
+{
+       u8 data;
+       int err;
+
+       mutex_lock(&hw->lock);
+
+       err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
+       if (err < 0) {
+               dev_err(hw->dev, "failed to read %02x register\n", addr);
+               goto out;
+       }
+
+       data = (data & ~mask) | ((val << __ffs(mask)) & mask);
+
+       err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
+       if (err < 0)
+               dev_err(hw->dev, "failed to write %02x register\n", addr);
+
+out:
+       mutex_unlock(&hw->lock);
+
+       return err;
+}
+
+static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
+{
+       int err, i;
+       u8 data;
+
+       for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
+               if (id == st_lsm6dsx_sensor_settings[i].id)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) {
+               dev_err(hw->dev, "unsupported hw id [%02x]\n", id);
+               return -ENODEV;
+       }
+
+       err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_WHOAMI_ADDR, sizeof(data),
+                          &data);
+       if (err < 0) {
+               dev_err(hw->dev, "failed to read whoami register\n");
+               return err;
+       }
+
+       if (data != st_lsm6dsx_sensor_settings[i].wai) {
+               dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
+               return -ENODEV;
+       }
+
+       hw->settings = &st_lsm6dsx_sensor_settings[i];
+
+       return 0;
+}
+
+static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
+                                    u32 gain)
+{
+       enum st_lsm6dsx_sensor_id id = sensor->id;
+       int i, err;
+       u8 val;
+
+       for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
+               if (st_lsm6dsx_fs_table[id].fs_avl[i].gain == gain)
+                       break;
+
+       if (i == ST_LSM6DSX_FS_LIST_SIZE)
+               return -EINVAL;
+
+       val = st_lsm6dsx_fs_table[id].fs_avl[i].val;
+       err = st_lsm6dsx_write_with_mask(sensor->hw,
+                                        st_lsm6dsx_fs_table[id].reg.addr,
+                                        st_lsm6dsx_fs_table[id].reg.mask,
+                                        val);
+       if (err < 0)
+               return err;
+
+       sensor->gain = gain;
+
+       return 0;
+}
+
+static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+{
+       enum st_lsm6dsx_sensor_id id = sensor->id;
+       int i, err;
+       u8 val;
+
+       for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+               if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr)
+                       break;
+
+       if (i == ST_LSM6DSX_ODR_LIST_SIZE)
+               return -EINVAL;
+
+       val = st_lsm6dsx_odr_table[id].odr_avl[i].val;
+       err = st_lsm6dsx_write_with_mask(sensor->hw,
+                                        st_lsm6dsx_odr_table[id].reg.addr,
+                                        st_lsm6dsx_odr_table[id].reg.mask,
+                                        val);
+       if (err < 0)
+               return err;
+
+       sensor->odr = odr;
+
+       return 0;
+}
+
+int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
+{
+       int err;
+
+       err = st_lsm6dsx_set_odr(sensor, sensor->odr);
+       if (err < 0)
+               return err;
+
+       sensor->hw->enable_mask |= BIT(sensor->id);
+
+       return 0;
+}
+
+int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
+{
+       enum st_lsm6dsx_sensor_id id = sensor->id;
+       int err;
+
+       err = st_lsm6dsx_write_with_mask(sensor->hw,
+                                        st_lsm6dsx_odr_table[id].reg.addr,
+                                        st_lsm6dsx_odr_table[id].reg.mask, 0);
+       if (err < 0)
+               return err;
+
+       sensor->hw->enable_mask &= ~BIT(id);
+
+       return 0;
+}
+
+static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
+                                  u8 addr, int *val)
+{
+       int err, delay;
+       __le16 data;
+
+       err = st_lsm6dsx_sensor_enable(sensor);
+       if (err < 0)
+               return err;
+
+       delay = 1000000 / sensor->odr;
+       usleep_range(delay, 2 * delay);
+
+       err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data),
+                                  (u8 *)&data);
+       if (err < 0)
+               return err;
+
+       st_lsm6dsx_sensor_disable(sensor);
+
+       *val = (s16)data;
+
+       return IIO_VAL_INT;
+}
+
+static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev,
+                              struct iio_chan_spec const *ch,
+                              int *val, int *val2, long mask)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = iio_device_claim_direct_mode(iio_dev);
+               if (ret)
+                       break;
+
+               ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val);
+               iio_device_release_direct_mode(iio_dev);
+               break;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *val = sensor->odr;
+               ret = IIO_VAL_INT;
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               *val = 0;
+               *val2 = sensor->gain;
+               ret = IIO_VAL_INT_PLUS_MICRO;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
+                               struct iio_chan_spec const *chan,
+                               int val, int val2, long mask)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       int err;
+
+       err = iio_device_claim_direct_mode(iio_dev);
+       if (err)
+               return err;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err = st_lsm6dsx_set_full_scale(sensor, val2);
+               break;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               err = st_lsm6dsx_set_odr(sensor, val);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+
+       iio_device_release_direct_mode(iio_dev);
+
+       return err;
+}
+
+static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+       int err, max_fifo_len;
+
+       max_fifo_len = hw->settings->max_fifo_size / ST_LSM6DSX_SAMPLE_SIZE;
+       if (val < 1 || val > max_fifo_len)
+               return -EINVAL;
+
+       err = st_lsm6dsx_update_watermark(sensor, val);
+       if (err < 0)
+               return err;
+
+       sensor->watermark = val;
+
+       return 0;
+}
+
+static ssize_t
+st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+       enum st_lsm6dsx_sensor_id id = sensor->id;
+       int i, len = 0;
+
+       for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+               len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+                                st_lsm6dsx_odr_table[id].odr_avl[i].hz);
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+       enum st_lsm6dsx_sensor_id id = sensor->id;
+       int i, len = 0;
+
+       for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
+               len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+                                st_lsm6dsx_fs_table[id].fs_avl[i].gain);
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail);
+static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
+                      st_lsm6dsx_sysfs_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444,
+                      st_lsm6dsx_sysfs_scale_avail, NULL, 0);
+
+static struct attribute *st_lsm6dsx_acc_attributes[] = {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_acc_attribute_group = {
+       .attrs = st_lsm6dsx_acc_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_acc_info = {
+       .driver_module = THIS_MODULE,
+       .attrs = &st_lsm6dsx_acc_attribute_group,
+       .read_raw = st_lsm6dsx_read_raw,
+       .write_raw = st_lsm6dsx_write_raw,
+       .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static struct attribute *st_lsm6dsx_gyro_attributes[] = {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_gyro_attribute_group = {
+       .attrs = st_lsm6dsx_gyro_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_gyro_info = {
+       .driver_module = THIS_MODULE,
+       .attrs = &st_lsm6dsx_gyro_attribute_group,
+       .read_raw = st_lsm6dsx_read_raw,
+       .write_raw = st_lsm6dsx_write_raw,
+       .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
+
+static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
+{
+       int err;
+       u8 data;
+
+       data = ST_LSM6DSX_REG_RESET_MASK;
+       err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data),
+                           &data);
+       if (err < 0)
+               return err;
+
+       msleep(200);
+
+       /* latch interrupts */
+       err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_LIR_ADDR,
+                                        ST_LSM6DSX_REG_LIR_MASK, 1);
+       if (err < 0)
+               return err;
+
+       /* enable Block Data Update */
+       err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_BDU_ADDR,
+                                        ST_LSM6DSX_REG_BDU_MASK, 1);
+       if (err < 0)
+               return err;
+
+       err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_ROUNDING_ADDR,
+                                        ST_LSM6DSX_REG_ROUNDING_MASK, 1);
+       if (err < 0)
+               return err;
+
+       /* enable FIFO watermak interrupt */
+       err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT1_ADDR,
+                                        ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
+       if (err < 0)
+               return err;
+
+       /* redirect INT2 on INT1 */
+       return st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT2_ON_INT1_ADDR,
+                                         ST_LSM6DSX_REG_INT2_ON_INT1_MASK, 1);
+}
+
+static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
+                                              enum st_lsm6dsx_sensor_id id)
+{
+       struct st_lsm6dsx_sensor *sensor;
+       struct iio_dev *iio_dev;
+
+       iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
+       if (!iio_dev)
+               return NULL;
+
+       iio_dev->modes = INDIO_DIRECT_MODE;
+       iio_dev->dev.parent = hw->dev;
+       iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
+
+       sensor = iio_priv(iio_dev);
+       sensor->id = id;
+       sensor->hw = hw;
+       sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
+       sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
+       sensor->watermark = 1;
+
+       switch (id) {
+       case ST_LSM6DSX_ID_ACC:
+               iio_dev->channels = st_lsm6dsx_acc_channels;
+               iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
+               iio_dev->name = "lsm6dsx_accel";
+               iio_dev->info = &st_lsm6dsx_acc_info;
+
+               sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
+               break;
+       case ST_LSM6DSX_ID_GYRO:
+               iio_dev->channels = st_lsm6dsx_gyro_channels;
+               iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
+               iio_dev->name = "lsm6dsx_gyro";
+               iio_dev->info = &st_lsm6dsx_gyro_info;
+
+               sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
+               break;
+       default:
+               return NULL;
+       }
+
+       return iio_dev;
+}
+
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
+                    const struct st_lsm6dsx_transfer_function *tf_ops)
+{
+       struct st_lsm6dsx_hw *hw;
+       int i, err;
+
+       hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+       if (!hw)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, (void *)hw);
+
+       mutex_init(&hw->lock);
+       mutex_init(&hw->fifo_lock);
+
+       hw->dev = dev;
+       hw->irq = irq;
+       hw->tf = tf_ops;
+
+       err = st_lsm6dsx_check_whoami(hw, hw_id);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
+               if (!hw->iio_devs[i])
+                       return -ENOMEM;
+       }
+
+       err = st_lsm6dsx_init_device(hw);
+       if (err < 0)
+               return err;
+
+       if (hw->irq > 0) {
+               err = st_lsm6dsx_fifo_setup(hw);
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(st_lsm6dsx_probe);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
new file mode 100644 (file)
index 0000000..ea30411
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * STMicroelectronics st_lsm6dsx i2c driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include "st_lsm6dsx.h"
+
+static int st_lsm6dsx_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct i2c_msg msg[2];
+
+       msg[0].addr = client->addr;
+       msg[0].flags = client->flags;
+       msg[0].len = 1;
+       msg[0].buf = &addr;
+
+       msg[1].addr = client->addr;
+       msg[1].flags = client->flags | I2C_M_RD;
+       msg[1].len = len;
+       msg[1].buf = data;
+
+       return i2c_transfer(client->adapter, msg, 2);
+}
+
+static int st_lsm6dsx_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct i2c_msg msg;
+       u8 send[len + 1];
+
+       send[0] = addr;
+       memcpy(&send[1], data, len * sizeof(u8));
+
+       msg.addr = client->addr;
+       msg.flags = client->flags;
+       msg.len = len + 1;
+       msg.buf = send;
+
+       return i2c_transfer(client->adapter, &msg, 1);
+}
+
+static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
+       .read = st_lsm6dsx_i2c_read,
+       .write = st_lsm6dsx_i2c_write,
+};
+
+static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       return st_lsm6dsx_probe(&client->dev, client->irq,
+                               (int)id->driver_data,
+                               &st_lsm6dsx_transfer_fn);
+}
+
+static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
+       {
+               .compatible = "st,lsm6ds3",
+               .data = (void *)ST_LSM6DS3_ID,
+       },
+       {
+               .compatible = "st,lsm6dsm",
+               .data = (void *)ST_LSM6DSM_ID,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
+
+static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
+       { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
+       { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
+
+static struct i2c_driver st_lsm6dsx_driver = {
+       .driver = {
+               .name = "st_lsm6dsx_i2c",
+               .of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match),
+       },
+       .probe = st_lsm6dsx_i2c_probe,
+       .id_table = st_lsm6dsx_i2c_id_table,
+};
+module_i2c_driver(st_lsm6dsx_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
new file mode 100644 (file)
index 0000000..fbe7247
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * STMicroelectronics st_lsm6dsx spi driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include "st_lsm6dsx.h"
+
+#define SENSORS_SPI_READ       BIT(7)
+
+static int st_lsm6dsx_spi_read(struct device *dev, u8 addr, int len,
+                              u8 *data)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct st_lsm6dsx_hw *hw = spi_get_drvdata(spi);
+       int err;
+
+       struct spi_transfer xfers[] = {
+               {
+                       .tx_buf = hw->tb.tx_buf,
+                       .bits_per_word = 8,
+                       .len = 1,
+               },
+               {
+                       .rx_buf = hw->tb.rx_buf,
+                       .bits_per_word = 8,
+                       .len = len,
+               }
+       };
+
+       hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
+
+       err = spi_sync_transfer(spi, xfers,  ARRAY_SIZE(xfers));
+       if (err < 0)
+               return err;
+
+       memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
+
+       return len;
+}
+
+static int st_lsm6dsx_spi_write(struct device *dev, u8 addr, int len,
+                               u8 *data)
+{
+       struct st_lsm6dsx_hw *hw;
+       struct spi_device *spi;
+
+       if (len >= ST_LSM6DSX_TX_MAX_LENGTH)
+               return -ENOMEM;
+
+       spi = to_spi_device(dev);
+       hw = spi_get_drvdata(spi);
+
+       hw->tb.tx_buf[0] = addr;
+       memcpy(&hw->tb.tx_buf[1], data, len);
+
+       return spi_write(spi, hw->tb.tx_buf, len + 1);
+}
+
+static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
+       .read = st_lsm6dsx_spi_read,
+       .write = st_lsm6dsx_spi_write,
+};
+
+static int st_lsm6dsx_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+
+       return st_lsm6dsx_probe(&spi->dev, spi->irq,
+                               (int)id->driver_data,
+                               &st_lsm6dsx_transfer_fn);
+}
+
+static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
+       {
+               .compatible = "st,lsm6ds3",
+               .data = (void *)ST_LSM6DS3_ID,
+       },
+       {
+               .compatible = "st,lsm6dsm",
+               .data = (void *)ST_LSM6DSM_ID,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
+
+static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
+       { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
+       { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
+
+static struct spi_driver st_lsm6dsx_driver = {
+       .driver = {
+               .name = "st_lsm6dsx_spi",
+               .of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match),
+       },
+       .probe = st_lsm6dsx_spi_probe,
+       .id_table = st_lsm6dsx_spi_id_table,
+};
+module_spi_driver(st_lsm6dsx_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver");
+MODULE_LICENSE("GPL v2");
index 978729f6d7c47744d166648c9f8f4a4b490d4efb..978e1592c2a37869eac3b07a48f27354dc6036ff 100644 (file)
@@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
        return NULL;
 }
 
-static struct iio_trigger *iio_trigger_find_by_name(const char *name,
-                                                   size_t len)
+static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
 {
        struct iio_trigger *trig = NULL, *iter;
 
@@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
        list_for_each_entry(iter, &iio_trigger_list, list)
                if (sysfs_streq(iter->name, name)) {
                        trig = iter;
+                       iio_trigger_get(trig);
                        break;
                }
        mutex_unlock(&iio_trigger_list_lock);
@@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev,
        }
        mutex_unlock(&indio_dev->mlock);
 
-       trig = iio_trigger_find_by_name(buf, len);
-       if (oldtrig == trig)
-               return len;
+       trig = iio_trigger_acquire_by_name(buf);
+       if (oldtrig == trig) {
+               ret = len;
+               goto out_trigger_put;
+       }
 
        if (trig && indio_dev->info->validate_trigger) {
                ret = indio_dev->info->validate_trigger(indio_dev, trig);
                if (ret)
-                       return ret;
+                       goto out_trigger_put;
        }
 
        if (trig && trig->ops->validate_device) {
                ret = trig->ops->validate_device(trig, indio_dev);
                if (ret)
-                       return ret;
+                       goto out_trigger_put;
        }
 
        indio_dev->trig = trig;
@@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev,
                iio_trigger_put(oldtrig);
        }
        if (indio_dev->trig) {
-               iio_trigger_get(indio_dev->trig);
                if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
                        iio_trigger_attach_poll_func(indio_dev->trig,
                                                     indio_dev->pollfunc_event);
        }
 
        return len;
+
+out_trigger_put:
+       iio_trigger_put(trig);
+       return ret;
 }
 
 static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
@@ -487,7 +492,7 @@ static void iio_trig_release(struct device *device)
        kfree(trig);
 }
 
-static struct device_type iio_trig_type = {
+static const struct device_type iio_trig_type = {
        .release = iio_trig_release,
        .groups = iio_trig_dev_groups,
 };
@@ -513,46 +518,45 @@ static void iio_trig_subirqunmask(struct irq_data *d)
 static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs)
 {
        struct iio_trigger *trig;
+       int i;
+
        trig = kzalloc(sizeof *trig, GFP_KERNEL);
-       if (trig) {
-               int i;
-               trig->dev.type = &iio_trig_type;
-               trig->dev.bus = &iio_bus_type;
-               device_initialize(&trig->dev);
-
-               mutex_init(&trig->pool_lock);
-               trig->subirq_base
-                       = irq_alloc_descs(-1, 0,
-                                         CONFIG_IIO_CONSUMERS_PER_TRIGGER,
-                                         0);
-               if (trig->subirq_base < 0) {
-                       kfree(trig);
-                       return NULL;
-               }
+       if (!trig)
+               return NULL;
 
-               trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
-               if (trig->name == NULL) {
-                       irq_free_descs(trig->subirq_base,
-                                      CONFIG_IIO_CONSUMERS_PER_TRIGGER);
-                       kfree(trig);
-                       return NULL;
-               }
-               trig->subirq_chip.name = trig->name;
-               trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
-               trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
-               for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
-                       irq_set_chip(trig->subirq_base + i,
-                                    &trig->subirq_chip);
-                       irq_set_handler(trig->subirq_base + i,
-                                       &handle_simple_irq);
-                       irq_modify_status(trig->subirq_base + i,
-                                         IRQ_NOREQUEST | IRQ_NOAUTOEN,
-                                         IRQ_NOPROBE);
-               }
-               get_device(&trig->dev);
+       trig->dev.type = &iio_trig_type;
+       trig->dev.bus = &iio_bus_type;
+       device_initialize(&trig->dev);
+
+       mutex_init(&trig->pool_lock);
+       trig->subirq_base = irq_alloc_descs(-1, 0,
+                                           CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+                                           0);
+       if (trig->subirq_base < 0)
+               goto free_trig;
+
+       trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+       if (trig->name == NULL)
+               goto free_descs;
+
+       trig->subirq_chip.name = trig->name;
+       trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
+       trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
+       for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
+               irq_set_chip(trig->subirq_base + i, &trig->subirq_chip);
+               irq_set_handler(trig->subirq_base + i, &handle_simple_irq);
+               irq_modify_status(trig->subirq_base + i,
+                                 IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
        }
+       get_device(&trig->dev);
 
        return trig;
+
+free_descs:
+       irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
+free_trig:
+       kfree(trig);
+       return NULL;
 }
 
 struct iio_trigger *iio_trigger_alloc(const char *fmt, ...)
index b0f4630a163f33cea23fad9c070babbb4d23c0e9..7a13535dc3e99b016c546f48a91009d458ea5b72 100644 (file)
@@ -601,8 +601,14 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
 
        scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
                                        IIO_CHAN_INFO_SCALE);
-       if (scale_type < 0)
-               return scale_type;
+       if (scale_type < 0) {
+               /*
+                * Just pass raw values as processed if no scaling is
+                * available.
+                */
+               *processed = raw;
+               return 0;
+       }
 
        switch (scale_type) {
        case IIO_VAL_INT:
index 2d96543db1ef87969415717314bf0122b9b9d750..980624e9ffb511b6b4f3a350a059f0f1afad6c99 100644 (file)
@@ -278,7 +278,7 @@ static int cm3605_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int cm3605_pm_suspend(struct device *dev)
+static int __maybe_unused cm3605_pm_suspend(struct device *dev)
 {
        struct iio_dev *indio_dev = dev_get_drvdata(dev);
        struct cm3605 *cm3605 = iio_priv(indio_dev);
@@ -289,7 +289,7 @@ static int cm3605_pm_suspend(struct device *dev)
        return 0;
 }
 
-static int cm3605_pm_resume(struct device *dev)
+static int __maybe_unused cm3605_pm_resume(struct device *dev)
 {
        struct iio_dev *indio_dev = dev_get_drvdata(dev);
        struct cm3605 *cm3605 = iio_priv(indio_dev);
index 78c9b3a6453ae57286923a42d9284774b2a696c0..b91ebc3483cea336b9dc4043e013cdf862bbf6a6 100644 (file)
@@ -840,6 +840,7 @@ static const struct of_device_id opt3001_of_match[] = {
        { .compatible = "ti,opt3001" },
        { }
 };
+MODULE_DEVICE_TABLE(of, opt3001_of_match);
 
 static struct i2c_driver opt3001_driver = {
        .probe = opt3001_probe,
index ce09d771c1fb5663c457cf8c85e4808ac114d2ca..6dd8cbd7ce9531a5883173f34eb30edc45301083 100644 (file)
@@ -278,13 +278,9 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
                if (val & AK8974_STATUS_DRDY)
                        return 0;
        } while (--timeout);
-       if (!timeout) {
-               dev_err(&ak8974->i2c->dev,
-                       "timeout waiting for DRDY\n");
-               return -ETIMEDOUT;
-       }
 
-       return 0;
+       dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n");
+       return -ETIMEDOUT;
 }
 
 static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
index f2b3bd7bf862114633519070da0901f48b2157e0..b4f643fb3b1ed995ce609883b16d35b69abdaf7d 100644 (file)
@@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev,
                             int val, int val2, long mask)
 {
        struct mag3110_data *data = iio_priv(indio_dev);
-       int rate;
+       int rate, ret;
 
-       if (iio_buffer_enabled(indio_dev))
-               return -EBUSY;
+       ret = iio_device_claim_direct_mode(indio_dev);
+       if (ret)
+               return ret;
 
        switch (mask) {
        case IIO_CHAN_INFO_SAMP_FREQ:
                rate = mag3110_get_samp_freq_index(data, val, val2);
-               if (rate < 0)
-                       return -EINVAL;
+               if (rate < 0) {
+                       ret = -EINVAL;
+                       break;
+               }
 
                data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
                data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
-               return i2c_smbus_write_byte_data(data->client,
+               ret = i2c_smbus_write_byte_data(data->client,
                        MAG3110_CTRL_REG1, data->ctrl_reg1);
+               break;
        case IIO_CHAN_INFO_CALIBBIAS:
-               if (val < -10000 || val > 10000)
-                       return -EINVAL;
-               return i2c_smbus_write_word_swapped(data->client,
+               if (val < -10000 || val > 10000) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = i2c_smbus_write_word_swapped(data->client,
                        MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
+               break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
+               break;
        }
+       iio_device_release_direct_mode(indio_dev);
+       return ret;
 }
 
 static irqreturn_t mag3110_trigger_handler(int irq, void *p)
index 0d1bcf89ae17cedbc4405970197554f6548e75d0..314353d7ab59183d3c5a37ed1384e03d4f58151c 100644 (file)
@@ -284,6 +284,7 @@ static const struct of_device_id mcp4531_of_match[] = {
        MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104),
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, mcp4531_of_match);
 #endif
 
 static int mcp4531_probe(struct i2c_client *client,
index 6bd53e7026674032791cd15c7cfceaf6b68a8ba5..2a77a2f157521c7383d666fd8b1ee4311cffb1db 100644 (file)
@@ -308,6 +308,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
 {
        struct ms5611_state *st = iio_priv(indio_dev);
        const struct ms5611_osr *osr = NULL;
+       int ret;
 
        if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
                return -EINVAL;
@@ -321,12 +322,11 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
        if (!osr)
                return -EINVAL;
 
-       mutex_lock(&st->lock);
+       ret = iio_device_claim_direct_mode(indio_dev);
+       if (ret)
+               return ret;
 
-       if (iio_buffer_enabled(indio_dev)) {
-               mutex_unlock(&st->lock);
-               return -EBUSY;
-       }
+       mutex_lock(&st->lock);
 
        if (chan->type == IIO_TEMP)
                st->temp_osr = osr;
@@ -334,6 +334,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
                st->pressure_osr = osr;
 
        mutex_unlock(&st->lock);
+       iio_device_release_direct_mode(indio_dev);
+
        return 0;
 }
 
index 1f06282ec793a07f66f7f34c187c302f5872577d..9ea147f1a50d10ee13412d41bac823f605bfc1b2 100644 (file)
@@ -387,14 +387,18 @@ 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:
                switch (mask) {
                case IIO_CHAN_INFO_RAW:
-                       if (iio_buffer_enabled(indio_dev))
-                               return -EBUSY;
-                       return sx9500_read_proximity(data, chan, val);
+                       ret = iio_device_claim_direct_mode(indio_dev);
+                       if (ret)
+                               return ret;
+                       ret = sx9500_read_proximity(data, chan, val);
+                       iio_device_release_direct_mode(indio_dev);
+                       return ret;
                case IIO_CHAN_INFO_SAMP_FREQ:
                        return sx9500_read_samp_freq(data, val, val2);
                default:
index 5ea77a7e261da87488b257123e152c5278bb41c9..3089e8d0a32d461b78eeff352f13853f881e09d5 100644 (file)
@@ -39,6 +39,16 @@ config TMP006
          This driver can also be built as a module. If so, the module will
          be called tmp006.
 
+config TMP007
+        tristate "TMP007 infrared thermopile sensor with Integrated Math Engine"
+        depends on I2C
+        help
+          If you say yes here you get support for the Texas Instruments
+          TMP007 infrared thermopile sensor with Integrated Math Engine.
+
+          This driver can also be built as a module. If so, the module will
+          be called tmp007.
+
 config TSYS01
        tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection"
        depends on I2C
index 78c3de0dc3f0d002402a06a7a9d471e8d402036a..4c4377480726fb3df0ced8264ad24f051bbf9a8a 100644 (file)
@@ -5,5 +5,6 @@
 obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
 obj-$(CONFIG_MLX90614) += mlx90614.o
 obj-$(CONFIG_TMP006) += tmp006.o
+obj-$(CONFIG_TMP007) += tmp007.o
 obj-$(CONFIG_TSYS01) += tsys01.o
 obj-$(CONFIG_TSYS02D) += tsys02d.o
diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c
new file mode 100644 (file)
index 0000000..24c6c16
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine
+ *
+ * Copyright (c) 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
+ *
+ * (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins)
+ *
+ * Note: This driver assumes that the sensor has been calibrated beforehand
+ *
+ * TODO: ALERT irq, limit threshold events
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define TMP007_TDIE 0x01
+#define TMP007_CONFIG 0x02
+#define TMP007_TOBJECT 0x03
+#define TMP007_STATUS 0x04
+#define TMP007_STATUS_MASK 0x05
+#define TMP007_MANUFACTURER_ID 0x1e
+#define TMP007_DEVICE_ID 0x1f
+
+#define TMP007_CONFIG_CONV_EN BIT(12)
+#define TMP007_CONFIG_COMP_EN BIT(5)
+#define TMP007_CONFIG_TC_EN BIT(6)
+#define TMP007_CONFIG_CR_MASK GENMASK(11, 9)
+#define TMP007_CONFIG_CR_SHIFT 9
+
+#define TMP007_STATUS_CONV_READY BIT(14)
+#define TMP007_STATUS_DATA_VALID BIT(9)
+
+#define TMP007_MANUFACTURER_MAGIC 0x5449
+#define TMP007_DEVICE_MAGIC 0x0078
+
+#define TMP007_TEMP_SHIFT 2
+
+struct tmp007_data {
+       struct i2c_client *client;
+       u16 config;
+};
+
+static const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0},
+                                       {0, 500000}, {0, 250000} };
+
+static int tmp007_read_temperature(struct tmp007_data *data, u8 reg)
+{
+       s32 ret;
+       int tries = 50;
+
+       while (tries-- > 0) {
+               ret = i2c_smbus_read_word_swapped(data->client,
+                       TMP007_STATUS);
+               if (ret < 0)
+                       return ret;
+               if ((ret & TMP007_STATUS_CONV_READY) &&
+                       !(ret & TMP007_STATUS_DATA_VALID))
+                               break;
+               msleep(100);
+       }
+
+       if (tries < 0)
+               return -EIO;
+
+       return i2c_smbus_read_word_swapped(data->client, reg);
+}
+
+static int tmp007_powerdown(struct tmp007_data *data)
+{
+       return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
+                       data->config & ~TMP007_CONFIG_CONV_EN);
+}
+
+static int tmp007_read_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *channel, int *val,
+               int *val2, long mask)
+{
+       struct tmp007_data *data = iio_priv(indio_dev);
+       s32 ret;
+       int conv_rate;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               switch (channel->channel2) {
+               case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.03125 degree Celsius */
+                       ret = i2c_smbus_read_word_swapped(data->client, TMP007_TDIE);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               case IIO_MOD_TEMP_OBJECT:
+                       ret = tmp007_read_temperature(data, TMP007_TOBJECT);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               *val = sign_extend32(ret, 15) >> TMP007_TEMP_SHIFT;
+
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val = 31;
+               *val2 = 250000;
+
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               conv_rate = (data->config & TMP007_CONFIG_CR_MASK)
+                               >> TMP007_CONFIG_CR_SHIFT;
+               *val = tmp007_avgs[conv_rate][0];
+               *val2 = tmp007_avgs[conv_rate][1];
+
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int tmp007_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *channel, int val,
+               int val2, long mask)
+{
+       struct tmp007_data *data = iio_priv(indio_dev);
+       int i;
+       u16 tmp;
+
+       if (mask == IIO_CHAN_INFO_SAMP_FREQ) {
+               for (i = 0; i < ARRAY_SIZE(tmp007_avgs); i++) {
+                       if ((val == tmp007_avgs[i][0]) &&
+                       (val2 == tmp007_avgs[i][1])) {
+                               tmp = data->config & ~TMP007_CONFIG_CR_MASK;
+                               tmp |= (i << TMP007_CONFIG_CR_SHIFT);
+
+                               return i2c_smbus_write_word_swapped(data->client,
+                                                               TMP007_CONFIG,
+                                                               data->config = tmp);
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+
+static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
+
+static struct attribute *tmp007_attributes[] = {
+       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group tmp007_attribute_group = {
+       .attrs = tmp007_attributes,
+};
+
+static const struct iio_chan_spec tmp007_channels[] = {
+       {
+               .type = IIO_TEMP,
+               .modified = 1,
+               .channel2 = IIO_MOD_TEMP_AMBIENT,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                               BIT(IIO_CHAN_INFO_SCALE),
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+       },
+       {
+               .type = IIO_TEMP,
+               .modified = 1,
+               .channel2 = IIO_MOD_TEMP_OBJECT,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                               BIT(IIO_CHAN_INFO_SCALE),
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+       }
+};
+
+static const struct iio_info tmp007_info = {
+       .read_raw = tmp007_read_raw,
+       .write_raw = tmp007_write_raw,
+       .attrs = &tmp007_attribute_group,
+       .driver_module = THIS_MODULE,
+};
+
+static bool tmp007_identify(struct i2c_client *client)
+{
+       int manf_id, dev_id;
+
+       manf_id = i2c_smbus_read_word_swapped(client, TMP007_MANUFACTURER_ID);
+       if (manf_id < 0)
+               return false;
+
+       dev_id = i2c_smbus_read_word_swapped(client, TMP007_DEVICE_ID);
+       if (dev_id < 0)
+               return false;
+
+       return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC);
+}
+
+static int tmp007_probe(struct i2c_client *client,
+                       const struct i2c_device_id *tmp007_id)
+{
+       struct tmp007_data *data;
+       struct iio_dev *indio_dev;
+       int ret;
+       u16  status;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+               return -EOPNOTSUPP;
+
+       if (!tmp007_identify(client)) {
+               dev_err(&client->dev, "TMP007 not found\n");
+               return -ENODEV;
+       }
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = dev_name(&client->dev);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &tmp007_info;
+
+       indio_dev->channels = tmp007_channels;
+       indio_dev->num_channels = ARRAY_SIZE(tmp007_channels);
+
+       /*
+        * Set Configuration register:
+        * 1. Conversion ON
+        * 2. Comparator mode
+        * 3. Transient correction enable
+        */
+
+       ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG);
+       if (ret < 0)
+               return ret;
+
+       data->config = ret;
+       data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_COMP_EN | TMP007_CONFIG_TC_EN);
+
+       ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
+                                       data->config);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Set Status Mask register:
+        * 1. Conversion ready enable
+        * 2. Data valid enable
+        */
+
+       ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK);
+       if (ret < 0)
+               goto error_powerdown;
+
+       status = ret;
+       status |= (TMP007_STATUS_CONV_READY | TMP007_STATUS_DATA_VALID);
+
+       ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, status);
+       if (ret < 0)
+               goto error_powerdown;
+
+       return iio_device_register(indio_dev);
+
+error_powerdown:
+       tmp007_powerdown(data);
+
+       return ret;
+}
+
+static int tmp007_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct tmp007_data *data = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       tmp007_powerdown(data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tmp007_suspend(struct device *dev)
+{
+       struct tmp007_data *data = iio_priv(i2c_get_clientdata(
+                       to_i2c_client(dev)));
+
+       return tmp007_powerdown(data);
+}
+
+static int tmp007_resume(struct device *dev)
+{
+       struct tmp007_data *data = iio_priv(i2c_get_clientdata(
+                       to_i2c_client(dev)));
+
+       return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
+                       data->config | TMP007_CONFIG_CONV_EN);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume);
+
+static const struct of_device_id tmp007_of_match[] = {
+       { .compatible = "ti,tmp007", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, tmp007_of_match);
+
+static const struct i2c_device_id tmp007_id[] = {
+       { "tmp007", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp007_id);
+
+static struct i2c_driver tmp007_driver = {
+       .driver = {
+               .name   = "tmp007",
+               .of_match_table = of_match_ptr(tmp007_of_match),
+               .pm     = &tmp007_pm_ops,
+       },
+       .probe          = tmp007_probe,
+       .remove         = tmp007_remove,
+       .id_table       = tmp007_id,
+};
+module_i2c_driver(tmp007_driver);
+
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>");
+MODULE_DESCRIPTION("TI TMP007 IR thermopile sensor driver");
+MODULE_LICENSE("GPL");
index 572bc6f02ca82987a9f3945bc073e4ab835effe9..e18f12b746100c0963c544598081b4cb2828e306 100644 (file)
@@ -58,7 +58,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev)
        trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
        if (!trig_info) {
                ret = -ENOMEM;
-               goto error_put_trigger;
+               goto error_free_trigger;
        }
        iio_trigger_set_drvdata(trig, trig_info);
        trig_info->irq = irq;
@@ -83,8 +83,8 @@ error_release_irq:
        free_irq(irq, trig);
 error_free_trig_info:
        kfree(trig_info);
-error_put_trigger:
-       iio_trigger_put(trig);
+error_free_trigger:
+       iio_trigger_free(trig);
 error_ret:
        return ret;
 }
@@ -99,7 +99,7 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev)
        iio_trigger_unregister(trig);
        free_irq(trig_info->irq, trig);
        kfree(trig_info);
-       iio_trigger_put(trig);
+       iio_trigger_free(trig);
 
        return 0;
 }
index 3dfab2bc6d6935368a6cec8cd5b45617a921bb0a..202e8b89caf2deda6c63bba11a30f32c91ed908c 100644 (file)
@@ -174,7 +174,7 @@ static int iio_sysfs_trigger_probe(int id)
        return 0;
 
 out2:
-       iio_trigger_put(t->trig);
+       iio_trigger_free(t->trig);
 free_t:
        kfree(t);
 out1:
index 1de81f53f34ab4aeaa4dc5208ccbedff31d3f174..6bb6d37cc7d13a36b795d4a221251e73b520429c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/regmap.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/pm_runtime.h>
 
 #define ISL29028_CONV_TIME_MS                  100
 
@@ -60,6 +61,8 @@
 
 #define ISL29028_NUM_REGS                      (ISL29028_REG_TEST2_MODE + 1)
 
+#define ISL29028_POWER_OFF_DELAY_MS            2000
+
 enum isl29028_als_ir_mode {
        ISL29028_MODE_NONE = 0,
        ISL29028_MODE_ALS,
@@ -67,66 +70,94 @@ enum isl29028_als_ir_mode {
 };
 
 struct isl29028_chip {
-       struct mutex            lock;
-       struct regmap           *regmap;
-
-       unsigned int            prox_sampling;
-       bool                    enable_prox;
-
-       int                     lux_scale;
+       struct mutex                    lock;
+       struct regmap                   *regmap;
+       unsigned int                    prox_sampling;
+       bool                            enable_prox;
+       int                             lux_scale;
        enum isl29028_als_ir_mode       als_ir_mode;
 };
 
 static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
                                        unsigned int sampling)
 {
+       struct device *dev = regmap_get_device(chip->regmap);
        static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0};
-       int sel;
        unsigned int period = DIV_ROUND_UP(1000, sampling);
+       int sel, ret;
 
        for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) {
                if (period >= prox_period[sel])
                        break;
        }
-       return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                 ISL29028_CONF_PROX_SLP_MASK,
-                                 sel << ISL29028_CONF_PROX_SLP_SH);
+
+       ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+                                ISL29028_CONF_PROX_SLP_MASK,
+                                sel << ISL29028_CONF_PROX_SLP_SH);
+
+       if (ret < 0) {
+               dev_err(dev, "%s(): Error %d setting the proximity sampling\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       chip->prox_sampling = sampling;
+
+       return ret;
 }
 
-static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
+static int isl29028_enable_proximity(struct isl29028_chip *chip)
 {
        int ret;
-       int val = 0;
 
-       if (enable)
-               val = ISL29028_CONF_PROX_EN;
+       ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
+       if (ret < 0)
+               return ret;
+
        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                ISL29028_CONF_PROX_EN_MASK, val);
+                                ISL29028_CONF_PROX_EN_MASK,
+                                ISL29028_CONF_PROX_EN);
        if (ret < 0)
                return ret;
 
        /* Wait for conversion to be complete for first sample */
        mdelay(DIV_ROUND_UP(1000, chip->prox_sampling));
+
        return 0;
 }
 
 static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
 {
+       struct device *dev = regmap_get_device(chip->regmap);
        int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
                                        ISL29028_CONF_ALS_RANGE_LOW_LUX;
+       int ret;
 
-       return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-               ISL29028_CONF_ALS_RANGE_MASK, val);
+       ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+                                ISL29028_CONF_ALS_RANGE_MASK, val);
+       if (ret < 0) {
+               dev_err(dev, "%s(): Error %d setting the ALS scale\n", __func__,
+                       ret);
+               return ret;
+       }
+
+       chip->lux_scale = lux_scale;
+
+       return ret;
 }
 
 static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
                                    enum isl29028_als_ir_mode mode)
 {
-       int ret = 0;
+       int ret;
 
        if (chip->als_ir_mode == mode)
                return 0;
 
+       ret = isl29028_set_als_scale(chip, chip->lux_scale);
+       if (ret < 0)
+               return ret;
+
        switch (mode) {
        case ISL29028_MODE_ALS:
                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
@@ -139,16 +170,15 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
                                         ISL29028_CONF_ALS_RANGE_MASK,
                                         ISL29028_CONF_ALS_RANGE_HIGH_LUX);
                break;
-
        case ISL29028_MODE_IR:
                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
                                         ISL29028_CONF_ALS_IR_MODE_MASK,
                                         ISL29028_CONF_ALS_IR_MODE_IR);
                break;
-
        case ISL29028_MODE_NONE:
                return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                       ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS);
+                                         ISL29028_CONF_ALS_EN_MASK,
+                                         ISL29028_CONF_ALS_DIS);
        }
 
        if (ret < 0)
@@ -179,18 +209,21 @@ static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir)
        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb);
        if (ret < 0) {
                dev_err(dev,
-                       "Error in reading register ALSIR_L err %d\n", ret);
+                       "%s(): Error %d reading register ALSIR_L\n",
+                       __func__, ret);
                return ret;
        }
 
        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb);
        if (ret < 0) {
                dev_err(dev,
-                       "Error in reading register ALSIR_U err %d\n", ret);
+                       "%s(): Error %d reading register ALSIR_U\n",
+                       __func__, ret);
                return ret;
        }
 
        *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
+
        return 0;
 }
 
@@ -200,27 +233,24 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox)
        unsigned int data;
        int ret;
 
+       if (!chip->enable_prox) {
+               ret = isl29028_enable_proximity(chip);
+               if (ret < 0)
+                       return ret;
+
+               chip->enable_prox = true;
+       }
+
        ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
        if (ret < 0) {
-               dev_err(dev, "Error in reading register %d, error %d\n",
-                       ISL29028_REG_PROX_DATA, ret);
+               dev_err(dev, "%s(): Error %d reading register PROX_DATA\n",
+                       __func__, ret);
                return ret;
        }
-       *prox = data;
-       return 0;
-}
 
-static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data)
-{
-       int ret;
+       *prox = data;
 
-       if (!chip->enable_prox) {
-               ret = isl29028_enable_proximity(chip, true);
-               if (ret < 0)
-                       return ret;
-               chip->enable_prox = true;
-       }
-       return isl29028_read_proxim(chip, prox_data);
+       return 0;
 }
 
 static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
@@ -231,7 +261,8 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
 
        ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
        if (ret < 0) {
-               dev_err(dev, "Error in enabling ALS mode err %d\n", ret);
+               dev_err(dev, "%s(): Error %d enabling ALS mode\n", __func__,
+                       ret);
                return ret;
        }
 
@@ -250,6 +281,7 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
                als_ir_data = (als_ir_data * 49) / 100;
 
        *als_data = als_ir_data;
+
        return 0;
 }
 
@@ -260,12 +292,31 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
 
        ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
        if (ret < 0) {
-               dev_err(dev, "Error in enabling IR mode err %d\n", ret);
+               dev_err(dev, "%s(): Error %d enabling IR mode\n", __func__,
+                       ret);
                return ret;
        }
+
        return isl29028_read_als_ir(chip, ir_data);
 }
 
+static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on)
+{
+       struct device *dev = regmap_get_device(chip->regmap);
+       int ret;
+
+       if (on) {
+               ret = pm_runtime_get_sync(dev);
+               if (ret < 0)
+                       pm_runtime_put_noidle(dev);
+       } else {
+               pm_runtime_mark_last_busy(dev);
+               ret = pm_runtime_put_autosuspend(dev);
+       }
+
+       return ret;
+}
+
 /* Channel IO */
 static int isl29028_write_raw(struct iio_dev *indio_dev,
                              struct iio_chan_spec const *chan,
@@ -273,58 +324,65 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
 {
        struct isl29028_chip *chip = iio_priv(indio_dev);
        struct device *dev = regmap_get_device(chip->regmap);
-       int ret = -EINVAL;
+       int ret;
+
+       ret = isl29028_set_pm_runtime_busy(chip, true);
+       if (ret < 0)
+               return ret;
 
        mutex_lock(&chip->lock);
+
+       ret = -EINVAL;
        switch (chan->type) {
        case IIO_PROXIMITY:
                if (mask != IIO_CHAN_INFO_SAMP_FREQ) {
                        dev_err(dev,
-                               "proximity: mask value 0x%08lx not supported\n",
-                               mask);
+                               "%s(): proximity: Mask value 0x%08lx is not supported\n",
+                               __func__, mask);
                        break;
                }
+
                if (val < 1 || val > 100) {
                        dev_err(dev,
-                               "Samp_freq %d is not in range[1:100]\n", val);
+                               "%s(): proximity: Sampling frequency %d is not in the range [1:100]\n",
+                               __func__, val);
                        break;
                }
+
                ret = isl29028_set_proxim_sampling(chip, val);
-               if (ret < 0) {
-                       dev_err(dev,
-                               "Setting proximity samp_freq fail, err %d\n",
-                               ret);
-                       break;
-               }
-               chip->prox_sampling = val;
                break;
-
        case IIO_LIGHT:
                if (mask != IIO_CHAN_INFO_SCALE) {
                        dev_err(dev,
-                               "light: mask value 0x%08lx not supported\n",
-                               mask);
+                               "%s(): light: Mask value 0x%08lx is not supported\n",
+                               __func__, mask);
                        break;
                }
-               if ((val != 125) && (val != 2000)) {
+
+               if (val != 125 && val != 2000) {
                        dev_err(dev,
-                               "lux scale %d is invalid [125, 2000]\n", val);
+                               "%s(): light: Lux scale %d is not in the set {125, 2000}\n",
+                               __func__, val);
                        break;
                }
+
                ret = isl29028_set_als_scale(chip, val);
-               if (ret < 0) {
-                       dev_err(dev,
-                               "Setting lux scale fail with error %d\n", ret);
-                       break;
-               }
-               chip->lux_scale = val;
                break;
-
        default:
-               dev_err(dev, "Unsupported channel type\n");
+               dev_err(dev, "%s(): Unsupported channel type %x\n",
+                       __func__, chan->type);
                break;
        }
+
        mutex_unlock(&chip->lock);
+
+       if (ret < 0)
+               return ret;
+
+       ret = isl29028_set_pm_runtime_busy(chip, false);
+       if (ret < 0)
+               return ret;
+
        return ret;
 }
 
@@ -334,9 +392,15 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
 {
        struct isl29028_chip *chip = iio_priv(indio_dev);
        struct device *dev = regmap_get_device(chip->regmap);
-       int ret = -EINVAL;
+       int ret, pm_ret;
+
+       ret = isl29028_set_pm_runtime_busy(chip, true);
+       if (ret < 0)
+               return ret;
 
        mutex_lock(&chip->lock);
+
+       ret = -EINVAL;
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
        case IIO_CHAN_INFO_PROCESSED:
@@ -348,35 +412,50 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
                        ret = isl29028_ir_get(chip, val);
                        break;
                case IIO_PROXIMITY:
-                       ret = isl29028_proxim_get(chip, val);
+                       ret = isl29028_read_proxim(chip, val);
                        break;
                default:
                        break;
                }
+
                if (ret < 0)
                        break;
+
                ret = IIO_VAL_INT;
                break;
-
        case IIO_CHAN_INFO_SAMP_FREQ:
                if (chan->type != IIO_PROXIMITY)
                        break;
+
                *val = chip->prox_sampling;
                ret = IIO_VAL_INT;
                break;
-
        case IIO_CHAN_INFO_SCALE:
                if (chan->type != IIO_LIGHT)
                        break;
                *val = chip->lux_scale;
                ret = IIO_VAL_INT;
                break;
-
        default:
-               dev_err(dev, "mask value 0x%08lx not supported\n", mask);
+               dev_err(dev, "%s(): mask value 0x%08lx is not supported\n",
+                       __func__, mask);
                break;
        }
+
        mutex_unlock(&chip->lock);
+
+       if (ret < 0)
+               return ret;
+
+       /**
+        * Preserve the ret variable if the call to
+        * isl29028_set_pm_runtime_busy() is successful so the reading
+        * (if applicable) is returned to user space.
+        */
+       pm_ret = isl29028_set_pm_runtime_busy(chip, false);
+       if (pm_ret < 0)
+               return pm_ret;
+
        return ret;
 }
 
@@ -384,7 +463,6 @@ static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
                                "1 3 5 10 13 20 83 100");
 static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000");
 
-#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
 static struct attribute *isl29028_attributes[] = {
        ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available),
@@ -418,27 +496,19 @@ static const struct iio_info isl29028_info = {
        .write_raw = isl29028_write_raw,
 };
 
-static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip)
+static int isl29028_clear_configure_reg(struct isl29028_chip *chip)
 {
        struct device *dev = regmap_get_device(chip->regmap);
        int ret;
 
        ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
-       if (ret < 0) {
-               dev_err(dev, "%s(): write to reg %d failed, err = %d\n",
-                       __func__, ISL29028_REG_CONFIGURE, ret);
-               return ret;
-       }
+       if (ret < 0)
+               dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n",
+                       __func__, ret);
 
-       ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
-       if (ret < 0) {
-               dev_err(dev, "setting the proximity, err = %d\n", ret);
-               return ret;
-       }
+       chip->als_ir_mode = ISL29028_MODE_NONE;
+       chip->enable_prox = false;
 
-       ret = isl29028_set_als_scale(chip, chip->lux_scale);
-       if (ret < 0)
-               dev_err(dev, "setting als scale failed, err = %d\n", ret);
        return ret;
 }
 
@@ -472,10 +542,8 @@ static int isl29028_probe(struct i2c_client *client,
        int ret;
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
-       if (!indio_dev) {
-               dev_err(&client->dev, "iio allocation fails\n");
+       if (!indio_dev)
                return -ENOMEM;
-       }
 
        chip = iio_priv(indio_dev);
 
@@ -485,36 +553,34 @@ static int isl29028_probe(struct i2c_client *client,
        chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config);
        if (IS_ERR(chip->regmap)) {
                ret = PTR_ERR(chip->regmap);
-               dev_err(&client->dev, "regmap initialization failed: %d\n",
-                       ret);
+               dev_err(&client->dev, "%s: Error %d initializing regmap\n",
+                       __func__, ret);
                return ret;
        }
 
        chip->enable_prox  = false;
        chip->prox_sampling = 20;
        chip->lux_scale = 2000;
-       chip->als_ir_mode = ISL29028_MODE_NONE;
 
        ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
        if (ret < 0) {
                dev_err(&client->dev,
-                       "%s(): write to reg %d failed, err = %d\n", __func__,
-                       ISL29028_REG_TEST1_MODE, ret);
+                       "%s(): Error %d writing to TEST1_MODE register\n",
+                       __func__, ret);
                return ret;
        }
+
        ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
        if (ret < 0) {
                dev_err(&client->dev,
-                       "%s(): write to reg %d failed, err = %d\n", __func__,
-                       ISL29028_REG_TEST2_MODE, ret);
+                       "%s(): Error %d writing to TEST2_MODE register\n",
+                       __func__, ret);
                return ret;
        }
 
-       ret = isl29028_chip_init_and_power_on(chip);
-       if (ret < 0) {
-               dev_err(&client->dev, "chip initialization failed: %d\n", ret);
+       ret = isl29028_clear_configure_reg(chip);
+       if (ret < 0)
                return ret;
-       }
 
        indio_dev->info = &isl29028_info;
        indio_dev->channels = isl29028_channels;
@@ -522,16 +588,67 @@ static int isl29028_probe(struct i2c_client *client,
        indio_dev->name = id->name;
        indio_dev->dev.parent = &client->dev;
        indio_dev->modes = INDIO_DIRECT_MODE;
+
+       pm_runtime_enable(&client->dev);
+       pm_runtime_set_autosuspend_delay(&client->dev,
+                                        ISL29028_POWER_OFF_DELAY_MS);
+       pm_runtime_use_autosuspend(&client->dev);
+
        ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
        if (ret < 0) {
                dev_err(&client->dev,
-                       "iio registration fails with error %d\n",
-                       ret);
+                       "%s(): iio registration failed with error %d\n",
+                       __func__, ret);
                return ret;
        }
+
        return 0;
 }
 
+static int isl29028_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct isl29028_chip *chip = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_put_noidle(&client->dev);
+
+       return isl29028_clear_configure_reg(chip);
+}
+
+static int __maybe_unused isl29028_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct isl29028_chip *chip = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = isl29028_clear_configure_reg(chip);
+
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static int __maybe_unused isl29028_resume(struct device *dev)
+{
+       /**
+        * The specific component (ALS/IR or proximity) will enable itself as
+        * needed the next time that the user requests a reading. This is done
+        * above in isl29028_set_als_ir_mode() and isl29028_enable_proximity().
+        */
+       return 0;
+}
+
+static const struct dev_pm_ops isl29028_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(isl29028_suspend, isl29028_resume)
+       SET_RUNTIME_PM_OPS(isl29028_suspend, isl29028_resume, NULL)
+};
+
 static const struct i2c_device_id isl29028_id[] = {
        {"isl29028", 0},
        {}
@@ -548,9 +665,11 @@ MODULE_DEVICE_TABLE(of, isl29028_of_match);
 static struct i2c_driver isl29028_driver = {
        .driver  = {
                .name = "isl29028",
+               .pm = &isl29028_pm_ops,
                .of_match_table = isl29028_of_match,
        },
        .probe   = isl29028_probe,
+       .remove  = isl29028_remove,
        .id_table = isl29028_id,
 };
 
index 9658f2097c957a7df333d568f8f69f6136dee9d2..4e0b4eedb53d52c96ae9f8765235ed5fb033a4d8 100644 (file)
@@ -260,7 +260,7 @@ out_free_irq:
 out1:
        iio_trigger_unregister(st->trig);
 out:
-       iio_trigger_put(st->trig);
+       iio_trigger_free(st->trig);
        return ret;
 }
 
@@ -273,7 +273,7 @@ static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
                peripheral_free(st->t->pin);
        free_irq(st->irq, st);
        iio_trigger_unregister(st->trig);
-       iio_trigger_put(st->trig);
+       iio_trigger_free(st->trig);
 
        return 0;
 }