]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'iio-for-4.6a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Feb 2016 21:10:03 +0000 (13:10 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Feb 2016 21:10:03 +0000 (13:10 -0800)
Jonathan writes:

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

Device Support
* ad5761
  - new driver
* at91_sama5d2 ADC.
  - new driver and MAINTAINERS entry.
  - minor cleanups followed.
* atlas pH-SM
  - new driver (this has possibly the prettiest data sheet I've ever seen)
* mcp3422
  - mcp3425 ADC added.
* mcp4725
  - mcp4726 DAC added.
* mma8452
  - mma8451q accelerometer added.
* mpl115
  - mpl115a1 added (a lot bigger than it seems as this is an SPI part whereas
    previous parts were i2c).
* si7005
  - Hoperf th02 (seems to be a repackaged part)
* si7020
  - Hoperf th06 (seems to be a repackaged part)

New features
* Core
  - IIO_PH type. Does what it says on the tin.
* max30100
  - LED current configuration support.
* mcp320x
  - more differential measurement combinations.
* mma8452
  - free fall deteciton
- opt3001
  - enable operation without a IRQ line.
  - device tree docs.  Somehow the original docs have disappeared down
    a rabbit hole, so here is a new set.
* st-sensors
  - Support active-low interrupts.

Cleanups and minor / not so minor reworks
* Documentation
  - drop some defunct ABI from the docs in staging.
* presure / Kconfig
  - white space cleanup.
* ad7150
  - BIT macro usage
  - Alignment fixes
* ad7192
  - false indent fixed.
* ak8975
  - constify the ak_def structures
* axp288
  - drop a redundant double const.
* dht11
  - substantial reliability improvements by being more tolerant
    of missing start bits.
  - simplify the decoding algorithm
* mma8452
  - whitespace cleanup
* mpl115
  - don't bother setting i2c_client_data as nothing uses it.
* mpu6050
  - drop unused function parameter.
* opt3001
  - extract integration time as constants.
  - trivial refactoring.

53 files changed:
CREDITS
Documentation/ABI/testing/sysfs-bus-iio
Documentation/devicetree/bindings/iio/accel/mma8452.txt
Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/adc/mcp3422.txt
Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iio/health/max30100.txt
Documentation/devicetree/bindings/iio/light/opt3001.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/iio/accel/Kconfig
drivers/iio/accel/mma8452.c
drivers/iio/accel/st_accel_core.c
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/at91-sama5d2_adc.c [new file with mode: 0644]
drivers/iio/adc/axp288_adc.c
drivers/iio/adc/mcp320x.c
drivers/iio/adc/mcp3422.c
drivers/iio/chemical/Kconfig
drivers/iio/chemical/Makefile
drivers/iio/chemical/atlas-ph-sensor.c [new file with mode: 0644]
drivers/iio/common/st_sensors/st_sensors_core.c
drivers/iio/common/st_sensors/st_sensors_core.h [new file with mode: 0644]
drivers/iio/common/st_sensors/st_sensors_trigger.c
drivers/iio/dac/Kconfig
drivers/iio/dac/Makefile
drivers/iio/dac/ad5761.c [new file with mode: 0644]
drivers/iio/dac/mcp4725.c
drivers/iio/gyro/st_gyro_core.c
drivers/iio/health/max30100.c
drivers/iio/humidity/Kconfig
drivers/iio/humidity/dht11.c
drivers/iio/humidity/si7005.c
drivers/iio/humidity/si7020.c
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
drivers/iio/industrialio-core.c
drivers/iio/light/opt3001.c
drivers/iio/magnetometer/ak8975.c
drivers/iio/magnetometer/st_magn_core.c
drivers/iio/pressure/Kconfig
drivers/iio/pressure/Makefile
drivers/iio/pressure/mpl115.c
drivers/iio/pressure/mpl115.h [new file with mode: 0644]
drivers/iio/pressure/mpl115_i2c.c [new file with mode: 0644]
drivers/iio/pressure/mpl115_spi.c [new file with mode: 0644]
drivers/iio/pressure/st_pressure_core.c
drivers/staging/iio/Documentation/sysfs-bus-iio-light
drivers/staging/iio/adc/ad7192.c
drivers/staging/iio/cdc/ad7150.c
include/linux/iio/common/st_sensors.h
include/linux/platform_data/ad5761.h [new file with mode: 0644]
include/uapi/linux/iio/types.h

diff --git a/CREDITS b/CREDITS
index a3887b59b9f94dffce186079756b29d01fb7b91b..4312cd076b5b4306f2a799e4d12bb8db15f0f011 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -3054,6 +3054,7 @@ D: PLX USB338x driver
 D: PCA9634 driver
 D: Option GTM671WFS
 D: Fintek F81216A
+D: AD5761 iio driver
 D: Various kernel hacks
 S: Qtechnology A/S
 S: Valby Langgade 142
index 0439c2aaf7419867c1dce0c223190530eb970d70..80c6fce9935bf28e000a795deff67d64ab6f7142 100644 (file)
@@ -497,7 +497,9 @@ Description:
                6kohm_to_gnd: connected to ground via a 6kOhm resistor,
                20kohm_to_gnd: connected to ground via a 20kOhm resistor,
                100kohm_to_gnd: connected to ground via an 100kOhm resistor,
+               125kohm_to_gnd: connected to ground via an 125kOhm resistor,
                500kohm_to_gnd: connected to ground via a 500kOhm resistor,
+               640kohm_to_gnd: connected to ground via a 640kOhm resistor,
                three_state: left floating.
                For a list of available output power down options read
                outX_powerdown_mode_available. If Y is not present the
@@ -1491,3 +1493,10 @@ Description:
                This ABI is especially applicable for humidity sensors
                to heatup the device and get rid of any condensation
                in some humidity environment
+
+What:          /sys/bus/iio/devices/iio:deviceX/in_ph_raw
+KernelVersion: 4.5
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Raw (unscaled no offset etc.) pH reading of a substance as a negative
+               base-10 logarithm of hydrodium ions in a litre of water.
index 3c10e8581144a128f380240ea8537bfc2a963956..165937e1ac1c4da1b029af3341b6f860e2f956ba 100644 (file)
@@ -1,8 +1,10 @@
-Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
+Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC
+triaxial accelerometer
 
 Required properties:
 
   - compatible: should contain one of
+    * "fsl,mma8451"
     * "fsl,mma8452"
     * "fsl,mma8453"
     * "fsl,mma8652"
diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
new file mode 100644 (file)
index 0000000..3223684
--- /dev/null
@@ -0,0 +1,28 @@
+* AT91 SAMA5D2 Analog to Digital Converter (ADC)
+
+Required properties:
+  - compatible: Should be "atmel,sama5d2-adc".
+  - reg: Should contain ADC registers location and length.
+  - interrupts: Should contain the IRQ line for the ADC.
+  - clocks: phandle to device clock.
+  - clock-names: Must be "adc_clk".
+  - vref-supply: Supply used as reference for conversions.
+  - vddana-supply: Supply for the adc device.
+  - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
+  - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
+  - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
+
+Example:
+
+adc: adc@fc030000 {
+       compatible = "atmel,sama5d2-adc";
+       reg = <0xfc030000 0x100>;
+       interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+       clocks = <&adc_clk>;
+       clock-names = "adc_clk";
+       atmel,min-sample-rate-hz = <200000>;
+       atmel,max-sample-rate-hz = <20000000>;
+       atmel,startup-time-ms = <4>;
+       vddana-supply = <&vdd_3v3_lp_reg>;
+       vref-supply = <&vdd_3v3_lp_reg>;
+}
index dcae4ccfcc5284b8f116e2c788fbc30c996603c9..82bcce07255d4792baee3038b0a2dfadbff7b3c4 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
        "microchip,mcp3422" or
        "microchip,mcp3423" or
        "microchip,mcp3424" or
+       "microchip,mcp3425" or
        "microchip,mcp3426" or
        "microchip,mcp3427" or
        "microchip,mcp3428"
diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt
new file mode 100644 (file)
index 0000000..cffa190
--- /dev/null
@@ -0,0 +1,22 @@
+* Atlas Scientific pH-SM OEM sensor
+
+http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf
+
+Required properties:
+
+  - compatible: must be "atlas,ph-sm"
+  - reg: the I2C address of the sensor
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: the sole interrupt generated by the device
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt client
+  node bindings.
+
+Example:
+
+atlas@65 {
+       compatible = "atlas,ph-sm";
+       reg = <0x65>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <16 2>;
+};
index f6fbac66ad06e7429e9ab218a48b20d16abe0ab9..295a9edfa4fdcf89506c54321e573fa7cf044bcd 100644 (file)
@@ -11,11 +11,19 @@ Required properties:
   Refer to interrupt-controller/interrupts.txt for generic
   interrupt client node bindings.
 
+Optional properties:
+  - maxim,led-current-microamp: configuration for LED current in microamperes
+    while the engine is running. First indexed value is the configuration for
+    the RED LED, and second value is for the IR LED.
+
+    Refer to the datasheet for the allowed current values.
+
 Example:
 
 max30100@057 {
        compatible = "maxim,max30100";
        reg = <57>;
+       maxim,led-current-microamp = <24000 50000>;
        interrupt-parent = <&gpio1>;
        interrupts = <16 2>;
 };
diff --git a/Documentation/devicetree/bindings/iio/light/opt3001.txt b/Documentation/devicetree/bindings/iio/light/opt3001.txt
new file mode 100644 (file)
index 0000000..eac30d5
--- /dev/null
@@ -0,0 +1,26 @@
+* Texas Instruments OPT3001 Ambient Light Sensor
+
+The driver supports interrupt-driven and interrupt-less operation, depending
+on whether an interrupt property has been populated into the DT. Note that
+the optional generation of IIO events on rising/falling light threshold changes
+requires the use of interrupts. Without interrupts, only the simple reading
+of the current light value is supported through the IIO API.
+
+http://www.ti.com/product/opt3001
+
+Required properties:
+  - compatible: should be "ti,opt3001"
+  - reg: the I2C address of the sensor
+
+Optional properties:
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: interrupt mapping for GPIO IRQ (configure for falling edge)
+
+Example:
+
+opt3001@44 {
+       compatible = "ti,opt3001";
+       reg = <0x44>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <28 IRQ_TYPE_EDGE_FALLING>;
+};
index 72e2c5a2b3278facb20378383cbb63baa6485e0f..44ddc980b085a7119c33f6061c345dba3eb57d81 100644 (file)
@@ -28,6 +28,7 @@ arm   ARM Ltd.
 armadeus       ARMadeus Systems SARL
 artesyn        Artesyn Embedded Technologies Inc.
 asahi-kasei    Asahi Kasei Corp.
+atlas  Atlas Scientific LLC
 atmel  Atmel Corporation
 auo    AU Optronics Corporation
 avago  Avago Technologies
index 79f7eb25ecd89c466286358b6c6f14e3112a6022..369f6aefc18985e9279a412b922725dc0eca4edf 100644 (file)
@@ -1945,6 +1945,12 @@ M:       Nicolas Ferre <nicolas.ferre@atmel.com>
 S:     Supported
 F:     drivers/tty/serial/atmel_serial.c
 
+ATMEL SAMA5D2 ADC DRIVER
+M:     Ludovic Desroches <ludovic.desroches@atmel.com>
+L:     linux-iio@vger.kernel.org
+S:     Supported
+F:     drivers/iio/adc/at91-sama5d2_adc.c
+
 ATMEL Audio ALSA driver
 M:     Nicolas Ferre <nicolas.ferre@atmel.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
index edc29b173f6c9012635771116a6cca23193a096e..d9feaa3c1fb80370d30cb5b94afa4162c0edbb61 100644 (file)
@@ -143,7 +143,7 @@ config MMA8452
        select IIO_TRIGGERED_BUFFER
        help
          Say yes here to build support for the following Freescale 3-axis
-         accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
+         accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
 
          To compile this driver as a module, choose M here: the module
          will be called mma8452.
index ccc632a7cf0191639dcab528bf145d8b813b9015..7f4994f32a9078ff58a2f8d4f803084cf83ddbf7 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * mma8452.c - Support for following Freescale 3-axis accelerometers:
  *
+ * MMA8451Q (14 bit)
  * MMA8452Q (12 bit)
  * MMA8453Q (10 bit)
  * MMA8652FC (12 bit)
@@ -15,7 +16,7 @@
  *
  * 7-bit I2C slave address 0x1c/0x1d (pin selectable)
  *
- * TODO: orientation / freefall events, autosleep
+ * TODO: orientation events, autosleep
  */
 
 #include <linux/module.h>
@@ -85,8 +86,9 @@
 #define  MMA8452_INT_FF_MT                     BIT(2)
 #define  MMA8452_INT_TRANS                     BIT(5)
 
-#define  MMA8452_DEVICE_ID                     0x2a
-#define  MMA8453_DEVICE_ID                     0x3a
+#define MMA8451_DEVICE_ID                      0x1a
+#define MMA8452_DEVICE_ID                      0x2a
+#define MMA8453_DEVICE_ID                      0x3a
 #define MMA8652_DEVICE_ID                      0x4a
 #define MMA8653_DEVICE_ID                      0x5a
 
@@ -416,6 +418,51 @@ fail:
        return ret;
 }
 
+/* returns >0 if in freefall mode, 0 if not or <0 if an error occured */
+static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
+{
+       int val;
+       const struct mma_chip_info *chip = data->chip_info;
+
+       val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
+       if (val < 0)
+               return val;
+
+       return !(val & MMA8452_FF_MT_CFG_OAE);
+}
+
+static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state)
+{
+       int val;
+       const struct mma_chip_info *chip = data->chip_info;
+
+       if ((state && mma8452_freefall_mode_enabled(data)) ||
+           (!state && !(mma8452_freefall_mode_enabled(data))))
+               return 0;
+
+       val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
+       if (val < 0)
+               return val;
+
+       if (state) {
+               val |= BIT(idx_x + chip->ev_cfg_chan_shift);
+               val |= BIT(idx_y + chip->ev_cfg_chan_shift);
+               val |= BIT(idx_z + chip->ev_cfg_chan_shift);
+               val &= ~MMA8452_FF_MT_CFG_OAE;
+       } else {
+               val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
+               val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
+               val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
+               val |= MMA8452_FF_MT_CFG_OAE;
+       }
+
+       val = mma8452_change_config(data, chip->ev_cfg, val);
+       if (val)
+               return val;
+
+       return 0;
+}
+
 static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
                                           int val, int val2)
 {
@@ -609,12 +656,22 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
        const struct mma_chip_info *chip = data->chip_info;
        int ret;
 
-       ret = i2c_smbus_read_byte_data(data->client,
-                                      data->chip_info->ev_cfg);
-       if (ret < 0)
-               return ret;
+       switch (dir) {
+       case IIO_EV_DIR_FALLING:
+               return mma8452_freefall_mode_enabled(data);
+       case IIO_EV_DIR_RISING:
+               if (mma8452_freefall_mode_enabled(data))
+                       return 0;
+
+               ret = i2c_smbus_read_byte_data(data->client,
+                                              data->chip_info->ev_cfg);
+               if (ret < 0)
+                       return ret;
 
-       return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
+               return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
+       default:
+               return -EINVAL;
+       }
 }
 
 static int mma8452_write_event_config(struct iio_dev *indio_dev,
@@ -627,19 +684,35 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
        const struct mma_chip_info *chip = data->chip_info;
        int val;
 
-       val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
-       if (val < 0)
-               return val;
+       switch (dir) {
+       case IIO_EV_DIR_FALLING:
+               return mma8452_set_freefall_mode(data, state);
+       case IIO_EV_DIR_RISING:
+               val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
+               if (val < 0)
+                       return val;
+
+               if (state) {
+                       if (mma8452_freefall_mode_enabled(data)) {
+                               val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
+                               val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
+                               val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
+                               val |= MMA8452_FF_MT_CFG_OAE;
+                       }
+                       val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
+               } else {
+                       if (mma8452_freefall_mode_enabled(data))
+                               return 0;
 
-       if (state)
-               val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
-       else
-               val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
+                       val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
+               }
 
-       val |= chip->ev_cfg_ele;
-       val |= MMA8452_FF_MT_CFG_OAE;
+               val |= chip->ev_cfg_ele;
 
-       return mma8452_change_config(data, chip->ev_cfg, val);
+               return mma8452_change_config(data, chip->ev_cfg, val);
+       default:
+               return -EINVAL;
+       }
 }
 
 static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
@@ -652,6 +725,16 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
        if (src < 0)
                return;
 
+       if (mma8452_freefall_mode_enabled(data)) {
+               iio_push_event(indio_dev,
+                              IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+                                                 IIO_MOD_X_AND_Y_AND_Z,
+                                                 IIO_EV_TYPE_MAG,
+                                                 IIO_EV_DIR_FALLING),
+                              ts);
+               return;
+       }
+
        if (src & data->chip_info->ev_src_xe)
                iio_push_event(indio_dev,
                               IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
@@ -745,6 +828,27 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
        return 0;
 }
 
+static const struct iio_event_spec mma8452_freefall_event[] = {
+       {
+               .type = IIO_EV_TYPE_MAG,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+               .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+                                       BIT(IIO_EV_INFO_PERIOD) |
+                                       BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
+       },
+};
+
+static const struct iio_event_spec mma8652_freefall_event[] = {
+       {
+               .type = IIO_EV_TYPE_MAG,
+               .dir = IIO_EV_DIR_FALLING,
+               .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+               .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+                                       BIT(IIO_EV_INFO_PERIOD)
+       },
+};
+
 static const struct iio_event_spec mma8452_transient_event[] = {
        {
                .type = IIO_EV_TYPE_MAG,
@@ -781,6 +885,24 @@ static struct attribute_group mma8452_event_attribute_group = {
        .attrs = mma8452_event_attributes,
 };
 
+#define MMA8452_FREEFALL_CHANNEL(modifier) { \
+       .type = IIO_ACCEL, \
+       .modified = 1, \
+       .channel2 = modifier, \
+       .scan_index = -1, \
+       .event_spec = mma8452_freefall_event, \
+       .num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
+}
+
+#define MMA8652_FREEFALL_CHANNEL(modifier) { \
+       .type = IIO_ACCEL, \
+       .modified = 1, \
+       .channel2 = modifier, \
+       .scan_index = -1, \
+       .event_spec = mma8652_freefall_event, \
+       .num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
+}
+
 #define MMA8452_CHANNEL(axis, idx, bits) { \
        .type = IIO_ACCEL, \
        .modified = 1, \
@@ -822,11 +944,20 @@ static struct attribute_group mma8452_event_attribute_group = {
        .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
 }
 
+static const struct iio_chan_spec mma8451_channels[] = {
+       MMA8452_CHANNEL(X, idx_x, 14),
+       MMA8452_CHANNEL(Y, idx_y, 14),
+       MMA8452_CHANNEL(Z, idx_z, 14),
+       IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
+       MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
+};
+
 static const struct iio_chan_spec mma8452_channels[] = {
        MMA8452_CHANNEL(X, idx_x, 12),
        MMA8452_CHANNEL(Y, idx_y, 12),
        MMA8452_CHANNEL(Z, idx_z, 12),
        IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
+       MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
 };
 
 static const struct iio_chan_spec mma8453_channels[] = {
@@ -834,6 +965,7 @@ static const struct iio_chan_spec mma8453_channels[] = {
        MMA8452_CHANNEL(Y, idx_y, 10),
        MMA8452_CHANNEL(Z, idx_z, 10),
        IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
+       MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
 };
 
 static const struct iio_chan_spec mma8652_channels[] = {
@@ -841,6 +973,7 @@ static const struct iio_chan_spec mma8652_channels[] = {
        MMA8652_CHANNEL(Y, idx_y, 12),
        MMA8652_CHANNEL(Z, idx_z, 12),
        IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
+       MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
 };
 
 static const struct iio_chan_spec mma8653_channels[] = {
@@ -848,9 +981,11 @@ static const struct iio_chan_spec mma8653_channels[] = {
        MMA8652_CHANNEL(Y, idx_y, 10),
        MMA8652_CHANNEL(Z, idx_z, 10),
        IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
+       MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
 };
 
 enum {
+       mma8451,
        mma8452,
        mma8453,
        mma8652,
@@ -858,17 +993,34 @@ enum {
 };
 
 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),
+       [mma8451] = {
+               .chip_id = MMA8451_DEVICE_ID,
+               .channels = mma8451_channels,
+               .num_channels = ARRAY_SIZE(mma8451_channels),
                /*
                 * Hardware has fullscale of -2G, -4G, -8G corresponding to
-                * raw value -2048 for 12 bit or -512 for 10 bit.
+                * raw value -8192 for 14 bit, -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
+                *      g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
                 */
+               .mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
+               .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,
+       },
+       [mma8452] = {
+               .chip_id = MMA8452_DEVICE_ID,
+               .channels = mma8452_channels,
+               .num_channels = ARRAY_SIZE(mma8452_channels),
                .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
                .ev_cfg = MMA8452_TRANSIENT_CFG,
                .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
@@ -1049,6 +1201,7 @@ static int mma8452_reset(struct i2c_client *client)
 }
 
 static const struct of_device_id mma8452_dt_ids[] = {
+       { .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] },
        { .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] },
@@ -1085,6 +1238,7 @@ static int mma8452_probe(struct i2c_client *client,
                return ret;
 
        switch (ret) {
+       case MMA8451_DEVICE_ID:
        case MMA8452_DEVICE_ID:
        case MMA8453_DEVICE_ID:
        case MMA8652_DEVICE_ID:
@@ -1190,6 +1344,10 @@ static int mma8452_probe(struct i2c_client *client,
        if (ret < 0)
                goto buffer_cleanup;
 
+       ret = mma8452_set_freefall_mode(data, false);
+       if (ret)
+               return ret;
+
        return 0;
 
 buffer_cleanup:
index 70f042797f152a42bff02304987dad0c60d2fd0f..a03a1417dd632da052f0ef03b478df18f15fd2cf 100644 (file)
@@ -67,6 +67,8 @@
 #define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
 #define ST_ACCEL_1_DRDY_IRQ_INT1_MASK          0x10
 #define ST_ACCEL_1_DRDY_IRQ_INT2_MASK          0x08
+#define ST_ACCEL_1_IHL_IRQ_ADDR                        0x25
+#define ST_ACCEL_1_IHL_IRQ_MASK                        0x02
 #define ST_ACCEL_1_MULTIREAD_BIT               true
 
 /* CUSTOM VALUES FOR SENSOR 2 */
@@ -92,6 +94,8 @@
 #define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
 #define ST_ACCEL_2_DRDY_IRQ_INT1_MASK          0x02
 #define ST_ACCEL_2_DRDY_IRQ_INT2_MASK          0x10
+#define ST_ACCEL_2_IHL_IRQ_ADDR                        0x22
+#define ST_ACCEL_2_IHL_IRQ_MASK                        0x80
 #define ST_ACCEL_2_MULTIREAD_BIT               true
 
 /* CUSTOM VALUES FOR SENSOR 3 */
 #define ST_ACCEL_3_DRDY_IRQ_ADDR               0x23
 #define ST_ACCEL_3_DRDY_IRQ_INT1_MASK          0x80
 #define ST_ACCEL_3_DRDY_IRQ_INT2_MASK          0x00
+#define ST_ACCEL_3_IHL_IRQ_ADDR                        0x23
+#define ST_ACCEL_3_IHL_IRQ_MASK                        0x40
 #define ST_ACCEL_3_IG1_EN_ADDR                 0x23
 #define ST_ACCEL_3_IG1_EN_MASK                 0x08
 #define ST_ACCEL_3_MULTIREAD_BIT               false
 #define ST_ACCEL_5_DRDY_IRQ_ADDR               0x22
 #define ST_ACCEL_5_DRDY_IRQ_INT1_MASK          0x04
 #define ST_ACCEL_5_DRDY_IRQ_INT2_MASK          0x20
+#define ST_ACCEL_5_IHL_IRQ_ADDR                        0x22
+#define ST_ACCEL_5_IHL_IRQ_MASK                        0x80
 #define ST_ACCEL_5_IG1_EN_ADDR                 0x21
 #define ST_ACCEL_5_IG1_EN_MASK                 0x08
 #define ST_ACCEL_5_MULTIREAD_BIT               false
@@ -292,6 +300,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
                        .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
                .bootime = 2,
@@ -355,6 +365,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
                        .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
                .bootime = 2,
@@ -430,6 +442,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
                        .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
                        .ig1 = {
                                .en_addr = ST_ACCEL_3_IG1_EN_ADDR,
                                .en_mask = ST_ACCEL_3_IG1_EN_MASK,
@@ -537,6 +551,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
                        .addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
                .bootime = 2, /* guess */
index 605ff42c46310201e7aba3fd2f7ab42a0a873e04..60673b40f2c3f3380d16651df6fcbec520073ebd 100644 (file)
@@ -131,6 +131,16 @@ config AT91_ADC
          To compile this driver as a module, choose M here: the module will be
          called at91_adc.
 
+config AT91_SAMA5D2_ADC
+       tristate "Atmel AT91 SAMA5D2 ADC"
+       depends on ARCH_AT91 || COMPILE_TEST
+       help
+         Say yes here to build support for Atmel SAMA5D2 ADC which is
+         available on SAMA5D2 SoC family.
+
+         To compile this driver as a module, choose M here: the module will be
+         called at91-sama5d2_adc.
+
 config AXP288_ADC
        tristate "X-Powers AXP288 ADC driver"
        depends on MFD_AXP20X
@@ -265,11 +275,11 @@ config MCP320X
          called mcp320x.
 
 config MCP3422
-       tristate "Microchip Technology MCP3422/3/4/6/7/8 driver"
+       tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver"
        depends on I2C
        help
-         Say yes here to build support for Microchip Technology's
-         MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428
+         Say yes here to build support for Microchip Technology's MCP3421
+         MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428
          analog to digital converters.
 
          This driver can also be built as a module. If so, the module will be
index 6435780e9b717374b11536cb5c072a10b2c63b85..fb57e12dbde23ac64ca45f56f1f830a950b8d003 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
 obj-$(CONFIG_AD7887) += ad7887.o
 obj-$(CONFIG_AD799X) += ad799x.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
 obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
 obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
new file mode 100644 (file)
index 0000000..dbee13a
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Atmel ADC driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2015 Atmel,
+ *               2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+/* Control Register */
+#define AT91_SAMA5D2_CR                0x00
+/* Software Reset */
+#define        AT91_SAMA5D2_CR_SWRST           BIT(0)
+/* Start Conversion */
+#define        AT91_SAMA5D2_CR_START           BIT(1)
+/* Touchscreen Calibration */
+#define        AT91_SAMA5D2_CR_TSCALIB         BIT(2)
+/* Comparison Restart */
+#define        AT91_SAMA5D2_CR_CMPRST          BIT(4)
+
+/* Mode Register */
+#define AT91_SAMA5D2_MR                0x04
+/* Trigger Selection */
+#define        AT91_SAMA5D2_MR_TRGSEL(v)       ((v) << 1)
+/* ADTRG */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG0    0
+/* TIOA0 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG1    1
+/* TIOA1 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG2    2
+/* TIOA2 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG3    3
+/* PWM event line 0 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG4    4
+/* PWM event line 1 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG5    5
+/* TIOA3 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG6    6
+/* RTCOUT0 */
+#define        AT91_SAMA5D2_MR_TRGSEL_TRIG7    7
+/* Sleep Mode */
+#define        AT91_SAMA5D2_MR_SLEEP           BIT(5)
+/* Fast Wake Up */
+#define        AT91_SAMA5D2_MR_FWUP            BIT(6)
+/* Prescaler Rate Selection */
+#define        AT91_SAMA5D2_MR_PRESCAL(v)      ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+#define        AT91_SAMA5D2_MR_PRESCAL_OFFSET  8
+#define        AT91_SAMA5D2_MR_PRESCAL_MAX     0xff
+/* Startup Time */
+#define        AT91_SAMA5D2_MR_STARTUP(v)      ((v) << 16)
+/* Analog Change */
+#define        AT91_SAMA5D2_MR_ANACH           BIT(23)
+/* Tracking Time */
+#define        AT91_SAMA5D2_MR_TRACKTIM(v)     ((v) << 24)
+#define        AT91_SAMA5D2_MR_TRACKTIM_MAX    0xff
+/* Transfer Time */
+#define        AT91_SAMA5D2_MR_TRANSFER(v)     ((v) << 28)
+#define        AT91_SAMA5D2_MR_TRANSFER_MAX    0x3
+/* Use Sequence Enable */
+#define        AT91_SAMA5D2_MR_USEQ            BIT(31)
+
+/* Channel Sequence Register 1 */
+#define AT91_SAMA5D2_SEQR1     0x08
+/* Channel Sequence Register 2 */
+#define AT91_SAMA5D2_SEQR2     0x0c
+/* Channel Enable Register */
+#define AT91_SAMA5D2_CHER      0x10
+/* Channel Disable Register */
+#define AT91_SAMA5D2_CHDR      0x14
+/* Channel Status Register */
+#define AT91_SAMA5D2_CHSR      0x18
+/* Last Converted Data Register */
+#define AT91_SAMA5D2_LCDR      0x20
+/* Interrupt Enable Register */
+#define AT91_SAMA5D2_IER               0x24
+/* Interrupt Disable Register */
+#define AT91_SAMA5D2_IDR               0x28
+/* Interrupt Mask Register */
+#define AT91_SAMA5D2_IMR               0x2c
+/* Interrupt Status Register */
+#define AT91_SAMA5D2_ISR               0x30
+/* Last Channel Trigger Mode Register */
+#define AT91_SAMA5D2_LCTMR     0x34
+/* Last Channel Compare Window Register */
+#define AT91_SAMA5D2_LCCWR     0x38
+/* Overrun Status Register */
+#define AT91_SAMA5D2_OVER      0x3c
+/* Extended Mode Register */
+#define AT91_SAMA5D2_EMR               0x40
+/* Compare Window Register */
+#define AT91_SAMA5D2_CWR               0x44
+/* Channel Gain Register */
+#define AT91_SAMA5D2_CGR               0x48
+/* Channel Offset Register */
+#define AT91_SAMA5D2_COR               0x4c
+/* Channel Data Register 0 */
+#define AT91_SAMA5D2_CDR0      0x50
+/* Analog Control Register */
+#define AT91_SAMA5D2_ACR               0x94
+/* Touchscreen Mode Register */
+#define AT91_SAMA5D2_TSMR      0xb0
+/* Touchscreen X Position Register */
+#define AT91_SAMA5D2_XPOSR     0xb4
+/* Touchscreen Y Position Register */
+#define AT91_SAMA5D2_YPOSR     0xb8
+/* Touchscreen Pressure Register */
+#define AT91_SAMA5D2_PRESSR    0xbc
+/* Trigger Register */
+#define AT91_SAMA5D2_TRGR      0xc0
+/* Correction Select Register */
+#define AT91_SAMA5D2_COSR      0xd0
+/* Correction Value Register */
+#define AT91_SAMA5D2_CVR               0xd4
+/* Channel Error Correction Register */
+#define AT91_SAMA5D2_CECR      0xd8
+/* Write Protection Mode Register */
+#define AT91_SAMA5D2_WPMR      0xe4
+/* Write Protection Status Register */
+#define AT91_SAMA5D2_WPSR      0xe8
+/* Version Register */
+#define AT91_SAMA5D2_VERSION   0xfc
+
+#define AT91_AT91_SAMA5D2_CHAN(num, addr)                              \
+       {                                                               \
+               .type = IIO_VOLTAGE,                                    \
+               .channel = num,                                         \
+               .address = addr,                                        \
+               .scan_type = {                                          \
+                       .sign = 'u',                                    \
+                       .realbits = 12,                                 \
+               },                                                      \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+               .datasheet_name = "CH"#num,                             \
+               .indexed = 1,                                           \
+       }
+
+#define at91_adc_readl(st, reg)                readl_relaxed(st->base + reg)
+#define at91_adc_writel(st, reg, val)  writel_relaxed(val, st->base + reg)
+
+struct at91_adc_soc_info {
+       unsigned                        startup_time;
+       unsigned                        min_sample_rate;
+       unsigned                        max_sample_rate;
+};
+
+struct at91_adc_state {
+       void __iomem                    *base;
+       int                             irq;
+       struct clk                      *per_clk;
+       struct regulator                *reg;
+       struct regulator                *vref;
+       int                             vref_uv;
+       const struct iio_chan_spec      *chan;
+       bool                            conversion_done;
+       u32                             conversion_value;
+       struct at91_adc_soc_info        soc_info;
+       wait_queue_head_t               wq_data_available;
+       /*
+        * lock to prevent concurrent 'single conversion' requests through
+        * sysfs.
+        */
+       struct mutex                    lock;
+};
+
+static const struct iio_chan_spec at91_adc_channels[] = {
+       AT91_AT91_SAMA5D2_CHAN(0, 0x50),
+       AT91_AT91_SAMA5D2_CHAN(1, 0x54),
+       AT91_AT91_SAMA5D2_CHAN(2, 0x58),
+       AT91_AT91_SAMA5D2_CHAN(3, 0x5c),
+       AT91_AT91_SAMA5D2_CHAN(4, 0x60),
+       AT91_AT91_SAMA5D2_CHAN(5, 0x64),
+       AT91_AT91_SAMA5D2_CHAN(6, 0x68),
+       AT91_AT91_SAMA5D2_CHAN(7, 0x6c),
+       AT91_AT91_SAMA5D2_CHAN(8, 0x70),
+       AT91_AT91_SAMA5D2_CHAN(9, 0x74),
+       AT91_AT91_SAMA5D2_CHAN(10, 0x78),
+       AT91_AT91_SAMA5D2_CHAN(11, 0x7c),
+};
+
+static unsigned at91_adc_startup_time(unsigned startup_time_min,
+                                     unsigned adc_clk_khz)
+{
+       const unsigned startup_lookup[] = {
+                 0,   8,  16,  24,
+                64,  80,  96, 112,
+               512, 576, 640, 704,
+               768, 832, 896, 960
+               };
+       unsigned ticks_min, i;
+
+       /*
+        * Since the adc frequency is checked before, there is no reason
+        * to not meet the startup time constraint.
+        */
+
+       ticks_min = startup_time_min * adc_clk_khz / 1000;
+       for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
+               if (startup_lookup[i] > ticks_min)
+                       break;
+
+       return i;
+}
+
+static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
+{
+       struct iio_dev *indio_dev = iio_priv_to_dev(st);
+       unsigned f_per, prescal, startup;
+
+       f_per = clk_get_rate(st->per_clk);
+       prescal = (f_per / (2 * freq)) - 1;
+
+       startup = at91_adc_startup_time(st->soc_info.startup_time,
+                                       freq / 1000);
+
+       at91_adc_writel(st, AT91_SAMA5D2_MR,
+                       AT91_SAMA5D2_MR_TRANSFER(2)
+                       | AT91_SAMA5D2_MR_STARTUP(startup)
+                       | AT91_SAMA5D2_MR_PRESCAL(prescal));
+
+       dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
+               freq, startup, prescal);
+}
+
+static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
+{
+       unsigned f_adc, f_per = clk_get_rate(st->per_clk);
+       unsigned mr, prescal;
+
+       mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
+       prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+                 & AT91_SAMA5D2_MR_PRESCAL_MAX;
+       f_adc = f_per / (2 * (prescal + 1));
+
+       return f_adc;
+}
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+       struct iio_dev *indio = private;
+       struct at91_adc_state *st = iio_priv(indio);
+       u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
+       u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
+
+       if (status & imr) {
+               st->conversion_value = at91_adc_readl(st, st->chan->address);
+               st->conversion_done = true;
+               wake_up_interruptible(&st->wq_data_available);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static int at91_adc_read_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int *val, int *val2, long mask)
+{
+       struct at91_adc_state *st = iio_priv(indio_dev);
+       int ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&st->lock);
+
+               st->chan = chan;
+
+               at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
+               at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
+               at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START);
+
+               ret = wait_event_interruptible_timeout(st->wq_data_available,
+                                                      st->conversion_done,
+                                                      msecs_to_jiffies(1000));
+               if (ret == 0)
+                       ret = -ETIMEDOUT;
+
+               if (ret > 0) {
+                       *val = st->conversion_value;
+                       ret = IIO_VAL_INT;
+                       st->conversion_done = false;
+               }
+
+               at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
+               at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
+
+               mutex_unlock(&st->lock);
+               return ret;
+
+       case IIO_CHAN_INFO_SCALE:
+               *val = st->vref_uv / 1000;
+               *val2 = chan->scan_type.realbits;
+               return IIO_VAL_FRACTIONAL_LOG2;
+
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *val = at91_adc_get_sample_freq(st);
+               return IIO_VAL_INT;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int at91_adc_write_raw(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan,
+                             int val, int val2, long mask)
+{
+       struct at91_adc_state *st = iio_priv(indio_dev);
+
+       if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+               return -EINVAL;
+
+       if (val < st->soc_info.min_sample_rate ||
+           val > st->soc_info.max_sample_rate)
+               return -EINVAL;
+
+       at91_adc_setup_samp_freq(st, val);
+
+       return 0;
+}
+
+static const struct iio_info at91_adc_info = {
+       .read_raw = &at91_adc_read_raw,
+       .write_raw = &at91_adc_write_raw,
+       .driver_module = THIS_MODULE,
+};
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev;
+       struct at91_adc_state *st;
+       struct resource *res;
+       int ret;
+
+       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+       if (!indio_dev)
+               return -ENOMEM;
+
+       indio_dev->dev.parent = &pdev->dev;
+       indio_dev->name = dev_name(&pdev->dev);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &at91_adc_info;
+       indio_dev->channels = at91_adc_channels;
+       indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
+
+       st = iio_priv(indio_dev);
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+                                  "atmel,min-sample-rate-hz",
+                                  &st->soc_info.min_sample_rate);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "invalid or missing value for atmel,min-sample-rate-hz\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+                                  "atmel,max-sample-rate-hz",
+                                  &st->soc_info.max_sample_rate);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "invalid or missing value for atmel,max-sample-rate-hz\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
+                                  &st->soc_info.startup_time);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "invalid or missing value for atmel,startup-time-ms\n");
+               return ret;
+       }
+
+       init_waitqueue_head(&st->wq_data_available);
+       mutex_init(&st->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       st->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(st->base))
+               return PTR_ERR(st->base);
+
+       st->irq = platform_get_irq(pdev, 0);
+       if (st->irq <= 0) {
+               if (!st->irq)
+                       st->irq = -ENXIO;
+
+               return st->irq;
+       }
+
+       st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
+       if (IS_ERR(st->per_clk))
+               return PTR_ERR(st->per_clk);
+
+       st->reg = devm_regulator_get(&pdev->dev, "vddana");
+       if (IS_ERR(st->reg))
+               return PTR_ERR(st->reg);
+
+       st->vref = devm_regulator_get(&pdev->dev, "vref");
+       if (IS_ERR(st->vref))
+               return PTR_ERR(st->vref);
+
+       ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
+                              pdev->dev.driver->name, indio_dev);
+       if (ret)
+               return ret;
+
+       ret = regulator_enable(st->reg);
+       if (ret)
+               return ret;
+
+       ret = regulator_enable(st->vref);
+       if (ret)
+               goto reg_disable;
+
+       st->vref_uv = regulator_get_voltage(st->vref);
+       if (st->vref_uv <= 0) {
+               ret = -EINVAL;
+               goto vref_disable;
+       }
+
+       at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+       at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
+
+       at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+
+       ret = clk_prepare_enable(st->per_clk);
+       if (ret)
+               goto vref_disable;
+
+       ret = iio_device_register(indio_dev);
+       if (ret < 0)
+               goto per_clk_disable_unprepare;
+
+       dev_info(&pdev->dev, "version: %x\n",
+                readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
+
+       return 0;
+
+per_clk_disable_unprepare:
+       clk_disable_unprepare(st->per_clk);
+vref_disable:
+       regulator_disable(st->vref);
+reg_disable:
+       regulator_disable(st->reg);
+       return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct at91_adc_state *st = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+
+       clk_disable_unprepare(st->per_clk);
+
+       regulator_disable(st->vref);
+       regulator_disable(st->reg);
+
+       return 0;
+}
+
+static const struct of_device_id at91_adc_dt_match[] = {
+       {
+               .compatible = "atmel,sama5d2-adc",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
+
+static struct platform_driver at91_adc_driver = {
+       .probe = at91_adc_probe,
+       .remove = at91_adc_remove,
+       .driver = {
+               .name = "at91-sama5d2_adc",
+               .of_match_table = at91_adc_dt_match,
+       },
+};
+module_platform_driver(at91_adc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
+MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
+MODULE_LICENSE("GPL v2");
index 0c904edd6c004c00581ca6651ef65aa0c982e9cc..7fd24949c0c14fb53cb3d6f37a8d1d6afa1c0f0e 100644 (file)
@@ -46,7 +46,7 @@ struct axp288_adc_info {
        struct regmap *regmap;
 };
 
-static const struct iio_chan_spec const axp288_adc_channels[] = {
+static const struct iio_chan_spec axp288_adc_channels[] = {
        {
                .indexed = 1,
                .type = IIO_TEMP,
index d1c05f6eed18124a907ab4e04fd255b1677c280b..a850ca7d1edacd38ef572234ff06b4613d2a74bd 100644 (file)
@@ -187,26 +187,27 @@ out:
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
        }
 
-#define MCP320X_VOLTAGE_CHANNEL_DIFF(num)                      \
+#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2)             \
        {                                                       \
                .type = IIO_VOLTAGE,                            \
                .indexed = 1,                                   \
-               .channel = (num * 2),                           \
-               .channel2 = (num * 2 + 1),                      \
-               .address = (num * 2),                           \
+               .channel = (chan1),                             \
+               .channel2 = (chan2),                            \
+               .address = (chan1),                             \
                .differential = 1,                              \
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
        }
 
 static const struct iio_chan_spec mcp3201_channels[] = {
-       MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
 };
 
 static const struct iio_chan_spec mcp3202_channels[] = {
        MCP320X_VOLTAGE_CHANNEL(0),
        MCP320X_VOLTAGE_CHANNEL(1),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
 };
 
 static const struct iio_chan_spec mcp3204_channels[] = {
@@ -214,8 +215,10 @@ static const struct iio_chan_spec mcp3204_channels[] = {
        MCP320X_VOLTAGE_CHANNEL(1),
        MCP320X_VOLTAGE_CHANNEL(2),
        MCP320X_VOLTAGE_CHANNEL(3),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(0),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(1),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
 };
 
 static const struct iio_chan_spec mcp3208_channels[] = {
@@ -227,10 +230,14 @@ static const struct iio_chan_spec mcp3208_channels[] = {
        MCP320X_VOLTAGE_CHANNEL(5),
        MCP320X_VOLTAGE_CHANNEL(6),
        MCP320X_VOLTAGE_CHANNEL(7),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(0),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(1),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(2),
-       MCP320X_VOLTAGE_CHANNEL_DIFF(3),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7),
+       MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6),
 };
 
 static const struct iio_info mcp320x_info = {
index 6eca7aea8a37493e55b25a4c4ac9907782a81792..ebad83e3fbf7a4d35978dfd2c75bcb628154f51b 100644 (file)
@@ -1,11 +1,12 @@
 /*
- * mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family
+ * mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
  *
  * Copyright (C) 2013, Angelo Compagnucci
  * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
  *
  * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
  *            http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
+ *            http://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf
  *
  * This driver exports the value of analog input voltage to sysfs, the
  * voltage unit is nV.
@@ -357,6 +358,7 @@ static int mcp3422_probe(struct i2c_client *client,
 
        switch (adc->id) {
        case 1:
+       case 5:
                indio_dev->channels = mcp3421_channels;
                indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
                break;
@@ -395,6 +397,7 @@ static const struct i2c_device_id mcp3422_id[] = {
        { "mcp3422", 2 },
        { "mcp3423", 3 },
        { "mcp3424", 4 },
+       { "mcp3425", 5 },
        { "mcp3426", 6 },
        { "mcp3427", 7 },
        { "mcp3428", 8 },
@@ -421,5 +424,5 @@ static struct i2c_driver mcp3422_driver = {
 module_i2c_driver(mcp3422_driver);
 
 MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
-MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver");
+MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver");
 MODULE_LICENSE("GPL v2");
index f16de61be46dff17551b78de2192e5908d914b80..ce7cd1370f74a13d31fb8390f4d570938304a81b 100644 (file)
@@ -4,6 +4,19 @@
 
 menu "Chemical Sensors"
 
+config ATLAS_PH_SENSOR
+       tristate "Atlas Scientific OEM pH-SM sensor"
+       depends on I2C
+       select REGMAP_I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       help
+        Say Y here to build I2C interface support for the Atlas
+        Scientific OEM pH-SM sensor.
+
+        To compile this driver as module, choose M here: the
+        module will be called atlas-ph-sensor.
+
 config IAQCORE
        tristate "AMS iAQ-Core VOC sensors"
        depends on I2C
index 167861fadfab70e192c7643b8141b7973fe004a7..b02202b412890cef588908c37769a1fcf60f9c42 100644 (file)
@@ -3,5 +3,6 @@
 #
 
 # When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_ATLAS_PH_SENSOR)  += atlas-ph-sensor.o
 obj-$(CONFIG_IAQCORE)          += ams-iaq-core.o
 obj-$(CONFIG_VZ89X)            += vz89x.o
diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c
new file mode 100644 (file)
index 0000000..06cd49c
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM 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.
+ */
+
+#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/irq_work.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/pm_runtime.h>
+
+#define ATLAS_REGMAP_NAME      "atlas_ph_regmap"
+#define ATLAS_DRV_NAME         "atlas_ph"
+
+#define ATLAS_REG_DEV_TYPE             0x00
+#define ATLAS_REG_DEV_VERSION          0x01
+
+#define ATLAS_REG_INT_CONTROL          0x04
+#define ATLAS_REG_INT_CONTROL_EN       BIT(3)
+
+#define ATLAS_REG_PWR_CONTROL          0x06
+
+#define ATLAS_REG_CALIB_STATUS         0x0d
+#define ATLAS_REG_CALIB_STATUS_MASK    0x07
+#define ATLAS_REG_CALIB_STATUS_LOW     BIT(0)
+#define ATLAS_REG_CALIB_STATUS_MID     BIT(1)
+#define ATLAS_REG_CALIB_STATUS_HIGH    BIT(2)
+
+#define ATLAS_REG_TEMP_DATA            0x0e
+#define ATLAS_REG_PH_DATA              0x16
+
+#define ATLAS_PH_INT_TIME_IN_US                450000
+
+struct atlas_data {
+       struct i2c_client *client;
+       struct iio_trigger *trig;
+       struct regmap *regmap;
+       struct irq_work work;
+
+       __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */
+};
+
+static const struct regmap_range atlas_volatile_ranges[] = {
+       regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
+       regmap_reg_range(ATLAS_REG_CALIB_STATUS, ATLAS_REG_CALIB_STATUS),
+       regmap_reg_range(ATLAS_REG_TEMP_DATA, ATLAS_REG_TEMP_DATA + 4),
+       regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
+};
+
+static const struct regmap_access_table atlas_volatile_table = {
+       .yes_ranges     = atlas_volatile_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(atlas_volatile_ranges),
+};
+
+static const struct regmap_config atlas_regmap_config = {
+       .name = ATLAS_REGMAP_NAME,
+
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .volatile_table = &atlas_volatile_table,
+       .max_register = ATLAS_REG_PH_DATA + 4,
+       .cache_type = REGCACHE_FLAT,
+};
+
+static const struct iio_chan_spec atlas_channels[] = {
+       {
+               .type = IIO_PH,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 'u',
+                       .realbits = 32,
+                       .storagebits = 32,
+                       .endianness = IIO_BE,
+               },
+       },
+       IIO_CHAN_SOFT_TIMESTAMP(1),
+       {
+               .type = IIO_TEMP,
+               .address = ATLAS_REG_TEMP_DATA,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .output = 1,
+               .scan_index = -1
+       },
+};
+
+static int atlas_set_powermode(struct atlas_data *data, int on)
+{
+       return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on);
+}
+
+static int atlas_set_interrupt(struct atlas_data *data, bool state)
+{
+       return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL,
+                                 ATLAS_REG_INT_CONTROL_EN,
+                                 state ? ATLAS_REG_INT_CONTROL_EN : 0);
+}
+
+static int atlas_buffer_postenable(struct iio_dev *indio_dev)
+{
+       struct atlas_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = iio_triggered_buffer_postenable(indio_dev);
+       if (ret)
+               return ret;
+
+       ret = pm_runtime_get_sync(&data->client->dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(&data->client->dev);
+               return ret;
+       }
+
+       return atlas_set_interrupt(data, true);
+}
+
+static int atlas_buffer_predisable(struct iio_dev *indio_dev)
+{
+       struct atlas_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = iio_triggered_buffer_predisable(indio_dev);
+       if (ret)
+               return ret;
+
+       ret = atlas_set_interrupt(data, false);
+       if (ret)
+               return ret;
+
+       pm_runtime_mark_last_busy(&data->client->dev);
+       return pm_runtime_put_autosuspend(&data->client->dev);
+}
+
+static const struct iio_trigger_ops atlas_interrupt_trigger_ops = {
+       .owner = THIS_MODULE,
+};
+
+static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = {
+       .postenable = atlas_buffer_postenable,
+       .predisable = atlas_buffer_predisable,
+};
+
+static void atlas_work_handler(struct irq_work *work)
+{
+       struct atlas_data *data = container_of(work, struct atlas_data, work);
+
+       iio_trigger_poll(data->trig);
+}
+
+static irqreturn_t atlas_trigger_handler(int irq, void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct atlas_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = i2c_smbus_read_i2c_block_data(data->client, ATLAS_REG_PH_DATA,
+                               sizeof(data->buffer[0]), (u8 *) &data->buffer);
+
+       if (ret > 0)
+               iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                               iio_get_time_ns());
+
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t atlas_interrupt_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct atlas_data *data = iio_priv(indio_dev);
+
+       irq_work_queue(&data->work);
+
+       return IRQ_HANDLED;
+}
+
+static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val)
+{
+       struct device *dev = &data->client->dev;
+       int suspended = pm_runtime_suspended(dev);
+       int ret;
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(dev);
+               return ret;
+       }
+
+       if (suspended)
+               usleep_range(ATLAS_PH_INT_TIME_IN_US,
+                            ATLAS_PH_INT_TIME_IN_US + 100000);
+
+       ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
+                             (u8 *) val, sizeof(*val));
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return ret;
+}
+
+static int atlas_read_raw(struct iio_dev *indio_dev,
+                         struct iio_chan_spec const *chan,
+                         int *val, int *val2, long mask)
+{
+       struct atlas_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW: {
+               int ret;
+               __be32 reg;
+
+               switch (chan->type) {
+               case IIO_TEMP:
+                       ret = regmap_bulk_read(data->regmap, chan->address,
+                                             (u8 *) &reg, sizeof(reg));
+                       break;
+               case IIO_PH:
+                       mutex_lock(&indio_dev->mlock);
+
+                       if (iio_buffer_enabled(indio_dev))
+                               ret = -EBUSY;
+                       else
+                               ret = atlas_read_ph_measurement(data, &reg);
+
+                       mutex_unlock(&indio_dev->mlock);
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+
+               if (!ret) {
+                       *val = be32_to_cpu(reg);
+                       ret = IIO_VAL_INT;
+               }
+               return ret;
+       }
+       case IIO_CHAN_INFO_SCALE:
+               switch (chan->type) {
+               case IIO_TEMP:
+                       *val = 1; /* 0.01 */
+                       *val2 = 100;
+                       break;
+               case IIO_PH:
+                       *val = 1; /* 0.001 */
+                       *val2 = 1000;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               return IIO_VAL_FRACTIONAL;
+       }
+
+       return -EINVAL;
+}
+
+static int atlas_write_raw(struct iio_dev *indio_dev,
+                          struct iio_chan_spec const *chan,
+                          int val, int val2, long mask)
+{
+       struct atlas_data *data = iio_priv(indio_dev);
+       __be32 reg = cpu_to_be32(val);
+
+       if (val2 != 0 || val < 0 || val > 20000)
+               return -EINVAL;
+
+       if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP)
+               return -EINVAL;
+
+       return regmap_bulk_write(data->regmap, chan->address,
+                                &reg, sizeof(reg));
+}
+
+static const struct iio_info atlas_info = {
+       .driver_module = THIS_MODULE,
+       .read_raw = atlas_read_raw,
+       .write_raw = atlas_write_raw,
+};
+
+static int atlas_check_calibration(struct atlas_data *data)
+{
+       struct device *dev = &data->client->dev;
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val);
+       if (ret)
+               return ret;
+
+       if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) {
+               dev_warn(dev, "device has not been calibrated\n");
+               return 0;
+       }
+
+       if (!(val & ATLAS_REG_CALIB_STATUS_LOW))
+               dev_warn(dev, "device missing low point calibration\n");
+
+       if (!(val & ATLAS_REG_CALIB_STATUS_MID))
+               dev_warn(dev, "device missing mid point calibration\n");
+
+       if (!(val & ATLAS_REG_CALIB_STATUS_HIGH))
+               dev_warn(dev, "device missing high point calibration\n");
+
+       return 0;
+};
+
+static int atlas_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       struct atlas_data *data;
+       struct iio_trigger *trig;
+       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 = &atlas_info;
+       indio_dev->name = ATLAS_DRV_NAME;
+       indio_dev->channels = atlas_channels;
+       indio_dev->num_channels = ARRAY_SIZE(atlas_channels);
+       indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE;
+       indio_dev->dev.parent = &client->dev;
+
+       trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
+                                     indio_dev->name, indio_dev->id);
+
+       if (!trig)
+               return -ENOMEM;
+
+       data = iio_priv(indio_dev);
+       data->client = client;
+       data->trig = trig;
+       trig->dev.parent = indio_dev->dev.parent;
+       trig->ops = &atlas_interrupt_trigger_ops;
+       iio_trigger_set_drvdata(trig, indio_dev);
+
+       i2c_set_clientdata(client, indio_dev);
+
+       data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config);
+       if (IS_ERR(data->regmap)) {
+               dev_err(&client->dev, "regmap initialization failed\n");
+               return PTR_ERR(data->regmap);
+       }
+
+       ret = pm_runtime_set_active(&client->dev);
+       if (ret)
+               return ret;
+
+       if (client->irq <= 0) {
+               dev_err(&client->dev, "no valid irq defined\n");
+               return -EINVAL;
+       }
+
+       ret = atlas_check_calibration(data);
+       if (ret)
+               return ret;
+
+       ret = iio_trigger_register(trig);
+       if (ret) {
+               dev_err(&client->dev, "failed to register trigger\n");
+               return ret;
+       }
+
+       ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+               &atlas_trigger_handler, &atlas_buffer_setup_ops);
+       if (ret) {
+               dev_err(&client->dev, "cannot setup iio trigger\n");
+               goto unregister_trigger;
+       }
+
+       init_irq_work(&data->work, atlas_work_handler);
+
+       /* interrupt pin toggles on new conversion */
+       ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                       NULL, atlas_interrupt_handler,
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       "atlas_irq",
+                                       indio_dev);
+       if (ret) {
+               dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+               goto unregister_buffer;
+       }
+
+       ret = atlas_set_powermode(data, 1);
+       if (ret) {
+               dev_err(&client->dev, "cannot power device on");
+               goto unregister_buffer;
+       }
+
+       pm_runtime_enable(&client->dev);
+       pm_runtime_set_autosuspend_delay(&client->dev, 2500);
+       pm_runtime_use_autosuspend(&client->dev);
+
+       ret = iio_device_register(indio_dev);
+       if (ret) {
+               dev_err(&client->dev, "unable to register device\n");
+               goto unregister_pm;
+       }
+
+       return 0;
+
+unregister_pm:
+       pm_runtime_disable(&client->dev);
+       atlas_set_powermode(data, 0);
+
+unregister_buffer:
+       iio_triggered_buffer_cleanup(indio_dev);
+
+unregister_trigger:
+       iio_trigger_unregister(data->trig);
+
+       return ret;
+}
+
+static int atlas_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       struct atlas_data *data = iio_priv(indio_dev);
+
+       iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
+       iio_trigger_unregister(data->trig);
+
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_put_noidle(&client->dev);
+
+       return atlas_set_powermode(data, 0);
+}
+
+#ifdef CONFIG_PM
+static int atlas_runtime_suspend(struct device *dev)
+{
+       struct atlas_data *data =
+                    iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+       return atlas_set_powermode(data, 0);
+}
+
+static int atlas_runtime_resume(struct device *dev)
+{
+       struct atlas_data *data =
+                    iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+       return atlas_set_powermode(data, 1);
+}
+#endif
+
+static const struct dev_pm_ops atlas_pm_ops = {
+       SET_RUNTIME_PM_OPS(atlas_runtime_suspend,
+                          atlas_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id atlas_id[] = {
+       { "atlas-ph-sm", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, atlas_id);
+
+static const struct of_device_id atlas_dt_ids[] = {
+       { .compatible = "atlas,ph-sm" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, atlas_dt_ids);
+
+static struct i2c_driver atlas_driver = {
+       .driver = {
+               .name   = ATLAS_DRV_NAME,
+               .of_match_table = of_match_ptr(atlas_dt_ids),
+               .pm     = &atlas_pm_ops,
+       },
+       .probe          = atlas_probe,
+       .remove         = atlas_remove,
+       .id_table       = atlas_id,
+};
+module_i2c_driver(atlas_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
+MODULE_LICENSE("GPL");
index 8447c31e27f26c9c08d51752239ba4cb70706450..f5a2d445d0c0b619292ab379c739c3a20cb17f13 100644 (file)
 #include <asm/unaligned.h>
 #include <linux/iio/common/st_sensors.h>
 
+#include "st_sensors_core.h"
+
 static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
 {
        return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
 }
 
-static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
-                                               u8 reg_addr, u8 mask, u8 data)
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+                                   u8 reg_addr, u8 mask, u8 data)
 {
        int err;
        u8 new_data;
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.h b/drivers/iio/common/st_sensors/st_sensors_core.h
new file mode 100644 (file)
index 0000000..cd88098
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * Local functions in the ST Sensors core
+ */
+#ifndef __ST_SENSORS_CORE_H
+#define __ST_SENSORS_CORE_H
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+                                   u8 reg_addr, u8 mask, u8 data);
+#endif
index 3e907040c2c7a7712035376b019c462bd441ddb8..6a8c983279457d62ad0349988f92dc5ed12069a3 100644 (file)
 #include <linux/iio/iio.h>
 #include <linux/iio/trigger.h>
 #include <linux/interrupt.h>
-
 #include <linux/iio/common/st_sensors.h>
-
+#include "st_sensors_core.h"
 
 int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
                                const struct iio_trigger_ops *trigger_ops)
 {
-       int err;
+       int err, irq;
        struct st_sensor_data *sdata = iio_priv(indio_dev);
+       unsigned long irq_trig;
 
        sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
        if (sdata->trig == NULL) {
-               err = -ENOMEM;
                dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
-               goto iio_trigger_alloc_error;
+               return -ENOMEM;
        }
 
-       err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+       irq = sdata->get_irq_data_ready(indio_dev);
+       irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+       /*
+        * If the IRQ is triggered on falling edge, we need to mark the
+        * interrupt as active low, if the hardware supports this.
+        */
+       if (irq_trig == IRQF_TRIGGER_FALLING) {
+               if (!sdata->sensor_settings->drdy_irq.addr_ihl) {
+                       dev_err(&indio_dev->dev,
+                               "falling edge specified for IRQ but hardware "
+                               "only support rising edge, will request "
+                               "rising edge\n");
+                       irq_trig = IRQF_TRIGGER_RISING;
+               } else {
+                       /* Set up INT active low i.e. falling edge */
+                       err = st_sensors_write_data_with_mask(indio_dev,
+                               sdata->sensor_settings->drdy_irq.addr_ihl,
+                               sdata->sensor_settings->drdy_irq.mask_ihl, 1);
+                       if (err < 0)
+                               goto iio_trigger_free;
+                       dev_info(&indio_dev->dev,
+                                "interrupts on the falling edge\n");
+               }
+       } else if (irq_trig == IRQF_TRIGGER_RISING) {
+               dev_info(&indio_dev->dev,
+                        "interrupts on the rising edge\n");
+
+       } else {
+               dev_err(&indio_dev->dev,
+               "unsupported IRQ trigger specified (%lx), only "
+                       "rising and falling edges supported, enforce "
+                       "rising edge\n", irq_trig);
+               irq_trig = IRQF_TRIGGER_RISING;
+       }
+       err = request_threaded_irq(irq,
                        iio_trigger_generic_data_rdy_poll,
                        NULL,
-                       IRQF_TRIGGER_RISING,
+                       irq_trig,
                        sdata->trig->name,
                        sdata->trig);
        if (err) {
                dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
-               goto request_irq_error;
+               goto iio_trigger_free;
        }
 
        iio_trigger_set_drvdata(sdata->trig, indio_dev);
@@ -57,9 +90,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
 
 iio_trigger_register_error:
        free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
-request_irq_error:
+iio_trigger_free:
        iio_trigger_free(sdata->trig);
-iio_trigger_alloc_error:
        return err;
 }
 EXPORT_SYMBOL(st_sensors_allocate_trigger);
index e701e28fb1cd6009212bae9a19c4ac81ae8cb90f..5dc71505da61a2089d738637123cd410041c041c 100644 (file)
@@ -111,6 +111,16 @@ config AD5755
          To compile this driver as a module, choose M here: the
          module will be called ad5755.
 
+config AD5761
+       tristate "Analog Devices AD5761/61R/21/21R DAC driver"
+       depends on SPI_MASTER
+       help
+         Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721,
+         AD5721R Digital to Analog Converter.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad5761.
+
 config AD5764
        tristate "Analog Devices AD5764/64R/44/44R DAC driver"
        depends on SPI_MASTER
@@ -176,11 +186,11 @@ config MAX5821
          10 bits DAC.
 
 config MCP4725
-       tristate "MCP4725 DAC driver"
+       tristate "MCP4725/6 DAC driver"
        depends on I2C
        ---help---
          Say Y here if you want to build a driver for the Microchip
-         MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
+         MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C
          interface.
 
          To compile this driver as a module, choose M here: the module
index 63ae05633e0c956ed96f833a5afffd00fe500812..cb525b53fc7b7bee2bf3c022bd421ba2457e1078 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o
 obj-$(CONFIG_AD5446) += ad5446.o
 obj-$(CONFIG_AD5449) += ad5449.o
 obj-$(CONFIG_AD5755) += ad5755.o
+obj-$(CONFIG_AD5761) += ad5761.o
 obj-$(CONFIG_AD5764) += ad5764.o
 obj-$(CONFIG_AD5791) += ad5791.o
 obj-$(CONFIG_AD5686) += ad5686.o
diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c
new file mode 100644 (file)
index 0000000..d6510d6
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
+ *
+ * Copyright 2016 Qtechnology A/S
+ * 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_data/ad5761.h>
+
+#define AD5761_ADDR(addr)              ((addr & 0xf) << 16)
+#define AD5761_ADDR_NOOP               0x0
+#define AD5761_ADDR_DAC_WRITE          0x3
+#define AD5761_ADDR_CTRL_WRITE_REG     0x4
+#define AD5761_ADDR_SW_DATA_RESET      0x7
+#define AD5761_ADDR_DAC_READ           0xb
+#define AD5761_ADDR_CTRL_READ_REG      0xc
+#define AD5761_ADDR_SW_FULL_RESET      0xf
+
+#define AD5761_CTRL_USE_INTVREF                BIT(5)
+#define AD5761_CTRL_ETS                        BIT(6)
+
+/**
+ * struct ad5761_chip_info - chip specific information
+ * @int_vref:  Value of the internal reference voltage in mV - 0 if external
+ *             reference voltage is used
+ * @channel:   channel specification
+*/
+
+struct ad5761_chip_info {
+       unsigned long int_vref;
+       const struct iio_chan_spec channel;
+};
+
+struct ad5761_range_params {
+       int m;
+       int c;
+};
+
+enum ad5761_supported_device_ids {
+       ID_AD5721,
+       ID_AD5721R,
+       ID_AD5761,
+       ID_AD5761R,
+};
+
+/**
+ * struct ad5761_state - driver instance specific data
+ * @spi:               spi_device
+ * @vref_reg:          reference voltage regulator
+ * @use_intref:                true when the internal voltage reference is used
+ * @vref:              actual voltage reference in mVolts
+ * @range:             output range mode used
+ * @data:              cache aligned spi buffer
+ */
+struct ad5761_state {
+       struct spi_device               *spi;
+       struct regulator                *vref_reg;
+
+       bool use_intref;
+       int vref;
+       enum ad5761_voltage_range range;
+
+       /*
+        * DMA (thus cache coherency maintenance) requires the
+        * transfer buffers to live in their own cache lines.
+        */
+       union {
+               __be32 d32;
+               u8 d8[4];
+       } data[3] ____cacheline_aligned;
+};
+
+static const struct ad5761_range_params ad5761_range_params[] = {
+       [AD5761_VOLTAGE_RANGE_M10V_10V] = {
+               .m = 80,
+               .c = 40,
+       },
+       [AD5761_VOLTAGE_RANGE_0V_10V] = {
+               .m = 40,
+               .c = 0,
+       },
+       [AD5761_VOLTAGE_RANGE_M5V_5V] = {
+               .m = 40,
+               .c = 20,
+       },
+       [AD5761_VOLTAGE_RANGE_0V_5V] = {
+               .m = 20,
+               .c = 0,
+       },
+       [AD5761_VOLTAGE_RANGE_M2V5_7V5] = {
+               .m = 40,
+               .c = 10,
+       },
+       [AD5761_VOLTAGE_RANGE_M3V_3V] = {
+               .m = 24,
+               .c = 12,
+       },
+       [AD5761_VOLTAGE_RANGE_0V_16V] = {
+               .m = 64,
+               .c = 0,
+       },
+       [AD5761_VOLTAGE_RANGE_0V_20V] = {
+               .m = 80,
+               .c = 0,
+       },
+};
+
+static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val)
+{
+       st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val);
+
+       return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val)
+{
+       struct ad5761_state *st = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&indio_dev->mlock);
+       ret = _ad5761_spi_write(st, addr, val);
+       mutex_unlock(&indio_dev->mlock);
+
+       return ret;
+}
+
+static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val)
+{
+       int ret;
+       struct spi_transfer xfers[] = {
+               {
+                       .tx_buf = &st->data[0].d8[1],
+                       .bits_per_word = 8,
+                       .len = 3,
+                       .cs_change = true,
+               }, {
+                       .tx_buf = &st->data[1].d8[1],
+                       .rx_buf = &st->data[2].d8[1],
+                       .bits_per_word = 8,
+                       .len = 3,
+               },
+       };
+
+       st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr));
+       st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP));
+
+       ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+
+       *val = be32_to_cpu(st->data[2].d32);
+
+       return ret;
+}
+
+static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val)
+{
+       struct ad5761_state *st = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&indio_dev->mlock);
+       ret = _ad5761_spi_read(st, addr, val);
+       mutex_unlock(&indio_dev->mlock);
+
+       return ret;
+}
+
+static int ad5761_spi_set_range(struct ad5761_state *st,
+                               enum ad5761_voltage_range range)
+{
+       u16 aux;
+       int ret;
+
+       aux = (range & 0x7) | AD5761_CTRL_ETS;
+
+       if (st->use_intref)
+               aux |= AD5761_CTRL_USE_INTVREF;
+
+       ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0);
+       if (ret)
+               return ret;
+
+       ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux);
+       if (ret)
+               return ret;
+
+       st->range = range;
+
+       return 0;
+}
+
+static int ad5761_read_raw(struct iio_dev *indio_dev,
+                          struct iio_chan_spec const *chan,
+                          int *val,
+                          int *val2,
+                          long mask)
+{
+       struct ad5761_state *st;
+       int ret;
+       u16 aux;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux);
+               if (ret)
+                       return ret;
+               *val = aux >> chan->scan_type.shift;
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               st = iio_priv(indio_dev);
+               *val = st->vref * ad5761_range_params[st->range].m;
+               *val /= 10;
+               *val2 = chan->scan_type.realbits;
+               return IIO_VAL_FRACTIONAL_LOG2;
+       case IIO_CHAN_INFO_OFFSET:
+               st = iio_priv(indio_dev);
+               *val = -(1 << chan->scan_type.realbits);
+               *val *= ad5761_range_params[st->range].c;
+               *val /= ad5761_range_params[st->range].m;
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int ad5761_write_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int val,
+                           int val2,
+                           long mask)
+{
+       u16 aux;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0)
+               return -EINVAL;
+
+       aux = val << chan->scan_type.shift;
+
+       return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux);
+}
+
+static const struct iio_info ad5761_info = {
+       .read_raw = &ad5761_read_raw,
+       .write_raw = &ad5761_write_raw,
+       .driver_module = THIS_MODULE,
+};
+
+#define AD5761_CHAN(_bits) {                           \
+       .type = IIO_VOLTAGE,                            \
+       .output = 1,                                    \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
+               BIT(IIO_CHAN_INFO_OFFSET),              \
+       .scan_type = {                                  \
+               .sign = 'u',                            \
+               .realbits = (_bits),                    \
+               .storagebits = 16,                      \
+               .shift = 16 - (_bits),                  \
+       },                                              \
+}
+
+static const struct ad5761_chip_info ad5761_chip_infos[] = {
+       [ID_AD5721] = {
+               .int_vref = 0,
+               .channel = AD5761_CHAN(12),
+       },
+       [ID_AD5721R] = {
+               .int_vref = 2500,
+               .channel = AD5761_CHAN(12),
+       },
+       [ID_AD5761] = {
+               .int_vref = 0,
+               .channel = AD5761_CHAN(16),
+       },
+       [ID_AD5761R] = {
+               .int_vref = 2500,
+               .channel = AD5761_CHAN(16),
+       },
+};
+
+static int ad5761_get_vref(struct ad5761_state *st,
+                          const struct ad5761_chip_info *chip_info)
+{
+       int ret;
+
+       st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref");
+       if (PTR_ERR(st->vref_reg) == -ENODEV) {
+               /* Use Internal regulator */
+               if (!chip_info->int_vref) {
+                       dev_err(&st->spi->dev,
+                               "Voltage reference not found\n");
+                       return -EIO;
+               }
+
+               st->use_intref = true;
+               st->vref = chip_info->int_vref;
+               return 0;
+       }
+
+       if (IS_ERR(st->vref_reg)) {
+               dev_err(&st->spi->dev,
+                       "Error getting voltage reference regulator\n");
+               return PTR_ERR(st->vref_reg);
+       }
+
+       ret = regulator_enable(st->vref_reg);
+       if (ret) {
+               dev_err(&st->spi->dev,
+                        "Failed to enable voltage reference\n");
+               return ret;
+       }
+
+       ret = regulator_get_voltage(st->vref_reg);
+       if (ret < 0) {
+               dev_err(&st->spi->dev,
+                        "Failed to get voltage reference value\n");
+               goto disable_regulator_vref;
+       }
+
+       if (ret < 2000000 || ret > 3000000) {
+               dev_warn(&st->spi->dev,
+                        "Invalid external voltage ref. value %d uV\n", ret);
+               ret = -EIO;
+               goto disable_regulator_vref;
+       }
+
+       st->vref = ret / 1000;
+       st->use_intref = false;
+
+       return 0;
+
+disable_regulator_vref:
+       regulator_disable(st->vref_reg);
+       st->vref_reg = NULL;
+       return ret;
+}
+
+static int ad5761_probe(struct spi_device *spi)
+{
+       struct iio_dev *iio_dev;
+       struct ad5761_state *st;
+       int ret;
+       const struct ad5761_chip_info *chip_info =
+               &ad5761_chip_infos[spi_get_device_id(spi)->driver_data];
+       enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V;
+       struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev);
+
+       iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+       if (!iio_dev)
+               return -ENOMEM;
+
+       st = iio_priv(iio_dev);
+
+       st->spi = spi;
+       spi_set_drvdata(spi, iio_dev);
+
+       ret = ad5761_get_vref(st, chip_info);
+       if (ret)
+               return ret;
+
+       if (pdata)
+               voltage_range = pdata->voltage_range;
+
+       ret = ad5761_spi_set_range(st, voltage_range);
+       if (ret)
+               goto disable_regulator_err;
+
+       iio_dev->dev.parent = &spi->dev;
+       iio_dev->info = &ad5761_info;
+       iio_dev->modes = INDIO_DIRECT_MODE;
+       iio_dev->channels = &chip_info->channel;
+       iio_dev->num_channels = 1;
+       iio_dev->name = spi_get_device_id(st->spi)->name;
+       ret = iio_device_register(iio_dev);
+       if (ret)
+               goto disable_regulator_err;
+
+       return 0;
+
+disable_regulator_err:
+       if (!IS_ERR_OR_NULL(st->vref_reg))
+               regulator_disable(st->vref_reg);
+
+       return ret;
+}
+
+static int ad5761_remove(struct spi_device *spi)
+{
+       struct iio_dev *iio_dev = spi_get_drvdata(spi);
+       struct ad5761_state *st = iio_priv(iio_dev);
+
+       iio_device_unregister(iio_dev);
+
+       if (!IS_ERR_OR_NULL(st->vref_reg))
+               regulator_disable(st->vref_reg);
+
+       return 0;
+}
+
+static const struct spi_device_id ad5761_id[] = {
+       {"ad5721", ID_AD5721},
+       {"ad5721r", ID_AD5721R},
+       {"ad5761", ID_AD5761},
+       {"ad5761r", ID_AD5761R},
+       {}
+};
+MODULE_DEVICE_TABLE(spi, ad5761_id);
+
+static struct spi_driver ad5761_driver = {
+       .driver = {
+                  .name = "ad5761",
+                  },
+       .probe = ad5761_probe,
+       .remove = ad5761_remove,
+       .id_table = ad5761_id,
+};
+module_spi_driver(ad5761_driver);
+
+MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
+MODULE_LICENSE("GPL v2");
index 43d14588448d65f507ab02ce22c0e01d7a9008ad..fb4b3364d8e09da93e945905c9bd4ff5e723f980 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * mcp4725.c - Support for Microchip MCP4725
+ * mcp4725.c - Support for Microchip MCP4725/6
  *
  * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
  *
@@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = {
        "500kohm_to_gnd"
 };
 
+static const char * const mcp4726_powerdown_modes[] = {
+       "1kohm_to_gnd",
+       "125kohm_to_gnd",
+       "640kohm_to_gnd"
+};
+
 static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
        const struct iio_chan_spec *chan)
 {
@@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
        return len;
 }
 
-static const struct iio_enum mcp4725_powerdown_mode_enum = {
-       .items = mcp4725_powerdown_modes,
-       .num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
-       .get = mcp4725_get_powerdown_mode,
-       .set = mcp4725_set_powerdown_mode,
+enum {
+       MCP4725,
+       MCP4726,
+};
+
+static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
+       [MCP4725] = {
+               .items = mcp4725_powerdown_modes,
+               .num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
+               .get = mcp4725_get_powerdown_mode,
+               .set = mcp4725_set_powerdown_mode,
+       },
+       [MCP4726] = {
+               .items = mcp4726_powerdown_modes,
+               .num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
+               .get = mcp4725_get_powerdown_mode,
+               .set = mcp4725_set_powerdown_mode,
+       },
 };
 
 static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
@@ -196,19 +215,46 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
                .write = mcp4725_write_powerdown,
                .shared = IIO_SEPARATE,
        },
-       IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
-       IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
+       IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+                       &mcp472x_powerdown_mode_enum[MCP4725]),
+       IIO_ENUM_AVAILABLE("powerdown_mode",
+                       &mcp472x_powerdown_mode_enum[MCP4725]),
+       { },
+};
+
+static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
+       {
+               .name = "powerdown",
+               .read = mcp4725_read_powerdown,
+               .write = mcp4725_write_powerdown,
+               .shared = IIO_SEPARATE,
+       },
+       IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+                       &mcp472x_powerdown_mode_enum[MCP4726]),
+       IIO_ENUM_AVAILABLE("powerdown_mode",
+                       &mcp472x_powerdown_mode_enum[MCP4726]),
        { },
 };
 
-static const struct iio_chan_spec mcp4725_channel = {
-       .type           = IIO_VOLTAGE,
-       .indexed        = 1,
-       .output         = 1,
-       .channel        = 0,
-       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
-       .ext_info       = mcp4725_ext_info,
+static const struct iio_chan_spec mcp472x_channel[] = {
+       [MCP4725] = {
+               .type           = IIO_VOLTAGE,
+               .indexed        = 1,
+               .output         = 1,
+               .channel        = 0,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .ext_info       = mcp4725_ext_info,
+       },
+       [MCP4726] = {
+               .type           = IIO_VOLTAGE,
+               .indexed        = 1,
+               .output         = 1,
+               .channel        = 0,
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .ext_info       = mcp4726_ext_info,
+       },
 };
 
 static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
@@ -301,7 +347,7 @@ static int mcp4725_probe(struct i2c_client *client,
 
        indio_dev->dev.parent = &client->dev;
        indio_dev->info = &mcp4725_info;
-       indio_dev->channels = &mcp4725_channel;
+       indio_dev->channels = &mcp472x_channel[id->driver_data];
        indio_dev->num_channels = 1;
        indio_dev->modes = INDIO_DIRECT_MODE;
 
@@ -315,7 +361,7 @@ static int mcp4725_probe(struct i2c_client *client,
        }
        pd = (inbuf[0] >> 1) & 0x3;
        data->powerdown = pd > 0 ? true : false;
-       data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
+       data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
        data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
 
        return iio_device_register(indio_dev);
@@ -328,7 +374,8 @@ static int mcp4725_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id mcp4725_id[] = {
-       { "mcp4725", 0 },
+       { "mcp4725", MCP4725 },
+       { "mcp4726", MCP4726 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, mcp4725_id);
@@ -345,5 +392,5 @@ static struct i2c_driver mcp4725_driver = {
 module_i2c_driver(mcp4725_driver);
 
 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
-MODULE_DESCRIPTION("MCP4725 12-bit DAC");
+MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
 MODULE_LICENSE("GPL");
index 02eddcebeea3e97fd38c5a9bfa60e427bd9f1ac9..110f95b6e52fa1a085523ba438b470be861edb35 100644 (file)
@@ -185,6 +185,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
                .drdy_irq = {
                        .addr = ST_GYRO_1_DRDY_IRQ_ADDR,
                        .mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK,
+                       /*
+                        * The sensor has IHL (active low) and open
+                        * drain settings, but only for INT1 and not
+                        * for the DRDY line on INT2.
+                        */
                },
                .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
                .bootime = 2,
@@ -248,6 +253,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
                .drdy_irq = {
                        .addr = ST_GYRO_2_DRDY_IRQ_ADDR,
                        .mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK,
+                       /*
+                        * The sensor has IHL (active low) and open
+                        * drain settings, but only for INT1 and not
+                        * for the DRDY line on INT2.
+                        */
                },
                .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
                .bootime = 2,
@@ -307,6 +317,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
                .drdy_irq = {
                        .addr = ST_GYRO_3_DRDY_IRQ_ADDR,
                        .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
+                       /*
+                        * The sensor has IHL (active low) and open
+                        * drain settings, but only for INT1 and not
+                        * for the DRDY line on INT2.
+                        */
                },
                .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
                .bootime = 2,
index 9d1c81f91dd771826638aaa8eb78824daf7a4aa3..09db89359544840b5f9ba7b98a8b81a046388459 100644 (file)
@@ -13,7 +13,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  * GNU General Public License for more details.
  *
- * TODO: allow LED current and pulse length controls via device tree properties
+ * TODO: enable pulse length controls via device tree properties
  */
 
 #include <linux/module.h>
@@ -24,6 +24,7 @@
 #include <linux/irq.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
@@ -65,6 +66,7 @@
 #define MAX30100_REG_SPO2_CONFIG_1600US                0x3
 
 #define MAX30100_REG_LED_CONFIG                        0x09
+#define MAX30100_REG_LED_CONFIG_LED_MASK       0x0f
 #define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT  4
 
 #define MAX30100_REG_LED_CONFIG_24MA           0x07
@@ -111,6 +113,12 @@ static const struct regmap_config max30100_regmap_config = {
        .volatile_reg = max30100_is_volatile_reg,
 };
 
+static const unsigned int max30100_led_current_mapping[] = {
+       4400, 7600, 11000, 14200, 17400,
+       20800, 24000, 27100, 30600, 33800,
+       37000, 40200, 43600, 46800, 50000
+};
+
 static const unsigned long max30100_scan_masks[] = {0x3, 0};
 
 static const struct iio_chan_spec max30100_channels[] = {
@@ -243,15 +251,76 @@ static irqreturn_t max30100_interrupt_handler(int irq, void *private)
        return IRQ_HANDLED;
 }
 
+static int max30100_get_current_idx(unsigned int val, int *reg)
+{
+       int idx;
+
+       /* LED turned off */
+       if (val == 0) {
+               *reg = 0;
+               return 0;
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(max30100_led_current_mapping); idx++) {
+               if (max30100_led_current_mapping[idx] == val) {
+                       *reg = idx + 1;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int max30100_led_init(struct max30100_data *data)
+{
+       struct device *dev = &data->client->dev;
+       struct device_node *np = dev->of_node;
+       unsigned int val[2];
+       int reg, ret;
+
+       ret = of_property_read_u32_array(np, "maxim,led-current-microamp",
+                                       (unsigned int *) &val, 2);
+       if (ret) {
+               /* Default to 24 mA RED LED, 50 mA IR LED */
+               reg = (MAX30100_REG_LED_CONFIG_24MA <<
+                       MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
+                       MAX30100_REG_LED_CONFIG_50MA;
+               dev_warn(dev, "no led-current-microamp set");
+
+               return regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, reg);
+       }
+
+       /* RED LED current */
+       ret = max30100_get_current_idx(val[0], &reg);
+       if (ret) {
+               dev_err(dev, "invalid RED current setting %d", val[0]);
+               return ret;
+       }
+
+       ret = regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
+               MAX30100_REG_LED_CONFIG_LED_MASK <<
+               MAX30100_REG_LED_CONFIG_RED_LED_SHIFT,
+               reg << MAX30100_REG_LED_CONFIG_RED_LED_SHIFT);
+       if (ret)
+               return ret;
+
+       /* IR LED current */
+       ret = max30100_get_current_idx(val[1], &reg);
+       if (ret) {
+               dev_err(dev, "invalid IR current setting %d", val[1]);
+               return ret;
+       }
+
+       return regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
+               MAX30100_REG_LED_CONFIG_LED_MASK, reg);
+}
+
 static int max30100_chip_init(struct max30100_data *data)
 {
        int ret;
 
-       /* RED IR LED = 24mA, IR LED = 50mA */
-       ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
-                               (MAX30100_REG_LED_CONFIG_24MA <<
-                                MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
-                                MAX30100_REG_LED_CONFIG_50MA);
+       /* setup LED current settings */
+       ret = max30100_led_init(data);
        if (ret)
                return ret;
 
index 6a23698d347c24c8e2a776ad09d42a0792d07309..866dda133336c07b43f58750c038e82952564f37 100644 (file)
@@ -43,14 +43,16 @@ config SI7005
          humidity and temperature sensor.
 
          To compile this driver as a module, choose M here: the module
-         will be called si7005.
+         will be called si7005. This driver also
+         supports Hoperf TH02 Humidity and Temperature Sensor.
 
 config SI7020
        tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
        depends on I2C
        help
          Say yes here to build support for the Silicon Labs Si7013/20/21
-         Relative Humidity and Temperature Sensors.
+         Relative Humidity and Temperature Sensors. This driver also
+         supports Hoperf TH06 Humidity and Temperature Sensor.
 
          To compile this driver as a module, choose M here: the module
          will be called si7020.
index 1165b1c4f9d67bd93db24f784da5b13010d9fd99..96185f8fad88fab9be3989c21e04aca2c80e3c79 100644 (file)
 #define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
                              DHT11_EDGES_PREAMBLE + 1)
 
-/* Data transmission timing (nano seconds) */
+/*
+ * Data transmission timing:
+ * Data bits are encoded as pulse length (high time) on the data line.
+ * 0-bit: 22-30uS -- typically 26uS (AM2302)
+ * 1-bit: 68-75uS -- typically 70uS (AM2302)
+ * The acutal timings also depend on the properties of the cable, with
+ * longer cables typically making pulses shorter.
+ *
+ * Our decoding depends on the time resolution of the system:
+ * timeres > 34uS ... don't know what a 1-tick pulse is
+ * 34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks)
+ * 30uS > timeres > 23uS ... don't know what a 2-tick pulse is
+ * timeres < 23uS ... no problem
+ *
+ * Luckily clocks in the 33-44kHz range are quite uncommon, so we can
+ * support most systems if the threshold for decoding a pulse as 1-bit
+ * is chosen carefully. If somebody really wants to support clocks around
+ * 40kHz, where this driver is most unreliable, there are two options.
+ * a) select an implementation using busy loop polling on those systems
+ * b) use the checksum to do some probabilistic decoding
+ */
 #define DHT11_START_TRANSMISSION       18  /* ms */
-#define DHT11_SENSOR_RESPONSE  80000
-#define DHT11_START_BIT                50000
-#define DHT11_DATA_BIT_LOW     27000
-#define DHT11_DATA_BIT_HIGH    70000
+#define DHT11_MIN_TIMERES      34000  /* ns */
+#define DHT11_THRESHOLD                49000  /* ns */
+#define DHT11_AMBIG_LOW                23000  /* ns */
+#define DHT11_AMBIG_HIGH       30000  /* ns */
 
 struct dht11 {
        struct device                   *dev;
@@ -76,43 +96,39 @@ struct dht11 {
        struct {s64 ts; int value; }    edges[DHT11_EDGES_PER_READ];
 };
 
-static unsigned char dht11_decode_byte(int *timing, int threshold)
+static unsigned char dht11_decode_byte(char *bits)
 {
        unsigned char ret = 0;
        int i;
 
        for (i = 0; i < 8; ++i) {
                ret <<= 1;
-               if (timing[i] >= threshold)
+               if (bits[i])
                        ++ret;
        }
 
        return ret;
 }
 
-static int dht11_decode(struct dht11 *dht11, int offset, int timeres)
+static int dht11_decode(struct dht11 *dht11, int offset)
 {
-       int i, t, timing[DHT11_BITS_PER_READ], threshold;
+       int i, t;
+       char bits[DHT11_BITS_PER_READ];
        unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
 
-       threshold = DHT11_DATA_BIT_HIGH / timeres;
-       if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold)
-               pr_err("dht11: WARNING: decoding ambiguous\n");
-
-       /* scale down with timeres and check validity */
        for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
                t = dht11->edges[offset + 2 * i + 2].ts -
                        dht11->edges[offset + 2 * i + 1].ts;
                if (!dht11->edges[offset + 2 * i + 1].value)
                        return -EIO;  /* lost synchronisation */
-               timing[i] = t / timeres;
+               bits[i] = t > DHT11_THRESHOLD;
        }
 
-       hum_int = dht11_decode_byte(timing, threshold);
-       hum_dec = dht11_decode_byte(&timing[8], threshold);
-       temp_int = dht11_decode_byte(&timing[16], threshold);
-       temp_dec = dht11_decode_byte(&timing[24], threshold);
-       checksum = dht11_decode_byte(&timing[32], threshold);
+       hum_int = dht11_decode_byte(bits);
+       hum_dec = dht11_decode_byte(&bits[8]);
+       temp_int = dht11_decode_byte(&bits[16]);
+       temp_dec = dht11_decode_byte(&bits[24]);
+       checksum = dht11_decode_byte(&bits[32]);
 
        if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
                return -EIO;
@@ -161,12 +177,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
                        int *val, int *val2, long m)
 {
        struct dht11 *dht11 = iio_priv(iio_dev);
-       int ret, timeres;
+       int ret, timeres, offset;
 
        mutex_lock(&dht11->lock);
        if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) {
                timeres = ktime_get_resolution_ns();
-               if (DHT11_DATA_BIT_HIGH < 2 * timeres) {
+               if (timeres > DHT11_MIN_TIMERES) {
                        dev_err(dht11->dev, "timeresolution %dns too low\n",
                                timeres);
                        /* In theory a better clock could become available
@@ -176,6 +192,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
                        ret = -EAGAIN;
                        goto err;
                }
+               if (timeres > DHT11_AMBIG_LOW && timeres < DHT11_AMBIG_HIGH)
+                       dev_warn(dht11->dev,
+                                "timeresolution: %dns - decoding ambiguous\n",
+                                timeres);
 
                reinit_completion(&dht11->completion);
 
@@ -208,11 +228,14 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
                if (ret < 0)
                        goto err;
 
-               ret = dht11_decode(dht11,
-                                  dht11->num_edges == DHT11_EDGES_PER_READ ?
-                                       DHT11_EDGES_PREAMBLE :
-                                       DHT11_EDGES_PREAMBLE - 2,
-                               timeres);
+               offset = DHT11_EDGES_PREAMBLE +
+                               dht11->num_edges - DHT11_EDGES_PER_READ;
+               for (; offset >= 0; --offset) {
+                       ret = dht11_decode(dht11, offset);
+                       if (!ret)
+                               break;
+               }
+
                if (ret)
                        goto err;
        }
index 91972ccd8aafb8d151953f344ba54933be79da3a..98a022fa26ad0c81999881326f879a1009c03f53 100644 (file)
@@ -170,6 +170,7 @@ static int si7005_probe(struct i2c_client *client,
 
 static const struct i2c_device_id si7005_id[] = {
        { "si7005", 0 },
+       { "th02", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, si7005_id);
index 71991b5c0658d05fe728f797573b08843e51d9ad..5ab4e06fb5449c9a9af97c2c0994c7906db63a03 100644 (file)
@@ -149,6 +149,7 @@ static int si7020_probe(struct i2c_client *client,
 
 static const struct i2c_device_id si7020_id[] = {
        { "si7020", 0 },
+       { "th06", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, si7020_id);
index f0e06093b5e8db3ce4f9fe89f983a01efba316a0..0852b7fa777e91924642221fc5488a722bb33048 100644 (file)
@@ -727,8 +727,7 @@ static const struct iio_info mpu_info = {
 /**
  *  inv_check_and_setup_chip() - check and setup chip.
  */
-static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
-               const struct i2c_device_id *id)
+static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
 {
        int result;
 
@@ -795,7 +794,7 @@ static int inv_mpu_probe(struct i2c_client *client,
        if (pdata)
                st->plat_data = *pdata;
        /* power is turned on inside check chip type*/
-       result = inv_check_and_setup_chip(st, id);
+       result = inv_check_and_setup_chip(st);
        if (result)
                return result;
 
index af7cc1e65656e1bf0ac83f561945bbbcf45d9de9..70cb7eb0a75cacc43c8dcdce72fc0ecf74af5398 100644 (file)
@@ -77,6 +77,7 @@ static const char * const iio_chan_type_name_spec[] = {
        [IIO_VELOCITY] = "velocity",
        [IIO_CONCENTRATION] = "concentration",
        [IIO_RESISTANCE] = "resistance",
+       [IIO_PH] = "ph",
 };
 
 static const char * const iio_modifier_names[] = {
index 01e111e72d4bb273c344b409119a134cd2a73a98..b776c8ed4387dc289eef2b5810b4217346c8f10f 100644 (file)
 #define OPT3001_REG_EXPONENT(n)                ((n) >> 12)
 #define OPT3001_REG_MANTISSA(n)                ((n) & 0xfff)
 
+#define OPT3001_INT_TIME_LONG          800000
+#define OPT3001_INT_TIME_SHORT         100000
+
 /*
  * Time to wait for conversion result to be ready. The device datasheet
- * worst-case max value is 880ms. Add some slack to be on the safe side.
+ * sect. 6.5 states results are ready after total integration time plus 3ms.
+ * This results in worst-case max values of 113ms or 883ms, respectively.
+ * Add some slack to be on the safe side.
  */
-#define OPT3001_RESULT_READY_TIMEOUT   msecs_to_jiffies(1000)
+#define OPT3001_RESULT_READY_SHORT     150
+#define OPT3001_RESULT_READY_LONG      1000
 
 struct opt3001 {
        struct i2c_client       *client;
        struct device           *dev;
 
        struct mutex            lock;
-       u16                     ok_to_ignore_lock:1;
-       u16                     result_ready:1;
+       bool                    ok_to_ignore_lock;
+       bool                    result_ready;
        wait_queue_head_t       result_ready_queue;
        u16                     result;
 
@@ -89,6 +95,8 @@ struct opt3001 {
 
        u8                      high_thresh_exp;
        u8                      low_thresh_exp;
+
+       bool                    use_irq;
 };
 
 struct opt3001_scale {
@@ -227,26 +235,30 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
        u16 reg;
        u8 exponent;
        u16 value;
+       long timeout;
 
-       /*
-        * Enable the end-of-conversion interrupt mechanism. Note that doing
-        * so will overwrite the low-level limit value however we will restore
-        * this value later on.
-        */
-       ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
-                       OPT3001_LOW_LIMIT_EOC_ENABLE);
-       if (ret < 0) {
-               dev_err(opt->dev, "failed to write register %02x\n",
-                               OPT3001_LOW_LIMIT);
-               return ret;
+       if (opt->use_irq) {
+               /*
+                * Enable the end-of-conversion interrupt mechanism. Note that
+                * doing so will overwrite the low-level limit value however we
+                * will restore this value later on.
+                */
+               ret = i2c_smbus_write_word_swapped(opt->client,
+                                       OPT3001_LOW_LIMIT,
+                                       OPT3001_LOW_LIMIT_EOC_ENABLE);
+               if (ret < 0) {
+                       dev_err(opt->dev, "failed to write register %02x\n",
+                                       OPT3001_LOW_LIMIT);
+                       return ret;
+               }
+
+               /* Allow IRQ to access the device despite lock being set */
+               opt->ok_to_ignore_lock = true;
        }
 
-       /* Reset data-ready indicator flag (will be set in the IRQ routine) */
+       /* Reset data-ready indicator flag */
        opt->result_ready = false;
 
-       /* Allow IRQ to access the device despite lock being set */
-       opt->ok_to_ignore_lock = true;
-
        /* Configure for single-conversion mode and start a new conversion */
        ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
        if (ret < 0) {
@@ -266,32 +278,69 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
                goto err;
        }
 
-       /* Wait for the IRQ to indicate the conversion is complete */
-       ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready,
-                       OPT3001_RESULT_READY_TIMEOUT);
+       if (opt->use_irq) {
+               /* Wait for the IRQ to indicate the conversion is complete */
+               ret = wait_event_timeout(opt->result_ready_queue,
+                               opt->result_ready,
+                               msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
+       } else {
+               /* Sleep for result ready time */
+               timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
+                       OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG;
+               msleep(timeout);
+
+               /* Check result ready flag */
+               ret = i2c_smbus_read_word_swapped(opt->client,
+                                                 OPT3001_CONFIGURATION);
+               if (ret < 0) {
+                       dev_err(opt->dev, "failed to read register %02x\n",
+                               OPT3001_CONFIGURATION);
+                       goto err;
+               }
+
+               if (!(ret & OPT3001_CONFIGURATION_CRF)) {
+                       ret = -ETIMEDOUT;
+                       goto err;
+               }
+
+               /* Obtain value */
+               ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
+               if (ret < 0) {
+                       dev_err(opt->dev, "failed to read register %02x\n",
+                               OPT3001_RESULT);
+                       goto err;
+               }
+               opt->result = ret;
+               opt->result_ready = true;
+       }
 
 err:
-       /* Disallow IRQ to access the device while lock is active */
-       opt->ok_to_ignore_lock = false;
+       if (opt->use_irq)
+               /* Disallow IRQ to access the device while lock is active */
+               opt->ok_to_ignore_lock = false;
 
        if (ret == 0)
                return -ETIMEDOUT;
        else if (ret < 0)
                return ret;
 
-       /*
-        * Disable the end-of-conversion interrupt mechanism by restoring the
-        * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note
-        * that selectively clearing those enable bits would affect the actual
-        * limit value due to bit-overlap and therefore can't be done.
-        */
-       value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
-       ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
-                       value);
-       if (ret < 0) {
-               dev_err(opt->dev, "failed to write register %02x\n",
-                               OPT3001_LOW_LIMIT);
-               return ret;
+       if (opt->use_irq) {
+               /*
+                * Disable the end-of-conversion interrupt mechanism by
+                * restoring the low-level limit value (clearing
+                * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
+                * those enable bits would affect the actual limit value due to
+                * bit-overlap and therefore can't be done.
+                */
+               value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
+               ret = i2c_smbus_write_word_swapped(opt->client,
+                                                  OPT3001_LOW_LIMIT,
+                                                  value);
+               if (ret < 0) {
+                       dev_err(opt->dev, "failed to write register %02x\n",
+                                       OPT3001_LOW_LIMIT);
+                       return ret;
+               }
        }
 
        exponent = OPT3001_REG_EXPONENT(opt->result);
@@ -325,13 +374,13 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time)
        reg = ret;
 
        switch (time) {
-       case 100000:
+       case OPT3001_INT_TIME_SHORT:
                reg &= ~OPT3001_CONFIGURATION_CT;
-               opt->int_time = 100000;
+               opt->int_time = OPT3001_INT_TIME_SHORT;
                break;
-       case 800000:
+       case OPT3001_INT_TIME_LONG:
                reg |= OPT3001_CONFIGURATION_CT;
-               opt->int_time = 800000;
+               opt->int_time = OPT3001_INT_TIME_LONG;
                break;
        default:
                return -EINVAL;
@@ -597,9 +646,9 @@ static int opt3001_configure(struct opt3001 *opt)
 
        /* Reflect status of the device's integration time setting */
        if (reg & OPT3001_CONFIGURATION_CT)
-               opt->int_time = 800000;
+               opt->int_time = OPT3001_INT_TIME_LONG;
        else
-               opt->int_time = 100000;
+               opt->int_time = OPT3001_INT_TIME_SHORT;
 
        /* Ensure device is in shutdown initially */
        opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SHUTDOWN);
@@ -733,12 +782,18 @@ static int opt3001_probe(struct i2c_client *client,
                return ret;
        }
 
-       ret = request_threaded_irq(irq, NULL, opt3001_irq,
-                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                       "opt3001", iio);
-       if (ret) {
-               dev_err(dev, "failed to request IRQ #%d\n", irq);
-               return ret;
+       /* Make use of INT pin only if valid IRQ no. is given */
+       if (irq > 0) {
+               ret = request_threaded_irq(irq, NULL, opt3001_irq,
+                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                               "opt3001", iio);
+               if (ret) {
+                       dev_err(dev, "failed to request IRQ #%d\n", irq);
+                       return ret;
+               }
+               opt->use_irq = true;
+       } else {
+               dev_dbg(opt->dev, "enabling interrupt-less operation\n");
        }
 
        return 0;
@@ -751,7 +806,8 @@ static int opt3001_remove(struct i2c_client *client)
        int ret;
        u16 reg;
 
-       free_irq(client->irq, iio);
+       if (opt->use_irq)
+               free_irq(client->irq, iio);
 
        ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
        if (ret < 0) {
index b13936dacc78377fec8254380ee92c0f95d50b25..9c5c9ef3f1dad25f37e108aeb9752940f46aa393 100644 (file)
@@ -252,7 +252,7 @@ struct ak_def {
        u8 data_regs[3];
 };
 
-static struct ak_def ak_def_array[AK_MAX_TYPE] = {
+static const struct ak_def ak_def_array[AK_MAX_TYPE] = {
        {
                .type = AK8975,
                .raw_to_gauss = ak8975_raw_to_gauss,
@@ -360,7 +360,7 @@ static struct ak_def ak_def_array[AK_MAX_TYPE] = {
  */
 struct ak8975_data {
        struct i2c_client       *client;
-       struct ak_def           *def;
+       const struct ak_def     *def;
        struct attribute_group  attrs;
        struct mutex            lock;
        u8                      asa[3];
index b27f0146647bb14996dda61d0a11bd31fc2de6cc..501f858df4137ea905eb78ea5b72badadcab4ce3 100644 (file)
 #define ST_MAGN_3_BDU_MASK                     0x10
 #define ST_MAGN_3_DRDY_IRQ_ADDR                        0x62
 #define ST_MAGN_3_DRDY_INT_MASK                        0x01
+#define ST_MAGN_3_IHL_IRQ_ADDR                 0x63
+#define ST_MAGN_3_IHL_IRQ_MASK                 0x04
 #define ST_MAGN_3_FS_AVL_15000_GAIN            1500
 #define ST_MAGN_3_MULTIREAD_BIT                        false
 #define ST_MAGN_3_OUT_X_L_ADDR                 0x68
@@ -480,6 +482,8 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
                .drdy_irq = {
                        .addr = ST_MAGN_3_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
+                       .addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_MAGN_3_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
                .bootime = 2,
index 6f2e7c9ac23e33c557ecfd0517dca15dce91807e..cf03a43545a10b145a898bb7b04757dc3d695027 100644 (file)
@@ -10,11 +10,11 @@ config BMP280
        depends on I2C
        select REGMAP_I2C
        help
-        Say yes here to build support for Bosch Sensortec BMP280
-        pressure and temperature sensor.
+         Say yes here to build support for Bosch Sensortec BMP280
+         pressure and temperature sensor.
 
-        To compile this driver as a module, choose M here: the module
-        will be called bmp280.
+         To compile this driver as a module, choose M here: the module
+         will be called bmp280.
 
 config HID_SENSOR_PRESS
        depends on HID_SENSOR_HUB
@@ -27,18 +27,33 @@ config HID_SENSOR_PRESS
          Say yes here to build support for the HID SENSOR
          Pressure driver
 
-          To compile this driver as a module, choose M here: the module
-          will be called hid-sensor-press.
+         To compile this driver as a module, choose M here: the module
+         will be called hid-sensor-press.
 
 config MPL115
+       tristate
+
+config MPL115_I2C
        tristate "Freescale MPL115A2 pressure sensor driver"
        depends on I2C
+       select MPL115
        help
          Say yes here to build support for the Freescale MPL115A2
          pressure sensor connected via I2C.
 
-          To compile this driver as a module, choose M here: the module
-          will be called mpl115.
+         To compile this driver as a module, choose M here: the module
+         will be called mpl115_i2c.
+
+config MPL115_SPI
+       tristate "Freescale MPL115A1 pressure sensor driver"
+       depends on SPI_MASTER
+       select MPL115
+       help
+         Say yes here to build support for the Freescale MPL115A1
+         pressure sensor connected via SPI.
+
+         To compile this driver as a module, choose M here: the module
+         will be called mpl115_spi.
 
 config MPL3115
        tristate "Freescale MPL3115A2 pressure sensor driver"
@@ -49,8 +64,8 @@ config MPL3115
          Say yes here to build support for the Freescale MPL3115A2
          pressure sensor / altimeter.
 
-          To compile this driver as a module, choose M here: the module
-          will be called mpl3115.
+         To compile this driver as a module, choose M here: the module
+         will be called mpl3115.
 
 config MS5611
        tristate "Measurement Specialties MS5611 pressure sensor driver"
@@ -82,7 +97,7 @@ config MS5611_SPI
 config MS5637
        tristate "Measurement Specialties MS5637 pressure & temperature sensor"
        depends on I2C
-        select IIO_MS_SENSORS_I2C
+       select IIO_MS_SENSORS_I2C
        help
          If you say yes here you get support for the Measurement Specialties
          MS5637 pressure and temperature sensor.
@@ -128,7 +143,7 @@ config T5403
          Say yes here to build support for the EPCOS T5403 pressure sensor
          connected via I2C.
 
-          To compile this driver as a module, choose M here: the module
-          will be called t5403.
+         To compile this driver as a module, choose M here: the module
+         will be called t5403.
 
 endmenu
index 46571c96823fce17c6eed8d3b9ef369371078eca..d336af14f3fe8e36c7c97d59cf411f1ac5632d4f 100644 (file)
@@ -6,6 +6,8 @@
 obj-$(CONFIG_BMP280) += bmp280.o
 obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
 obj-$(CONFIG_MPL115) += mpl115.o
+obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
+obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
 obj-$(CONFIG_MPL3115) += mpl3115.o
 obj-$(CONFIG_MS5611) += ms5611_core.o
 obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
index f5ecd6e19f5de725e715e547a5527f5875050561..138344ca3ffd352f8c438dbc620b1b9b09c6b0f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor
+ * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
  *
  * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
  *
@@ -7,17 +7,16 @@
  * the GNU General Public License.  See the file COPYING in the main
  * directory of this archive for more details.
  *
- * (7-bit I2C slave address 0x60)
- *
  * TODO: shutdown pin
  *
  */
 
 #include <linux/module.h>
-#include <linux/i2c.h>
 #include <linux/iio/iio.h>
 #include <linux/delay.h>
 
+#include "mpl115.h"
+
 #define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
 #define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
 #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
 #define MPL115_CONVERT 0x12 /* convert temperature and pressure */
 
 struct mpl115_data {
-       struct i2c_client *client;
+       struct device *dev;
        struct mutex lock;
        s16 a0;
        s16 b1, b2;
        s16 c12;
+       const struct mpl115_ops *ops;
 };
 
 static int mpl115_request(struct mpl115_data *data)
 {
-       int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0);
+       int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
+
        if (ret < 0)
                return ret;
 
@@ -57,12 +58,12 @@ static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
        if (ret < 0)
                goto done;
 
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC);
+       ret = data->ops->read(data->dev, MPL115_PADC);
        if (ret < 0)
                goto done;
        padc = ret >> 6;
 
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
+       ret = data->ops->read(data->dev, MPL115_TADC);
        if (ret < 0)
                goto done;
        tadc = ret >> 6;
@@ -90,7 +91,7 @@ static int mpl115_read_temp(struct mpl115_data *data)
        ret = mpl115_request(data);
        if (ret < 0)
                goto done;
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
+       ret = data->ops->read(data->dev, MPL115_TADC);
 done:
        mutex_unlock(&data->lock);
        return ret;
@@ -145,66 +146,53 @@ static const struct iio_info mpl115_info = {
        .driver_module = THIS_MODULE,
 };
 
-static int mpl115_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
+int mpl115_probe(struct device *dev, const char *name,
+                       const struct mpl115_ops *ops)
 {
        struct mpl115_data *data;
        struct iio_dev *indio_dev;
        int ret;
 
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
-               return -ENODEV;
-
-       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
        if (!indio_dev)
                return -ENOMEM;
 
        data = iio_priv(indio_dev);
-       data->client = client;
+       data->dev = dev;
+       data->ops = ops;
        mutex_init(&data->lock);
 
-       i2c_set_clientdata(client, indio_dev);
        indio_dev->info = &mpl115_info;
-       indio_dev->name = id->name;
-       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = name;
+       indio_dev->dev.parent = dev;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->channels = mpl115_channels;
        indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
 
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0);
+       ret = data->ops->init(data->dev);
+       if (ret)
+               return ret;
+
+       ret = data->ops->read(data->dev, MPL115_A0);
        if (ret < 0)
                return ret;
        data->a0 = ret;
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1);
+       ret = data->ops->read(data->dev, MPL115_B1);
        if (ret < 0)
                return ret;
        data->b1 = ret;
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2);
+       ret = data->ops->read(data->dev, MPL115_B2);
        if (ret < 0)
                return ret;
        data->b2 = ret;
-       ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12);
+       ret = data->ops->read(data->dev, MPL115_C12);
        if (ret < 0)
                return ret;
        data->c12 = ret;
 
-       return devm_iio_device_register(&client->dev, indio_dev);
+       return devm_iio_device_register(dev, indio_dev);
 }
-
-static const struct i2c_device_id mpl115_id[] = {
-       { "mpl115", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, mpl115_id);
-
-static struct i2c_driver mpl115_driver = {
-       .driver = {
-               .name   = "mpl115",
-       },
-       .probe = mpl115_probe,
-       .id_table = mpl115_id,
-};
-module_i2c_driver(mpl115_driver);
+EXPORT_SYMBOL_GPL(mpl115_probe);
 
 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
diff --git a/drivers/iio/pressure/mpl115.h b/drivers/iio/pressure/mpl115.h
new file mode 100644 (file)
index 0000000..01b6527
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Freescale MPL115A pressure/temperature sensor
+ *
+ * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ */
+
+#ifndef _MPL115_H_
+#define _MPL115_H_
+
+struct mpl115_ops {
+       int (*init)(struct device *);
+       int (*read)(struct device *, u8);
+       int (*write)(struct device *, u8, u8);
+};
+
+int mpl115_probe(struct device *dev, const char *name,
+                       const struct mpl115_ops *ops);
+
+#endif
diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c
new file mode 100644 (file)
index 0000000..9ea055c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Freescale MPL115A2 pressure/temperature sensor
+ *
+ * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * (7-bit I2C slave address 0x60)
+ *
+ * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "mpl115.h"
+
+static int mpl115_i2c_init(struct device *dev)
+{
+       return 0;
+}
+
+static int mpl115_i2c_read(struct device *dev, u8 address)
+{
+       return i2c_smbus_read_word_swapped(to_i2c_client(dev), address);
+}
+
+static int mpl115_i2c_write(struct device *dev, u8 address, u8 value)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(dev), address, value);
+}
+
+static const struct mpl115_ops mpl115_i2c_ops = {
+       .init = mpl115_i2c_init,
+       .read = mpl115_i2c_read,
+       .write = mpl115_i2c_write,
+};
+
+static int mpl115_i2c_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+               return -ENODEV;
+
+       return mpl115_probe(&client->dev, id->name, &mpl115_i2c_ops);
+}
+
+static const struct i2c_device_id mpl115_i2c_id[] = {
+       { "mpl115", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id);
+
+static struct i2c_driver mpl115_i2c_driver = {
+       .driver = {
+               .name   = "mpl115",
+       },
+       .probe = mpl115_i2c_probe,
+       .id_table = mpl115_i2c_id,
+};
+module_i2c_driver(mpl115_i2c_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c
new file mode 100644 (file)
index 0000000..9ebf55f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Freescale MPL115A1 pressure/temperature sensor
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "mpl115.h"
+
+#define MPL115_SPI_WRITE(address)      ((address) << 1)
+#define MPL115_SPI_READ(address)       (0x80 | (address) << 1)
+
+struct mpl115_spi_buf {
+       u8 tx[4];
+       u8 rx[4];
+};
+
+static int mpl115_spi_init(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct mpl115_spi_buf *buf;
+
+       buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, buf);
+
+       return 0;
+}
+
+static int mpl115_spi_read(struct device *dev, u8 address)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
+       struct spi_transfer xfer = {
+               .tx_buf = buf->tx,
+               .rx_buf = buf->rx,
+               .len = 4,
+       };
+       int ret;
+
+       buf->tx[0] = MPL115_SPI_READ(address);
+       buf->tx[2] = MPL115_SPI_READ(address + 1);
+
+       ret = spi_sync_transfer(spi, &xfer, 1);
+       if (ret)
+               return ret;
+
+       return (buf->rx[1] << 8) | buf->rx[3];
+}
+
+static int mpl115_spi_write(struct device *dev, u8 address, u8 value)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
+       struct spi_transfer xfer = {
+               .tx_buf = buf->tx,
+               .len = 2,
+       };
+
+       buf->tx[0] = MPL115_SPI_WRITE(address);
+       buf->tx[1] = value;
+
+       return spi_sync_transfer(spi, &xfer, 1);
+}
+
+static const struct mpl115_ops mpl115_spi_ops = {
+       .init = mpl115_spi_init,
+       .read = mpl115_spi_read,
+       .write = mpl115_spi_write,
+};
+
+static int mpl115_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+
+       return mpl115_probe(&spi->dev, id->name, &mpl115_spi_ops);
+}
+
+static const struct spi_device_id mpl115_spi_ids[] = {
+       { "mpl115", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(spi, mpl115_spi_ids);
+
+static struct spi_driver mpl115_spi_driver = {
+       .driver = {
+               .name   = "mpl115",
+       },
+       .probe = mpl115_spi_probe,
+       .id_table = mpl115_spi_ids,
+};
+module_spi_driver(mpl115_spi_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver");
+MODULE_LICENSE("GPL");
index b39a2fb0671cd15638c0db19c2f068a52a02f8b6..172393ad34aff04f9b4909a73e3a16985f82c109 100644 (file)
@@ -62,6 +62,8 @@
 #define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR                0x22
 #define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK   0x04
 #define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK   0x20
+#define ST_PRESS_LPS331AP_IHL_IRQ_ADDR         0x22
+#define ST_PRESS_LPS331AP_IHL_IRQ_MASK         0x80
 #define ST_PRESS_LPS331AP_MULTIREAD_BIT                true
 #define ST_PRESS_LPS331AP_TEMP_OFFSET          42500
 
 #define ST_PRESS_LPS25H_DRDY_IRQ_ADDR          0x23
 #define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK     0x01
 #define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK     0x10
+#define ST_PRESS_LPS25H_IHL_IRQ_ADDR           0x22
+#define ST_PRESS_LPS25H_IHL_IRQ_MASK           0x80
 #define ST_PRESS_LPS25H_MULTIREAD_BIT          true
 #define ST_PRESS_LPS25H_TEMP_OFFSET            42500
 #define ST_PRESS_LPS25H_OUT_XL_ADDR            0x28
@@ -220,6 +224,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
                        .addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT,
                .bootime = 2,
@@ -304,6 +310,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
                        .addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR,
                        .mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK,
                        .mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK,
+                       .addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR,
+                       .mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK,
                },
                .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
                .bootime = 2,
index 17e5c9c515d42ffec1f669336ea901de34445910..7c7cd84560603af5729c553071912ae2b19ccfef 100644 (file)
@@ -1,31 +1,3 @@
-
-What:          /sys/bus/iio/devices/device[n]/range
-KernelVersion: 2.6.37
-Contact:       linux-iio@vger.kernel.org
-Description:
-               Hardware dependent ADC Full Scale Range used for some ambient
-               light sensors in calculating lux.
-
-What:          /sys/bus/iio/devices/device[n]/range_available
-KernelVersion: 2.6.37
-Contact:       linux-iio@vger.kernel.org
-Description:
-               Hardware dependent supported vales for ADC Full Scale Range.
-
-What:          /sys/bus/iio/devices/device[n]/adc_resolution
-KernelVersion: 2.6.37
-Contact:       linux-iio@vger.kernel.org
-Description:
-               Hardware dependent ADC resolution of the ambient light sensor
-               used in calculating the lux.
-
-What:          /sys/bus/iio/devices/device[n]/adc_resolution_available
-KernelVersion: 2.6.37
-Contact:       linux-iio@vger.kernel.org
-Description:
-               Hardware dependent list of possible values supported for the
-               adc_resolution of the given sensor.
-
 What:          /sys/bus/iio/devices/device[n]/in_illuminance0[_input|_raw]
 KernelVersion: 2.6.35
 Contact:       linux-iio@vger.kernel.org
index 92211039ffa941270b5047bbab16c5e411665940..92f2b72cce30b7a0585ad3102babbd69c2473c94 100644 (file)
@@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st,
                        st->mclk = pdata->ext_clk_hz;
                else
                        st->mclk = AD7192_INT_FREQ_MHZ;
-                       break;
+               break;
        default:
                ret = -EINVAL;
                goto out;
index e8d0ff2d5c9b9cbd1ec0ffcb7b0c027c44beed0a..f6b9a10326ea450a9873a077f09a2008c1b1cc1d 100644 (file)
@@ -21,8 +21,8 @@
  */
 
 #define AD7150_STATUS              0
-#define AD7150_STATUS_OUT1         (1 << 3)
-#define AD7150_STATUS_OUT2         (1 << 5)
+#define AD7150_STATUS_OUT1         BIT(3)
+#define AD7150_STATUS_OUT2         BIT(5)
 #define AD7150_CH1_DATA_HIGH       1
 #define AD7150_CH2_DATA_HIGH       3
 #define AD7150_CH1_AVG_HIGH        5
@@ -36,7 +36,7 @@
 #define AD7150_CH2_TIMEOUT         13
 #define AD7150_CH2_SETUP           14
 #define AD7150_CFG                 15
-#define AD7150_CFG_FIX             (1 << 7)
+#define AD7150_CFG_FIX             BIT(7)
 #define AD7150_PD_TIMER            16
 #define AD7150_CH1_CAPDAC          17
 #define AD7150_CH2_CAPDAC          18
@@ -160,8 +160,9 @@ static int ad7150_read_event_config(struct iio_dev *indio_dev,
 
 /* lock should be held */
 static int ad7150_write_event_params(struct iio_dev *indio_dev,
-        unsigned int chan, enum iio_event_type type,
-        enum iio_event_direction dir)
+                                    unsigned int chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir)
 {
        int ret;
        u16 value;
@@ -209,8 +210,9 @@ static int ad7150_write_event_params(struct iio_dev *indio_dev,
 }
 
 static int ad7150_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)
+                                    const struct iio_chan_spec *chan,
+                                    enum iio_event_type type,
+                                    enum iio_event_direction dir, int state)
 {
        u8 thresh_type, cfg, adaptive;
        int ret;
@@ -302,11 +304,11 @@ static int ad7150_read_event_value(struct iio_dev *indio_dev,
 }
 
 static int ad7150_write_event_value(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)
+                                   const struct iio_chan_spec *chan,
+                                   enum iio_event_type type,
+                                   enum iio_event_direction dir,
+                                   enum iio_event_info info,
+                                   int val, int val2)
 {
        int ret;
        struct ad7150_chip_info *chip = iio_priv(indio_dev);
@@ -365,9 +367,9 @@ static ssize_t ad7150_show_timeout(struct device *dev,
 }
 
 static ssize_t ad7150_store_timeout(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t len)
+                                   struct device_attribute *attr,
+                                   const char *buf,
+                                   size_t len)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7150_chip_info *chip = iio_priv(indio_dev);
@@ -580,7 +582,7 @@ static const struct iio_info ad7150_info = {
  */
 
 static int ad7150_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
+                       const struct i2c_device_id *id)
 {
        int ret;
        struct ad7150_chip_info *chip;
index 2fe939c73cd241c6a24eabea83abf8761fb1de0a..6670c3d25c58c8d0c1b0e1c0260dc3e52f57bc46 100644 (file)
@@ -119,6 +119,8 @@ struct st_sensor_bdu {
  * @addr: address of the register.
  * @mask_int1: mask to enable/disable IRQ on INT1 pin.
  * @mask_int2: mask to enable/disable IRQ on INT2 pin.
+ * @addr_ihl: address to enable/disable active low on the INT lines.
+ * @mask_ihl: mask to enable/disable active low on the INT lines.
  * struct ig1 - represents the Interrupt Generator 1 of sensors.
  * @en_addr: address of the enable ig1 register.
  * @en_mask: mask to write the on/off value for enable.
@@ -127,6 +129,8 @@ struct st_sensor_data_ready_irq {
        u8 addr;
        u8 mask_int1;
        u8 mask_int2;
+       u8 addr_ihl;
+       u8 mask_ihl;
        struct {
                u8 en_addr;
                u8 en_mask;
diff --git a/include/linux/platform_data/ad5761.h b/include/linux/platform_data/ad5761.h
new file mode 100644 (file)
index 0000000..7bd8ed7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
+ *
+ * Copyright 2016 Qtechnology A/S
+ * 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef __LINUX_PLATFORM_DATA_AD5761_H__
+#define __LINUX_PLATFORM_DATA_AD5761_H__
+
+/**
+ * enum ad5761_voltage_range - Voltage range the AD5761 is configured for.
+ * @AD5761_VOLTAGE_RANGE_M10V_10V:  -10V to  10V
+ * @AD5761_VOLTAGE_RANGE_0V_10V:      0V to  10V
+ * @AD5761_VOLTAGE_RANGE_M5V_5V:     -5V to   5V
+ * @AD5761_VOLTAGE_RANGE_0V_5V:       0V to   5V
+ * @AD5761_VOLTAGE_RANGE_M2V5_7V5: -2.5V to 7.5V
+ * @AD5761_VOLTAGE_RANGE_M3V_3V:     -3V to   3V
+ * @AD5761_VOLTAGE_RANGE_0V_16V:      0V to  16V
+ * @AD5761_VOLTAGE_RANGE_0V_20V:      0V to  20V
+ */
+
+enum ad5761_voltage_range {
+       AD5761_VOLTAGE_RANGE_M10V_10V,
+       AD5761_VOLTAGE_RANGE_0V_10V,
+       AD5761_VOLTAGE_RANGE_M5V_5V,
+       AD5761_VOLTAGE_RANGE_0V_5V,
+       AD5761_VOLTAGE_RANGE_M2V5_7V5,
+       AD5761_VOLTAGE_RANGE_M3V_3V,
+       AD5761_VOLTAGE_RANGE_0V_16V,
+       AD5761_VOLTAGE_RANGE_0V_20V,
+};
+
+/**
+ * struct ad5761_platform_data - AD5761 DAC driver platform data
+ * @voltage_range: Voltage range the AD5761 is configured for
+ */
+
+struct ad5761_platform_data {
+       enum ad5761_voltage_range voltage_range;
+};
+
+#endif
index 7c63bd67c36e274f933a857b23ee00e5e1c2daed..c077617f3304355d2d465e3760a5f46f5de2bbb9 100644 (file)
@@ -37,6 +37,7 @@ enum iio_chan_type {
        IIO_VELOCITY,
        IIO_CONCENTRATION,
        IIO_RESISTANCE,
+       IIO_PH,
 };
 
 enum iio_modifier {