iio: st_sensors: support active-low interrupts
authorLinus Walleij <linus.walleij@linaro.org>
Thu, 19 Nov 2015 09:15:17 +0000 (10:15 +0100)
committerJonathan Cameron <jic23@kernel.org>
Sun, 10 Jan 2016 12:35:32 +0000 (12:35 +0000)
Most ST MEMS Sensors that support interrupts can also handle sending
an active low interrupt, i.e. going from high to low on data ready
(or other interrupt) and thus triggering on a falling edge to the
interrupt controller.

Set up logic to inspect the interrupt line we get for a sensor: if
it is triggering on rising edge, leave everything alone, but if it
triggers on falling edges, set up active low, and if unsupported
configurations appear: warn with errors and reconfigure the interrupt
to a rising edge, which all interrupt generating sensors support.

Create a local header for st_sensors_core.h to share functions
between the sensor core and the trigger setup code.

Cc: Giuseppe Barba <giuseppe.barba@st.com>
Cc: Denis Ciocca <denis.ciocca@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/accel/st_accel_core.c
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/gyro/st_gyro_core.c
drivers/iio/magnetometer/st_magn_core.c
drivers/iio/pressure/st_pressure_core.c
include/linux/iio/common/st_sensors.h

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 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 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 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 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 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;