iio: st_sensors: read each channel individually
authorLinus Walleij <linus.walleij@linaro.org>
Thu, 24 Mar 2016 13:18:04 +0000 (14:18 +0100)
committerJonathan Cameron <jic23@kernel.org>
Tue, 19 Apr 2016 18:58:11 +0000 (19:58 +0100)
The current buffer read code tries to optimize reads from the
sensor data registers by issuing a single read operation across
all the indata registers.

This doesn't work: when the LIS331DL accelerometer sensor is
configured to open drain, active low interrupt mode, this will
just clear the XDA (X-axis data available) bit in the STATUS_REG
register (0x27), while YDA, ZDA and even ZYXDA remain set to 1,
and the internal logic of the sensor holds the DRDY (INT1) line
asserted (the value of the status register is 0xee).

If we instead issue one read operation per enabled channel
(X, Y, Z) things start working and we can use open drain and
active low interrupts.

Note that a backported patch fixing this issue will be heading
via the fixes branch but changes in this file already in staging-next
will make that patch 'look' rather different.  The code in here
is the correct one when that clash hits.

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/common/st_sensors/st_sensors_buffer.c

index 73764961feac5bda1b0912e32644d3f0d0d59a8f..2ce0d2a3f85572ed2e0b9faf1790818e9e8235e9 100644 (file)
 
 int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
 {
-       u8 addr[3]; /* no ST sensor has more than 3 channels */
-       int i, n = 0, len;
+       int i, len;
+       int total = 0;
        struct st_sensor_data *sdata = iio_priv(indio_dev);
        unsigned int num_data_channels = sdata->num_data_channels;
-       unsigned int byte_for_channel =
-                       indio_dev->channels[0].scan_type.storagebits >> 3;
 
        for (i = 0; i < num_data_channels; i++) {
+               unsigned int bytes_to_read;
+
                if (test_bit(i, indio_dev->active_scan_mask)) {
-                       addr[n] = indio_dev->channels[i].address;
-                       n++;
-               }
-       }
-       switch (n) {
-       case 1:
-               len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
-                       addr[0], byte_for_channel, buf, sdata->multiread_bit);
-               break;
-       case 2:
-               if ((addr[1] - addr[0]) == byte_for_channel) {
+                       bytes_to_read = indio_dev->channels[i].scan_type.storagebits >> 3;
                        len = sdata->tf->read_multiple_byte(&sdata->tb,
-                               sdata->dev, addr[0], byte_for_channel * n,
-                               buf, sdata->multiread_bit);
-               } else {
-                       u8 *rx_array;
-                       rx_array = kmalloc(byte_for_channel * num_data_channels,
-                                          GFP_KERNEL);
-                       if (!rx_array)
-                               return -ENOMEM;
+                               sdata->dev, indio_dev->channels[i].address,
+                               bytes_to_read,
+                               buf + total, sdata->multiread_bit);
 
-                       len = sdata->tf->read_multiple_byte(&sdata->tb,
-                               sdata->dev, addr[0],
-                               byte_for_channel * num_data_channels,
-                               rx_array, sdata->multiread_bit);
-                       if (len < 0) {
-                               kfree(rx_array);
-                               return len;
-                       }
+                       if (len < bytes_to_read)
+                               return -EIO;
 
-                       for (i = 0; i < n * byte_for_channel; i++) {
-                               if (i < n)
-                                       buf[i] = rx_array[i];
-                               else
-                                       buf[i] = rx_array[n + i];
-                       }
-                       kfree(rx_array);
-                       len = byte_for_channel * n;
+                       /* Advance the buffer pointer */
+                       total += len;
                }
-               break;
-       case 3:
-               len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
-                       addr[0], byte_for_channel * num_data_channels,
-                       buf, sdata->multiread_bit);
-               break;
-       default:
-               return -EINVAL;
        }
-       if (len != byte_for_channel * n)
-               return -EIO;
 
-       return len;
+       return total;
 }
 EXPORT_SYMBOL(st_sensors_get_buffer_element);