Merge tag 'iio-for-5.5a-take3' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Oct 2019 08:59:05 +0000 (10:59 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Oct 2019 08:59:05 +0000 (10:59 +0200)
Jonathan writes:

First set of IIO new device support, cleanups and features for the 5.5 cycle

Third version with the adis rework set dropped as better to just have
a fresh version of that at some future date.

The usual mixed backs of new device support being added to drivers,
long term reworks continuing and little per driver cleanups and
features.

Also a few trivial counter subsystem tidy ups on behalf of William.

Core new feature
* Device label support.  A long requested feature no one got around to
  implementing before.  Allows DT based provision of a 'label' that
  identifies a device uniquely within a system.  This differs from existing
  'name' which is meant to be the part number.
New device support
* ingenic-adc
  - Support for the JZ4770 SoC ADC including bindings.
* inv_mpu6050
  - Add support for magnetometer in MPU925x parts.
    Fiddly to do as this is actually a separate device sitting inside the
    package, but with the master device being able to schedule reads etc.
    Will only run if the auxiliary bus is not in use for any other devices.

Features
* ad7192
  - Userspace calibration controls to do zero and full scale.
* st_lsm6dsx
  - Enable latched interrupts by default for sensors events with related clear.
  - Motion events and related wakeup source.  This needed quite a bit of
    refactoring as well.

Cleanups and minor features
* ad7192
  - sysfs ABI docs
* ad7949
  - Remove code to readback configuration word as driver never actually enabled
    it.
  - Fix incorrect xfer length.  Not actually known to cause problems other
    than wasted bus usage.
* adis16080
  - Replace core mlock usage with local lock with more appropriate scope.
* adis16130
  - Remove pointless mlock usage.
* adis16240
  - Remove include of gpio.h as no gpio usage.
* atlas-ph-sensor
  - Improve logical ordering of buffer predisable / postenable functions.
    This is part of a longer term rework Alexandru is driving towards.
* bh1750
  - Fix up a static compiler warning and make the code more readable.
  - yaml conversion of binding + MAINTAINERS entry.
* bmp280
  - Drop a stray newline.
* cm36651
  - Drop a redundant assignment
* itg3200
  - Alignment cleanup.
* max31856
  - Add missing of_node and parent references, useful to identify the device.
* sc27xx_adc
  - Use devm_hwspin_lock_request_specific rather than local rolled version.
* stm32-lptimer counter
  - kernel-doc warning.
* stm32-timer counter
  - kernel-doc warning.
  - Alignment cleanup.
* sx9500
  - Improve logical ordering of buffer predisable / postenable functions.
    This is part of a longer term rework Alexandru is driving towards.
* tcs3414
  - Improve logical ordering of buffer predisable / postenable functions.
    This is part of a longer term rework Alexandru is driving towards.

* tag 'iio-for-5.5a-take3' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (41 commits)
  iio: pressure: bmp280: remove stray newline
  iio: adc: sc27xx: Use devm_hwspin_lock_request_specific() to simplify code
  iio: chemical: atlas-ph-sensor: fix iio_triggered_buffer_predisable() position
  iio: gyro: clean up indentation issue
  counter: stm32: clean up indentation issue
  iio: proximity: sx9500: fix iio_triggered_buffer_{predisable,postenable} positions
  iio: core: Add optional symbolic label to device attributes
  dt-binding: iio: Add optional label property
  iio: gyro: adis16080: replace mlock with own lock
  counter: stm32-lptimer-cnt: fix a kernel-doc warning
  counter: stm32-timer-cnt: fix a kernel-doc warning
  iio: gyro: adis16130: remove mlock usage
  MAINTAINERS: add entry for ROHM BH1750 driver
  dt-bindings: iio: light: bh1750: convert bindings to yaml
  iio: imu: st_lsm6dsx: add motion report function and call from interrupt
  iio: imu: st_lsm6dsx: always enter interrupt thread
  iio: imu: st_lsm6dsx: add wakeup-source option
  iio: imu: st_lsm6dsx: add motion events
  iio: imu: st_lsm6dsx: move interrupt thread to core
  iio: imu: inv_mpu6050: add fifo support for magnetometer data
  ...

41 files changed:
Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt
Documentation/devicetree/bindings/iio/iio-bindings.txt
Documentation/devicetree/bindings/iio/light/bh1750.txt [deleted file]
Documentation/devicetree/bindings/iio/light/bh1750.yaml [new file with mode: 0644]
MAINTAINERS
drivers/counter/stm32-lptimer-cnt.c
drivers/counter/stm32-timer-cnt.c
drivers/iio/adc/ad7949.c
drivers/iio/adc/ad_sigma_delta.c
drivers/iio/adc/ingenic-adc.c
drivers/iio/adc/sc27xx_adc.c
drivers/iio/chemical/atlas-ph-sensor.c
drivers/iio/gyro/adis16080.c
drivers/iio/gyro/adis16130.c
drivers/iio/gyro/itg3200_core.c
drivers/iio/imu/inv_mpu6050/Makefile
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c [new file with mode: 0644]
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h [new file with mode: 0644]
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c [new file with mode: 0644]
drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h [new file with mode: 0644]
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
drivers/iio/industrialio-core.c
drivers/iio/light/bh1750.c
drivers/iio/light/cm36651.c
drivers/iio/light/tcs3414.c
drivers/iio/pressure/bmp280-core.c
drivers/iio/proximity/sx9500.c
drivers/iio/temperature/max31856.c
drivers/staging/iio/accel/adis16240.c
drivers/staging/iio/adc/ad7192.c
include/dt-bindings/iio/adc/ingenic,adc.h
include/linux/iio/adc/ad_sigma_delta.h
include/linux/iio/iio.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192
new file mode 100644 (file)
index 0000000..7627d3b
--- /dev/null
@@ -0,0 +1,39 @@
+What:          /sys/bus/iio/devices/iio:deviceX/ac_excitation_en
+KernelVersion:
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading gives the state of AC excitation.
+               Writing '1' enables AC excitation.
+
+What:          /sys/bus/iio/devices/iio:deviceX/bridge_switch_en
+KernelVersion:
+Contact:       linux-iio@vger.kernel.org
+Description:
+               This bridge switch is used to disconnect it when there is a
+               need to minimize the system current consumption.
+               Reading gives the state of the bridge switch.
+               Writing '1' enables the bridge switch.
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration
+KernelVersion:
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Initiates the system calibration procedure. This is done on a
+               single channel at a time. Write '1' to start the calibration.
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available
+KernelVersion:
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns a list with the possible calibration modes.
+               There are two available options:
+               "zero_scale" - calibrate to zero scale
+               "full_scale" - calibrate to full scale
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode
+KernelVersion:
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Sets up the calibration mode used in the system calibration
+               procedure. Reading returns the current calibration mode.
+               Writing sets the system calibration mode.
index f01159f20d87c33a120b784695205a5b200fa8b7..cd9048cf9dcf9276d0f0d756a10467e27e16cdca 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
 - compatible: Should be one of:
   * ingenic,jz4725b-adc
   * ingenic,jz4740-adc
+  * ingenic,jz4770-adc
 - reg: ADC controller registers location and length.
 - clocks: phandle to the SoC's ADC clock.
 - clock-names: Must be set to "adc".
index 68d6f8ce063b87ca41496d387f58916916e004ae..af33267727f4e121839c94e244b9ef26f19310a2 100644 (file)
@@ -18,12 +18,17 @@ Required properties:
                   with a single IIO output and 1 for nodes with multiple
                   IIO outputs.
 
+Optional properties:
+label:            A symbolic name for the device.
+
+
 Example for a simple configuration with no trigger:
 
        adc: voltage-sensor@35 {
                compatible = "maxim,max1139";
                reg = <0x35>;
                #io-channel-cells = <1>;
+               label = "voltage_feedback_group1";
        };
 
 Example for a configuration with trigger:
diff --git a/Documentation/devicetree/bindings/iio/light/bh1750.txt b/Documentation/devicetree/bindings/iio/light/bh1750.txt
deleted file mode 100644 (file)
index 1e76857..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-ROHM BH1750 - ALS, Ambient light sensor
-
-Required properties:
-
-- compatible: Must be one of:
-    "rohm,bh1710"
-    "rohm,bh1715"
-    "rohm,bh1721"
-    "rohm,bh1750"
-    "rohm,bh1751"
-- reg: the I2C address of the sensor
-
-Example:
-
-light-sensor@23 {
-       compatible = "rohm,bh1750";
-       reg = <0x23>;
-};
diff --git a/Documentation/devicetree/bindings/iio/light/bh1750.yaml b/Documentation/devicetree/bindings/iio/light/bh1750.yaml
new file mode 100644 (file)
index 0000000..1cc60d7
--- /dev/null
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/light/bh1750.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BH1750 ambient light sensor
+
+maintainers:
+  - Tomasz Duszynski <tduszyns@gmail.com>
+
+description: |
+  Ambient light sensor with an i2c interface.
+
+properties:
+  compatible:
+    enum:
+      - rohm,bh1710
+      - rohm,bh1715
+      - rohm,bh1721
+      - rohm,bh1750
+      - rohm,bh1751
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      light-sensor@23 {
+        compatible = "rohm,bh1750";
+        reg = <0x23>;
+      };
+    };
+
+...
index 8f5b4847a9e8ded5d963f7ad2f59c040c4dd46b4..ef4cd6b4aa8573b477adf22c3f8ed707fefc2cf4 100644 (file)
@@ -13951,6 +13951,12 @@ L:     linux-serial@vger.kernel.org
 S:     Odd Fixes
 F:     drivers/tty/serial/rp2.*
 
+ROHM BH1750 AMBIENT LIGHT SENSOR DRIVER
+M:     Tomasz Duszynski <tduszyns@gmail.com>
+S:     Maintained
+F:     drivers/iio/light/bh1750.c
+F:     Documentation/devicetree/bindings/iio/light/bh1750.yaml
+
 ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
 M:     Marek Vasut <marek.vasut+renesas@gmail.com>
 L:     linux-kernel@vger.kernel.org
index bbc930a5962c64fa2d7dfd5f5c383b34731e7cb4..28b63645c41187014e803da13c963ee88aef6966 100644 (file)
@@ -347,7 +347,7 @@ static const struct iio_chan_spec stm32_lptim_cnt_channels = {
 };
 
 /**
- * stm32_lptim_cnt_function - enumerates stm32 LPTimer counter & encoder modes
+ * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
  * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
  * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
  */
index 644ba18a72ad5b06f9429342f05c243f19def08a..b61135b63ee80bd7afe689494cfadb0402f10fbb 100644 (file)
@@ -28,7 +28,7 @@ struct stm32_timer_cnt {
 };
 
 /**
- * stm32_count_function - enumerates stm32 timer counter encoder modes
+ * enum stm32_count_function - enumerates stm32 timer counter encoder modes
  * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
  * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
  * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
@@ -219,8 +219,8 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
 
        if (enable) {
                regmap_read(priv->regmap, TIM_CR1, &cr1);
-                       if (!(cr1 & TIM_CR1_CEN))
-                               clk_enable(priv->clk);
+               if (!(cr1 & TIM_CR1_CEN))
+                       clk_enable(priv->clk);
 
                regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
                                   TIM_CR1_CEN);
index ac0ffff6c5ae1bd5d93ae3042e99fbe5ecd3430a..5c2b3446fa4afe8e1719c0842c1b89359433527a 100644 (file)
@@ -54,38 +54,20 @@ struct ad7949_adc_chip {
        u8 resolution;
        u16 cfg;
        unsigned int current_channel;
-       u32 buffer ____cacheline_aligned;
+       u16 buffer ____cacheline_aligned;
 };
 
-static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc)
-{
-       if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK))
-               return true;
-
-       return false;
-}
-
-static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc)
-{
-       int ret = ad7949_adc->resolution;
-
-       if (ad7949_spi_cfg_is_read_back(ad7949_adc))
-               ret += AD7949_CFG_REG_SIZE_BITS;
-
-       return ret;
-}
-
 static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
                                u16 mask)
 {
        int ret;
-       int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+       int bits_per_word = ad7949_adc->resolution;
        int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS;
        struct spi_message msg;
        struct spi_transfer tx[] = {
                {
                        .tx_buf = &ad7949_adc->buffer,
-                       .len = 4,
+                       .len = 2,
                        .bits_per_word = bits_per_word,
                },
        };
@@ -107,13 +89,13 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
                                   unsigned int channel)
 {
        int ret;
-       int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+       int bits_per_word = ad7949_adc->resolution;
        int mask = GENMASK(ad7949_adc->resolution, 0);
        struct spi_message msg;
        struct spi_transfer tx[] = {
                {
                        .rx_buf = &ad7949_adc->buffer,
-                       .len = 4,
+                       .len = 2,
                        .bits_per_word = bits_per_word,
                },
        };
@@ -138,10 +120,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
 
        ad7949_adc->current_channel = channel;
 
-       if (ad7949_spi_cfg_is_read_back(ad7949_adc))
-               *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask;
-       else
-               *val = ad7949_adc->buffer & mask;
+       *val = ad7949_adc->buffer & mask;
 
        return 0;
 }
index 2640b75fb774cae659bcdd718967504f25e3a83d..8ba90486c7870ba62f96fac2d0e8551cf5d65d71 100644 (file)
@@ -205,7 +205,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
 }
 EXPORT_SYMBOL_GPL(ad_sd_reset);
 
-static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
        unsigned int mode, unsigned int channel)
 {
        int ret;
@@ -242,6 +242,7 @@ out:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(ad_sd_calibrate);
 
 /**
  * ad_sd_calibrate_all() - Performs channel calibration
index e234970b7150fb743e33f56e5cb28662b3ecf92c..7a53c2f8d4387ecf8c5e2be64003210523f11dca 100644 (file)
 #define JZ_ADC_REG_ADSDAT              0x20
 #define JZ_ADC_REG_ADCLK               0x28
 
+#define JZ_ADC_REG_ENABLE_PD           BIT(7)
+#define JZ_ADC_REG_CFG_AUX_MD          (BIT(0) | BIT(1))
 #define JZ_ADC_REG_CFG_BAT_MD          BIT(4)
 #define JZ_ADC_REG_ADCLK_CLKDIV_LSB    0
-#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB        16
+#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB   16
+#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB    8
+#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB      16
 
 #define JZ_ADC_AUX_VREF                                3300
 #define JZ_ADC_AUX_VREF_BITS                   12
@@ -37,6 +41,8 @@
 #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS     10
 #define JZ4740_ADC_BATTERY_HIGH_VREF           (7500 * 0.986)
 #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS      12
+#define JZ4770_ADC_BATTERY_VREF                        6600
+#define JZ4770_ADC_BATTERY_VREF_BITS           12
 
 struct ingenic_adc;
 
@@ -47,6 +53,8 @@ struct ingenic_adc_soc_data {
        size_t battery_raw_avail_size;
        const int *battery_scale_avail;
        size_t battery_scale_avail_size;
+       unsigned int battery_vref_mode: 1;
+       unsigned int has_aux2: 1;
        int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
 };
 
@@ -54,6 +62,7 @@ struct ingenic_adc {
        void __iomem *base;
        struct clk *clk;
        struct mutex lock;
+       struct mutex aux_lock;
        const struct ingenic_adc_soc_data *soc_data;
        bool low_vref_mode;
 };
@@ -120,6 +129,8 @@ static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
        case IIO_CHAN_INFO_SCALE:
                switch (chan->channel) {
                case INGENIC_ADC_BATTERY:
+                       if (!adc->soc_data->battery_vref_mode)
+                               return -EINVAL;
                        if (val > JZ_ADC_BATTERY_LOW_VREF) {
                                ingenic_adc_set_config(adc,
                                                       JZ_ADC_REG_CFG_BAT_MD,
@@ -158,6 +169,14 @@ static const int jz4740_adc_battery_scale_avail[] = {
        JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
 };
 
+static const int jz4770_adc_battery_raw_avail[] = {
+       0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
+};
+
+static const int jz4770_adc_battery_scale_avail[] = {
+       JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
+};
+
 static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
 {
        struct clk *parent_clk;
@@ -187,7 +206,45 @@ static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
        /* We also need a divider that produces a 10us clock. */
        div_10us = DIV_ROUND_UP(rate, 100000);
 
-       writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+       writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+              (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
+              adc->base + JZ_ADC_REG_ADCLK);
+
+       return 0;
+}
+
+static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
+{
+       struct clk *parent_clk;
+       unsigned long parent_rate, rate;
+       unsigned int div_main, div_ms, div_10us;
+
+       parent_clk = clk_get_parent(adc->clk);
+       if (!parent_clk) {
+               dev_err(dev, "ADC clock has no parent\n");
+               return -ENODEV;
+       }
+       parent_rate = clk_get_rate(parent_clk);
+
+       /*
+        * The JZ4770 ADC works at 20 kHz to 200 kHz.
+        * We pick the highest rate possible.
+        */
+       div_main = DIV_ROUND_UP(parent_rate, 200000);
+       div_main = clamp(div_main, 1u, 256u);
+       rate = parent_rate / div_main;
+       if (rate < 20000 || rate > 200000) {
+               dev_err(dev, "No valid divider for ADC main clock\n");
+               return -EINVAL;
+       }
+
+       /* We also need a divider that produces a 10us clock. */
+       div_10us = DIV_ROUND_UP(rate, 10000);
+       /* And another, which produces a 1ms clock. */
+       div_ms = DIV_ROUND_UP(rate, 1000);
+
+       writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) |
+              ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) |
               (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
               adc->base + JZ_ADC_REG_ADCLK);
 
@@ -201,6 +258,8 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
        .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
        .battery_scale_avail = jz4725b_adc_battery_scale_avail,
        .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
+       .battery_vref_mode = true,
+       .has_aux2 = false,
        .init_clk_div = jz4725b_adc_init_clk_div,
 };
 
@@ -211,9 +270,23 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
        .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
        .battery_scale_avail = jz4740_adc_battery_scale_avail,
        .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
+       .battery_vref_mode = true,
+       .has_aux2 = false,
        .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
 };
 
+static const struct ingenic_adc_soc_data jz4770_adc_soc_data = {
+       .battery_high_vref = JZ4770_ADC_BATTERY_VREF,
+       .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
+       .battery_raw_avail = jz4770_adc_battery_raw_avail,
+       .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
+       .battery_scale_avail = jz4770_adc_battery_scale_avail,
+       .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail),
+       .battery_vref_mode = false,
+       .has_aux2 = true,
+       .init_clk_div = jz4770_adc_init_clk_div,
+};
+
 static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
                                  struct iio_chan_spec const *chan,
                                  const int **vals,
@@ -239,6 +312,42 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
        };
 }
 
+static int ingenic_adc_read_chan_info_raw(struct ingenic_adc *adc,
+                                         struct iio_chan_spec const *chan,
+                                         int *val)
+{
+       int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY);
+
+       /* We cannot sample AUX/AUX2 in parallel. */
+       mutex_lock(&adc->aux_lock);
+       if (adc->soc_data->has_aux2 && engine == 0) {
+               bit = BIT(chan->channel == INGENIC_ADC_AUX2);
+               ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit);
+       }
+
+       clk_enable(adc->clk);
+       ret = ingenic_adc_capture(adc, engine);
+       if (ret)
+               goto out;
+
+       switch (chan->channel) {
+       case INGENIC_ADC_AUX:
+       case INGENIC_ADC_AUX2:
+               *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
+               break;
+       case INGENIC_ADC_BATTERY:
+               *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
+               break;
+       }
+
+       ret = IIO_VAL_INT;
+out:
+       clk_disable(adc->clk);
+       mutex_unlock(&adc->aux_lock);
+
+       return ret;
+}
+
 static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
                                struct iio_chan_spec const *chan,
                                int *val,
@@ -246,32 +355,14 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
                                long m)
 {
        struct ingenic_adc *adc = iio_priv(iio_dev);
-       int ret;
 
        switch (m) {
        case IIO_CHAN_INFO_RAW:
-               clk_enable(adc->clk);
-               ret = ingenic_adc_capture(adc, chan->channel);
-               if (ret) {
-                       clk_disable(adc->clk);
-                       return ret;
-               }
-
-               switch (chan->channel) {
-               case INGENIC_ADC_AUX:
-                       *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
-                       break;
-               case INGENIC_ADC_BATTERY:
-                       *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
-                       break;
-               }
-
-               clk_disable(adc->clk);
-
-               return IIO_VAL_INT;
+               return ingenic_adc_read_chan_info_raw(adc, chan, val);
        case IIO_CHAN_INFO_SCALE:
                switch (chan->channel) {
                case INGENIC_ADC_AUX:
+               case INGENIC_ADC_AUX2:
                        *val = JZ_ADC_AUX_VREF;
                        *val2 = JZ_ADC_AUX_VREF_BITS;
                        break;
@@ -322,6 +413,14 @@ static const struct iio_chan_spec ingenic_channels[] = {
                .indexed = 1,
                .channel = INGENIC_ADC_BATTERY,
        },
+       { /* Must always be last in the array. */
+               .extend_name = "aux2",
+               .type = IIO_VOLTAGE,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                                     BIT(IIO_CHAN_INFO_SCALE),
+               .indexed = 1,
+               .channel = INGENIC_ADC_AUX2,
+       },
 };
 
 static int ingenic_adc_probe(struct platform_device *pdev)
@@ -343,6 +442,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
 
        adc = iio_priv(iio_dev);
        mutex_init(&adc->lock);
+       mutex_init(&adc->aux_lock);
        adc->soc_data = soc_data;
 
        mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -374,6 +474,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
        /* Put hardware in a known passive state. */
        writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
        writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
+       usleep_range(2000, 3000); /* Must wait at least 2ms. */
        clk_disable(adc->clk);
 
        ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
@@ -387,6 +488,9 @@ static int ingenic_adc_probe(struct platform_device *pdev)
        iio_dev->modes = INDIO_DIRECT_MODE;
        iio_dev->channels = ingenic_channels;
        iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
+       /* Remove AUX2 from the list of supported channels. */
+       if (!adc->soc_data->has_aux2)
+               iio_dev->num_channels -= 1;
        iio_dev->info = &ingenic_adc_info;
 
        ret = devm_iio_device_register(dev, iio_dev);
@@ -400,6 +504,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
 static const struct of_device_id ingenic_adc_of_match[] = {
        { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
        { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
+       { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
        { },
 };
 MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
index a6c046575ec3aec1cf855350e3b197df38e459b6..66b387f9b36d94a48d699b2e9a940dd9eae5f441 100644 (file)
@@ -477,13 +477,6 @@ static void sc27xx_adc_disable(void *_data)
                           SC27XX_MODULE_ADC_EN, 0);
 }
 
-static void sc27xx_adc_free_hwlock(void *_data)
-{
-       struct hwspinlock *hwlock = _data;
-
-       hwspin_lock_free(hwlock);
-}
-
 static int sc27xx_adc_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -520,19 +513,12 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
+       sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
        if (!sc27xx_data->hwlock) {
                dev_err(dev, "failed to request hwspinlock\n");
                return -ENXIO;
        }
 
-       ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
-                             sc27xx_data->hwlock);
-       if (ret) {
-               dev_err(dev, "failed to add hwspinlock action\n");
-               return ret;
-       }
-
        sc27xx_data->dev = dev;
 
        ret = sc27xx_adc_enable(sc27xx_data);
index 3a20cb5d9bffc29b6869954a6bbef94ecdcd42b5..6c175eb1c7a7fb803c90b547e84673386577e8d3 100644 (file)
@@ -323,16 +323,16 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev)
        struct atlas_data *data = iio_priv(indio_dev);
        int ret;
 
-       ret = iio_triggered_buffer_predisable(indio_dev);
+       ret = atlas_set_interrupt(data, false);
        if (ret)
                return ret;
 
-       ret = atlas_set_interrupt(data, false);
+       pm_runtime_mark_last_busy(&data->client->dev);
+       ret = pm_runtime_put_autosuspend(&data->client->dev);
        if (ret)
                return ret;
 
-       pm_runtime_mark_last_busy(&data->client->dev);
-       return pm_runtime_put_autosuspend(&data->client->dev);
+       return iio_triggered_buffer_predisable(indio_dev);
 }
 
 static const struct iio_trigger_ops atlas_interrupt_trigger_ops = {
index 236220d6de029afd210d0dee3d0c7016921de748..1b84b8e112fe1df59e928a8f8ff40e2fc9bb4ffd 100644 (file)
@@ -38,10 +38,12 @@ struct adis16080_chip_info {
  * @us:                        actual spi_device to write data
  * @info:              chip specific parameters
  * @buf:               transmit or receive buffer
+ * @lock               lock to protect buffer during reads
  **/
 struct adis16080_state {
        struct spi_device               *us;
        const struct adis16080_chip_info *info;
+       struct mutex                    lock;
 
        __be16 buf ____cacheline_aligned;
 };
@@ -82,9 +84,9 @@ static int adis16080_read_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
-               mutex_lock(&indio_dev->mlock);
+               mutex_lock(&st->lock);
                ret = adis16080_read_sample(indio_dev, chan->address, val);
-               mutex_unlock(&indio_dev->mlock);
+               mutex_unlock(&st->lock);
                return ret ? ret : IIO_VAL_INT;
        case IIO_CHAN_INFO_SCALE:
                switch (chan->type) {
@@ -196,6 +198,8 @@ static int adis16080_probe(struct spi_device *spi)
        /* this is only used for removal purposes */
        spi_set_drvdata(spi, indio_dev);
 
+       mutex_init(&st->lock);
+
        /* Allocate the comms buffers */
        st->us = spi;
        st->info = &adis16080_chip_info[id->driver_data];
index de3f66f89496c43870d8611ae57501643036048c..79e63c8a2ea874347d0fe06e99a269d02e686ab3 100644 (file)
@@ -76,9 +76,7 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                /* Take the iio_dev status lock */
-               mutex_lock(&indio_dev->mlock);
                ret = adis16130_spi_read(indio_dev, chan->address, &temp);
-               mutex_unlock(&indio_dev->mlock);
                if (ret)
                        return ret;
                *val = temp;
index 998fb8d66fe3e05093c02a652d8c0c759b1a32e5..981ae22915058dfb03bb5c5148284f0eb97c928b 100644 (file)
@@ -154,7 +154,7 @@ static int itg3200_write_raw(struct iio_dev *indio_dev,
                                          t);
 
                mutex_unlock(&indio_dev->mlock);
-       return ret;
+               return ret;
 
        default:
                return -EINVAL;
index 70ffe0d13d8c5cee62921828823c7e32d502e736..c103441a906b186f9a999fe935b0cb86ac16fbd2 100644 (file)
@@ -4,10 +4,11 @@
 #
 
 obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
-inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
+inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \
+                inv_mpu_aux.o inv_mpu_magn.o
 
 obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o
-inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o
+inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o
 
 obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o
-inv-mpu6050-spi-objs := inv_mpu_spi.o
+inv-mpu6050-spi-y := inv_mpu_spi.o
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c
new file mode 100644 (file)
index 0000000..7327e57
--- /dev/null
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 TDK-InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+
+#include "inv_mpu_aux.h"
+#include "inv_mpu_iio.h"
+
+/*
+ * i2c master auxiliary bus transfer function.
+ * Requires the i2c operations to be correctly setup before.
+ */
+static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st)
+{
+       /* use 50hz frequency for xfer */
+       const unsigned int freq = 50;
+       const unsigned int period_ms = 1000 / freq;
+       uint8_t d;
+       unsigned int user_ctrl;
+       int ret;
+
+       /* set sample rate */
+       d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq);
+       ret = regmap_write(st->map, st->reg->sample_rate_div, d);
+       if (ret)
+               return ret;
+
+       /* start i2c master */
+       user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
+       ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
+       if (ret)
+               goto error_restore_rate;
+
+       /* wait for xfer: 1 period + half-period margin */
+       msleep(period_ms + period_ms / 2);
+
+       /* stop i2c master */
+       user_ctrl = st->chip_config.user_ctrl;
+       ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
+       if (ret)
+               goto error_stop_i2c;
+
+       /* restore sample rate */
+       d = st->chip_config.divider;
+       ret = regmap_write(st->map, st->reg->sample_rate_div, d);
+       if (ret)
+               goto error_restore_rate;
+
+       return 0;
+
+error_stop_i2c:
+       regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
+error_restore_rate:
+       regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider);
+       return ret;
+}
+
+/**
+ * inv_mpu_aux_init() - init i2c auxiliary bus
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int inv_mpu_aux_init(const struct inv_mpu6050_state *st)
+{
+       unsigned int val;
+       int ret;
+
+       /* configure i2c master */
+       val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ |
+                       INV_MPU6050_BIT_WAIT_FOR_ES;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val);
+       if (ret)
+               return ret;
+
+       /* configure i2c master delay */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0);
+       if (ret)
+               return ret;
+
+       val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN |
+                       INV_MPU6050_BIT_I2C_SLV1_DLY_EN |
+                       INV_MPU6050_BIT_I2C_SLV2_DLY_EN |
+                       INV_MPU6050_BIT_I2C_SLV3_DLY_EN |
+                       INV_MPU6050_BIT_DELAY_ES_SHADOW;
+       return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val);
+}
+
+/**
+ * inv_mpu_aux_read() - read register function for i2c auxiliary bus
+ * @st: driver internal state.
+ * @addr: chip i2c Address
+ * @reg: chip register address
+ * @val: buffer for storing read bytes
+ * @size: number of bytes to read
+ *
+ *  Returns 0 on success, a negative error code otherwise.
+ */
+int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
+                    uint8_t reg, uint8_t *val, size_t size)
+{
+       unsigned int status;
+       int ret;
+
+       if (size > 0x0F)
+               return -EINVAL;
+
+       /* setup i2c SLV0 control: i2c addr, register, enable + size */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0),
+                          INV_MPU6050_BIT_I2C_SLV_RNW | addr);
+       if (ret)
+               return ret;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
+       if (ret)
+               return ret;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
+                          INV_MPU6050_BIT_SLV_EN | size);
+       if (ret)
+               return ret;
+
+       /* do i2c xfer */
+       ret = inv_mpu_i2c_master_xfer(st);
+       if (ret)
+               goto error_disable_i2c;
+
+       /* disable i2c slave */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
+       if (ret)
+               goto error_disable_i2c;
+
+       /* check i2c status */
+       ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
+       if (ret)
+               return ret;
+       if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
+               return -EIO;
+
+       /* read data in registers */
+       return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
+                               val, size);
+
+error_disable_i2c:
+       regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
+       return ret;
+}
+
+/**
+ * inv_mpu_aux_write() - write register function for i2c auxiliary bus
+ * @st: driver internal state.
+ * @addr: chip i2c Address
+ * @reg: chip register address
+ * @val: 1 byte value to write
+ *
+ *  Returns 0 on success, a negative error code otherwise.
+ */
+int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
+                     uint8_t reg, uint8_t val)
+{
+       unsigned int status;
+       int ret;
+
+       /* setup i2c SLV0 control: i2c addr, register, value, enable + size */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr);
+       if (ret)
+               return ret;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
+       if (ret)
+               return ret;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val);
+       if (ret)
+               return ret;
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
+                          INV_MPU6050_BIT_SLV_EN | 1);
+       if (ret)
+               return ret;
+
+       /* do i2c xfer */
+       ret = inv_mpu_i2c_master_xfer(st);
+       if (ret)
+               goto error_disable_i2c;
+
+       /* disable i2c slave */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
+       if (ret)
+               goto error_disable_i2c;
+
+       /* check i2c status */
+       ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
+       if (ret)
+               return ret;
+       if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
+               return -EIO;
+
+       return 0;
+
+error_disable_i2c:
+       regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
+       return ret;
+}
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h
new file mode 100644 (file)
index 0000000..b669975
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 TDK-InvenSense, Inc.
+ */
+
+#ifndef INV_MPU_AUX_H_
+#define INV_MPU_AUX_H_
+
+#include "inv_mpu_iio.h"
+
+int inv_mpu_aux_init(const struct inv_mpu6050_state *st);
+
+int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
+                    uint8_t reg, uint8_t *val, size_t size);
+
+int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
+                     uint8_t reg, uint8_t val);
+
+#endif         /* INV_MPU_AUX_H_ */
index b17f060b52fc7ba39f2dc2eb3f7ea7b545beee14..354030e9bed5686849e77a584bc846f6ce8a3ca6 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include "inv_mpu_iio.h"
+#include "inv_mpu_magn.h"
 
 /*
  * this is the gyro scale translated from dynamic range plus/minus
@@ -103,6 +104,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
        .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
        .gyro_fifo_enable = false,
        .accl_fifo_enable = false,
+       .magn_fifo_enable = false,
        .accl_fs = INV_MPU6050_FS_02G,
        .user_ctrl = 0,
 };
@@ -332,6 +334,11 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
         */
        st->chip_period = NSEC_PER_MSEC;
 
+       /* magn chip init, noop if not present in the chip */
+       result = inv_mpu_magn_probe(st);
+       if (result)
+               goto error_power_off;
+
        return inv_mpu6050_set_power_itg(st, false);
 
 error_power_off:
@@ -411,6 +418,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
                ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
                                              IIO_MOD_X, val);
                break;
+       case IIO_MAGN:
+               ret = inv_mpu_magn_read(st, chan->channel2, val);
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -469,6 +479,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
                                *val2 = INV_MPU6050_TEMP_SCALE;
 
                        return IIO_VAL_INT_PLUS_MICRO;
+               case IIO_MAGN:
+                       return inv_mpu_magn_get_scale(st, chan, val, val2);
                default:
                        return -EINVAL;
                }
@@ -710,6 +722,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
        if (result)
                goto fifo_rate_fail_power_off;
 
+       /* update rate for magn, noop if not present in chip */
+       result = inv_mpu_magn_set_rate(st, fifo_rate);
+       if (result)
+               goto fifo_rate_fail_power_off;
+
 fifo_rate_fail_power_off:
        result |= inv_mpu6050_set_power_itg(st, false);
 fifo_rate_fail_unlock:
@@ -795,8 +812,14 @@ inv_get_mount_matrix(const struct iio_dev *indio_dev,
                     const struct iio_chan_spec *chan)
 {
        struct inv_mpu6050_state *data = iio_priv(indio_dev);
+       const struct iio_mount_matrix *matrix;
 
-       return &data->orientation;
+       if (chan->type == IIO_MAGN)
+               matrix = &data->magn_orient;
+       else
+               matrix = &data->orientation;
+
+       return matrix;
 }
 
 static const struct iio_chan_spec_ext_info inv_ext_info[] = {
@@ -864,6 +887,98 @@ static const unsigned long inv_mpu_scan_masks[] = {
        0,
 };
 
+#define INV_MPU9X50_MAGN_CHAN(_chan2, _bits, _index)                   \
+       {                                                               \
+               .type = IIO_MAGN,                                       \
+               .modified = 1,                                          \
+               .channel2 = _chan2,                                     \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |        \
+                                     BIT(IIO_CHAN_INFO_RAW),           \
+               .scan_index = _index,                                   \
+               .scan_type = {                                          \
+                       .sign = 's',                                    \
+                       .realbits = _bits,                              \
+                       .storagebits = 16,                              \
+                       .shift = 0,                                     \
+                       .endianness = IIO_BE,                           \
+               },                                                      \
+               .ext_info = inv_ext_info,                               \
+       }
+
+static const struct iio_chan_spec inv_mpu9250_channels[] = {
+       IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
+       /*
+        * Note that temperature should only be via polled reading only,
+        * not the final scan elements output.
+        */
+       {
+               .type = IIO_TEMP,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+                               | BIT(IIO_CHAN_INFO_OFFSET)
+                               | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = -1,
+       },
+       INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
+       INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
+       INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
+
+       INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
+       INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
+       INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+
+       /* Magnetometer resolution is 16 bits */
+       INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 16, INV_MPU9X50_SCAN_MAGN_X),
+       INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 16, INV_MPU9X50_SCAN_MAGN_Y),
+       INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z),
+};
+
+static const unsigned long inv_mpu9x50_scan_masks[] = {
+       /* 3-axis accel */
+       BIT(INV_MPU6050_SCAN_ACCL_X)
+               | BIT(INV_MPU6050_SCAN_ACCL_Y)
+               | BIT(INV_MPU6050_SCAN_ACCL_Z),
+       /* 3-axis gyro */
+       BIT(INV_MPU6050_SCAN_GYRO_X)
+               | BIT(INV_MPU6050_SCAN_GYRO_Y)
+               | BIT(INV_MPU6050_SCAN_GYRO_Z),
+       /* 3-axis magn */
+       BIT(INV_MPU9X50_SCAN_MAGN_X)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Y)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Z),
+       /* 6-axis accel + gyro */
+       BIT(INV_MPU6050_SCAN_ACCL_X)
+               | BIT(INV_MPU6050_SCAN_ACCL_Y)
+               | BIT(INV_MPU6050_SCAN_ACCL_Z)
+               | BIT(INV_MPU6050_SCAN_GYRO_X)
+               | BIT(INV_MPU6050_SCAN_GYRO_Y)
+               | BIT(INV_MPU6050_SCAN_GYRO_Z),
+       /* 6-axis accel + magn */
+       BIT(INV_MPU6050_SCAN_ACCL_X)
+               | BIT(INV_MPU6050_SCAN_ACCL_Y)
+               | BIT(INV_MPU6050_SCAN_ACCL_Z)
+               | BIT(INV_MPU9X50_SCAN_MAGN_X)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Y)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Z),
+       /* 6-axis gyro + magn */
+       BIT(INV_MPU6050_SCAN_GYRO_X)
+               | BIT(INV_MPU6050_SCAN_GYRO_Y)
+               | BIT(INV_MPU6050_SCAN_GYRO_Z)
+               | BIT(INV_MPU9X50_SCAN_MAGN_X)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Y)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Z),
+       /* 9-axis accel + gyro + magn */
+       BIT(INV_MPU6050_SCAN_ACCL_X)
+               | BIT(INV_MPU6050_SCAN_ACCL_Y)
+               | BIT(INV_MPU6050_SCAN_ACCL_Z)
+               | BIT(INV_MPU6050_SCAN_GYRO_X)
+               | BIT(INV_MPU6050_SCAN_GYRO_Y)
+               | BIT(INV_MPU6050_SCAN_GYRO_Z)
+               | BIT(INV_MPU9X50_SCAN_MAGN_X)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Y)
+               | BIT(INV_MPU9X50_SCAN_MAGN_Z),
+       0,
+};
+
 static const struct iio_chan_spec inv_icm20602_channels[] = {
        IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
        {
@@ -1145,6 +1260,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
                return result;
        }
 
+       /* fill magnetometer orientation */
+       result = inv_mpu_magn_set_orient(st);
+       if (result)
+               return result;
+
        /* power is turned on inside check chip type*/
        result = inv_check_and_setup_chip(st);
        if (result)
@@ -1156,9 +1276,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
                return result;
        }
 
-       if (inv_mpu_bus_setup)
-               inv_mpu_bus_setup(indio_dev);
-
        dev_set_drvdata(dev, indio_dev);
        indio_dev->dev.parent = dev;
        /* name will be NULL when enumerated via ACPI */
@@ -1167,14 +1284,37 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
        else
                indio_dev->name = dev_name(dev);
 
-       if (chip_type == INV_ICM20602) {
+       /* requires parent device set in indio_dev */
+       if (inv_mpu_bus_setup)
+               inv_mpu_bus_setup(indio_dev);
+
+       switch (chip_type) {
+       case INV_MPU9250:
+       case INV_MPU9255:
+               /*
+                * Use magnetometer inside the chip only if there is no i2c
+                * auxiliary device in use.
+                */
+               if (!st->magn_disabled) {
+                       indio_dev->channels = inv_mpu9250_channels;
+                       indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
+                       indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
+               } else {
+                       indio_dev->channels = inv_mpu_channels;
+                       indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+                       indio_dev->available_scan_masks = inv_mpu_scan_masks;
+               }
+               break;
+       case INV_ICM20602:
                indio_dev->channels = inv_icm20602_channels;
                indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
                indio_dev->available_scan_masks = inv_icm20602_scan_masks;
-       } else {
+               break;
+       default:
                indio_dev->channels = inv_mpu_channels;
                indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
                indio_dev->available_scan_masks = inv_mpu_scan_masks;
+               break;
        }
 
        indio_dev->info = &mpu_info;
index 4b8b5a87398c206a71db724d7f4ffcb730cb52c9..389cc8505e0e9db59ac994c3998b34dfadf960a3 100644 (file)
@@ -68,6 +68,56 @@ static const char *inv_mpu_match_acpi_device(struct device *dev,
        return dev_name(dev);
 }
 
+static bool inv_mpu_i2c_aux_bus(struct device *dev)
+{
+       struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
+
+       switch (st->chip_type) {
+       case INV_ICM20608:
+       case INV_ICM20602:
+               /* no i2c auxiliary bus on the chip */
+               return false;
+       case INV_MPU9250:
+       case INV_MPU9255:
+               if (st->magn_disabled)
+                       return true;
+               else
+                       return false;
+       default:
+               return true;
+       }
+}
+
+/*
+ * MPU9xxx magnetometer support requires to disable i2c auxiliary bus support.
+ * To ensure backward compatibility with existing setups, do not disable
+ * i2c auxiliary bus if it used.
+ * Check for i2c-gate node in devicetree and set magnetometer disabled.
+ * Only MPU6500 is supported by ACPI, no need to check.
+ */
+static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
+{
+       struct inv_mpu6050_state *st = iio_priv(indio_dev);
+       struct device *dev = indio_dev->dev.parent;
+       struct device_node *mux_node;
+
+       switch (st->chip_type) {
+       case INV_MPU9250:
+       case INV_MPU9255:
+               mux_node = of_get_child_by_name(dev->of_node, "i2c-gate");
+               if (mux_node != NULL) {
+                       st->magn_disabled = true;
+                       dev_warn(dev, "disable internal use of magnetometer\n");
+               }
+               of_node_put(mux_node);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 /**
  *  inv_mpu_probe() - probe function.
  *  @client:          i2c client.
@@ -112,17 +162,12 @@ static int inv_mpu_probe(struct i2c_client *client,
        }
 
        result = inv_mpu_core_probe(regmap, client->irq, name,
-                                   NULL, chip_type);
+                                   inv_mpu_magn_disable, chip_type);
        if (result < 0)
                return result;
 
        st = iio_priv(dev_get_drvdata(&client->dev));
-       switch (st->chip_type) {
-       case INV_ICM20608:
-       case INV_ICM20602:
-               /* no i2c auxiliary bus on the chip */
-               break;
-       default:
+       if (inv_mpu_i2c_aux_bus(&client->dev)) {
                /* declare i2c auxiliary bus */
                st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
                                         1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
@@ -137,7 +182,6 @@ static int inv_mpu_probe(struct i2c_client *client,
                result = inv_mpu_acpi_create_mux_client(client);
                if (result)
                        goto out_del_mux;
-               break;
        }
 
        return 0;
index db1c6904388b67f2e39cbccf85f1315caaa61d73..52fcf45050a537d629c73e0e0f8a27c694b4dfe6 100644 (file)
@@ -2,6 +2,10 @@
 /*
 * Copyright (C) 2012 Invensense, Inc.
 */
+
+#ifndef INV_MPU_IIO_H_
+#define INV_MPU_IIO_H_
+
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
 #include <linux/mutex.h>
@@ -82,6 +86,7 @@ enum inv_devices {
  *  @accl_fs:          accel full scale range.
  *  @accl_fifo_enable: enable accel data output
  *  @gyro_fifo_enable: enable gyro data output
+ *  @magn_fifo_enable: enable magn data output
  *  @divider:          chip sample rate divider (sample rate divider - 1)
  */
 struct inv_mpu6050_chip_config {
@@ -90,6 +95,7 @@ struct inv_mpu6050_chip_config {
        unsigned int accl_fs:2;
        unsigned int accl_fifo_enable:1;
        unsigned int gyro_fifo_enable:1;
+       unsigned int magn_fifo_enable:1;
        u8 divider;
        u8 user_ctrl;
 };
@@ -125,6 +131,9 @@ struct inv_mpu6050_hw {
  *  @it_timestamp:     timestamp from previous interrupt.
  *  @data_timestamp:   timestamp for next data sample.
  *  @vddio_supply      voltage regulator for the chip.
+ *  @magn_disabled:     magnetometer disabled for backward compatibility reason.
+ *  @magn_raw_to_gauss:        coefficient to convert mag raw value to Gauss.
+ *  @magn_orient:       magnetometer sensor chip orientation if available.
  */
 struct inv_mpu6050_state {
        struct mutex lock;
@@ -146,6 +155,9 @@ struct inv_mpu6050_state {
        s64 it_timestamp;
        s64 data_timestamp;
        struct regulator *vddio_supply;
+       bool magn_disabled;
+       s32 magn_raw_to_gauss[3];
+       struct iio_mount_matrix magn_orient;
 };
 
 /*register and associated bit definition*/
@@ -158,9 +170,41 @@ struct inv_mpu6050_state {
 #define INV_MPU6050_REG_ACCEL_CONFIG        0x1C
 
 #define INV_MPU6050_REG_FIFO_EN             0x23
+#define INV_MPU6050_BIT_SLAVE_0             0x01
+#define INV_MPU6050_BIT_SLAVE_1             0x02
+#define INV_MPU6050_BIT_SLAVE_2             0x04
 #define INV_MPU6050_BIT_ACCEL_OUT           0x08
 #define INV_MPU6050_BITS_GYRO_OUT           0x70
 
+#define INV_MPU6050_REG_I2C_MST_CTRL        0x24
+#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D
+#define INV_MPU6050_BIT_I2C_MST_P_NSR       0x10
+#define INV_MPU6050_BIT_SLV3_FIFO_EN        0x20
+#define INV_MPU6050_BIT_WAIT_FOR_ES         0x40
+#define INV_MPU6050_BIT_MULT_MST_EN         0x80
+
+/* control I2C slaves from 0 to 3 */
+#define INV_MPU6050_REG_I2C_SLV_ADDR(_x)    (0x25 + 3 * (_x))
+#define INV_MPU6050_BIT_I2C_SLV_RNW         0x80
+
+#define INV_MPU6050_REG_I2C_SLV_REG(_x)     (0x26 + 3 * (_x))
+
+#define INV_MPU6050_REG_I2C_SLV_CTRL(_x)    (0x27 + 3 * (_x))
+#define INV_MPU6050_BIT_SLV_GRP             0x10
+#define INV_MPU6050_BIT_SLV_REG_DIS         0x20
+#define INV_MPU6050_BIT_SLV_BYTE_SW         0x40
+#define INV_MPU6050_BIT_SLV_EN              0x80
+
+/* I2C master delay register */
+#define INV_MPU6050_REG_I2C_SLV4_CTRL       0x34
+#define INV_MPU6050_BITS_I2C_MST_DLY(_x)    ((_x) & 0x1F)
+
+#define INV_MPU6050_REG_I2C_MST_STATUS      0x36
+#define INV_MPU6050_BIT_I2C_SLV0_NACK       0x01
+#define INV_MPU6050_BIT_I2C_SLV1_NACK       0x02
+#define INV_MPU6050_BIT_I2C_SLV2_NACK       0x04
+#define INV_MPU6050_BIT_I2C_SLV3_NACK       0x08
+
 #define INV_MPU6050_REG_INT_ENABLE          0x38
 #define INV_MPU6050_BIT_DATA_RDY_EN         0x01
 #define INV_MPU6050_BIT_DMP_INT_EN          0x02
@@ -173,6 +217,18 @@ struct inv_mpu6050_state {
 #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT   0x10
 #define INV_MPU6050_BIT_RAW_DATA_RDY_INT    0x01
 
+#define INV_MPU6050_REG_EXT_SENS_DATA       0x49
+
+/* I2C slaves data output from 0 to 3 */
+#define INV_MPU6050_REG_I2C_SLV_DO(_x)      (0x63 + (_x))
+
+#define INV_MPU6050_REG_I2C_MST_DELAY_CTRL  0x67
+#define INV_MPU6050_BIT_I2C_SLV0_DLY_EN     0x01
+#define INV_MPU6050_BIT_I2C_SLV1_DLY_EN     0x02
+#define INV_MPU6050_BIT_I2C_SLV2_DLY_EN     0x04
+#define INV_MPU6050_BIT_I2C_SLV3_DLY_EN     0x08
+#define INV_MPU6050_BIT_DELAY_ES_SHADOW     0x80
+
 #define INV_MPU6050_REG_USER_CTRL           0x6A
 #define INV_MPU6050_BIT_FIFO_RST            0x04
 #define INV_MPU6050_BIT_DMP_RST             0x08
@@ -200,6 +256,9 @@ struct inv_mpu6050_state {
 #define INV_MPU6050_BYTES_PER_3AXIS_SENSOR   6
 #define INV_MPU6050_FIFO_COUNT_BYTE          2
 
+/* MPU9X50 9-axis magnetometer */
+#define INV_MPU9X50_BYTES_MAGN               7
+
 /* ICM20602 FIFO samples include temperature readings */
 #define INV_ICM20602_BYTES_PER_TEMP_SENSOR   2
 
@@ -227,8 +286,8 @@ struct inv_mpu6050_state {
 #define INV_ICM20602_TEMP_OFFSET            8170
 #define INV_ICM20602_TEMP_SCALE                     3060
 
-/* 6 + 6 round up and plus 8 */
-#define INV_MPU6050_OUTPUT_DATA_SIZE         24
+/* 6 + 6 + 7 (for MPU9x50) = 19 round up to 24 and plus 8 */
+#define INV_MPU6050_OUTPUT_DATA_SIZE         32
 
 #define INV_MPU6050_REG_INT_PIN_CFG    0x37
 #define INV_MPU6050_ACTIVE_HIGH                0x00
@@ -277,6 +336,11 @@ enum inv_mpu6050_scan {
        INV_MPU6050_SCAN_GYRO_Y,
        INV_MPU6050_SCAN_GYRO_Z,
        INV_MPU6050_SCAN_TIMESTAMP,
+
+       INV_MPU9X50_SCAN_MAGN_X = INV_MPU6050_SCAN_GYRO_Z + 1,
+       INV_MPU9X50_SCAN_MAGN_Y,
+       INV_MPU9X50_SCAN_MAGN_Z,
+       INV_MPU9X50_SCAN_TIMESTAMP,
 };
 
 /* scan element definition for ICM20602, which includes temperature */
@@ -342,3 +406,5 @@ void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
 int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
                int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type);
 extern const struct dev_pm_ops inv_mpu_pmops;
+
+#endif
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
new file mode 100644 (file)
index 0000000..02735af
--- /dev/null
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 TDK-InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/string.h>
+
+#include "inv_mpu_aux.h"
+#include "inv_mpu_iio.h"
+#include "inv_mpu_magn.h"
+
+/*
+ * MPU9250 magnetometer is an AKM AK8963 chip on I2C aux bus
+ */
+#define INV_MPU_MAGN_I2C_ADDR          0x0C
+
+#define INV_MPU_MAGN_REG_WIA           0x00
+#define INV_MPU_MAGN_BITS_WIA          0x48
+
+#define INV_MPU_MAGN_REG_ST1           0x02
+#define INV_MPU_MAGN_BIT_DRDY          0x01
+#define INV_MPU_MAGN_BIT_DOR           0x02
+
+#define INV_MPU_MAGN_REG_DATA          0x03
+
+#define INV_MPU_MAGN_REG_ST2           0x09
+#define INV_MPU_MAGN_BIT_HOFL          0x08
+#define INV_MPU_MAGN_BIT_BITM          0x10
+
+#define INV_MPU_MAGN_REG_CNTL1         0x0A
+#define INV_MPU_MAGN_BITS_MODE_PWDN    0x00
+#define INV_MPU_MAGN_BITS_MODE_SINGLE  0x01
+#define INV_MPU_MAGN_BITS_MODE_FUSE    0x0F
+#define INV_MPU_MAGN_BIT_OUTPUT_BIT    0x10
+
+#define INV_MPU_MAGN_REG_CNTL2         0x0B
+#define INV_MPU_MAGN_BIT_SRST          0x01
+
+#define INV_MPU_MAGN_REG_ASAX          0x10
+#define INV_MPU_MAGN_REG_ASAY          0x11
+#define INV_MPU_MAGN_REG_ASAZ          0x12
+
+/* Magnetometer maximum frequency */
+#define INV_MPU_MAGN_FREQ_HZ_MAX       50
+
+static bool inv_magn_supported(const struct inv_mpu6050_state *st)
+{
+       switch (st->chip_type) {
+       case INV_MPU9250:
+       case INV_MPU9255:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/* init magnetometer chip */
+static int inv_magn_init(struct inv_mpu6050_state *st)
+{
+       uint8_t val;
+       uint8_t asa[3];
+       int ret;
+
+       /* check whoami */
+       ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_WIA,
+                              &val, sizeof(val));
+       if (ret)
+               return ret;
+       if (val != INV_MPU_MAGN_BITS_WIA)
+               return -ENODEV;
+
+       /* reset chip */
+       ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
+                               INV_MPU_MAGN_REG_CNTL2,
+                               INV_MPU_MAGN_BIT_SRST);
+       if (ret)
+               return ret;
+
+       /* read fuse ROM data */
+       ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
+                               INV_MPU_MAGN_REG_CNTL1,
+                               INV_MPU_MAGN_BITS_MODE_FUSE);
+       if (ret)
+               return ret;
+
+       ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_ASAX,
+                              asa, sizeof(asa));
+       if (ret)
+               return ret;
+
+       /* switch back to power-down */
+       ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
+                               INV_MPU_MAGN_REG_CNTL1,
+                               INV_MPU_MAGN_BITS_MODE_PWDN);
+       if (ret)
+               return ret;
+
+       /*
+        * Sensitivity adjustement and scale to Gauss
+        *
+        * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1)
+        * Factor simplification:
+        * Hadj = H * ((ASA + 128) / 256)
+        *
+        * Sensor sentivity
+        * 0.15 uT in 16 bits mode
+        * 1 uT = 0.01 G and value is in micron (1e6)
+        * sensitvity = 0.15 uT * 0.01 * 1e6
+        *
+        * raw_to_gauss = Hadj * 1500
+        */
+       st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * 1500) / 256;
+       st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * 1500) / 256;
+       st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * 1500) / 256;
+
+       return 0;
+}
+
+/**
+ * inv_mpu_magn_probe() - probe and setup magnetometer chip
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise
+ *
+ * It is probing the chip and setting up all needed i2c transfers.
+ * Noop if there is no magnetometer in the chip.
+ */
+int inv_mpu_magn_probe(struct inv_mpu6050_state *st)
+{
+       int ret;
+
+       /* quit if chip is not supported */
+       if (!inv_magn_supported(st))
+               return 0;
+
+       /* configure i2c master aux port */
+       ret = inv_mpu_aux_init(st);
+       if (ret)
+               return ret;
+
+       /* check and init mag chip */
+       ret = inv_magn_init(st);
+       if (ret)
+               return ret;
+
+       /*
+        * configure mpu i2c master accesses
+        * i2c SLV0: read sensor data, 7 bytes data(6)-ST2
+        * Byte swap data to store them in big-endian in impair address groups
+        */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0),
+                          INV_MPU6050_BIT_I2C_SLV_RNW | INV_MPU_MAGN_I2C_ADDR);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0),
+                          INV_MPU_MAGN_REG_DATA);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
+                          INV_MPU6050_BIT_SLV_EN |
+                          INV_MPU6050_BIT_SLV_BYTE_SW |
+                          INV_MPU6050_BIT_SLV_GRP |
+                          INV_MPU9X50_BYTES_MAGN);
+       if (ret)
+               return ret;
+
+       /* i2c SLV1: launch single measurement */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(1),
+                          INV_MPU_MAGN_I2C_ADDR);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(1),
+                          INV_MPU_MAGN_REG_CNTL1);
+       if (ret)
+               return ret;
+
+       /* add 16 bits mode */
+       ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1),
+                          INV_MPU_MAGN_BITS_MODE_SINGLE |
+                          INV_MPU_MAGN_BIT_OUTPUT_BIT);
+       if (ret)
+               return ret;
+
+       return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(1),
+                           INV_MPU6050_BIT_SLV_EN | 1);
+}
+
+/**
+ * inv_mpu_magn_set_rate() - set magnetometer sampling rate
+ * @st: driver internal state
+ * @fifo_rate: mpu set fifo rate
+ *
+ * Returns 0 on success, a negative error code otherwise
+ *
+ * Limit sampling frequency to the maximum value supported by the
+ * magnetometer chip. Resulting in duplicated data for higher frequencies.
+ * Noop if there is no magnetometer in the chip.
+ */
+int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate)
+{
+       uint8_t d;
+
+       /* quit if chip is not supported */
+       if (!inv_magn_supported(st))
+               return 0;
+
+       /*
+        * update i2c master delay to limit mag sampling to max frequency
+        * compute fifo_rate divider d: rate = fifo_rate / (d + 1)
+        */
+       if (fifo_rate > INV_MPU_MAGN_FREQ_HZ_MAX)
+               d = fifo_rate / INV_MPU_MAGN_FREQ_HZ_MAX - 1;
+       else
+               d = 0;
+
+       return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, d);
+}
+
+/**
+ * inv_mpu_magn_set_orient() - fill magnetometer mounting matrix
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise
+ *
+ * Fill magnetometer mounting matrix using the provided chip matrix.
+ */
+int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st)
+{
+       const char *orient;
+       char *str;
+       int i;
+
+       /* fill magnetometer orientation */
+       switch (st->chip_type) {
+       case INV_MPU9250:
+       case INV_MPU9255:
+               /* x <- y */
+               st->magn_orient.rotation[0] = st->orientation.rotation[3];
+               st->magn_orient.rotation[1] = st->orientation.rotation[4];
+               st->magn_orient.rotation[2] = st->orientation.rotation[5];
+               /* y <- x */
+               st->magn_orient.rotation[3] = st->orientation.rotation[0];
+               st->magn_orient.rotation[4] = st->orientation.rotation[1];
+               st->magn_orient.rotation[5] = st->orientation.rotation[2];
+               /* z <- -z */
+               for (i = 0; i < 3; ++i) {
+                       orient = st->orientation.rotation[6 + i];
+                       /* use length + 2 for adding minus sign if needed */
+                       str = devm_kzalloc(regmap_get_device(st->map),
+                                          strlen(orient) + 2, GFP_KERNEL);
+                       if (str == NULL)
+                               return -ENOMEM;
+                       if (strcmp(orient, "0") == 0) {
+                               strcpy(str, orient);
+                       } else if (orient[0] == '-') {
+                               strcpy(str, &orient[1]);
+                       } else {
+                               str[0] = '-';
+                               strcpy(&str[1], orient);
+                       }
+                       st->magn_orient.rotation[6 + i] = str;
+               }
+               break;
+       default:
+               st->magn_orient = st->orientation;
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * inv_mpu_magn_read() - read magnetometer data
+ * @st: driver internal state
+ * @axis: IIO modifier axis value
+ * @val: store corresponding axis value
+ *
+ * Returns 0 on success, a negative error code otherwise
+ */
+int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val)
+{
+       unsigned int user_ctrl, status;
+       __be16 data[3];
+       uint8_t addr;
+       uint8_t d;
+       unsigned int period_ms;
+       int ret;
+
+       /* quit if chip is not supported */
+       if (!inv_magn_supported(st))
+               return -ENODEV;
+
+       /* Mag data: X - Y - Z */
+       switch (axis) {
+       case IIO_MOD_X:
+               addr = 0;
+               break;
+       case IIO_MOD_Y:
+               addr = 1;
+               break;
+       case IIO_MOD_Z:
+               addr = 2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set sample rate to max mag freq */
+       d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU_MAGN_FREQ_HZ_MAX);
+       ret = regmap_write(st->map, st->reg->sample_rate_div, d);
+       if (ret)
+               return ret;
+
+       /* start i2c master, wait for xfer, stop */
+       user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
+       ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
+       if (ret)
+               return ret;
+
+       /* need to wait 2 periods + half-period margin */
+       period_ms = 1000 / INV_MPU_MAGN_FREQ_HZ_MAX;
+       msleep(period_ms * 2 + period_ms / 2);
+       user_ctrl = st->chip_config.user_ctrl;
+       ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
+       if (ret)
+               return ret;
+
+       /* restore sample rate */
+       d = st->chip_config.divider;
+       ret = regmap_write(st->map, st->reg->sample_rate_div, d);
+       if (ret)
+               return ret;
+
+       /* check i2c status and read raw data */
+       ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
+       if (ret)
+               return ret;
+
+       if (status & INV_MPU6050_BIT_I2C_SLV0_NACK ||
+                       status & INV_MPU6050_BIT_I2C_SLV1_NACK)
+               return -EIO;
+
+       ret = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
+                              data, sizeof(data));
+       if (ret)
+               return ret;
+
+       *val = (int16_t)be16_to_cpu(data[addr]);
+
+       return IIO_VAL_INT;
+}
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h
new file mode 100644 (file)
index 0000000..b41bd05
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 TDK-InvenSense, Inc.
+ */
+
+#ifndef INV_MPU_MAGN_H_
+#define INV_MPU_MAGN_H_
+
+#include <linux/kernel.h>
+
+#include "inv_mpu_iio.h"
+
+int inv_mpu_magn_probe(struct inv_mpu6050_state *st);
+
+/**
+ * inv_mpu_magn_get_scale() - get magnetometer scale value
+ * @st: driver internal state
+ *
+ * Returns IIO data format.
+ */
+static inline int inv_mpu_magn_get_scale(const struct inv_mpu6050_state *st,
+                                        const struct iio_chan_spec *chan,
+                                        int *val, int *val2)
+{
+       *val = 0;
+       *val2 = st->magn_raw_to_gauss[chan->address];
+       return IIO_VAL_INT_PLUS_MICRO;
+}
+
+int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate);
+
+int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st);
+
+int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val);
+
+#endif         /* INV_MPU_MAGN_H_ */
index 5f9a5de0bab44abe645598fd7bf6beba6a6a98e4..bbf68b474556be2f22a1d80ad1e1ec958e45cdbf 100644 (file)
@@ -124,7 +124,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
 
        /* enable interrupt */
        if (st->chip_config.accl_fifo_enable ||
-           st->chip_config.gyro_fifo_enable) {
+           st->chip_config.gyro_fifo_enable ||
+           st->chip_config.magn_fifo_enable) {
                result = regmap_write(st->map, st->reg->int_enable,
                                      INV_MPU6050_BIT_DATA_RDY_EN);
                if (result)
@@ -141,6 +142,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
                d |= INV_MPU6050_BITS_GYRO_OUT;
        if (st->chip_config.accl_fifo_enable)
                d |= INV_MPU6050_BIT_ACCEL_OUT;
+       if (st->chip_config.magn_fifo_enable)
+               d |= INV_MPU6050_BIT_SLAVE_0;
        result = regmap_write(st->map, st->reg->fifo_en, d);
        if (result)
                goto reset_fifo_fail;
@@ -190,7 +193,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
        }
 
        if (!(st->chip_config.accl_fifo_enable |
-               st->chip_config.gyro_fifo_enable))
+               st->chip_config.gyro_fifo_enable |
+               st->chip_config.magn_fifo_enable))
                goto end_session;
        bytes_per_datum = 0;
        if (st->chip_config.accl_fifo_enable)
@@ -202,6 +206,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
        if (st->chip_type == INV_ICM20602)
                bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR;
 
+       if (st->chip_config.magn_fifo_enable)
+               bytes_per_datum += INV_MPU9X50_BYTES_MAGN;
+
        /*
         * read fifo_count register to know how many bytes are inside the FIFO
         * right now
index dd55e70b6f777209a96ed70a5d5bbf6a7251b8dc..d7d951927a442dfec1e3b312868246665188959e 100644 (file)
@@ -5,7 +5,7 @@
 
 #include "inv_mpu_iio.h"
 
-static void inv_scan_query(struct iio_dev *indio_dev)
+static void inv_scan_query_mpu6050(struct iio_dev *indio_dev)
 {
        struct inv_mpu6050_state  *st = iio_priv(indio_dev);
 
@@ -26,6 +26,60 @@ static void inv_scan_query(struct iio_dev *indio_dev)
                         indio_dev->active_scan_mask);
 }
 
+static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev)
+{
+       struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+       inv_scan_query_mpu6050(indio_dev);
+
+       /* no magnetometer if i2c auxiliary bus is used */
+       if (st->magn_disabled)
+               return;
+
+       st->chip_config.magn_fifo_enable =
+               test_bit(INV_MPU9X50_SCAN_MAGN_X,
+                        indio_dev->active_scan_mask) ||
+               test_bit(INV_MPU9X50_SCAN_MAGN_Y,
+                        indio_dev->active_scan_mask) ||
+               test_bit(INV_MPU9X50_SCAN_MAGN_Z,
+                        indio_dev->active_scan_mask);
+}
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+       struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+       switch (st->chip_type) {
+       case INV_MPU9250:
+       case INV_MPU9255:
+               return inv_scan_query_mpu9x50(indio_dev);
+       default:
+               return inv_scan_query_mpu6050(indio_dev);
+       }
+}
+
+static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st)
+{
+       unsigned int gyro_skip = 0;
+       unsigned int magn_skip = 0;
+       unsigned int skip_samples;
+
+       /* gyro first sample is out of specs, skip it */
+       if (st->chip_config.gyro_fifo_enable)
+               gyro_skip = 1;
+
+       /* mag first sample is always not ready, skip it */
+       if (st->chip_config.magn_fifo_enable)
+               magn_skip = 1;
+
+       /* compute first samples to skip */
+       skip_samples = gyro_skip;
+       if (magn_skip > skip_samples)
+               skip_samples = magn_skip;
+
+       return skip_samples;
+}
+
 /**
  *  inv_mpu6050_set_enable() - enable chip functions.
  *  @indio_dev:        Device driver instance.
@@ -34,6 +88,7 @@ static void inv_scan_query(struct iio_dev *indio_dev)
 static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
 {
        struct inv_mpu6050_state *st = iio_priv(indio_dev);
+       uint8_t d;
        int result;
 
        if (enable) {
@@ -41,14 +96,11 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
                if (result)
                        return result;
                inv_scan_query(indio_dev);
-               st->skip_samples = 0;
                if (st->chip_config.gyro_fifo_enable) {
                        result = inv_mpu6050_switch_engine(st, true,
                                        INV_MPU6050_BIT_PWR_GYRO_STBY);
                        if (result)
                                goto error_power_off;
-                       /* gyro first sample is out of specs, skip it */
-                       st->skip_samples = 1;
                }
                if (st->chip_config.accl_fifo_enable) {
                        result = inv_mpu6050_switch_engine(st, true,
@@ -56,22 +108,32 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
                        if (result)
                                goto error_gyro_off;
                }
+               if (st->chip_config.magn_fifo_enable) {
+                       d = st->chip_config.user_ctrl |
+                                       INV_MPU6050_BIT_I2C_MST_EN;
+                       result = regmap_write(st->map, st->reg->user_ctrl, d);
+                       if (result)
+                               goto error_accl_off;
+                       st->chip_config.user_ctrl = d;
+               }
+               st->skip_samples = inv_compute_skip_samples(st);
                result = inv_reset_fifo(indio_dev);
                if (result)
-                       goto error_accl_off;
+                       goto error_magn_off;
        } else {
                result = regmap_write(st->map, st->reg->fifo_en, 0);
                if (result)
-                       goto error_accl_off;
+                       goto error_magn_off;
 
                result = regmap_write(st->map, st->reg->int_enable, 0);
                if (result)
-                       goto error_accl_off;
+                       goto error_magn_off;
 
-               result = regmap_write(st->map, st->reg->user_ctrl,
-                                     st->chip_config.user_ctrl);
+               d = st->chip_config.user_ctrl & ~INV_MPU6050_BIT_I2C_MST_EN;
+               result = regmap_write(st->map, st->reg->user_ctrl, d);
                if (result)
-                       goto error_accl_off;
+                       goto error_magn_off;
+               st->chip_config.user_ctrl = d;
 
                result = inv_mpu6050_switch_engine(st, false,
                                        INV_MPU6050_BIT_PWR_ACCL_STBY);
@@ -90,6 +152,10 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
 
        return 0;
 
+error_magn_off:
+       /* always restore user_ctrl to disable fifo properly */
+       st->chip_config.user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN;
+       regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
 error_accl_off:
        if (st->chip_config.accl_fifo_enable)
                inv_mpu6050_switch_engine(st, false,
index 80e42c7dbcbe63039f4d23ed363799c75a1d9698..fd02d0e184f382f0c5f2000b3cebb479840129da 100644 (file)
@@ -12,6 +12,7 @@
 #define ST_LSM6DSX_H
 
 #include <linux/device.h>
+#include <linux/iio/iio.h>
 
 #define ST_LSM6DS3_DEV_NAME    "lsm6ds3"
 #define ST_LSM6DS3H_DEV_NAME   "lsm6ds3h"
@@ -54,6 +55,26 @@ enum st_lsm6dsx_hw_id {
                                         * ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
 #define ST_LSM6DSX_SHIFT_VAL(val, mask)        (((val) << __ffs(mask)) & (mask))
 
+#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx)         \
+{                                                                      \
+       .type = chan_type,                                              \
+       .address = addr,                                                \
+       .modified = 1,                                                  \
+       .channel2 = mod,                                                \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
+       .info_mask_shared_by_type = 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,                                   \
+       },                                                              \
+       .event_spec = &st_lsm6dsx_event,                                \
+       .num_event_specs = 1,                                           \
+}
+
 #define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)             \
 {                                                                      \
        .type = chan_type,                                              \
@@ -162,6 +183,16 @@ struct st_lsm6dsx_shub_settings {
        u8 batch_en;
 };
 
+struct st_lsm6dsx_event_settings {
+       struct st_lsm6dsx_reg enable_reg;
+       struct st_lsm6dsx_reg wakeup_reg;
+       u8 wakeup_src_reg;
+       u8 wakeup_src_status_mask;
+       u8 wakeup_src_z_mask;
+       u8 wakeup_src_y_mask;
+       u8 wakeup_src_x_mask;
+};
+
 enum st_lsm6dsx_ext_sensor_id {
        ST_LSM6DSX_ID_MAGN,
 };
@@ -215,6 +246,8 @@ struct st_lsm6dsx_ext_dev_settings {
  * @fs_table: Hw sensors gain table (gain + val).
  * @decimator: List of decimator register info (addr + mask).
  * @batch: List of FIFO batching register info (addr + mask).
+ * @lir: Latched interrupt register info (addr + mask).
+ * @clear_on_read: Clear on read register info (addr + mask).
  * @fifo_ops: Sensor hw FIFO parameters.
  * @ts_settings: Hw timer related settings.
  * @shub_settings: i2c controller related settings.
@@ -223,6 +256,9 @@ struct st_lsm6dsx_settings {
        u8 wai;
        u8 int1_addr;
        u8 int2_addr;
+       u8 int1_func_addr;
+       u8 int2_func_addr;
+       u8 int_func_mask;
        u8 reset_addr;
        u16 max_fifo_size;
        struct {
@@ -237,9 +273,12 @@ struct st_lsm6dsx_settings {
        struct st_lsm6dsx_fs_table_entry fs_table[2];
        struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
        struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
+       struct st_lsm6dsx_reg lir;
+       struct st_lsm6dsx_reg clear_on_read;
        struct st_lsm6dsx_fifo_ops fifo_ops;
        struct st_lsm6dsx_hw_ts_settings ts_settings;
        struct st_lsm6dsx_shub_settings shub_settings;
+       struct st_lsm6dsx_event_settings event_settings;
 };
 
 enum st_lsm6dsx_sensor_id {
@@ -320,6 +359,10 @@ struct st_lsm6dsx_hw {
        u8 ts_sip;
        u8 sip;
 
+       u8 event_threshold;
+       u8 enable_event;
+       struct st_lsm6dsx_reg irq_routing;
+
        u8 *buff;
 
        struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX];
@@ -327,6 +370,13 @@ struct st_lsm6dsx_hw {
        const struct st_lsm6dsx_settings *settings;
 };
 
+static const struct iio_event_spec st_lsm6dsx_event = {
+       .type = IIO_EV_TYPE_THRESH,
+       .dir = IIO_EV_DIR_EITHER,
+       .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+                        BIT(IIO_EV_INFO_ENABLE)
+};
+
 static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
 extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
 
index b0f3da1976e4f46949c2f23e097707989a9aea93..ef579650fd5232775478e790f2e2b2a683fa1c8f 100644 (file)
@@ -30,8 +30,6 @@
  * Denis Ciocca <denis.ciocca@st.com>
  */
 #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_HLACTIVE_ADDR           0x12
-#define ST_LSM6DSX_REG_HLACTIVE_MASK           BIT(5)
-#define ST_LSM6DSX_REG_PP_OD_ADDR              0x12
-#define ST_LSM6DSX_REG_PP_OD_MASK              BIT(4)
 #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)
@@ -654,25 +648,6 @@ out:
        return err;
 }
 
-static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
-{
-       struct st_lsm6dsx_hw *hw = private;
-
-       return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE;
-}
-
-static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
-{
-       struct st_lsm6dsx_hw *hw = private;
-       int count;
-
-       mutex_lock(&hw->fifo_lock);
-       count = hw->settings->fifo_ops.read_fifo(hw);
-       mutex_unlock(&hw->fifo_lock);
-
-       return count ? IRQ_HANDLED : IRQ_NONE;
-}
-
 static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
 {
        struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
@@ -702,59 +677,8 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
 
 int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
 {
-       struct device_node *np = hw->dev->of_node;
-       struct st_sensors_platform_data *pdata;
        struct iio_buffer *buffer;
-       unsigned long irq_type;
-       bool irq_active_low;
-       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:
-               irq_active_low = false;
-               break;
-       case IRQF_TRIGGER_LOW:
-       case IRQF_TRIGGER_FALLING:
-               irq_active_low = true;
-               break;
-       default:
-               dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
-               return -EINVAL;
-       }
-
-       err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_HLACTIVE_ADDR,
-                                ST_LSM6DSX_REG_HLACTIVE_MASK,
-                                FIELD_PREP(ST_LSM6DSX_REG_HLACTIVE_MASK,
-                                           irq_active_low));
-       if (err < 0)
-               return err;
-
-       pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
-       if ((np && of_property_read_bool(np, "drive-open-drain")) ||
-           (pdata && pdata->open_drain)) {
-               err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_PP_OD_ADDR,
-                                        ST_LSM6DSX_REG_PP_OD_MASK,
-                                        FIELD_PREP(ST_LSM6DSX_REG_PP_OD_MASK,
-                                                   1));
-               if (err < 0)
-                       return err;
-
-               irq_type |= IRQF_SHARED;
-       }
-
-       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;
-       }
+       int i;
 
        for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
                if (!hw->iio_devs[i])
index 2d34955601368870cb8ee931600a67146c5bb3e7..8a813ddba19c3832685862d0888f6112ef3d7a26 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/iio/events.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/pm.h>
 #include <linux/regmap.h>
 #include <linux/bitfield.h>
 #define ST_LSM6DSX_REG_BDU_ADDR                        0x12
 #define ST_LSM6DSX_REG_BDU_MASK                        BIT(6)
 
+#define ST_LSM6DSX_REG_HLACTIVE_ADDR           0x12
+#define ST_LSM6DSX_REG_HLACTIVE_MASK           BIT(5)
+#define ST_LSM6DSX_REG_PP_OD_ADDR              0x12
+#define ST_LSM6DSX_REG_PP_OD_MASK              BIT(4)
+
 static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
-       ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
-       ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
-       ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
+       ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
+       ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
+       ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
        IIO_CHAN_SOFT_TIMESTAMP(3),
 };
 
@@ -161,6 +169,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                .wai = 0x69,
                .int1_addr = 0x0d,
                .int2_addr = 0x0e,
+               .int1_func_addr = 0x5e,
+               .int2_func_addr = 0x5f,
+               .int_func_mask = BIT(5),
                .reset_addr = 0x12,
                .max_fifo_size = 1365,
                .id = {
@@ -237,6 +248,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .lir = {
+                       .addr = 0x58,
+                       .mask = BIT(0),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_fifo,
@@ -268,11 +283,25 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .event_settings = {
+                       .wakeup_reg = {
+                               .addr = 0x5B,
+                               .mask = GENMASK(5, 0),
+                       },
+                       .wakeup_src_reg = 0x1b,
+                       .wakeup_src_status_mask = BIT(3),
+                       .wakeup_src_z_mask = BIT(0),
+                       .wakeup_src_y_mask = BIT(1),
+                       .wakeup_src_x_mask = BIT(2),
+               },
        },
        {
                .wai = 0x69,
                .int1_addr = 0x0d,
                .int2_addr = 0x0e,
+               .int1_func_addr = 0x5e,
+               .int2_func_addr = 0x5f,
+               .int_func_mask = BIT(5),
                .reset_addr = 0x12,
                .max_fifo_size = 682,
                .id = {
@@ -349,6 +378,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .lir = {
+                       .addr = 0x58,
+                       .mask = BIT(0),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_fifo,
@@ -380,11 +413,25 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .event_settings = {
+                       .wakeup_reg = {
+                               .addr = 0x5B,
+                               .mask = GENMASK(5, 0),
+                       },
+                       .wakeup_src_reg = 0x1b,
+                       .wakeup_src_status_mask = BIT(3),
+                       .wakeup_src_z_mask = BIT(0),
+                       .wakeup_src_y_mask = BIT(1),
+                       .wakeup_src_x_mask = BIT(2),
+               },
        },
        {
                .wai = 0x6a,
                .int1_addr = 0x0d,
                .int2_addr = 0x0e,
+               .int1_func_addr = 0x5e,
+               .int2_func_addr = 0x5f,
+               .int_func_mask = BIT(5),
                .reset_addr = 0x12,
                .max_fifo_size = 682,
                .id = {
@@ -470,6 +517,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .lir = {
+                       .addr = 0x58,
+                       .mask = BIT(0),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_fifo,
@@ -501,6 +552,21 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(5, 3),
                        },
                },
+               .event_settings = {
+                       .enable_reg = {
+                               .addr = 0x58,
+                               .mask = BIT(7),
+                       },
+                       .wakeup_reg = {
+                               .addr = 0x5B,
+                               .mask = GENMASK(5, 0),
+                       },
+                       .wakeup_src_reg = 0x1b,
+                       .wakeup_src_status_mask = BIT(3),
+                       .wakeup_src_z_mask = BIT(0),
+                       .wakeup_src_y_mask = BIT(1),
+                       .wakeup_src_x_mask = BIT(2),
+               },
        },
        {
                .wai = 0x6c,
@@ -585,6 +651,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(7, 4),
                        },
                },
+               .lir = {
+                       .addr = 0x56,
+                       .mask = BIT(0),
+               },
+               .clear_on_read = {
+                       .addr = 0x56,
+                       .mask = BIT(6),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_tagged_fifo,
@@ -639,6 +713,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                .wai = 0x6b,
                .int1_addr = 0x0d,
                .int2_addr = 0x0e,
+               .int1_func_addr = 0x5e,
+               .int2_func_addr = 0x5f,
+               .int_func_mask = BIT(5),
                .reset_addr = 0x12,
                .max_fifo_size = 512,
                .id = {
@@ -715,6 +792,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(7, 4),
                        },
                },
+               .lir = {
+                       .addr = 0x56,
+                       .mask = BIT(0),
+               },
+               .clear_on_read = {
+                       .addr = 0x56,
+                       .mask = BIT(6),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_tagged_fifo,
@@ -738,11 +823,29 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(7, 6),
                        },
                },
+               .event_settings = {
+                       .enable_reg = {
+                               .addr = 0x58,
+                               .mask = BIT(7),
+                       },
+                       .wakeup_reg = {
+                               .addr = 0x5B,
+                               .mask = GENMASK(5, 0),
+                       },
+                       .wakeup_src_reg = 0x1b,
+                       .wakeup_src_status_mask = BIT(3),
+                       .wakeup_src_z_mask = BIT(0),
+                       .wakeup_src_y_mask = BIT(1),
+                       .wakeup_src_x_mask = BIT(2),
+               },
        },
        {
                .wai = 0x6b,
                .int1_addr = 0x0d,
                .int2_addr = 0x0e,
+               .int1_func_addr = 0x5e,
+               .int2_func_addr = 0x5f,
+               .int_func_mask = BIT(5),
                .reset_addr = 0x12,
                .max_fifo_size = 512,
                .id = {
@@ -822,6 +925,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                                .mask = GENMASK(7, 4),
                        },
                },
+               .lir = {
+                       .addr = 0x56,
+                       .mask = BIT(0),
+               },
+               .clear_on_read = {
+                       .addr = 0x56,
+                       .mask = BIT(6),
+               },
                .fifo_ops = {
                        .update_fifo = st_lsm6dsx_update_fifo,
                        .read_fifo = st_lsm6dsx_read_tagged_fifo,
@@ -870,6 +981,21 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
                        .slv0_addr = 0x15,
                        .dw_slv0_addr = 0x21,
                        .batch_en = BIT(3),
+               },
+               .event_settings = {
+                       .enable_reg = {
+                               .addr = 0x58,
+                               .mask = BIT(7),
+                       },
+                       .wakeup_reg = {
+                               .addr = 0x5B,
+                               .mask = GENMASK(5, 0),
+                       },
+                       .wakeup_src_reg = 0x1b,
+                       .wakeup_src_status_mask = BIT(3),
+                       .wakeup_src_z_mask = BIT(0),
+                       .wakeup_src_y_mask = BIT(1),
+                       .wakeup_src_x_mask = BIT(2),
                }
        },
 };
@@ -1076,7 +1202,8 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
        if (err < 0)
                return err;
 
-       st_lsm6dsx_sensor_set_enable(sensor, false);
+       if (!hw->enable_event)
+               st_lsm6dsx_sensor_set_enable(sensor, false);
 
        *val = (s16)le16_to_cpu(data);
 
@@ -1149,6 +1276,139 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
        return err;
 }
 
+static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, int state)
+{
+       int err;
+       u8 enable = 0;
+
+       if (!hw->settings->int1_func_addr)
+               return -ENOTSUPP;
+
+       enable = state ? hw->settings->event_settings.enable_reg.mask : 0;
+
+       err = regmap_update_bits(hw->regmap,
+                                hw->settings->event_settings.enable_reg.addr,
+                                hw->settings->event_settings.enable_reg.mask,
+                                enable);
+       if (err < 0)
+               return err;
+
+       enable = state ? hw->irq_routing.mask : 0;
+
+       /* Enable wakeup interrupt */
+       return regmap_update_bits(hw->regmap, hw->irq_routing.addr,
+                                 hw->irq_routing.mask,
+                                 enable);
+}
+
+static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
+                                  const struct iio_chan_spec *chan,
+                                  enum iio_event_type type,
+                                  enum iio_event_direction dir,
+                                  enum iio_event_info info,
+                                  int *val, int *val2)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+
+       if (type != IIO_EV_TYPE_THRESH)
+               return -EINVAL;
+
+       *val2 = 0;
+       *val = hw->event_threshold;
+
+       return IIO_VAL_INT;
+}
+
+static int st_lsm6dsx_write_event(struct iio_dev *iio_dev,
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir,
+                                   enum iio_event_info info,
+                                   int val, int val2)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+       int err;
+
+       if (type != IIO_EV_TYPE_THRESH)
+               return -EINVAL;
+
+       if (val < 0 || val > 31)
+               return -EINVAL;
+
+       err = regmap_update_bits(hw->regmap,
+                                hw->settings->event_settings.wakeup_reg.addr,
+                                hw->settings->event_settings.wakeup_reg.mask,
+                                val);
+       if (err)
+               return -EINVAL;
+
+       hw->event_threshold = val;
+
+       return 0;
+}
+
+static int st_lsm6dsx_read_event_config(struct iio_dev *iio_dev,
+                                         const struct iio_chan_spec *chan,
+                                         enum iio_event_type type,
+                                         enum iio_event_direction dir)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+
+       if (type != IIO_EV_TYPE_THRESH)
+               return -EINVAL;
+
+       return !!(hw->enable_event & BIT(chan->channel2));
+}
+
+static int st_lsm6dsx_write_event_config(struct iio_dev *iio_dev,
+                                          const struct iio_chan_spec *chan,
+                                          enum iio_event_type type,
+                                          enum iio_event_direction dir,
+                                          int state)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+       u8 enable_event;
+       int err = 0;
+
+       if (type != IIO_EV_TYPE_THRESH)
+               return -EINVAL;
+
+       if (state) {
+               enable_event = hw->enable_event | BIT(chan->channel2);
+
+               /* do not enable events if they are already enabled */
+               if (hw->enable_event)
+                       goto out;
+       } else {
+               enable_event = hw->enable_event & ~BIT(chan->channel2);
+
+               /* only turn off sensor if no events is enabled */
+               if (enable_event)
+                       goto out;
+       }
+
+       /* stop here if no changes have been made */
+       if (hw->enable_event == enable_event)
+               return 0;
+
+       err = st_lsm6dsx_event_setup(hw, state);
+       if (err < 0)
+               return err;
+
+       err = st_lsm6dsx_sensor_set_enable(sensor, state);
+       if (err < 0)
+               return err;
+
+out:
+       hw->enable_event = enable_event;
+
+       return 0;
+}
+
 int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
 {
        struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
@@ -1233,6 +1493,10 @@ static const struct iio_info st_lsm6dsx_acc_info = {
        .attrs = &st_lsm6dsx_acc_attribute_group,
        .read_raw = st_lsm6dsx_read_raw,
        .write_raw = st_lsm6dsx_write_raw,
+       .read_event_value = st_lsm6dsx_read_event,
+       .write_event_value = st_lsm6dsx_write_event,
+       .read_event_config = st_lsm6dsx_read_event_config,
+       .write_event_config = st_lsm6dsx_write_event_config,
        .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
 };
 
@@ -1278,9 +1542,13 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
        switch (drdy_pin) {
        case 1:
                *drdy_reg = hw->settings->int1_addr;
+               hw->irq_routing.addr = hw->settings->int1_func_addr;
+               hw->irq_routing.mask = hw->settings->int_func_mask;
                break;
        case 2:
                *drdy_reg = hw->settings->int2_addr;
+               hw->irq_routing.addr = hw->settings->int2_func_addr;
+               hw->irq_routing.mask = hw->settings->int_func_mask;
                break;
        default:
                dev_err(hw->dev, "unsupported data ready pin\n");
@@ -1416,6 +1684,29 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
        if (err < 0)
                return err;
 
+       /* enable Latched interrupts for device events */
+       if (hw->settings->lir.addr) {
+               unsigned int data;
+
+               data = ST_LSM6DSX_SHIFT_VAL(1, hw->settings->lir.mask);
+               err = regmap_update_bits(hw->regmap, hw->settings->lir.addr,
+                                        hw->settings->lir.mask, data);
+               if (err < 0)
+                       return err;
+
+               /* enable clear on read for latched interrupts */
+               if (hw->settings->clear_on_read.addr) {
+                       data = ST_LSM6DSX_SHIFT_VAL(1,
+                                       hw->settings->clear_on_read.mask);
+                       err = regmap_update_bits(hw->regmap,
+                                       hw->settings->clear_on_read.addr,
+                                       hw->settings->clear_on_read.mask,
+                                       data);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
        err = st_lsm6dsx_init_shub(hw);
        if (err < 0)
                return err;
@@ -1466,6 +1757,123 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
        return iio_dev;
 }
 
+static void st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw, int data)
+{
+       s64 timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+
+       if ((data & hw->settings->event_settings.wakeup_src_z_mask) &&
+           (hw->enable_event & BIT(IIO_MOD_Z)))
+               iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
+                              IIO_MOD_EVENT_CODE(IIO_ACCEL,
+                                                 0,
+                                                 IIO_MOD_Z,
+                                                 IIO_EV_TYPE_THRESH,
+                                                 IIO_EV_DIR_EITHER),
+                                                 timestamp);
+
+       if ((data & hw->settings->event_settings.wakeup_src_y_mask) &&
+           (hw->enable_event & BIT(IIO_MOD_Y)))
+               iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
+                              IIO_MOD_EVENT_CODE(IIO_ACCEL,
+                                                 0,
+                                                 IIO_MOD_Y,
+                                                 IIO_EV_TYPE_THRESH,
+                                                 IIO_EV_DIR_EITHER),
+                                                 timestamp);
+
+       if ((data & hw->settings->event_settings.wakeup_src_x_mask) &&
+           (hw->enable_event & BIT(IIO_MOD_X)))
+               iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
+                              IIO_MOD_EVENT_CODE(IIO_ACCEL,
+                                                 0,
+                                                 IIO_MOD_X,
+                                                 IIO_EV_TYPE_THRESH,
+                                                 IIO_EV_DIR_EITHER),
+                                                 timestamp);
+}
+
+static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
+{
+       struct st_lsm6dsx_hw *hw = private;
+       int count;
+       int data, err;
+
+       if (hw->enable_event) {
+               err = regmap_read(hw->regmap,
+                                 hw->settings->event_settings.wakeup_src_reg,
+                                 &data);
+               if (err < 0)
+                       return IRQ_NONE;
+
+               if (data & hw->settings->event_settings.wakeup_src_status_mask)
+                       st_lsm6dsx_report_motion_event(hw, data);
+       }
+
+       mutex_lock(&hw->fifo_lock);
+       count = hw->settings->fifo_ops.read_fifo(hw);
+       mutex_unlock(&hw->fifo_lock);
+
+       return count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw)
+{
+       struct st_sensors_platform_data *pdata;
+       struct device_node *np = hw->dev->of_node;
+       unsigned long irq_type;
+       bool irq_active_low;
+       int err;
+
+       irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
+
+       switch (irq_type) {
+       case IRQF_TRIGGER_HIGH:
+       case IRQF_TRIGGER_RISING:
+               irq_active_low = false;
+               break;
+       case IRQF_TRIGGER_LOW:
+       case IRQF_TRIGGER_FALLING:
+               irq_active_low = true;
+               break;
+       default:
+               dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
+               return -EINVAL;
+       }
+
+       err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_HLACTIVE_ADDR,
+                                ST_LSM6DSX_REG_HLACTIVE_MASK,
+                                FIELD_PREP(ST_LSM6DSX_REG_HLACTIVE_MASK,
+                                           irq_active_low));
+       if (err < 0)
+               return err;
+
+       pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
+       if ((np && of_property_read_bool(np, "drive-open-drain")) ||
+           (pdata && pdata->open_drain)) {
+               err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_PP_OD_ADDR,
+                                        ST_LSM6DSX_REG_PP_OD_MASK,
+                                        FIELD_PREP(ST_LSM6DSX_REG_PP_OD_MASK,
+                                                   1));
+               if (err < 0)
+                       return err;
+
+               irq_type |= IRQF_SHARED;
+       }
+
+       err = devm_request_threaded_irq(hw->dev, hw->irq,
+                                       NULL,
+                                       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;
+       }
+
+       return 0;
+}
+
 int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
                     struct regmap *regmap)
 {
@@ -1514,6 +1922,10 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
        }
 
        if (hw->irq > 0) {
+               err = st_lsm6dsx_irq_setup(hw);
+               if (err < 0)
+                       return err;
+
                err = st_lsm6dsx_fifo_setup(hw);
                if (err < 0)
                        return err;
@@ -1528,6 +1940,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
                        return err;
        }
 
+       if (dev->of_node && of_property_read_bool(dev->of_node, "wakeup-source"))
+               device_init_wakeup(dev, true);
+
        return 0;
 }
 EXPORT_SYMBOL(st_lsm6dsx_probe);
@@ -1546,6 +1961,13 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
                if (!(hw->enable_mask & BIT(sensor->id)))
                        continue;
 
+               if (device_may_wakeup(dev) &&
+                   sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) {
+                       /* Enable wake from IRQ */
+                       enable_irq_wake(hw->irq);
+                       continue;
+               }
+
                if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
                    sensor->id == ST_LSM6DSX_ID_EXT1 ||
                    sensor->id == ST_LSM6DSX_ID_EXT2)
@@ -1575,6 +1997,10 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
                        continue;
 
                sensor = iio_priv(hw->iio_devs[i]);
+               if (device_may_wakeup(dev) &&
+                   sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event)
+                       disable_irq_wake(hw->irq);
+
                if (!(hw->suspend_mask & BIT(sensor->id)))
                        continue;
 
index 524a686077ca6d3f3b0765e8cf0c0b96d0bcf6af..f72c2dc5f703fd9fcd7786d7eeccb97f1a761129 100644 (file)
@@ -1238,6 +1238,16 @@ static ssize_t iio_show_dev_name(struct device *dev,
 
 static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
 
+static ssize_t iio_show_dev_label(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n", indio_dev->label);
+}
+
+static DEVICE_ATTR(label, S_IRUGO, iio_show_dev_label, NULL);
+
 static ssize_t iio_show_timestamp_clock(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -1354,6 +1364,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
 
        if (indio_dev->name)
                attrcount++;
+       if (indio_dev->label)
+               attrcount++;
        if (clk)
                attrcount++;
 
@@ -1376,6 +1388,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
                indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
        if (indio_dev->name)
                indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+       if (indio_dev->label)
+               indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_label.attr;
        if (clk)
                indio_dev->chan_attr_group.attrs[attrn++] = clk;
 
@@ -1647,6 +1661,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
        if (!indio_dev->dev.of_node && indio_dev->dev.parent)
                indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
 
+       indio_dev->label = of_get_property(indio_dev->dev.of_node, "label",
+                                          NULL);
+
        ret = iio_check_unique_scan_index(indio_dev);
        if (ret < 0)
                return ret;
index 28347df78cff6830f2a027f89545559e53f811ff..adb5ab9e343909ba2ae3a1a01c65aa27d3009096 100644 (file)
@@ -59,9 +59,9 @@ struct bh1750_chip_info {
 
        u16 int_time_low_mask;
        u16 int_time_high_mask;
-}
+};
 
-static const bh1750_chip_info_tbl[] = {
+static const struct bh1750_chip_info bh1750_chip_info_tbl[] = {
        [BH1710] = { 140, 1022, 300, 400,  250000000, 2, 0x001F, 0x03E0 },
        [BH1721] = { 140, 1020, 300, 400,  250000000, 2, 0x0010, 0x03E0 },
        [BH1750] = { 31,  254,  69,  1740, 57500000,  1, 0x001F, 0x00E0 },
index 1019d625adb1e355a837e6313af81ac14ab4b769..90e38fcc974b05b9dd47e336954e0aa9e20862d5 100644 (file)
@@ -532,7 +532,7 @@ static int cm36651_write_prox_event_config(struct iio_dev *indio_dev,
                                        int state)
 {
        struct cm36651_data *cm36651 = iio_priv(indio_dev);
-       int cmd, ret = -EINVAL;
+       int cmd, ret;
 
        mutex_lock(&cm36651->lock);
 
index 7c0291c5fe76e45ec010d84f34357fcdb679c5dd..b542e5619ead85679b63bda47c5d51a1305bc666 100644 (file)
@@ -240,32 +240,42 @@ static const struct iio_info tcs3414_info = {
        .attrs = &tcs3414_attribute_group,
 };
 
-static int tcs3414_buffer_preenable(struct iio_dev *indio_dev)
+static int tcs3414_buffer_postenable(struct iio_dev *indio_dev)
 {
        struct tcs3414_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = iio_triggered_buffer_postenable(indio_dev);
+       if (ret)
+               return ret;
 
        data->control |= TCS3414_CONTROL_ADC_EN;
-       return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
+       ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
                data->control);
+       if (ret)
+               iio_triggered_buffer_predisable(indio_dev);
+
+       return ret;
 }
 
 static int tcs3414_buffer_predisable(struct iio_dev *indio_dev)
 {
        struct tcs3414_data *data = iio_priv(indio_dev);
-       int ret;
-
-       ret = iio_triggered_buffer_predisable(indio_dev);
-       if (ret < 0)
-               return ret;
+       int ret, ret2;
 
        data->control &= ~TCS3414_CONTROL_ADC_EN;
-       return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
+       ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
                data->control);
+
+       ret2 = iio_triggered_buffer_predisable(indio_dev);
+       if (!ret)
+               ret = ret2;
+
+       return ret;
 }
 
 static const struct iio_buffer_setup_ops tcs3414_buffer_setup_ops = {
-       .preenable = tcs3414_buffer_preenable,
-       .postenable = &iio_triggered_buffer_postenable,
+       .postenable = tcs3414_buffer_postenable,
        .predisable = tcs3414_buffer_predisable,
 };
 
index 8d0f15f27dc5592621b4ff2ebbd3bb324f413e68..c3b5c1f6614dabcdb40efc24101047c5033a8bb5 100644 (file)
@@ -1130,7 +1130,6 @@ int bmp280_common_probe(struct device *dev,
        if (ret)
                goto out_runtime_pm_disable;
 
-
        return 0;
 
 out_runtime_pm_disable:
index 612f79c53cfc6dbc22ef4029f815756b0ba7c4ed..287d288e40c27b107e838762c4fd17a519c0c7cb 100644 (file)
@@ -675,11 +675,15 @@ out:
        return IRQ_HANDLED;
 }
 
-static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
+static int sx9500_buffer_postenable(struct iio_dev *indio_dev)
 {
        struct sx9500_data *data = iio_priv(indio_dev);
        int ret = 0, i;
 
+       ret = iio_triggered_buffer_postenable(indio_dev);
+       if (ret)
+               return ret;
+
        mutex_lock(&data->mutex);
 
        for (i = 0; i < SX9500_NUM_CHANNELS; i++)
@@ -696,6 +700,9 @@ static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
 
        mutex_unlock(&data->mutex);
 
+       if (ret)
+               iio_triggered_buffer_predisable(indio_dev);
+
        return ret;
 }
 
@@ -704,8 +711,6 @@ static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
        struct sx9500_data *data = iio_priv(indio_dev);
        int ret = 0, i;
 
-       iio_triggered_buffer_predisable(indio_dev);
-
        mutex_lock(&data->mutex);
 
        for (i = 0; i < SX9500_NUM_CHANNELS; i++)
@@ -722,12 +727,13 @@ static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
 
        mutex_unlock(&data->mutex);
 
+       iio_triggered_buffer_predisable(indio_dev);
+
        return ret;
 }
 
 static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
-       .preenable = sx9500_buffer_preenable,
-       .postenable = iio_triggered_buffer_postenable,
+       .postenable = sx9500_buffer_postenable,
        .predisable = sx9500_buffer_predisable,
 };
 
index f184ba5601d94beaeca2b149ff146968ed4eef6f..73ed550e3fc95ece760fcbc65828d7d439634efe 100644 (file)
@@ -284,6 +284,8 @@ static int max31856_probe(struct spi_device *spi)
        spi_set_drvdata(spi, indio_dev);
 
        indio_dev->info = &max31856_info;
+       indio_dev->dev.parent = &spi->dev;
+       indio_dev->dev.of_node = spi->dev.of_node;
        indio_dev->name = id->name;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->channels = max31856_channels;
index 82099db4bf0c6f89db89b17667fe657e62f79edd..a480409090c0d1024a0ecf6cecfc2dcf41b6abd9 100644 (file)
@@ -7,7 +7,6 @@
 
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
index e6b66048916571ac048794972df0d43051ea8d62..bf3e2a9cc07feb9dde76fd9489e9ddcdbb31aaaa 100644 (file)
  * The DOUT/RDY output must also be wired to an interrupt capable GPIO.
  */
 
+enum {
+   AD7192_SYSCALIB_ZERO_SCALE,
+   AD7192_SYSCALIB_FULL_SCALE,
+};
+
 struct ad7192_state {
        struct regulator                *avdd;
        struct regulator                *dvdd;
@@ -169,10 +174,80 @@ struct ad7192_state {
        u8                              devid;
        u8                              clock_sel;
        struct mutex                    lock;   /* protect sensor state */
+       u8                              syscalib_mode[8];
 
        struct ad_sigma_delta           sd;
 };
 
+static const char * const ad7192_syscalib_modes[] = {
+       [AD7192_SYSCALIB_ZERO_SCALE] = "zero_scale",
+       [AD7192_SYSCALIB_FULL_SCALE] = "full_scale",
+};
+
+static int ad7192_set_syscalib_mode(struct iio_dev *indio_dev,
+                                   const struct iio_chan_spec *chan,
+                                   unsigned int mode)
+{
+       struct ad7192_state *st = iio_priv(indio_dev);
+
+       st->syscalib_mode[chan->channel] = mode;
+
+       return 0;
+}
+
+static int ad7192_get_syscalib_mode(struct iio_dev *indio_dev,
+                                   const struct iio_chan_spec *chan)
+{
+       struct ad7192_state *st = iio_priv(indio_dev);
+
+       return st->syscalib_mode[chan->channel];
+}
+
+static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
+                                    uintptr_t private,
+                                    const struct iio_chan_spec *chan,
+                                    const char *buf, size_t len)
+{
+       struct ad7192_state *st = iio_priv(indio_dev);
+       bool sys_calib;
+       int ret, temp;
+
+       ret = strtobool(buf, &sys_calib);
+       if (ret)
+               return ret;
+
+       temp = st->syscalib_mode[chan->channel];
+       if (sys_calib) {
+               if (temp == AD7192_SYSCALIB_ZERO_SCALE)
+                       ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_ZERO,
+                                             chan->address);
+               else
+                       ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_FULL,
+                                             chan->address);
+       }
+
+       return ret ? ret : len;
+}
+
+static const struct iio_enum ad7192_syscalib_mode_enum = {
+       .items = ad7192_syscalib_modes,
+       .num_items = ARRAY_SIZE(ad7192_syscalib_modes),
+       .set = ad7192_set_syscalib_mode,
+       .get = ad7192_get_syscalib_mode
+};
+
+static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = {
+       {
+               .name = "sys_calibration",
+               .write = ad7192_write_syscalib,
+               .shared = IIO_SEPARATE,
+       },
+       IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
+                &ad7192_syscalib_mode_enum),
+       IIO_ENUM_AVAILABLE("sys_calibration_mode", &ad7192_syscalib_mode_enum),
+       {}
+};
+
 static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd)
 {
        return container_of(sd, struct ad7192_state, sd);
@@ -770,9 +845,11 @@ static int ad7192_channels_config(struct iio_dev *indio_dev)
                *chan = channels[i];
                chan->info_mask_shared_by_all |=
                        BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY);
-               if (chan->type != IIO_TEMP)
+               if (chan->type != IIO_TEMP) {
                        chan->info_mask_shared_by_type_available |=
                                BIT(IIO_CHAN_INFO_SCALE);
+                       chan->ext_info = ad7192_calibsys_ext_info;
+               }
                chan++;
        }
 
index 82706b2706acb2f50502a4f29529d847ff870ca3..42f871ab3272b60f351b21907c8ce0a3f5986df9 100644 (file)
@@ -6,5 +6,6 @@
 /* ADC channel idx. */
 #define INGENIC_ADC_AUX                0
 #define INGENIC_ADC_BATTERY    1
+#define INGENIC_ADC_AUX2       2
 
 #endif
index 7716fa0c9fce6998fe7e64ee8d90882dfb6a73bd..8a4e25a7080c2de1567887780aa857b1a4b9f9fe 100644 (file)
@@ -119,6 +119,8 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
 
 int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
        const struct iio_chan_spec *chan, int *val);
+int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+       unsigned int mode, unsigned int channel);
 int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
        const struct ad_sd_calib_data *cd, unsigned int n);
 int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
index 8e132cf819e4eeb87f0e1e9a16a8e9f02835abf5..862ce0019eba566a7b5e27b5c70128812782e2dc 100644 (file)
@@ -510,6 +510,7 @@ struct iio_buffer_setup_ops {
  *                     attributes
  * @chan_attr_group:   [INTERN] group for all attrs in base directory
  * @name:              [DRIVER] name of the device.
+ * @label:              [DRIVER] unique name to identify which device this is
  * @info:              [DRIVER] callbacks and constant info from driver
  * @clock_id:          [INTERN] timestamping clock posix identifier
  * @info_exist_lock:   [INTERN] lock to prevent use during removal
@@ -553,6 +554,7 @@ struct iio_dev {
        struct list_head                channel_attr_list;
        struct attribute_group          chan_attr_group;
        const char                      *name;
+       const char                      *label;
        const struct iio_info           *info;
        clockid_t                       clock_id;
        struct mutex                    info_exist_lock;