]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/iio/common/st_sensors/st_sensors_trigger.c
Merge branch 'for-4.8/core' of git://git.kernel.dk/linux-block
[karo-tx-linux.git] / drivers / iio / common / st_sensors / st_sensors_trigger.c
index fab494d719511447e8be4538e84688a5b466cdee..e66f12ee8a5575005745141bca46c8755ae8750c 100644 (file)
 #include <linux/iio/common/st_sensors.h>
 #include "st_sensors_core.h"
 
+/**
+ * st_sensors_new_samples_available() - check if more samples came in
+ * returns:
+ * 0 - no new samples available
+ * 1 - new samples available
+ * negative - error or unknown
+ */
+static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
+                                           struct st_sensor_data *sdata)
+{
+       u8 status;
+       int ret;
+
+       /* How would I know if I can't check it? */
+       if (!sdata->sensor_settings->drdy_irq.addr_stat_drdy)
+               return -EINVAL;
+
+       /* No scan mask, no interrupt */
+       if (!indio_dev->active_scan_mask)
+               return 0;
+
+       ret = sdata->tf->read_byte(&sdata->tb, sdata->dev,
+                       sdata->sensor_settings->drdy_irq.addr_stat_drdy,
+                       &status);
+       if (ret < 0) {
+               dev_err(sdata->dev,
+                       "error checking samples available\n");
+               return ret;
+       }
+       /*
+        * the lower bits of .active_scan_mask[0] is directly mapped
+        * to the channels on the sensor: either bit 0 for
+        * one-dimensional sensors, or e.g. x,y,z for accelerometers,
+        * gyroscopes or magnetometers. No sensor use more than 3
+        * channels, so cut the other status bits here.
+        */
+       status &= 0x07;
+
+       if (status & (u8)indio_dev->active_scan_mask[0])
+               return 1;
+
+       return 0;
+}
+
 /**
  * st_sensors_irq_handler() - top half of the IRQ-based triggers
  * @irq: irq number
@@ -43,44 +87,43 @@ irqreturn_t st_sensors_irq_thread(int irq, void *p)
        struct iio_trigger *trig = p;
        struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
        struct st_sensor_data *sdata = iio_priv(indio_dev);
-       int ret;
 
        /*
         * If this trigger is backed by a hardware interrupt and we have a
-        * status register, check if this IRQ came from us
+        * status register, check if this IRQ came from us. Notice that
+        * we will process also if st_sensors_new_samples_available()
+        * returns negative: if we can't check status, then poll
+        * unconditionally.
         */
-       if (sdata->sensor_settings->drdy_irq.addr_stat_drdy) {
-               u8 status;
-
-               ret = sdata->tf->read_byte(&sdata->tb, sdata->dev,
-                          sdata->sensor_settings->drdy_irq.addr_stat_drdy,
-                          &status);
-               if (ret < 0) {
-                       dev_err(sdata->dev, "could not read channel status\n");
-                       goto out_poll;
-               }
-               /*
-                * the lower bits of .active_scan_mask[0] is directly mapped
-                * to the channels on the sensor: either bit 0 for
-                * one-dimensional sensors, or e.g. x,y,z for accelerometers,
-                * gyroscopes or magnetometers. No sensor use more than 3
-                * channels, so cut the other status bits here.
-                */
-               status &= 0x07;
+       if (sdata->hw_irq_trigger &&
+           st_sensors_new_samples_available(indio_dev, sdata)) {
+               iio_trigger_poll_chained(p);
+       } else {
+               dev_dbg(sdata->dev, "spurious IRQ\n");
+               return IRQ_NONE;
+       }
 
-               /*
-                * If this was not caused by any channels on this sensor,
-                * return IRQ_NONE
-                */
-               if (!indio_dev->active_scan_mask)
-                       return IRQ_NONE;
-               if (!(status & (u8)indio_dev->active_scan_mask[0]))
-                       return IRQ_NONE;
+       /*
+        * If we have proper level IRQs the handler will be re-entered if
+        * the line is still active, so return here and come back in through
+        * the top half if need be.
+        */
+       if (!sdata->edge_irq)
+               return IRQ_HANDLED;
+
+       /*
+        * If we are using egde IRQs, new samples arrived while processing
+        * the IRQ and those may be missed unless we pick them here, so poll
+        * again. If the sensor delivery frequency is very high, this thread
+        * turns into a polled loop handler.
+        */
+       while (sdata->hw_irq_trigger &&
+              st_sensors_new_samples_available(indio_dev, sdata)) {
+               dev_dbg(sdata->dev, "more samples came in during polling\n");
+               sdata->hw_timestamp = iio_get_time_ns(indio_dev);
+               iio_trigger_poll_chained(p);
        }
 
-out_poll:
-       /* It's our IRQ: proceed to handle the register polling */
-       iio_trigger_poll_chained(p);
        return IRQ_HANDLED;
 }
 
@@ -107,13 +150,18 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
         * 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) {
+       switch(irq_trig) {
+       case IRQF_TRIGGER_FALLING:
+       case IRQF_TRIGGER_LOW:
                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;
+                               "falling/low specified for IRQ "
+                               "but hardware only support rising/high: "
+                               "will request rising/high\n");
+                       if (irq_trig == IRQF_TRIGGER_FALLING)
+                               irq_trig = IRQF_TRIGGER_RISING;
+                       if (irq_trig == IRQF_TRIGGER_LOW)
+                               irq_trig = IRQF_TRIGGER_HIGH;
                } else {
                        /* Set up INT active low i.e. falling edge */
                        err = st_sensors_write_data_with_mask(indio_dev,
@@ -122,20 +170,39 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
                        if (err < 0)
                                goto iio_trigger_free;
                        dev_info(&indio_dev->dev,
-                                "interrupts on the falling edge\n");
+                                "interrupts on the falling edge or "
+                                "active low level\n");
                }
-       } else if (irq_trig == IRQF_TRIGGER_RISING) {
+               break;
+       case IRQF_TRIGGER_RISING:
                dev_info(&indio_dev->dev,
                         "interrupts on the rising edge\n");
-
-       } else {
+               break;
+       case IRQF_TRIGGER_HIGH:
+               dev_info(&indio_dev->dev,
+                        "interrupts active high level\n");
+               break;
+       default:
+               /* This is the most preferred mode, if possible */
                dev_err(&indio_dev->dev,
-               "unsupported IRQ trigger specified (%lx), only "
-                       "rising and falling edges supported, enforce "
+                       "unsupported IRQ trigger specified (%lx), enforce "
                        "rising edge\n", irq_trig);
                irq_trig = IRQF_TRIGGER_RISING;
        }
 
+       /* Tell the interrupt handler that we're dealing with edges */
+       if (irq_trig == IRQF_TRIGGER_FALLING ||
+           irq_trig == IRQF_TRIGGER_RISING)
+               sdata->edge_irq = true;
+       else
+               /*
+                * If we're not using edges (i.e. level interrupts) we
+                * just mask off the IRQ, handle one interrupt, then
+                * if the line is still low, we return to the
+                * interrupt handler top half again and start over.
+                */
+               irq_trig |= IRQF_ONESHOT;
+
        /*
         * If the interrupt pin is Open Drain, by definition this
         * means that the interrupt line may be shared with other
@@ -148,9 +215,6 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
            sdata->sensor_settings->drdy_irq.addr_stat_drdy)
                irq_trig |= IRQF_SHARED;
 
-       /* Let's create an interrupt thread masking the hard IRQ here */
-       irq_trig |= IRQF_ONESHOT;
-
        err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
                        st_sensors_irq_handler,
                        st_sensors_irq_thread,