]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge tag 'iio-for-4.9a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Aug 2016 21:50:16 +0000 (17:50 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Aug 2016 21:50:16 +0000 (17:50 -0400)
Jonathan writes:

First round of new features, device support and cleanups for IIO in the 4.9 cycle.

Device support

* ak8974
  - New driver and bindings for this 2009 vintage magnetometer (it was very
    popular back then!)
* atlas-ph-sensor
  -  ORP sensor support(I had to look up what one of these was)
* cio-dac
  - New driver for Measurement Computing DAC boards
* dmard06
  - New driver for Domintech DMARDO6 accelerometer. Also vendor prefix.
* dmard09
  - New driver for Domintech DMARD09 accelerometer.
* maxim-thermocouple
  - max6675 and max31855 new driver
* mt6577 auxdac
  - new driver for this Mediatek chip mt2701, mt6577 and mt8173 have this
    hardware.
* ti-adc161s626
  - new driver for this TI single channel differential ADC.
* vcnl4000
  - support vcnl4010 and vcnl4020 which are compatible for all features
    currently supported by this driver.

New features

* Core
  - Allow retrieving of underlying iio_dev from a callback buffer handle.
    This is needed to allow client drivers to perform operations such as
    configuring the trigger used.
* hid-sensors
  - asynchronous resume support to avoid really long resume times.
* kxcjk-1013
  - add the mysterious KIOX000A ACPI id seen in the wild.
* Tools
  - lsiio now enumerates processed as well as raw channels.

Cleanup

* ad7298
  - use iio_device_claim_direct_mode and friends to simplify locking around
    mode switching and drop some boilerplate.
* ad7793
  - use iio_device_claim_direct_mode and friends to simplify locking around
    mode switching and drop some boilerplate.
ade7854
  - checkpatch fixups (alignment of parameters)
* atlas-ph-sensor
  - use iio_device_claim_direct_mode and friends to simplify locking around
    mode switching and drop some boilerplate.
  - Switch to REGCACHE_NONE as there are no useful register to cache.
* bma180
  - use iio_device_claim_direct_mode and friends to simplify locking around
    mode switching and drop some boilerplate.
* hdc100x
  - Add mention of the HDC1000 and HDC1008 to the Kconfig help text.
* isl29018
  - Add driver specific prefixes to defines and function names.
  - Remove excessive logging.
  - Drop newlines which add nothing to readability.
  - General tidying up of comments.
  - Drop I2C_CLASS_HWMON as irrelevant to driver.
* isl29028
  - Add driver specific prefixes to defines, enums and function names.
  - Drop comma's from available attribute output as not ABI compliant.
  - Drop I2C_CLASS_HWMON as irrelevant to driver.
* kxsd9
  - devicetree bindings.
* mag3110
  - This one wasn't locking to protect against mode switches during
    raw_reads.  Use the iio_claim_direct_mode function to fix this buglet.
* maxim-theromcouple
  - Fix missing selects for triggered buffer support in Kconfig.
* nau7802
  - Use complete instead of complete_all as only one completion at a time.
* sx9500
  - Use complete instead of complete_all as only one completion at a time.
* us5182d
  - Add a missing error code asignment instead of checking the result of
    an already checked statement.
* vcnl4000
  - Use BIT macro where appropriate.
  - Refactor return codes in read_raw callback.
  - Add some missing locking for concurrent accesses to the device.

49 files changed:
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/iio/accel/dmard06.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/mt6577_auxadc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/magnetometer/ak8974.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/temperature/maxim_thermocouple.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/mediatek/auxadc.txt [deleted file]
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/iio/accel/Kconfig
drivers/iio/accel/Makefile
drivers/iio/accel/bma180.c
drivers/iio/accel/dmard06.c [new file with mode: 0644]
drivers/iio/accel/dmard09.c [new file with mode: 0644]
drivers/iio/accel/kxcjk-1013.c
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/ad7298.c
drivers/iio/adc/ad7793.c
drivers/iio/adc/mt6577_auxadc.c [new file with mode: 0644]
drivers/iio/adc/nau7802.c
drivers/iio/adc/ti-adc161s626.c [new file with mode: 0644]
drivers/iio/buffer/industrialio-buffer-cb.c
drivers/iio/chemical/Kconfig
drivers/iio/chemical/atlas-ph-sensor.c
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
drivers/iio/dac/Kconfig
drivers/iio/dac/Makefile
drivers/iio/dac/cio-dac.c [new file with mode: 0644]
drivers/iio/humidity/Kconfig
drivers/iio/light/Kconfig
drivers/iio/light/us5182d.c
drivers/iio/light/vcnl4000.c
drivers/iio/magnetometer/Kconfig
drivers/iio/magnetometer/Makefile
drivers/iio/magnetometer/ak8974.c [new file with mode: 0644]
drivers/iio/magnetometer/mag3110.c
drivers/iio/proximity/sx9500.c
drivers/iio/temperature/Kconfig
drivers/iio/temperature/Makefile
drivers/iio/temperature/maxim_thermocouple.c [new file with mode: 0644]
drivers/staging/iio/light/isl29018.c
drivers/staging/iio/light/isl29028.c
drivers/staging/iio/meter/ade7854.c
include/linux/hid-sensor-hub.h
include/linux/iio/consumer.h
tools/iio/lsiio.c

index 5c70ce9c19544565a035d61718011f324403961a..310b1bbef5fcd28c48408af7c26a5ff08fa6064e 100644 (file)
@@ -38,6 +38,7 @@ dallas,ds4510         CPU Supervisor with Nonvolatile Memory and Programmable I/O
 dallas,ds75            Digital Thermometer and Thermostat
 dlg,da9053             DA9053: flexible system level PMIC with multicore support
 dlg,da9063             DA9063: system PMIC for quad-core application processors
+domintech,dmard09      DMARD09: 3-axis Accelerometer
 epson,rx8010           I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 epson,rx8025           High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
 epson,rx8581           I2C-BUS INTERFACE REAL TIME CLOCK MODULE
diff --git a/Documentation/devicetree/bindings/iio/accel/dmard06.txt b/Documentation/devicetree/bindings/iio/accel/dmard06.txt
new file mode 100644 (file)
index 0000000..ce105a1
--- /dev/null
@@ -0,0 +1,19 @@
+Device tree bindings for Domintech DMARD05, DMARD06, DMARD07 accelerometers
+
+Required properties:
+ - compatible          : Should be "domintech,dmard05"
+                                or "domintech,dmard06"
+                                or "domintech,dmard07"
+ - reg                 : I2C address of the chip. Should be 0x1c
+
+Example:
+       &i2c1 {
+               /* ... */
+
+               accelerometer@1c {
+                       compatible = "domintech,dmard06";
+                       reg = <0x1c>;
+               };
+
+               /* ... */
+       };
diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.txt b/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.txt
new file mode 100644 (file)
index 0000000..b25bf3a
--- /dev/null
@@ -0,0 +1,22 @@
+Kionix KXSD9 Accelerometer device tree bindings
+
+Required properties:
+ - compatible:                 should be set to "kionix,kxsd9"
+ - reg:                        i2c slave address
+
+Optional properties:
+ - vdd-supply:         The input supply for VDD
+ - iovdd-supply:       The input supply for IOVDD
+ - interrupts:         The movement detection interrupt
+ - mount-matrix:       See mount-matrix.txt
+
+Example:
+
+kxsd9@18 {
+       compatible = "kionix,kxsd9";
+       reg = <0x18>;
+       interrupt-parent = <&foo>;
+       interrupts = <57 IRQ_TYPE_EDGE_FALLING>;
+       iovdd-supply = <&bar>;
+       vdd-supply = <&baz>;
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/mt6577_auxadc.txt b/Documentation/devicetree/bindings/iio/adc/mt6577_auxadc.txt
new file mode 100644 (file)
index 0000000..68c45cb
--- /dev/null
@@ -0,0 +1,29 @@
+* Mediatek AUXADC - Analog to Digital Converter on Mediatek mobile soc (mt65xx/mt81xx/mt27xx)
+===============
+
+The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
+in some Mediatek SoCs which among other things measures the temperatures
+in the SoC. It can be used directly with register accesses, but it is also
+used by thermal controller which reads the temperatures from the AUXADC
+directly via its own bus interface. See
+Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
+for the Thermal Controller which holds a phandle to the AUXADC.
+
+Required properties:
+  - compatible: Should be one of:
+    - "mediatek,mt2701-auxadc": For MT2701 family of SoCs
+    - "mediatek,mt8173-auxadc": For MT8173 family of SoCs
+  - reg: Address range of the AUXADC unit.
+  - clocks: Should contain a clock specifier for each entry in clock-names
+  - clock-names: Should contain "main".
+  - #io-channel-cells: Should be 1, see ../iio-bindings.txt
+
+Example:
+
+auxadc: adc@11001000 {
+       compatible = "mediatek,mt2701-auxadc";
+       reg = <0 0x11001000 0 0x1000>;
+       clocks = <&pericfg CLK_PERI_AUXADC>;
+       clock-names = "main";
+       #io-channel-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt b/Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt
new file mode 100644 (file)
index 0000000..9ed2315
--- /dev/null
@@ -0,0 +1,16 @@
+* Texas Instruments ADC141S626 and ADC161S626 chips
+
+Required properties:
+ - compatible: Should be "ti,adc141s626" or "ti,adc161s626"
+ - reg: spi chip select number for the device
+
+Recommended properties:
+ - spi-max-frequency: Definition as per
+               Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+adc@0 {
+       compatible = "ti,adc161s626";
+       reg = <0>;
+       spi-max-frequency = <4300000>;
+};
diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt
new file mode 100644 (file)
index 0000000..5d8b687
--- /dev/null
@@ -0,0 +1,22 @@
+* Atlas Scientific ORP-SM OEM sensor
+
+https://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf
+
+Required properties:
+
+  - compatible: must be "atlas,orp-sm"
+  - reg: the I2C address of the sensor
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: the sole interrupt generated by the device
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt client
+  node bindings.
+
+Example:
+
+atlas@66 {
+       compatible = "atlas,orp-sm";
+       reg = <0x66>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <16 2>;
+};
diff --git a/Documentation/devicetree/bindings/iio/magnetometer/ak8974.txt b/Documentation/devicetree/bindings/iio/magnetometer/ak8974.txt
new file mode 100644 (file)
index 0000000..77d5aba
--- /dev/null
@@ -0,0 +1,29 @@
+* Asahi Kasei AK8974 magnetometer sensor
+
+Required properties:
+
+- compatible : should be "asahi-kasei,ak8974"
+- reg : the I2C address of the magnetometer
+
+Optional properties:
+
+- avdd-supply: regulator supply for the analog voltage
+  (see regulator/regulator.txt)
+- dvdd-supply: regulator supply for the digital voltage
+  (see regulator/regulator.txt)
+- interrupts: data ready (DRDY) and interrupt (INT1) lines
+  from the chip, the DRDY interrupt must be placed first.
+  The interrupts can be triggered on rising or falling
+  edges alike.
+- mount-matrix: an optional 3x3 mounting rotation matrix
+
+Example:
+
+ak8974@0f {
+       compatible = "asahi-kasei,ak8974";
+       reg = <0x0f>;
+       avdd-supply = <&foo_reg>;
+       dvdd-supply = <&bar_reg>;
+       interrupts = <0 IRQ_TYPE_EDGE_RISING>,
+                    <1 IRQ_TYPE_EDGE_RISING>;
+};
diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim_thermocouple.txt b/Documentation/devicetree/bindings/iio/temperature/maxim_thermocouple.txt
new file mode 100644 (file)
index 0000000..28bc5c4
--- /dev/null
@@ -0,0 +1,21 @@
+Maxim thermocouple support
+
+* https://datasheets.maximintegrated.com/en/ds/MAX6675.pdf
+* https://datasheets.maximintegrated.com/en/ds/MAX31855.pdf
+
+Required properties:
+
+       - compatible: must be "maxim,max31855" or "maxim,max6675"
+       - reg: SPI chip select number for the device
+       - spi-max-frequency: must be 4300000
+       - spi-cpha: must be defined for max6675 to enable SPI mode 1
+
+       Refer to spi/spi-bus.txt for generic SPI slave bindings.
+
+Example:
+
+       max31855@0 {
+               compatible = "maxim,max31855";
+               reg = <0>;
+               spi-max-frequency = <4300000>;
+       };
diff --git a/Documentation/devicetree/bindings/soc/mediatek/auxadc.txt b/Documentation/devicetree/bindings/soc/mediatek/auxadc.txt
deleted file mode 100644 (file)
index bdb7829..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-MediaTek AUXADC
-===============
-
-The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
-in some Mediatek SoCs which among other things measures the temperatures
-in the SoC. It can be used directly with register accesses, but it is also
-used by thermal controller which reads the temperatures from the AUXADC
-directly via its own bus interface. See
-Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
-for the Thermal Controller which holds a phandle to the AUXADC.
-
-Required properties:
-- compatible: Must be "mediatek,mt8173-auxadc"
-- reg: Address range of the AUXADC unit
-
-Example:
-
-auxadc: auxadc@11001000 {
-       compatible = "mediatek,mt8173-auxadc";
-       reg = <0 0x11001000 0 0x1000>;
-};
index 1992aa97d45ac8b0a9cfaefbaf5de40780c601ad..0d9d4d80479c1ea2a4914265fc9a94ee266ed915 100644 (file)
@@ -75,6 +75,7 @@ digilent      Diglent, Inc.
 dlg    Dialog Semiconductor
 dlink  D-Link Corporation
 dmo    Data Modul AG
+domintech      Domintech Co., Ltd.
 dptechnics     DPTechnics
 dragino        Dragino Technology Co., Limited
 ea     Embedded Artists AB
index f277cfd3dcfb63648da1ea0ad61a6a2f38f8557c..ae09eb4f4a760f983e51a48702f46823da4dc21b 100644 (file)
@@ -1965,6 +1965,13 @@ S:       Maintained
 F:     drivers/media/i2c/as3645a.c
 F:     include/media/i2c/as3645a.h
 
+ASAHI KASEI AK8974 DRIVER
+M:     Linus Walleij <linus.walleij@linaro.org>
+L:     linux-iio@vger.kernel.org
+W:     http://www.akm.com/
+S:     Supported
+F:     drivers/iio/magnetometer/ak8974.c
+
 ASC7621 HARDWARE MONITOR DRIVER
 M:     George Joseph <george.joseph@fairview5.com>
 L:     linux-hwmon@vger.kernel.org
@@ -7497,6 +7504,12 @@ L:       linux-iio@vger.kernel.org
 S:     Maintained
 F:     drivers/iio/potentiometer/mcp4531.c
 
+MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
+M:     William Breathitt Gray <vilhelm.gray@gmail.com>
+L:     linux-iio@vger.kernel.org
+S:     Maintained
+F:     drivers/iio/dac/cio-dac.c
+
 MEDIA DRIVERS FOR RENESAS - FCP
 M:     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 L:     linux-media@vger.kernel.org
index 89d78208de3f30d6396162a208eb0f29460087b7..5630f2316c585fc82bb58f9e58fb86312ff6663a 100644 (file)
@@ -50,6 +50,27 @@ config BMC150_ACCEL_SPI
        tristate
        select REGMAP_SPI
 
+config DMARD06
+       tristate "Domintech DMARD06 Digital Accelerometer Driver"
+       depends on OF || COMPILE_TEST
+       depends on I2C
+       help
+         Say yes here to build support for the Domintech low-g tri-axial
+         digital accelerometers: DMARD05, DMARD06, DMARD07.
+
+         To compile this driver as a module, choose M here: the
+         module will be called dmard06.
+
+config DMARD09
+       tristate "Domintech DMARD09 3-axis Accelerometer Driver"
+       depends on I2C
+       help
+         Say yes here to get support for the Domintech DMARD09 3-axis
+         accelerometer.
+
+         Choosing M will build the driver as a module. If so, the module
+         will be called dmard09.
+
 config HID_SENSOR_ACCEL_3D
        depends on HID_SENSOR_HUB
        select IIO_BUFFER
index 6cedbecca2eed6db31a527dcdfaa4891e2c4dc00..e974841ec9cf994ee11999e442681ac1253a2491 100644 (file)
@@ -8,6 +8,8 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
 obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
 obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
 obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
+obj-$(CONFIG_DMARD06)  += dmard06.o
+obj-$(CONFIG_DMARD09)  += dmard09.o
 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
 obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
 obj-$(CONFIG_KXSD9)    += kxsd9.o
index e3f88ba5faf33ff4b7179fb96f603d20875f77a6..0890934ef66f66bca02521ffe71192591afe3aa5 100644 (file)
@@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
+
                mutex_lock(&data->mutex);
-               if (iio_buffer_enabled(indio_dev)) {
-                       mutex_unlock(&data->mutex);
-                       return -EBUSY;
-               }
                ret = bma180_get_data_reg(data, chan->scan_index);
                mutex_unlock(&data->mutex);
+               iio_device_release_direct_mode(indio_dev);
                if (ret < 0)
                        return ret;
                *val = sign_extend32(ret >> chan->scan_type.shift,
diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c
new file mode 100644 (file)
index 0000000..656ca8e
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * IIO driver for Domintech DMARD06 accelerometer
+ *
+ * Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define DMARD06_DRV_NAME               "dmard06"
+
+/* Device data registers */
+#define DMARD06_CHIP_ID_REG            0x0f
+#define DMARD06_TOUT_REG               0x40
+#define DMARD06_XOUT_REG               0x41
+#define DMARD06_YOUT_REG               0x42
+#define DMARD06_ZOUT_REG               0x43
+#define DMARD06_CTRL1_REG              0x44
+
+/* Device ID value */
+#define DMARD05_CHIP_ID                        0x05
+#define DMARD06_CHIP_ID                        0x06
+#define DMARD07_CHIP_ID                        0x07
+
+/* Device values */
+#define DMARD05_AXIS_SCALE_VAL         15625
+#define DMARD06_AXIS_SCALE_VAL         31250
+#define DMARD06_TEMP_CENTER_VAL                25
+#define DMARD06_SIGN_BIT               7
+
+/* Device power modes */
+#define DMARD06_MODE_NORMAL            0x27
+#define DMARD06_MODE_POWERDOWN         0x00
+
+/* Device channels */
+#define DMARD06_ACCEL_CHANNEL(_axis, _reg) {                   \
+       .type = IIO_ACCEL,                                      \
+       .address = _reg,                                        \
+       .channel2 = IIO_MOD_##_axis,                            \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+       .modified = 1,                                          \
+}
+
+#define DMARD06_TEMP_CHANNEL(_reg) {                           \
+       .type = IIO_TEMP,                                       \
+       .address = _reg,                                        \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
+                             BIT(IIO_CHAN_INFO_OFFSET),        \
+}
+
+struct dmard06_data {
+       struct i2c_client *client;
+       u8 chip_id;
+};
+
+static const struct iio_chan_spec dmard06_channels[] = {
+       DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG),
+       DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG),
+       DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG),
+       DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG),
+};
+
+static int dmard06_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct dmard06_data *dmard06 = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = i2c_smbus_read_byte_data(dmard06->client,
+                                              chan->address);
+               if (ret < 0) {
+                       dev_err(&dmard06->client->dev,
+                               "Error reading data: %d\n", ret);
+                       return ret;
+               }
+
+               *val = sign_extend32(ret, DMARD06_SIGN_BIT);
+
+               if (dmard06->chip_id == DMARD06_CHIP_ID)
+                       *val = *val >> 1;
+
+               switch (chan->type) {
+               case IIO_ACCEL:
+                       return IIO_VAL_INT;
+               case IIO_TEMP:
+                       if (dmard06->chip_id != DMARD06_CHIP_ID)
+                               *val = *val / 2;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_OFFSET:
+               switch (chan->type) {
+               case IIO_TEMP:
+                       *val = DMARD06_TEMP_CENTER_VAL;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->type) {
+               case IIO_ACCEL:
+                       *val = 0;
+                       if (dmard06->chip_id == DMARD06_CHIP_ID)
+                               *val2 = DMARD06_AXIS_SCALE_VAL;
+                       else
+                               *val2 = DMARD05_AXIS_SCALE_VAL;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info dmard06_info = {
+       .driver_module  = THIS_MODULE,
+       .read_raw       = dmard06_read_raw,
+};
+
+static int dmard06_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int ret;
+       struct iio_dev *indio_dev;
+       struct dmard06_data *dmard06;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "I2C check functionality failed\n");
+               return -ENXIO;
+       }
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
+       if (!indio_dev) {
+               dev_err(&client->dev, "Failed to allocate iio device\n");
+               return -ENOMEM;
+       }
+
+       dmard06 = iio_priv(indio_dev);
+       dmard06->client = client;
+
+       ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG);
+       if (ret < 0) {
+               dev_err(&client->dev, "Error reading chip id: %d\n", ret);
+               return ret;
+       }
+
+       if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID &&
+           ret != DMARD07_CHIP_ID) {
+               dev_err(&client->dev, "Invalid chip id: %02d\n", ret);
+               return -ENODEV;
+       }
+
+       dmard06->chip_id = ret;
+
+       i2c_set_clientdata(client, indio_dev);
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = DMARD06_DRV_NAME;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = dmard06_channels;
+       indio_dev->num_channels = ARRAY_SIZE(dmard06_channels);
+       indio_dev->info = &dmard06_info;
+
+       return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dmard06_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct dmard06_data *dmard06 = iio_priv(indio_dev);
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
+                                       DMARD06_MODE_POWERDOWN);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int dmard06_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       struct dmard06_data *dmard06 = iio_priv(indio_dev);
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
+                                       DMARD06_MODE_NORMAL);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
+#define DMARD06_PM_OPS (&dmard06_pm_ops)
+#else
+#define DMARD06_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id dmard06_id[] = {
+       { "dmard05", 0 },
+       { "dmard06", 0 },
+       { "dmard07", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, dmard06_id);
+
+static const struct of_device_id dmard06_of_match[] = {
+       { .compatible = "domintech,dmard05" },
+       { .compatible = "domintech,dmard06" },
+       { .compatible = "domintech,dmard07" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, dmard06_of_match);
+
+static struct i2c_driver dmard06_driver = {
+       .probe = dmard06_probe,
+       .id_table = dmard06_id,
+       .driver = {
+               .name = DMARD06_DRV_NAME,
+               .of_match_table = of_match_ptr(dmard06_of_match),
+               .pm = DMARD06_PM_OPS,
+       },
+};
+module_i2c_driver(dmard06_driver);
+
+MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
+MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
new file mode 100644 (file)
index 0000000..d3a28f9
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * IIO driver for the 3-axis accelerometer Domintech DMARD09.
+ *
+ * Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define DMARD09_DRV_NAME       "dmard09"
+
+#define DMARD09_REG_CHIPID      0x18
+#define DMARD09_REG_STAT       0x0A
+#define DMARD09_REG_X          0x0C
+#define DMARD09_REG_Y          0x0E
+#define DMARD09_REG_Z          0x10
+#define DMARD09_CHIPID         0x95
+
+#define DMARD09_BUF_LEN 8
+#define DMARD09_AXIS_X 0
+#define DMARD09_AXIS_Y 1
+#define DMARD09_AXIS_Z 2
+#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2)
+#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2)
+#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2)
+
+struct dmard09_data {
+       struct i2c_client *client;
+};
+
+#define DMARD09_CHANNEL(_axis, offset) {                       \
+       .type = IIO_ACCEL,                                      \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+       .modified = 1,                                          \
+       .address = offset,                                      \
+       .channel2 = IIO_MOD_##_axis,                            \
+}
+
+static const struct iio_chan_spec dmard09_channels[] = {
+       DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET),
+       DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET),
+       DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET),
+};
+
+static int dmard09_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct dmard09_data *data = iio_priv(indio_dev);
+       u8 buf[DMARD09_BUF_LEN];
+       int ret;
+       s16 accel;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               /*
+                * Read from the DMAR09_REG_STAT register, since the chip
+                * caches reads from the individual X, Y, Z registers.
+                */
+               ret = i2c_smbus_read_i2c_block_data(data->client,
+                                                   DMARD09_REG_STAT,
+                                                   DMARD09_BUF_LEN, buf);
+               if (ret < 0) {
+                       dev_err(&data->client->dev, "Error reading reg %d\n",
+                               DMARD09_REG_STAT);
+                       return ret;
+               }
+
+               accel = get_unaligned_le16(&buf[chan->address]);
+
+               /* Remove lower 3 bits and sign extend */
+               accel <<= 4;
+               accel >>= 7;
+
+               *val = accel;
+
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info dmard09_info = {
+       .driver_module  = THIS_MODULE,
+       .read_raw       = dmard09_read_raw,
+};
+
+static int dmard09_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int ret;
+       struct iio_dev *indio_dev;
+       struct dmard09_data *data;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev) {
+               dev_err(&client->dev, "iio allocation failed\n");
+               return -ENOMEM;
+       }
+
+       data = iio_priv(indio_dev);
+       data->client = client;
+
+       ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID);
+       if (ret < 0) {
+               dev_err(&client->dev, "Error reading chip id %d\n", ret);
+               return ret;
+       }
+
+       if (ret != DMARD09_CHIPID) {
+               dev_err(&client->dev, "Invalid chip id %d\n", ret);
+               return -ENODEV;
+       }
+
+       i2c_set_clientdata(client, indio_dev);
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = DMARD09_DRV_NAME;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = dmard09_channels;
+       indio_dev->num_channels = ARRAY_SIZE(dmard09_channels);
+       indio_dev->info = &dmard09_info;
+
+       return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id dmard09_id[] = {
+       { "dmard09", 0},
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, dmard09_id);
+
+static struct i2c_driver dmard09_driver = {
+       .driver = {
+               .name = DMARD09_DRV_NAME
+       },
+       .probe = dmard09_probe,
+       .id_table = dmard09_id,
+};
+
+module_i2c_driver(dmard09_driver);
+
+MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>");
+MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver");
+MODULE_LICENSE("GPL");
index 765a72362dc61e37d32ad7cf75e805fcba1cc8a1..3f968c46e6677c416440ed581a4a260239214662 100644 (file)
@@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
        {"KXCJ1013", KXCJK1013},
        {"KXCJ1008", KXCJ91008},
        {"KXCJ9000", KXCJ91008},
+       {"KIOX000A", KXCJ91008},
        {"KXTJ1009", KXTJ21009},
        {"SMO8500",  KXCJ91008},
        { },
index 1de31bdd4ce4e2f12cda1c3e9c597df5c323038e..e4022fd893934c8c41e67b4f4916f8d1107a08ad 100644 (file)
@@ -317,6 +317,19 @@ config MCP3422
          This driver can also be built as a module. If so, the module will be
          called mcp3422.
 
+config MEDIATEK_MT6577_AUXADC
+        tristate "MediaTek AUXADC driver"
+        depends on ARCH_MEDIATEK || COMPILE_TEST
+        depends on HAS_IOMEM
+        help
+          Say yes here to enable support for MediaTek mt65xx AUXADC.
+
+          The driver supports immediate mode operation to read from one of sixteen
+          channels (external or internal).
+
+          This driver can also be built as a module. If so, the module will be
+          called mt6577_auxadc.
+
 config MEN_Z188_ADC
        tristate "MEN 16z188 ADC IP Core support"
        depends on MCB
@@ -426,6 +439,18 @@ config TI_ADC128S052
          This driver can also be built as a module. If so, the module will be
          called ti-adc128s052.
 
+config TI_ADC161S626
+       tristate "Texas Instruments ADC161S626 1-channel differential ADC"
+       depends on SPI
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         If you say yes here you get support for Texas Instruments ADC141S626,
+         and ADC161S626 chips.
+
+         This driver can also be built as a module. If so, the module will be
+         called ti-adc161s626.
+
 config TI_ADS1015
        tristate "Texas Instruments ADS1015 ADC"
        depends on I2C && !SENSORS_ADS1015
index 0ba0d500eedbb4c0b1a2ac8bbef3e4a23741cb35..33254eb96bec024bbea45acd81239e1db833a579 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_MCP320X) += mcp320x.o
 obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
 obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
 obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
@@ -41,6 +42,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
+obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
 obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
 obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
index 10ec8fce395fc974dc9befc7cafc1900e9bf1a8b..e399bf04c73ad283702e1e1432bb7bbb4f1a65d0 100644 (file)
@@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
 
        switch (m) {
        case IIO_CHAN_INFO_RAW:
-               mutex_lock(&indio_dev->mlock);
-               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
-                       ret = -EBUSY;
-               } else {
-                       if (chan->address == AD7298_CH_TEMP)
-                               ret = ad7298_scan_temp(st, val);
-                       else
-                               ret = ad7298_scan_direct(st, chan->address);
-               }
-               mutex_unlock(&indio_dev->mlock);
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
+
+               if (chan->address == AD7298_CH_TEMP)
+                       ret = ad7298_scan_temp(st, val);
+               else
+                       ret = ad7298_scan_direct(st, chan->address);
+
+               iio_device_release_direct_mode(indio_dev);
 
                if (ret < 0)
                        return ret;
index 847789bae821c6870f64ecc504c5b78d79558cdd..e6706a09e100d271ea5528e3c8b8c289d6fca9a3 100644 (file)
@@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
        int ret, i;
        unsigned int tmp;
 
-       mutex_lock(&indio_dev->mlock);
-       if (iio_buffer_enabled(indio_dev)) {
-               mutex_unlock(&indio_dev->mlock);
-               return -EBUSY;
-       }
+       ret = iio_device_claim_direct_mode(indio_dev);
+       if (ret)
+               return ret;
 
        switch (mask) {
        case IIO_CHAN_INFO_SCALE:
@@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
                ret = -EINVAL;
        }
 
-       mutex_unlock(&indio_dev->mlock);
+       iio_device_release_direct_mode(indio_dev);
        return ret;
 }
 
diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
new file mode 100644 (file)
index 0000000..2d104c8
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+
+/* Register definitions */
+#define MT6577_AUXADC_CON0                    0x00
+#define MT6577_AUXADC_CON1                    0x04
+#define MT6577_AUXADC_CON2                    0x10
+#define MT6577_AUXADC_STA                     BIT(0)
+
+#define MT6577_AUXADC_DAT0                    0x14
+#define MT6577_AUXADC_RDY0                    BIT(12)
+
+#define MT6577_AUXADC_MISC                    0x94
+#define MT6577_AUXADC_PDN_EN                  BIT(14)
+
+#define MT6577_AUXADC_DAT_MASK                0xfff
+#define MT6577_AUXADC_SLEEP_US                1000
+#define MT6577_AUXADC_TIMEOUT_US              10000
+#define MT6577_AUXADC_POWER_READY_MS          1
+#define MT6577_AUXADC_SAMPLE_READY_US         25
+
+struct mt6577_auxadc_device {
+       void __iomem *reg_base;
+       struct clk *adc_clk;
+       struct mutex lock;
+};
+
+#define MT6577_AUXADC_CHANNEL(idx) {                               \
+               .type = IIO_VOLTAGE,                                \
+               .indexed = 1,                                       \
+               .channel = (idx),                                   \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
+static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
+       MT6577_AUXADC_CHANNEL(0),
+       MT6577_AUXADC_CHANNEL(1),
+       MT6577_AUXADC_CHANNEL(2),
+       MT6577_AUXADC_CHANNEL(3),
+       MT6577_AUXADC_CHANNEL(4),
+       MT6577_AUXADC_CHANNEL(5),
+       MT6577_AUXADC_CHANNEL(6),
+       MT6577_AUXADC_CHANNEL(7),
+       MT6577_AUXADC_CHANNEL(8),
+       MT6577_AUXADC_CHANNEL(9),
+       MT6577_AUXADC_CHANNEL(10),
+       MT6577_AUXADC_CHANNEL(11),
+       MT6577_AUXADC_CHANNEL(12),
+       MT6577_AUXADC_CHANNEL(13),
+       MT6577_AUXADC_CHANNEL(14),
+       MT6577_AUXADC_CHANNEL(15),
+};
+
+static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
+                                        u32 or_mask, u32 and_mask)
+{
+       u32 val;
+
+       val = readl(reg);
+       val |= or_mask;
+       val &= ~and_mask;
+       writel(val, reg);
+}
+
+static int mt6577_auxadc_read(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan)
+{
+       u32 val;
+       void __iomem *reg_channel;
+       int ret;
+       struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+       reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
+                     chan->channel * 0x04;
+
+       mutex_lock(&adc_dev->lock);
+
+       mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+                             0, 1 << chan->channel);
+
+       /* read channel and make sure old ready bit == 0 */
+       ret = readl_poll_timeout(reg_channel, val,
+                                ((val & MT6577_AUXADC_RDY0) == 0),
+                                MT6577_AUXADC_SLEEP_US,
+                                MT6577_AUXADC_TIMEOUT_US);
+       if (ret < 0) {
+               dev_err(indio_dev->dev.parent,
+                       "wait for channel[%d] ready bit clear time out\n",
+                       chan->channel);
+               goto err_timeout;
+       }
+
+       /* set bit to trigger sample */
+       mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+                             1 << chan->channel, 0);
+
+       /* we must delay here for hardware sample channel data */
+       udelay(MT6577_AUXADC_SAMPLE_READY_US);
+
+       /* check MTK_AUXADC_CON2 if auxadc is idle */
+       ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
+                                ((val & MT6577_AUXADC_STA) == 0),
+                                MT6577_AUXADC_SLEEP_US,
+                                MT6577_AUXADC_TIMEOUT_US);
+       if (ret < 0) {
+               dev_err(indio_dev->dev.parent,
+                       "wait for auxadc idle time out\n");
+               goto err_timeout;
+       }
+
+       /* read channel and make sure ready bit == 1 */
+       ret = readl_poll_timeout(reg_channel, val,
+                                ((val & MT6577_AUXADC_RDY0) != 0),
+                                MT6577_AUXADC_SLEEP_US,
+                                MT6577_AUXADC_TIMEOUT_US);
+       if (ret < 0) {
+               dev_err(indio_dev->dev.parent,
+                       "wait for channel[%d] data ready time out\n",
+                       chan->channel);
+               goto err_timeout;
+       }
+
+       /* read data */
+       val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
+
+       mutex_unlock(&adc_dev->lock);
+
+       return val;
+
+err_timeout:
+
+       mutex_unlock(&adc_dev->lock);
+
+       return -ETIMEDOUT;
+}
+
+static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
+                                 struct iio_chan_spec const *chan,
+                                 int *val,
+                                 int *val2,
+                                 long info)
+{
+       switch (info) {
+       case IIO_CHAN_INFO_PROCESSED:
+               *val = mt6577_auxadc_read(indio_dev, chan);
+               if (*val < 0) {
+                       dev_err(indio_dev->dev.parent,
+                               "failed to sample data on channel[%d]\n",
+                               chan->channel);
+                       return *val;
+               }
+               return IIO_VAL_INT;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info mt6577_auxadc_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = &mt6577_auxadc_read_raw,
+};
+
+static int mt6577_auxadc_probe(struct platform_device *pdev)
+{
+       struct mt6577_auxadc_device *adc_dev;
+       unsigned long adc_clk_rate;
+       struct resource *res;
+       struct iio_dev *indio_dev;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       adc_dev = iio_priv(indio_dev);
+       indio_dev->dev.parent = &pdev->dev;
+       indio_dev->name = dev_name(&pdev->dev);
+       indio_dev->info = &mt6577_auxadc_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = mt6577_auxadc_iio_channels;
+       indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(adc_dev->reg_base)) {
+               dev_err(&pdev->dev, "failed to get auxadc base address\n");
+               return PTR_ERR(adc_dev->reg_base);
+       }
+
+       adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
+       if (IS_ERR(adc_dev->adc_clk)) {
+               dev_err(&pdev->dev, "failed to get auxadc clock\n");
+               return PTR_ERR(adc_dev->adc_clk);
+       }
+
+       ret = clk_prepare_enable(adc_dev->adc_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable auxadc clock\n");
+               return ret;
+       }
+
+       adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+       if (!adc_clk_rate) {
+               ret = -EINVAL;
+               dev_err(&pdev->dev, "null clock rate\n");
+               goto err_disable_clk;
+       }
+
+       mutex_init(&adc_dev->lock);
+
+       mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+                             MT6577_AUXADC_PDN_EN, 0);
+       mdelay(MT6577_AUXADC_POWER_READY_MS);
+
+       platform_set_drvdata(pdev, indio_dev);
+
+       ret = iio_device_register(indio_dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register iio device\n");
+               goto err_power_off;
+       }
+
+       return 0;
+
+err_power_off:
+       mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+                             0, MT6577_AUXADC_PDN_EN);
+err_disable_clk:
+       clk_disable_unprepare(adc_dev->adc_clk);
+       return ret;
+}
+
+static int mt6577_auxadc_remove(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+
+       mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+                             0, MT6577_AUXADC_PDN_EN);
+
+       clk_disable_unprepare(adc_dev->adc_clk);
+
+       return 0;
+}
+
+static const struct of_device_id mt6577_auxadc_of_match[] = {
+       { .compatible = "mediatek,mt2701-auxadc", },
+       { .compatible = "mediatek,mt8173-auxadc", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
+
+static struct platform_driver mt6577_auxadc_driver = {
+       .driver = {
+               .name   = "mt6577-auxadc",
+               .of_match_table = mt6577_auxadc_of_match,
+       },
+       .probe  = mt6577_auxadc_probe,
+       .remove = mt6577_auxadc_remove,
+};
+module_platform_driver(mt6577_auxadc_driver);
+
+MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
+MODULE_DESCRIPTION("MTK AUXADC Device Driver");
+MODULE_LICENSE("GPL v2");
index db9b829ccf0daef20b6d9201deeba23e31b4222f..08f446695f976ce202b1c7e9542b4a120de5e1d1 100644 (file)
@@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
        if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
                st->conversion_count++;
        if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
-               complete_all(&st->value_ok);
+               complete(&st->value_ok);
 
        return IRQ_HANDLED;
 }
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
new file mode 100644 (file)
index 0000000..f94b69f
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
+ *
+ * ADC Devices Supported:
+ *  adc141s626 - 14-bit ADC
+ *  adc161s626 - 16-bit ADC
+ *
+ * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define TI_ADC_DRV_NAME        "ti-adc161s626"
+
+enum {
+       TI_ADC141S626,
+       TI_ADC161S626,
+};
+
+static const struct iio_chan_spec ti_adc141s626_channels[] = {
+       {
+               .type = IIO_VOLTAGE,
+               .channel = 0,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 14,
+                       .storagebits = 16,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec ti_adc161s626_channels[] = {
+       {
+               .type = IIO_VOLTAGE,
+               .channel = 0,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 16,
+                       .storagebits = 16,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+struct ti_adc_data {
+       struct iio_dev *indio_dev;
+       struct spi_device *spi;
+       u8 read_size;
+       u8 shift;
+
+       u8 buffer[16] ____cacheline_aligned;
+};
+
+static int ti_adc_read_measurement(struct ti_adc_data *data,
+                                  struct iio_chan_spec const *chan, int *val)
+{
+       int ret;
+
+       switch (data->read_size) {
+       case 2: {
+               __be16 buf;
+
+               ret = spi_read(data->spi, (void *) &buf, 2);
+               if (ret)
+                       return ret;
+
+               *val = be16_to_cpu(buf);
+               break;
+       }
+       case 3: {
+               __be32 buf;
+
+               ret = spi_read(data->spi, (void *) &buf, 3);
+               if (ret)
+                       return ret;
+
+               *val = be32_to_cpu(buf) >> 8;
+               break;
+       }
+       default:
+               return -EINVAL;
+       }
+
+       *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
+
+       return 0;
+}
+
+static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct ti_adc_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
+                                    (int *) &data->buffer);
+       if (!ret)
+               iio_push_to_buffers_with_timestamp(indio_dev,
+                                       data->buffer,
+                                       iio_get_time_ns(indio_dev));
+
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int ti_adc_read_raw(struct iio_dev *indio_dev,
+                          struct iio_chan_spec const *chan,
+                          int *val, int *val2, long mask)
+{
+       struct ti_adc_data *data = iio_priv(indio_dev);
+       int ret;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       ret = iio_device_claim_direct_mode(indio_dev);
+       if (ret)
+               return ret;
+
+       ret = ti_adc_read_measurement(data, chan, val);
+       iio_device_release_direct_mode(indio_dev);
+
+       if (!ret)
+               return IIO_VAL_INT;
+
+       return 0;
+}
+
+static const struct iio_info ti_adc_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = ti_adc_read_raw,
+};
+
+static int ti_adc_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct ti_adc_data *data;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       indio_dev->info = &ti_adc_info;
+       indio_dev->dev.parent = &spi->dev;
+       indio_dev->dev.of_node = spi->dev.of_node;
+       indio_dev->name = TI_ADC_DRV_NAME;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       spi_set_drvdata(spi, indio_dev);
+
+       data = iio_priv(indio_dev);
+       data->spi = spi;
+
+       switch (spi_get_device_id(spi)->driver_data) {
+       case TI_ADC141S626:
+               indio_dev->channels = ti_adc141s626_channels;
+               indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
+               data->shift = 0;
+               data->read_size = 2;
+               break;
+       case TI_ADC161S626:
+               indio_dev->channels = ti_adc161s626_channels;
+               indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
+               data->shift = 6;
+               data->read_size = 3;
+               break;
+       }
+
+       ret = iio_triggered_buffer_setup(indio_dev, NULL,
+                                        ti_adc_trigger_handler, NULL);
+       if (ret)
+               return ret;
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto error_unreg_buffer;
+
+       return 0;
+
+error_unreg_buffer:
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return ret;
+}
+
+static int ti_adc_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return 0;
+}
+
+static const struct of_device_id ti_adc_dt_ids[] = {
+       { .compatible = "ti,adc141s626", },
+       { .compatible = "ti,adc161s626", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
+
+static const struct spi_device_id ti_adc_id[] = {
+       {"adc141s626", TI_ADC141S626},
+       {"adc161s626", TI_ADC161S626},
+       {},
+};
+MODULE_DEVICE_TABLE(spi, ti_adc_id);
+
+static struct spi_driver ti_adc_driver = {
+       .driver = {
+               .name   = TI_ADC_DRV_NAME,
+               .of_match_table = of_match_ptr(ti_adc_dt_ids),
+       },
+       .probe          = ti_adc_probe,
+       .remove         = ti_adc_remove,
+       .id_table       = ti_adc_id,
+};
+module_spi_driver(ti_adc_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
+MODULE_LICENSE("GPL");
index 323079c3ccce6677492e0855a2e9e20d2a200217..b8f550e47d3da86119038d6e8fb4b32604b57ca7 100644 (file)
@@ -18,6 +18,7 @@ struct iio_cb_buffer {
        int (*cb)(const void *data, void *private);
        void *private;
        struct iio_channel *channels;
+       struct iio_dev *indio_dev;
 };
 
 static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
@@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 {
        int ret;
        struct iio_cb_buffer *cb_buff;
-       struct iio_dev *indio_dev;
        struct iio_channel *chan;
 
        cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
@@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
                goto error_free_cb_buff;
        }
 
-       indio_dev = cb_buff->channels[0].indio_dev;
+       cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
        cb_buff->buffer.scan_mask
-               = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
-                         GFP_KERNEL);
+               = kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
+                         sizeof(long), GFP_KERNEL);
        if (cb_buff->buffer.scan_mask == NULL) {
                ret = -ENOMEM;
                goto error_release_channels;
        }
        chan = &cb_buff->channels[0];
        while (chan->indio_dev) {
-               if (chan->indio_dev != indio_dev) {
+               if (chan->indio_dev != cb_buff->indio_dev) {
                        ret = -EINVAL;
                        goto error_free_scan_mask;
                }
@@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
 
 int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
 {
-       return iio_update_buffers(cb_buff->channels[0].indio_dev,
-                                 &cb_buff->buffer,
+       return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
                                  NULL);
 }
 EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
 
 void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
 {
-       iio_update_buffers(cb_buff->channels[0].indio_dev,
-                          NULL,
-                          &cb_buff->buffer);
+       iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer);
 }
 EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
 
@@ -133,6 +130,13 @@ struct iio_channel
 }
 EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
 
+struct iio_dev
+*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer)
+{
+       return cb_buffer->indio_dev;
+}
+EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
+
 MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
 MODULE_DESCRIPTION("Industrial I/O callback buffer");
 MODULE_LICENSE("GPL");
index 4bcc025e8c8a5a41c5918bbd86cfb3d668fd7969..cea7f9857a1f7ee920ef6db7eb4aaf8bca32980e 100644 (file)
@@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR
         Atlas Scientific OEM SM sensors:
            * pH SM sensor
            * EC SM sensor
+           * ORP SM sensor
 
         To compile this driver as module, choose M here: the
         module will be called atlas-ph-sensor.
index ae038a59d256c1c9f6ff89acb65819090e66a829..84fbff32b96d04eb90ba53d11573282e72911bd3 100644 (file)
 #define ATLAS_REG_TDS_DATA             0x1c
 #define ATLAS_REG_PSS_DATA             0x20
 
+#define ATLAS_REG_ORP_CALIB_STATUS     0x0d
+#define ATLAS_REG_ORP_DATA             0x0e
+
 #define ATLAS_PH_INT_TIME_IN_US                450000
 #define ATLAS_EC_INT_TIME_IN_US                650000
+#define ATLAS_ORP_INT_TIME_IN_US       450000
 
 enum {
        ATLAS_PH_SM,
        ATLAS_EC_SM,
+       ATLAS_ORP_SM,
 };
 
 struct atlas_data {
@@ -84,26 +89,10 @@ struct atlas_data {
        __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */
 };
 
-static const struct regmap_range atlas_volatile_ranges[] = {
-       regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
-       regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
-       regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4),
-};
-
-static const struct regmap_access_table atlas_volatile_table = {
-       .yes_ranges     = atlas_volatile_ranges,
-       .n_yes_ranges   = ARRAY_SIZE(atlas_volatile_ranges),
-};
-
 static const struct regmap_config atlas_regmap_config = {
        .name = ATLAS_REGMAP_NAME,
-
        .reg_bits = 8,
        .val_bits = 8,
-
-       .volatile_table = &atlas_volatile_table,
-       .max_register = ATLAS_REG_PSS_DATA + 4,
-       .cache_type = REGCACHE_RBTREE,
 };
 
 static const struct iio_chan_spec atlas_ph_channels[] = {
@@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = {
        },
 };
 
+static const struct iio_chan_spec atlas_orp_channels[] = {
+       {
+               .type = IIO_VOLTAGE,
+               .address = ATLAS_REG_ORP_DATA,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 32,
+                       .storagebits = 32,
+                       .endianness = IIO_BE,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
 static int atlas_check_ph_calibration(struct atlas_data *data)
 {
        struct device *dev = &data->client->dev;
@@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data)
        return 0;
 }
 
+static int atlas_check_orp_calibration(struct atlas_data *data)
+{
+       struct device *dev = &data->client->dev;
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val);
+       if (ret)
+               return ret;
+
+       if (!val)
+               dev_warn(dev, "device has not been calibrated\n");
+
+       return 0;
+};
+
 struct atlas_device {
        const struct iio_chan_spec *channels;
        int num_channels;
@@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = {
                                .calibration = &atlas_check_ec_calibration,
                                .delay = ATLAS_EC_INT_TIME_IN_US,
        },
-
+       [ATLAS_ORP_SM] = {
+                               .channels = atlas_orp_channels,
+                               .num_channels = 2,
+                               .data_reg = ATLAS_REG_ORP_DATA,
+                               .calibration = &atlas_check_orp_calibration,
+                               .delay = ATLAS_ORP_INT_TIME_IN_US,
+       },
 };
 
 static int atlas_set_powermode(struct atlas_data *data, int on)
@@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
                case IIO_PH:
                case IIO_CONCENTRATION:
                case IIO_ELECTRICALCONDUCTIVITY:
-                       mutex_lock(&indio_dev->mlock);
+               case IIO_VOLTAGE:
+                       ret = iio_device_claim_direct_mode(indio_dev);
+                       if (ret)
+                               return ret;
 
-                       if (iio_buffer_enabled(indio_dev))
-                               ret = -EBUSY;
-                       else
-                               ret = atlas_read_measurement(data,
-                                                       chan->address, &reg);
+                       ret = atlas_read_measurement(data, chan->address, &reg);
 
-                       mutex_unlock(&indio_dev->mlock);
+                       iio_device_release_direct_mode(indio_dev);
                        break;
                default:
                        ret = -EINVAL;
@@ -440,6 +467,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
                        *val = 0; /* 0.000000001 */
                        *val2 = 1000;
                        return IIO_VAL_INT_PLUS_NANO;
+               case IIO_VOLTAGE:
+                       *val = 1; /* 0.1 */
+                       *val2 = 10;
+                       break;
                default:
                        return -EINVAL;
                }
@@ -475,6 +506,7 @@ static const struct iio_info atlas_info = {
 static const struct i2c_device_id atlas_id[] = {
        { "atlas-ph-sm", ATLAS_PH_SM},
        { "atlas-ec-sm", ATLAS_EC_SM},
+       { "atlas-orp-sm", ATLAS_ORP_SM},
        {}
 };
 MODULE_DEVICE_TABLE(i2c, atlas_id);
@@ -482,6 +514,7 @@ MODULE_DEVICE_TABLE(i2c, atlas_id);
 static const struct of_device_id atlas_dt_ids[] = {
        { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
        { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
+       { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, },
        { }
 };
 MODULE_DEVICE_TABLE(of, atlas_dt_ids);
index 5b41f9d0d4f3d805b927a6f3bcee9cb85ece1e3f..5264ed6e03e5bca576d6e2db4734845e8c8b9c7d 100644 (file)
@@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
 #endif
 }
 
+static void hid_sensor_set_power_work(struct work_struct *work)
+{
+       struct hid_sensor_common *attrb = container_of(work,
+                                                      struct hid_sensor_common,
+                                                      work);
+       _hid_sensor_power_state(attrb, true);
+}
+
 static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
                                                bool state)
 {
@@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
 
 void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
 {
+       cancel_work_sync(&attrb->work);
        iio_trigger_unregister(attrb->trigger);
        iio_trigger_free(attrb->trigger);
 }
@@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
                goto error_unreg_trigger;
 
        iio_device_set_drvdata(indio_dev, attrb);
+
+       INIT_WORK(&attrb->work, hid_sensor_set_power_work);
+
        pm_suspend_ignore_children(&attrb->pdev->dev, true);
        pm_runtime_enable(&attrb->pdev->dev);
        /* Default to 3 seconds, but can be changed from sysfs */
@@ -202,7 +214,15 @@ static int hid_sensor_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
        struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+       schedule_work(&attrb->work);
+       return 0;
+}
 
+static int hid_sensor_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
        return _hid_sensor_power_state(attrb, true);
 }
 
@@ -211,7 +231,7 @@ static int hid_sensor_resume(struct device *dev)
 const struct dev_pm_ops hid_sensor_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
        SET_RUNTIME_PM_OPS(hid_sensor_suspend,
-                          hid_sensor_resume, NULL)
+                          hid_sensor_runtime_resume, NULL)
 };
 EXPORT_SYMBOL(hid_sensor_pm_ops);
 
index ca814479fadfa0773fd7bb580690b91b745d5349..b9f04424919473ffdef97b3653af761bb7db3707 100644 (file)
@@ -181,6 +181,15 @@ config AD7303
          To compile this driver as module choose M here: the module will be called
          ad7303.
 
+config CIO_DAC
+       tristate "Measurement Computing CIO-DAC IIO driver"
+       depends on X86 && ISA_BUS_API
+       help
+         Say yes here to build support for the Measurement Computing CIO-DAC
+         analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
+         base port addresses for the devices may be configured via the base
+         array module parameter.
+
 config LPC18XX_DAC
        tristate "NXP LPC18xx DAC driver"
        depends on ARCH_LPC18XX || COMPILE_TEST
index 8b78d5ca9b11c1d266f946e9ed144546cb9cb4af..b1a120612462195277aaf8407da2fcfd5fa20eab 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
 obj-$(CONFIG_AD5791) += ad5791.o
 obj-$(CONFIG_AD5686) += ad5686.o
 obj-$(CONFIG_AD7303) += ad7303.o
+obj-$(CONFIG_CIO_DAC) += cio-dac.o
 obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
 obj-$(CONFIG_M62332) += m62332.o
 obj-$(CONFIG_MAX517) += max517.o
diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c
new file mode 100644 (file)
index 0000000..5a743e2
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * IIO driver for the Measurement Computing CIO-DAC
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * This driver supports the following Measurement Computing devices: CIO-DAC16,
+ * CIO-DAC06, and PC104-DAC06.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#define CIO_DAC_NUM_CHAN 16
+
+#define CIO_DAC_CHAN(chan) {                           \
+       .type = IIO_VOLTAGE,                            \
+       .channel = chan,                                \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
+       .indexed = 1,                                   \
+       .output = 1                                     \
+}
+
+#define CIO_DAC_EXTENT 32
+
+static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
+static unsigned int num_cio_dac;
+module_param_array(base, uint, &num_cio_dac, 0);
+MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
+
+/**
+ * struct cio_dac_iio - IIO device private data structure
+ * @chan_out_states:   channels' output states
+ * @base:              base port address of the IIO device
+ */
+struct cio_dac_iio {
+       int chan_out_states[CIO_DAC_NUM_CHAN];
+       unsigned int base;
+};
+
+static int cio_dac_read_raw(struct iio_dev *indio_dev,
+       struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+       struct cio_dac_iio *const priv = iio_priv(indio_dev);
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       *val = priv->chan_out_states[chan->channel];
+
+       return IIO_VAL_INT;
+}
+
+static int cio_dac_write_raw(struct iio_dev *indio_dev,
+       struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+       struct cio_dac_iio *const priv = iio_priv(indio_dev);
+       const unsigned int chan_addr_offset = 2 * chan->channel;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       /* DAC can only accept up to a 16-bit value */
+       if ((unsigned int)val > 65535)
+               return -EINVAL;
+
+       priv->chan_out_states[chan->channel] = val;
+       outw(val, priv->base + chan_addr_offset);
+
+       return 0;
+}
+
+static const struct iio_info cio_dac_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = cio_dac_read_raw,
+       .write_raw = cio_dac_write_raw
+};
+
+static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
+       CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
+       CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
+       CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
+       CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
+};
+
+static int cio_dac_probe(struct device *dev, unsigned int id)
+{
+       struct iio_dev *indio_dev;
+       struct cio_dac_iio *priv;
+       unsigned int i;
+
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
+               dev_name(dev))) {
+               dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
+                       base[id], base[id] + CIO_DAC_EXTENT);
+               return -EBUSY;
+       }
+
+       indio_dev->info = &cio_dac_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = cio_dac_channels;
+       indio_dev->num_channels = CIO_DAC_NUM_CHAN;
+       indio_dev->name = dev_name(dev);
+
+       priv = iio_priv(indio_dev);
+       priv->base = base[id];
+
+       /* initialize DAC outputs to 0V */
+       for (i = 0; i < 32; i += 2)
+               outw(0, base[id] + i);
+
+       return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver cio_dac_driver = {
+       .probe = cio_dac_probe,
+       .driver = {
+               .name = "cio-dac"
+       }
+};
+
+module_isa_driver(cio_dac_driver, num_cio_dac);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
+MODULE_LICENSE("GPL v2");
index 738a86d9e4a9f8b17efc91a987a840169fe47f99..f155386286bf0e3efd48a45ed214b96b6bce7307 100644 (file)
@@ -26,11 +26,11 @@ config HDC100X
        tristate "TI HDC100x relative humidity and temperature sensor"
        depends on I2C
        help
-        Say yes here to build support for the TI HDC100x series of
-        relative humidity and temperature sensors.
+         Say yes here to build support for the Texas Instruments
+         HDC1000 and HDC1008 relative humidity and temperature sensors.
 
-        To compile this driver as a module, choose M here: the module
-        will be called hdc100x.
+         To compile this driver as a module, choose M here: the module
+         will be called hdc100x.
 
 config HTU21
        tristate "Measurement Specialties HTU21 humidity & temperature sensor"
index 7c566f516572957e7a6ab36764bc5ad79c16f058..28217478d82836bbd623348ce2a3830e8046f45d 100644 (file)
@@ -333,11 +333,11 @@ config US5182D
         will be called us5182d.
 
 config VCNL4000
-       tristate "VCNL4000 combined ALS and proximity sensor"
+       tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
        depends on I2C
        help
-        Say Y here if you want to build a driver for the Vishay VCNL4000
-        combined ambient light and proximity sensor.
+        Say Y here if you want to build a driver for the Vishay VCNL4000,
+        VCNL4010, VCNL4020 combined ambient light and proximity sensor.
 
         To compile this driver as a module, choose M here: the
         module will be called vcnl4000.
index 20c40f7809643342756db86060b98ea6536053e9..18cf2e29e4d5561c94d347ed535ebb9fdcf48788 100644 (file)
@@ -894,7 +894,7 @@ static int us5182d_probe(struct i2c_client *client,
                goto out_err;
 
        if (data->default_continuous) {
-               pm_runtime_set_active(&client->dev);
+               ret = pm_runtime_set_active(&client->dev);
                if (ret < 0)
                        goto out_err;
        }
index c9d85bbc9230059d4483567150b7838f01c65c54..360b6e98137acbfbd604e6492687f0b34f424de5 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
- * proximity sensor
+ * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
+ * light and proximity sensor
  *
  * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
  *
@@ -13,6 +13,8 @@
  * TODO:
  *   allow to adjust IR current
  *   proximity threshold and event handling
+ *   periodic ALS/proximity measurement (VCNL4010/20)
+ *   interrupts (VCNL4010/20)
  */
 
 #include <linux/module.h>
@@ -24,6 +26,8 @@
 #include <linux/iio/sysfs.h>
 
 #define VCNL4000_DRV_NAME "vcnl4000"
+#define VCNL4000_ID            0x01
+#define VCNL4010_ID            0x02 /* for VCNL4020, VCNL4010 */
 
 #define VCNL4000_COMMAND       0x80 /* Command register */
 #define VCNL4000_PROD_REV      0x81 /* Product ID and Revision ID */
 #define VCNL4000_PS_MOD_ADJ    0x8a /* Proximity modulator timing adjustment */
 
 /* Bit masks for COMMAND register */
-#define VCNL4000_AL_RDY                0x40 /* ALS data ready? */
-#define VCNL4000_PS_RDY                0x20 /* proximity data ready? */
-#define VCNL4000_AL_OD         0x10 /* start on-demand ALS measurement */
-#define VCNL4000_PS_OD         0x08 /* start on-demand proximity measurement */
+#define VCNL4000_AL_RDY                BIT(6) /* ALS data ready? */
+#define VCNL4000_PS_RDY                BIT(5) /* proximity data ready? */
+#define VCNL4000_AL_OD         BIT(4) /* start on-demand ALS measurement */
+#define VCNL4000_PS_OD         BIT(3) /* start on-demand proximity measurement */
 
 struct vcnl4000_data {
        struct i2c_client *client;
+       struct mutex lock;
 };
 
 static const struct i2c_device_id vcnl4000_id[] = {
@@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
        __be16 buf;
        int ret;
 
+       mutex_lock(&data->lock);
+
        ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
                                        req_mask);
        if (ret < 0)
-               return ret;
+               goto fail;
 
        /* wait for data to become ready */
        while (tries--) {
                ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
                if (ret < 0)
-                       return ret;
+                       goto fail;
                if (ret & rdy_mask)
                        break;
                msleep(20); /* measurement takes up to 100 ms */
@@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
        if (tries < 0) {
                dev_err(&data->client->dev,
                        "vcnl4000_measure() failed, data not ready\n");
-               return -EIO;
+               ret = -EIO;
+               goto fail;
        }
 
        ret = i2c_smbus_read_i2c_block_data(data->client,
                data_reg, sizeof(buf), (u8 *) &buf);
        if (ret < 0)
-               return ret;
+               goto fail;
 
+       mutex_unlock(&data->lock);
        *val = be16_to_cpu(buf);
 
        return 0;
+
+fail:
+       mutex_unlock(&data->lock);
+       return ret;
 }
 
 static const struct iio_chan_spec vcnl4000_channels[] = {
@@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
                                struct iio_chan_spec const *chan,
                                int *val, int *val2, long mask)
 {
-       int ret = -EINVAL;
+       int ret;
        struct vcnl4000_data *data = iio_priv(indio_dev);
 
        switch (mask) {
@@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
                                VCNL4000_AL_RESULT_HI, val);
                        if (ret < 0)
                                return ret;
-                       ret = IIO_VAL_INT;
-                       break;
+                       return IIO_VAL_INT;
                case IIO_PROXIMITY:
                        ret = vcnl4000_measure(data,
                                VCNL4000_PS_OD, VCNL4000_PS_RDY,
                                VCNL4000_PS_RESULT_HI, val);
                        if (ret < 0)
                                return ret;
-                       ret = IIO_VAL_INT;
-                       break;
+                       return IIO_VAL_INT;
                default:
-                       break;
+                       return -EINVAL;
                }
-               break;
        case IIO_CHAN_INFO_SCALE:
-               if (chan->type == IIO_LIGHT) {
-                       *val = 0;
-                       *val2 = 250000;
-                       ret = IIO_VAL_INT_PLUS_MICRO;
-               }
-               break;
+               if (chan->type != IIO_LIGHT)
+                       return -EINVAL;
+
+               *val = 0;
+               *val2 = 250000;
+               return IIO_VAL_INT_PLUS_MICRO;
        default:
-               break;
+               return -EINVAL;
        }
-
-       return ret;
 }
 
 static const struct iio_info vcnl4000_info = {
@@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client,
 {
        struct vcnl4000_data *data;
        struct iio_dev *indio_dev;
-       int ret;
+       int ret, prod_id;
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
        if (!indio_dev)
@@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client,
        data = iio_priv(indio_dev);
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
+       mutex_init(&data->lock);
 
        ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
        if (ret < 0)
                return ret;
 
-       dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
-               ret >> 4, ret & 0xf);
+       prod_id = ret >> 4;
+       if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
+               return -ENODEV;
+
+       dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
+               (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
+               ret & 0xf);
 
        indio_dev->dev.parent = &client->dev;
        indio_dev->info = &vcnl4000_info;
index 1f842abcb4a4db4ff8b9b24159819f2f9a241274..421ad90a5fbe13b7b2969bcbcc5196a91c01d77f 100644 (file)
@@ -5,8 +5,22 @@
 
 menu "Magnetometer sensors"
 
+config AK8974
+       tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
+       depends on I2C
+       depends on OF
+       select REGMAP_I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         Say yes here to build support for Asahi Kasei AK8974 or
+         AMI305 I2C-based 3-axis magnetometer chips.
+
+         To compile this driver as a module, choose M here: the module
+         will be called ak8974.
+
 config AK8975
-       tristate "Asahi Kasei AK 3-Axis Magnetometer"
+       tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
        depends on I2C
        depends on GPIOLIB || COMPILE_TEST
        select IIO_BUFFER
index 92a745c9a6e8d5ee483e7f53e602de4dbfcb6c4b..b86d6cb7f285146fe9358e3a3427d705e611fb58 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 # When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AK8974)   += ak8974.o
 obj-$(CONFIG_AK8975)   += ak8975.o
 obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
 obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c
new file mode 100644 (file)
index 0000000..e70e4e2
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * Driver for the Asahi Kasei EMD Corporation AK8974
+ * and Aichi Steel AMI305 magnetometer chips.
+ * Based on a patch from Samu Onkalo and the AK8975 IIO driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Samu Onkalo <samu.p.onkalo@nokia.com>
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h> /* For irq_get_irq_data() */
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/*
+ * 16-bit registers are little-endian. LSB is at the address defined below
+ * and MSB is at the next higher address.
+ */
+
+/* These registers are common for AK8974 and AMI305 */
+#define AK8974_SELFTEST                0x0C
+#define AK8974_SELFTEST_IDLE   0x55
+#define AK8974_SELFTEST_OK     0xAA
+
+#define AK8974_INFO            0x0D
+
+#define AK8974_WHOAMI          0x0F
+#define AK8974_WHOAMI_VALUE_AMI305 0x47
+#define AK8974_WHOAMI_VALUE_AK8974 0x48
+
+#define AK8974_DATA_X          0x10
+#define AK8974_DATA_Y          0x12
+#define AK8974_DATA_Z          0x14
+#define AK8974_INT_SRC         0x16
+#define AK8974_STATUS          0x18
+#define AK8974_INT_CLEAR       0x1A
+#define AK8974_CTRL1           0x1B
+#define AK8974_CTRL2           0x1C
+#define AK8974_CTRL3           0x1D
+#define AK8974_INT_CTRL                0x1E
+#define AK8974_INT_THRES       0x26  /* Absolute any axis value threshold */
+#define AK8974_PRESET          0x30
+
+/* AK8974-specific offsets */
+#define AK8974_OFFSET_X                0x20
+#define AK8974_OFFSET_Y                0x22
+#define AK8974_OFFSET_Z                0x24
+/* AMI305-specific offsets */
+#define AMI305_OFFSET_X                0x6C
+#define AMI305_OFFSET_Y                0x72
+#define AMI305_OFFSET_Z                0x78
+
+/* Different temperature registers */
+#define AK8974_TEMP            0x31
+#define AMI305_TEMP            0x60
+
+#define AK8974_INT_X_HIGH      BIT(7) /* Axis over +threshold  */
+#define AK8974_INT_Y_HIGH      BIT(6)
+#define AK8974_INT_Z_HIGH      BIT(5)
+#define AK8974_INT_X_LOW       BIT(4) /* Axis below -threshold */
+#define AK8974_INT_Y_LOW       BIT(3)
+#define AK8974_INT_Z_LOW       BIT(2)
+#define AK8974_INT_RANGE       BIT(1) /* Range overflow (any axis) */
+
+#define AK8974_STATUS_DRDY     BIT(6) /* Data ready */
+#define AK8974_STATUS_OVERRUN  BIT(5) /* Data overrun */
+#define AK8974_STATUS_INT      BIT(4) /* Interrupt occurred */
+
+#define AK8974_CTRL1_POWER     BIT(7) /* 0 = standby; 1 = active */
+#define AK8974_CTRL1_RATE      BIT(4) /* 0 = 10 Hz; 1 = 20 Hz   */
+#define AK8974_CTRL1_FORCE_EN  BIT(1) /* 0 = normal; 1 = force  */
+#define AK8974_CTRL1_MODE2     BIT(0) /* 0 */
+
+#define AK8974_CTRL2_INT_EN    BIT(4)  /* 1 = enable interrupts              */
+#define AK8974_CTRL2_DRDY_EN   BIT(3)  /* 1 = enable data ready signal */
+#define AK8974_CTRL2_DRDY_POL  BIT(2)  /* 1 = data ready active high   */
+#define AK8974_CTRL2_RESDEF    (AK8974_CTRL2_DRDY_POL)
+
+#define AK8974_CTRL3_RESET     BIT(7) /* Software reset                  */
+#define AK8974_CTRL3_FORCE     BIT(6) /* Start forced measurement */
+#define AK8974_CTRL3_SELFTEST  BIT(4) /* Set selftest register   */
+#define AK8974_CTRL3_RESDEF    0x00
+
+#define AK8974_INT_CTRL_XEN    BIT(7) /* Enable interrupt for this axis */
+#define AK8974_INT_CTRL_YEN    BIT(6)
+#define AK8974_INT_CTRL_ZEN    BIT(5)
+#define AK8974_INT_CTRL_XYZEN  (BIT(7)|BIT(6)|BIT(5))
+#define AK8974_INT_CTRL_POL    BIT(3) /* 0 = active low; 1 = active high */
+#define AK8974_INT_CTRL_PULSE  BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
+#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
+
+/* The AMI305 has elaborate FW version and serial number registers */
+#define AMI305_VER             0xE8
+#define AMI305_SN              0xEA
+
+#define AK8974_MAX_RANGE       2048
+
+#define AK8974_POWERON_DELAY   50
+#define AK8974_ACTIVATE_DELAY  1
+#define AK8974_SELFTEST_DELAY  1
+/*
+ * Set the autosuspend to two orders of magnitude larger than the poweron
+ * delay to make sane reasonable power tradeoff savings (5 seconds in
+ * this case).
+ */
+#define AK8974_AUTOSUSPEND_DELAY 5000
+
+#define AK8974_MEASTIME                3
+
+#define AK8974_PWR_ON          1
+#define AK8974_PWR_OFF         0
+
+/**
+ * struct ak8974 - state container for the AK8974 driver
+ * @i2c: parent I2C client
+ * @orientation: mounting matrix, flipped axis etc
+ * @map: regmap to access the AK8974 registers over I2C
+ * @regs: the avdd and dvdd power regulators
+ * @name: the name of the part
+ * @variant: the whoami ID value (for selecting code paths)
+ * @lock: locks the magnetometer for exclusive use during a measurement
+ * @drdy_irq: uses the DRDY IRQ line
+ * @drdy_complete: completion for DRDY
+ * @drdy_active_low: the DRDY IRQ is active low
+ */
+struct ak8974 {
+       struct i2c_client *i2c;
+       struct iio_mount_matrix orientation;
+       struct regmap *map;
+       struct regulator_bulk_data regs[2];
+       const char *name;
+       u8 variant;
+       struct mutex lock;
+       bool drdy_irq;
+       struct completion drdy_complete;
+       bool drdy_active_low;
+};
+
+static const char ak8974_reg_avdd[] = "avdd";
+static const char ak8974_reg_dvdd[] = "dvdd";
+
+static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
+{
+       int ret;
+       u8 val;
+
+       val = mode ? AK8974_CTRL1_POWER : 0;
+       val |= AK8974_CTRL1_FORCE_EN;
+       ret = regmap_write(ak8974->map, AK8974_CTRL1, val);
+       if (ret < 0)
+               return ret;
+
+       if (mode)
+               msleep(AK8974_ACTIVATE_DELAY);
+
+       return 0;
+}
+
+static int ak8974_reset(struct ak8974 *ak8974)
+{
+       int ret;
+
+       /* Power on to get register access. Sets CTRL1 reg to reset state */
+       ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+       if (ret)
+               return ret;
+       ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF);
+       if (ret)
+               return ret;
+       ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
+       if (ret)
+               return ret;
+       ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
+                          AK8974_INT_CTRL_RESDEF);
+       if (ret)
+               return ret;
+
+       /* After reset, power off is default state */
+       return ak8974_set_power(ak8974, AK8974_PWR_OFF);
+}
+
+static int ak8974_configure(struct ak8974 *ak8974)
+{
+       int ret;
+
+       ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
+                          AK8974_CTRL2_INT_EN);
+       if (ret)
+               return ret;
+       ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
+       if (ret)
+               return ret;
+       ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
+       if (ret)
+               return ret;
+
+       return regmap_write(ak8974->map, AK8974_PRESET, 0);
+}
+
+static int ak8974_trigmeas(struct ak8974 *ak8974)
+{
+       unsigned int clear;
+       u8 mask;
+       u8 val;
+       int ret;
+
+       /* Clear any previous measurement overflow status */
+       ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear);
+       if (ret)
+               return ret;
+
+       /* If we have a DRDY IRQ line, use it */
+       if (ak8974->drdy_irq) {
+               mask = AK8974_CTRL2_INT_EN |
+                       AK8974_CTRL2_DRDY_EN |
+                       AK8974_CTRL2_DRDY_POL;
+               val = AK8974_CTRL2_DRDY_EN;
+
+               if (!ak8974->drdy_active_low)
+                       val |= AK8974_CTRL2_DRDY_POL;
+
+               init_completion(&ak8974->drdy_complete);
+               ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
+                                        mask, val);
+               if (ret)
+                       return ret;
+       }
+
+       /* Force a measurement */
+       return regmap_update_bits(ak8974->map,
+                                 AK8974_CTRL3,
+                                 AK8974_CTRL3_FORCE,
+                                 AK8974_CTRL3_FORCE);
+}
+
+static int ak8974_await_drdy(struct ak8974 *ak8974)
+{
+       int timeout = 2;
+       unsigned int val;
+       int ret;
+
+       if (ak8974->drdy_irq) {
+               ret = wait_for_completion_timeout(&ak8974->drdy_complete,
+                                       1 + msecs_to_jiffies(1000));
+               if (!ret) {
+                       dev_err(&ak8974->i2c->dev,
+                               "timeout waiting for DRDY IRQ\n");
+                       return -ETIMEDOUT;
+               }
+               return 0;
+       }
+
+       /* Default delay-based poll loop */
+       do {
+               msleep(AK8974_MEASTIME);
+               ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+               if (ret < 0)
+                       return ret;
+               if (val & AK8974_STATUS_DRDY)
+                       return 0;
+       } while (--timeout);
+       if (!timeout) {
+               dev_err(&ak8974->i2c->dev,
+                       "timeout waiting for DRDY\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
+{
+       unsigned int src;
+       int ret;
+
+       ret = ak8974_await_drdy(ak8974);
+       if (ret)
+               return ret;
+       ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src);
+       if (ret < 0)
+               return ret;
+
+       /* Out of range overflow! Strong magnet close? */
+       if (src & AK8974_INT_RANGE) {
+               dev_err(&ak8974->i2c->dev,
+                       "range overflow in sensor\n");
+               return -ERANGE;
+       }
+
+       ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static irqreturn_t ak8974_drdy_irq(int irq, void *d)
+{
+       struct ak8974 *ak8974 = d;
+
+       if (!ak8974->drdy_irq)
+               return IRQ_NONE;
+
+       /* TODO: timestamp here to get good measurement stamps */
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d)
+{
+       struct ak8974 *ak8974 = d;
+       unsigned int val;
+       int ret;
+
+       /* Check if this was a DRDY from us */
+       ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+       if (ret < 0) {
+               dev_err(&ak8974->i2c->dev, "error reading DRDY status\n");
+               return IRQ_HANDLED;
+       }
+       if (val & AK8974_STATUS_DRDY) {
+               /* Yes this was our IRQ */
+               complete(&ak8974->drdy_complete);
+               return IRQ_HANDLED;
+       }
+
+       /* We may be on a shared IRQ, let the next client check */
+       return IRQ_NONE;
+}
+
+static int ak8974_selftest(struct ak8974 *ak8974)
+{
+       struct device *dev = &ak8974->i2c->dev;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+       if (ret)
+               return ret;
+       if (val != AK8974_SELFTEST_IDLE) {
+               dev_err(dev, "selftest not idle before test\n");
+               return -EIO;
+       }
+
+       /* Trigger self-test */
+       ret = regmap_update_bits(ak8974->map,
+                       AK8974_CTRL3,
+                       AK8974_CTRL3_SELFTEST,
+                       AK8974_CTRL3_SELFTEST);
+       if (ret) {
+               dev_err(dev, "could not write CTRL3\n");
+               return ret;
+       }
+
+       msleep(AK8974_SELFTEST_DELAY);
+
+       ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+       if (ret)
+               return ret;
+       if (val != AK8974_SELFTEST_OK) {
+               dev_err(dev, "selftest result NOT OK (%02x)\n", val);
+               return -EIO;
+       }
+
+       ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+       if (ret)
+               return ret;
+       if (val != AK8974_SELFTEST_IDLE) {
+               dev_err(dev, "selftest not idle after test (%02x)\n", val);
+               return -EIO;
+       }
+       dev_dbg(dev, "passed self-test\n");
+
+       return 0;
+}
+
+static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
+{
+       int ret;
+       u16 bulk;
+
+       ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
+       if (ret)
+               return ret;
+       *val = le16_to_cpu(bulk);
+
+       return 0;
+}
+
+static int ak8974_detect(struct ak8974 *ak8974)
+{
+       unsigned int whoami;
+       const char *name;
+       int ret;
+       unsigned int fw;
+       u16 sn;
+
+       ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami);
+       if (ret)
+               return ret;
+
+       switch (whoami) {
+       case AK8974_WHOAMI_VALUE_AMI305:
+               name = "ami305";
+               ret = regmap_read(ak8974->map, AMI305_VER, &fw);
+               if (ret)
+                       return ret;
+               fw &= 0x7f; /* only bits 0 thru 6 valid */
+               ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn);
+               if (ret)
+                       return ret;
+               dev_info(&ak8974->i2c->dev,
+                        "detected %s, FW ver %02x, S/N: %04x\n",
+                        name, fw, sn);
+               break;
+       case AK8974_WHOAMI_VALUE_AK8974:
+               name = "ak8974";
+               dev_info(&ak8974->i2c->dev, "detected AK8974\n");
+               break;
+       default:
+               dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
+                       whoami);
+               return -ENODEV;
+       }
+
+       ak8974->name = name;
+       ak8974->variant = whoami;
+
+       return 0;
+}
+
+static int ak8974_read_raw(struct iio_dev *indio_dev,
+                          struct iio_chan_spec const *chan,
+                          int *val, int *val2,
+                          long mask)
+{
+       struct ak8974 *ak8974 = iio_priv(indio_dev);
+       s16 hw_values[3];
+       int ret = -EINVAL;
+
+       pm_runtime_get_sync(&ak8974->i2c->dev);
+       mutex_lock(&ak8974->lock);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (chan->address > 2) {
+                       dev_err(&ak8974->i2c->dev, "faulty channel address\n");
+                       ret = -EIO;
+                       goto out_unlock;
+               }
+               ret = ak8974_trigmeas(ak8974);
+               if (ret)
+                       goto out_unlock;
+               ret = ak8974_getresult(ak8974, hw_values);
+               if (ret)
+                       goto out_unlock;
+
+               /*
+                * We read all axes and discard all but one, for optimized
+                * reading, use the triggered buffer.
+                */
+               *val = le16_to_cpu(hw_values[chan->address]);
+
+               ret = IIO_VAL_INT;
+       }
+
+ out_unlock:
+       mutex_unlock(&ak8974->lock);
+       pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+       pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+
+       return ret;
+}
+
+static void ak8974_fill_buffer(struct iio_dev *indio_dev)
+{
+       struct ak8974 *ak8974 = iio_priv(indio_dev);
+       int ret;
+       s16 hw_values[8]; /* Three axes + 64bit padding */
+
+       pm_runtime_get_sync(&ak8974->i2c->dev);
+       mutex_lock(&ak8974->lock);
+
+       ret = ak8974_trigmeas(ak8974);
+       if (ret) {
+               dev_err(&ak8974->i2c->dev, "error triggering measure\n");
+               goto out_unlock;
+       }
+       ret = ak8974_getresult(ak8974, hw_values);
+       if (ret) {
+               dev_err(&ak8974->i2c->dev, "error getting measures\n");
+               goto out_unlock;
+       }
+
+       iio_push_to_buffers_with_timestamp(indio_dev, hw_values,
+                                          iio_get_time_ns(indio_dev));
+
+ out_unlock:
+       mutex_unlock(&ak8974->lock);
+       pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+       pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+}
+
+static irqreturn_t ak8974_handle_trigger(int irq, void *p)
+{
+       const struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+
+       ak8974_fill_buffer(indio_dev);
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static const struct iio_mount_matrix *
+ak8974_get_mount_matrix(const struct iio_dev *indio_dev,
+                       const struct iio_chan_spec *chan)
+{
+       struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+       return &ak8974->orientation;
+}
+
+static const struct iio_chan_spec_ext_info ak8974_ext_info[] = {
+       IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix),
+       { },
+};
+
+#define AK8974_AXIS_CHANNEL(axis, index)                               \
+       {                                                               \
+               .type = IIO_MAGN,                                       \
+               .modified = 1,                                          \
+               .channel2 = IIO_MOD_##axis,                             \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+               .ext_info = ak8974_ext_info,                            \
+               .address = index,                                       \
+               .scan_index = index,                                    \
+               .scan_type = {                                          \
+                       .sign = 's',                                    \
+                       .realbits = 16,                                 \
+                       .storagebits = 16,                              \
+                       .endianness = IIO_LE                            \
+               },                                                      \
+       }
+
+static const struct iio_chan_spec ak8974_channels[] = {
+       AK8974_AXIS_CHANNEL(X, 0),
+       AK8974_AXIS_CHANNEL(Y, 1),
+       AK8974_AXIS_CHANNEL(Z, 2),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long ak8974_scan_masks[] = { 0x7, 0 };
+
+static const struct iio_info ak8974_info = {
+       .read_raw = &ak8974_read_raw,
+       .driver_module = THIS_MODULE,
+};
+
+static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
+{
+       struct i2c_client *i2c = to_i2c_client(dev);
+       struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+       struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+       switch (reg) {
+       case AK8974_CTRL1:
+       case AK8974_CTRL2:
+       case AK8974_CTRL3:
+       case AK8974_INT_CTRL:
+       case AK8974_INT_THRES:
+       case AK8974_INT_THRES + 1:
+       case AK8974_PRESET:
+       case AK8974_PRESET + 1:
+               return true;
+       case AK8974_OFFSET_X:
+       case AK8974_OFFSET_X + 1:
+       case AK8974_OFFSET_Y:
+       case AK8974_OFFSET_Y + 1:
+       case AK8974_OFFSET_Z:
+       case AK8974_OFFSET_Z + 1:
+               if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
+                       return true;
+               return false;
+       case AMI305_OFFSET_X:
+       case AMI305_OFFSET_X + 1:
+       case AMI305_OFFSET_Y:
+       case AMI305_OFFSET_Y + 1:
+       case AMI305_OFFSET_Z:
+       case AMI305_OFFSET_Z + 1:
+               if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305)
+                       return true;
+               return false;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config ak8974_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0xff,
+       .writeable_reg = ak8974_writeable_reg,
+};
+
+static int ak8974_probe(struct i2c_client *i2c,
+                       const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct ak8974 *ak8974;
+       unsigned long irq_trig;
+       int irq = i2c->irq;
+       int ret;
+
+       /* Register with IIO */
+       indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974));
+       if (indio_dev == NULL)
+               return -ENOMEM;
+
+       ak8974 = iio_priv(indio_dev);
+       i2c_set_clientdata(i2c, indio_dev);
+       ak8974->i2c = i2c;
+       mutex_init(&ak8974->lock);
+
+       ret = of_iio_read_mount_matrix(&i2c->dev,
+                                      "mount-matrix",
+                                      &ak8974->orientation);
+       if (ret)
+               return ret;
+
+       ak8974->regs[0].supply = ak8974_reg_avdd;
+       ak8974->regs[1].supply = ak8974_reg_dvdd;
+
+       ret = devm_regulator_bulk_get(&i2c->dev,
+                                     ARRAY_SIZE(ak8974->regs),
+                                     ak8974->regs);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "cannot get regulators\n");
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "cannot enable regulators\n");
+               return ret;
+       }
+
+       /* Take runtime PM online */
+       pm_runtime_get_noresume(&i2c->dev);
+       pm_runtime_set_active(&i2c->dev);
+       pm_runtime_enable(&i2c->dev);
+
+       ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config);
+       if (IS_ERR(ak8974->map)) {
+               dev_err(&i2c->dev, "failed to allocate register map\n");
+               return PTR_ERR(ak8974->map);
+       }
+
+       ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+       if (ret) {
+               dev_err(&i2c->dev, "could not power on\n");
+               goto power_off;
+       }
+
+       ret = ak8974_detect(ak8974);
+       if (ret) {
+               dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n");
+               goto power_off;
+       }
+
+       ret = ak8974_selftest(ak8974);
+       if (ret)
+               dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
+
+       ret = ak8974_reset(ak8974);
+       if (ret) {
+               dev_err(&i2c->dev, "AK8974 reset failed\n");
+               goto power_off;
+       }
+
+       pm_runtime_set_autosuspend_delay(&i2c->dev,
+                                        AK8974_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(&i2c->dev);
+       pm_runtime_put(&i2c->dev);
+
+       indio_dev->dev.parent = &i2c->dev;
+       indio_dev->channels = ak8974_channels;
+       indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
+       indio_dev->info = &ak8974_info;
+       indio_dev->available_scan_masks = ak8974_scan_masks;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->name = ak8974->name;
+
+       ret = iio_triggered_buffer_setup(indio_dev, NULL,
+                                        ak8974_handle_trigger,
+                                        NULL);
+       if (ret) {
+               dev_err(&i2c->dev, "triggered buffer setup failed\n");
+               goto disable_pm;
+       }
+
+       /* If we have a valid DRDY IRQ, make use of it */
+       if (irq > 0) {
+               irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+               if (irq_trig == IRQF_TRIGGER_RISING) {
+                       dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n");
+               } else if (irq_trig == IRQF_TRIGGER_FALLING) {
+                       ak8974->drdy_active_low = true;
+                       dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n");
+               } else {
+                       irq_trig = IRQF_TRIGGER_RISING;
+               }
+               irq_trig |= IRQF_ONESHOT;
+               irq_trig |= IRQF_SHARED;
+
+               ret = devm_request_threaded_irq(&i2c->dev,
+                                               irq,
+                                               ak8974_drdy_irq,
+                                               ak8974_drdy_irq_thread,
+                                               irq_trig,
+                                               ak8974->name,
+                                               ak8974);
+               if (ret) {
+                       dev_err(&i2c->dev, "unable to request DRDY IRQ "
+                               "- proceeding without IRQ\n");
+                       goto no_irq;
+               }
+               ak8974->drdy_irq = true;
+       }
+
+no_irq:
+       ret = iio_device_register(indio_dev);
+       if (ret) {
+               dev_err(&i2c->dev, "device register failed\n");
+               goto cleanup_buffer;
+       }
+
+       return 0;
+
+cleanup_buffer:
+       iio_triggered_buffer_cleanup(indio_dev);
+disable_pm:
+       pm_runtime_put_noidle(&i2c->dev);
+       pm_runtime_disable(&i2c->dev);
+       ak8974_set_power(ak8974, AK8974_PWR_OFF);
+power_off:
+       regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+       return ret;
+}
+
+static int __exit ak8974_remove(struct i2c_client *i2c)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+       struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+       pm_runtime_get_sync(&i2c->dev);
+       pm_runtime_put_noidle(&i2c->dev);
+       pm_runtime_disable(&i2c->dev);
+       ak8974_set_power(ak8974, AK8974_PWR_OFF);
+       regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak8974_runtime_suspend(struct device *dev)
+{
+       struct ak8974 *ak8974 =
+               iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+       ak8974_set_power(ak8974, AK8974_PWR_OFF);
+       regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+       return 0;
+}
+
+static int ak8974_runtime_resume(struct device *dev)
+{
+       struct ak8974 *ak8974 =
+               iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+       int ret;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+       if (ret)
+               return ret;
+       msleep(AK8974_POWERON_DELAY);
+       ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+       if (ret)
+               goto out_regulator_disable;
+
+       ret = ak8974_configure(ak8974);
+       if (ret)
+               goto out_disable_power;
+
+       return 0;
+
+out_disable_power:
+       ak8974_set_power(ak8974, AK8974_PWR_OFF);
+out_regulator_disable:
+       regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+       return ret;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ak8974_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(ak8974_runtime_suspend,
+                          ak8974_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id ak8974_id[] = {
+       {"ami305", 0 },
+       {"ak8974", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, ak8974_id);
+
+static const struct of_device_id ak8974_of_match[] = {
+       { .compatible = "asahi-kasei,ak8974", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, ak8974_of_match);
+
+static struct i2c_driver ak8974_driver = {
+       .driver  = {
+               .name   = "ak8974",
+               .owner  = THIS_MODULE,
+               .pm = &ak8974_dev_pm_ops,
+               .of_match_table = of_match_ptr(ak8974_of_match),
+       },
+       .probe    = ak8974_probe,
+       .remove   = __exit_p(ak8974_remove),
+       .id_table = ak8974_id,
+};
+module_i2c_driver(ak8974_driver);
+
+MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver");
+MODULE_AUTHOR("Samu Onkalo");
+MODULE_AUTHOR("Linus Walleij");
+MODULE_LICENSE("GPL v2");
index f2be4a04905665426a198bfda9628beaa365d7cb..f2b3bd7bf862114633519070da0901f48b2157e0 100644 (file)
@@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
-               if (iio_buffer_enabled(indio_dev))
-                       return -EBUSY;
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
 
                switch (chan->type) {
                case IIO_MAGN: /* in 0.1 uT / LSB */
                        ret = mag3110_read(data, buffer);
                        if (ret < 0)
-                               return ret;
+                               goto release;
                        *val = sign_extend32(
                                be16_to_cpu(buffer[chan->scan_index]), 15);
-                       return IIO_VAL_INT;
+                       ret = IIO_VAL_INT;
+                       break;
                case IIO_TEMP: /* in 1 C / LSB */
                        mutex_lock(&data->lock);
                        ret = mag3110_request(data);
                        if (ret < 0) {
                                mutex_unlock(&data->lock);
-                               return ret;
+                               goto release;
                        }
                        ret = i2c_smbus_read_byte_data(data->client,
                                MAG3110_DIE_TEMP);
                        mutex_unlock(&data->lock);
                        if (ret < 0)
-                               return ret;
+                               goto release;
                        *val = sign_extend32(ret, 7);
-                       return IIO_VAL_INT;
+                       ret = IIO_VAL_INT;
+                       break;
                default:
-                       return -EINVAL;
+                       ret = -EINVAL;
                }
+release:
+               iio_device_release_direct_mode(indio_dev);
+               return ret;
+
        case IIO_CHAN_INFO_SCALE:
                switch (chan->type) {
                case IIO_MAGN:
index 1d74b3aafeedbcc7a7c44dc8abd8505424b17932..6f84f53dfe541550af8fef6b5c1d804f27709aa3 100644 (file)
@@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
                sx9500_push_events(indio_dev);
 
        if (val & SX9500_CONVDONE_IRQ)
-               complete_all(&data->completion);
+               complete(&data->completion);
 
 out:
        mutex_unlock(&data->mutex);
index c4664e5de791577e117a00f2fa276897d343e7a7..5ea77a7e261da87488b257123e152c5278bb41c9 100644 (file)
@@ -3,6 +3,22 @@
 #
 menu "Temperature sensors"
 
+config MAXIM_THERMOCOUPLE
+       tristate "Maxim thermocouple sensors"
+       depends on SPI
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         If you say yes here you get support for the Maxim series of
+         thermocouple sensors connected via SPI.
+
+         Supported sensors:
+          * MAX6675
+          * MAX31855
+
+         This driver can also be built as a module. If so, the module will
+         be called maxim_thermocouple.
+
 config MLX90614
        tristate "MLX90614 contact-less infrared sensor"
        depends on I2C
index 02bc79d49b24cf83bb284b36d23bf18f7ebbbfa8..78c3de0dc3f0d002402a06a7a9d471e8d402036a 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for industrial I/O temperature drivers
 #
 
+obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
 obj-$(CONFIG_MLX90614) += mlx90614.o
 obj-$(CONFIG_TMP006) += tmp006.o
 obj-$(CONFIG_TSYS01) += tsys01.o
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
new file mode 100644 (file)
index 0000000..030827e
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * maxim_thermocouple.c  - Support for Maxim thermocouple chips
+ *
+ * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MAXIM_THERMOCOUPLE_DRV_NAME    "maxim_thermocouple"
+
+enum {
+       MAX6675,
+       MAX31855,
+};
+
+const struct iio_chan_spec max6675_channels[] = {
+       {       /* thermocouple temperature */
+               .type = IIO_TEMP,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 13,
+                       .storagebits = 16,
+                       .shift = 3,
+                       .endianness = IIO_BE,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+const struct iio_chan_spec max31855_channels[] = {
+       {       /* thermocouple temperature */
+               .type = IIO_TEMP,
+               .address = 2,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 14,
+                       .storagebits = 16,
+                       .shift = 2,
+                       .endianness = IIO_BE,
+               },
+       },
+       {       /* cold junction temperature */
+               .type = IIO_TEMP,
+               .address = 0,
+               .channel2 = IIO_MOD_TEMP_AMBIENT,
+               .modified = 1,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 1,
+               .scan_type = {
+                       .sign = 's',
+                       .realbits = 12,
+                       .storagebits = 16,
+                       .shift = 4,
+                       .endianness = IIO_BE,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const unsigned long max31855_scan_masks[] = {0x3, 0};
+
+struct maxim_thermocouple_chip {
+       const struct iio_chan_spec *channels;
+       const unsigned long *scan_masks;
+       u8 num_channels;
+       u8 read_size;
+
+       /* bit-check for valid input */
+       u32 status_bit;
+};
+
+const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = {
+       [MAX6675] = {
+                       .channels = max6675_channels,
+                       .num_channels = ARRAY_SIZE(max6675_channels),
+                       .read_size = 2,
+                       .status_bit = BIT(2),
+               },
+       [MAX31855] = {
+                       .channels = max31855_channels,
+                       .num_channels = ARRAY_SIZE(max31855_channels),
+                       .read_size = 4,
+                       .scan_masks = max31855_scan_masks,
+                       .status_bit = BIT(16),
+               },
+};
+
+struct maxim_thermocouple_data {
+       struct spi_device *spi;
+       const struct maxim_thermocouple_chip *chip;
+
+       u8 buffer[16] ____cacheline_aligned;
+};
+
+static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
+                                  struct iio_chan_spec const *chan, int *val)
+{
+       unsigned int storage_bytes = data->chip->read_size;
+       unsigned int shift = chan->scan_type.shift + (chan->address * 8);
+       unsigned int buf;
+       int ret;
+
+       ret = spi_read(data->spi, (void *) &buf, storage_bytes);
+       if (ret)
+               return ret;
+
+       switch (storage_bytes) {
+       case 2:
+               *val = be16_to_cpu(buf);
+               break;
+       case 4:
+               *val = be32_to_cpu(buf);
+               break;
+       }
+
+       /* check to be sure this is a valid reading */
+       if (*val & data->chip->status_bit)
+               return -EINVAL;
+
+       *val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1);
+
+       return 0;
+}
+
+static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct maxim_thermocouple_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = spi_read(data->spi, data->buffer, data->chip->read_size);
+       if (!ret) {
+               iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                                                  iio_get_time_ns(indio_dev));
+       }
+
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev,
+                                      struct iio_chan_spec const *chan,
+                                      int *val, int *val2, long mask)
+{
+       struct maxim_thermocouple_data *data = iio_priv(indio_dev);
+       int ret = -EINVAL;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
+
+               ret = maxim_thermocouple_read(data, chan, val);
+               iio_device_release_direct_mode(indio_dev);
+
+               if (!ret)
+                       return IIO_VAL_INT;
+
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->channel2) {
+               case IIO_MOD_TEMP_AMBIENT:
+                       *val = 62;
+                       *val2 = 500000; /* 1000 * 0.0625 */
+                       ret = IIO_VAL_INT_PLUS_MICRO;
+                       break;
+               default:
+                       *val = 250; /* 1000 * 0.25 */
+                       ret = IIO_VAL_INT;
+               };
+               break;
+       }
+
+       return ret;
+}
+
+static const struct iio_info maxim_thermocouple_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = maxim_thermocouple_read_raw,
+};
+
+static int maxim_thermocouple_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+       struct iio_dev *indio_dev;
+       struct maxim_thermocouple_data *data;
+       const struct maxim_thermocouple_chip *chip =
+                       &maxim_thermocouple_chips[id->driver_data];
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       indio_dev->info = &maxim_thermocouple_info;
+       indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME;
+       indio_dev->channels = chip->channels;
+       indio_dev->available_scan_masks = chip->scan_masks;
+       indio_dev->num_channels = chip->num_channels;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+
+       data = iio_priv(indio_dev);
+       data->spi = spi;
+       data->chip = chip;
+
+       ret = iio_triggered_buffer_setup(indio_dev, NULL,
+                               maxim_thermocouple_trigger_handler, NULL);
+       if (ret)
+               return ret;
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto error_unreg_buffer;
+
+       return 0;
+
+error_unreg_buffer:
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return ret;
+}
+
+static int maxim_thermocouple_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return 0;
+}
+
+static const struct spi_device_id maxim_thermocouple_id[] = {
+       {"max6675", MAX6675},
+       {"max31855", MAX31855},
+       {},
+};
+MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
+
+static struct spi_driver maxim_thermocouple_driver = {
+       .driver = {
+               .name   = MAXIM_THERMOCOUPLE_DRV_NAME,
+       },
+       .probe          = maxim_thermocouple_probe,
+       .remove         = maxim_thermocouple_remove,
+       .id_table       = maxim_thermocouple_id,
+};
+module_spi_driver(maxim_thermocouple_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("Maxim thermocouple sensors");
+MODULE_LICENSE("GPL");
index 76d9f74e7dcbfc9cfe6dd503f5e1bdf3bae37ba3..f47a17d7f6b3ec4421750cd9ea2851a220caab33 100644 (file)
 #include <linux/iio/sysfs.h>
 #include <linux/acpi.h>
 
-#define CONVERSION_TIME_MS             100
+#define ISL29018_CONV_TIME_MS          100
 
 #define ISL29018_REG_ADD_COMMAND1      0x00
-#define COMMMAND1_OPMODE_SHIFT         5
-#define COMMMAND1_OPMODE_MASK          (7 << COMMMAND1_OPMODE_SHIFT)
-#define COMMMAND1_OPMODE_POWER_DOWN    0
-#define COMMMAND1_OPMODE_ALS_ONCE      1
-#define COMMMAND1_OPMODE_IR_ONCE       2
-#define COMMMAND1_OPMODE_PROX_ONCE     3
+#define ISL29018_CMD1_OPMODE_SHIFT     5
+#define ISL29018_CMD1_OPMODE_MASK      (7 << ISL29018_CMD1_OPMODE_SHIFT)
+#define ISL29018_CMD1_OPMODE_POWER_DOWN        0
+#define ISL29018_CMD1_OPMODE_ALS_ONCE  1
+#define ISL29018_CMD1_OPMODE_IR_ONCE   2
+#define ISL29018_CMD1_OPMODE_PROX_ONCE 3
 
-#define ISL29018_REG_ADD_COMMANDII     0x01
-#define COMMANDII_RESOLUTION_SHIFT     2
-#define COMMANDII_RESOLUTION_MASK      (0x3 << COMMANDII_RESOLUTION_SHIFT)
+#define ISL29018_REG_ADD_COMMAND     0x01
+#define ISL29018_CMD2_RESOLUTION_SHIFT 2
+#define ISL29018_CMD2_RESOLUTION_MASK  (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
 
-#define COMMANDII_RANGE_SHIFT          0
-#define COMMANDII_RANGE_MASK           (0x3 << COMMANDII_RANGE_SHIFT)
+#define ISL29018_CMD2_RANGE_SHIFT      0
+#define ISL29018_CMD2_RANGE_MASK       (0x3 << ISL29018_CMD2_RANGE_SHIFT)
 
-#define COMMANDII_SCHEME_SHIFT         7
-#define COMMANDII_SCHEME_MASK          (0x1 << COMMANDII_SCHEME_SHIFT)
+#define ISL29018_CMD2_SCHEME_SHIFT     7
+#define ISL29018_CMD2_SCHEME_MASK      (0x1 << ISL29018_CMD2_SCHEME_SHIFT)
 
 #define ISL29018_REG_ADD_DATA_LSB      0x02
 #define ISL29018_REG_ADD_DATA_MSB      0x03
@@ -127,13 +127,13 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip,
        if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
                return -EINVAL;
 
-       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
-                                COMMANDII_RESOLUTION_MASK,
-                                i << COMMANDII_RESOLUTION_SHIFT);
+       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+                                ISL29018_CMD2_RESOLUTION_MASK,
+                                i << ISL29018_CMD2_RESOLUTION_SHIFT);
        if (ret < 0)
                return ret;
 
-       /* keep the same range when integration time changes */
+       /* Keep the same range when integration time changes */
        int_time = chip->int_time;
        for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
                if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
@@ -163,9 +163,9 @@ static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
        if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
                return -EINVAL;
 
-       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
-                                COMMANDII_RANGE_MASK,
-                                i << COMMANDII_RANGE_SHIFT);
+       ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+                                ISL29018_CMD2_RANGE_MASK,
+                                i << ISL29018_CMD2_RANGE_SHIFT);
        if (ret < 0)
                return ret;
 
@@ -183,13 +183,13 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
 
        /* Set mode */
        status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
-                             mode << COMMMAND1_OPMODE_SHIFT);
+                             mode << ISL29018_CMD1_OPMODE_SHIFT);
        if (status) {
                dev_err(dev,
                        "Error in setting operating mode err %d\n", status);
                return status;
        }
-       msleep(CONVERSION_TIME_MS);
+       msleep(ISL29018_CONV_TIME_MS);
        status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
        if (status < 0) {
                dev_err(dev,
@@ -213,8 +213,8 @@ static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
        int lux_data;
        unsigned int data_x_range;
 
-       lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
-
+       lux_data = isl29018_read_sensor_input(chip,
+                                             ISL29018_CMD1_OPMODE_ALS_ONCE);
        if (lux_data < 0)
                return lux_data;
 
@@ -230,8 +230,8 @@ static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
 {
        int ir_data;
 
-       ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
-
+       ir_data = isl29018_read_sensor_input(chip,
+                                            ISL29018_CMD1_OPMODE_IR_ONCE);
        if (ir_data < 0)
                return ir_data;
 
@@ -249,16 +249,16 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
        struct device *dev = regmap_get_device(chip->regmap);
 
        /* Do proximity sensing with required scheme */
-       status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
-                                   COMMANDII_SCHEME_MASK,
-                                   scheme << COMMANDII_SCHEME_SHIFT);
+       status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+                                   ISL29018_CMD2_SCHEME_MASK,
+                                   scheme << ISL29018_CMD2_SCHEME_SHIFT);
        if (status) {
                dev_err(dev, "Error in setting operating mode\n");
                return status;
        }
 
        prox_data = isl29018_read_sensor_input(chip,
-                                              COMMMAND1_OPMODE_PROX_ONCE);
+                                              ISL29018_CMD1_OPMODE_PROX_ONCE);
        if (prox_data < 0)
                return prox_data;
 
@@ -267,8 +267,8 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
                return 0;
        }
 
-       ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
-
+       ir_data = isl29018_read_sensor_input(chip,
+                                            ISL29018_CMD1_OPMODE_IR_ONCE);
        if (ir_data < 0)
                return ir_data;
 
@@ -280,7 +280,7 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
        return 0;
 }
 
-static ssize_t show_scale_available(struct device *dev,
+static ssize_t isl29018_show_scale_available(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -297,7 +297,7 @@ static ssize_t show_scale_available(struct device *dev,
        return len;
 }
 
-static ssize_t show_int_time_available(struct device *dev,
+static ssize_t isl29018_show_int_time_available(struct device *dev,
                                       struct device_attribute *attr, char *buf)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -313,8 +313,7 @@ static ssize_t show_int_time_available(struct device *dev,
        return len;
 }
 
-/* proximity scheme */
-static ssize_t show_prox_infrared_suppression(struct device *dev,
+static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
                                              struct device_attribute *attr,
                                              char *buf)
 {
@@ -322,13 +321,13 @@ static ssize_t show_prox_infrared_suppression(struct device *dev,
        struct isl29018_chip *chip = iio_priv(indio_dev);
 
        /*
-        * return the "proximity scheme" i.e. if the chip does on chip
+        * Return the "proximity scheme" i.e. if the chip does on chip
         * infrared suppression (1 means perform on chip suppression)
         */
        return sprintf(buf, "%d\n", chip->prox_scheme);
 }
 
-static ssize_t store_prox_infrared_suppression(struct device *dev,
+static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev,
                                               struct device_attribute *attr,
                                               const char *buf, size_t count)
 {
@@ -338,13 +337,11 @@ static ssize_t store_prox_infrared_suppression(struct device *dev,
 
        if (kstrtoint(buf, 10, &val))
                return -EINVAL;
-       if (!(val == 0 || val == 1)) {
-               dev_err(dev, "The mode is not supported\n");
+       if (!(val == 0 || val == 1))
                return -EINVAL;
-       }
 
        /*
-        * get the  "proximity scheme" i.e. if the chip does on chip
+        * Get the "proximity scheme" i.e. if the chip does on chip
         * infrared suppression (1 means perform on chip suppression)
         */
        mutex_lock(&chip->lock);
@@ -354,7 +351,6 @@ static ssize_t store_prox_infrared_suppression(struct device *dev,
        return count;
 }
 
-/* Channel IO */
 static int isl29018_write_raw(struct iio_dev *indio_dev,
                              struct iio_chan_spec const *chan,
                              int val,
@@ -491,13 +487,13 @@ static const struct iio_chan_spec isl29023_channels[] = {
 };
 
 static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
-                      show_int_time_available, NULL, 0);
+                      isl29018_show_int_time_available, NULL, 0);
 static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
-                     show_scale_available, NULL, 0);
+                     isl29018_show_scale_available, NULL, 0);
 static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
                                        S_IRUGO | S_IWUSR,
-                                       show_prox_infrared_suppression,
-                                       store_prox_infrared_suppression, 0);
+                                       isl29018_show_prox_infrared_suppression,
+                                       isl29018_store_prox_infrared_suppression, 0);
 
 #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 
@@ -541,7 +537,7 @@ static int isl29035_detect(struct isl29018_chip *chip)
        if (id != ISL29035_DEVICE_ID)
                return -ENODEV;
 
-       /* clear out brownout bit */
+       /* Clear brownout bit */
        return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
                                  ISL29035_BOUT_MASK, 0);
 }
@@ -574,7 +570,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
         * conversions, clear the test registers, and then rewrite all
         * registers to the desired values.
         * ...
-        * FOR ISL29011, ISL29018, ISL29021, ISL29023
+        * For ISL29011, ISL29018, ISL29021, ISL29023
         * 1. Write 0x00 to register 0x08 (TEST)
         * 2. Write 0x00 to register 0x00 (CMD1)
         * 3. Rewrite all registers to the desired values
@@ -603,7 +599,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
 
        usleep_range(1000, 2000);       /* per data sheet, page 10 */
 
-       /* set defaults */
+       /* Set defaults */
        status = isl29018_set_scale(chip, chip->scale.scale,
                                    chip->scale.uscale);
        if (status < 0) {
@@ -635,7 +631,7 @@ static const struct iio_info isl29023_info = {
        .write_raw = isl29018_write_raw,
 };
 
-static bool is_volatile_reg(struct device *dev, unsigned int reg)
+static bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case ISL29018_REG_ADD_DATA_LSB:
@@ -649,37 +645,32 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg)
        }
 }
 
-/*
- * isl29018_regmap_config: regmap configuration.
- * Use RBTREE mechanism for caching.
- */
 static const struct regmap_config isl29018_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
-       .volatile_reg = is_volatile_reg,
+       .volatile_reg = isl29018_is_volatile_reg,
        .max_register = ISL29018_REG_TEST,
        .num_reg_defaults_raw = ISL29018_REG_TEST + 1,
        .cache_type = REGCACHE_RBTREE,
 };
 
-/* isl29035_regmap_config: regmap configuration for ISL29035 */
 static const struct regmap_config isl29035_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
-       .volatile_reg = is_volatile_reg,
+       .volatile_reg = isl29018_is_volatile_reg,
        .max_register = ISL29035_REG_DEVICE_ID,
        .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
        .cache_type = REGCACHE_RBTREE,
 };
 
-struct chip_info {
+struct isl29018_chip_info {
        const struct iio_chan_spec *channels;
        int num_channels;
        const struct iio_info *indio_info;
        const struct regmap_config *regmap_cfg;
 };
 
-static const struct chip_info chip_info_tbl[] = {
+static const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
        [isl29018] = {
                .channels = isl29018_channels,
                .num_channels = ARRAY_SIZE(isl29018_channels),
@@ -724,10 +715,8 @@ static int isl29018_probe(struct i2c_client *client,
        int dev_id = 0;
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
-       if (!indio_dev) {
-               dev_err(&client->dev, "iio allocation fails\n");
+       if (!indio_dev)
                return -ENOMEM;
-       }
        chip = iio_priv(indio_dev);
 
        i2c_set_clientdata(client, indio_dev);
@@ -750,7 +739,7 @@ static int isl29018_probe(struct i2c_client *client,
        chip->suspended = false;
 
        chip->regmap = devm_regmap_init_i2c(client,
-                               chip_info_tbl[dev_id].regmap_cfg);
+                               isl29018_chip_info_tbl[dev_id].regmap_cfg);
        if (IS_ERR(chip->regmap)) {
                err = PTR_ERR(chip->regmap);
                dev_err(&client->dev, "regmap initialization fails: %d\n", err);
@@ -761,19 +750,13 @@ static int isl29018_probe(struct i2c_client *client,
        if (err)
                return err;
 
-       indio_dev->info = chip_info_tbl[dev_id].indio_info;
-       indio_dev->channels = chip_info_tbl[dev_id].channels;
-       indio_dev->num_channels = chip_info_tbl[dev_id].num_channels;
+       indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info;
+       indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
+       indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
        indio_dev->name = name;
        indio_dev->dev.parent = &client->dev;
        indio_dev->modes = INDIO_DIRECT_MODE;
-       err = devm_iio_device_register(&client->dev, indio_dev);
-       if (err) {
-               dev_err(&client->dev, "iio registration fails\n");
-               return err;
-       }
-
-       return 0;
+       return devm_iio_device_register(&client->dev, indio_dev);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -840,7 +823,6 @@ static const struct of_device_id isl29018_of_match[] = {
 MODULE_DEVICE_TABLE(of, isl29018_of_match);
 
 static struct i2c_driver isl29018_driver = {
-       .class  = I2C_CLASS_HWMON,
        .driver  = {
                        .name = "isl29018",
                        .acpi_match_table = ACPI_PTR(isl29018_acpi_match),
index 2e3b1d64e32ad7b45795f0ea056ac3909d332668..aa413e5878b9d82986f5fdd096100b5262ff1b30 100644 (file)
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 
-#define CONVERSION_TIME_MS             100
+#define ISL29028_CONV_TIME_MS          100
 
 #define ISL29028_REG_CONFIGURE         0x01
 
-#define CONFIGURE_ALS_IR_MODE_ALS      0
-#define CONFIGURE_ALS_IR_MODE_IR       BIT(0)
-#define CONFIGURE_ALS_IR_MODE_MASK     BIT(0)
+#define ISL29028_CONF_ALS_IR_MODE_ALS  0
+#define ISL29028_CONF_ALS_IR_MODE_IR   BIT(0)
+#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0)
 
-#define CONFIGURE_ALS_RANGE_LOW_LUX    0
-#define CONFIGURE_ALS_RANGE_HIGH_LUX   BIT(1)
-#define CONFIGURE_ALS_RANGE_MASK       BIT(1)
+#define ISL29028_CONF_ALS_RANGE_LOW_LUX        0
+#define ISL29028_CONF_ALS_RANGE_HIGH_LUX       BIT(1)
+#define ISL29028_CONF_ALS_RANGE_MASK   BIT(1)
 
-#define CONFIGURE_ALS_DIS              0
-#define CONFIGURE_ALS_EN               BIT(2)
-#define CONFIGURE_ALS_EN_MASK          BIT(2)
+#define ISL29028_CONF_ALS_DIS          0
+#define ISL29028_CONF_ALS_EN           BIT(2)
+#define ISL29028_CONF_ALS_EN_MASK      BIT(2)
 
-#define CONFIGURE_PROX_DRIVE           BIT(3)
+#define ISL29028_CONF_PROX_SLP_SH      4
+#define ISL29028_CONF_PROX_SLP_MASK    (7 << ISL29028_CONF_PROX_SLP_SH)
 
-#define CONFIGURE_PROX_SLP_SH          4
-#define CONFIGURE_PROX_SLP_MASK                (7 << CONFIGURE_PROX_SLP_SH)
-
-#define CONFIGURE_PROX_EN              BIT(7)
-#define CONFIGURE_PROX_EN_MASK         BIT(7)
+#define ISL29028_CONF_PROX_EN          BIT(7)
+#define ISL29028_CONF_PROX_EN_MASK     BIT(7)
 
 #define ISL29028_REG_INTERRUPT         0x02
 
 
 #define ISL29028_NUM_REGS              (ISL29028_REG_TEST2_MODE + 1)
 
-enum als_ir_mode {
-       MODE_NONE = 0,
-       MODE_ALS,
-       MODE_IR
+enum isl29028_als_ir_mode {
+       ISL29028_MODE_NONE = 0,
+       ISL29028_MODE_ALS,
+       ISL29028_MODE_IR,
 };
 
 struct isl29028_chip {
@@ -76,7 +74,7 @@ struct isl29028_chip {
        bool                    enable_prox;
 
        int                     lux_scale;
-       int                     als_ir_mode;
+       enum isl29028_als_ir_mode       als_ir_mode;
 };
 
 static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
@@ -91,7 +89,8 @@ static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
                        break;
        }
        return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                       CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH);
+                                 ISL29028_CONF_PROX_SLP_MASK,
+                                 sel << ISL29028_CONF_PROX_SLP_SH);
 }
 
 static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
@@ -100,9 +99,9 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
        int val = 0;
 
        if (enable)
-               val = CONFIGURE_PROX_EN;
+               val = ISL29028_CONF_PROX_EN;
        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                CONFIGURE_PROX_EN_MASK, val);
+                                ISL29028_CONF_PROX_EN_MASK, val);
        if (ret < 0)
                return ret;
 
@@ -113,40 +112,40 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
 
 static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
 {
-       int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX :
-                                       CONFIGURE_ALS_RANGE_LOW_LUX;
+       int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
+                                       ISL29028_CONF_ALS_RANGE_LOW_LUX;
 
        return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-               CONFIGURE_ALS_RANGE_MASK, val);
+               ISL29028_CONF_ALS_RANGE_MASK, val);
 }
 
 static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
-                                   enum als_ir_mode mode)
+                                   enum isl29028_als_ir_mode mode)
 {
        int ret = 0;
 
        switch (mode) {
-       case MODE_ALS:
+       case ISL29028_MODE_ALS:
                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                        CONFIGURE_ALS_IR_MODE_MASK,
-                                        CONFIGURE_ALS_IR_MODE_ALS);
+                                        ISL29028_CONF_ALS_IR_MODE_MASK,
+                                        ISL29028_CONF_ALS_IR_MODE_ALS);
                if (ret < 0)
                        return ret;
 
                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                        CONFIGURE_ALS_RANGE_MASK,
-                                        CONFIGURE_ALS_RANGE_HIGH_LUX);
+                                        ISL29028_CONF_ALS_RANGE_MASK,
+                                        ISL29028_CONF_ALS_RANGE_HIGH_LUX);
                break;
 
-       case MODE_IR:
+       case ISL29028_MODE_IR:
                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                        CONFIGURE_ALS_IR_MODE_MASK,
-                                        CONFIGURE_ALS_IR_MODE_IR);
+                                        ISL29028_CONF_ALS_IR_MODE_MASK,
+                                        ISL29028_CONF_ALS_IR_MODE_IR);
                break;
 
-       case MODE_NONE:
+       case ISL29028_MODE_NONE:
                return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                       CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS);
+                       ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS);
        }
 
        if (ret < 0)
@@ -154,12 +153,13 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
 
        /* Enable the ALS/IR */
        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
-                                CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN);
+                                ISL29028_CONF_ALS_EN_MASK,
+                                ISL29028_CONF_ALS_EN);
        if (ret < 0)
                return ret;
 
        /* Need to wait for conversion time if ALS/IR mode enabled */
-       mdelay(CONVERSION_TIME_MS);
+       mdelay(ISL29028_CONV_TIME_MS);
        return 0;
 }
 
@@ -223,14 +223,14 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
        int ret;
        int als_ir_data;
 
-       if (chip->als_ir_mode != MODE_ALS) {
-               ret = isl29028_set_als_ir_mode(chip, MODE_ALS);
+       if (chip->als_ir_mode != ISL29028_MODE_ALS) {
+               ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
                if (ret < 0) {
                        dev_err(dev,
                                "Error in enabling ALS mode err %d\n", ret);
                        return ret;
                }
-               chip->als_ir_mode = MODE_ALS;
+               chip->als_ir_mode = ISL29028_MODE_ALS;
        }
 
        ret = isl29028_read_als_ir(chip, &als_ir_data);
@@ -256,14 +256,14 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
        struct device *dev = regmap_get_device(chip->regmap);
        int ret;
 
-       if (chip->als_ir_mode != MODE_IR) {
-               ret = isl29028_set_als_ir_mode(chip, MODE_IR);
+       if (chip->als_ir_mode != ISL29028_MODE_IR) {
+               ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
                if (ret < 0) {
                        dev_err(dev,
                                "Error in enabling IR mode err %d\n", ret);
                        return ret;
                }
-               chip->als_ir_mode = MODE_IR;
+               chip->als_ir_mode = ISL29028_MODE_IR;
        }
        return isl29028_read_als_ir(chip, ir_data);
 }
@@ -383,8 +383,8 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
 }
 
 static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
-                               "1, 3, 5, 10, 13, 20, 83, 100");
-static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000");
+                               "1 3 5 10 13 20 83 100");
+static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000");
 
 #define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
@@ -428,7 +428,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip)
        chip->enable_prox  = false;
        chip->prox_sampling = 20;
        chip->lux_scale = 2000;
-       chip->als_ir_mode = MODE_NONE;
+       chip->als_ir_mode = ISL29028_MODE_NONE;
 
        ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
        if (ret < 0) {
@@ -462,7 +462,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip)
        return ret;
 }
 
-static bool is_volatile_reg(struct device *dev, unsigned int reg)
+static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case ISL29028_REG_INTERRUPT:
@@ -478,7 +478,7 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg)
 static const struct regmap_config isl29028_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
-       .volatile_reg = is_volatile_reg,
+       .volatile_reg = isl29028_is_volatile_reg,
        .max_register = ISL29028_NUM_REGS - 1,
        .num_reg_defaults_raw = ISL29028_NUM_REGS,
        .cache_type = REGCACHE_RBTREE,
@@ -546,7 +546,6 @@ static const struct of_device_id isl29028_of_match[] = {
 MODULE_DEVICE_TABLE(of, isl29028_of_match);
 
 static struct i2c_driver isl29028_driver = {
-       .class  = I2C_CLASS_HWMON,
        .driver  = {
                .name = "isl29028",
                .of_match_table = isl29028_of_match,
index 75e8685e6df23b7fc4232d1e61010a148093f0e9..24edbc39ab4e7fb3a3ec80d1329e426158f506fa 100644 (file)
@@ -23,8 +23,8 @@
 #include "ade7854.h"
 
 static ssize_t ade7854_read_8bit(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
+                                struct device_attribute *attr,
+                                char *buf)
 {
        int ret;
        u8 val = 0;
@@ -40,8 +40,8 @@ static ssize_t ade7854_read_8bit(struct device *dev,
 }
 
 static ssize_t ade7854_read_16bit(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
+                                 struct device_attribute *attr,
+                                 char *buf)
 {
        int ret;
        u16 val = 0;
@@ -57,8 +57,8 @@ static ssize_t ade7854_read_16bit(struct device *dev,
 }
 
 static ssize_t ade7854_read_24bit(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
+                                 struct device_attribute *attr,
+                                 char *buf)
 {
        int ret;
        u32 val;
@@ -74,8 +74,8 @@ static ssize_t ade7854_read_24bit(struct device *dev,
 }
 
 static ssize_t ade7854_read_32bit(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
+                                 struct device_attribute *attr,
+                                 char *buf)
 {
        int ret;
        u32 val = 0;
@@ -91,9 +91,9 @@ static ssize_t ade7854_read_32bit(struct device *dev,
 }
 
 static ssize_t ade7854_write_8bit(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t len)
+                                 struct device_attribute *attr,
+                                 const char *buf,
+                                 size_t len)
 {
        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -112,9 +112,9 @@ error_ret:
 }
 
 static ssize_t ade7854_write_16bit(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t len)
+                                  struct device_attribute *attr,
+                                  const char *buf,
+                                  size_t len)
 {
        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -133,9 +133,9 @@ error_ret:
 }
 
 static ssize_t ade7854_write_24bit(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t len)
+                                  struct device_attribute *attr,
+                                  const char *buf,
+                                  size_t len)
 {
        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -154,9 +154,9 @@ error_ret:
 }
 
 static ssize_t ade7854_write_32bit(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t len)
+                                  struct device_attribute *attr,
+                                  const char *buf,
+                                  size_t len)
 {
        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
index c02b5ce6c5cdb787c3cb5ddb1ca7652dc5fe9d1e..dd85f3503410c16c7bfa98c44f5b13edcda1f4e2 100644 (file)
@@ -236,6 +236,7 @@ struct hid_sensor_common {
        struct hid_sensor_hub_attribute_info report_state;
        struct hid_sensor_hub_attribute_info power_state;
        struct hid_sensor_hub_attribute_info sensitivity;
+       struct work_struct work;
 };
 
 /* Convert from hid unit expo to regular exponent */
index 3d672f72e7ecc7ad1218c9888c6f805d0d0c4589..9edccfba1ffbbae780dfe278eb6a1c48f2be2b43 100644 (file)
@@ -164,6 +164,18 @@ void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff);
 struct iio_channel
 *iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer);
 
+/**
+ * iio_channel_cb_get_iio_dev() - get access to the underlying device.
+ * @cb_buffer:         The callback buffer from whom we want the device
+ *                     information.
+ *
+ * This function allows one to obtain information about the device.
+ * The primary aim is to allow drivers that are consuming a device to query
+ * things like current trigger.
+ */
+struct iio_dev
+*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer);
+
 /**
  * iio_read_channel_raw() - read from a given channel
  * @chan:              The channel being queried.
index 3d650e668252f8f195a8e6a14380117d60b329cc..ab0f5cf1602592c7b5e8f9846657d6234208851b 100644 (file)
@@ -51,7 +51,8 @@ static int dump_channels(const char *dev_dir_name)
 
        while (ent = readdir(dp), ent)
                if (check_prefix(ent->d_name, "in_") &&
-                   check_postfix(ent->d_name, "_raw"))
+                  (check_postfix(ent->d_name, "_raw") ||
+                   check_postfix(ent->d_name, "_input")))
                        printf("   %-10s\n", ent->d_name);
 
        return (closedir(dp) == -1) ? -errno : 0;