iio: imu: st_lsm6dsx: introduce sw trigger support
authorLorenzo Bianconi <lorenzo@kernel.org>
Fri, 28 Oct 2022 11:23:42 +0000 (13:23 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 23 Nov 2022 19:44:04 +0000 (19:44 +0000)
There are some hw configuration where irq0 and/or irq1 pins are not
connected to the SPI or I2C/I3C controller. In order to avoid polling
the output register introduce iio-sw trigger support when irq line is
not available (or hw FIFO is not supported).

Suggested-by: Mario Tesi <mario.tesi@st.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://lore.kernel.org/r/93ae6ff1150b531a9d7a4d3d1b1adb8383613717.1666955685.git.lorenzo@kernel.org
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c

index 07ad802..6399b0b 100644 (file)
@@ -424,7 +424,7 @@ struct st_lsm6dsx_hw {
        struct {
                __le16 channels[3];
                s64 ts __aligned(8);
-       } scan[3];
+       } scan[ST_LSM6DSX_ID_MAX];
 };
 
 static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = {
@@ -456,6 +456,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val);
 int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
 int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
+int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len);
 int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
 
 static inline int
index fe5fa08..73fd5f0 100644 (file)
@@ -53,6 +53,8 @@
 #include <linux/iio/events.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/minmax.h>
@@ -2117,6 +2119,32 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
        return fifo_len || event ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static irqreturn_t st_lsm6dsx_sw_trigger_handler_thread(int irq,
+                                                       void *private)
+{
+       struct iio_poll_func *pf = private;
+       struct iio_dev *iio_dev = pf->indio_dev;
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+       struct st_lsm6dsx_hw *hw = sensor->hw;
+
+       if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
+           sensor->id == ST_LSM6DSX_ID_EXT1 ||
+           sensor->id == ST_LSM6DSX_ID_EXT2)
+               st_lsm6dsx_shub_read_output(hw,
+                                           (u8 *)hw->scan[sensor->id].channels,
+                                           sizeof(hw->scan[sensor->id].channels));
+       else
+               st_lsm6dsx_read_locked(hw, iio_dev->channels[0].address,
+                                      hw->scan[sensor->id].channels,
+                                      sizeof(hw->scan[sensor->id].channels));
+
+       iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan[sensor->id],
+                                          iio_get_time_ns(iio_dev));
+       iio_trigger_notify_done(iio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
 static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw)
 {
        struct st_sensors_platform_data *pdata;
@@ -2175,6 +2203,46 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw)
        return 0;
 }
 
+static int st_lsm6dsx_sw_buffer_preenable(struct iio_dev *iio_dev)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+
+       return st_lsm6dsx_device_set_enable(sensor, true);
+}
+
+static int st_lsm6dsx_sw_buffer_postdisable(struct iio_dev *iio_dev)
+{
+       struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+
+       return st_lsm6dsx_device_set_enable(sensor, false);
+}
+
+static const struct iio_buffer_setup_ops st_lsm6dsx_sw_buffer_ops = {
+       .preenable = st_lsm6dsx_sw_buffer_preenable,
+       .postdisable = st_lsm6dsx_sw_buffer_postdisable,
+};
+
+static int st_lsm6dsx_sw_buffers_setup(struct st_lsm6dsx_hw *hw)
+{
+       int i;
+
+       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+               int err;
+
+               if (!hw->iio_devs[i])
+                       continue;
+
+               err = devm_iio_triggered_buffer_setup(hw->dev,
+                                       hw->iio_devs[i], NULL,
+                                       st_lsm6dsx_sw_trigger_handler_thread,
+                                       &st_lsm6dsx_sw_buffer_ops);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int st_lsm6dsx_init_regulators(struct device *dev)
 {
        /* vdd-vddio power regulators */
@@ -2255,6 +2323,16 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
                        return err;
        }
 
+       if (!hw->irq || !hw->settings->fifo_ops.read_fifo) {
+               /*
+                * Rely on sw triggers (e.g. hr-timers) if irq pin is not
+                * connected of if the device does not support HW FIFO
+                */
+               err = st_lsm6dsx_sw_buffers_setup(hw);
+               if (err)
+                       return err;
+       }
+
        err = iio_read_mount_matrix(hw->dev, &hw->orientation);
        if (err)
                return err;
index 99562ba..f2b64b4 100644 (file)
@@ -170,9 +170,7 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
  *
  * Read st_lsm6dsx i2c controller register
  */
-static int
-st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data,
-                           int len)
+int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len)
 {
        const struct st_lsm6dsx_shub_settings *hub_settings;
        int err;