Merge tag 'iio-for-4.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Sep 2015 01:37:48 +0000 (03:37 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Sep 2015 01:37:48 +0000 (03:37 +0200)
Jonathan writes:

First round of new driver, new functionality and cleanups for IIO in the 4.4 cycle

New device support
* APDS9960 ALS + proximity driver
* bmg160 SPI devices.
* HDC100x humidity sensors
* Holt HI-8435 threshold detector
* mma8453Q accelerometer added to the mma8452 driver
* mma86452FC and mma8653FC accelerometers added to the mma8452 driver
* mxc4005 accelerometer
* PulsedLight LIDAR
* SensorTech VZ89x volatile organic compound sensor
* UPISEMI uS5182d ALS and proximity sensors

New core functionality
* triggered events - use triggers to check for changes in threshold type
  detectors on devices with out interrupt support.  First user is the holt
  comparator.
* chemical concentration and resistance channel types.

New driver functionality
* vf610
  - buffer support.
  - followup coccinelle warning fix.

Core rework
* buffers
  - break out callback buffer to own module.
  - move buffer implementations to a new subdirectory
* percolate the error code form iio_event_getfd out to userspace
  rather than giving a missleading error later on.

Cleanups
* adddac drivers
  - use BIT macro where appropriate.
* meter drivers
  - use BIT macro where appropriate.

* ad7303
 - add an OF match table to line up with the binding docs.
* adc128s052
  - add an OF match table to line up with the binding docs.
adf4350
  - add an OF match table to line up with the binding docs
* as3935
  - add an OF match table to line up with the binding docs.
* berlin2-adc
  - use GENMASK and BIT for masks
  - prevent attempting to sample multiple channels at once by moving a
    mutex scop
  - coding style cleanups
* bmg150_magn
  - kconfig sort order was wrong - fix it.
* bmg160
  - use i2c regmap and drop all uses of i2c_client
  - separate i2c and core driver
* cc10001_adc
  - kconfig sort order was wrong - fix it.
* evgen (dummy driver helper module)
  - move interrupt generation to irq_work to reduce differences between
    the dummy driver and real hardware drivers.
* hmc5843
  - set the name dynamically rather than to a fixed value for one of the
    suported parts.
  - export module alias information to allow autoprobing of module.
* lpc32xx
  - on failure to get resource or irq return -ENXIO as uppose to -EBUSY
* max1027
  - set .of_match_table to actually allow OF style matching.
* max5821
  - add MODULE_DEVICE_TABLE for OF table.
* mma8452
  - refactor to separate out chip specific data.
  - add freefall / motion interrupt source for devices that do their
    interrupts slightly differently.
  - update copywrite notice.
  - leave naming of events directory in sysfs to the core
* mcp320x
  - set .of_match_table so that it can be use for OF style matching.
* mlx90614
  - Implement filter configuration (note the datasheet changed as a result
    of the driver reviews to include the values we needed ;)
* opt3001
  - drop .owner field as assigned by platform driver core.
* si7020
  - replace a bitmask on the humidity values with a more correct range
    check.
* stk310
  - improved error handling.
  - use BIT macro where appropriate and use the resulting defines
    instead of magic numbers in the code.
  - fix indentation
* st-sensors
  - add debugfs register read hook
* tsl4531
  - fix error handling in check_id
* twl6030
  - fix module autoload for OF
* iio-trig-sysfs
  - document add and remove attribute
* trigger in staging
  - code alignment fixes.
  - braces on both branches of if statement if needed for one.
* xilinx-xadc
  - push interrupts into hardirq context as there isn't much in them
    any more and it avoids breaking PREEMPT_RT builds due to the use
    of a spinlock between the hardirq and the thread.

Tools
* event-monitor
  - report unsupported events.  We keep expanding what can come from drivers
    so give a helpful error if one turns up in an out of date userspace
    program.
* generic-buffer
  - helpful message about needing to enable a channel to start the buffer.

92 files changed:
Documentation/ABI/testing/sysfs-bus-iio
Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
Documentation/DocBook/iio.tmpl
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/iio/accel/mma8452.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/hi8435.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/light/apds9960.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/light/us5182d.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/iio/Kconfig
drivers/iio/Makefile
drivers/iio/accel/Kconfig
drivers/iio/accel/Makefile
drivers/iio/accel/mma8452.c
drivers/iio/accel/mxc4005.c [new file with mode: 0644]
drivers/iio/accel/st_accel_core.c
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/berlin2-adc.c
drivers/iio/adc/hi8435.c [new file with mode: 0644]
drivers/iio/adc/max1027.c
drivers/iio/adc/mcp320x.c
drivers/iio/adc/ti-adc128s052.c
drivers/iio/adc/twl6030-gpadc.c
drivers/iio/adc/vf610_adc.c
drivers/iio/adc/xilinx-xadc-core.c
drivers/iio/adc/xilinx-xadc.h
drivers/iio/amplifiers/ad8366.c
drivers/iio/buffer/Kconfig [new file with mode: 0644]
drivers/iio/buffer/Makefile [new file with mode: 0644]
drivers/iio/buffer/industrialio-buffer-cb.c [new file with mode: 0644]
drivers/iio/buffer/industrialio-triggered-buffer.c [new file with mode: 0644]
drivers/iio/buffer/kfifo_buf.c [new file with mode: 0644]
drivers/iio/buffer_cb.c [deleted file]
drivers/iio/chemical/Kconfig [new file with mode: 0644]
drivers/iio/chemical/Makefile [new file with mode: 0644]
drivers/iio/chemical/vz89x.c [new file with mode: 0644]
drivers/iio/common/st_sensors/st_sensors_core.c
drivers/iio/dac/ad7303.c
drivers/iio/dac/max5821.c
drivers/iio/frequency/adf4350.c
drivers/iio/gyro/Kconfig
drivers/iio/gyro/Makefile
drivers/iio/gyro/bmg160.c [deleted file]
drivers/iio/gyro/bmg160.h [new file with mode: 0644]
drivers/iio/gyro/bmg160_core.c [new file with mode: 0644]
drivers/iio/gyro/bmg160_i2c.c [new file with mode: 0644]
drivers/iio/gyro/bmg160_spi.c [new file with mode: 0644]
drivers/iio/gyro/st_gyro_core.c
drivers/iio/humidity/Kconfig
drivers/iio/humidity/Makefile
drivers/iio/humidity/hdc100x.c [new file with mode: 0644]
drivers/iio/humidity/si7020.c
drivers/iio/industrialio-core.c
drivers/iio/industrialio-trigger.c
drivers/iio/industrialio-triggered-buffer.c [deleted file]
drivers/iio/industrialio-triggered-event.c [new file with mode: 0644]
drivers/iio/kfifo_buf.c [deleted file]
drivers/iio/light/Kconfig
drivers/iio/light/Makefile
drivers/iio/light/apds9960.c [new file with mode: 0644]
drivers/iio/light/opt3001.c
drivers/iio/light/stk3310.c
drivers/iio/light/tsl4531.c
drivers/iio/light/us5182d.c [new file with mode: 0644]
drivers/iio/magnetometer/Kconfig
drivers/iio/magnetometer/Makefile
drivers/iio/magnetometer/st_magn_core.c
drivers/iio/pressure/st_pressure_core.c
drivers/iio/proximity/Kconfig
drivers/iio/proximity/Makefile
drivers/iio/proximity/as3935.c
drivers/iio/proximity/pulsedlight-lidar-lite-v2.c [new file with mode: 0644]
drivers/iio/temperature/mlx90614.c
drivers/staging/iio/adc/lpc32xx_adc.c
drivers/staging/iio/iio_dummy_evgen.c
drivers/staging/iio/iio_simple_dummy.h
drivers/staging/iio/iio_simple_dummy_events.c
drivers/staging/iio/magnetometer/hmc5843.h
drivers/staging/iio/magnetometer/hmc5843_core.c
drivers/staging/iio/magnetometer/hmc5843_i2c.c
drivers/staging/iio/magnetometer/hmc5843_spi.c
include/linux/iio/common/st_sensors.h
include/linux/iio/iio.h
include/linux/iio/triggered_event.h [new file with mode: 0644]
include/uapi/linux/iio/types.h
tools/iio/generic_buffer.c
tools/iio/iio_event_monitor.c

index 42d360f..8ce14c8 100644 (file)
@@ -581,6 +581,7 @@ What:               /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
 What:          /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
 What:          /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
 What:          /sys/.../iio:deviceX/events/in_voltageY_thresh_falling_en
+What:          /sys/.../iio:deviceX/events/in_voltageY_thresh_either_en
 What:          /sys/.../iio:deviceX/events/in_tempY_thresh_rising_en
 What:          /sys/.../iio:deviceX/events/in_tempY_thresh_falling_en
 KernelVersion: 2.6.37
@@ -1459,3 +1460,22 @@ Description:
                measurements and return the average value as output data. Each
                value resulted from <type>[_name]_oversampling_ratio measurements
                is considered as one sample for <type>[_name]_sampling_frequency.
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
+KernelVersion: 4.3
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Raw (unscaled no offset etc.) percentage reading of a substance.
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_resistance_raw
+What:          /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw
+KernelVersion: 4.3
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Raw (unscaled no offset etc.) resistance reading that can be processed
+               into an ohm value.
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
new file mode 100644 (file)
index 0000000..f30b4c4
--- /dev/null
@@ -0,0 +1,43 @@
+What:          /sys/bus/iio/devices/iio:deviceX/in_voltageY_sensing_mode
+Date:          August 2015
+KernelVersion: 4.2.0
+Contact:       source@cogentembedded.com
+Description:
+               Program sensor type for threshold detector inputs.
+               Could be either "GND-Open" or "Supply-Open" mode. Y is a
+               threshold detector input channel. Channels 0..7, 8..15, 16..23
+               and 24..31 has common sensor types.
+
+What:          /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_falling_value
+Date:          August 2015
+KernelVersion: 4.2.0
+Contact:       source@cogentembedded.com
+Description:
+               Channel Y low voltage threshold. If sensor input voltage goes lower then
+               this value then the threshold falling event is pushed.
+               Depending on in_voltageY_sensing_mode the low voltage threshold
+               is separately set for "GND-Open" and "Supply-Open" modes.
+               Channels 0..31 have common low threshold values, but could have different
+               sensing_modes.
+               The low voltage threshold range is between 2..21V.
+               Hysteresis between low and high thresholds can not be lower then 2 and
+               can not be odd.
+               If falling threshold results hysteresis to odd value then rising
+               threshold is automatically subtracted by one.
+
+What:          /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_rising_value
+Date:          August 2015
+KernelVersion: 4.2.0
+Contact:       source@cogentembedded.com
+Description:
+               Channel Y high voltage threshold. If sensor input voltage goes higher then
+               this value then the threshold rising event is pushed.
+               Depending on in_voltageY_sensing_mode the high voltage threshold
+               is separately set for "GND-Open" and "Supply-Open" modes.
+               Channels 0..31 have common high threshold values, but could have different
+               sensing_modes.
+               The high voltage threshold range is between 3..22V.
+               Hysteresis between low and high thresholds can not be lower then 2 and
+               can not be odd.
+               If rising threshold results hysteresis to odd value then falling
+               threshold is automatically appended by one.
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x b/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x
new file mode 100644 (file)
index 0000000..c0c1ea9
--- /dev/null
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
+Date:          September 2015
+KernelVersion: 4.3
+Contact:       Matt Ranostay <mranostay@gmail.com>
+Description:
+               Get the raw calibration VOC value from the sensor.
+               This value has little application outside of calibration.
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x b/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x
new file mode 100644 (file)
index 0000000..b72bb62
--- /dev/null
@@ -0,0 +1,9 @@
+What:          /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw
+What:          /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available
+KernelVersion: 4.3
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Controls the heater device within the humidity sensor to get
+               rid of excess condensation.
+
+               Valid control values are 0 = OFF, and 1 = ON.
index bbb0392..04ac623 100644 (file)
@@ -18,3 +18,25 @@ Description:
                trigger. In order to associate the trigger with an IIO device
                one should write this name string to
                /sys/bus/iio/devices/iio:deviceY/trigger/current_trigger.
+
+What:          /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
+KernelVersion: 2.6.39
+Contact:       linux-iio@vger.kernel.org
+Description:
+               This attribute is provided by the iio-trig-sysfs stand-alone
+               driver and it is used to activate the creation of a new trigger.
+               In order to achieve this, one should write a positive integer
+               into the associated file, which will serve as the id of the
+               trigger. If the trigger with the specified id is already present
+               in the system, an invalid argument message will be returned.
+
+What:          /sys/bus/iio/devices/iio_sysfs_trigger/remove_trigger
+KernelVersion: 2.6.39
+Contact:       linux-iio@vger.kernel.org
+Description:
+               This attribute is used to unregister and delete a previously
+               created trigger from the list of available triggers. In order to
+               achieve this, one should write a positive integer into the
+               associated file, representing the id of the trigger that needs
+               to be removed. If the trigger can't be found, an invalid
+               argument message will be returned to the user.
index 06bb53d..98be322 100644 (file)
     work together.
     </para>
     <sect2 id="iiotrigbufsetup"> <title> IIO triggered buffer setup</title>
-!Edrivers/iio/industrialio-triggered-buffer.c
+!Edrivers/iio/buffer/industrialio-triggered-buffer.c
 !Finclude/linux/iio/iio.h iio_buffer_setup_ops
 
 
index d77d412..c50cf13 100644 (file)
@@ -54,7 +54,6 @@ epson,rx8581          I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 fsl,mag3110            MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
 fsl,mc13892            MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
 fsl,mma8450            MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
-fsl,mma8452            MMA8452Q: 3-axis 12-bit / 8-bit Digital Accelerometer
 fsl,mpr121             MPR121: Proximity Capacitive Touch Sensor Controller
 fsl,sgtl5000           SGTL5000: Ultra Low-Power Audio Codec
 gmt,g751               G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
@@ -80,6 +79,7 @@ oki,ml86v7667         OKI ML86V7667 video decoder
 ovti,ov5642            OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI and Embedded TrueFocus
 pericom,pt7c4338       Real-time Clock Module
 plx,pex8648            48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
+pulsedlight,lidar-lite-v2      Pulsedlight LIDAR range-finding sensor
 ramtron,24c64          i2c serial eeprom  (24cxx)
 ricoh,r2025sd          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,r2221tl          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
@@ -88,6 +88,7 @@ ricoh,rs5c372b                I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,rv5c386          I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 ricoh,rv5c387a         I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
 samsung,24ad0xd1       S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
+sgx,vz89x              SGX Sensortech VZ89X Sensors
 sii,s35390a            2-wire CMOS real-time clock
 skyworks,sky81452      Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
 st-micro,24c256                i2c serial eeprom  (24cxx)
diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt
new file mode 100644 (file)
index 0000000..e3c3746
--- /dev/null
@@ -0,0 +1,24 @@
+Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
+
+Required properties:
+
+  - compatible: should contain one of
+    * "fsl,mma8452"
+    * "fsl,mma8453"
+    * "fsl,mma8652"
+    * "fsl,mma8653"
+  - reg: the I2C address of the chip
+
+Optional properties:
+
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: interrupt mapping for GPIO IRQ
+
+Example:
+
+       mma8453fc@1d {
+               compatible = "fsl,mma8453";
+               reg = <0x1d>;
+               interrupt-parent = <&gpio1>;
+               interrupts = <5 0>;
+       };
diff --git a/Documentation/devicetree/bindings/iio/adc/hi8435.txt b/Documentation/devicetree/bindings/iio/adc/hi8435.txt
new file mode 100644 (file)
index 0000000..3b0348c
--- /dev/null
@@ -0,0 +1,21 @@
+Holt Integrated Circuits HI-8435 threshold detector bindings
+
+Required properties:
+ - compatible: should be "holt,hi8435"
+ - reg: spi chip select number for the device
+
+Recommended properties:
+ - spi-max-frequency: definition as per
+               Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Optional properties:
+ - gpios: GPIO used for controlling the reset pin
+
+Example:
+sensor@0 {
+       compatible = "holt,hi8435";
+       reg = <0>;
+       gpios = <&gpio6 1 0>;
+
+       spi-max-frequency = <1000000>;
+};
diff --git a/Documentation/devicetree/bindings/iio/light/apds9960.txt b/Documentation/devicetree/bindings/iio/light/apds9960.txt
new file mode 100644 (file)
index 0000000..174b709
--- /dev/null
@@ -0,0 +1,22 @@
+* Avago APDS9960 gesture/RGB/ALS/proximity sensor
+
+http://www.avagotech.com/docs/AV02-4191EN
+
+Required properties:
+
+  - compatible: must be "avago,apds9960"
+  - 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:
+
+apds9960@39 {
+       compatible = "avago,apds9960";
+       reg = <0x39>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <16 1>;
+};
diff --git a/Documentation/devicetree/bindings/iio/light/us5182d.txt b/Documentation/devicetree/bindings/iio/light/us5182d.txt
new file mode 100644 (file)
index 0000000..6f0a530
--- /dev/null
@@ -0,0 +1,34 @@
+* UPISEMI us5182d I2C ALS and Proximity sensor
+
+Required properties:
+- compatible: must be "upisemi,usd5182"
+- reg: the I2C address of the device
+
+Optional properties:
+- upisemi,glass-coef: glass attenuation factor - compensation factor of
+                      resolution 1000 for material transmittance.
+- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
+                    counts) corresponding to every scale.
+- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
+                           fractional bits - Q4.4) applied when light > threshold
+- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
+                           fractional bits - Q4.4) applied when light < threshold
+
+If the optional properties are not specified these factors will default to the
+values in the below example.
+The glass-coef defaults to no compensation for the covering material.
+The threshold array defaults to experimental values that work with US5182D
+sensor on evaluation board - roughly between 12-32 lux.
+There will be no dark-gain compensation by default when ALS > thresh
+(0 * dark-gain), and a 1.35 compensation factor when ALS < thresh.
+
+Example:
+
+    usd5182@39 {
+                compatible = "upisemi,usd5182";
+                reg = <0x39>;
+                upisemi,glass-coef = < 1000 >;
+                upisemi,dark-ths = /bits/ 16 <170 200 512 512 800 2000 4000 8000>;
+                upisemi,upper-dark-gain = /bits/ 8 <0x00>;
+                upisemi,lower-dark-gain = /bits/ 8 <0x16>;
+    };
index 82d2ac9..13e54a0 100644 (file)
@@ -101,6 +101,7 @@ himax       Himax Technologies, Inc.
 hisilicon      Hisilicon Limited.
 hit    Hitachi Ltd.
 hitex  Hitex Development Tools
+holt   Holt Integrated Circuits, Inc.
 honeywell      Honeywell
 hp     Hewlett Packard
 i2se   I2SE GmbH
@@ -169,6 +170,7 @@ phytec      PHYTEC Messtechnik GmbH
 picochip       Picochip Ltd
 plathome       Plat'Home Co., Ltd.
 pixcir  PIXCIR MICROELECTRONICS Co., Ltd
+pulsedlight    PulsedLight, Inc
 powervr        PowerVR (deprecated, use img)
 qca    Qualcomm Atheros, Inc.
 qcom   Qualcomm Technologies, Inc
@@ -191,6 +193,7 @@ sbs Smart Battery System
 schindler      Schindler
 seagate        Seagate Technology PLC
 semtech        Semtech Corporation
+sgx    SGX Sensortech
 sharp  Sharp Corporation
 sil    Silicon Image
 silabs Silicon Laboratories
@@ -223,6 +226,7 @@ toshiba     Toshiba Corporation
 toumaz Toumaz
 tplink TP-LINK Technologies Co., Ltd.
 truly  Truly Semiconductors Limited
+upisemi        uPI Semiconductor Corp.
 usi    Universal Scientific Industrial Co., Ltd.
 v3     V3 Semiconductor
 variscite      Variscite Ltd.
index 7b181d5..6d7af82 100644 (file)
@@ -6918,6 +6918,13 @@ S:       Supported
 F:     include/linux/mlx5/
 F:     drivers/infiniband/hw/mlx5/
 
+MELEXIS MLX90614 DRIVER
+M:     Crt Mori <cmo@melexis.com>
+L:     linux-iio@vger.kernel.org
+W:     http://www.melexis.com
+S:     Supported
+F:     drivers/iio/temperature/mlx90614.c
+
 MN88472 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
 L:     linux-media@vger.kernel.org
index 4011eff..119c94d 100644 (file)
@@ -19,27 +19,7 @@ config IIO_BUFFER
          acquisition methods.
 
 if IIO_BUFFER
-
-config IIO_BUFFER_CB
-       bool "IIO callback buffer used for push in-kernel interfaces"
-       help
-         Should be selected by any drivers that do in-kernel push
-         usage.  That is, those where the data is pushed to the consumer.
-
-config IIO_KFIFO_BUF
-       tristate "Industrial I/O buffering based on kfifo"
-       help
-         A simple fifo based on kfifo.  Note that this currently provides
-         no buffer events so it is up to userspace to work out how
-         often to read from the buffer.
-
-config IIO_TRIGGERED_BUFFER
-       tristate
-       select IIO_TRIGGER
-       select IIO_KFIFO_BUF
-       help
-         Provides helper functions for setting up triggered buffers.
-
+       source "drivers/iio/buffer/Kconfig"
 endif # IIO_BUFFER
 
 config IIO_TRIGGER
@@ -58,9 +38,16 @@ config IIO_CONSUMERS_PER_TRIGGER
        This value controls the maximum number of consumers that a
        given trigger may handle. Default is 2.
 
+config IIO_TRIGGERED_EVENT
+       tristate
+       select IIO_TRIGGER
+       help
+         Provides helper functions for setting up triggered events.
+
 source "drivers/iio/accel/Kconfig"
 source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/chemical/Kconfig"
 source "drivers/iio/common/Kconfig"
 source "drivers/iio/dac/Kconfig"
 source "drivers/iio/frequency/Kconfig"
index 698afc2..e210055 100644 (file)
@@ -6,14 +6,14 @@ obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
-industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
 
-obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
-obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
+obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
 
 obj-y += accel/
 obj-y += adc/
 obj-y += amplifiers/
+obj-y += buffer/
+obj-y += chemical/
 obj-y += common/
 obj-y += dac/
 obj-y += gyro/
index a59047d..8172ae5 100644 (file)
@@ -100,13 +100,13 @@ config KXCJK1013
          be called kxcjk-1013.
 
 config MMA8452
-       tristate "Freescale MMA8452Q Accelerometer Driver"
+       tristate "Freescale MMA8452Q and similar Accelerometers Driver"
        depends on I2C
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        help
-         Say yes here to build support for the Freescale MMA8452Q 3-axis
-         accelerometer.
+         Say yes here to build support for the following Freescale 3-axis
+         accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
 
          To compile this driver as a module, choose M here: the module
          will be called mma8452.
@@ -137,6 +137,19 @@ config MMA9553
          To compile this driver as a module, choose M here: the module
          will be called mma9553.
 
+config MXC4005
+       tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
+       depends on I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       select REGMAP_I2C
+       help
+         Say yes here to build support for the Memsic MXC4005XC 3-axis
+         accelerometer.
+
+         To compile this driver as a module, choose M. The module will be
+         called mxc4005.
+
 config STK8312
        tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
        depends on I2C
index ebd2675..020dda0 100644 (file)
@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)    += mma9551_core.o
 obj-$(CONFIG_MMA9551)          += mma9551.o
 obj-$(CONFIG_MMA9553)          += mma9553.o
 
+obj-$(CONFIG_MXC4005)          += mxc4005.o
+
 obj-$(CONFIG_STK8312)          += stk8312.o
 obj-$(CONFIG_STK8BA50)         += stk8ba50.o
 
index b921d84..1eccc2d 100644 (file)
@@ -1,6 +1,12 @@
 /*
- * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
+ * mma8452.c - Support for following Freescale 3-axis accelerometers:
  *
+ * MMA8452Q (12 bit)
+ * MMA8453Q (10 bit)
+ * MMA8652FC (12 bit)
+ * MMA8653FC (10 bit)
+ *
+ * Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
  * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
  *
  * This file is subject to the terms and conditions of version 2 of
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/events.h>
 #include <linux/delay.h>
+#include <linux/of_device.h>
 
 #define MMA8452_STATUS                         0x00
 #define  MMA8452_STATUS_DRDY                   (BIT(2) | BIT(1) | BIT(0))
-#define MMA8452_OUT_X                          0x01 /* MSB first, 12-bit  */
+#define MMA8452_OUT_X                          0x01 /* MSB first */
 #define MMA8452_OUT_Y                          0x03
 #define MMA8452_OUT_Z                          0x05
 #define MMA8452_INT_SRC                                0x0c
 #define  MMA8452_DATA_CFG_HPF_MASK             BIT(4)
 #define MMA8452_HP_FILTER_CUTOFF               0x0f
 #define  MMA8452_HP_FILTER_CUTOFF_SEL_MASK     GENMASK(1, 0)
+#define MMA8452_FF_MT_CFG                      0x15
+#define  MMA8452_FF_MT_CFG_OAE                 BIT(6)
+#define  MMA8452_FF_MT_CFG_ELE                 BIT(7)
+#define MMA8452_FF_MT_SRC                      0x16
+#define  MMA8452_FF_MT_SRC_XHE                 BIT(1)
+#define  MMA8452_FF_MT_SRC_YHE                 BIT(3)
+#define  MMA8452_FF_MT_SRC_ZHE                 BIT(5)
+#define MMA8452_FF_MT_THS                      0x17
+#define  MMA8452_FF_MT_THS_MASK                        0x7f
+#define MMA8452_FF_MT_COUNT                    0x18
 #define MMA8452_TRANSIENT_CFG                  0x1d
 #define  MMA8452_TRANSIENT_CFG_HPF_BYP         BIT(0)
 #define  MMA8452_TRANSIENT_CFG_CHAN(chan)      BIT(chan + 1)
 #define MMA8452_MAX_REG                                0x31
 
 #define  MMA8452_INT_DRDY                      BIT(0)
+#define  MMA8452_INT_FF_MT                     BIT(2)
 #define  MMA8452_INT_TRANS                     BIT(5)
 
 #define  MMA8452_DEVICE_ID                     0x2a
+#define  MMA8453_DEVICE_ID                     0x3a
+#define MMA8652_DEVICE_ID                      0x4a
+#define MMA8653_DEVICE_ID                      0x5a
 
 struct mma8452_data {
        struct i2c_client *client;
        struct mutex lock;
        u8 ctrl_reg1;
        u8 data_cfg;
+       const struct mma_chip_info *chip_info;
+};
+
+/**
+ * struct mma_chip_info - chip specific data for Freescale's accelerometers
+ * @chip_id:                   WHO_AM_I register's value
+ * @channels:                  struct iio_chan_spec matching the device's
+ *                             capabilities
+ * @num_channels:              number of channels
+ * @mma_scales:                        scale factors for converting register values
+ *                             to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
+ *                             per mode: m/s^2 and micro m/s^2
+ * @ev_cfg:                    event config register address
+ * @ev_cfg_ele:                        latch bit in event config register
+ * @ev_cfg_chan_shift:         number of the bit to enable events in X
+ *                             direction; in event config register
+ * @ev_src:                    event source register address
+ * @ev_src_xe:                 bit in event source register that indicates
+ *                             an event in X direction
+ * @ev_src_ye:                 bit in event source register that indicates
+ *                             an event in Y direction
+ * @ev_src_ze:                 bit in event source register that indicates
+ *                             an event in Z direction
+ * @ev_ths:                    event threshold register address
+ * @ev_ths_mask:               mask for the threshold value
+ * @ev_count:                  event count (period) register address
+ *
+ * Since not all chips supported by the driver support comparing high pass
+ * filtered data for events (interrupts), different interrupt sources are
+ * used for different chips and the relevant registers are included here.
+ */
+struct mma_chip_info {
+       u8 chip_id;
+       const struct iio_chan_spec *channels;
+       int num_channels;
+       const int mma_scales[3][2];
+       u8 ev_cfg;
+       u8 ev_cfg_ele;
+       u8 ev_cfg_chan_shift;
+       u8 ev_src;
+       u8 ev_src_xe;
+       u8 ev_src_ye;
+       u8 ev_src_ze;
+       u8 ev_ths;
+       u8 ev_ths_mask;
+       u8 ev_count;
 };
 
 static int mma8452_drdy(struct mma8452_data *data)
@@ -143,16 +210,6 @@ static const int mma8452_samp_freq[8][2] = {
        {6, 250000}, {1, 560000}
 };
 
-/*
- * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
- * The userspace interface uses m/s^2 and we declare micro units
- * So scale factor is given by:
- *     g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665
- */
-static const int mma8452_scales[3][2] = {
-       {0, 9577}, {0, 19154}, {0, 38307}
-};
-
 /* Datasheet table 35  (step time vs sample frequency) */
 static const int mma8452_transient_time_step_us[8] = {
        1250,
@@ -189,8 +246,11 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf)
 {
-       return mma8452_show_int_plus_micros(buf, mma8452_scales,
-                                           ARRAY_SIZE(mma8452_scales));
+       struct mma8452_data *data = iio_priv(i2c_get_clientdata(
+                                            to_i2c_client(dev)));
+
+       return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
+               ARRAY_SIZE(data->chip_info->mma_scales));
 }
 
 static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
@@ -221,9 +281,8 @@ static int mma8452_get_samp_freq_index(struct mma8452_data *data,
 
 static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
 {
-       return mma8452_get_int_plus_micros_index(mma8452_scales,
-                                                ARRAY_SIZE(mma8452_scales),
-                                                val, val2);
+       return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
+                       ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
 }
 
 static int mma8452_get_hp_filter_index(struct mma8452_data *data,
@@ -270,14 +329,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
                if (ret < 0)
                        return ret;
 
-               *val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4,
-                                    11);
+               *val = sign_extend32(be16_to_cpu(
+                       buffer[chan->scan_index]) >> chan->scan_type.shift,
+                       chan->scan_type.realbits - 1);
 
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_SCALE:
                i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
-               *val = mma8452_scales[i][0];
-               *val2 = mma8452_scales[i][1];
+               *val = data->chip_info->mma_scales[i][0];
+               *val2 = data->chip_info->mma_scales[i][1];
 
                return IIO_VAL_INT_PLUS_MICRO;
        case IIO_CHAN_INFO_SAMP_FREQ:
@@ -439,17 +499,17 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
        switch (info) {
        case IIO_EV_INFO_VALUE:
                ret = i2c_smbus_read_byte_data(data->client,
-                                              MMA8452_TRANSIENT_THS);
+                                              data->chip_info->ev_ths);
                if (ret < 0)
                        return ret;
 
-               *val = ret & MMA8452_TRANSIENT_THS_MASK;
+               *val = ret & data->chip_info->ev_ths_mask;
 
                return IIO_VAL_INT;
 
        case IIO_EV_INFO_PERIOD:
                ret = i2c_smbus_read_byte_data(data->client,
-                                              MMA8452_TRANSIENT_COUNT);
+                                              data->chip_info->ev_count);
                if (ret < 0)
                        return ret;
 
@@ -497,7 +557,8 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
                if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
                        return -EINVAL;
 
-               return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val);
+               return mma8452_change_config(data, data->chip_info->ev_ths,
+                                            val);
 
        case IIO_EV_INFO_PERIOD:
                steps = (val * USEC_PER_SEC + val2) /
@@ -507,7 +568,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
                if (steps < 0 || steps > 0xff)
                        return -EINVAL;
 
-               return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
+               return mma8452_change_config(data, data->chip_info->ev_count,
                                             steps);
 
        case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
@@ -538,13 +599,15 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
                                     enum iio_event_direction dir)
 {
        struct mma8452_data *data = iio_priv(indio_dev);
+       const struct mma_chip_info *chip = data->chip_info;
        int ret;
 
-       ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+       ret = i2c_smbus_read_byte_data(data->client,
+                                      data->chip_info->ev_cfg);
        if (ret < 0)
                return ret;
 
-       return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
+       return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
 }
 
 static int mma8452_write_event_config(struct iio_dev *indio_dev,
@@ -554,20 +617,22 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
                                      int state)
 {
        struct mma8452_data *data = iio_priv(indio_dev);
+       const struct mma_chip_info *chip = data->chip_info;
        int val;
 
-       val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+       val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
        if (val < 0)
                return val;
 
        if (state)
-               val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+               val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
        else
-               val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+               val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
 
-       val |= MMA8452_TRANSIENT_CFG_ELE;
+       val |= chip->ev_cfg_ele;
+       val |= MMA8452_FF_MT_CFG_OAE;
 
-       return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
+       return mma8452_change_config(data, chip->ev_cfg, val);
 }
 
 static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
@@ -576,25 +641,25 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
        s64 ts = iio_get_time_ns();
        int src;
 
-       src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
+       src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
        if (src < 0)
                return;
 
-       if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
+       if (src & data->chip_info->ev_src_xe)
                iio_push_event(indio_dev,
                               IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
                                                  IIO_EV_TYPE_MAG,
                                                  IIO_EV_DIR_RISING),
                               ts);
 
-       if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
+       if (src & data->chip_info->ev_src_ye)
                iio_push_event(indio_dev,
                               IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
                                                  IIO_EV_TYPE_MAG,
                                                  IIO_EV_DIR_RISING),
                               ts);
 
-       if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
+       if (src & data->chip_info->ev_src_ze)
                iio_push_event(indio_dev,
                               IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
                                                  IIO_EV_TYPE_MAG,
@@ -606,6 +671,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
 {
        struct iio_dev *indio_dev = p;
        struct mma8452_data *data = iio_priv(indio_dev);
+       const struct mma_chip_info *chip = data->chip_info;
        int ret = IRQ_NONE;
        int src;
 
@@ -618,7 +684,10 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
                ret = IRQ_HANDLED;
        }
 
-       if (src & MMA8452_INT_TRANS) {
+       if ((src & MMA8452_INT_TRANS &&
+            chip->ev_src == MMA8452_TRANSIENT_SRC) ||
+           (src & MMA8452_INT_FF_MT &&
+            chip->ev_src == MMA8452_FF_MT_SRC)) {
                mma8452_transient_interrupt(indio_dev);
                ret = IRQ_HANDLED;
        }
@@ -680,6 +749,16 @@ static const struct iio_event_spec mma8452_transient_event[] = {
        },
 };
 
+static const struct iio_event_spec mma8452_motion_event[] = {
+       {
+               .type = IIO_EV_TYPE_MAG,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+               .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+                                       BIT(IIO_EV_INFO_PERIOD)
+       },
+};
+
 /*
  * Threshold is configured in fixed 8G/127 steps regardless of
  * currently selected scale for measurement.
@@ -693,10 +772,9 @@ static struct attribute *mma8452_event_attributes[] = {
 
 static struct attribute_group mma8452_event_attribute_group = {
        .attrs = mma8452_event_attributes,
-       .name = "events",
 };
 
-#define MMA8452_CHANNEL(axis, idx) { \
+#define MMA8452_CHANNEL(axis, idx, bits) { \
        .type = IIO_ACCEL, \
        .modified = 1, \
        .channel2 = IIO_MOD_##axis, \
@@ -708,22 +786,144 @@ static struct attribute_group mma8452_event_attribute_group = {
        .scan_index = idx, \
        .scan_type = { \
                .sign = 's', \
-               .realbits = 12, \
+               .realbits = (bits), \
                .storagebits = 16, \
-               .shift = 4, \
+               .shift = 16 - (bits), \
                .endianness = IIO_BE, \
        }, \
        .event_spec = mma8452_transient_event, \
        .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
 }
 
+#define MMA8652_CHANNEL(axis, idx, bits) { \
+       .type = IIO_ACCEL, \
+       .modified = 1, \
+       .channel2 = IIO_MOD_##axis, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+               BIT(IIO_CHAN_INFO_CALIBBIAS), \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+               BIT(IIO_CHAN_INFO_SCALE), \
+       .scan_index = idx, \
+       .scan_type = { \
+               .sign = 's', \
+               .realbits = (bits), \
+               .storagebits = 16, \
+               .shift = 16 - (bits), \
+               .endianness = IIO_BE, \
+       }, \
+       .event_spec = mma8452_motion_event, \
+       .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
+}
+
 static const struct iio_chan_spec mma8452_channels[] = {
-       MMA8452_CHANNEL(X, 0),
-       MMA8452_CHANNEL(Y, 1),
-       MMA8452_CHANNEL(Z, 2),
+       MMA8452_CHANNEL(X, 0, 12),
+       MMA8452_CHANNEL(Y, 1, 12),
+       MMA8452_CHANNEL(Z, 2, 12),
        IIO_CHAN_SOFT_TIMESTAMP(3),
 };
 
+static const struct iio_chan_spec mma8453_channels[] = {
+       MMA8452_CHANNEL(X, 0, 10),
+       MMA8452_CHANNEL(Y, 1, 10),
+       MMA8452_CHANNEL(Z, 2, 10),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8652_channels[] = {
+       MMA8652_CHANNEL(X, 0, 12),
+       MMA8652_CHANNEL(Y, 1, 12),
+       MMA8652_CHANNEL(Z, 2, 12),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8653_channels[] = {
+       MMA8652_CHANNEL(X, 0, 10),
+       MMA8652_CHANNEL(Y, 1, 10),
+       MMA8652_CHANNEL(Z, 2, 10),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+enum {
+       mma8452,
+       mma8453,
+       mma8652,
+       mma8653,
+};
+
+static const struct mma_chip_info mma_chip_info_table[] = {
+       [mma8452] = {
+               .chip_id = MMA8452_DEVICE_ID,
+               .channels = mma8452_channels,
+               .num_channels = ARRAY_SIZE(mma8452_channels),
+               /*
+                * Hardware has fullscale of -2G, -4G, -8G corresponding to
+                * raw value -2048 for 12 bit or -512 for 10 bit.
+                * The userspace interface uses m/s^2 and we declare micro units
+                * So scale factor for 12 bit here is given by:
+                *      g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
+                */
+               .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+               .ev_cfg = MMA8452_TRANSIENT_CFG,
+               .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+               .ev_cfg_chan_shift = 1,
+               .ev_src = MMA8452_TRANSIENT_SRC,
+               .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+               .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+               .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+               .ev_ths = MMA8452_TRANSIENT_THS,
+               .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+               .ev_count = MMA8452_TRANSIENT_COUNT,
+       },
+       [mma8453] = {
+               .chip_id = MMA8453_DEVICE_ID,
+               .channels = mma8453_channels,
+               .num_channels = ARRAY_SIZE(mma8453_channels),
+               .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+               .ev_cfg = MMA8452_TRANSIENT_CFG,
+               .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+               .ev_cfg_chan_shift = 1,
+               .ev_src = MMA8452_TRANSIENT_SRC,
+               .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+               .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+               .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+               .ev_ths = MMA8452_TRANSIENT_THS,
+               .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+               .ev_count = MMA8452_TRANSIENT_COUNT,
+       },
+       [mma8652] = {
+               .chip_id = MMA8652_DEVICE_ID,
+               .channels = mma8652_channels,
+               .num_channels = ARRAY_SIZE(mma8652_channels),
+               .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+               .ev_cfg = MMA8452_FF_MT_CFG,
+               .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+               .ev_cfg_chan_shift = 3,
+               .ev_src = MMA8452_FF_MT_SRC,
+               .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+               .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+               .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+               .ev_ths = MMA8452_FF_MT_THS,
+               .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+               .ev_count = MMA8452_FF_MT_COUNT,
+       },
+       [mma8653] = {
+               .chip_id = MMA8653_DEVICE_ID,
+               .channels = mma8653_channels,
+               .num_channels = ARRAY_SIZE(mma8653_channels),
+               .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+               .ev_cfg = MMA8452_FF_MT_CFG,
+               .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+               .ev_cfg_chan_shift = 3,
+               .ev_src = MMA8452_FF_MT_SRC,
+               .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+               .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+               .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+               .ev_ths = MMA8452_FF_MT_THS,
+               .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+               .ev_count = MMA8452_FF_MT_COUNT,
+       },
+};
+
 static struct attribute *mma8452_attributes[] = {
        &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
        &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
@@ -841,18 +1041,28 @@ static int mma8452_reset(struct i2c_client *client)
        return -ETIMEDOUT;
 }
 
+static const struct of_device_id mma8452_dt_ids[] = {
+       { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
+       { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
+       { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
+       { .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
+
 static int mma8452_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct mma8452_data *data;
        struct iio_dev *indio_dev;
        int ret;
+       const struct of_device_id *match;
 
-       ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
-       if (ret < 0)
-               return ret;
-       if (ret != MMA8452_DEVICE_ID)
+       match = of_match_device(mma8452_dt_ids, &client->dev);
+       if (!match) {
+               dev_err(&client->dev, "unknown device model\n");
                return -ENODEV;
+       }
 
        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
        if (!indio_dev)
@@ -861,14 +1071,33 @@ static int mma8452_probe(struct i2c_client *client,
        data = iio_priv(indio_dev);
        data->client = client;
        mutex_init(&data->lock);
+       data->chip_info = match->data;
+
+       ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
+       if (ret < 0)
+               return ret;
+
+       switch (ret) {
+       case MMA8452_DEVICE_ID:
+       case MMA8453_DEVICE_ID:
+       case MMA8652_DEVICE_ID:
+       case MMA8653_DEVICE_ID:
+               if (ret == data->chip_info->chip_id)
+                       break;
+       default:
+               return -ENODEV;
+       }
+
+       dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
+                match->compatible, data->chip_info->chip_id);
 
        i2c_set_clientdata(client, indio_dev);
        indio_dev->info = &mma8452_info;
        indio_dev->name = id->name;
        indio_dev->dev.parent = &client->dev;
        indio_dev->modes = INDIO_DIRECT_MODE;
-       indio_dev->channels = mma8452_channels;
-       indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
+       indio_dev->channels = data->chip_info->channels;
+       indio_dev->num_channels = data->chip_info->num_channels;
        indio_dev->available_scan_masks = mma8452_scan_masks;
 
        ret = mma8452_reset(client);
@@ -892,13 +1121,15 @@ static int mma8452_probe(struct i2c_client *client,
 
        if (client->irq) {
                /*
-                * Although we enable the transient interrupt source once and
-                * for all here the transient event detection itself is not
-                * enabled until userspace asks for it by
-                * mma8452_write_event_config()
+                * Although we enable the interrupt sources once and for
+                * all here the event detection itself is not enabled until
+                * userspace asks for it by mma8452_write_event_config()
                 */
-               int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
-               int enabled_interrupts = MMA8452_INT_TRANS;
+               int supported_interrupts = MMA8452_INT_DRDY |
+                                          MMA8452_INT_TRANS |
+                                          MMA8452_INT_FF_MT;
+               int enabled_interrupts = MMA8452_INT_TRANS |
+                                        MMA8452_INT_FF_MT;
 
                /* Assume wired to INT1 pin */
                ret = i2c_smbus_write_byte_data(client,
@@ -987,17 +1218,14 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
 #endif
 
 static const struct i2c_device_id mma8452_id[] = {
-       { "mma8452", 0 },
+       { "mma8452", mma8452 },
+       { "mma8453", mma8453 },
+       { "mma8652", mma8652 },
+       { "mma8653", mma8653 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, mma8452_id);
 
-static const struct of_device_id mma8452_dt_ids[] = {
-       { .compatible = "fsl,mma8452" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
-
 static struct i2c_driver mma8452_driver = {
        .driver = {
                .name   = "mma8452",
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
new file mode 100644 (file)
index 0000000..e72e218
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * 3-axis accelerometer driver for MXC4005XC Memsic sensor
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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 <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MXC4005_DRV_NAME               "mxc4005"
+#define MXC4005_IRQ_NAME               "mxc4005_event"
+#define MXC4005_REGMAP_NAME            "mxc4005_regmap"
+
+#define MXC4005_REG_XOUT_UPPER         0x03
+#define MXC4005_REG_XOUT_LOWER         0x04
+#define MXC4005_REG_YOUT_UPPER         0x05
+#define MXC4005_REG_YOUT_LOWER         0x06
+#define MXC4005_REG_ZOUT_UPPER         0x07
+#define MXC4005_REG_ZOUT_LOWER         0x08
+
+#define MXC4005_REG_INT_MASK1          0x0B
+#define MXC4005_REG_INT_MASK1_BIT_DRDYE        0x01
+
+#define MXC4005_REG_INT_CLR1           0x01
+#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01
+
+#define MXC4005_REG_CONTROL            0x0D
+#define MXC4005_REG_CONTROL_MASK_FSR   GENMASK(6, 5)
+#define MXC4005_CONTROL_FSR_SHIFT      5
+
+#define MXC4005_REG_DEVICE_ID          0x0E
+
+enum mxc4005_axis {
+       AXIS_X,
+       AXIS_Y,
+       AXIS_Z,
+};
+
+enum mxc4005_range {
+       MXC4005_RANGE_2G,
+       MXC4005_RANGE_4G,
+       MXC4005_RANGE_8G,
+};
+
+struct mxc4005_data {
+       struct device *dev;
+       struct mutex mutex;
+       struct regmap *regmap;
+       struct iio_trigger *dready_trig;
+       __be16 buffer[8];
+       bool trigger_enabled;
+};
+
+/*
+ * MXC4005 can operate in the following ranges:
+ * +/- 2G, 4G, 8G (the default +/-2G)
+ *
+ * (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
+ * (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
+ * (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
+ */
+static const struct {
+       u8 range;
+       int scale;
+} mxc4005_scale_table[] = {
+       {MXC4005_RANGE_2G, 9582},
+       {MXC4005_RANGE_4G, 19164},
+       {MXC4005_RANGE_8G, 38329},
+};
+
+
+static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
+
+static struct attribute *mxc4005_attributes[] = {
+       &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group mxc4005_attrs_group = {
+       .attrs = mxc4005_attributes,
+};
+
+static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MXC4005_REG_XOUT_UPPER:
+       case MXC4005_REG_XOUT_LOWER:
+       case MXC4005_REG_YOUT_UPPER:
+       case MXC4005_REG_YOUT_LOWER:
+       case MXC4005_REG_ZOUT_UPPER:
+       case MXC4005_REG_ZOUT_LOWER:
+       case MXC4005_REG_DEVICE_ID:
+       case MXC4005_REG_CONTROL:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MXC4005_REG_INT_CLR1:
+       case MXC4005_REG_INT_MASK1:
+       case MXC4005_REG_CONTROL:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config mxc4005_regmap_config = {
+       .name = MXC4005_REGMAP_NAME,
+
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = MXC4005_REG_DEVICE_ID,
+
+       .readable_reg = mxc4005_is_readable_reg,
+       .writeable_reg = mxc4005_is_writeable_reg,
+};
+
+static int mxc4005_read_xyz(struct mxc4005_data *data)
+{
+       int ret;
+
+       ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
+                              (u8 *) data->buffer, sizeof(data->buffer));
+       if (ret < 0) {
+               dev_err(data->dev, "failed to read axes\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int mxc4005_read_axis(struct mxc4005_data *data,
+                            unsigned int addr)
+{
+       __be16 reg;
+       int ret;
+
+       ret = regmap_bulk_read(data->regmap, addr, (u8 *) &reg, sizeof(reg));
+       if (ret < 0) {
+               dev_err(data->dev, "failed to read reg %02x\n", addr);
+               return ret;
+       }
+
+       return be16_to_cpu(reg);
+}
+
+static int mxc4005_read_scale(struct mxc4005_data *data)
+{
+       unsigned int reg;
+       int ret;
+       int i;
+
+       ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &reg);
+       if (ret < 0) {
+               dev_err(data->dev, "failed to read reg_control\n");
+               return ret;
+       }
+
+       i = reg >> MXC4005_CONTROL_FSR_SHIFT;
+
+       if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
+               return -EINVAL;
+
+       return mxc4005_scale_table[i].scale;
+}
+
+static int mxc4005_set_scale(struct mxc4005_data *data, int val)
+{
+       unsigned int reg;
+       int i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
+               if (mxc4005_scale_table[i].scale == val) {
+                       reg = i << MXC4005_CONTROL_FSR_SHIFT;
+                       ret = regmap_update_bits(data->regmap,
+                                                MXC4005_REG_CONTROL,
+                                                MXC4005_REG_CONTROL_MASK_FSR,
+                                                reg);
+                       if (ret < 0)
+                               dev_err(data->dev,
+                                       "failed to write reg_control\n");
+                       return ret;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int mxc4005_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct mxc4005_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               switch (chan->type) {
+               case IIO_ACCEL:
+                       if (iio_buffer_enabled(indio_dev))
+                               return -EBUSY;
+
+                       ret = mxc4005_read_axis(data, chan->address);
+                       if (ret < 0)
+                               return ret;
+                       *val = sign_extend32(ret >> chan->scan_type.shift,
+                                            chan->scan_type.realbits - 1);
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SCALE:
+               ret = mxc4005_read_scale(data);
+               if (ret < 0)
+                       return ret;
+
+               *val = 0;
+               *val2 = ret;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int mxc4005_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct mxc4005_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               if (val != 0)
+                       return -EINVAL;
+
+               return mxc4005_set_scale(data, val2);
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info mxc4005_info = {
+       .driver_module  = THIS_MODULE,
+       .read_raw       = mxc4005_read_raw,
+       .write_raw      = mxc4005_write_raw,
+       .attrs          = &mxc4005_attrs_group,
+};
+
+static const unsigned long mxc4005_scan_masks[] = {
+       BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
+       0
+};
+
+#define MXC4005_CHANNEL(_axis, _addr) {                                \
+       .type = IIO_ACCEL,                                      \
+       .modified = 1,                                          \
+       .channel2 = IIO_MOD_##_axis,                            \
+       .address = _addr,                                       \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+       .scan_index = AXIS_##_axis,                             \
+       .scan_type = {                                          \
+               .sign = 's',                                    \
+               .realbits = 12,                                 \
+               .storagebits = 16,                              \
+               .shift = 4,                                     \
+               .endianness = IIO_BE,                           \
+       },                                                      \
+}
+
+static const struct iio_chan_spec mxc4005_channels[] = {
+       MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
+       MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
+       MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct mxc4005_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = mxc4005_read_xyz(data);
+       if (ret < 0)
+               goto err;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                                          pf->timestamp);
+
+err:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int mxc4005_clr_intr(struct mxc4005_data *data)
+{
+       int ret;
+
+       /* clear interrupt */
+       ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
+                          MXC4005_REG_INT_CLR1_BIT_DRDYC);
+       if (ret < 0) {
+               dev_err(data->dev, "failed to write to reg_int_clr1\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int mxc4005_set_trigger_state(struct iio_trigger *trig,
+                                    bool state)
+{
+       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+       struct mxc4005_data *data = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&data->mutex);
+       if (state) {
+               ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+                                  MXC4005_REG_INT_MASK1_BIT_DRDYE);
+       } else {
+               ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+                                  ~MXC4005_REG_INT_MASK1_BIT_DRDYE);
+       }
+
+       if (ret < 0) {
+               mutex_unlock(&data->mutex);
+               dev_err(data->dev, "failed to update reg_int_mask1");
+               return ret;
+       }
+
+       data->trigger_enabled = state;
+       mutex_unlock(&data->mutex);
+
+       return 0;
+}
+
+static int mxc4005_trigger_try_reen(struct iio_trigger *trig)
+{
+       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+       struct mxc4005_data *data = iio_priv(indio_dev);
+
+       if (!data->dready_trig)
+               return 0;
+
+       return mxc4005_clr_intr(data);
+}
+
+static const struct iio_trigger_ops mxc4005_trigger_ops = {
+       .set_trigger_state = mxc4005_set_trigger_state,
+       .try_reenable = mxc4005_trigger_try_reen,
+       .owner = THIS_MODULE,
+};
+
+static int mxc4005_gpio_probe(struct i2c_client *client,
+                             struct mxc4005_data *data)
+{
+       struct device *dev;
+       struct gpio_desc *gpio;
+       int ret;
+
+       if (!client)
+               return -EINVAL;
+
+       dev = &client->dev;
+
+       gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN);
+       if (IS_ERR(gpio)) {
+               dev_err(dev, "failed to get acpi gpio index\n");
+               return PTR_ERR(gpio);
+       }
+
+       ret = gpiod_to_irq(gpio);
+
+       dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+       return ret;
+}
+
+static int mxc4005_chip_init(struct mxc4005_data *data)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, &reg);
+       if (ret < 0) {
+               dev_err(data->dev, "failed to read chip id\n");
+               return ret;
+       }
+
+       dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
+
+       return 0;
+}
+
+static int mxc4005_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct mxc4005_data *data;
+       struct iio_dev *indio_dev;
+       struct regmap *regmap;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       regmap = devm_regmap_init_i2c(client, &mxc4005_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&client->dev, "failed to initialize regmap\n");
+               return PTR_ERR(regmap);
+       }
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->dev = &client->dev;
+       data->regmap = regmap;
+
+       ret = mxc4005_chip_init(data);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to initialize chip\n");
+               return ret;
+       }
+
+       mutex_init(&data->mutex);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->channels = mxc4005_channels;
+       indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
+       indio_dev->available_scan_masks = mxc4005_scan_masks;
+       indio_dev->name = MXC4005_DRV_NAME;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &mxc4005_info;
+
+       ret = iio_triggered_buffer_setup(indio_dev,
+                                        iio_pollfunc_store_time,
+                                        mxc4005_trigger_handler,
+                                        NULL);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "failed to setup iio triggered buffer\n");
+               return ret;
+       }
+
+       if (client->irq < 0)
+               client->irq = mxc4005_gpio_probe(client, data);
+
+       if (client->irq > 0) {
+               data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+                                                          "%s-dev%d",
+                                                          indio_dev->name,
+                                                          indio_dev->id);
+               if (!data->dready_trig)
+                       return -ENOMEM;
+
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               iio_trigger_generic_data_rdy_poll,
+                                               NULL,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               MXC4005_IRQ_NAME,
+                                               data->dready_trig);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed to init threaded irq\n");
+                       goto err_buffer_cleanup;
+               }
+
+               data->dready_trig->dev.parent = &client->dev;
+               data->dready_trig->ops = &mxc4005_trigger_ops;
+               iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+               indio_dev->trig = data->dready_trig;
+               iio_trigger_get(indio_dev->trig);
+               ret = iio_trigger_register(data->dready_trig);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed to register trigger\n");
+                       goto err_trigger_unregister;
+               }
+       }
+
+       ret = iio_device_register(indio_dev);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "unable to register iio device %d\n", ret);
+               goto err_buffer_cleanup;
+       }
+
+       return 0;
+
+err_trigger_unregister:
+       iio_trigger_unregister(data->dready_trig);
+err_buffer_cleanup:
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return ret;
+}
+
+static int mxc4005_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct mxc4005_data *data = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+
+       iio_triggered_buffer_cleanup(indio_dev);
+       if (data->dready_trig)
+               iio_trigger_unregister(data->dready_trig);
+
+       return 0;
+}
+
+static const struct acpi_device_id mxc4005_acpi_match[] = {
+       {"MXC4005",     0},
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
+
+static const struct i2c_device_id mxc4005_id[] = {
+       {"mxc4005",     0},
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mxc4005_id);
+
+static struct i2c_driver mxc4005_driver = {
+       .driver = {
+               .name = MXC4005_DRV_NAME,
+               .acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
+       },
+       .probe          = mxc4005_probe,
+       .remove         = mxc4005_remove,
+       .id_table       = mxc4005_id,
+};
+
+module_i2c_driver(mxc4005_driver);
+
+MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");
index ff30f88..dab8b76 100644 (file)
@@ -618,6 +618,7 @@ static const struct iio_info accel_info = {
        .attrs = &st_accel_attribute_group,
        .read_raw = &st_accel_read_raw,
        .write_raw = &st_accel_write_raw,
+       .debugfs_reg_access = &st_sensors_debugfs_reg_access,
 };
 
 #ifdef CONFIG_IIO_TRIGGER
index 50c103d..7868c74 100644 (file)
@@ -149,6 +149,17 @@ config BERLIN2_ADC
          Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
          temperature measurement.
 
+config CC10001_ADC
+       tristate "Cosmic Circuits 10001 ADC driver"
+       depends on HAS_IOMEM && HAVE_CLK && REGULATOR
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         Say yes here to build support for Cosmic Circuits 10001 ADC.
+
+         This driver can also be built as a module. If so, the module will be
+         called cc10001_adc.
+
 config DA9150_GPADC
        tristate "Dialog DA9150 GPADC driver support"
        depends on MFD_DA9150
@@ -161,17 +172,6 @@ config DA9150_GPADC
          To compile this driver as a module, choose M here: the module will be
          called berlin2-adc.
 
-config CC10001_ADC
-       tristate "Cosmic Circuits 10001 ADC driver"
-       depends on HAS_IOMEM && HAVE_CLK && REGULATOR
-       select IIO_BUFFER
-       select IIO_TRIGGERED_BUFFER
-       help
-         Say yes here to build support for Cosmic Circuits 10001 ADC.
-
-         This driver can also be built as a module. If so, the module will be
-         called cc10001_adc.
-
 config EXYNOS_ADC
        tristate "Exynos ADC driver support"
        depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@@ -183,6 +183,17 @@ config EXYNOS_ADC
          To compile this driver as a module, choose M here: the module will be
          called exynos_adc.
 
+config HI8435
+       tristate "Holt Integrated Circuits HI-8435 threshold detector"
+       select IIO_TRIGGERED_EVENT
+       depends on SPI
+       help
+         If you say yes here you get support for Holt Integrated Circuits
+         HI-8435 chip.
+
+         This driver can also be built as a module. If so, the module will be
+         called hi8435.
+
 config LP8788_ADC
        tristate "LP8788 ADC driver"
        depends on MFD_LP8788
@@ -361,6 +372,8 @@ config TWL6030_GPADC
 config VF610_ADC
        tristate "Freescale vf610 ADC driver"
        depends on OF
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        help
          Say yes here to support for Vybrid board analog-to-digital converter.
          Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
index a096210..99b37a9 100644 (file)
@@ -16,9 +16,10 @@ obj-$(CONFIG_AD799X) += ad799x.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
 obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
 obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
-obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX1363) += max1363.o
index 4946d9b..71c806e 100644 (file)
 #define  BERLIN2_SM_CTRL_SM_SOC_INT            BIT(1)
 #define  BERLIN2_SM_CTRL_SOC_SM_INT            BIT(2)
 #define  BERLIN2_SM_CTRL_ADC_SEL(x)            ((x) << 5)      /* 0-15 */
-#define  BERLIN2_SM_CTRL_ADC_SEL_MASK          (0xf << 5)
+#define  BERLIN2_SM_CTRL_ADC_SEL_MASK          GENMASK(8, 5)
 #define  BERLIN2_SM_CTRL_ADC_POWER             BIT(9)
 #define  BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2       (0x0 << 10)
 #define  BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3       (0x1 << 10)
 #define  BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4       (0x2 << 10)
 #define  BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8       (0x3 << 10)
-#define  BERLIN2_SM_CTRL_ADC_CLKSEL_MASK       (0x3 << 10)
+#define  BERLIN2_SM_CTRL_ADC_CLKSEL_MASK       GENMASK(11, 10)
 #define  BERLIN2_SM_CTRL_ADC_START             BIT(12)
 #define  BERLIN2_SM_CTRL_ADC_RESET             BIT(13)
 #define  BERLIN2_SM_CTRL_ADC_BANDGAP_RDY       BIT(14)
@@ -50,7 +50,7 @@
 #define  BERLIN2_SM_CTRL_TSEN_MODE_10_50       (0x1 << 22)     /* 10-50 C */
 #define  BERLIN2_SM_CTRL_TSEN_RESET            BIT(29)
 #define BERLIN2_SM_ADC_DATA                    0x20
-#define  BERLIN2_SM_ADC_MASK                   0x3ff
+#define  BERLIN2_SM_ADC_MASK                   GENMASK(9, 0)
 #define BERLIN2_SM_ADC_STATUS                  0x1c
 #define  BERLIN2_SM_ADC_STATUS_DATA_RDY(x)     BIT(x)          /* 0-15 */
 #define  BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK   GENMASK(15, 0)
@@ -65,9 +65,9 @@
 #define  BERLIN2_SM_TSEN_CTRL_START            BIT(8)
 #define  BERLIN2_SM_TSEN_CTRL_SETTLING_4       (0x0 << 21)     /* 4 us */
 #define  BERLIN2_SM_TSEN_CTRL_SETTLING_12      (0x1 << 21)     /* 12 us */
-#define  BERLIN2_SM_TSEN_CTRL_SETTLING_MASK    (0x1 << 21)
+#define  BERLIN2_SM_TSEN_CTRL_SETTLING_MASK    BIT(21)
 #define  BERLIN2_SM_TSEN_CTRL_TRIM(x)          ((x) << 22)
-#define  BERLIN2_SM_TSEN_CTRL_TRIM_MASK                (0xf << 22)
+#define  BERLIN2_SM_TSEN_CTRL_TRIM_MASK                GENMASK(25, 22)
 
 struct berlin2_adc_priv {
        struct regmap           *regmap;
@@ -78,13 +78,13 @@ struct berlin2_adc_priv {
 };
 
 #define BERLIN2_ADC_CHANNEL(n, t)                                      \
-               {                                                       \
-                       .channel        = n,                            \
-                       .datasheet_name = "channel"#n,                  \
-                       .type           = t,                            \
-                       .indexed        = 1,                            \
-                       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
-               }
+       {                                                               \
+               .channel                = n,                            \
+               .datasheet_name         = "channel"#n,                  \
+               .type                   = t,                            \
+               .indexed                = 1,                            \
+               .info_mask_separate     = BIT(IIO_CHAN_INFO_RAW),       \
+       }
 
 static const struct iio_chan_spec berlin2_adc_channels[] = {
        BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE),    /* external input */
@@ -111,18 +111,24 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
 
        mutex_lock(&priv->lock);
 
+       /* Enable the interrupts */
+       regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
+                    BERLIN2_SM_ADC_STATUS_INT_EN(channel));
+
        /* Configure the ADC */
        regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                       BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
-                       | BERLIN2_SM_CTRL_ADC_START,
-                       BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
+                          BERLIN2_SM_CTRL_ADC_RESET |
+                          BERLIN2_SM_CTRL_ADC_SEL_MASK |
+                          BERLIN2_SM_CTRL_ADC_START,
+                          BERLIN2_SM_CTRL_ADC_SEL(channel) |
+                          BERLIN2_SM_CTRL_ADC_START);
 
        ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
-                       msecs_to_jiffies(1000));
+                                              msecs_to_jiffies(1000));
 
        /* Disable the interrupts */
        regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
-                       BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
+                          BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
 
        if (ret == 0)
                ret = -ETIMEDOUT;
@@ -132,7 +138,7 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
        }
 
        regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                       BERLIN2_SM_CTRL_ADC_START, 0);
+                          BERLIN2_SM_CTRL_ADC_START, 0);
 
        data = priv->data;
        priv->data_available = false;
@@ -149,24 +155,31 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
 
        mutex_lock(&priv->lock);
 
+       /* Enable interrupts */
+       regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+                    BERLIN2_SM_TSEN_STATUS_INT_EN);
+
        /* Configure the ADC */
        regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                       BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
-                       BERLIN2_SM_CTRL_ADC_ROTATE);
+                          BERLIN2_SM_CTRL_TSEN_RESET |
+                          BERLIN2_SM_CTRL_ADC_ROTATE,
+                          BERLIN2_SM_CTRL_ADC_ROTATE);
 
        /* Configure the temperature sensor */
        regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
-                       BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
-                       | BERLIN2_SM_TSEN_CTRL_START,
-                       BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
-                       | BERLIN2_SM_TSEN_CTRL_START);
+                          BERLIN2_SM_TSEN_CTRL_TRIM_MASK |
+                          BERLIN2_SM_TSEN_CTRL_SETTLING_MASK |
+                          BERLIN2_SM_TSEN_CTRL_START,
+                          BERLIN2_SM_TSEN_CTRL_TRIM(3) |
+                          BERLIN2_SM_TSEN_CTRL_SETTLING_12 |
+                          BERLIN2_SM_TSEN_CTRL_START);
 
        ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
-                       msecs_to_jiffies(1000));
+                                              msecs_to_jiffies(1000));
 
        /* Disable interrupts */
        regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
-                       BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
+                          BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
 
        if (ret == 0)
                ret = -ETIMEDOUT;
@@ -176,7 +189,7 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
        }
 
        regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
-                       BERLIN2_SM_TSEN_CTRL_START, 0);
+                          BERLIN2_SM_TSEN_CTRL_START, 0);
 
        data = priv->data;
        priv->data_available = false;
@@ -187,10 +200,9 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
 }
 
 static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
-               struct iio_chan_spec const *chan, int *val, int *val2,
-               long mask)
+                               struct iio_chan_spec const *chan, int *val,
+                               int *val2, long mask)
 {
-       struct berlin2_adc_priv *priv = iio_priv(indio_dev);
        int temp;
 
        switch (mask) {
@@ -198,10 +210,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
                if (chan->type != IIO_VOLTAGE)
                        return -EINVAL;
 
-               /* Enable the interrupts */
-               regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
-                               BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
-
                *val = berlin2_adc_read(indio_dev, chan->channel);
                if (*val < 0)
                        return *val;
@@ -211,10 +219,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
                if (chan->type != IIO_TEMP)
                        return -EINVAL;
 
-               /* Enable interrupts */
-               regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
-                               BERLIN2_SM_TSEN_STATUS_INT_EN);
-
                temp = berlin2_adc_tsen_read(indio_dev);
                if (temp < 0)
                        return temp;
@@ -306,12 +310,12 @@ static int berlin2_adc_probe(struct platform_device *pdev)
                return tsen_irq;
 
        ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
-                       pdev->dev.driver->name, indio_dev);
+                              pdev->dev.driver->name, indio_dev);
        if (ret)
                return ret;
 
        ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
-                       0, pdev->dev.driver->name, indio_dev);
+                              0, pdev->dev.driver->name, indio_dev);
        if (ret)
                return ret;
 
@@ -328,13 +332,14 @@ static int berlin2_adc_probe(struct platform_device *pdev)
 
        /* Power up the ADC */
        regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                       BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
+                          BERLIN2_SM_CTRL_ADC_POWER,
+                          BERLIN2_SM_CTRL_ADC_POWER);
 
        ret = iio_device_register(indio_dev);
        if (ret) {
                /* Power down the ADC */
                regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                               BERLIN2_SM_CTRL_ADC_POWER, 0);
+                                  BERLIN2_SM_CTRL_ADC_POWER, 0);
                return ret;
        }
 
@@ -350,7 +355,7 @@ static int berlin2_adc_remove(struct platform_device *pdev)
 
        /* Power down the ADC */
        regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
-                       BERLIN2_SM_CTRL_ADC_POWER, 0);
+                          BERLIN2_SM_CTRL_ADC_POWER, 0);
 
        return 0;
 }
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
new file mode 100644 (file)
index 0000000..c73c6c6
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * Holt Integrated Circuits HI-8435 threshold detector driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#define DRV_NAME "hi8435"
+
+/* Register offsets for HI-8435 */
+#define HI8435_CTRL_REG                0x02
+#define HI8435_PSEN_REG                0x04
+#define HI8435_TMDATA_REG      0x1E
+#define HI8435_GOCENHYS_REG    0x3A
+#define HI8435_SOCENHYS_REG    0x3C
+#define HI8435_SO7_0_REG       0x10
+#define HI8435_SO15_8_REG      0x12
+#define HI8435_SO23_16_REG     0x14
+#define HI8435_SO31_24_REG     0x16
+#define HI8435_SO31_0_REG      0x78
+
+#define HI8435_WRITE_OPCODE    0x00
+#define HI8435_READ_OPCODE     0x80
+
+/* CTRL register bits */
+#define HI8435_CTRL_TEST       0x01
+#define HI8435_CTRL_SRST       0x02
+
+struct hi8435_priv {
+       struct spi_device *spi;
+       struct mutex lock;
+
+       unsigned long event_scan_mask; /* soft mask/unmask channels events */
+       unsigned int event_prev_val;
+
+       unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
+       unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
+       u8 reg_buffer[3] ____cacheline_aligned;
+};
+
+static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
+{
+       reg |= HI8435_READ_OPCODE;
+       return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
+{
+       int ret;
+       __be16 be_val;
+
+       reg |= HI8435_READ_OPCODE;
+       ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 2);
+       *val = be16_to_cpu(be_val);
+
+       return ret;
+}
+
+static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
+{
+       int ret;
+       __be32 be_val;
+
+       reg |= HI8435_READ_OPCODE;
+       ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 4);
+       *val = be32_to_cpu(be_val);
+
+       return ret;
+}
+
+static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
+{
+       priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+       priv->reg_buffer[1] = val;
+
+       return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
+{
+       priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+       priv->reg_buffer[1] = (val >> 8) & 0xff;
+       priv->reg_buffer[2] = val & 0xff;
+
+       return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+static int hi8435_read_event_config(struct iio_dev *idev,
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+
+       return !!(priv->event_scan_mask & BIT(chan->channel));
+}
+
+static int hi8435_write_event_config(struct iio_dev *idev,
+                                    const struct iio_chan_spec *chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir, int state)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+
+       priv->event_scan_mask &= ~BIT(chan->channel);
+       if (state)
+               priv->event_scan_mask |= BIT(chan->channel);
+
+       return 0;
+}
+
+static int hi8435_read_event_value(struct iio_dev *idev,
+                                  const struct iio_chan_spec *chan,
+                                  enum iio_event_type type,
+                                  enum iio_event_direction dir,
+                                  enum iio_event_info info,
+                                  int *val, int *val2)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       int ret;
+       u8 mode, psen;
+       u16 reg;
+
+       ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+       if (ret < 0)
+               return ret;
+
+       /* Supply-Open or GND-Open sensing mode */
+       mode = !!(psen & BIT(chan->channel / 8));
+
+       ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+                                HI8435_GOCENHYS_REG, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (dir == IIO_EV_DIR_FALLING)
+               *val = ((reg & 0xff) - (reg >> 8)) / 2;
+       else if (dir == IIO_EV_DIR_RISING)
+               *val = ((reg & 0xff) + (reg >> 8)) / 2;
+
+       return IIO_VAL_INT;
+}
+
+static int hi8435_write_event_value(struct iio_dev *idev,
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir,
+                                   enum iio_event_info info,
+                                   int val, int val2)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       int ret;
+       u8 mode, psen;
+       u16 reg;
+
+       ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+       if (ret < 0)
+               return ret;
+
+       /* Supply-Open or GND-Open sensing mode */
+       mode = !!(psen & BIT(chan->channel / 8));
+
+       ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+                                HI8435_GOCENHYS_REG, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (dir == IIO_EV_DIR_FALLING) {
+               /* falling threshold range 2..21V, hysteresis minimum 2V */
+               if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])
+                       return -EINVAL;
+
+               if (val == priv->threshold_lo[mode])
+                       return 0;
+
+               priv->threshold_lo[mode] = val;
+
+               /* hysteresis must not be odd */
+               if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+                       priv->threshold_hi[mode]--;
+       } else if (dir == IIO_EV_DIR_RISING) {
+               /* rising threshold range 3..22V, hysteresis minimum 2V */
+               if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))
+                       return -EINVAL;
+
+               if (val == priv->threshold_hi[mode])
+                       return 0;
+
+               priv->threshold_hi[mode] = val;
+
+               /* hysteresis must not be odd */
+               if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+                       priv->threshold_lo[mode]++;
+       }
+
+       /* program thresholds */
+       mutex_lock(&priv->lock);
+
+       ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+                                HI8435_GOCENHYS_REG, &reg);
+       if (ret < 0) {
+               mutex_unlock(&priv->lock);
+               return ret;
+       }
+
+       /* hysteresis */
+       reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
+       reg <<= 8;
+       /* threshold center */
+       reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
+
+       ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
+                                 HI8435_GOCENHYS_REG, reg);
+
+       mutex_unlock(&priv->lock);
+
+       return ret;
+}
+
+static int hi8435_debugfs_reg_access(struct iio_dev *idev,
+                                    unsigned reg, unsigned writeval,
+                                    unsigned *readval)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       int ret;
+       u8 val;
+
+       if (readval != NULL) {
+               ret = hi8435_readb(priv, reg, &val);
+               *readval = val;
+       } else {
+               val = (u8)writeval;
+               ret = hi8435_writeb(priv, reg, val);
+       }
+
+       return ret;
+}
+
+static const struct iio_event_spec hi8435_events[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE),
+       }, {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+       },
+};
+
+static int hi8435_get_sensing_mode(struct iio_dev *idev,
+                                  const struct iio_chan_spec *chan)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       int ret;
+       u8 reg;
+
+       ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+       if (ret < 0)
+               return ret;
+
+       return !!(reg & BIT(chan->channel / 8));
+}
+
+static int hi8435_set_sensing_mode(struct iio_dev *idev,
+                                  const struct iio_chan_spec *chan,
+                                  unsigned int mode)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       int ret;
+       u8 reg;
+
+       mutex_lock(&priv->lock);
+
+       ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+       if (ret < 0) {
+               mutex_unlock(&priv->lock);
+               return ret;
+       }
+
+       reg &= ~BIT(chan->channel / 8);
+       if (mode)
+               reg |= BIT(chan->channel / 8);
+
+       ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
+
+       mutex_unlock(&priv->lock);
+
+       return ret;
+}
+
+static const char * const hi8435_sensing_modes[] = { "GND-Open",
+                                                    "Supply-Open" };
+
+static const struct iio_enum hi8435_sensing_mode = {
+       .items = hi8435_sensing_modes,
+       .num_items = ARRAY_SIZE(hi8435_sensing_modes),
+       .get = hi8435_get_sensing_mode,
+       .set = hi8435_set_sensing_mode,
+};
+
+static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
+       IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
+       {},
+};
+
+#define HI8435_VOLTAGE_CHANNEL(num)                    \
+{                                                      \
+       .type = IIO_VOLTAGE,                            \
+       .indexed = 1,                                   \
+       .channel = num,                                 \
+       .event_spec = hi8435_events,                    \
+       .num_event_specs = ARRAY_SIZE(hi8435_events),   \
+       .ext_info = hi8435_ext_info,                    \
+}
+
+static const struct iio_chan_spec hi8435_channels[] = {
+       HI8435_VOLTAGE_CHANNEL(0),
+       HI8435_VOLTAGE_CHANNEL(1),
+       HI8435_VOLTAGE_CHANNEL(2),
+       HI8435_VOLTAGE_CHANNEL(3),
+       HI8435_VOLTAGE_CHANNEL(4),
+       HI8435_VOLTAGE_CHANNEL(5),
+       HI8435_VOLTAGE_CHANNEL(6),
+       HI8435_VOLTAGE_CHANNEL(7),
+       HI8435_VOLTAGE_CHANNEL(8),
+       HI8435_VOLTAGE_CHANNEL(9),
+       HI8435_VOLTAGE_CHANNEL(10),
+       HI8435_VOLTAGE_CHANNEL(11),
+       HI8435_VOLTAGE_CHANNEL(12),
+       HI8435_VOLTAGE_CHANNEL(13),
+       HI8435_VOLTAGE_CHANNEL(14),
+       HI8435_VOLTAGE_CHANNEL(15),
+       HI8435_VOLTAGE_CHANNEL(16),
+       HI8435_VOLTAGE_CHANNEL(17),
+       HI8435_VOLTAGE_CHANNEL(18),
+       HI8435_VOLTAGE_CHANNEL(19),
+       HI8435_VOLTAGE_CHANNEL(20),
+       HI8435_VOLTAGE_CHANNEL(21),
+       HI8435_VOLTAGE_CHANNEL(22),
+       HI8435_VOLTAGE_CHANNEL(23),
+       HI8435_VOLTAGE_CHANNEL(24),
+       HI8435_VOLTAGE_CHANNEL(25),
+       HI8435_VOLTAGE_CHANNEL(26),
+       HI8435_VOLTAGE_CHANNEL(27),
+       HI8435_VOLTAGE_CHANNEL(28),
+       HI8435_VOLTAGE_CHANNEL(29),
+       HI8435_VOLTAGE_CHANNEL(30),
+       HI8435_VOLTAGE_CHANNEL(31),
+       IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static const struct iio_info hi8435_info = {
+       .driver_module = THIS_MODULE,
+       .read_event_config = &hi8435_read_event_config,
+       .write_event_config = hi8435_write_event_config,
+       .read_event_value = &hi8435_read_event_value,
+       .write_event_value = &hi8435_write_event_value,
+       .debugfs_reg_access = &hi8435_debugfs_reg_access,
+};
+
+static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
+{
+       struct hi8435_priv *priv = iio_priv(idev);
+       enum iio_event_direction dir;
+       unsigned int i;
+       unsigned int status = priv->event_prev_val ^ val;
+
+       if (!status)
+               return;
+
+       for_each_set_bit(i, &priv->event_scan_mask, 32) {
+               if (status & BIT(i)) {
+                       dir = val & BIT(i) ? IIO_EV_DIR_RISING :
+                                            IIO_EV_DIR_FALLING;
+                       iio_push_event(idev,
+                                      IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+                                                   IIO_EV_TYPE_THRESH, dir),
+                                      iio_get_time_ns());
+               }
+       }
+
+       priv->event_prev_val = val;
+}
+
+static irqreturn_t hi8435_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *idev = pf->indio_dev;
+       struct hi8435_priv *priv = iio_priv(idev);
+       u32 val;
+       int ret;
+
+       ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+       if (ret < 0)
+               goto err_read;
+
+       hi8435_iio_push_event(idev, val);
+
+err_read:
+       iio_trigger_notify_done(idev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int hi8435_probe(struct spi_device *spi)
+{
+       struct iio_dev *idev;
+       struct hi8435_priv *priv;
+       struct gpio_desc *reset_gpio;
+       int ret;
+
+       idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+       if (!idev)
+               return -ENOMEM;
+
+       priv = iio_priv(idev);
+       priv->spi = spi;
+
+       reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
+       if (IS_ERR(reset_gpio)) {
+               /* chip s/w reset if h/w reset failed */
+               hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
+               hi8435_writeb(priv, HI8435_CTRL_REG, 0);
+       } else {
+               udelay(5);
+               gpiod_set_value(reset_gpio, 1);
+       }
+
+       spi_set_drvdata(spi, idev);
+       mutex_init(&priv->lock);
+
+       idev->dev.parent        = &spi->dev;
+       idev->name              = spi_get_device_id(spi)->name;
+       idev->modes             = INDIO_DIRECT_MODE;
+       idev->info              = &hi8435_info;
+       idev->channels          = hi8435_channels;
+       idev->num_channels      = ARRAY_SIZE(hi8435_channels);
+
+       /* unmask all events */
+       priv->event_scan_mask = ~(0);
+       /*
+        * There is a restriction in the chip - the hysteresis can not be odd.
+        * If the hysteresis is set to odd value then chip gets into lock state
+        * and not functional anymore.
+        * After chip reset the thresholds are in undefined state, so we need to
+        * initialize thresholds to some initial values and then prevent
+        * userspace setting odd hysteresis.
+        *
+        * Set threshold low voltage to 2V, threshold high voltage to 4V
+        * for both GND-Open and Supply-Open sensing modes.
+        */
+       priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
+       priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
+       hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
+       hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
+
+       ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
+       if (ret)
+               return ret;
+
+       ret = iio_device_register(idev);
+       if (ret < 0) {
+               dev_err(&spi->dev, "unable to register device\n");
+               goto unregister_triggered_event;
+       }
+
+       return 0;
+
+unregister_triggered_event:
+       iio_triggered_event_cleanup(idev);
+       return ret;
+}
+
+static int hi8435_remove(struct spi_device *spi)
+{
+       struct iio_dev *idev = spi_get_drvdata(spi);
+
+       iio_device_unregister(idev);
+       iio_triggered_event_cleanup(idev);
+
+       return 0;
+}
+
+static const struct of_device_id hi8435_dt_ids[] = {
+       { .compatible = "holt,hi8435" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
+
+static const struct spi_device_id hi8435_id[] = {
+       { "hi8435", 0},
+       { }
+};
+MODULE_DEVICE_TABLE(spi, hi8435_id);
+
+static struct spi_driver hi8435_driver = {
+       .driver = {
+               .name           = DRV_NAME,
+               .of_match_table = of_match_ptr(hi8435_dt_ids),
+       },
+       .probe          = hi8435_probe,
+       .remove         = hi8435_remove,
+       .id_table       = hi8435_id,
+};
+module_spi_driver(hi8435_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435 threshold detector");
index 44bf815..54a8302 100644 (file)
@@ -508,6 +508,7 @@ static int max1027_remove(struct spi_device *spi)
 static struct spi_driver max1027_driver = {
        .driver = {
                .name   = "max1027",
+               .of_match_table = of_match_ptr(max1027_adc_dt_ids),
                .owner  = THIS_MODULE,
        },
        .probe          = max1027_probe,
index b19e4f9..41a21e9 100644 (file)
@@ -404,6 +404,7 @@ MODULE_DEVICE_TABLE(spi, mcp320x_id);
 static struct spi_driver mcp320x_driver = {
        .driver = {
                .name = "mcp320x",
+               .of_match_table = of_match_ptr(mcp320x_dt_ids),
                .owner = THIS_MODULE,
        },
        .probe = mcp320x_probe,
index 915be6b..98c0d2b 100644 (file)
@@ -174,6 +174,13 @@ static int adc128_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id adc128_of_match[] = {
+       { .compatible = "ti,adc128s052", },
+       { .compatible = "ti,adc122s021", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, adc128_of_match);
+
 static const struct spi_device_id adc128_id[] = {
        { "adc128s052", 0},     /* index into adc128_config */
        { "adc122s021", 1},
@@ -184,6 +191,7 @@ MODULE_DEVICE_TABLE(spi, adc128_id);
 static struct spi_driver adc128_driver = {
        .driver = {
                .name = "adc128s052",
+               .of_match_table = of_match_ptr(adc128_of_match),
                .owner = THIS_MODULE,
        },
        .probe = adc128_probe,
index df12c57..becbb0a 100644 (file)
@@ -875,6 +875,7 @@ static const struct of_device_id of_twl6030_match_tbl[] = {
        },
        { /* end */ }
 };
+MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl);
 
 static int twl6030_gpadc_probe(struct platform_device *pdev)
 {
index 6bf4c20..599cde3 100644 (file)
 #include <linux/err.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
 #include <linux/iio/sysfs.h>
-#include <linux/iio/driver.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 
 /* This will be the driver name the kernel reports */
 #define DRIVER_NAME "vf610-adc"
@@ -170,6 +173,7 @@ struct vf610_adc {
        u32 sample_freq_avail[5];
 
        struct completion completion;
+       u16 buffer[8];
 };
 
 static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
@@ -505,12 +509,24 @@ static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
                                BIT(IIO_CHAN_INFO_SAMP_FREQ),   \
        .ext_info = vf610_ext_info,                             \
+       .scan_index = (_idx),                   \
+       .scan_type = {                                  \
+               .sign = 'u',                            \
+               .realbits = 12,                         \
+               .storagebits = 16,                      \
+       },                                              \
 }
 
 #define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
        .type = (_chan_type),   \
        .channel = (_idx),              \
        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),     \
+       .scan_index = (_idx),                                   \
+       .scan_type = {                                          \
+               .sign = 'u',                                    \
+               .realbits = 12,                                 \
+               .storagebits = 16,                              \
+       },                                                      \
 }
 
 static const struct iio_chan_spec vf610_adc_iio_channels[] = {
@@ -531,6 +547,7 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
        VF610_ADC_CHAN(14, IIO_VOLTAGE),
        VF610_ADC_CHAN(15, IIO_VOLTAGE),
        VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
+       IIO_CHAN_SOFT_TIMESTAMP(32),
        /* sentinel */
 };
 
@@ -559,13 +576,20 @@ static int vf610_adc_read_data(struct vf610_adc *info)
 
 static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
 {
-       struct vf610_adc *info = (struct vf610_adc *)dev_id;
+       struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
+       struct vf610_adc *info = iio_priv(indio_dev);
        int coco;
 
        coco = readl(info->regs + VF610_REG_ADC_HS);
        if (coco & VF610_ADC_HS_COCO0) {
                info->value = vf610_adc_read_data(info);
-               complete(&info->completion);
+               if (iio_buffer_enabled(indio_dev)) {
+                       info->buffer[0] = info->value;
+                       iio_push_to_buffers_with_timestamp(indio_dev,
+                                       info->buffer, iio_get_time_ns());
+                       iio_trigger_notify_done(indio_dev->trig);
+               } else
+                       complete(&info->completion);
        }
 
        return IRQ_HANDLED;
@@ -613,8 +637,12 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_RAW:
        case IIO_CHAN_INFO_PROCESSED:
                mutex_lock(&indio_dev->mlock);
-               reinit_completion(&info->completion);
+               if (iio_buffer_enabled(indio_dev)) {
+                       mutex_unlock(&indio_dev->mlock);
+                       return -EBUSY;
+               }
 
+               reinit_completion(&info->completion);
                hc_cfg = VF610_ADC_ADCHC(chan->channel);
                hc_cfg |= VF610_ADC_AIEN;
                writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
@@ -694,6 +722,56 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
        return -EINVAL;
 }
 
+static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+       struct vf610_adc *info = iio_priv(indio_dev);
+       unsigned int channel;
+       int ret;
+       int val;
+
+       ret = iio_triggered_buffer_postenable(indio_dev);
+       if (ret)
+               return ret;
+
+       val = readl(info->regs + VF610_REG_ADC_GC);
+       val |= VF610_ADC_ADCON;
+       writel(val, info->regs + VF610_REG_ADC_GC);
+
+       channel = find_first_bit(indio_dev->active_scan_mask,
+                                               indio_dev->masklength);
+
+       val = VF610_ADC_ADCHC(channel);
+       val |= VF610_ADC_AIEN;
+
+       writel(val, info->regs + VF610_REG_ADC_HC0);
+
+       return 0;
+}
+
+static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+       struct vf610_adc *info = iio_priv(indio_dev);
+       unsigned int hc_cfg = 0;
+       int val;
+
+       val = readl(info->regs + VF610_REG_ADC_GC);
+       val &= ~VF610_ADC_ADCON;
+       writel(val, info->regs + VF610_REG_ADC_GC);
+
+       hc_cfg |= VF610_ADC_CONV_DISABLE;
+       hc_cfg &= ~VF610_ADC_AIEN;
+
+       writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+       return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+       .postenable = &vf610_adc_buffer_postenable,
+       .predisable = &vf610_adc_buffer_predisable,
+       .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
 static int vf610_adc_reg_access(struct iio_dev *indio_dev,
                        unsigned reg, unsigned writeval,
                        unsigned *readval)
@@ -753,7 +831,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
 
        ret = devm_request_irq(info->dev, irq,
                                vf610_adc_isr, 0,
-                               dev_name(&pdev->dev), info);
+                               dev_name(&pdev->dev), indio_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
                return ret;
@@ -806,15 +884,23 @@ static int vf610_adc_probe(struct platform_device *pdev)
        vf610_adc_cfg_init(info);
        vf610_adc_hw_init(info);
 
+       ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+                                       NULL, &iio_triggered_buffer_setup_ops);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Couldn't initialise the buffer\n");
+               goto error_iio_device_register;
+       }
+
        ret = iio_device_register(indio_dev);
        if (ret) {
                dev_err(&pdev->dev, "Couldn't register the device.\n");
-               goto error_iio_device_register;
+               goto error_adc_buffer_init;
        }
 
        return 0;
 
-
+error_adc_buffer_init:
+       iio_triggered_buffer_cleanup(indio_dev);
 error_iio_device_register:
        clk_disable_unprepare(info->clk);
 error_adc_clk_enable:
@@ -829,6 +915,7 @@ static int vf610_adc_remove(struct platform_device *pdev)
        struct vf610_adc *info = iio_priv(indio_dev);
 
        iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
        regulator_disable(info->vref);
        clk_disable_unprepare(info->clk);
 
index ce93bd8..0370624 100644 (file)
@@ -273,33 +273,13 @@ static void xadc_zynq_unmask_worker(struct work_struct *work)
                schedule_delayed_work(&xadc->zynq_unmask_work,
                                msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
        }
-}
-
-static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid)
-{
-       struct iio_dev *indio_dev = devid;
-       struct xadc *xadc = iio_priv(indio_dev);
-       unsigned int alarm;
-
-       spin_lock_irq(&xadc->lock);
-       alarm = xadc->zynq_alarm;
-       xadc->zynq_alarm = 0;
-       spin_unlock_irq(&xadc->lock);
-
-       xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm));
 
-       /* unmask the required interrupts in timer. */
-       schedule_delayed_work(&xadc->zynq_unmask_work,
-                       msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
-
-       return IRQ_HANDLED;
 }
 
 static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
 {
        struct iio_dev *indio_dev = devid;
        struct xadc *xadc = iio_priv(indio_dev);
-       irqreturn_t ret = IRQ_HANDLED;
        uint32_t status;
 
        xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
@@ -321,18 +301,23 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
 
        status &= XADC_ZYNQ_INT_ALARM_MASK;
        if (status) {
-               xadc->zynq_alarm |= status;
                xadc->zynq_masked_alarm |= status;
                /*
                 * mask the current event interrupt,
                 * unmask it when the interrupt is no more active.
                 */
                xadc_zynq_update_intmsk(xadc, 0, 0);
-               ret = IRQ_WAKE_THREAD;
+
+               xadc_handle_events(indio_dev,
+                               xadc_zynq_transform_alarm(status));
+
+               /* unmask the required interrupts in timer. */
+               schedule_delayed_work(&xadc->zynq_unmask_work,
+                               msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
        }
        spin_unlock(&xadc->lock);
 
-       return ret;
+       return IRQ_HANDLED;
 }
 
 #define XADC_ZYNQ_TCK_RATE_MAX 50000000
@@ -437,7 +422,6 @@ static const struct xadc_ops xadc_zynq_ops = {
        .setup = xadc_zynq_setup,
        .get_dclk_rate = xadc_zynq_get_dclk_rate,
        .interrupt_handler = xadc_zynq_interrupt_handler,
-       .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler,
        .update_alarm = xadc_zynq_update_alarm,
 };
 
@@ -1225,9 +1209,8 @@ static int xadc_probe(struct platform_device *pdev)
        if (ret)
                goto err_free_samplerate_trigger;
 
-       ret = request_threaded_irq(irq, xadc->ops->interrupt_handler,
-                               xadc->ops->threaded_interrupt_handler,
-                               0, dev_name(&pdev->dev), indio_dev);
+       ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
+                       dev_name(&pdev->dev), indio_dev);
        if (ret)
                goto err_clk_disable_unprepare;
 
index 54adc50..f6f0819 100644 (file)
@@ -60,7 +60,6 @@ struct xadc {
 
        enum xadc_external_mux_mode external_mux_mode;
 
-       unsigned int zynq_alarm;
        unsigned int zynq_masked_alarm;
        unsigned int zynq_intmask;
        struct delayed_work zynq_unmask_work;
@@ -79,7 +78,6 @@ struct xadc_ops {
        void (*update_alarm)(struct xadc *, unsigned int);
        unsigned long (*get_dclk_rate)(struct xadc *);
        irqreturn_t (*interrupt_handler)(int, void *);
-       irqreturn_t (*threaded_interrupt_handler)(int, void *);
 
        unsigned int flags;
 };
index c0d364e..32b82a2 100644 (file)
@@ -195,6 +195,7 @@ static const struct spi_device_id ad8366_id[] = {
        {"ad8366", 0},
        {}
 };
+MODULE_DEVICE_TABLE(spi, ad8366_id);
 
 static struct spi_driver ad8366_driver = {
        .driver = {
diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig
new file mode 100644 (file)
index 0000000..0a7b2fd
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# Industrial I/O generic buffer implementations
+#
+# When adding new entries keep the list in alphabetical order
+
+config IIO_BUFFER_CB
+       tristate "IIO callback buffer used for push in-kernel interfaces"
+       help
+         Should be selected by any drivers that do in-kernel push
+         usage.  That is, those where the data is pushed to the consumer.
+
+config IIO_KFIFO_BUF
+       tristate "Industrial I/O buffering based on kfifo"
+       help
+         A simple fifo based on kfifo.  Note that this currently provides
+         no buffer events so it is up to userspace to work out how
+         often to read from the buffer.
+
+config IIO_TRIGGERED_BUFFER
+       tristate
+       select IIO_TRIGGER
+       select IIO_KFIFO_BUF
+       help
+         Provides helper functions for setting up triggered buffers.
diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile
new file mode 100644 (file)
index 0000000..4d193b9
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the industrial I/O buffer implementations
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
+obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
+obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
new file mode 100644 (file)
index 0000000..323079c
--- /dev/null
@@ -0,0 +1,138 @@
+/* The industrial I/O callback buffer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/consumer.h>
+
+struct iio_cb_buffer {
+       struct iio_buffer buffer;
+       int (*cb)(const void *data, void *private);
+       void *private;
+       struct iio_channel *channels;
+};
+
+static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
+{
+       return container_of(buffer, struct iio_cb_buffer, buffer);
+}
+
+static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
+{
+       struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
+       return cb_buff->cb(data, cb_buff->private);
+}
+
+static void iio_buffer_cb_release(struct iio_buffer *buffer)
+{
+       struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
+       kfree(cb_buff->buffer.scan_mask);
+       kfree(cb_buff);
+}
+
+static const struct iio_buffer_access_funcs iio_cb_access = {
+       .store_to = &iio_buffer_cb_store_to,
+       .release = &iio_buffer_cb_release,
+
+       .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
+};
+
+struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
+                                            int (*cb)(const void *data,
+                                                      void *private),
+                                            void *private)
+{
+       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);
+       if (cb_buff == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       iio_buffer_init(&cb_buff->buffer);
+
+       cb_buff->private = private;
+       cb_buff->cb = cb;
+       cb_buff->buffer.access = &iio_cb_access;
+       INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
+
+       cb_buff->channels = iio_channel_get_all(dev);
+       if (IS_ERR(cb_buff->channels)) {
+               ret = PTR_ERR(cb_buff->channels);
+               goto error_free_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);
+       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) {
+                       ret = -EINVAL;
+                       goto error_free_scan_mask;
+               }
+               set_bit(chan->channel->scan_index,
+                       cb_buff->buffer.scan_mask);
+               chan++;
+       }
+
+       return cb_buff;
+
+error_free_scan_mask:
+       kfree(cb_buff->buffer.scan_mask);
+error_release_channels:
+       iio_channel_release_all(cb_buff->channels);
+error_free_cb_buff:
+       kfree(cb_buff);
+       return ERR_PTR(ret);
+}
+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,
+                                 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);
+}
+EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
+
+void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
+{
+       iio_channel_release_all(cb_buff->channels);
+       iio_buffer_put(&cb_buff->buffer);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
+
+struct iio_channel
+*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer)
+{
+       return cb_buffer->channels;
+}
+EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Industrial I/O callback buffer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c
new file mode 100644 (file)
index 0000000..4b2858b
--- /dev/null
@@ -0,0 +1,103 @@
+ /*
+ * Copyright (c) 2012 Analog Devices, Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+       .postenable = &iio_triggered_buffer_postenable,
+       .predisable = &iio_triggered_buffer_predisable,
+};
+
+/**
+ * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc
+ * @indio_dev:         IIO device structure
+ * @h:                 Function which will be used as pollfunc top half
+ * @thread:            Function which will be used as pollfunc bottom half
+ * @setup_ops:         Buffer setup functions to use for this device.
+ *                     If NULL the default setup functions for triggered
+ *                     buffers will be used.
+ *
+ * This function combines some common tasks which will normally be performed
+ * when setting up a triggered buffer. It will allocate the buffer and the
+ * pollfunc.
+ *
+ * Before calling this function the indio_dev structure should already be
+ * completely initialized, but not yet registered. In practice this means that
+ * this function should be called right before iio_device_register().
+ *
+ * To free the resources allocated by this function call
+ * iio_triggered_buffer_cleanup().
+ */
+int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
+       irqreturn_t (*h)(int irq, void *p),
+       irqreturn_t (*thread)(int irq, void *p),
+       const struct iio_buffer_setup_ops *setup_ops)
+{
+       struct iio_buffer *buffer;
+       int ret;
+
+       buffer = iio_kfifo_allocate();
+       if (!buffer) {
+               ret = -ENOMEM;
+               goto error_ret;
+       }
+
+       iio_device_attach_buffer(indio_dev, buffer);
+
+       indio_dev->pollfunc = iio_alloc_pollfunc(h,
+                                                thread,
+                                                IRQF_ONESHOT,
+                                                indio_dev,
+                                                "%s_consumer%d",
+                                                indio_dev->name,
+                                                indio_dev->id);
+       if (indio_dev->pollfunc == NULL) {
+               ret = -ENOMEM;
+               goto error_kfifo_free;
+       }
+
+       /* Ring buffer functions - here trigger setup related */
+       if (setup_ops)
+               indio_dev->setup_ops = setup_ops;
+       else
+               indio_dev->setup_ops = &iio_triggered_buffer_setup_ops;
+
+       /* Flag that polled ring buffering is possible */
+       indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+       return 0;
+
+error_kfifo_free:
+       iio_kfifo_free(indio_dev->buffer);
+error_ret:
+       return ret;
+}
+EXPORT_SYMBOL(iio_triggered_buffer_setup);
+
+/**
+ * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup()
+ * @indio_dev: IIO device structure
+ */
+void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
+{
+       iio_dealloc_pollfunc(indio_dev->pollfunc);
+       iio_kfifo_free(indio_dev->buffer);
+}
+EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
new file mode 100644 (file)
index 0000000..c5b999f
--- /dev/null
@@ -0,0 +1,221 @@
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/kfifo.h>
+#include <linux/mutex.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+struct iio_kfifo {
+       struct iio_buffer buffer;
+       struct kfifo kf;
+       struct mutex user_lock;
+       int update_needed;
+};
+
+#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer)
+
+static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
+                               int bytes_per_datum, int length)
+{
+       if ((length == 0) || (bytes_per_datum == 0))
+               return -EINVAL;
+
+       return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+                            bytes_per_datum, GFP_KERNEL);
+}
+
+static int iio_request_update_kfifo(struct iio_buffer *r)
+{
+       int ret = 0;
+       struct iio_kfifo *buf = iio_to_kfifo(r);
+
+       mutex_lock(&buf->user_lock);
+       if (buf->update_needed) {
+               kfifo_free(&buf->kf);
+               ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
+                                  buf->buffer.length);
+               if (ret >= 0)
+                       buf->update_needed = false;
+       } else {
+               kfifo_reset_out(&buf->kf);
+       }
+       mutex_unlock(&buf->user_lock);
+
+       return ret;
+}
+
+static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
+{
+       struct iio_kfifo *kf = iio_to_kfifo(r);
+       kf->update_needed = true;
+       return 0;
+}
+
+static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
+{
+       if (r->bytes_per_datum != bpd) {
+               r->bytes_per_datum = bpd;
+               iio_mark_update_needed_kfifo(r);
+       }
+       return 0;
+}
+
+static int iio_set_length_kfifo(struct iio_buffer *r, int length)
+{
+       /* Avoid an invalid state */
+       if (length < 2)
+               length = 2;
+       if (r->length != length) {
+               r->length = length;
+               iio_mark_update_needed_kfifo(r);
+       }
+       return 0;
+}
+
+static int iio_store_to_kfifo(struct iio_buffer *r,
+                             const void *data)
+{
+       int ret;
+       struct iio_kfifo *kf = iio_to_kfifo(r);
+       ret = kfifo_in(&kf->kf, data, 1);
+       if (ret != 1)
+               return -EBUSY;
+       return 0;
+}
+
+static int iio_read_first_n_kfifo(struct iio_buffer *r,
+                          size_t n, char __user *buf)
+{
+       int ret, copied;
+       struct iio_kfifo *kf = iio_to_kfifo(r);
+
+       if (mutex_lock_interruptible(&kf->user_lock))
+               return -ERESTARTSYS;
+
+       if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf))
+               ret = -EINVAL;
+       else
+               ret = kfifo_to_user(&kf->kf, buf, n, &copied);
+       mutex_unlock(&kf->user_lock);
+       if (ret < 0)
+               return ret;
+
+       return copied;
+}
+
+static size_t iio_kfifo_buf_data_available(struct iio_buffer *r)
+{
+       struct iio_kfifo *kf = iio_to_kfifo(r);
+       size_t samples;
+
+       mutex_lock(&kf->user_lock);
+       samples = kfifo_len(&kf->kf);
+       mutex_unlock(&kf->user_lock);
+
+       return samples;
+}
+
+static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
+{
+       struct iio_kfifo *kf = iio_to_kfifo(buffer);
+
+       mutex_destroy(&kf->user_lock);
+       kfifo_free(&kf->kf);
+       kfree(kf);
+}
+
+static const struct iio_buffer_access_funcs kfifo_access_funcs = {
+       .store_to = &iio_store_to_kfifo,
+       .read_first_n = &iio_read_first_n_kfifo,
+       .data_available = iio_kfifo_buf_data_available,
+       .request_update = &iio_request_update_kfifo,
+       .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
+       .set_length = &iio_set_length_kfifo,
+       .release = &iio_kfifo_buffer_release,
+
+       .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
+};
+
+struct iio_buffer *iio_kfifo_allocate(void)
+{
+       struct iio_kfifo *kf;
+
+       kf = kzalloc(sizeof(*kf), GFP_KERNEL);
+       if (!kf)
+               return NULL;
+
+       kf->update_needed = true;
+       iio_buffer_init(&kf->buffer);
+       kf->buffer.access = &kfifo_access_funcs;
+       kf->buffer.length = 2;
+       mutex_init(&kf->user_lock);
+
+       return &kf->buffer;
+}
+EXPORT_SYMBOL(iio_kfifo_allocate);
+
+void iio_kfifo_free(struct iio_buffer *r)
+{
+       iio_buffer_put(r);
+}
+EXPORT_SYMBOL(iio_kfifo_free);
+
+static void devm_iio_kfifo_release(struct device *dev, void *res)
+{
+       iio_kfifo_free(*(struct iio_buffer **)res);
+}
+
+static int devm_iio_kfifo_match(struct device *dev, void *res, void *data)
+{
+       struct iio_buffer **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+/**
+ * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate()
+ * @dev:               Device to allocate kfifo buffer for
+ *
+ * RETURNS:
+ * Pointer to allocated iio_buffer on success, NULL on failure.
+ */
+struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
+{
+       struct iio_buffer **ptr, *r;
+
+       ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       r = iio_kfifo_allocate();
+       if (r) {
+               *ptr = r;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return r;
+}
+EXPORT_SYMBOL(devm_iio_kfifo_allocate);
+
+/**
+ * devm_iio_fifo_free - Resource-managed iio_kfifo_free()
+ * @dev:               Device the buffer belongs to
+ * @r:                 The buffer associated with the device
+ */
+void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r)
+{
+       WARN_ON(devres_release(dev, devm_iio_kfifo_release,
+                              devm_iio_kfifo_match, r));
+}
+EXPORT_SYMBOL(devm_iio_kfifo_free);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c
deleted file mode 100644 (file)
index 1648e6e..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/iio/buffer.h>
-#include <linux/iio/consumer.h>
-
-struct iio_cb_buffer {
-       struct iio_buffer buffer;
-       int (*cb)(const void *data, void *private);
-       void *private;
-       struct iio_channel *channels;
-};
-
-static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
-{
-       return container_of(buffer, struct iio_cb_buffer, buffer);
-}
-
-static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
-{
-       struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
-       return cb_buff->cb(data, cb_buff->private);
-}
-
-static void iio_buffer_cb_release(struct iio_buffer *buffer)
-{
-       struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
-       kfree(cb_buff->buffer.scan_mask);
-       kfree(cb_buff);
-}
-
-static const struct iio_buffer_access_funcs iio_cb_access = {
-       .store_to = &iio_buffer_cb_store_to,
-       .release = &iio_buffer_cb_release,
-
-       .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
-};
-
-struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
-                                            int (*cb)(const void *data,
-                                                      void *private),
-                                            void *private)
-{
-       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);
-       if (cb_buff == NULL)
-               return ERR_PTR(-ENOMEM);
-
-       iio_buffer_init(&cb_buff->buffer);
-
-       cb_buff->private = private;
-       cb_buff->cb = cb;
-       cb_buff->buffer.access = &iio_cb_access;
-       INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
-
-       cb_buff->channels = iio_channel_get_all(dev);
-       if (IS_ERR(cb_buff->channels)) {
-               ret = PTR_ERR(cb_buff->channels);
-               goto error_free_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);
-       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) {
-                       ret = -EINVAL;
-                       goto error_free_scan_mask;
-               }
-               set_bit(chan->channel->scan_index,
-                       cb_buff->buffer.scan_mask);
-               chan++;
-       }
-
-       return cb_buff;
-
-error_free_scan_mask:
-       kfree(cb_buff->buffer.scan_mask);
-error_release_channels:
-       iio_channel_release_all(cb_buff->channels);
-error_free_cb_buff:
-       kfree(cb_buff);
-       return ERR_PTR(ret);
-}
-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,
-                                 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);
-}
-EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
-
-void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
-{
-       iio_channel_release_all(cb_buff->channels);
-       iio_buffer_put(&cb_buff->buffer);
-}
-EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
-
-struct iio_channel
-*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer)
-{
-       return cb_buffer->channels;
-}
-EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
new file mode 100644 (file)
index 0000000..3061b72
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Chemical sensors
+#
+
+menu "Chemical Sensors"
+
+config VZ89X
+       tristate "SGX Sensortech MiCS VZ89X VOC sensor"
+       depends on I2C
+       help
+         Say Y here to build I2C interface support for the SGX
+         Sensortech MiCS VZ89X VOC (Volatile Organic Compounds)
+         sensors
+
+endmenu
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
new file mode 100644 (file)
index 0000000..7292f2d
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO chemical sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_VZ89X)            += vz89x.o
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
new file mode 100644 (file)
index 0000000..b454200
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
+ *
+ * Copyright (C) 2015 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/mutex.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VZ89X_REG_MEASUREMENT          0x09
+#define VZ89X_REG_MEASUREMENT_SIZE     6
+
+#define VZ89X_VOC_CO2_IDX              0
+#define VZ89X_VOC_SHORT_IDX            1
+#define VZ89X_VOC_TVOC_IDX             2
+#define VZ89X_VOC_RESISTANCE_IDX       3
+
+struct vz89x_data {
+       struct i2c_client *client;
+       struct mutex lock;
+       unsigned long last_update;
+
+       u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
+};
+
+static const struct iio_chan_spec vz89x_channels[] = {
+       {
+               .type = IIO_CONCENTRATION,
+               .channel2 = IIO_MOD_CO2,
+               .modified = 1,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+               .address = VZ89X_VOC_CO2_IDX,
+       },
+       {
+               .type = IIO_CONCENTRATION,
+               .channel2 = IIO_MOD_VOC,
+               .modified = 1,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .address = VZ89X_VOC_SHORT_IDX,
+               .extend_name = "short",
+       },
+       {
+               .type = IIO_CONCENTRATION,
+               .channel2 = IIO_MOD_VOC,
+               .modified = 1,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+               .address = VZ89X_VOC_TVOC_IDX,
+       },
+       {
+               .type = IIO_RESISTANCE,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .address = VZ89X_VOC_RESISTANCE_IDX,
+       },
+};
+
+static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
+static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
+
+static struct attribute *vz89x_attributes[] = {
+       &iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
+       &iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group vz89x_attrs_group = {
+       .attrs = vz89x_attributes,
+};
+
+static int vz89x_get_measurement(struct vz89x_data *data)
+{
+       int ret;
+       int i;
+
+       /* sensor can only be polled once a second max per datasheet */
+       if (!time_after(jiffies, data->last_update + HZ))
+               return 0;
+
+       ret = i2c_smbus_write_word_data(data->client,
+                                       VZ89X_REG_MEASUREMENT, 0);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
+               ret = i2c_smbus_read_byte(data->client);
+               if (ret < 0)
+                       return ret;
+               data->buffer[i] = ret;
+       }
+
+       data->last_update = jiffies;
+
+       return 0;
+}
+
+static int vz89x_get_resistance_reading(struct vz89x_data *data)
+{
+       u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX];
+
+       return buf[0] | (buf[1] << 8) | (buf[2] << 16);
+}
+
+static int vz89x_read_raw(struct iio_dev *indio_dev,
+                         struct iio_chan_spec const *chan, int *val,
+                         int *val2, long mask)
+{
+       struct vz89x_data *data = iio_priv(indio_dev);
+       int ret = -EINVAL;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&data->lock);
+               ret = vz89x_get_measurement(data);
+               mutex_unlock(&data->lock);
+
+               if (ret)
+                       return ret;
+
+               switch (chan->address) {
+               case VZ89X_VOC_CO2_IDX:
+               case VZ89X_VOC_SHORT_IDX:
+               case VZ89X_VOC_TVOC_IDX:
+                       *val = data->buffer[chan->address];
+                       return IIO_VAL_INT;
+               case VZ89X_VOC_RESISTANCE_IDX:
+                       *val = vz89x_get_resistance_reading(data);
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->type) {
+               case IIO_RESISTANCE:
+                       *val = 10;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case IIO_CHAN_INFO_OFFSET:
+               switch (chan->address) {
+               case VZ89X_VOC_CO2_IDX:
+                       *val = 44;
+                       *val2 = 250000;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               case VZ89X_VOC_TVOC_IDX:
+                       *val = -13;
+                       return IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return ret;
+}
+
+static const struct iio_info vz89x_info = {
+       .attrs          = &vz89x_attrs_group,
+       .read_raw       = vz89x_read_raw,
+       .driver_module  = THIS_MODULE,
+};
+
+static int vz89x_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct vz89x_data *data;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
+                                    I2C_FUNC_SMBUS_BYTE))
+               return -ENODEV;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+       data->last_update = jiffies - HZ;
+       mutex_init(&data->lock);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->info = &vz89x_info,
+       indio_dev->name = dev_name(&client->dev);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+
+       indio_dev->channels = vz89x_channels;
+       indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
+
+       return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id vz89x_id[] = {
+       { "vz89x", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, vz89x_id);
+
+static const struct of_device_id vz89x_dt_ids[] = {
+       { .compatible = "sgx,vz89x" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
+
+static struct i2c_driver vz89x_driver = {
+       .driver = {
+               .name   = "vz89x",
+               .of_match_table = of_match_ptr(vz89x_dt_ids),
+       },
+       .probe = vz89x_probe,
+       .id_table = vz89x_id,
+};
+module_i2c_driver(vz89x_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
+MODULE_LICENSE("GPL v2");
index 2e7fdb5..25258e2 100644 (file)
@@ -44,6 +44,28 @@ st_sensors_write_data_with_mask_error:
        return err;
 }
 
+int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
+                                 unsigned reg, unsigned writeval,
+                                 unsigned *readval)
+{
+       struct st_sensor_data *sdata = iio_priv(indio_dev);
+       u8 readdata;
+       int err;
+
+       if (!readval)
+               return sdata->tf->write_byte(&sdata->tb, sdata->dev,
+                                            (u8)reg, (u8)writeval);
+
+       err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
+       if (err < 0)
+               return err;
+
+       *readval = (unsigned)readdata;
+
+       return 0;
+}
+EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
+
 static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
                        unsigned int odr, struct st_sensor_odr_avl *odr_out)
 {
index fa28100..18a4ad5 100644 (file)
@@ -281,6 +281,12 @@ static int ad7303_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id ad7303_spi_of_match[] = {
+       { .compatible = "adi,ad7303", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
+
 static const struct spi_device_id ad7303_spi_ids[] = {
        { "ad7303", 0 },
        {}
@@ -290,6 +296,7 @@ MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
 static struct spi_driver ad7303_driver = {
        .driver = {
                .name = "ad7303",
+               .of_match_table = of_match_ptr(ad7303_spi_of_match),
                .owner = THIS_MODULE,
        },
        .probe = ad7303_probe,
index 28b8748..86e9e11 100644 (file)
@@ -387,6 +387,7 @@ static const struct of_device_id max5821_of_match[] = {
        { .compatible = "maxim,max5821" },
        { }
 };
+MODULE_DEVICE_TABLE(of, max5821_of_match);
 
 static struct i2c_driver max5821_driver = {
        .driver = {
index 9890c81..73f27e0 100644 (file)
@@ -616,15 +616,24 @@ static int adf4350_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id adf4350_of_match[] = {
+       { .compatible = "adi,adf4350", },
+       { .compatible = "adi,adf4351", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, adf4350_of_match);
+
 static const struct spi_device_id adf4350_id[] = {
        {"adf4350", 4350},
        {"adf4351", 4351},
        {}
 };
+MODULE_DEVICE_TABLE(spi, adf4350_id);
 
 static struct spi_driver adf4350_driver = {
        .driver = {
                .name   = "adf4350",
+               .of_match_table = of_match_ptr(adf4350_of_match),
                .owner  = THIS_MODULE,
        },
        .probe          = adf4350_probe,
index 8d24393..e816d29 100644 (file)
@@ -52,15 +52,26 @@ config ADXRS450
 
 config BMG160
        tristate "BOSCH BMG160 Gyro Sensor"
-       depends on I2C
+       depends on (I2C || SPI_MASTER)
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
+       select BMG160_I2C if (I2C)
+       select BMG160_SPI if (SPI)
        help
-         Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
-         driver. This driver also supports BMI055 gyroscope.
+         Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor
+         driver connected via I2C or SPI. This driver also supports BMI055
+         gyroscope.
 
          This driver can also be built as a module.  If so, the module
-         will be called bmg160.
+         will be called bmg160_i2c or bmg160_spi.
+
+config BMG160_I2C
+       tristate
+       select REGMAP_I2C
+
+config BMG160_SPI
+       tristate
+       select REGMAP_SPI
 
 config HID_SENSOR_GYRO_3D
        depends on HID_SENSOR_HUB
index f46341b..f866a4b 100644 (file)
@@ -8,7 +8,9 @@ obj-$(CONFIG_ADIS16130) += adis16130.o
 obj-$(CONFIG_ADIS16136) += adis16136.o
 obj-$(CONFIG_ADIS16260) += adis16260.o
 obj-$(CONFIG_ADXRS450) += adxrs450.o
-obj-$(CONFIG_BMG160) += bmg160.o
+obj-$(CONFIG_BMG160) += bmg160_core.o
+obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
+obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
 
 obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
 
diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c
deleted file mode 100644 (file)
index 460bf71..0000000
+++ /dev/null
@@ -1,1274 +0,0 @@
-/*
- * BMG160 Gyro Sensor driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * 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 <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/acpi.h>
-#include <linux/gpio/consumer.h>
-#include <linux/pm.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/events.h>
-#include <linux/iio/trigger_consumer.h>
-#include <linux/iio/triggered_buffer.h>
-
-#define BMG160_DRV_NAME                "bmg160"
-#define BMG160_IRQ_NAME                "bmg160_event"
-#define BMG160_GPIO_NAME               "gpio_int"
-
-#define BMG160_REG_CHIP_ID             0x00
-#define BMG160_CHIP_ID_VAL             0x0F
-
-#define BMG160_REG_PMU_LPW             0x11
-#define BMG160_MODE_NORMAL             0x00
-#define BMG160_MODE_DEEP_SUSPEND       0x20
-#define BMG160_MODE_SUSPEND            0x80
-
-#define BMG160_REG_RANGE               0x0F
-
-#define BMG160_RANGE_2000DPS           0
-#define BMG160_RANGE_1000DPS           1
-#define BMG160_RANGE_500DPS            2
-#define BMG160_RANGE_250DPS            3
-#define BMG160_RANGE_125DPS            4
-
-#define BMG160_REG_PMU_BW              0x10
-#define BMG160_NO_FILTER               0
-#define BMG160_DEF_BW                  100
-
-#define BMG160_REG_INT_MAP_0           0x17
-#define BMG160_INT_MAP_0_BIT_ANY       BIT(1)
-
-#define BMG160_REG_INT_MAP_1           0x18
-#define BMG160_INT_MAP_1_BIT_NEW_DATA  BIT(0)
-
-#define BMG160_REG_INT_RST_LATCH       0x21
-#define BMG160_INT_MODE_LATCH_RESET    0x80
-#define BMG160_INT_MODE_LATCH_INT      0x0F
-#define BMG160_INT_MODE_NON_LATCH_INT  0x00
-
-#define BMG160_REG_INT_EN_0            0x15
-#define BMG160_DATA_ENABLE_INT         BIT(7)
-
-#define BMG160_REG_INT_EN_1            0x16
-#define BMG160_INT1_BIT_OD             BIT(1)
-
-#define BMG160_REG_XOUT_L              0x02
-#define BMG160_AXIS_TO_REG(axis)       (BMG160_REG_XOUT_L + (axis * 2))
-
-#define BMG160_REG_SLOPE_THRES         0x1B
-#define BMG160_SLOPE_THRES_MASK        0x0F
-
-#define BMG160_REG_MOTION_INTR         0x1C
-#define BMG160_INT_MOTION_X            BIT(0)
-#define BMG160_INT_MOTION_Y            BIT(1)
-#define BMG160_INT_MOTION_Z            BIT(2)
-#define BMG160_ANY_DUR_MASK            0x30
-#define BMG160_ANY_DUR_SHIFT           4
-
-#define BMG160_REG_INT_STATUS_2        0x0B
-#define BMG160_ANY_MOTION_MASK         0x07
-#define BMG160_ANY_MOTION_BIT_X                BIT(0)
-#define BMG160_ANY_MOTION_BIT_Y                BIT(1)
-#define BMG160_ANY_MOTION_BIT_Z                BIT(2)
-
-#define BMG160_REG_TEMP                0x08
-#define BMG160_TEMP_CENTER_VAL         23
-
-#define BMG160_MAX_STARTUP_TIME_MS     80
-
-#define BMG160_AUTO_SUSPEND_DELAY_MS   2000
-
-struct bmg160_data {
-       struct i2c_client *client;
-       struct iio_trigger *dready_trig;
-       struct iio_trigger *motion_trig;
-       struct mutex mutex;
-       s16 buffer[8];
-       u8 bw_bits;
-       u32 dps_range;
-       int ev_enable_state;
-       int slope_thres;
-       bool dready_trigger_on;
-       bool motion_trigger_on;
-};
-
-enum bmg160_axis {
-       AXIS_X,
-       AXIS_Y,
-       AXIS_Z,
-};
-
-static const struct {
-       int val;
-       int bw_bits;
-} bmg160_samp_freq_table[] = { {100, 0x07},
-                              {200, 0x06},
-                              {400, 0x03},
-                              {1000, 0x02},
-                              {2000, 0x01} };
-
-static const struct {
-       int scale;
-       int dps_range;
-} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS},
-                          { 532, BMG160_RANGE_1000DPS},
-                          { 266, BMG160_RANGE_500DPS},
-                          { 133, BMG160_RANGE_250DPS},
-                          { 66, BMG160_RANGE_125DPS} };
-
-static int bmg160_set_mode(struct bmg160_data *data, u8 mode)
-{
-       int ret;
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_PMU_LPW, mode);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmg160_convert_freq_to_bit(int val)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) {
-               if (bmg160_samp_freq_table[i].val == val)
-                       return bmg160_samp_freq_table[i].bw_bits;
-       }
-
-       return -EINVAL;
-}
-
-static int bmg160_set_bw(struct bmg160_data *data, int val)
-{
-       int ret;
-       int bw_bits;
-
-       bw_bits = bmg160_convert_freq_to_bit(val);
-       if (bw_bits < 0)
-               return bw_bits;
-
-       ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW,
-                                       bw_bits);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_pmu_bw\n");
-               return ret;
-       }
-
-       data->bw_bits = bw_bits;
-
-       return 0;
-}
-
-static int bmg160_chip_init(struct bmg160_data *data)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_chip_id\n");
-               return ret;
-       }
-
-       dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
-       if (ret != BMG160_CHIP_ID_VAL) {
-               dev_err(&data->client->dev, "invalid chip %x\n", ret);
-               return -ENODEV;
-       }
-
-       ret = bmg160_set_mode(data, BMG160_MODE_NORMAL);
-       if (ret < 0)
-               return ret;
-
-       /* Wait upto 500 ms to be ready after changing mode */
-       usleep_range(500, 1000);
-
-       /* Set Bandwidth */
-       ret = bmg160_set_bw(data, BMG160_DEF_BW);
-       if (ret < 0)
-               return ret;
-
-       /* Set Default Range */
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_RANGE,
-                                       BMG160_RANGE_500DPS);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_range\n");
-               return ret;
-       }
-       data->dps_range = BMG160_RANGE_500DPS;
-
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_slope_thres\n");
-               return ret;
-       }
-       data->slope_thres = ret;
-
-       /* Set default interrupt mode */
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
-               return ret;
-       }
-       ret &= ~BMG160_INT1_BIT_OD;
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_EN_1, ret);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
-               return ret;
-       }
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_RST_LATCH,
-                                       BMG160_INT_MODE_LATCH_INT |
-                                       BMG160_INT_MODE_LATCH_RESET);
-       if (ret < 0) {
-               dev_err(&data->client->dev,
-                       "Error writing reg_motion_intr\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmg160_set_power_state(struct bmg160_data *data, bool on)
-{
-#ifdef CONFIG_PM
-       int ret;
-
-       if (on)
-               ret = pm_runtime_get_sync(&data->client->dev);
-       else {
-               pm_runtime_mark_last_busy(&data->client->dev);
-               ret = pm_runtime_put_autosuspend(&data->client->dev);
-       }
-
-       if (ret < 0) {
-               dev_err(&data->client->dev,
-                       "Failed: bmg160_set_power_state for %d\n", on);
-               if (on)
-                       pm_runtime_put_noidle(&data->client->dev);
-
-               return ret;
-       }
-#endif
-
-       return 0;
-}
-
-static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
-                                            bool status)
-{
-       int ret;
-
-       /* Enable/Disable INT_MAP0 mapping */
-       ret = i2c_smbus_read_byte_data(data->client,  BMG160_REG_INT_MAP_0);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_map0\n");
-               return ret;
-       }
-       if (status)
-               ret |= BMG160_INT_MAP_0_BIT_ANY;
-       else
-               ret &= ~BMG160_INT_MAP_0_BIT_ANY;
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_MAP_0,
-                                       ret);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_map0\n");
-               return ret;
-       }
-
-       /* Enable/Disable slope interrupts */
-       if (status) {
-               /* Update slope thres */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_SLOPE_THRES,
-                                               data->slope_thres);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_slope_thres\n");
-                       return ret;
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_MOTION_INTR,
-                                               BMG160_INT_MOTION_X |
-                                               BMG160_INT_MOTION_Y |
-                                               BMG160_INT_MOTION_Z);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_motion_intr\n");
-                       return ret;
-               }
-
-               /*
-                * New data interrupt is always non-latched,
-                * which will have higher priority, so no need
-                * to set latched mode, we will be flooded anyway with INTR
-                */
-               if (!data->dready_trigger_on) {
-                       ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_RST_LATCH,
-                                               BMG160_INT_MODE_LATCH_INT |
-                                               BMG160_INT_MODE_LATCH_RESET);
-                       if (ret < 0) {
-                               dev_err(&data->client->dev,
-                                       "Error writing reg_rst_latch\n");
-                               return ret;
-                       }
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_EN_0,
-                                               BMG160_DATA_ENABLE_INT);
-
-       } else
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_EN_0,
-                                               0);
-
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_en0\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
-                                          bool status)
-{
-       int ret;
-
-       /* Enable/Disable INT_MAP1 mapping */
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_map1\n");
-               return ret;
-       }
-
-       if (status)
-               ret |= BMG160_INT_MAP_1_BIT_NEW_DATA;
-       else
-               ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA;
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_MAP_1,
-                                       ret);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_map1\n");
-               return ret;
-       }
-
-       if (status) {
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_RST_LATCH,
-                                               BMG160_INT_MODE_NON_LATCH_INT |
-                                               BMG160_INT_MODE_LATCH_RESET);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_rst_latch\n");
-                               return ret;
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_EN_0,
-                                               BMG160_DATA_ENABLE_INT);
-
-       } else {
-               /* Restore interrupt mode */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_RST_LATCH,
-                                               BMG160_INT_MODE_LATCH_INT |
-                                               BMG160_INT_MODE_LATCH_RESET);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_rst_latch\n");
-                               return ret;
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMG160_REG_INT_EN_0,
-                                               0);
-       }
-
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_en0\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmg160_get_bw(struct bmg160_data *data, int *val)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) {
-               if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) {
-                       *val = bmg160_samp_freq_table[i].val;
-                       return IIO_VAL_INT;
-               }
-       }
-
-       return -EINVAL;
-}
-
-static int bmg160_set_scale(struct bmg160_data *data, int val)
-{
-       int ret, i;
-
-       for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
-               if (bmg160_scale_table[i].scale == val) {
-                       ret = i2c_smbus_write_byte_data(
-                                       data->client,
-                                       BMG160_REG_RANGE,
-                                       bmg160_scale_table[i].dps_range);
-                       if (ret < 0) {
-                               dev_err(&data->client->dev,
-                                       "Error writing reg_range\n");
-                               return ret;
-                       }
-                       data->dps_range = bmg160_scale_table[i].dps_range;
-                       return 0;
-               }
-       }
-
-       return -EINVAL;
-}
-
-static int bmg160_get_temp(struct bmg160_data *data, int *val)
-{
-       int ret;
-
-       mutex_lock(&data->mutex);
-       ret = bmg160_set_power_state(data, true);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_temp\n");
-               bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       *val = sign_extend32(ret, 7);
-       ret = bmg160_set_power_state(data, false);
-       mutex_unlock(&data->mutex);
-       if (ret < 0)
-               return ret;
-
-       return IIO_VAL_INT;
-}
-
-static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
-{
-       int ret;
-
-       mutex_lock(&data->mutex);
-       ret = bmg160_set_power_state(data, true);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis));
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading axis %d\n", axis);
-               bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       *val = sign_extend32(ret, 15);
-       ret = bmg160_set_power_state(data, false);
-       mutex_unlock(&data->mutex);
-       if (ret < 0)
-               return ret;
-
-       return IIO_VAL_INT;
-}
-
-static int bmg160_read_raw(struct iio_dev *indio_dev,
-                          struct iio_chan_spec const *chan,
-                          int *val, int *val2, long mask)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       switch (mask) {
-       case IIO_CHAN_INFO_RAW:
-               switch (chan->type) {
-               case IIO_TEMP:
-                       return bmg160_get_temp(data, val);
-               case IIO_ANGL_VEL:
-                       if (iio_buffer_enabled(indio_dev))
-                               return -EBUSY;
-                       else
-                               return bmg160_get_axis(data, chan->scan_index,
-                                                      val);
-               default:
-                       return -EINVAL;
-               }
-       case IIO_CHAN_INFO_OFFSET:
-               if (chan->type == IIO_TEMP) {
-                       *val = BMG160_TEMP_CENTER_VAL;
-                       return IIO_VAL_INT;
-               } else
-                       return -EINVAL;
-       case IIO_CHAN_INFO_SCALE:
-               *val = 0;
-               switch (chan->type) {
-               case IIO_TEMP:
-                       *val2 = 500000;
-                       return IIO_VAL_INT_PLUS_MICRO;
-               case IIO_ANGL_VEL:
-               {
-                       int i;
-
-                       for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
-                               if (bmg160_scale_table[i].dps_range ==
-                                                       data->dps_range) {
-                                       *val2 = bmg160_scale_table[i].scale;
-                                       return IIO_VAL_INT_PLUS_MICRO;
-                               }
-                       }
-                       return -EINVAL;
-               }
-               default:
-                       return -EINVAL;
-               }
-       case IIO_CHAN_INFO_SAMP_FREQ:
-               *val2 = 0;
-               mutex_lock(&data->mutex);
-               ret = bmg160_get_bw(data, val);
-               mutex_unlock(&data->mutex);
-               return ret;
-       default:
-               return -EINVAL;
-       }
-}
-
-static int bmg160_write_raw(struct iio_dev *indio_dev,
-                           struct iio_chan_spec const *chan,
-                           int val, int val2, long mask)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       switch (mask) {
-       case IIO_CHAN_INFO_SAMP_FREQ:
-               mutex_lock(&data->mutex);
-               /*
-                * Section 4.2 of spec
-                * In suspend mode, the only supported operations are reading
-                * registers as well as writing to the (0x14) softreset
-                * register. Since we will be in suspend mode by default, change
-                * mode to power on for other writes.
-                */
-               ret = bmg160_set_power_state(data, true);
-               if (ret < 0) {
-                       mutex_unlock(&data->mutex);
-                       return ret;
-               }
-               ret = bmg160_set_bw(data, val);
-               if (ret < 0) {
-                       bmg160_set_power_state(data, false);
-                       mutex_unlock(&data->mutex);
-                       return ret;
-               }
-               ret = bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       case IIO_CHAN_INFO_SCALE:
-               if (val)
-                       return -EINVAL;
-
-               mutex_lock(&data->mutex);
-               /* Refer to comments above for the suspend mode ops */
-               ret = bmg160_set_power_state(data, true);
-               if (ret < 0) {
-                       mutex_unlock(&data->mutex);
-                       return ret;
-               }
-               ret = bmg160_set_scale(data, val2);
-               if (ret < 0) {
-                       bmg160_set_power_state(data, false);
-                       mutex_unlock(&data->mutex);
-                       return ret;
-               }
-               ret = bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       default:
-               return -EINVAL;
-       }
-
-       return -EINVAL;
-}
-
-static int bmg160_read_event(struct iio_dev *indio_dev,
-                            const struct iio_chan_spec *chan,
-                            enum iio_event_type type,
-                            enum iio_event_direction dir,
-                            enum iio_event_info info,
-                            int *val, int *val2)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       *val2 = 0;
-       switch (info) {
-       case IIO_EV_INFO_VALUE:
-               *val = data->slope_thres & BMG160_SLOPE_THRES_MASK;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return IIO_VAL_INT;
-}
-
-static int bmg160_write_event(struct iio_dev *indio_dev,
-                             const struct iio_chan_spec *chan,
-                             enum iio_event_type type,
-                             enum iio_event_direction dir,
-                             enum iio_event_info info,
-                             int val, int val2)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       switch (info) {
-       case IIO_EV_INFO_VALUE:
-               if (data->ev_enable_state)
-                       return -EBUSY;
-               data->slope_thres &= ~BMG160_SLOPE_THRES_MASK;
-               data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int bmg160_read_event_config(struct iio_dev *indio_dev,
-                                   const struct iio_chan_spec *chan,
-                                   enum iio_event_type type,
-                                   enum iio_event_direction dir)
-{
-
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       return data->ev_enable_state;
-}
-
-static int bmg160_write_event_config(struct iio_dev *indio_dev,
-                                    const struct iio_chan_spec *chan,
-                                    enum iio_event_type type,
-                                    enum iio_event_direction dir,
-                                    int state)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       if (state && data->ev_enable_state)
-               return 0;
-
-       mutex_lock(&data->mutex);
-
-       if (!state && data->motion_trigger_on) {
-               data->ev_enable_state = 0;
-               mutex_unlock(&data->mutex);
-               return 0;
-       }
-       /*
-        * We will expect the enable and disable to do operation in
-        * in reverse order. This will happen here anyway as our
-        * resume operation uses sync mode runtime pm calls, the
-        * suspend operation will be delayed by autosuspend delay
-        * So the disable operation will still happen in reverse of
-        * enable operation. When runtime pm is disabled the mode
-        * is always on so sequence doesn't matter
-        */
-       ret = bmg160_set_power_state(data, state);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       ret =  bmg160_setup_any_motion_interrupt(data, state);
-       if (ret < 0) {
-               bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
-       data->ev_enable_state = state;
-       mutex_unlock(&data->mutex);
-
-       return 0;
-}
-
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000");
-
-static IIO_CONST_ATTR(in_anglvel_scale_available,
-                     "0.001065 0.000532 0.000266 0.000133 0.000066");
-
-static struct attribute *bmg160_attributes[] = {
-       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
-       &iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
-       NULL,
-};
-
-static const struct attribute_group bmg160_attrs_group = {
-       .attrs = bmg160_attributes,
-};
-
-static const struct iio_event_spec bmg160_event = {
-               .type = IIO_EV_TYPE_ROC,
-               .dir = IIO_EV_DIR_EITHER,
-               .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
-                                      BIT(IIO_EV_INFO_ENABLE)
-};
-
-#define BMG160_CHANNEL(_axis) {                                        \
-       .type = IIO_ANGL_VEL,                                           \
-       .modified = 1,                                                  \
-       .channel2 = IIO_MOD_##_axis,                                    \
-       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
-       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |          \
-                                   BIT(IIO_CHAN_INFO_SAMP_FREQ),       \
-       .scan_index = AXIS_##_axis,                                     \
-       .scan_type = {                                                  \
-               .sign = 's',                                            \
-               .realbits = 16,                                 \
-               .storagebits = 16,                                      \
-       },                                                              \
-       .event_spec = &bmg160_event,                                    \
-       .num_event_specs = 1                                            \
-}
-
-static const struct iio_chan_spec bmg160_channels[] = {
-       {
-               .type = IIO_TEMP,
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-                                     BIT(IIO_CHAN_INFO_SCALE) |
-                                     BIT(IIO_CHAN_INFO_OFFSET),
-               .scan_index = -1,
-       },
-       BMG160_CHANNEL(X),
-       BMG160_CHANNEL(Y),
-       BMG160_CHANNEL(Z),
-       IIO_CHAN_SOFT_TIMESTAMP(3),
-};
-
-static const struct iio_info bmg160_info = {
-       .attrs                  = &bmg160_attrs_group,
-       .read_raw               = bmg160_read_raw,
-       .write_raw              = bmg160_write_raw,
-       .read_event_value       = bmg160_read_event,
-       .write_event_value      = bmg160_write_event,
-       .write_event_config     = bmg160_write_event_config,
-       .read_event_config      = bmg160_read_event_config,
-       .driver_module          = THIS_MODULE,
-};
-
-static irqreturn_t bmg160_trigger_handler(int irq, void *p)
-{
-       struct iio_poll_func *pf = p;
-       struct iio_dev *indio_dev = pf->indio_dev;
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int bit, ret, i = 0;
-
-       mutex_lock(&data->mutex);
-       for_each_set_bit(bit, indio_dev->active_scan_mask,
-                        indio_dev->masklength) {
-               ret = i2c_smbus_read_word_data(data->client,
-                                              BMG160_AXIS_TO_REG(bit));
-               if (ret < 0) {
-                       mutex_unlock(&data->mutex);
-                       goto err;
-               }
-               data->buffer[i++] = ret;
-       }
-       mutex_unlock(&data->mutex);
-
-       iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
-                                          pf->timestamp);
-err:
-       iio_trigger_notify_done(indio_dev->trig);
-
-       return IRQ_HANDLED;
-}
-
-static int bmg160_trig_try_reen(struct iio_trigger *trig)
-{
-       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       /* new data interrupts don't need ack */
-       if (data->dready_trigger_on)
-               return 0;
-
-       /* Set latched mode interrupt and clear any latched interrupt */
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_RST_LATCH,
-                                       BMG160_INT_MODE_LATCH_INT |
-                                       BMG160_INT_MODE_LATCH_RESET);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_rst_latch\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig,
-                                            bool state)
-{
-       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       mutex_lock(&data->mutex);
-
-       if (!state && data->ev_enable_state && data->motion_trigger_on) {
-               data->motion_trigger_on = false;
-               mutex_unlock(&data->mutex);
-               return 0;
-       }
-
-       /*
-        * Refer to comment in bmg160_write_event_config for
-        * enable/disable operation order
-        */
-       ret = bmg160_set_power_state(data, state);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-       if (data->motion_trig == trig)
-               ret =  bmg160_setup_any_motion_interrupt(data, state);
-       else
-               ret = bmg160_setup_new_data_interrupt(data, state);
-       if (ret < 0) {
-               bmg160_set_power_state(data, false);
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-       if (data->motion_trig == trig)
-               data->motion_trigger_on = state;
-       else
-               data->dready_trigger_on = state;
-
-       mutex_unlock(&data->mutex);
-
-       return 0;
-}
-
-static const struct iio_trigger_ops bmg160_trigger_ops = {
-       .set_trigger_state = bmg160_data_rdy_trigger_set_state,
-       .try_reenable = bmg160_trig_try_reen,
-       .owner = THIS_MODULE,
-};
-
-static irqreturn_t bmg160_event_handler(int irq, void *private)
-{
-       struct iio_dev *indio_dev = private;
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-       int dir;
-
-       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_status2\n");
-               goto ack_intr_status;
-       }
-
-       if (ret & 0x08)
-               dir = IIO_EV_DIR_RISING;
-       else
-               dir = IIO_EV_DIR_FALLING;
-
-       if (ret & BMG160_ANY_MOTION_BIT_X)
-               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
-                                                       0,
-                                                       IIO_MOD_X,
-                                                       IIO_EV_TYPE_ROC,
-                                                       dir),
-                                                       iio_get_time_ns());
-       if (ret & BMG160_ANY_MOTION_BIT_Y)
-               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
-                                                       0,
-                                                       IIO_MOD_Y,
-                                                       IIO_EV_TYPE_ROC,
-                                                       dir),
-                                                       iio_get_time_ns());
-       if (ret & BMG160_ANY_MOTION_BIT_Z)
-               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
-                                                       0,
-                                                       IIO_MOD_Z,
-                                                       IIO_EV_TYPE_ROC,
-                                                       dir),
-                                                       iio_get_time_ns());
-
-ack_intr_status:
-       if (!data->dready_trigger_on) {
-               ret = i2c_smbus_write_byte_data(data->client,
-                                       BMG160_REG_INT_RST_LATCH,
-                                       BMG160_INT_MODE_LATCH_INT |
-                                       BMG160_INT_MODE_LATCH_RESET);
-               if (ret < 0)
-                       dev_err(&data->client->dev,
-                               "Error writing reg_rst_latch\n");
-       }
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
-{
-       struct iio_dev *indio_dev = private;
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       if (data->dready_trigger_on)
-               iio_trigger_poll(data->dready_trig);
-       else if (data->motion_trigger_on)
-               iio_trigger_poll(data->motion_trig);
-
-       if (data->ev_enable_state)
-               return IRQ_WAKE_THREAD;
-       else
-               return IRQ_HANDLED;
-
-}
-
-static int bmg160_buffer_preenable(struct iio_dev *indio_dev)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       return bmg160_set_power_state(data, true);
-}
-
-static int bmg160_buffer_postdisable(struct iio_dev *indio_dev)
-{
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       return bmg160_set_power_state(data, false);
-}
-
-static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
-       .preenable = bmg160_buffer_preenable,
-       .postenable = iio_triggered_buffer_postenable,
-       .predisable = iio_triggered_buffer_predisable,
-       .postdisable = bmg160_buffer_postdisable,
-};
-
-static int bmg160_gpio_probe(struct i2c_client *client,
-                            struct bmg160_data *data)
-
-{
-       struct device *dev;
-       struct gpio_desc *gpio;
-       int ret;
-
-       if (!client)
-               return -EINVAL;
-
-       dev = &client->dev;
-
-       /* data ready gpio interrupt pin */
-       gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
-       if (IS_ERR(gpio)) {
-               dev_err(dev, "acpi gpio get index failed\n");
-               return PTR_ERR(gpio);
-       }
-
-       ret = gpiod_to_irq(gpio);
-
-       dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
-       return ret;
-}
-
-static const char *bmg160_match_acpi_device(struct device *dev)
-{
-       const struct acpi_device_id *id;
-
-       id = acpi_match_device(dev->driver->acpi_match_table, dev);
-       if (!id)
-               return NULL;
-
-       return dev_name(dev);
-}
-
-static int bmg160_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
-{
-       struct bmg160_data *data;
-       struct iio_dev *indio_dev;
-       int ret;
-       const char *name = NULL;
-
-       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
-       if (!indio_dev)
-               return -ENOMEM;
-
-       data = iio_priv(indio_dev);
-       i2c_set_clientdata(client, indio_dev);
-       data->client = client;
-
-       ret = bmg160_chip_init(data);
-       if (ret < 0)
-               return ret;
-
-       mutex_init(&data->mutex);
-
-       if (id)
-               name = id->name;
-
-       if (ACPI_HANDLE(&client->dev))
-               name = bmg160_match_acpi_device(&client->dev);
-
-       indio_dev->dev.parent = &client->dev;
-       indio_dev->channels = bmg160_channels;
-       indio_dev->num_channels = ARRAY_SIZE(bmg160_channels);
-       indio_dev->name = name;
-       indio_dev->modes = INDIO_DIRECT_MODE;
-       indio_dev->info = &bmg160_info;
-
-       if (client->irq <= 0)
-               client->irq = bmg160_gpio_probe(client, data);
-
-       if (client->irq > 0) {
-               ret = devm_request_threaded_irq(&client->dev,
-                                               client->irq,
-                                               bmg160_data_rdy_trig_poll,
-                                               bmg160_event_handler,
-                                               IRQF_TRIGGER_RISING,
-                                               BMG160_IRQ_NAME,
-                                               indio_dev);
-               if (ret)
-                       return ret;
-
-               data->dready_trig = devm_iio_trigger_alloc(&client->dev,
-                                                          "%s-dev%d",
-                                                          indio_dev->name,
-                                                          indio_dev->id);
-               if (!data->dready_trig)
-                       return -ENOMEM;
-
-               data->motion_trig = devm_iio_trigger_alloc(&client->dev,
-                                                         "%s-any-motion-dev%d",
-                                                         indio_dev->name,
-                                                         indio_dev->id);
-               if (!data->motion_trig)
-                       return -ENOMEM;
-
-               data->dready_trig->dev.parent = &client->dev;
-               data->dready_trig->ops = &bmg160_trigger_ops;
-               iio_trigger_set_drvdata(data->dready_trig, indio_dev);
-               ret = iio_trigger_register(data->dready_trig);
-               if (ret)
-                       return ret;
-
-               data->motion_trig->dev.parent = &client->dev;
-               data->motion_trig->ops = &bmg160_trigger_ops;
-               iio_trigger_set_drvdata(data->motion_trig, indio_dev);
-               ret = iio_trigger_register(data->motion_trig);
-               if (ret) {
-                       data->motion_trig = NULL;
-                       goto err_trigger_unregister;
-               }
-       }
-
-       ret = iio_triggered_buffer_setup(indio_dev,
-                                        iio_pollfunc_store_time,
-                                        bmg160_trigger_handler,
-                                        &bmg160_buffer_setup_ops);
-       if (ret < 0) {
-               dev_err(&client->dev,
-                       "iio triggered buffer setup failed\n");
-               goto err_trigger_unregister;
-       }
-
-       ret = iio_device_register(indio_dev);
-       if (ret < 0) {
-               dev_err(&client->dev, "unable to register iio device\n");
-               goto err_buffer_cleanup;
-       }
-
-       ret = pm_runtime_set_active(&client->dev);
-       if (ret)
-               goto err_iio_unregister;
-
-       pm_runtime_enable(&client->dev);
-       pm_runtime_set_autosuspend_delay(&client->dev,
-                                        BMG160_AUTO_SUSPEND_DELAY_MS);
-       pm_runtime_use_autosuspend(&client->dev);
-
-       return 0;
-
-err_iio_unregister:
-       iio_device_unregister(indio_dev);
-err_buffer_cleanup:
-       iio_triggered_buffer_cleanup(indio_dev);
-err_trigger_unregister:
-       if (data->dready_trig)
-               iio_trigger_unregister(data->dready_trig);
-       if (data->motion_trig)
-               iio_trigger_unregister(data->motion_trig);
-
-       return ret;
-}
-
-static int bmg160_remove(struct i2c_client *client)
-{
-       struct iio_dev *indio_dev = i2c_get_clientdata(client);
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       pm_runtime_disable(&client->dev);
-       pm_runtime_set_suspended(&client->dev);
-       pm_runtime_put_noidle(&client->dev);
-
-       iio_device_unregister(indio_dev);
-       iio_triggered_buffer_cleanup(indio_dev);
-
-       if (data->dready_trig) {
-               iio_trigger_unregister(data->dready_trig);
-               iio_trigger_unregister(data->motion_trig);
-       }
-
-       mutex_lock(&data->mutex);
-       bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND);
-       mutex_unlock(&data->mutex);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bmg160_suspend(struct device *dev)
-{
-       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       mutex_lock(&data->mutex);
-       bmg160_set_mode(data, BMG160_MODE_SUSPEND);
-       mutex_unlock(&data->mutex);
-
-       return 0;
-}
-
-static int bmg160_resume(struct device *dev)
-{
-       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
-       struct bmg160_data *data = iio_priv(indio_dev);
-
-       mutex_lock(&data->mutex);
-       if (data->dready_trigger_on || data->motion_trigger_on ||
-                                                       data->ev_enable_state)
-               bmg160_set_mode(data, BMG160_MODE_NORMAL);
-       mutex_unlock(&data->mutex);
-
-       return 0;
-}
-#endif
-
-#ifdef CONFIG_PM
-static int bmg160_runtime_suspend(struct device *dev)
-{
-       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "set mode failed\n");
-               return -EAGAIN;
-       }
-
-       return 0;
-}
-
-static int bmg160_runtime_resume(struct device *dev)
-{
-       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
-       struct bmg160_data *data = iio_priv(indio_dev);
-       int ret;
-
-       ret = bmg160_set_mode(data, BMG160_MODE_NORMAL);
-       if (ret < 0)
-               return ret;
-
-       msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS);
-
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops bmg160_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume)
-       SET_RUNTIME_PM_OPS(bmg160_runtime_suspend,
-                          bmg160_runtime_resume, NULL)
-};
-
-static const struct acpi_device_id bmg160_acpi_match[] = {
-       {"BMG0160", 0},
-       {"BMI055B", 0},
-       {},
-};
-
-MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
-
-static const struct i2c_device_id bmg160_id[] = {
-       {"bmg160", 0},
-       {"bmi055_gyro", 0},
-       {}
-};
-
-MODULE_DEVICE_TABLE(i2c, bmg160_id);
-
-static struct i2c_driver bmg160_driver = {
-       .driver = {
-               .name   = BMG160_DRV_NAME,
-               .acpi_match_table = ACPI_PTR(bmg160_acpi_match),
-               .pm     = &bmg160_pm_ops,
-       },
-       .probe          = bmg160_probe,
-       .remove         = bmg160_remove,
-       .id_table       = bmg160_id,
-};
-module_i2c_driver(bmg160_driver);
-
-MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("BMG160 Gyro driver");
diff --git a/drivers/iio/gyro/bmg160.h b/drivers/iio/gyro/bmg160.h
new file mode 100644 (file)
index 0000000..72db723
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef BMG160_H_
+#define BMG160_H_
+
+extern const struct dev_pm_ops bmg160_pm_ops;
+
+int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
+                     const char *name);
+void bmg160_core_remove(struct device *dev);
+
+#endif  /* BMG160_H_ */
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
new file mode 100644 (file)
index 0000000..02ff789
--- /dev/null
@@ -0,0 +1,1203 @@
+/*
+ * BMG160 Gyro Sensor driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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 <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm.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/events.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+#include "bmg160.h"
+
+#define BMG160_IRQ_NAME                "bmg160_event"
+#define BMG160_GPIO_NAME               "gpio_int"
+
+#define BMG160_REG_CHIP_ID             0x00
+#define BMG160_CHIP_ID_VAL             0x0F
+
+#define BMG160_REG_PMU_LPW             0x11
+#define BMG160_MODE_NORMAL             0x00
+#define BMG160_MODE_DEEP_SUSPEND       0x20
+#define BMG160_MODE_SUSPEND            0x80
+
+#define BMG160_REG_RANGE               0x0F
+
+#define BMG160_RANGE_2000DPS           0
+#define BMG160_RANGE_1000DPS           1
+#define BMG160_RANGE_500DPS            2
+#define BMG160_RANGE_250DPS            3
+#define BMG160_RANGE_125DPS            4
+
+#define BMG160_REG_PMU_BW              0x10
+#define BMG160_NO_FILTER               0
+#define BMG160_DEF_BW                  100
+
+#define BMG160_REG_INT_MAP_0           0x17
+#define BMG160_INT_MAP_0_BIT_ANY       BIT(1)
+
+#define BMG160_REG_INT_MAP_1           0x18
+#define BMG160_INT_MAP_1_BIT_NEW_DATA  BIT(0)
+
+#define BMG160_REG_INT_RST_LATCH       0x21
+#define BMG160_INT_MODE_LATCH_RESET    0x80
+#define BMG160_INT_MODE_LATCH_INT      0x0F
+#define BMG160_INT_MODE_NON_LATCH_INT  0x00
+
+#define BMG160_REG_INT_EN_0            0x15
+#define BMG160_DATA_ENABLE_INT         BIT(7)
+
+#define BMG160_REG_INT_EN_1            0x16
+#define BMG160_INT1_BIT_OD             BIT(1)
+
+#define BMG160_REG_XOUT_L              0x02
+#define BMG160_AXIS_TO_REG(axis)       (BMG160_REG_XOUT_L + (axis * 2))
+
+#define BMG160_REG_SLOPE_THRES         0x1B
+#define BMG160_SLOPE_THRES_MASK        0x0F
+
+#define BMG160_REG_MOTION_INTR         0x1C
+#define BMG160_INT_MOTION_X            BIT(0)
+#define BMG160_INT_MOTION_Y            BIT(1)
+#define BMG160_INT_MOTION_Z            BIT(2)
+#define BMG160_ANY_DUR_MASK            0x30
+#define BMG160_ANY_DUR_SHIFT           4
+
+#define BMG160_REG_INT_STATUS_2        0x0B
+#define BMG160_ANY_MOTION_MASK         0x07
+#define BMG160_ANY_MOTION_BIT_X                BIT(0)
+#define BMG160_ANY_MOTION_BIT_Y                BIT(1)
+#define BMG160_ANY_MOTION_BIT_Z                BIT(2)
+
+#define BMG160_REG_TEMP                0x08
+#define BMG160_TEMP_CENTER_VAL         23
+
+#define BMG160_MAX_STARTUP_TIME_MS     80
+
+#define BMG160_AUTO_SUSPEND_DELAY_MS   2000
+
+struct bmg160_data {
+       struct device *dev;
+       struct regmap *regmap;
+       struct iio_trigger *dready_trig;
+       struct iio_trigger *motion_trig;
+       struct mutex mutex;
+       s16 buffer[8];
+       u8 bw_bits;
+       u32 dps_range;
+       int ev_enable_state;
+       int slope_thres;
+       bool dready_trigger_on;
+       bool motion_trigger_on;
+       int irq;
+};
+
+enum bmg160_axis {
+       AXIS_X,
+       AXIS_Y,
+       AXIS_Z,
+};
+
+static const struct {
+       int val;
+       int bw_bits;
+} bmg160_samp_freq_table[] = { {100, 0x07},
+                              {200, 0x06},
+                              {400, 0x03},
+                              {1000, 0x02},
+                              {2000, 0x01} };
+
+static const struct {
+       int scale;
+       int dps_range;
+} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS},
+                          { 532, BMG160_RANGE_1000DPS},
+                          { 266, BMG160_RANGE_500DPS},
+                          { 133, BMG160_RANGE_250DPS},
+                          { 66, BMG160_RANGE_125DPS} };
+
+static int bmg160_set_mode(struct bmg160_data *data, u8 mode)
+{
+       int ret;
+
+       ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode);
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_pmu_lpw\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bmg160_convert_freq_to_bit(int val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) {
+               if (bmg160_samp_freq_table[i].val == val)
+                       return bmg160_samp_freq_table[i].bw_bits;
+       }
+
+       return -EINVAL;
+}
+
+static int bmg160_set_bw(struct bmg160_data *data, int val)
+{
+       int ret;
+       int bw_bits;
+
+       bw_bits = bmg160_convert_freq_to_bit(val);
+       if (bw_bits < 0)
+               return bw_bits;
+
+       ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits);
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_pmu_bw\n");
+               return ret;
+       }
+
+       data->bw_bits = bw_bits;
+
+       return 0;
+}
+
+static int bmg160_chip_init(struct bmg160_data *data)
+{
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
+       if (ret < 0) {
+               dev_err(data->dev, "Error reading reg_chip_id\n");
+               return ret;
+       }
+
+       dev_dbg(data->dev, "Chip Id %x\n", val);
+       if (val != BMG160_CHIP_ID_VAL) {
+               dev_err(data->dev, "invalid chip %x\n", val);
+               return -ENODEV;
+       }
+
+       ret = bmg160_set_mode(data, BMG160_MODE_NORMAL);
+       if (ret < 0)
+               return ret;
+
+       /* Wait upto 500 ms to be ready after changing mode */
+       usleep_range(500, 1000);
+
+       /* Set Bandwidth */
+       ret = bmg160_set_bw(data, BMG160_DEF_BW);
+       if (ret < 0)
+               return ret;
+
+       /* Set Default Range */
+       ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS);
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_range\n");
+               return ret;
+       }
+       data->dps_range = BMG160_RANGE_500DPS;
+
+       ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val);
+       if (ret < 0) {
+               dev_err(data->dev, "Error reading reg_slope_thres\n");
+               return ret;
+       }
+       data->slope_thres = val;
+
+       /* Set default interrupt mode */
+       ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1,
+                                BMG160_INT1_BIT_OD, 0);
+       if (ret < 0) {
+               dev_err(data->dev, "Error updating bits in reg_int_en_1\n");
+               return ret;
+       }
+
+       ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+                          BMG160_INT_MODE_LATCH_INT |
+                          BMG160_INT_MODE_LATCH_RESET);
+       if (ret < 0) {
+               dev_err(data->dev,
+                       "Error writing reg_motion_intr\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bmg160_set_power_state(struct bmg160_data *data, bool on)
+{
+#ifdef CONFIG_PM
+       int ret;
+
+       if (on)
+               ret = pm_runtime_get_sync(data->dev);
+       else {
+               pm_runtime_mark_last_busy(data->dev);
+               ret = pm_runtime_put_autosuspend(data->dev);
+       }
+
+       if (ret < 0) {
+               dev_err(data->dev,
+                       "Failed: bmg160_set_power_state for %d\n", on);
+               if (on)
+                       pm_runtime_put_noidle(data->dev);
+
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+
+static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
+                                            bool status)
+{
+       int ret;
+
+       /* Enable/Disable INT_MAP0 mapping */
+       ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0,
+                                BMG160_INT_MAP_0_BIT_ANY,
+                                (status ? BMG160_INT_MAP_0_BIT_ANY : 0));
+       if (ret < 0) {
+               dev_err(data->dev, "Error updating bits reg_int_map0\n");
+               return ret;
+       }
+
+       /* Enable/Disable slope interrupts */
+       if (status) {
+               /* Update slope thres */
+               ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES,
+                                  data->slope_thres);
+               if (ret < 0) {
+                       dev_err(data->dev,
+                               "Error writing reg_slope_thres\n");
+                       return ret;
+               }
+
+               ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR,
+                                  BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y |
+                                  BMG160_INT_MOTION_Z);
+               if (ret < 0) {
+                       dev_err(data->dev,
+                               "Error writing reg_motion_intr\n");
+                       return ret;
+               }
+
+               /*
+                * New data interrupt is always non-latched,
+                * which will have higher priority, so no need
+                * to set latched mode, we will be flooded anyway with INTR
+                */
+               if (!data->dready_trigger_on) {
+                       ret = regmap_write(data->regmap,
+                                          BMG160_REG_INT_RST_LATCH,
+                                          BMG160_INT_MODE_LATCH_INT |
+                                          BMG160_INT_MODE_LATCH_RESET);
+                       if (ret < 0) {
+                               dev_err(data->dev,
+                                       "Error writing reg_rst_latch\n");
+                               return ret;
+                       }
+               }
+
+               ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
+                                  BMG160_DATA_ENABLE_INT);
+
+       } else {
+               ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
+       }
+
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_int_en0\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
+                                          bool status)
+{
+       int ret;
+
+       /* Enable/Disable INT_MAP1 mapping */
+       ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1,
+                                BMG160_INT_MAP_1_BIT_NEW_DATA,
+                                (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0));
+       if (ret < 0) {
+               dev_err(data->dev, "Error updating bits in reg_int_map1\n");
+               return ret;
+       }
+
+       if (status) {
+               ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+                                  BMG160_INT_MODE_NON_LATCH_INT |
+                                  BMG160_INT_MODE_LATCH_RESET);
+               if (ret < 0) {
+                       dev_err(data->dev,
+                               "Error writing reg_rst_latch\n");
+                               return ret;
+               }
+
+               ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
+                                  BMG160_DATA_ENABLE_INT);
+
+       } else {
+               /* Restore interrupt mode */
+               ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+                                  BMG160_INT_MODE_LATCH_INT |
+                                  BMG160_INT_MODE_LATCH_RESET);
+               if (ret < 0) {
+                       dev_err(data->dev,
+                               "Error writing reg_rst_latch\n");
+                               return ret;
+               }
+
+               ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
+       }
+
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_int_en0\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bmg160_get_bw(struct bmg160_data *data, int *val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) {
+               if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) {
+                       *val = bmg160_samp_freq_table[i].val;
+                       return IIO_VAL_INT;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int bmg160_set_scale(struct bmg160_data *data, int val)
+{
+       int ret, i;
+
+       for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
+               if (bmg160_scale_table[i].scale == val) {
+                       ret = regmap_write(data->regmap, BMG160_REG_RANGE,
+                                          bmg160_scale_table[i].dps_range);
+                       if (ret < 0) {
+                               dev_err(data->dev,
+                                       "Error writing reg_range\n");
+                               return ret;
+                       }
+                       data->dps_range = bmg160_scale_table[i].dps_range;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int bmg160_get_temp(struct bmg160_data *data, int *val)
+{
+       int ret;
+       unsigned int raw_val;
+
+       mutex_lock(&data->mutex);
+       ret = bmg160_set_power_state(data, true);
+       if (ret < 0) {
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val);
+       if (ret < 0) {
+               dev_err(data->dev, "Error reading reg_temp\n");
+               bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       *val = sign_extend32(raw_val, 7);
+       ret = bmg160_set_power_state(data, false);
+       mutex_unlock(&data->mutex);
+       if (ret < 0)
+               return ret;
+
+       return IIO_VAL_INT;
+}
+
+static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
+{
+       int ret;
+       unsigned int raw_val;
+
+       mutex_lock(&data->mutex);
+       ret = bmg160_set_power_state(data, true);
+       if (ret < 0) {
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val,
+                              2);
+       if (ret < 0) {
+               dev_err(data->dev, "Error reading axis %d\n", axis);
+               bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       *val = sign_extend32(raw_val, 15);
+       ret = bmg160_set_power_state(data, false);
+       mutex_unlock(&data->mutex);
+       if (ret < 0)
+               return ret;
+
+       return IIO_VAL_INT;
+}
+
+static int bmg160_read_raw(struct iio_dev *indio_dev,
+                          struct iio_chan_spec const *chan,
+                          int *val, int *val2, long mask)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               switch (chan->type) {
+               case IIO_TEMP:
+                       return bmg160_get_temp(data, val);
+               case IIO_ANGL_VEL:
+                       if (iio_buffer_enabled(indio_dev))
+                               return -EBUSY;
+                       else
+                               return bmg160_get_axis(data, chan->scan_index,
+                                                      val);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_OFFSET:
+               if (chan->type == IIO_TEMP) {
+                       *val = BMG160_TEMP_CENTER_VAL;
+                       return IIO_VAL_INT;
+               } else
+                       return -EINVAL;
+       case IIO_CHAN_INFO_SCALE:
+               *val = 0;
+               switch (chan->type) {
+               case IIO_TEMP:
+                       *val2 = 500000;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               case IIO_ANGL_VEL:
+               {
+                       int i;
+
+                       for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
+                               if (bmg160_scale_table[i].dps_range ==
+                                                       data->dps_range) {
+                                       *val2 = bmg160_scale_table[i].scale;
+                                       return IIO_VAL_INT_PLUS_MICRO;
+                               }
+                       }
+                       return -EINVAL;
+               }
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *val2 = 0;
+               mutex_lock(&data->mutex);
+               ret = bmg160_get_bw(data, val);
+               mutex_unlock(&data->mutex);
+               return ret;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int bmg160_write_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int val, int val2, long mask)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               mutex_lock(&data->mutex);
+               /*
+                * Section 4.2 of spec
+                * In suspend mode, the only supported operations are reading
+                * registers as well as writing to the (0x14) softreset
+                * register. Since we will be in suspend mode by default, change
+                * mode to power on for other writes.
+                */
+               ret = bmg160_set_power_state(data, true);
+               if (ret < 0) {
+                       mutex_unlock(&data->mutex);
+                       return ret;
+               }
+               ret = bmg160_set_bw(data, val);
+               if (ret < 0) {
+                       bmg160_set_power_state(data, false);
+                       mutex_unlock(&data->mutex);
+                       return ret;
+               }
+               ret = bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       case IIO_CHAN_INFO_SCALE:
+               if (val)
+                       return -EINVAL;
+
+               mutex_lock(&data->mutex);
+               /* Refer to comments above for the suspend mode ops */
+               ret = bmg160_set_power_state(data, true);
+               if (ret < 0) {
+                       mutex_unlock(&data->mutex);
+                       return ret;
+               }
+               ret = bmg160_set_scale(data, val2);
+               if (ret < 0) {
+                       bmg160_set_power_state(data, false);
+                       mutex_unlock(&data->mutex);
+                       return ret;
+               }
+               ret = bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int bmg160_read_event(struct iio_dev *indio_dev,
+                            const struct iio_chan_spec *chan,
+                            enum iio_event_type type,
+                            enum iio_event_direction dir,
+                            enum iio_event_info info,
+                            int *val, int *val2)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       *val2 = 0;
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               *val = data->slope_thres & BMG160_SLOPE_THRES_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return IIO_VAL_INT;
+}
+
+static int bmg160_write_event(struct iio_dev *indio_dev,
+                             const struct iio_chan_spec *chan,
+                             enum iio_event_type type,
+                             enum iio_event_direction dir,
+                             enum iio_event_info info,
+                             int val, int val2)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       switch (info) {
+       case IIO_EV_INFO_VALUE:
+               if (data->ev_enable_state)
+                       return -EBUSY;
+               data->slope_thres &= ~BMG160_SLOPE_THRES_MASK;
+               data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bmg160_read_event_config(struct iio_dev *indio_dev,
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir)
+{
+
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       return data->ev_enable_state;
+}
+
+static int bmg160_write_event_config(struct iio_dev *indio_dev,
+                                    const struct iio_chan_spec *chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir,
+                                    int state)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       if (state && data->ev_enable_state)
+               return 0;
+
+       mutex_lock(&data->mutex);
+
+       if (!state && data->motion_trigger_on) {
+               data->ev_enable_state = 0;
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+       /*
+        * We will expect the enable and disable to do operation in
+        * in reverse order. This will happen here anyway as our
+        * resume operation uses sync mode runtime pm calls, the
+        * suspend operation will be delayed by autosuspend delay
+        * So the disable operation will still happen in reverse of
+        * enable operation. When runtime pm is disabled the mode
+        * is always on so sequence doesn't matter
+        */
+       ret = bmg160_set_power_state(data, state);
+       if (ret < 0) {
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       ret =  bmg160_setup_any_motion_interrupt(data, state);
+       if (ret < 0) {
+               bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+
+       data->ev_enable_state = state;
+       mutex_unlock(&data->mutex);
+
+       return 0;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000");
+
+static IIO_CONST_ATTR(in_anglvel_scale_available,
+                     "0.001065 0.000532 0.000266 0.000133 0.000066");
+
+static struct attribute *bmg160_attributes[] = {
+       &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group bmg160_attrs_group = {
+       .attrs = bmg160_attributes,
+};
+
+static const struct iio_event_spec bmg160_event = {
+               .type = IIO_EV_TYPE_ROC,
+               .dir = IIO_EV_DIR_EITHER,
+               .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+                                      BIT(IIO_EV_INFO_ENABLE)
+};
+
+#define BMG160_CHANNEL(_axis) {                                        \
+       .type = IIO_ANGL_VEL,                                           \
+       .modified = 1,                                                  \
+       .channel2 = IIO_MOD_##_axis,                                    \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |          \
+                                   BIT(IIO_CHAN_INFO_SAMP_FREQ),       \
+       .scan_index = AXIS_##_axis,                                     \
+       .scan_type = {                                                  \
+               .sign = 's',                                            \
+               .realbits = 16,                                 \
+               .storagebits = 16,                                      \
+       },                                                              \
+       .event_spec = &bmg160_event,                                    \
+       .num_event_specs = 1                                            \
+}
+
+static const struct iio_chan_spec bmg160_channels[] = {
+       {
+               .type = IIO_TEMP,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                                     BIT(IIO_CHAN_INFO_SCALE) |
+                                     BIT(IIO_CHAN_INFO_OFFSET),
+               .scan_index = -1,
+       },
+       BMG160_CHANNEL(X),
+       BMG160_CHANNEL(Y),
+       BMG160_CHANNEL(Z),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_info bmg160_info = {
+       .attrs                  = &bmg160_attrs_group,
+       .read_raw               = bmg160_read_raw,
+       .write_raw              = bmg160_write_raw,
+       .read_event_value       = bmg160_read_event,
+       .write_event_value      = bmg160_write_event,
+       .write_event_config     = bmg160_write_event_config,
+       .read_event_config      = bmg160_read_event_config,
+       .driver_module          = THIS_MODULE,
+};
+
+static irqreturn_t bmg160_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int bit, ret, i = 0;
+       unsigned int val;
+
+       mutex_lock(&data->mutex);
+       for_each_set_bit(bit, indio_dev->active_scan_mask,
+                        indio_dev->masklength) {
+               ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit),
+                                      &val, 2);
+               if (ret < 0) {
+                       mutex_unlock(&data->mutex);
+                       goto err;
+               }
+               data->buffer[i++] = ret;
+       }
+       mutex_unlock(&data->mutex);
+
+       iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                                          pf->timestamp);
+err:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int bmg160_trig_try_reen(struct iio_trigger *trig)
+{
+       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       /* new data interrupts don't need ack */
+       if (data->dready_trigger_on)
+               return 0;
+
+       /* Set latched mode interrupt and clear any latched interrupt */
+       ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+                          BMG160_INT_MODE_LATCH_INT |
+                          BMG160_INT_MODE_LATCH_RESET);
+       if (ret < 0) {
+               dev_err(data->dev, "Error writing reg_rst_latch\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig,
+                                            bool state)
+{
+       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&data->mutex);
+
+       if (!state && data->ev_enable_state && data->motion_trigger_on) {
+               data->motion_trigger_on = false;
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+
+       /*
+        * Refer to comment in bmg160_write_event_config for
+        * enable/disable operation order
+        */
+       ret = bmg160_set_power_state(data, state);
+       if (ret < 0) {
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+       if (data->motion_trig == trig)
+               ret =  bmg160_setup_any_motion_interrupt(data, state);
+       else
+               ret = bmg160_setup_new_data_interrupt(data, state);
+       if (ret < 0) {
+               bmg160_set_power_state(data, false);
+               mutex_unlock(&data->mutex);
+               return ret;
+       }
+       if (data->motion_trig == trig)
+               data->motion_trigger_on = state;
+       else
+               data->dready_trigger_on = state;
+
+       mutex_unlock(&data->mutex);
+
+       return 0;
+}
+
+static const struct iio_trigger_ops bmg160_trigger_ops = {
+       .set_trigger_state = bmg160_data_rdy_trigger_set_state,
+       .try_reenable = bmg160_trig_try_reen,
+       .owner = THIS_MODULE,
+};
+
+static irqreturn_t bmg160_event_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+       int dir;
+       unsigned int val;
+
+       ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val);
+       if (ret < 0) {
+               dev_err(data->dev, "Error reading reg_int_status2\n");
+               goto ack_intr_status;
+       }
+
+       if (val & 0x08)
+               dir = IIO_EV_DIR_RISING;
+       else
+               dir = IIO_EV_DIR_FALLING;
+
+       if (val & BMG160_ANY_MOTION_BIT_X)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+                                                       0,
+                                                       IIO_MOD_X,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       iio_get_time_ns());
+       if (val & BMG160_ANY_MOTION_BIT_Y)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+                                                       0,
+                                                       IIO_MOD_Y,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       iio_get_time_ns());
+       if (val & BMG160_ANY_MOTION_BIT_Z)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+                                                       0,
+                                                       IIO_MOD_Z,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       iio_get_time_ns());
+
+ack_intr_status:
+       if (!data->dready_trigger_on) {
+               ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+                                  BMG160_INT_MODE_LATCH_INT |
+                                  BMG160_INT_MODE_LATCH_RESET);
+               if (ret < 0)
+                       dev_err(data->dev,
+                               "Error writing reg_rst_latch\n");
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       if (data->dready_trigger_on)
+               iio_trigger_poll(data->dready_trig);
+       else if (data->motion_trigger_on)
+               iio_trigger_poll(data->motion_trig);
+
+       if (data->ev_enable_state)
+               return IRQ_WAKE_THREAD;
+       else
+               return IRQ_HANDLED;
+
+}
+
+static int bmg160_buffer_preenable(struct iio_dev *indio_dev)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       return bmg160_set_power_state(data, true);
+}
+
+static int bmg160_buffer_postdisable(struct iio_dev *indio_dev)
+{
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       return bmg160_set_power_state(data, false);
+}
+
+static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
+       .preenable = bmg160_buffer_preenable,
+       .postenable = iio_triggered_buffer_postenable,
+       .predisable = iio_triggered_buffer_predisable,
+       .postdisable = bmg160_buffer_postdisable,
+};
+
+static int bmg160_gpio_probe(struct bmg160_data *data)
+
+{
+       struct device *dev;
+       struct gpio_desc *gpio;
+
+       dev = data->dev;
+
+       /* data ready gpio interrupt pin */
+       gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
+       if (IS_ERR(gpio)) {
+               dev_err(dev, "acpi gpio get index failed\n");
+               return PTR_ERR(gpio);
+       }
+
+       data->irq = gpiod_to_irq(gpio);
+
+       dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio),
+               data->irq);
+
+       return 0;
+}
+
+static const char *bmg160_match_acpi_device(struct device *dev)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return NULL;
+
+       return dev_name(dev);
+}
+
+int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
+                     const char *name)
+{
+       struct bmg160_data *data;
+       struct iio_dev *indio_dev;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       dev_set_drvdata(dev, indio_dev);
+       data->dev = dev;
+       data->irq = irq;
+       data->regmap = regmap;
+
+       ret = bmg160_chip_init(data);
+       if (ret < 0)
+               return ret;
+
+       mutex_init(&data->mutex);
+
+       if (ACPI_HANDLE(dev))
+               name = bmg160_match_acpi_device(dev);
+
+       indio_dev->dev.parent = dev;
+       indio_dev->channels = bmg160_channels;
+       indio_dev->num_channels = ARRAY_SIZE(bmg160_channels);
+       indio_dev->name = name;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &bmg160_info;
+
+       if (data->irq <= 0)
+               bmg160_gpio_probe(data);
+
+       if (data->irq > 0) {
+               ret = devm_request_threaded_irq(dev,
+                                               data->irq,
+                                               bmg160_data_rdy_trig_poll,
+                                               bmg160_event_handler,
+                                               IRQF_TRIGGER_RISING,
+                                               BMG160_IRQ_NAME,
+                                               indio_dev);
+               if (ret)
+                       return ret;
+
+               data->dready_trig = devm_iio_trigger_alloc(dev,
+                                                          "%s-dev%d",
+                                                          indio_dev->name,
+                                                          indio_dev->id);
+               if (!data->dready_trig)
+                       return -ENOMEM;
+
+               data->motion_trig = devm_iio_trigger_alloc(dev,
+                                                         "%s-any-motion-dev%d",
+                                                         indio_dev->name,
+                                                         indio_dev->id);
+               if (!data->motion_trig)
+                       return -ENOMEM;
+
+               data->dready_trig->dev.parent = dev;
+               data->dready_trig->ops = &bmg160_trigger_ops;
+               iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+               ret = iio_trigger_register(data->dready_trig);
+               if (ret)
+                       return ret;
+
+               data->motion_trig->dev.parent = dev;
+               data->motion_trig->ops = &bmg160_trigger_ops;
+               iio_trigger_set_drvdata(data->motion_trig, indio_dev);
+               ret = iio_trigger_register(data->motion_trig);
+               if (ret) {
+                       data->motion_trig = NULL;
+                       goto err_trigger_unregister;
+               }
+       }
+
+       ret = iio_triggered_buffer_setup(indio_dev,
+                                        iio_pollfunc_store_time,
+                                        bmg160_trigger_handler,
+                                        &bmg160_buffer_setup_ops);
+       if (ret < 0) {
+               dev_err(dev,
+                       "iio triggered buffer setup failed\n");
+               goto err_trigger_unregister;
+       }
+
+       ret = iio_device_register(indio_dev);
+       if (ret < 0) {
+               dev_err(dev, "unable to register iio device\n");
+               goto err_buffer_cleanup;
+       }
+
+       ret = pm_runtime_set_active(dev);
+       if (ret)
+               goto err_iio_unregister;
+
+       pm_runtime_enable(dev);
+       pm_runtime_set_autosuspend_delay(dev,
+                                        BMG160_AUTO_SUSPEND_DELAY_MS);
+       pm_runtime_use_autosuspend(dev);
+
+       return 0;
+
+err_iio_unregister:
+       iio_device_unregister(indio_dev);
+err_buffer_cleanup:
+       iio_triggered_buffer_cleanup(indio_dev);
+err_trigger_unregister:
+       if (data->dready_trig)
+               iio_trigger_unregister(data->dready_trig);
+       if (data->motion_trig)
+               iio_trigger_unregister(data->motion_trig);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(bmg160_core_probe);
+
+void bmg160_core_remove(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_put_noidle(dev);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       if (data->dready_trig) {
+               iio_trigger_unregister(data->dready_trig);
+               iio_trigger_unregister(data->motion_trig);
+       }
+
+       mutex_lock(&data->mutex);
+       bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND);
+       mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL_GPL(bmg160_core_remove);
+
+#ifdef CONFIG_PM_SLEEP
+static int bmg160_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       mutex_lock(&data->mutex);
+       bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+       mutex_unlock(&data->mutex);
+
+       return 0;
+}
+
+static int bmg160_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct bmg160_data *data = iio_priv(indio_dev);
+
+       mutex_lock(&data->mutex);
+       if (data->dready_trigger_on || data->motion_trigger_on ||
+                                                       data->ev_enable_state)
+               bmg160_set_mode(data, BMG160_MODE_NORMAL);
+       mutex_unlock(&data->mutex);
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int bmg160_runtime_suspend(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+       if (ret < 0) {
+               dev_err(data->dev, "set mode failed\n");
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+static int bmg160_runtime_resume(struct device *dev)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = bmg160_set_mode(data, BMG160_MODE_NORMAL);
+       if (ret < 0)
+               return ret;
+
+       msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS);
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops bmg160_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume)
+       SET_RUNTIME_PM_OPS(bmg160_runtime_suspend,
+                          bmg160_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(bmg160_pm_ops);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMG160 Gyro driver");
diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c
new file mode 100644 (file)
index 0000000..90126a5
--- /dev/null
@@ -0,0 +1,71 @@
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+#include "bmg160.h"
+
+static const struct regmap_config bmg160_regmap_i2c_conf = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0x3f
+};
+
+static int bmg160_i2c_probe(struct i2c_client *client,
+                           const struct i2c_device_id *id)
+{
+       struct regmap *regmap;
+       const char *name = NULL;
+
+       regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf);
+       if (IS_ERR(regmap)) {
+               dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+                       (int)PTR_ERR(regmap));
+               return PTR_ERR(regmap);
+       }
+
+       if (id)
+               name = id->name;
+
+       return bmg160_core_probe(&client->dev, regmap, client->irq, name);
+}
+
+static int bmg160_i2c_remove(struct i2c_client *client)
+{
+       bmg160_core_remove(&client->dev);
+
+       return 0;
+}
+
+static const struct acpi_device_id bmg160_acpi_match[] = {
+       {"BMG0160", 0},
+       {"BMI055B", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
+
+static const struct i2c_device_id bmg160_i2c_id[] = {
+       {"bmg160", 0},
+       {"bmi055_gyro", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id);
+
+static struct i2c_driver bmg160_i2c_driver = {
+       .driver = {
+               .name   = "bmg160_i2c",
+               .acpi_match_table = ACPI_PTR(bmg160_acpi_match),
+               .pm     = &bmg160_pm_ops,
+       },
+       .probe          = bmg160_i2c_probe,
+       .remove         = bmg160_i2c_remove,
+       .id_table       = bmg160_i2c_id,
+};
+module_i2c_driver(bmg160_i2c_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMG160 I2C Gyro driver");
diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c
new file mode 100644 (file)
index 0000000..021ea5f
--- /dev/null
@@ -0,0 +1,57 @@
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+
+#include "bmg160.h"
+
+static const struct regmap_config bmg160_regmap_spi_conf = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = 0x3f,
+};
+
+static int bmg160_spi_probe(struct spi_device *spi)
+{
+       struct regmap *regmap;
+       const struct spi_device_id *id = spi_get_device_id(spi);
+
+       regmap = devm_regmap_init_spi(spi, &bmg160_regmap_spi_conf);
+       if (IS_ERR(regmap)) {
+               dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+                       (int)PTR_ERR(regmap));
+               return PTR_ERR(regmap);
+       }
+
+       return bmg160_core_probe(&spi->dev, regmap, spi->irq, id->name);
+}
+
+static int bmg160_spi_remove(struct spi_device *spi)
+{
+       bmg160_core_remove(&spi->dev);
+
+       return 0;
+}
+
+static const struct spi_device_id bmg160_spi_id[] = {
+       {"bmg160", 0},
+       {"bmi055_gyro", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(spi, bmg160_spi_id);
+
+static struct spi_driver bmg160_spi_driver = {
+       .driver = {
+               .name   = "bmg160_spi",
+               .pm     = &bmg160_pm_ops,
+       },
+       .probe          = bmg160_spi_probe,
+       .remove         = bmg160_spi_remove,
+       .id_table       = bmg160_spi_id,
+};
+module_spi_driver(bmg160_spi_driver);
+
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMG160 SPI Gyro driver");
index 4b993a5..02eddce 100644 (file)
@@ -383,6 +383,7 @@ static const struct iio_info gyro_info = {
        .attrs = &st_gyro_attribute_group,
        .read_raw = &st_gyro_read_raw,
        .write_raw = &st_gyro_write_raw,
+       .debugfs_reg_access = &st_sensors_debugfs_reg_access,
 };
 
 #ifdef CONFIG_IIO_TRIGGER
index 688c0d1..353ee9a 100644 (file)
@@ -12,6 +12,16 @@ config DHT11
          Other sensors should work as well as long as they speak the
          same protocol.
 
+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.
+
+        To compile this driver as a module, choose M here: the module
+        will be called hdc100x.
+
 config SI7005
        tristate "SI7005 relative humidity and temperature sensor"
        depends on I2C
index 86e2d26..3e62c0a 100644 (file)
@@ -3,5 +3,6 @@
 #
 
 obj-$(CONFIG_DHT11) += dht11.o
+obj-$(CONFIG_HDC100X) += hdc100x.o
 obj-$(CONFIG_SI7005) += si7005.o
 obj-$(CONFIG_SI7020) += si7020.o
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
new file mode 100644 (file)
index 0000000..2824578
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
+ *
+ * Copyright (C) 2015 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/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define HDC100X_REG_TEMP                       0x00
+#define HDC100X_REG_HUMIDITY                   0x01
+
+#define HDC100X_REG_CONFIG                     0x02
+#define HDC100X_REG_CONFIG_HEATER_EN           BIT(13)
+
+struct hdc100x_data {
+       struct i2c_client *client;
+       struct mutex lock;
+       u16 config;
+
+       /* integration time of the sensor */
+       int adc_int_us[2];
+};
+
+/* integration time in us */
+static const int hdc100x_int_time[][3] = {
+       { 6350, 3650, 0 },      /* IIO_TEMP channel*/
+       { 6500, 3850, 2500 },   /* IIO_HUMIDITYRELATIVE channel */
+};
+
+/* HDC100X_REG_CONFIG shift and mask values */
+static const struct {
+       int shift;
+       int mask;
+} hdc100x_resolution_shift[2] = {
+       { /* IIO_TEMP channel */
+               .shift = 10,
+               .mask = 1
+       },
+       { /* IIO_HUMIDITYRELATIVE channel */
+               .shift = 8,
+               .mask = 2,
+       },
+};
+
+static IIO_CONST_ATTR(temp_integration_time_available,
+               "0.00365 0.00635");
+
+static IIO_CONST_ATTR(humidityrelative_integration_time_available,
+               "0.0025 0.00385 0.0065");
+
+static IIO_CONST_ATTR(out_current_heater_raw_available,
+               "0 1");
+
+static struct attribute *hdc100x_attributes[] = {
+       &iio_const_attr_temp_integration_time_available.dev_attr.attr,
+       &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr,
+       &iio_const_attr_out_current_heater_raw_available.dev_attr.attr,
+       NULL
+};
+
+static struct attribute_group hdc100x_attribute_group = {
+       .attrs = hdc100x_attributes,
+};
+
+static const struct iio_chan_spec hdc100x_channels[] = {
+       {
+               .type = IIO_TEMP,
+               .address = HDC100X_REG_TEMP,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                       BIT(IIO_CHAN_INFO_SCALE) |
+                       BIT(IIO_CHAN_INFO_INT_TIME) |
+                       BIT(IIO_CHAN_INFO_OFFSET),
+       },
+       {
+               .type = IIO_HUMIDITYRELATIVE,
+               .address = HDC100X_REG_HUMIDITY,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                       BIT(IIO_CHAN_INFO_SCALE) |
+                       BIT(IIO_CHAN_INFO_INT_TIME)
+       },
+       {
+               .type = IIO_CURRENT,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .extend_name = "heater",
+               .output = 1,
+       },
+};
+
+static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
+{
+       int tmp = (~mask & data->config) | val;
+       int ret;
+
+       ret = i2c_smbus_write_word_swapped(data->client,
+                                               HDC100X_REG_CONFIG, tmp);
+       if (!ret)
+               data->config = tmp;
+
+       return ret;
+}
+
+static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2)
+{
+       int shift = hdc100x_resolution_shift[chan].shift;
+       int ret = -EINVAL;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) {
+               if (val2 && val2 == hdc100x_int_time[chan][i]) {
+                       ret = hdc100x_update_config(data,
+                               hdc100x_resolution_shift[chan].mask << shift,
+                               i << shift);
+                       if (!ret)
+                               data->adc_int_us[chan] = val2;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int hdc100x_get_measurement(struct hdc100x_data *data,
+                                  struct iio_chan_spec const *chan)
+{
+       struct i2c_client *client = data->client;
+       int delay = data->adc_int_us[chan->address];
+       int ret;
+       int val;
+
+       /* start measurement */
+       ret = i2c_smbus_write_byte(client, chan->address);
+       if (ret < 0) {
+               dev_err(&client->dev, "cannot start measurement");
+               return ret;
+       }
+
+       /* wait for integration time to pass */
+       usleep_range(delay, delay + 1000);
+
+       /*
+        * i2c_smbus_read_word_data cannot() be used here due to the command
+        * value not being understood and causes NAKs preventing any reading
+        * from being accessed.
+        */
+       ret = i2c_smbus_read_byte(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "cannot read high byte measurement");
+               return ret;
+       }
+       val = ret << 6;
+
+       ret = i2c_smbus_read_byte(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "cannot read low byte measurement");
+               return ret;
+       }
+       val |= ret >> 2;
+
+       return val;
+}
+
+static int hdc100x_get_heater_status(struct hdc100x_data *data)
+{
+       return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN);
+}
+
+static int hdc100x_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan, int *val,
+                           int *val2, long mask)
+{
+       struct hdc100x_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW: {
+               int ret;
+
+               mutex_lock(&data->lock);
+               if (chan->type == IIO_CURRENT) {
+                       *val = hdc100x_get_heater_status(data);
+                       ret = IIO_VAL_INT;
+               } else {
+                       ret = hdc100x_get_measurement(data, chan);
+                       if (ret >= 0) {
+                               *val = ret;
+                               ret = IIO_VAL_INT;
+                       }
+               }
+               mutex_unlock(&data->lock);
+               return ret;
+       }
+       case IIO_CHAN_INFO_INT_TIME:
+               *val = 0;
+               *val2 = data->adc_int_us[chan->address];
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_SCALE:
+               if (chan->type == IIO_TEMP) {
+                       *val = 165;
+                       *val2 = 65536 >> 2;
+                       return IIO_VAL_FRACTIONAL;
+               } else {
+                       *val = 0;
+                       *val2 = 10000;
+                       return IIO_VAL_INT_PLUS_MICRO;
+               }
+               break;
+       case IIO_CHAN_INFO_OFFSET:
+               *val = -40;
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int hdc100x_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct hdc100x_data *data = iio_priv(indio_dev);
+       int ret = -EINVAL;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_INT_TIME:
+               if (val != 0)
+                       return -EINVAL;
+
+               mutex_lock(&data->lock);
+               ret = hdc100x_set_it_time(data, chan->address, val2);
+               mutex_unlock(&data->lock);
+               return ret;
+       case IIO_CHAN_INFO_RAW:
+               if (chan->type != IIO_CURRENT || val2 != 0)
+                       return -EINVAL;
+
+               mutex_lock(&data->lock);
+               ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN,
+                                       val ? HDC100X_REG_CONFIG_HEATER_EN : 0);
+               mutex_unlock(&data->lock);
+               return ret;
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct iio_info hdc100x_info = {
+       .read_raw = hdc100x_read_raw,
+       .write_raw = hdc100x_write_raw,
+       .attrs = &hdc100x_attribute_group,
+       .driver_module = THIS_MODULE,
+};
+
+static int hdc100x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct hdc100x_data *data;
+
+       if (!i2c_check_functionality(client->adapter,
+                               I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+               return -ENODEV;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+       mutex_init(&data->lock);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = dev_name(&client->dev);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &hdc100x_info;
+
+       indio_dev->channels = hdc100x_channels;
+       indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
+
+       /* be sure we are in a known state */
+       hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
+       hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
+
+       return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id hdc100x_id[] = {
+       { "hdc100x", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, hdc100x_id);
+
+static struct i2c_driver hdc100x_driver = {
+       .driver = {
+               .name   = "hdc100x",
+       },
+       .probe = hdc100x_probe,
+       .id_table = hdc100x_id,
+};
+module_i2c_driver(hdc100x_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
index fa3b809..12128d1 100644 (file)
@@ -57,8 +57,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev,
                if (ret < 0)
                        return ret;
                *val = ret >> 2;
+               /*
+                * Humidity values can slightly exceed the 0-100%RH
+                * range and should be corrected by software
+                */
                if (chan->type == IIO_HUMIDITYRELATIVE)
-                       *val &= GENMASK(11, 0);
+                       *val = clamp_val(*val, 786, 13893);
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_SCALE:
                if (chan->type == IIO_TEMP)
index b3fcc2c..208358f 100644 (file)
@@ -75,6 +75,8 @@ static const char * const iio_chan_type_name_spec[] = {
        [IIO_ENERGY] = "energy",
        [IIO_DISTANCE] = "distance",
        [IIO_VELOCITY] = "velocity",
+       [IIO_CONCENTRATION] = "concentration",
+       [IIO_RESISTANCE] = "resistance",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -111,6 +113,8 @@ static const char * const iio_modifier_names[] = {
        [IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
        [IIO_MOD_I] = "i",
        [IIO_MOD_Q] = "q",
+       [IIO_MOD_CO2] = "co2",
+       [IIO_MOD_VOC] = "voc",
 };
 
 /* relies on pairs of these shared then separate */
@@ -962,7 +966,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
 static void iio_dev_release(struct device *device)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(device);
-       if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+       if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
                iio_device_unregister_trigger_consumer(indio_dev);
        iio_device_unregister_eventset(indio_dev);
        iio_device_unregister_sysfs(indio_dev);
@@ -1153,6 +1157,8 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
        if (cmd == IIO_GET_EVENT_FD_IOCTL) {
                fd = iio_event_getfd(indio_dev);
+               if (fd < 0)
+                       return fd;
                if (copy_to_user(ip, &fd, sizeof(fd)))
                        return -EFAULT;
                return 0;
@@ -1241,7 +1247,7 @@ int iio_device_register(struct iio_dev *indio_dev)
                        "Failed to register event set\n");
                goto error_free_sysfs;
        }
-       if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+       if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
                iio_device_register_trigger_consumer(indio_dev);
 
        if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
index 570606c..ae2806a 100644 (file)
@@ -366,10 +366,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
 
        indio_dev->trig = trig;
 
-       if (oldtrig)
+       if (oldtrig) {
+               if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
+                       iio_trigger_detach_poll_func(oldtrig,
+                                                    indio_dev->pollfunc_event);
                iio_trigger_put(oldtrig);
-       if (indio_dev->trig)
+       }
+       if (indio_dev->trig) {
                iio_trigger_get(indio_dev->trig);
+               if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
+                       iio_trigger_attach_poll_func(indio_dev->trig,
+                                                    indio_dev->pollfunc_event);
+       }
 
        return len;
 }
diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c
deleted file mode 100644 (file)
index 4b2858b..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
- /*
- * Copyright (c) 2012 Analog Devices, Inc.
- *  Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/buffer.h>
-#include <linux/iio/kfifo_buf.h>
-#include <linux/iio/triggered_buffer.h>
-#include <linux/iio/trigger_consumer.h>
-
-static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
-       .postenable = &iio_triggered_buffer_postenable,
-       .predisable = &iio_triggered_buffer_predisable,
-};
-
-/**
- * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc
- * @indio_dev:         IIO device structure
- * @h:                 Function which will be used as pollfunc top half
- * @thread:            Function which will be used as pollfunc bottom half
- * @setup_ops:         Buffer setup functions to use for this device.
- *                     If NULL the default setup functions for triggered
- *                     buffers will be used.
- *
- * This function combines some common tasks which will normally be performed
- * when setting up a triggered buffer. It will allocate the buffer and the
- * pollfunc.
- *
- * Before calling this function the indio_dev structure should already be
- * completely initialized, but not yet registered. In practice this means that
- * this function should be called right before iio_device_register().
- *
- * To free the resources allocated by this function call
- * iio_triggered_buffer_cleanup().
- */
-int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
-       irqreturn_t (*h)(int irq, void *p),
-       irqreturn_t (*thread)(int irq, void *p),
-       const struct iio_buffer_setup_ops *setup_ops)
-{
-       struct iio_buffer *buffer;
-       int ret;
-
-       buffer = iio_kfifo_allocate();
-       if (!buffer) {
-               ret = -ENOMEM;
-               goto error_ret;
-       }
-
-       iio_device_attach_buffer(indio_dev, buffer);
-
-       indio_dev->pollfunc = iio_alloc_pollfunc(h,
-                                                thread,
-                                                IRQF_ONESHOT,
-                                                indio_dev,
-                                                "%s_consumer%d",
-                                                indio_dev->name,
-                                                indio_dev->id);
-       if (indio_dev->pollfunc == NULL) {
-               ret = -ENOMEM;
-               goto error_kfifo_free;
-       }
-
-       /* Ring buffer functions - here trigger setup related */
-       if (setup_ops)
-               indio_dev->setup_ops = setup_ops;
-       else
-               indio_dev->setup_ops = &iio_triggered_buffer_setup_ops;
-
-       /* Flag that polled ring buffering is possible */
-       indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
-
-       return 0;
-
-error_kfifo_free:
-       iio_kfifo_free(indio_dev->buffer);
-error_ret:
-       return ret;
-}
-EXPORT_SYMBOL(iio_triggered_buffer_setup);
-
-/**
- * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup()
- * @indio_dev: IIO device structure
- */
-void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
-{
-       iio_dealloc_pollfunc(indio_dev->pollfunc);
-       iio_kfifo_free(indio_dev->buffer);
-}
-EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers");
-MODULE_LICENSE("GPL");
diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
new file mode 100644 (file)
index 0000000..8cc254f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/iio/trigger_consumer.h>
+
+/**
+ * iio_triggered_event_setup() - Setup pollfunc_event for triggered event
+ * @indio_dev: IIO device structure
+ * @h:         Function which will be used as pollfunc_event top half
+ * @thread:    Function which will be used as pollfunc_event bottom half
+ *
+ * This function combines some common tasks which will normally be performed
+ * when setting up a triggered event. It will allocate the pollfunc_event and
+ * set mode to use it for triggered event.
+ *
+ * Before calling this function the indio_dev structure should already be
+ * completely initialized, but not yet registered. In practice this means that
+ * this function should be called right before iio_device_register().
+ *
+ * To free the resources allocated by this function call
+ * iio_triggered_event_cleanup().
+ */
+int iio_triggered_event_setup(struct iio_dev *indio_dev,
+                             irqreturn_t (*h)(int irq, void *p),
+                             irqreturn_t (*thread)(int irq, void *p))
+{
+       indio_dev->pollfunc_event = iio_alloc_pollfunc(h,
+                                                      thread,
+                                                      IRQF_ONESHOT,
+                                                      indio_dev,
+                                                      "%s_consumer%d",
+                                                      indio_dev->name,
+                                                      indio_dev->id);
+       if (indio_dev->pollfunc_event == NULL)
+               return -ENOMEM;
+
+       /* Flag that events polling is possible */
+       indio_dev->modes |= INDIO_EVENT_TRIGGERED;
+
+       return 0;
+}
+EXPORT_SYMBOL(iio_triggered_event_setup);
+
+/**
+ * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
+ * @indio_dev: IIO device structure
+ */
+void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
+{
+       indio_dev->modes &= ~INDIO_EVENT_TRIGGERED;
+       iio_dealloc_pollfunc(indio_dev->pollfunc_event);
+}
+EXPORT_SYMBOL(iio_triggered_event_cleanup);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
deleted file mode 100644 (file)
index c5b999f..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/workqueue.h>
-#include <linux/kfifo.h>
-#include <linux/mutex.h>
-#include <linux/iio/kfifo_buf.h>
-#include <linux/sched.h>
-#include <linux/poll.h>
-
-struct iio_kfifo {
-       struct iio_buffer buffer;
-       struct kfifo kf;
-       struct mutex user_lock;
-       int update_needed;
-};
-
-#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer)
-
-static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
-                               int bytes_per_datum, int length)
-{
-       if ((length == 0) || (bytes_per_datum == 0))
-               return -EINVAL;
-
-       return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
-                            bytes_per_datum, GFP_KERNEL);
-}
-
-static int iio_request_update_kfifo(struct iio_buffer *r)
-{
-       int ret = 0;
-       struct iio_kfifo *buf = iio_to_kfifo(r);
-
-       mutex_lock(&buf->user_lock);
-       if (buf->update_needed) {
-               kfifo_free(&buf->kf);
-               ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
-                                  buf->buffer.length);
-               if (ret >= 0)
-                       buf->update_needed = false;
-       } else {
-               kfifo_reset_out(&buf->kf);
-       }
-       mutex_unlock(&buf->user_lock);
-
-       return ret;
-}
-
-static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
-{
-       struct iio_kfifo *kf = iio_to_kfifo(r);
-       kf->update_needed = true;
-       return 0;
-}
-
-static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
-{
-       if (r->bytes_per_datum != bpd) {
-               r->bytes_per_datum = bpd;
-               iio_mark_update_needed_kfifo(r);
-       }
-       return 0;
-}
-
-static int iio_set_length_kfifo(struct iio_buffer *r, int length)
-{
-       /* Avoid an invalid state */
-       if (length < 2)
-               length = 2;
-       if (r->length != length) {
-               r->length = length;
-               iio_mark_update_needed_kfifo(r);
-       }
-       return 0;
-}
-
-static int iio_store_to_kfifo(struct iio_buffer *r,
-                             const void *data)
-{
-       int ret;
-       struct iio_kfifo *kf = iio_to_kfifo(r);
-       ret = kfifo_in(&kf->kf, data, 1);
-       if (ret != 1)
-               return -EBUSY;
-       return 0;
-}
-
-static int iio_read_first_n_kfifo(struct iio_buffer *r,
-                          size_t n, char __user *buf)
-{
-       int ret, copied;
-       struct iio_kfifo *kf = iio_to_kfifo(r);
-
-       if (mutex_lock_interruptible(&kf->user_lock))
-               return -ERESTARTSYS;
-
-       if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf))
-               ret = -EINVAL;
-       else
-               ret = kfifo_to_user(&kf->kf, buf, n, &copied);
-       mutex_unlock(&kf->user_lock);
-       if (ret < 0)
-               return ret;
-
-       return copied;
-}
-
-static size_t iio_kfifo_buf_data_available(struct iio_buffer *r)
-{
-       struct iio_kfifo *kf = iio_to_kfifo(r);
-       size_t samples;
-
-       mutex_lock(&kf->user_lock);
-       samples = kfifo_len(&kf->kf);
-       mutex_unlock(&kf->user_lock);
-
-       return samples;
-}
-
-static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
-{
-       struct iio_kfifo *kf = iio_to_kfifo(buffer);
-
-       mutex_destroy(&kf->user_lock);
-       kfifo_free(&kf->kf);
-       kfree(kf);
-}
-
-static const struct iio_buffer_access_funcs kfifo_access_funcs = {
-       .store_to = &iio_store_to_kfifo,
-       .read_first_n = &iio_read_first_n_kfifo,
-       .data_available = iio_kfifo_buf_data_available,
-       .request_update = &iio_request_update_kfifo,
-       .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
-       .set_length = &iio_set_length_kfifo,
-       .release = &iio_kfifo_buffer_release,
-
-       .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
-};
-
-struct iio_buffer *iio_kfifo_allocate(void)
-{
-       struct iio_kfifo *kf;
-
-       kf = kzalloc(sizeof(*kf), GFP_KERNEL);
-       if (!kf)
-               return NULL;
-
-       kf->update_needed = true;
-       iio_buffer_init(&kf->buffer);
-       kf->buffer.access = &kfifo_access_funcs;
-       kf->buffer.length = 2;
-       mutex_init(&kf->user_lock);
-
-       return &kf->buffer;
-}
-EXPORT_SYMBOL(iio_kfifo_allocate);
-
-void iio_kfifo_free(struct iio_buffer *r)
-{
-       iio_buffer_put(r);
-}
-EXPORT_SYMBOL(iio_kfifo_free);
-
-static void devm_iio_kfifo_release(struct device *dev, void *res)
-{
-       iio_kfifo_free(*(struct iio_buffer **)res);
-}
-
-static int devm_iio_kfifo_match(struct device *dev, void *res, void *data)
-{
-       struct iio_buffer **r = res;
-
-       if (WARN_ON(!r || !*r))
-               return 0;
-
-       return *r == data;
-}
-
-/**
- * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate()
- * @dev:               Device to allocate kfifo buffer for
- *
- * RETURNS:
- * Pointer to allocated iio_buffer on success, NULL on failure.
- */
-struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
-{
-       struct iio_buffer **ptr, *r;
-
-       ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
-               return NULL;
-
-       r = iio_kfifo_allocate();
-       if (r) {
-               *ptr = r;
-               devres_add(dev, ptr);
-       } else {
-               devres_free(ptr);
-       }
-
-       return r;
-}
-EXPORT_SYMBOL(devm_iio_kfifo_allocate);
-
-/**
- * devm_iio_fifo_free - Resource-managed iio_kfifo_free()
- * @dev:               Device the buffer belongs to
- * @r:                 The buffer associated with the device
- */
-void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r)
-{
-       WARN_ON(devres_release(dev, devm_iio_kfifo_release,
-                              devm_iio_kfifo_match, r));
-}
-EXPORT_SYMBOL(devm_iio_kfifo_free);
-
-MODULE_LICENSE("GPL");
index 7ed859a..cfd3df8 100644 (file)
@@ -50,6 +50,19 @@ config APDS9300
         To compile this driver as a module, choose M here: the
         module will be called apds9300.
 
+config APDS9960
+       tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
+       select REGMAP_I2C
+       select IIO_BUFFER
+       select IIO_KFIFO_BUF
+       depends on I2C
+       help
+         Say Y here to build I2C interface support for the Avago
+         APDS9960 gesture/RGB/ALS/proximity sensor.
+
+         To compile this driver as a module, choose M here: the
+         module will be called apds9960
+
 config BH1750
        tristate "ROHM BH1750 ambient light sensor"
        depends on I2C
@@ -287,6 +300,16 @@ config TSL4531
         To compile this driver as a module, choose M here: the
         module will be called tsl4531.
 
+config US5182D
+       tristate "UPISEMI light and proximity sensor"
+       depends on I2C
+       help
+        If you say yes here you get support for the UPISEMI US5182D
+        ambient light and proximity sensor.
+
+        This driver can also be built as a module.  If so, the module
+        will be called us5182d.
+
 config VCNL4000
        tristate "VCNL4000 combined ALS and proximity sensor"
        depends on I2C
index 91c74c0..b2c3105 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS)          += acpi-als.o
 obj-$(CONFIG_ADJD_S311)                += adjd_s311.o
 obj-$(CONFIG_AL3320A)          += al3320a.o
 obj-$(CONFIG_APDS9300)         += apds9300.o
+obj-$(CONFIG_APDS9960)         += apds9960.o
 obj-$(CONFIG_BH1750)           += bh1750.o
 obj-$(CONFIG_CM32181)          += cm32181.o
 obj-$(CONFIG_CM3232)           += cm3232.o
@@ -27,4 +28,5 @@ obj-$(CONFIG_STK3310)          += stk3310.o
 obj-$(CONFIG_TCS3414)          += tcs3414.o
 obj-$(CONFIG_TCS3472)          += tcs3472.o
 obj-$(CONFIG_TSL4531)          += tsl4531.o
+obj-$(CONFIG_US5182D)          += us5182d.o
 obj-$(CONFIG_VCNL4000)         += vcnl4000.o
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
new file mode 100644 (file)
index 0000000..bf80ce4
--- /dev/null
@@ -0,0 +1,1130 @@
+/*
+ * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
+ *
+ * Copyright (C) 2015 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.
+ *
+ * TODO: gesture + proximity calib offsets
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/of_gpio.h>
+
+#define APDS9960_REGMAP_NAME   "apds9960_regmap"
+#define APDS9960_DRV_NAME      "apds9960"
+
+#define APDS9960_REG_RAM_START 0x00
+#define APDS9960_REG_RAM_END   0x7f
+
+#define APDS9960_REG_ENABLE    0x80
+#define APDS9960_REG_ATIME     0x81
+#define APDS9960_REG_WTIME     0x83
+
+#define APDS9960_REG_AILTL     0x84
+#define APDS9960_REG_AILTH     0x85
+#define APDS9960_REG_AIHTL     0x86
+#define APDS9960_REG_AIHTH     0x87
+
+#define APDS9960_REG_PILT      0x89
+#define APDS9960_REG_PIHT      0x8b
+#define APDS9960_REG_PERS      0x8c
+
+#define APDS9960_REG_CONFIG_1  0x8d
+#define APDS9960_REG_PPULSE    0x8e
+
+#define APDS9960_REG_CONTROL   0x8f
+#define APDS9960_REG_CONTROL_AGAIN_MASK                0x03
+#define APDS9960_REG_CONTROL_PGAIN_MASK                0x0c
+#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT  0
+#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT  2
+
+#define APDS9960_REG_CONFIG_2  0x90
+#define APDS9960_REG_CONFIG_2_GGAIN_MASK       0x60
+#define APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT 5
+
+#define APDS9960_REG_ID                0x92
+
+#define APDS9960_REG_STATUS    0x93
+#define APDS9960_REG_STATUS_PS_INT     BIT(5)
+#define APDS9960_REG_STATUS_ALS_INT    BIT(4)
+#define APDS9960_REG_STATUS_GINT       BIT(2)
+
+#define APDS9960_REG_PDATA     0x9c
+#define APDS9960_REG_POFFSET_UR        0x9d
+#define APDS9960_REG_POFFSET_DL 0x9e
+#define APDS9960_REG_CONFIG_3  0x9f
+
+#define APDS9960_REG_GPENTH    0xa0
+#define APDS9960_REG_GEXTH     0xa1
+
+#define APDS9960_REG_GCONF_1   0xa2
+#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK          0xc0
+#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT    6
+
+#define APDS9960_REG_GCONF_2   0xa3
+#define APDS9960_REG_GOFFSET_U 0xa4
+#define APDS9960_REG_GOFFSET_D 0xa5
+#define APDS9960_REG_GPULSE    0xa6
+#define APDS9960_REG_GOFFSET_L 0xa7
+#define APDS9960_REG_GOFFSET_R 0xa9
+#define APDS9960_REG_GCONF_3   0xaa
+
+#define APDS9960_REG_GCONF_4   0xab
+#define APDS9960_REG_GFLVL     0xae
+#define APDS9960_REG_GSTATUS   0xaf
+
+#define APDS9960_REG_IFORCE    0xe4
+#define APDS9960_REG_PICLEAR   0xe5
+#define APDS9960_REG_CICLEAR   0xe6
+#define APDS9960_REG_AICLEAR   0xe7
+
+#define APDS9960_DEFAULT_PERS  0x33
+#define APDS9960_DEFAULT_GPENTH        0x50
+#define APDS9960_DEFAULT_GEXTH 0x40
+
+#define APDS9960_MAX_PXS_THRES_VAL     255
+#define APDS9960_MAX_ALS_THRES_VAL     0xffff
+#define APDS9960_MAX_INT_TIME_IN_US    1000000
+
+enum apds9960_als_channel_idx {
+       IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE,
+};
+
+#define APDS9960_REG_ALS_BASE  0x94
+#define APDS9960_REG_ALS_CHANNEL(_colour) \
+       (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2))
+
+enum apds9960_gesture_channel_idx {
+       IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT,
+};
+
+#define APDS9960_REG_GFIFO_BASE        0xfc
+#define APDS9960_REG_GFIFO_DIR(_dir) \
+       (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir)
+
+struct apds9960_data {
+       struct i2c_client *client;
+       struct iio_dev *indio_dev;
+       struct mutex lock;
+
+       /* regmap fields */
+       struct regmap *regmap;
+       struct regmap_field *reg_int_als;
+       struct regmap_field *reg_int_ges;
+       struct regmap_field *reg_int_pxs;
+
+       struct regmap_field *reg_enable_als;
+       struct regmap_field *reg_enable_ges;
+       struct regmap_field *reg_enable_pxs;
+
+       /* state */
+       int als_int;
+       int pxs_int;
+       int gesture_mode_running;
+
+       /* gain values */
+       int als_gain;
+       int pxs_gain;
+
+       /* integration time value in us */
+       int als_adc_int_us;
+
+       /* gesture buffer */
+       u8 buffer[4]; /* 4 8-bit channels */
+};
+
+static const struct reg_default apds9960_reg_defaults[] = {
+       /* Default ALS integration time = 2.48ms */
+       { APDS9960_REG_ATIME, 0xff },
+};
+
+static const struct regmap_range apds9960_volatile_ranges[] = {
+       regmap_reg_range(APDS9960_REG_STATUS,
+                               APDS9960_REG_PDATA),
+       regmap_reg_range(APDS9960_REG_GFLVL,
+                               APDS9960_REG_GSTATUS),
+       regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
+                               APDS9960_REG_GFIFO_DIR(RIGHT)),
+       regmap_reg_range(APDS9960_REG_IFORCE,
+                               APDS9960_REG_AICLEAR),
+};
+
+static const struct regmap_access_table apds9960_volatile_table = {
+       .yes_ranges     = apds9960_volatile_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(apds9960_volatile_ranges),
+};
+
+static const struct regmap_range apds9960_precious_ranges[] = {
+       regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END),
+};
+
+static const struct regmap_access_table apds9960_precious_table = {
+       .yes_ranges     = apds9960_precious_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(apds9960_precious_ranges),
+};
+
+static const struct regmap_range apds9960_readable_ranges[] = {
+       regmap_reg_range(APDS9960_REG_ENABLE,
+                               APDS9960_REG_GSTATUS),
+       regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
+                               APDS9960_REG_GFIFO_DIR(RIGHT)),
+};
+
+static const struct regmap_access_table apds9960_readable_table = {
+       .yes_ranges     = apds9960_readable_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(apds9960_readable_ranges),
+};
+
+static const struct regmap_range apds9960_writeable_ranges[] = {
+       regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2),
+       regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4),
+       regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR),
+};
+
+static const struct regmap_access_table apds9960_writeable_table = {
+       .yes_ranges     = apds9960_writeable_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(apds9960_writeable_ranges),
+};
+
+static const struct regmap_config apds9960_regmap_config = {
+       .name = APDS9960_REGMAP_NAME,
+       .reg_bits = 8,
+       .val_bits = 8,
+       .use_single_rw = 1,
+
+       .volatile_table = &apds9960_volatile_table,
+       .precious_table = &apds9960_precious_table,
+       .rd_table = &apds9960_readable_table,
+       .wr_table = &apds9960_writeable_table,
+
+       .reg_defaults = apds9960_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults),
+       .max_register = APDS9960_REG_GFIFO_DIR(RIGHT),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct iio_event_spec apds9960_pxs_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+                       BIT(IIO_EV_INFO_ENABLE),
+       },
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+                       BIT(IIO_EV_INFO_ENABLE),
+       },
+};
+
+static const struct iio_event_spec apds9960_als_event_spec[] = {
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_RISING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+                       BIT(IIO_EV_INFO_ENABLE),
+       },
+       {
+               .type = IIO_EV_TYPE_THRESH,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+                       BIT(IIO_EV_INFO_ENABLE),
+       },
+};
+
+#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \
+       .type = IIO_PROXIMITY, \
+       .channel = _si + 1, \
+       .scan_index = _si, \
+       .indexed = 1, \
+       .scan_type = { \
+               .sign = 'u', \
+               .realbits = 8, \
+               .storagebits = 8, \
+       }, \
+}
+
+#define APDS9960_INTENSITY_CHANNEL(_colour) { \
+       .type = IIO_INTENSITY, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+                       BIT(IIO_CHAN_INFO_INT_TIME), \
+       .channel2 = IIO_MOD_LIGHT_##_colour, \
+       .address = APDS9960_REG_ALS_CHANNEL(_colour), \
+       .modified = 1, \
+       .scan_index = -1, \
+}
+
+static const unsigned long apds9960_scan_masks[] = {0xf, 0};
+
+static const struct iio_chan_spec apds9960_channels[] = {
+       {
+               .type = IIO_PROXIMITY,
+               .address = APDS9960_REG_PDATA,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .channel = 0,
+               .indexed = 0,
+               .scan_index = -1,
+
+               .event_spec = apds9960_pxs_event_spec,
+               .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec),
+       },
+       /* Gesture Sensor */
+       APDS9960_GESTURE_CHANNEL(UP, 0),
+       APDS9960_GESTURE_CHANNEL(DOWN, 1),
+       APDS9960_GESTURE_CHANNEL(LEFT, 2),
+       APDS9960_GESTURE_CHANNEL(RIGHT, 3),
+       /* ALS */
+       {
+               .type = IIO_INTENSITY,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+                       BIT(IIO_CHAN_INFO_INT_TIME),
+               .channel2 = IIO_MOD_LIGHT_CLEAR,
+               .address = APDS9960_REG_ALS_CHANNEL(CLEAR),
+               .modified = 1,
+               .scan_index = -1,
+
+               .event_spec = apds9960_als_event_spec,
+               .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec),
+       },
+       /* RGB Sensor */
+       APDS9960_INTENSITY_CHANNEL(RED),
+       APDS9960_INTENSITY_CHANNEL(GREEN),
+       APDS9960_INTENSITY_CHANNEL(BLUE),
+};
+
+/* integration time in us */
+static const int apds9960_int_time[][2] =
+       { {28000, 246}, {100000, 219}, {200000, 182}, {700000, 0} };
+
+/* gain mapping */
+static const int apds9960_pxs_gain_map[] = {1, 2, 4, 8};
+static const int apds9960_als_gain_map[] = {1, 4, 16, 64};
+
+static IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8");
+static IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64");
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7");
+
+static struct attribute *apds9960_attributes[] = {
+       &iio_const_attr_proximity_scale_available.dev_attr.attr,
+       &iio_const_attr_intensity_scale_available.dev_attr.attr,
+       &iio_const_attr_integration_time_available.dev_attr.attr,
+       NULL,
+};
+
+static struct attribute_group apds9960_attribute_group = {
+       .attrs = apds9960_attributes,
+};
+
+static const struct reg_field apds9960_reg_field_int_als =
+                               REG_FIELD(APDS9960_REG_ENABLE, 4, 4);
+
+static const struct reg_field apds9960_reg_field_int_ges =
+                               REG_FIELD(APDS9960_REG_GCONF_4, 1, 1);
+
+static const struct reg_field apds9960_reg_field_int_pxs =
+                               REG_FIELD(APDS9960_REG_ENABLE, 5, 5);
+
+static const struct reg_field apds9960_reg_field_enable_als =
+                               REG_FIELD(APDS9960_REG_ENABLE, 1, 1);
+
+static const struct reg_field apds9960_reg_field_enable_ges =
+                               REG_FIELD(APDS9960_REG_ENABLE, 6, 6);
+
+static const struct reg_field apds9960_reg_field_enable_pxs =
+                               REG_FIELD(APDS9960_REG_ENABLE, 2, 2);
+
+static int apds9960_set_it_time(struct apds9960_data *data, int val2)
+{
+       int ret = -EINVAL;
+       int idx;
+
+       for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) {
+               if (apds9960_int_time[idx][0] == val2) {
+                       mutex_lock(&data->lock);
+                       ret = regmap_write(data->regmap, APDS9960_REG_ATIME,
+                                                apds9960_int_time[idx][1]);
+                       if (!ret)
+                               data->als_adc_int_us = val2;
+                       mutex_unlock(&data->lock);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int apds9960_set_pxs_gain(struct apds9960_data *data, int val)
+{
+       int ret = -EINVAL;
+       int idx;
+
+       for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) {
+               if (apds9960_pxs_gain_map[idx] == val) {
+                       /* pxs + gesture gains are mirrored */
+                       mutex_lock(&data->lock);
+                       ret = regmap_update_bits(data->regmap,
+                               APDS9960_REG_CONTROL,
+                               APDS9960_REG_CONTROL_PGAIN_MASK,
+                               idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT);
+                       if (ret) {
+                               mutex_unlock(&data->lock);
+                               break;
+                       }
+
+                       ret = regmap_update_bits(data->regmap,
+                               APDS9960_REG_CONFIG_2,
+                               APDS9960_REG_CONFIG_2_GGAIN_MASK,
+                               idx << APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT);
+                       if (!ret)
+                               data->pxs_gain = idx;
+                       mutex_unlock(&data->lock);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int apds9960_set_als_gain(struct apds9960_data *data, int val)
+{
+       int ret = -EINVAL;
+       int idx;
+
+       for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) {
+               if (apds9960_als_gain_map[idx] == val) {
+                       mutex_lock(&data->lock);
+                       ret = regmap_update_bits(data->regmap,
+                                       APDS9960_REG_CONTROL,
+                                       APDS9960_REG_CONTROL_AGAIN_MASK, idx);
+                       if (!ret)
+                               data->als_gain = idx;
+                       mutex_unlock(&data->lock);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int apds9960_set_power_state(struct apds9960_data *data, bool on)
+{
+       struct device *dev = &data->client->dev;
+       int ret = 0;
+
+       mutex_lock(&data->lock);
+
+       if (on) {
+               int suspended;
+
+               suspended = pm_runtime_suspended(dev);
+               ret = pm_runtime_get_sync(dev);
+
+               /* Allow one integration cycle before allowing a reading */
+               if (suspended)
+                       usleep_range(data->als_adc_int_us,
+                                    APDS9960_MAX_INT_TIME_IN_US);
+       } else {
+               ret = pm_runtime_put_autosuspend(dev);
+       }
+
+       mutex_unlock(&data->lock);
+
+       return ret;
+}
+#else
+static int apds9960_set_power_state(struct apds9960_data *data, bool on)
+{
+       return 0;
+}
+#endif
+
+static int apds9960_read_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int *val, int *val2, long mask)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+       u16 buf;
+       int ret = -EINVAL;
+
+       if (data->gesture_mode_running)
+               return -EBUSY;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               apds9960_set_power_state(data, true);
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       ret = regmap_read(data->regmap, chan->address, val);
+                       if (!ret)
+                               ret = IIO_VAL_INT;
+                       break;
+               case IIO_INTENSITY:
+                       ret = regmap_bulk_read(data->regmap, chan->address,
+                                              &buf, 2);
+                       if (!ret)
+                               ret = IIO_VAL_INT;
+                       *val = le16_to_cpu(buf);
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+               apds9960_set_power_state(data, false);
+               break;
+       case IIO_CHAN_INFO_INT_TIME:
+               /* RGB + ALS sensors only have integration time */
+               mutex_lock(&data->lock);
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       *val = 0;
+                       *val2 = data->als_adc_int_us;
+                       ret = IIO_VAL_INT_PLUS_MICRO;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+               mutex_unlock(&data->lock);
+               break;
+       case IIO_CHAN_INFO_SCALE:
+               mutex_lock(&data->lock);
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       *val = apds9960_pxs_gain_map[data->pxs_gain];
+                       ret = IIO_VAL_INT;
+                       break;
+               case IIO_INTENSITY:
+                       *val = apds9960_als_gain_map[data->als_gain];
+                       ret = IIO_VAL_INT;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+               mutex_unlock(&data->lock);
+               break;
+       }
+
+       return ret;
+};
+
+static int apds9960_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_INT_TIME:
+               /* RGB + ALS sensors only have int time */
+               switch (chan->type) {
+               case IIO_INTENSITY:
+                       if (val != 0)
+                               return -EINVAL;
+                       return apds9960_set_it_time(data, val2);
+               default:
+                       return -EINVAL;
+               }
+       case IIO_CHAN_INFO_SCALE:
+               if (val2 != 0)
+                       return -EINVAL;
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       return apds9960_set_pxs_gain(data, val);
+               case IIO_INTENSITY:
+                       return apds9960_set_als_gain(data, val);
+               default:
+                       return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       };
+
+       return 0;
+}
+
+static inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan,
+                                        enum iio_event_direction dir,
+                                        u8 *reg)
+{
+       switch (dir) {
+       case IIO_EV_DIR_RISING:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       *reg = APDS9960_REG_PIHT;
+                       break;
+               case IIO_INTENSITY:
+                       *reg = APDS9960_REG_AIHTL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case IIO_EV_DIR_FALLING:
+               switch (chan->type) {
+               case IIO_PROXIMITY:
+                       *reg = APDS9960_REG_PILT;
+                       break;
+               case IIO_INTENSITY:
+                       *reg = APDS9960_REG_AILTL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int apds9960_read_event(struct iio_dev *indio_dev,
+                              const struct iio_chan_spec *chan,
+                              enum iio_event_type type,
+                              enum iio_event_direction dir,
+                              enum iio_event_info info,
+                              int *val, int *val2)
+{
+       u8 reg;
+       u16 buf;
+       int ret = 0;
+       struct apds9960_data *data = iio_priv(indio_dev);
+
+       if (info != IIO_EV_INFO_VALUE)
+               return -EINVAL;
+
+       ret = apds9960_get_thres_reg(chan, dir, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (chan->type == IIO_PROXIMITY) {
+               ret = regmap_read(data->regmap, reg, val);
+               if (ret < 0)
+                       return ret;
+       } else if (chan->type == IIO_INTENSITY) {
+               ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
+               if (ret < 0)
+                       return ret;
+               *val = le16_to_cpu(buf);
+       } else
+               return -EINVAL;
+
+       *val2 = 0;
+
+       return IIO_VAL_INT;
+}
+
+static int apds9960_write_event(struct iio_dev *indio_dev,
+                               const struct iio_chan_spec *chan,
+                               enum iio_event_type type,
+                               enum iio_event_direction dir,
+                               enum iio_event_info info,
+                               int val, int val2)
+{
+       u8 reg;
+       u16 buf;
+       int ret = 0;
+       struct apds9960_data *data = iio_priv(indio_dev);
+
+       if (info != IIO_EV_INFO_VALUE)
+               return -EINVAL;
+
+       ret = apds9960_get_thres_reg(chan, dir, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (chan->type == IIO_PROXIMITY) {
+               if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL)
+                       return -EINVAL;
+               ret = regmap_write(data->regmap, reg, val);
+               if (ret < 0)
+                       return ret;
+       } else if (chan->type == IIO_INTENSITY) {
+               if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL)
+                       return -EINVAL;
+               buf = cpu_to_le16(val);
+               ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
+               if (ret < 0)
+                       return ret;
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int apds9960_read_event_config(struct iio_dev *indio_dev,
+                                     const struct iio_chan_spec *chan,
+                                     enum iio_event_type type,
+                                     enum iio_event_direction dir)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+
+       switch (chan->type) {
+       case IIO_PROXIMITY:
+               return data->pxs_int;
+       case IIO_INTENSITY:
+               return data->als_int;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int apds9960_write_event_config(struct iio_dev *indio_dev,
+                                      const struct iio_chan_spec *chan,
+                                      enum iio_event_type type,
+                                      enum iio_event_direction dir,
+                                      int state)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+       int ret;
+
+       state = !!state;
+
+       switch (chan->type) {
+       case IIO_PROXIMITY:
+               if (data->pxs_int == state)
+                       return -EINVAL;
+
+               ret = regmap_field_write(data->reg_int_pxs, state);
+               if (ret)
+                       return ret;
+               data->pxs_int = state;
+               apds9960_set_power_state(data, state);
+               break;
+       case IIO_INTENSITY:
+               if (data->als_int == state)
+                       return -EINVAL;
+
+               ret = regmap_field_write(data->reg_int_als, state);
+               if (ret)
+                       return ret;
+               data->als_int = state;
+               apds9960_set_power_state(data, state);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct iio_info apds9960_info = {
+       .driver_module = THIS_MODULE,
+       .attrs = &apds9960_attribute_group,
+       .read_raw = apds9960_read_raw,
+       .write_raw = apds9960_write_raw,
+       .read_event_value = apds9960_read_event,
+       .write_event_value = apds9960_write_event,
+       .read_event_config = apds9960_read_event_config,
+       .write_event_config = apds9960_write_event_config,
+
+};
+
+static inline int apds9660_fifo_is_empty(struct apds9960_data *data)
+{
+       int cnt;
+       int ret;
+
+       ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt);
+       if (ret)
+               return ret;
+
+       return cnt;
+}
+
+static void apds9960_read_gesture_fifo(struct apds9960_data *data)
+{
+       int ret, cnt = 0;
+
+       mutex_lock(&data->lock);
+       data->gesture_mode_running = 1;
+
+       while (cnt-- || (cnt = apds9660_fifo_is_empty(data) > 0)) {
+               ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE,
+                                     &data->buffer, 4);
+
+               if (ret)
+                       goto err_read;
+
+               iio_push_to_buffers(data->indio_dev, data->buffer);
+       }
+
+err_read:
+       data->gesture_mode_running = 0;
+       mutex_unlock(&data->lock);
+}
+
+static irqreturn_t apds9960_interrupt_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct apds9960_data *data = iio_priv(indio_dev);
+       int ret, status;
+
+       ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "irq status reg read failed\n");
+               return IRQ_HANDLED;
+       }
+
+       if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) {
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_EITHER),
+                              iio_get_time_ns());
+               regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1);
+       }
+
+       if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) {
+               iio_push_event(indio_dev,
+                              IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+                                                   IIO_EV_TYPE_THRESH,
+                                                   IIO_EV_DIR_EITHER),
+                              iio_get_time_ns());
+               regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1);
+       }
+
+       if (status & APDS9960_REG_STATUS_GINT)
+               apds9960_read_gesture_fifo(data);
+
+       return IRQ_HANDLED;
+}
+
+static int apds9960_set_powermode(struct apds9960_data *data, bool state)
+{
+       return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state);
+}
+
+static int apds9960_buffer_postenable(struct iio_dev *indio_dev)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = regmap_field_write(data->reg_int_ges, 1);
+       if (ret)
+               return ret;
+
+       ret = regmap_field_write(data->reg_enable_ges, 1);
+       if (ret)
+               return ret;
+
+       pm_runtime_get_sync(&data->client->dev);
+
+       return 0;
+}
+
+static int apds9960_buffer_predisable(struct iio_dev *indio_dev)
+{
+       struct apds9960_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = regmap_field_write(data->reg_enable_ges, 0);
+       if (ret)
+               return ret;
+
+       ret = regmap_field_write(data->reg_int_ges, 0);
+       if (ret)
+               return ret;
+
+       pm_runtime_put_autosuspend(&data->client->dev);
+
+       return 0;
+}
+
+static const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = {
+       .postenable = apds9960_buffer_postenable,
+       .predisable = apds9960_buffer_predisable,
+};
+
+static int apds9960_regfield_init(struct apds9960_data *data)
+{
+       struct device *dev = &data->client->dev;
+       struct regmap *regmap = data->regmap;
+
+       data->reg_int_als = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_int_als);
+       if (IS_ERR(data->reg_int_als)) {
+               dev_err(dev, "INT ALS reg field init failed\n");
+               return PTR_ERR(data->reg_int_als);
+       }
+
+       data->reg_int_ges = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_int_ges);
+       if (IS_ERR(data->reg_int_ges)) {
+               dev_err(dev, "INT gesture reg field init failed\n");
+               return PTR_ERR(data->reg_int_ges);
+       }
+
+       data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_int_pxs);
+       if (IS_ERR(data->reg_int_pxs)) {
+               dev_err(dev, "INT pxs reg field init failed\n");
+               return PTR_ERR(data->reg_int_pxs);
+       }
+
+       data->reg_enable_als = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_enable_als);
+       if (IS_ERR(data->reg_enable_als)) {
+               dev_err(dev, "Enable ALS reg field init failed\n");
+               return PTR_ERR(data->reg_enable_als);
+       }
+
+       data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_enable_ges);
+       if (IS_ERR(data->reg_enable_ges)) {
+               dev_err(dev, "Enable gesture reg field init failed\n");
+               return PTR_ERR(data->reg_enable_ges);
+       }
+
+       data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap,
+                                               apds9960_reg_field_enable_pxs);
+       if (IS_ERR(data->reg_enable_pxs)) {
+               dev_err(dev, "Enable PXS reg field init failed\n");
+               return PTR_ERR(data->reg_enable_pxs);
+       }
+
+       return 0;
+}
+
+static int apds9960_chip_init(struct apds9960_data *data)
+{
+       int ret;
+
+       /* Default IT for ALS of 28 ms */
+       ret = apds9960_set_it_time(data, 28000);
+       if (ret)
+               return ret;
+
+       /* Ensure gesture interrupt is OFF */
+       ret = regmap_field_write(data->reg_int_ges, 0);
+       if (ret)
+               return ret;
+
+       /* Disable gesture sensor, since polling is useless from user-space */
+       ret = regmap_field_write(data->reg_enable_ges, 0);
+       if (ret)
+               return ret;
+
+       /* Ensure proximity interrupt is OFF */
+       ret = regmap_field_write(data->reg_int_pxs, 0);
+       if (ret)
+               return ret;
+
+       /* Enable proximity sensor for polling */
+       ret = regmap_field_write(data->reg_enable_pxs, 1);
+       if (ret)
+               return ret;
+
+       /* Ensure ALS interrupt is OFF */
+       ret = regmap_field_write(data->reg_int_als, 0);
+       if (ret)
+               return ret;
+
+       /* Enable ALS sensor for polling */
+       ret = regmap_field_write(data->reg_enable_als, 1);
+       if (ret)
+               return ret;
+       /*
+        * When enabled trigger an interrupt after 3 readings
+        * outside threshold for ALS + PXS
+        */
+       ret = regmap_write(data->regmap, APDS9960_REG_PERS,
+                          APDS9960_DEFAULT_PERS);
+       if (ret)
+               return ret;
+
+       /*
+        * Wait for 4 event outside gesture threshold to prevent interrupt
+        * flooding.
+        */
+       ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1,
+                       APDS9960_REG_GCONF_1_GFIFO_THRES_MASK,
+                       BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT);
+       if (ret)
+               return ret;
+
+       /* Default ENTER and EXIT thresholds for the GESTURE engine. */
+       ret = regmap_write(data->regmap, APDS9960_REG_GPENTH,
+                          APDS9960_DEFAULT_GPENTH);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(data->regmap, APDS9960_REG_GEXTH,
+                          APDS9960_DEFAULT_GEXTH);
+       if (ret)
+               return ret;
+
+       return apds9960_set_powermode(data, 1);
+}
+
+static int apds9960_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct apds9960_data *data;
+       struct iio_buffer *buffer;
+       struct iio_dev *indio_dev;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       buffer = devm_iio_kfifo_allocate(&client->dev);
+       if (!buffer)
+               return -ENOMEM;
+
+       iio_device_attach_buffer(indio_dev, buffer);
+
+       indio_dev->info = &apds9960_info;
+       indio_dev->name = APDS9960_DRV_NAME;
+       indio_dev->channels = apds9960_channels;
+       indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
+       indio_dev->available_scan_masks = apds9960_scan_masks;
+       indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
+       indio_dev->setup_ops = &apds9960_buffer_setup_ops;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+
+       data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config);
+       if (IS_ERR(data->regmap)) {
+               dev_err(&client->dev, "regmap initialization failed.\n");
+               return PTR_ERR(data->regmap);
+       }
+
+       data->client = client;
+       data->indio_dev = indio_dev;
+       mutex_init(&data->lock);
+
+       ret = pm_runtime_set_active(&client->dev);
+       if (ret)
+               goto error_power_down;
+
+       pm_runtime_enable(&client->dev);
+       pm_runtime_set_autosuspend_delay(&client->dev, 5000);
+       pm_runtime_use_autosuspend(&client->dev);
+
+       apds9960_set_power_state(data, true);
+
+       ret = apds9960_regfield_init(data);
+       if (ret)
+               goto error_power_down;
+
+       ret = apds9960_chip_init(data);
+       if (ret)
+               goto error_power_down;
+
+       if (client->irq <= 0) {
+               dev_err(&client->dev, "no valid irq defined\n");
+               ret = -EINVAL;
+               goto error_power_down;
+       }
+       ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                       NULL, apds9960_interrupt_handler,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       "apds9960_event",
+                                       indio_dev);
+       if (ret) {
+               dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+               goto error_power_down;
+       }
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto error_power_down;
+
+       apds9960_set_power_state(data, false);
+
+       return 0;
+
+error_power_down:
+       apds9960_set_power_state(data, false);
+
+       return ret;
+}
+
+static int apds9960_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct apds9960_data *data = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       apds9960_set_powermode(data, 0);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int apds9960_runtime_suspend(struct device *dev)
+{
+       struct apds9960_data *data =
+                       iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+       return apds9960_set_powermode(data, 0);
+}
+
+static int apds9960_runtime_resume(struct device *dev)
+{
+       struct apds9960_data *data =
+                       iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+       return apds9960_set_powermode(data, 1);
+}
+#endif
+
+static const struct dev_pm_ops apds9960_pm_ops = {
+       SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
+                          apds9960_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id apds9960_id[] = {
+       { "apds9960", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, apds9960_id);
+
+static struct i2c_driver apds9960_driver = {
+       .driver = {
+               .name   = APDS9960_DRV_NAME,
+               .pm     = &apds9960_pm_ops,
+       },
+       .probe          = apds9960_probe,
+       .remove         = apds9960_remove,
+       .id_table       = apds9960_id,
+};
+module_i2c_driver(apds9960_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
+MODULE_LICENSE("GPL");
index 923aa6a..01e111e 100644 (file)
@@ -793,7 +793,6 @@ static struct i2c_driver opt3001_driver = {
        .driver = {
                .name = "opt3001",
                .of_match_table = of_match_ptr(opt3001_of_match),
-               .owner = THIS_MODULE,
        },
 };
 
index 993eb20..013b217 100644 (file)
@@ -35,8 +35,8 @@
 #define STK3310_REG_ID                         0x3E
 #define STK3310_MAX_REG                                0x80
 
-#define STK3310_STATE_EN_PS                    0x01
-#define STK3310_STATE_EN_ALS                   0x02
+#define STK3310_STATE_EN_PS                    BIT(0)
+#define STK3310_STATE_EN_ALS                   BIT(1)
 #define STK3310_STATE_STANDBY                  0x00
 
 #define STK3310_CHIP_ID_VAL                    0x13
@@ -241,8 +241,11 @@ static int stk3310_write_event(struct iio_dev *indio_dev,
        struct stk3310_data *data = iio_priv(indio_dev);
        struct i2c_client *client = data->client;
 
-       regmap_field_read(data->reg_ps_gain, &index);
-       if (val > stk3310_ps_max[index])
+       ret = regmap_field_read(data->reg_ps_gain, &index);
+       if (ret < 0)
+               return ret;
+
+       if (val < 0 || val > stk3310_ps_max[index])
                return -EINVAL;
 
        if (dir == IIO_EV_DIR_RISING)
@@ -266,9 +269,12 @@ static int stk3310_read_event_config(struct iio_dev *indio_dev,
                                     enum iio_event_direction dir)
 {
        unsigned int event_val;
+       int ret;
        struct stk3310_data *data = iio_priv(indio_dev);
 
-       regmap_field_read(data->reg_int_ps, &event_val);
+       ret = regmap_field_read(data->reg_int_ps, &event_val);
+       if (ret < 0)
+               return ret;
 
        return event_val;
 }
@@ -307,14 +313,16 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
        struct stk3310_data *data = iio_priv(indio_dev);
        struct i2c_client *client = data->client;
 
+       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+               return -EINVAL;
+
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                if (chan->type == IIO_LIGHT)
                        reg = STK3310_REG_ALS_DATA_MSB;
-               else if (chan->type == IIO_PROXIMITY)
-                       reg = STK3310_REG_PS_DATA_MSB;
                else
-                       return -EINVAL;
+                       reg = STK3310_REG_PS_DATA_MSB;
+
                mutex_lock(&data->lock);
                ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
                if (ret < 0) {
@@ -327,17 +335,23 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_INT_TIME:
                if (chan->type == IIO_LIGHT)
-                       regmap_field_read(data->reg_als_it, &index);
+                       ret = regmap_field_read(data->reg_als_it, &index);
                else
-                       regmap_field_read(data->reg_ps_it, &index);
+                       ret = regmap_field_read(data->reg_ps_it, &index);
+               if (ret < 0)
+                       return ret;
+
                *val = stk3310_it_table[index][0];
                *val2 = stk3310_it_table[index][1];
                return IIO_VAL_INT_PLUS_MICRO;
        case IIO_CHAN_INFO_SCALE:
                if (chan->type == IIO_LIGHT)
-                       regmap_field_read(data->reg_als_gain, &index);
+                       ret = regmap_field_read(data->reg_als_gain, &index);
                else
-                       regmap_field_read(data->reg_ps_gain, &index);
+                       ret = regmap_field_read(data->reg_ps_gain, &index);
+               if (ret < 0)
+                       return ret;
+
                *val = stk3310_scale_table[index][0];
                *val2 = stk3310_scale_table[index][1];
                return IIO_VAL_INT_PLUS_MICRO;
@@ -354,6 +368,9 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
        int index;
        struct stk3310_data *data = iio_priv(indio_dev);
 
+       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+               return -EINVAL;
+
        switch (mask) {
        case IIO_CHAN_INFO_INT_TIME:
                index = stk3310_get_index(stk3310_it_table,
@@ -368,7 +385,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
                        ret = regmap_field_write(data->reg_ps_it, index);
                if (ret < 0)
                        dev_err(&data->client->dev,
-                                       "sensor configuration failed\n");
+                               "sensor configuration failed\n");
                mutex_unlock(&data->lock);
                return ret;
 
@@ -385,7 +402,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
                        ret = regmap_field_write(data->reg_ps_gain, index);
                if (ret < 0)
                        dev_err(&data->client->dev,
-                                       "sensor configuration failed\n");
+                               "sensor configuration failed\n");
                mutex_unlock(&data->lock);
                return ret;
        }
@@ -419,8 +436,8 @@ static int stk3310_set_state(struct stk3310_data *data, u8 state)
                dev_err(&client->dev, "failed to change sensor state\n");
        } else if (state != STK3310_STATE_STANDBY) {
                /* Don't reset the 'enabled' flags if we're going in standby */
-               data->ps_enabled  = !!(state & 0x01);
-               data->als_enabled = !!(state & 0x02);
+               data->ps_enabled  = !!(state & STK3310_STATE_EN_PS);
+               data->als_enabled = !!(state & STK3310_STATE_EN_ALS);
        }
        mutex_unlock(&data->lock);
 
@@ -435,7 +452,10 @@ static int stk3310_init(struct iio_dev *indio_dev)
        struct stk3310_data *data = iio_priv(indio_dev);
        struct i2c_client *client = data->client;
 
-       regmap_read(data->regmap, STK3310_REG_ID, &chipid);
+       ret = regmap_read(data->regmap, STK3310_REG_ID, &chipid);
+       if (ret < 0)
+               return ret;
+
        if (chipid != STK3310_CHIP_ID_VAL &&
            chipid != STK3311_CHIP_ID_VAL) {
                dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
@@ -604,8 +624,13 @@ static int stk3310_probe(struct i2c_client *client,
        if (ret < 0)
                return ret;
 
-       if (client->irq < 0)
+       if (client->irq < 0) {
                client->irq = stk3310_gpio_probe(client);
+               if (client->irq < 0) {
+                       ret = client->irq;
+                       goto err_standby;
+               }
+       }
 
        if (client->irq >= 0) {
                ret = devm_request_threaded_irq(&client->dev, client->irq,
@@ -614,17 +639,23 @@ static int stk3310_probe(struct i2c_client *client,
                                                IRQF_TRIGGER_FALLING |
                                                IRQF_ONESHOT,
                                                STK3310_EVENT, indio_dev);
-               if (ret < 0)
+               if (ret < 0) {
                        dev_err(&client->dev, "request irq %d failed\n",
-                                       client->irq);
+                               client->irq);
+                       goto err_standby;
+               }
        }
 
        ret = iio_device_register(indio_dev);
        if (ret < 0) {
                dev_err(&client->dev, "device_register failed\n");
-               stk3310_set_state(data, STK3310_STATE_STANDBY);
+               goto err_standby;
        }
 
+       return 0;
+
+err_standby:
+       stk3310_set_state(data, STK3310_STATE_STANDBY);
        return ret;
 }
 
@@ -648,7 +679,7 @@ static int stk3310_suspend(struct device *dev)
 
 static int stk3310_resume(struct device *dev)
 {
-       int state = 0;
+       u8 state = 0;
        struct stk3310_data *data;
 
        data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
index 2697918..cf94ec7 100644 (file)
@@ -158,9 +158,9 @@ static int tsl4531_check_id(struct i2c_client *client)
        case TSL45313_ID:
        case TSL45315_ID:
        case TSL45317_ID:
-               return 1;
-       default:
                return 0;
+       default:
+               return -ENODEV;
        }
 }
 
@@ -180,9 +180,10 @@ static int tsl4531_probe(struct i2c_client *client,
        data->client = client;
        mutex_init(&data->lock);
 
-       if (!tsl4531_check_id(client)) {
+       ret = tsl4531_check_id(client);
+       if (ret) {
                dev_err(&client->dev, "no TSL4531 sensor\n");
-               return -ENODEV;
+               return ret;
        }
 
        ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
new file mode 100644 (file)
index 0000000..49dab3c
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Driver for UPISEMI us5182d Proximity and Ambient Light Sensor.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * To do: Interrupt support.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/mutex.h>
+
+#define US5182D_REG_CFG0                               0x00
+#define US5182D_CFG0_ONESHOT_EN                                BIT(6)
+#define US5182D_CFG0_SHUTDOWN_EN                       BIT(7)
+#define US5182D_CFG0_WORD_ENABLE                       BIT(0)
+
+#define US5182D_REG_CFG1                               0x01
+#define US5182D_CFG1_ALS_RES16                         BIT(4)
+#define US5182D_CFG1_AGAIN_DEFAULT                     0x00
+
+#define US5182D_REG_CFG2                               0x02
+#define US5182D_CFG2_PX_RES16                          BIT(4)
+#define US5182D_CFG2_PXGAIN_DEFAULT                    BIT(2)
+
+#define US5182D_REG_CFG3                               0x03
+#define US5182D_CFG3_LED_CURRENT100                    (BIT(4) | BIT(5))
+
+#define US5182D_REG_CFG4                               0x10
+
+/*
+ * Registers for tuning the auto dark current cancelling feature.
+ * DARK_TH(reg 0x27,0x28) - threshold (counts) for auto dark cancelling.
+ * when ALS  > DARK_TH --> ALS_Code = ALS - Upper(0x2A) * Dark
+ * when ALS < DARK_TH --> ALS_Code = ALS - Lower(0x29) * Dark
+ */
+#define US5182D_REG_UDARK_TH                   0x27
+#define US5182D_REG_DARK_AUTO_EN               0x2b
+#define US5182D_REG_AUTO_LDARK_GAIN            0x29
+#define US5182D_REG_AUTO_HDARK_GAIN            0x2a
+
+#define US5182D_OPMODE_ALS                     0x01
+#define US5182D_OPMODE_PX                      0x02
+#define US5182D_OPMODE_SHIFT                   4
+
+#define US5182D_REG_DARK_AUTO_EN_DEFAULT       0x80
+#define US5182D_REG_AUTO_LDARK_GAIN_DEFAULT    0x16
+#define US5182D_REG_AUTO_HDARK_GAIN_DEFAULT    0x00
+
+#define US5182D_REG_ADL                                0x0c
+#define US5182D_REG_PDL                                0x0e
+
+#define US5182D_REG_MODE_STORE                 0x21
+#define US5182D_STORE_MODE                     0x01
+
+#define US5182D_REG_CHIPID                     0xb2
+
+#define US5182D_OPMODE_MASK                    GENMASK(5, 4)
+#define US5182D_AGAIN_MASK                     0x07
+#define US5182D_RESET_CHIP                     0x01
+
+#define US5182D_CHIPID                         0x26
+#define US5182D_DRV_NAME                       "us5182d"
+
+#define US5182D_GA_RESOLUTION                  1000
+
+#define US5182D_READ_BYTE                      1
+#define US5182D_READ_WORD                      2
+#define US5182D_OPSTORE_SLEEP_TIME             20 /* ms */
+
+/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
+static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
+                                    3900, 2100};
+
+/*
+ * Experimental thresholds that work with US5182D sensor on evaluation board
+ * roughly between 12-32 lux
+ */
+static u16 us5182d_dark_ths_vals[] = {170, 200, 512, 512, 800, 2000, 4000,
+                                     8000};
+
+enum mode {
+       US5182D_ALS_PX,
+       US5182D_ALS_ONLY,
+       US5182D_PX_ONLY
+};
+
+struct us5182d_data {
+       struct i2c_client *client;
+       struct mutex lock;
+
+       /* Glass attenuation factor */
+       u32 ga;
+
+       /* Dark gain tuning */
+       u8 lower_dark_gain;
+       u8 upper_dark_gain;
+       u16 *us5182d_dark_ths;
+
+       u8 opmode;
+};
+
+static IIO_CONST_ATTR(in_illuminance_scale_available,
+                     "0.0021 0.0039 0.0076 0.0196 0.0336 0.061 0.1078 0.1885");
+
+static struct attribute *us5182d_attrs[] = {
+       &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group us5182d_attr_group = {
+       .attrs = us5182d_attrs,
+};
+
+static const struct {
+       u8 reg;
+       u8 val;
+} us5182d_regvals[] = {
+       {US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
+                           US5182D_CFG0_WORD_ENABLE)},
+       {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
+       {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
+                           US5182D_CFG2_PXGAIN_DEFAULT)},
+       {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
+       {US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
+       {US5182D_REG_CFG4, 0x00},
+};
+
+static const struct iio_chan_spec us5182d_channels[] = {
+       {
+               .type = IIO_LIGHT,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                                     BIT(IIO_CHAN_INFO_SCALE),
+       },
+       {
+               .type = IIO_PROXIMITY,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+       }
+};
+
+static int us5182d_get_als(struct us5182d_data *data)
+{
+       int ret;
+       unsigned long result;
+
+       ret = i2c_smbus_read_word_data(data->client,
+                                      US5182D_REG_ADL);
+       if (ret < 0)
+               return ret;
+
+       result = ret * data->ga / US5182D_GA_RESOLUTION;
+       if (result > 0xffff)
+               result = 0xffff;
+
+       return result;
+}
+
+static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * In oneshot mode the chip will power itself down after taking the
+        * required measurement.
+        */
+       ret = ret | US5182D_CFG0_ONESHOT_EN;
+
+       /* update mode */
+       ret = ret & ~US5182D_OPMODE_MASK;
+       ret = ret | (mode << US5182D_OPMODE_SHIFT);
+
+       /*
+        * After updating the operating mode, the chip requires that
+        * the operation is stored, by writing 1 in the STORE_MODE
+        * register (auto-clearing).
+        */
+       ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
+       if (ret < 0)
+               return ret;
+
+       if (mode == data->opmode)
+               return 0;
+
+       ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
+                                       US5182D_STORE_MODE);
+       if (ret < 0)
+               return ret;
+
+       data->opmode = mode;
+       msleep(US5182D_OPSTORE_SLEEP_TIME);
+
+       return 0;
+}
+
+static int us5182d_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan, int *val,
+                           int *val2, long mask)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               switch (chan->type) {
+               case IIO_LIGHT:
+                       mutex_lock(&data->lock);
+                       ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
+                       if (ret < 0)
+                               goto out_err;
+
+                       ret = us5182d_get_als(data);
+                       if (ret < 0)
+                               goto out_err;
+                       mutex_unlock(&data->lock);
+                       *val = ret;
+                       return IIO_VAL_INT;
+               case IIO_PROXIMITY:
+                       mutex_lock(&data->lock);
+                       ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
+                       if (ret < 0)
+                               goto out_err;
+
+                       ret = i2c_smbus_read_word_data(data->client,
+                                                      US5182D_REG_PDL);
+                       if (ret < 0)
+                               goto out_err;
+                       mutex_unlock(&data->lock);
+                       *val = ret;
+                       return  IIO_VAL_INT;
+               default:
+                       return -EINVAL;
+               }
+
+       case IIO_CHAN_INFO_SCALE:
+               ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
+               if (ret < 0)
+                       return ret;
+
+               *val = 0;
+               *val2 = us5182d_scales[ret & US5182D_AGAIN_MASK];
+
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+out_err:
+       mutex_unlock(&data->lock);
+       return ret;
+}
+
+/**
+ * us5182d_update_dark_th - update Darh_Th registers
+ * @data       us5182d_data structure
+ * @index      index in us5182d_dark_ths array to use for the updated value
+ *
+ * Function needs to be called with a lock held because it needs two i2c write
+ * byte operations as these registers (0x27 0x28) don't work in word mode
+ * accessing.
+ */
+static int us5182d_update_dark_th(struct us5182d_data *data, int index)
+{
+       __be16 dark_th = cpu_to_be16(data->us5182d_dark_ths[index]);
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH,
+                                       ((u8 *)&dark_th)[0]);
+       if (ret < 0)
+               return ret;
+
+       return i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH + 1,
+                                       ((u8 *)&dark_th)[1]);
+}
+
+/**
+ * us5182d_apply_scale - update the ALS scale
+ * @data       us5182d_data structure
+ * @index      index in us5182d_scales array to use for the updated value
+ *
+ * Function needs to be called with a lock held as we're having more than one
+ * i2c operation.
+ */
+static int us5182d_apply_scale(struct us5182d_data *data, int index)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
+       if (ret < 0)
+               return ret;
+
+       ret = ret & (~US5182D_AGAIN_MASK);
+       ret |= index;
+
+       ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG1, ret);
+       if (ret < 0)
+               return ret;
+
+       return us5182d_update_dark_th(data, index);
+}
+
+static int us5182d_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan, int val,
+                            int val2, long mask)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+       int ret, i;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               if (val != 0)
+                       return -EINVAL;
+               for (i = 0; i < ARRAY_SIZE(us5182d_scales); i++)
+                       if (val2 == us5182d_scales[i]) {
+                               mutex_lock(&data->lock);
+                               ret = us5182d_apply_scale(data, i);
+                               mutex_unlock(&data->lock);
+                               return ret;
+                       }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static const struct iio_info us5182d_info = {
+       .driver_module  = THIS_MODULE,
+       .read_raw = us5182d_read_raw,
+       .write_raw = us5182d_write_raw,
+       .attrs = &us5182d_attr_group,
+};
+
+static int us5182d_reset(struct iio_dev *indio_dev)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+
+       return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG3,
+                                        US5182D_RESET_CHIP);
+}
+
+static int us5182d_init(struct iio_dev *indio_dev)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+       int i, ret;
+
+       ret = us5182d_reset(indio_dev);
+       if (ret < 0)
+               return ret;
+
+       data->opmode = 0;
+       for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
+               ret = i2c_smbus_write_byte_data(data->client,
+                                               us5182d_regvals[i].reg,
+                                               us5182d_regvals[i].val);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void us5182d_get_platform_data(struct iio_dev *indio_dev)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+
+       if (device_property_read_u32(&data->client->dev, "upisemi,glass-coef",
+                                    &data->ga))
+               data->ga = US5182D_GA_RESOLUTION;
+       if (device_property_read_u16_array(&data->client->dev,
+                                          "upisemi,dark-ths",
+                                          data->us5182d_dark_ths,
+                                          ARRAY_SIZE(us5182d_dark_ths_vals)))
+               data->us5182d_dark_ths = us5182d_dark_ths_vals;
+       if (device_property_read_u8(&data->client->dev,
+                                   "upisemi,upper-dark-gain",
+                                   &data->upper_dark_gain))
+               data->upper_dark_gain = US5182D_REG_AUTO_HDARK_GAIN_DEFAULT;
+       if (device_property_read_u8(&data->client->dev,
+                                   "upisemi,lower-dark-gain",
+                                   &data->lower_dark_gain))
+               data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
+}
+
+static int  us5182d_dark_gain_config(struct iio_dev *indio_dev)
+{
+       struct us5182d_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = us5182d_update_dark_th(data, US5182D_CFG1_AGAIN_DEFAULT);
+       if (ret < 0)
+               return ret;
+
+       ret = i2c_smbus_write_byte_data(data->client,
+                                       US5182D_REG_AUTO_LDARK_GAIN,
+                                       data->lower_dark_gain);
+       if (ret < 0)
+               return ret;
+
+       ret = i2c_smbus_write_byte_data(data->client,
+                                       US5182D_REG_AUTO_HDARK_GAIN,
+                                       data->upper_dark_gain);
+       if (ret < 0)
+               return ret;
+
+       return i2c_smbus_write_byte_data(data->client, US5182D_REG_DARK_AUTO_EN,
+                                        US5182D_REG_DARK_AUTO_EN_DEFAULT);
+}
+
+static int us5182d_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct us5182d_data *data;
+       struct iio_dev *indio_dev;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+
+       mutex_init(&data->lock);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->info = &us5182d_info;
+       indio_dev->name = US5182D_DRV_NAME;
+       indio_dev->channels = us5182d_channels;
+       indio_dev->num_channels = ARRAY_SIZE(us5182d_channels);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+
+       ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CHIPID);
+       if (ret != US5182D_CHIPID) {
+               dev_err(&data->client->dev,
+                       "Failed to detect US5182 light chip\n");
+               return (ret < 0) ? ret : -ENODEV;
+       }
+
+       us5182d_get_platform_data(indio_dev);
+       ret = us5182d_init(indio_dev);
+       if (ret < 0)
+               return ret;
+
+       ret = us5182d_dark_gain_config(indio_dev);
+       if (ret < 0)
+               return ret;
+
+       return iio_device_register(indio_dev);
+}
+
+static int us5182d_remove(struct i2c_client *client)
+{
+       iio_device_unregister(i2c_get_clientdata(client));
+       return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
+                                        US5182D_CFG0_SHUTDOWN_EN);
+}
+
+static const struct acpi_device_id us5182d_acpi_match[] = {
+       { "USD5182", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match);
+
+static const struct i2c_device_id us5182d_id[] = {
+               {"usd5182", 0},
+               {}
+};
+
+MODULE_DEVICE_TABLE(i2c, us5182d_id);
+
+static struct i2c_driver us5182d_driver = {
+       .driver = {
+               .name = US5182D_DRV_NAME,
+               .acpi_match_table = ACPI_PTR(us5182d_acpi_match),
+       },
+       .probe = us5182d_probe,
+       .remove = us5182d_remove,
+       .id_table = us5182d_id,
+
+};
+module_i2c_driver(us5182d_driver);
+
+MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
+MODULE_DESCRIPTION("Driver for us5182d Proximity and Light Sensor");
+MODULE_LICENSE("GPL v2");
index efb9350..868abad 100644 (file)
@@ -24,6 +24,24 @@ config AK09911
        help
          Deprecated: AK09911 is now supported by AK8975 driver.
 
+config BMC150_MAGN
+       tristate "Bosch BMC150 Magnetometer Driver"
+       depends on I2C
+       select REGMAP_I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+         Say yes here to build support for the BMC150 magnetometer.
+
+         Currently this only supports the device via an i2c interface.
+
+         This is a combo module with both accelerometer and magnetometer.
+         This driver is only implementing magnetometer part, which has
+         its own address and register map.
+
+         To compile this driver as a module, choose M here: the module will be
+         called bmc150_magn.
+
 config MAG3110
        tristate "Freescale MAG3110 3-Axis Magnetometer"
        depends on I2C
@@ -87,19 +105,4 @@ config IIO_ST_MAGN_SPI_3AXIS
        depends on IIO_ST_MAGN_3AXIS
        depends on IIO_ST_SENSORS_SPI
 
-config BMC150_MAGN
-       tristate "Bosch BMC150 Magnetometer Driver"
-       depends on I2C
-       select REGMAP_I2C
-       select IIO_BUFFER
-       select IIO_TRIGGERED_BUFFER
-       help
-         Say yes here to build support for the BMC150 magnetometer.
-
-         Currently this only supports the device via an i2c interface.
-
-         This is a combo module with both accelerometer and magnetometer.
-         This driver is only implementing magnetometer part, which has
-         its own address and register map.
-
 endmenu
index 33b1d4d..2c72df4 100644 (file)
@@ -4,6 +4,7 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_AK8975)   += ak8975.o
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
 obj-$(CONFIG_MAG3110)  += mag3110.o
 obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
 obj-$(CONFIG_MMC35240) += mmc35240.o
@@ -14,5 +15,3 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
 
 obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
 obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
-
-obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
index f8dc4b8..b27f014 100644 (file)
@@ -560,6 +560,7 @@ static const struct iio_info magn_info = {
        .attrs = &st_magn_attribute_group,
        .read_raw = &st_magn_read_raw,
        .write_raw = &st_magn_write_raw,
+       .debugfs_reg_access = &st_sensors_debugfs_reg_access,
 };
 
 #ifdef CONFIG_IIO_TRIGGER
index eb41d2b..b39a2fb 100644 (file)
@@ -400,6 +400,7 @@ static const struct iio_info press_info = {
        .attrs = &st_press_attribute_group,
        .read_raw = &st_press_read_raw,
        .write_raw = &st_press_write_raw,
+       .debugfs_reg_access = &st_sensors_debugfs_reg_access,
 };
 
 #ifdef CONFIG_IIO_TRIGGER
index 41a8d8f..ef4c73d 100644 (file)
@@ -20,6 +20,18 @@ endmenu
 
 menu "Proximity sensors"
 
+config LIDAR_LITE_V2
+       tristate "PulsedLight LIDAR sensor"
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       depends on I2C
+       help
+         Say Y to build a driver for PulsedLight LIDAR range finding
+         sensor.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pulsedlight-lite-v2
+
 config SX9500
        tristate "SX9500 Semtech proximity sensor"
        select IIO_BUFFER
index 9818dc5..9aadd9a 100644 (file)
@@ -4,4 +4,5 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_AS3935)           += as3935.o
+obj-$(CONFIG_LIDAR_LITE_V2)    += pulsedlight-lidar-lite-v2.o
 obj-$(CONFIG_SX9500)           += sx9500.o
index bc0d68e..e950351 100644 (file)
@@ -434,6 +434,12 @@ static int as3935_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id as3935_of_match[] = {
+       { .compatible = "ams,as3935", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, as3935_of_match);
+
 static const struct spi_device_id as3935_id[] = {
        {"as3935", 0},
        {},
@@ -443,6 +449,7 @@ MODULE_DEVICE_TABLE(spi, as3935_id);
 static struct spi_driver as3935_driver = {
        .driver = {
                .name   = "as3935",
+               .of_match_table = of_match_ptr(as3935_of_match),
                .owner  = THIS_MODULE,
                .pm     = AS3935_PM_OPS,
        },
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
new file mode 100644 (file)
index 0000000..185a7ab
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
+ *
+ * Copyright (C) 2015 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.
+ *
+ * TODO: runtime pm, interrupt mode, and signal strength reporting
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define LIDAR_REG_CONTROL              0x00
+#define LIDAR_REG_CONTROL_ACQUIRE      BIT(2)
+
+#define LIDAR_REG_STATUS               0x01
+#define LIDAR_REG_STATUS_INVALID       BIT(3)
+#define LIDAR_REG_STATUS_READY         BIT(0)
+
+#define LIDAR_REG_DATA_HBYTE   0x0f
+#define LIDAR_REG_DATA_LBYTE   0x10
+
+#define LIDAR_DRV_NAME "lidar"
+
+struct lidar_data {
+       struct iio_dev *indio_dev;
+       struct i2c_client *client;
+
+       u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
+};
+
+static const struct iio_chan_spec lidar_channels[] = {
+       {
+               .type = IIO_DISTANCE,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 'u',
+                       .realbits = 16,
+                       .storagebits = 16,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int lidar_read_byte(struct lidar_data *data, int reg)
+{
+       struct i2c_client *client = data->client;
+       int ret;
+
+       /*
+        * Device needs a STOP condition between address write, and data read
+        * so in turn i2c_smbus_read_byte_data cannot be used
+        */
+
+       ret = i2c_smbus_write_byte(client, reg);
+       if (ret < 0) {
+               dev_err(&client->dev, "cannot write addr value");
+               return ret;
+       }
+
+       ret = i2c_smbus_read_byte(client);
+       if (ret < 0)
+               dev_err(&client->dev, "cannot read data value");
+
+       return ret;
+}
+
+static inline int lidar_write_control(struct lidar_data *data, int val)
+{
+       return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
+}
+
+static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+{
+       int ret;
+       int val;
+
+       ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
+       if (ret < 0)
+               return ret;
+       val = ret << 8;
+
+       ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
+       if (ret < 0)
+               return ret;
+
+       val |= ret;
+       *reg = val;
+
+       return 0;
+}
+
+static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
+{
+       struct i2c_client *client = data->client;
+       int tries = 10;
+       int ret;
+
+       /* start sample */
+       ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
+       if (ret < 0) {
+               dev_err(&client->dev, "cannot send start measurement command");
+               return ret;
+       }
+
+       while (tries--) {
+               usleep_range(1000, 2000);
+
+               ret = lidar_read_byte(data, LIDAR_REG_STATUS);
+               if (ret < 0)
+                       break;
+
+               /* return 0 since laser is likely pointed out of range */
+               if (ret & LIDAR_REG_STATUS_INVALID) {
+                       *reg = 0;
+                       ret = 0;
+                       break;
+               }
+
+               /* sample ready to read */
+               if (!(ret & LIDAR_REG_STATUS_READY)) {
+                       ret = lidar_read_measurement(data, reg);
+                       break;
+               }
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int lidar_read_raw(struct iio_dev *indio_dev,
+                         struct iio_chan_spec const *chan,
+                         int *val, int *val2, long mask)
+{
+       struct lidar_data *data = iio_priv(indio_dev);
+       int ret = -EINVAL;
+
+       mutex_lock(&indio_dev->mlock);
+
+       if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) {
+               ret = -EBUSY;
+               goto error_busy;
+       }
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW: {
+               u16 reg;
+
+               ret = lidar_get_measurement(data, &reg);
+               if (!ret) {
+                       *val = reg;
+                       ret = IIO_VAL_INT;
+               }
+               break;
+       }
+       case IIO_CHAN_INFO_SCALE:
+               *val = 0;
+               *val2 = 10000;
+               ret = IIO_VAL_INT_PLUS_MICRO;
+               break;
+       }
+
+error_busy:
+       mutex_unlock(&indio_dev->mlock);
+
+       return ret;
+}
+
+static irqreturn_t lidar_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct lidar_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = lidar_get_measurement(data, data->buffer);
+       if (!ret) {
+               iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                                                  iio_get_time_ns());
+       } else {
+               dev_err(&data->client->dev, "cannot read LIDAR measurement");
+       }
+
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static const struct iio_info lidar_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = lidar_read_raw,
+};
+
+static int lidar_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       struct lidar_data *data;
+       struct iio_dev *indio_dev;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       indio_dev->info = &lidar_info;
+       indio_dev->name = LIDAR_DRV_NAME;
+       indio_dev->channels = lidar_channels;
+       indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+
+       data->client = client;
+       data->indio_dev = indio_dev;
+
+       ret = iio_triggered_buffer_setup(indio_dev, NULL,
+                                        lidar_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 lidar_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id lidar_id[] = {
+       {"lidar-lite-v2", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, lidar_id);
+
+static const struct of_device_id lidar_dt_ids[] = {
+       { .compatible = "pulsedlight,lidar-lite-v2" },
+       { }
+};
+
+static struct i2c_driver lidar_driver = {
+       .driver = {
+               .name   = LIDAR_DRV_NAME,
+               .of_match_table = of_match_ptr(lidar_dt_ids),
+       },
+       .probe          = lidar_probe,
+       .remove         = lidar_remove,
+       .id_table       = lidar_id,
+};
+module_i2c_driver(lidar_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
+MODULE_LICENSE("GPL");
index 5d033a5..3fd3ba4 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
  * Copyright (c) 2015 Essensium NV
+ * Copyright (c) 2015 Melexis
  *
  * This file is subject to the terms and conditions of version 2 of
  * the GNU General Public License.  See the file COPYING in the main
@@ -20,7 +21,6 @@
  * always has a pull-up so we do not need an extra GPIO to drive it high.  If
  * the "wakeup" GPIO is not given, power management will be disabled.
  *
- * TODO: filter configuration
  */
 
 #include <linux/err.h>
@@ -32,6 +32,7 @@
 #include <linux/pm_runtime.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
 
 #define MLX90614_OP_RAM                0x00
 #define MLX90614_OP_EEPROM     0x20
@@ -79,6 +80,20 @@ struct mlx90614_data {
        unsigned long ready_timestamp; /* in jiffies */
 };
 
+/* Bandwidth values for IIR filtering */
+static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86};
+static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available,
+                     "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23");
+
+static struct attribute *mlx90614_attributes[] = {
+       &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group mlx90614_attr_group = {
+       .attrs = mlx90614_attributes,
+};
+
 /*
  * Erase an address and write word.
  * The mutex must be locked before calling.
@@ -117,6 +132,42 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
        return ret;
 }
 
+/*
+ * Find the IIR value inside mlx90614_iir_values array and return its position
+ * which is equivalent to the bit value in sensor register
+ */
+static inline s32 mlx90614_iir_search(const struct i2c_client *client,
+                                     int value)
+{
+       int i;
+       s32 ret;
+
+       for (i = 0; i < ARRAY_SIZE(mlx90614_iir_values); ++i) {
+               if (value == mlx90614_iir_values[i])
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(mlx90614_iir_values))
+               return -EINVAL;
+
+       /*
+        * CONFIG register values must not be changed so
+        * we must read them before we actually write
+        * changes
+        */
+       ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
+       if (ret > 0)
+               return ret;
+
+       /* Write changed values */
+       ret = mlx90614_write_word(client, MLX90614_CONFIG,
+                       (i << MLX90614_CONFIG_IIR_SHIFT) |
+                       (((u16) ((0x7 << MLX90614_CONFIG_FIR_SHIFT) |
+                       ((u16) ret & (~((u16) MLX90614_CONFIG_FIR_MASK))))) &
+                       (~(u16) MLX90614_CONFIG_IIR_MASK)));
+       return ret;
+}
+
 #ifdef CONFIG_PM
 /*
  * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
@@ -236,6 +287,21 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
                        *val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION;
                }
                return IIO_VAL_INT_PLUS_NANO;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with
+                                                            FIR = 1024 */
+               mlx90614_power_get(data, false);
+               mutex_lock(&data->lock);
+               ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+               mutex_unlock(&data->lock);
+               mlx90614_power_put(data);
+
+               if (ret < 0)
+                       return ret;
+
+               *val = mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] / 100;
+               *val2 = (mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] % 100) *
+                       10000;
+               return IIO_VAL_INT_PLUS_MICRO;
        default:
                return -EINVAL;
        }
@@ -262,6 +328,18 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
                mutex_unlock(&data->lock);
                mlx90614_power_put(data);
 
+               return ret;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR Filter setting */
+               if (val < 0 || val2 < 0)
+                       return -EINVAL;
+
+               mlx90614_power_get(data, false);
+               mutex_lock(&data->lock);
+               ret = mlx90614_iir_search(data->client,
+                                         val * 100 + val2 / 10000);
+               mutex_unlock(&data->lock);
+               mlx90614_power_put(data);
+
                return ret;
        default:
                return -EINVAL;
@@ -275,6 +353,8 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
        switch (mask) {
        case IIO_CHAN_INFO_CALIBEMISSIVITY:
                return IIO_VAL_INT_PLUS_NANO;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               return IIO_VAL_INT_PLUS_MICRO;
        default:
                return -EINVAL;
        }
@@ -294,7 +374,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
                .modified = 1,
                .channel2 = IIO_MOD_TEMP_OBJECT,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
+                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
+                       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
                    BIT(IIO_CHAN_INFO_SCALE),
        },
@@ -305,7 +386,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
                .channel = 1,
                .channel2 = IIO_MOD_TEMP_OBJECT,
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
+                   BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
+                       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
                    BIT(IIO_CHAN_INFO_SCALE),
        },
@@ -315,6 +397,7 @@ static const struct iio_info mlx90614_info = {
        .read_raw = mlx90614_read_raw,
        .write_raw = mlx90614_write_raw,
        .write_raw_get_fmt = mlx90614_write_raw_get_fmt,
+       .attrs = &mlx90614_attr_group,
        .driver_module = THIS_MODULE,
 };
 
@@ -569,5 +652,6 @@ module_i2c_driver(mlx90614_driver);
 
 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
+MODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
 MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
 MODULE_LICENSE("GPL");
index 5331c44..bcf4ebb 100644 (file)
@@ -137,7 +137,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "failed to get platform I/O memory\n");
-               return -EBUSY;
+               return -ENXIO;
        }
 
        iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
@@ -162,7 +162,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
        irq = platform_get_irq(pdev, 0);
        if (irq <= 0) {
                dev_err(&pdev->dev, "failed getting interrupt resource\n");
-               return -EINVAL;
+               return -ENXIO;
        }
 
        retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
index 6d38854..86d8447 100644 (file)
 #include "iio_dummy_evgen.h"
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/irq_work.h>
 
 /* Fiddly bit of faking and irq without hardware */
 #define IIO_EVENTGEN_NO 10
+
+/**
+ * struct iio_dummy_handle_irq - helper struct to simulate interrupt generation
+ * @work: irq_work used to run handlers from hardirq context
+ * @irq: fake irq line number to trigger an interrupt
+ */
+struct iio_dummy_handle_irq {
+       struct irq_work work;
+       int irq;
+};
+
 /**
  * struct iio_dummy_evgen - evgen state
  * @chip: irq chip we are faking
@@ -35,6 +47,7 @@
  * @inuse: mask of which irqs are connected
  * @regs: irq regs we are faking
  * @lock: protect the evgen state
+ * @handler: helper for a 'hardware-like' interrupt simulation
  */
 struct iio_dummy_eventgen {
        struct irq_chip chip;
@@ -43,6 +56,7 @@ struct iio_dummy_eventgen {
        bool inuse[IIO_EVENTGEN_NO];
        struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
        struct mutex lock;
+       struct iio_dummy_handle_irq handler;
 };
 
 /* We can only ever have one instance of this 'device' */
@@ -67,6 +81,14 @@ static void iio_dummy_event_irqunmask(struct irq_data *d)
        evgen->enabled[d->irq - evgen->base] = true;
 }
 
+static void iio_dummy_work_handler(struct irq_work *work)
+{
+       struct iio_dummy_handle_irq *irq_handler;
+
+       irq_handler = container_of(work, struct iio_dummy_handle_irq, work);
+       handle_simple_irq(irq_handler->irq, irq_to_desc(irq_handler->irq));
+}
+
 static int iio_dummy_evgen_create(void)
 {
        int ret, i;
@@ -91,6 +113,7 @@ static int iio_dummy_evgen_create(void)
                                  IRQ_NOREQUEST | IRQ_NOAUTOEN,
                                  IRQ_NOPROBE);
        }
+       init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler);
        mutex_init(&iio_evgen->lock);
        return 0;
 }
@@ -169,8 +192,9 @@ static ssize_t iio_evgen_poke(struct device *dev,
        iio_evgen->regs[this_attr->address].reg_id   = this_attr->address;
        iio_evgen->regs[this_attr->address].reg_data = event;
 
+       iio_evgen->handler.irq = iio_evgen->base + this_attr->address;
        if (iio_evgen->enabled[this_attr->address])
-               handle_nested_irq(iio_evgen->base + this_attr->address);
+               irq_work_queue(&iio_evgen->handler.work);
 
        return len;
 }
index 8d00224..5c2f4d0 100644 (file)
@@ -46,6 +46,7 @@ struct iio_dummy_state {
        int event_irq;
        int event_val;
        bool event_en;
+       s64 event_timestamp;
 #endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
 };
 
index 73108ba..bfbf1c5 100644 (file)
@@ -153,6 +153,15 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
        return 0;
 }
 
+static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct iio_dummy_state *st = iio_priv(indio_dev);
+
+       st->event_timestamp = iio_get_time_ns();
+       return IRQ_HANDLED;
+}
+
 /**
  * iio_simple_dummy_event_handler() - identify and pass on event
  * @irq: irq of event line
@@ -177,7 +186,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
                               IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
                                              IIO_EV_DIR_RISING,
                                              IIO_EV_TYPE_THRESH, 0, 0, 0),
-                              iio_get_time_ns());
+                              st->event_timestamp);
                break;
        case 1:
                if (st->activity_running > st->event_val)
@@ -187,7 +196,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
                                                      IIO_EV_DIR_RISING,
                                                      IIO_EV_TYPE_THRESH,
                                                      0, 0, 0),
-                                      iio_get_time_ns());
+                                      st->event_timestamp);
                break;
        case 2:
                if (st->activity_walking < st->event_val)
@@ -197,14 +206,14 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
                                                      IIO_EV_DIR_FALLING,
                                                      IIO_EV_TYPE_THRESH,
                                                      0, 0, 0),
-                                      iio_get_time_ns());
+                                      st->event_timestamp);
                break;
        case 3:
                iio_push_event(indio_dev,
                               IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
                                              IIO_EV_DIR_NONE,
                                              IIO_EV_TYPE_CHANGE, 0, 0, 0),
-                              iio_get_time_ns());
+                              st->event_timestamp);
                break;
        default:
                break;
@@ -238,7 +247,7 @@ int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
        st->regs = iio_dummy_evgen_get_regs(st->event_irq);
 
        ret = request_threaded_irq(st->event_irq,
-                                  NULL,
+                                  &iio_simple_dummy_get_timestamp,
                                   &iio_simple_dummy_event_handler,
                                   IRQF_ONESHOT,
                                   "iio_simple_event",
index f3d0da2..06f35d3 100644 (file)
@@ -48,7 +48,7 @@ struct hmc5843_data {
 };
 
 int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
-                        enum hmc5843_ids id);
+                        enum hmc5843_ids id, const char *name);
 int hmc5843_common_remove(struct device *dev);
 
 int hmc5843_common_suspend(struct device *dev);
index fffca3a..4aab022 100644 (file)
@@ -577,7 +577,7 @@ int hmc5843_common_resume(struct device *dev)
 EXPORT_SYMBOL(hmc5843_common_resume);
 
 int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
-                        enum hmc5843_ids id)
+                        enum hmc5843_ids id, const char *name)
 {
        struct hmc5843_data *data;
        struct iio_dev *indio_dev;
@@ -597,7 +597,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
        mutex_init(&data->lock);
 
        indio_dev->dev.parent = dev;
-       indio_dev->name = dev->driver->name;
+       indio_dev->name = name;
        indio_dev->info = &hmc5843_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->channels = data->variant->channels;
index ff08667..3e06ceb 100644 (file)
@@ -61,7 +61,7 @@ static int hmc5843_i2c_probe(struct i2c_client *cli,
 {
        return hmc5843_common_probe(&cli->dev,
                        devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config),
-                       id->driver_data);
+                       id->driver_data, id->name);
 }
 
 static int hmc5843_i2c_remove(struct i2c_client *client)
index 8e658f7..1549192 100644 (file)
@@ -59,6 +59,7 @@ static const struct regmap_config hmc5843_spi_regmap_config = {
 static int hmc5843_spi_probe(struct spi_device *spi)
 {
        int ret;
+       const struct spi_device_id *id = spi_get_device_id(spi);
 
        spi->mode = SPI_MODE_3;
        spi->max_speed_hz = 8000000;
@@ -69,7 +70,7 @@ static int hmc5843_spi_probe(struct spi_device *spi)
 
        return hmc5843_common_probe(&spi->dev,
                        devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config),
-                       HMC5983_ID);
+                       id->driver_data, id->name);
 }
 
 static int hmc5843_spi_remove(struct spi_device *spi)
@@ -81,6 +82,7 @@ static const struct spi_device_id hmc5843_id[] = {
        { "hmc5983", HMC5983_ID },
        { }
 };
+MODULE_DEVICE_TABLE(spi, hmc5843_id);
 
 static struct spi_driver hmc5843_driver = {
                .driver = {
index 3c17cd7..2fe939c 100644 (file)
@@ -271,6 +271,10 @@ void st_sensors_power_enable(struct iio_dev *indio_dev);
 
 void st_sensors_power_disable(struct iio_dev *indio_dev);
 
+int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
+                                 unsigned reg, unsigned writeval,
+                                 unsigned *readval);
+
 int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr);
 
 int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
index 7bb7f67..19c94c9 100644 (file)
@@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
 #define INDIO_BUFFER_TRIGGERED         0x02
 #define INDIO_BUFFER_SOFTWARE          0x04
 #define INDIO_BUFFER_HARDWARE          0x08
+#define INDIO_EVENT_TRIGGERED          0x10
 
 #define INDIO_ALL_BUFFER_MODES                                 \
        (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
@@ -457,6 +458,7 @@ struct iio_buffer_setup_ops {
  * @scan_index_timestamp:[INTERN] cache of the index to the timestamp
  * @trig:              [INTERN] current device trigger (buffer modes)
  * @pollfunc:          [DRIVER] function run on trigger being received
+ * @pollfunc_event:    [DRIVER] function run on events trigger being received
  * @channels:          [DRIVER] channel specification structure table
  * @num_channels:      [DRIVER] number of channels specified in @channels.
  * @channel_attr_list: [INTERN] keep track of automatically created channel
@@ -495,6 +497,7 @@ struct iio_dev {
        unsigned                        scan_index_timestamp;
        struct iio_trigger              *trig;
        struct iio_poll_func            *pollfunc;
+       struct iio_poll_func            *pollfunc_event;
 
        struct iio_chan_spec const      *channels;
        int                             num_channels;
diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
new file mode 100644 (file)
index 0000000..8fe8537
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
+#define _LINUX_IIO_TRIGGERED_EVENT_H_
+
+#include <linux/interrupt.h>
+
+int iio_triggered_event_setup(struct iio_dev *indio_dev,
+       irqreturn_t (*h)(int irq, void *p),
+       irqreturn_t (*thread)(int irq, void *p));
+void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
+
+#endif
index 2f8b117..7c63bd6 100644 (file)
@@ -35,6 +35,8 @@ enum iio_chan_type {
        IIO_ENERGY,
        IIO_DISTANCE,
        IIO_VELOCITY,
+       IIO_CONCENTRATION,
+       IIO_RESISTANCE,
 };
 
 enum iio_modifier {
@@ -72,6 +74,8 @@ enum iio_modifier {
        IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
        IIO_MOD_I,
        IIO_MOD_Q,
+       IIO_MOD_CO2,
+       IIO_MOD_VOC,
 };
 
 enum iio_event_type {
index 9f7b85b..01c4f67 100644 (file)
@@ -328,6 +328,15 @@ int main(int argc, char **argv)
                        "diag %s\n", dev_dir_name);
                goto error_free_triggername;
        }
+       if (!num_channels) {
+               fprintf(stderr,
+                       "No channels are enabled, we have nothing to scan.\n");
+               fprintf(stderr, "Enable channels manually in "
+                       FORMAT_SCAN_ELEMENTS_DIR
+                       "/*_en and try again.\n", dev_dir_name);
+               ret = -ENOENT;
+               goto error_free_triggername;
+       }
 
        /*
         * Construct the directory name for the associated buffer.
index cd3fd41..d51eb04 100644 (file)
@@ -284,7 +284,11 @@ int main(int argc, char **argv)
        ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd);
        if (ret == -1 || event_fd == -1) {
                ret = -errno;
-               fprintf(stderr, "Failed to retrieve event fd\n");
+               if (ret == -ENODEV)
+                       fprintf(stderr,
+                               "This device does not support events\n");
+               else
+                       fprintf(stderr, "Failed to retrieve event fd\n");
                if (close(fd) == -1)
                        perror("Failed to close character device file");