All of lore.kernel.org
 help / color / mirror / Atom feed
* STMicroelectronics accelerometers driver.
@ 2012-10-08 15:39 Denis CIOCCA
  2012-10-08 19:14 ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-08 15:39 UTC (permalink / raw)
  To: linux-iio; +Cc: Jonathan Cameron, burman.yan, pavel

Hi everybody,

I submit to you my linux device driver to support the latest ST
accelerometers. I want do submission to the linux community and I ask
you suggestions and proposals.
Thanks

Denis


 From c965b7f522d858a48e3bbcc723cb2ff4c00397f4 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 8 Oct 2012 17:04:32 +0200
Subject: [PATCH 1/2] add driver

---
  .../STMicroelectronics_accelerometers_iio_buffer.c |  155 ++
  .../STMicroelectronics_accelerometers_iio_core.c   | 1495
++++++++++++++++++++
  .../STMicroelectronics_accelerometers_iio_i2c.c    |  124 ++
  .../STMicroelectronics_accelerometers_iio_spi.c    |  209 +++
  ...STMicroelectronics_accelerometers_iio_trigger.c |   96 ++
  5 files changed, 2079 insertions(+), 0 deletions(-)
  create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
  create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
  create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
  create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
  create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c

diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
new file mode 100644
index 0000000..44e3f3d
--- /dev/null
+++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
@@ -0,0 +1,155 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
+
+
+static int acc_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+       int len;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       len =3D adata->read_multiple_byte(adata,
+               indio_dev->channels[ACC_SCAN_X].address,
+               ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS, rx_array);
+       if (len < 0)
+               goto read_error;
+
+       return len;
+
+read_error:
+       return -EIO;
+}
+
+static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int ret, i, n =3D 0;
+       u8 *rx_array;
+       u8 mask =3D 0x01;
+       s16 *data =3D (s16 *)buf;
+
+       rx_array =3D kzalloc(ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS,
+                                                               GFP_KERNEL)=
;
+       if (rx_array =3D=3D NULL)
+               return -ENOMEM;
+       ret =3D acc_read_all(indio_dev, rx_array);
+       if (ret < 0)
+               return ret;
+       for (i =3D 0; i < ACC_NUMBER_DATA_CHANNELS; i++) {
+               if ((*indio_dev->active_scan_mask & mask) > 0) {
+                       if (indio_dev->channels[i].scan_type.endianness =3D=
=3D
+                                                               IIO_LE) {
+                               data[n] =3D (s16)(cpu_to_le16(le16_to_cpu((
+                               (__le16 *)rx_array)[i]))) >>
+                               indio_dev->channels[i].scan_type.shift;
+                               n++;
+                       } else {
+                               data[n] =3D (s16)(cpu_to_le16(be16_to_cpu((
+                               (__be16 *)rx_array)[i]))) >>
+                               indio_dev->channels[i].scan_type.shift;
+                               n++;
+                       }
+               }
+               mask =3D mask << 1;
+       }
+       kfree(rx_array);
+       return i*sizeof(data[0]);
+}
+
+static irqreturn_t acc_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       int len =3D 0;
+       char *data;
+
+       data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (data =3D=3D NULL)
+               goto done;
+       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklengt=
h))
+               len =3D acc_get_buffer_element(indio_dev, data);
+       else
+               goto done;
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =3D pf->time=
stamp;
+       iio_push_to_buffer(indio_dev->buffer, data);
+       kfree(data);
+
+done:
+       iio_trigger_notify_done(indio_dev->trig);
+       return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops acc_buf_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &iio_triggered_buffer_postenable,
+       .predisable =3D &iio_triggered_buffer_predisable,
+};
+
+int acc_allocate_ring(struct iio_dev *indio_dev)
+{
+       int ret;
+       struct iio_buffer *buffer;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       buffer =3D iio_kfifo_allocate(indio_dev);
+       if (buffer =3D=3D NULL) {
+               ret =3D -ENOMEM;
+               goto acc_configure_ring_error;
+       }
+       indio_dev->buffer =3D buffer;
+       indio_dev->scan_timestamp =3D true;
+       indio_dev->buffer->scan_timestamp =3D true;
+       indio_dev->setup_ops =3D &acc_buf_setup_ops;
+       indio_dev->pollfunc =3D iio_alloc_pollfunc(
+                                               &iio_pollfunc_store_time,
+                                               &acc_trigger_handler,
+                                               IRQF_ONESHOT,
+                                               indio_dev,
+                                               "%s_consumer%d",
+                                               indio_dev->name,
+                                               indio_dev->id);
+       if (indio_dev->pollfunc =3D=3D NULL) {
+               ret =3D -ENOMEM;
+               goto iio_alloc_pollfunc_error;
+       }
+       indio_dev->modes |=3D INDIO_BUFFER_TRIGGERED;
+       ret =3D iio_buffer_register(indio_dev, indio_dev->channels,
+                                               ACC_NUMBER_ALL_CHANNELS);
+       if (ret < 0) {
+               pr_err("%s: iio_buffer_register failed.\n", adata->name);
+               goto iio_buffer_register_error;
+       }
+       pr_info("%s: allocate buffer -> done.\n", adata->name);
+       return 0;
+
+iio_buffer_register_error:
+iio_alloc_pollfunc_error:
+       iio_kfifo_free(buffer);
+acc_configure_ring_error:
+       return ret;
+}
+
+void acc_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_dealloc_pollfunc(indio_dev->pollfunc);
+       iio_kfifo_free(indio_dev->buffer);
+       iio_buffer_unregister(indio_dev);
+}
diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
new file mode 100644
index 0000000..f9ccbf6
--- /dev/null
+++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
@@ -0,0 +1,1495 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
+
+
+#define FULLSCALE_AVAILABLE_MAX_NUMBER 5
+#define ODR_LIST_MAX_NUMBER            10
+#define X_AXIS                         0
+#define Y_AXIS                         1
+#define Z_AXIS                         2
+#define TIMESTAMP_AXIS                 3
+#define DATA_HIGH                      1
+#define DATA_LOW                       0
+#define POWER_ON                       1
+#define POWER_OFF                      0
+#define UG_TO_MS2                      9800
+#define OFFSET_1                       0x01
+
+/* DEFAULT VALUE FOR SENSORS */
+#define DEFAULT_WAI_ADDRESS            0x0f
+#define DEFAULT_ODR_ADDR               0x20
+#define DEFAULT_ODR_MASK               0xf0
+#define DEFAULT_ODR_N_BIT              4
+#define DEFAULT_ODR_AVL_1HZ            1
+#define DEFAULT_ODR_AVL_1HZ_VALUE      0x01
+#define DEFAULT_ODR_AVL_10HZ           10
+#define DEFAULT_ODR_AVL_10HZ_VALUE     0x02
+#define DEFAULT_ODR_AVL_25HZ           25
+#define DEFAULT_ODR_AVL_25HZ_VALUE     0x03
+#define DEFAULT_ODR_AVL_50HZ           50
+#define DEFAULT_ODR_AVL_50HZ_VALUE     0x04
+#define DEFAULT_ODR_AVL_100HZ          100
+#define DEFAULT_ODR_AVL_100HZ_VALUE    0x05
+#define DEFAULT_ODR_AVL_200HZ          200
+#define DEFAULT_ODR_AVL_200HZ_VALUE    0x06
+#define DEFAULT_ODR_AVL_400HZ          400
+#define DEFAULT_ODR_AVL_400HZ_VALUE    0x07
+#define DEFAULT_ODR_AVL_1600HZ         1600
+#define DEFAULT_ODR_AVL_1600HZ_VALUE   0x08
+#define DEFAULT_POWER_N_BIT            4
+#define DEFAULT_POWER_ADDR             DEFAULT_ODR_ADDR
+#define DEFAULT_POWER_MASK             DEFAULT_ODR_MASK
+#define DEFAULT_POWER_ON_VALUE         0x01
+#define DEFAULT_POWER_OFF_VALUE                0x00
+#define DEFAULT_FS_N_BIT               2
+#define DEFAULT_FS_ADDR                        0x23
+#define DEFAULT_FS_MASK                        0x30
+#define DEFAULT_FS_AVL_2_NUM           2
+#define DEFAULT_FS_AVL_2_VALUE         0x00
+#define DEFAULT_FS_AVL_2_GAIN          1000
+#define DEFAULT_FS_AVL_4_NUM           4
+#define DEFAULT_FS_AVL_4_VALUE         0x01
+#define DEFAULT_FS_AVL_4_GAIN          2000
+#define DEFAULT_FS_AVL_8_NUM           8
+#define DEFAULT_FS_AVL_8_VALUE         0x02
+#define DEFAULT_FS_AVL_8_GAIN          4000
+#define DEFAULT_FS_AVL_16_NUM          16
+#define DEFAULT_FS_AVL_16_VALUE                0x03
+#define DEFAULT_FS_AVL_16_GAIN         12000
+#define DEFAULT_OUT_X_H                        0x29
+#define DEFAULT_OUT_X_L                        0x28
+#define DEFAULT_OUT_Y_H                        0x2b
+#define DEFAULT_OUT_Y_L                        0x2a
+#define DEFAULT_OUT_Z_H                        0x2d
+#define DEFAULT_OUT_Z_L                        0x2c
+#define DEFAULT_REALBITS               12
+#define DEFAULT_DRDY_IRQ_ADDR          0x22
+#define DEFAULT_DRDY_IRQ_MASK          0x10
+#define DEFAULT_BDU_ADDR               0x23
+#define DEFAULT_BDU_MASK               0x80
+#define DEFAULT_MULTIREAD_BIT          1
+#define DEFAULT_IG1_EN_ADDR            0x22
+#define DEFAULT_IG1_EN_MASK            0x10
+#define DEFAULT_IG1_LATCHING_MASK_ADDR 0x24
+#define DEFAULT_IG1_LATCHING_MASK      0x08
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define S1_WAI_EXP                     0x33
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define S2_WAI_EXP                     0x49
+#define S2_ODR_AVL_3HZ                 3
+#define S2_ODR_AVL_3HZ_VALUE           0x01
+#define S2_ODR_AVL_6HZ                 6
+#define S2_ODR_AVL_6HZ_VALUE           0x02
+#define S2_ODR_AVL_12HZ                        12
+#define S2_ODR_AVL_12HZ_VALUE          0x03
+#define S2_ODR_AVL_800HZ               800
+#define S2_ODR_AVL_800HZ_VALUE         0x09
+#define S2_FS_N_BIT                    3
+#define S2_FS_ADDR                     0x21
+#define S2_FS_MASK                     0x38
+#define S2_FS_AVL_2_GAIN               61
+#define S2_FS_AVL_4_GAIN               122
+#define S2_FS_AVL_8_GAIN               244
+#define S2_FS_AVL_16_GAIN              732
+#define S2_FS_AVL_6_NUM                        6
+#define S2_FS_AVL_6_VALUE              0x02
+#define S2_FS_AVL_6_GAIN               183
+#define S2_REALBITS                    16
+#define S2_DRDY_IRQ_MASK               0x04
+#define S2_BDU_ADDR                    0x20
+#define S2_BDU_MASK                    0x08
+
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define S3_WAI_EXP                     0x3a
+#define S3_ODR_MASK                    0x03
+#define S3_ODR_N_BIT                   2
+#define S3_ODR_AVL_40HZ                        40
+#define S3_ODR_AVL_40HZ_VALUE          0x00
+#define S3_ODR_AVL_160HZ               166
+#define S3_ODR_AVL_160HZ_VALUE         0x01
+#define S3_ODR_AVL_640HZ               640
+#define S3_ODR_AVL_640HZ_VALUE         0x02
+#define S3_ODR_AVL_2560HZ              2560
+#define S3_ODR_AVL_2560HZ_VALUE                0x30
+#define S3_POWER_N_BIT                 2
+#define S3_POWER_ADDR                  DEFAULT_ODR_ADDR
+#define S3_POWER_MASK                  0XC0
+#define S3_POWER_ON_VALUE              0x03
+#define S3_POWER_OFF_VALUE             0x00
+#define S3_FS_N_BIT                    1
+#define S3_FS_ADDR                     0x21
+#define S3_FS_MASK                     0x80
+#define S3_FS_AVL_2_NUM                        2
+#define S3_FS_AVL_2_VALUE              0x00
+#define S3_FS_AVL_2_GAIN               976
+#define S3_FS_AVL_6_NUM                        6
+#define S3_FS_AVL_6_VALUE              0x01
+#define S3_FS_AVL_6_GAIN               2941
+#define S3_DRDY_IRQ_ADDR               0x21
+#define S3_DRDY_IRQ_MASK               0x04
+#define S3_BDU_ADDR                    0x21
+#define S3_BDU_MASK                    0x40
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define S4_WAI_EXP                     0x3c
+#define S4_ODR_MASK                    0x18
+#define S4_ODR_N_BIT                   2
+#define S4_ODR_AVL_50HZ_VALUE          0x01
+#define S4_ODR_AVL_100HZ_VALUE         0x02
+#define S4_ODR_AVL_400HZ_VALUE         0x03
+#define S4_ODR_AVL_1000HZ              1000
+#define S4_ODR_AVL_1000HZ_VALUE                0x03
+#define S4_POWER_N_BIT                 3
+#define S4_POWER_MASK                  0xe0
+#define S4_FS_AVL_2_NUM                        2
+#define S4_FS_AVL_2_VALUE              0x00
+#define S4_FS_AVL_2_GAIN               1000
+#define S4_FS_AVL_4_NUM                        4
+#define S4_FS_AVL_4_VALUE              0x01
+#define S4_FS_AVL_4_GAIN               2000
+#define S4_FS_AVL_8_NUM                        8
+#define S4_FS_AVL_8_VALUE              0x03
+#define S4_FS_AVL_8_GAIN               3900
+
+/* CUSTOM VALUES FOR SENSOR 5 */
+#define S5_WAI_EXP                     0x40
+#define S5_ODR_AVL_3HZ                 3
+#define S5_ODR_AVL_3HZ_VALUE           0x01
+#define S5_ODR_AVL_6HZ                 6
+#define S5_ODR_AVL_6HZ_VALUE           0x02
+#define S5_ODR_AVL_12HZ                        12
+#define S5_ODR_AVL_12HZ_VALUE          0x03
+#define S5_ODR_AVL_800HZ               800
+#define S5_ODR_AVL_800HZ_VALUE         0x09
+#define S5_FS_N_BIT                    3
+#define S5_FS_ADDR                     0x24
+#define S5_FS_MASK                     0x38
+#define S5_FS_AVL_2_GAIN               61
+#define S5_FS_AVL_4_GAIN               122
+#define S5_FS_AVL_6_NUM                        6
+#define S5_FS_AVL_6_VALUE              0x02
+#define S5_FS_AVL_6_GAIN               183
+#define S5_FS_AVL_8_GAIN               244
+#define S5_FS_AVL_16_GAIN              732
+#define S5_REALBITS                    16
+#define S5_DRDY_IRQ_ADDR               0x23
+#define S5_DRDY_IRQ_MASK               0x80
+#define S5_BDU_ADDR                    0x20
+#define S5_BDU_MASK                    0x08
+#define S5_MULTIREAD_BIT               0
+#define S5_IG1_EN_ADDR                 0x23
+#define S5_IG1_EN_MASK                 0x08
+#define S5_IG1_LATCHING_MASK_ADDR      0x23
+#define S5_IG1_LATCHING_MASK           0x20
+
+
+struct odr_available {
+       int hz;
+       u8 value;
+};
+
+struct odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct odr_available odr_avl[ODR_LIST_MAX_NUMBER];
+};
+
+struct power {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct fullscale_available {
+       int num;
+       u8 value;
+       int gain;
+};
+
+struct fullscale {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct fullscale_available fs_avl[FULLSCALE_AVAILABLE_MAX_NUMBER];
+};
+
+struct bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct data_out {
+       u8 addr[ACC_NUMBER_DATA_CHANNELS][2];
+       u8 bit;
+       struct bdu bdu;
+};
+
+struct interrupt_generator {
+       u8 en_addr;
+       u8 latching_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct interrupt_generator ig1;
+};
+
+#define LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec default_channels[] =3D {
+       LSM_CHANNELS(ACC_SCAN_X, IIO_MOD_X, IIO_LE,
+                                       DEFAULT_REALBITS, DEFAULT_OUT_X_L),
+       LSM_CHANNELS(ACC_SCAN_Y, IIO_MOD_Y, IIO_LE,
+                                       DEFAULT_REALBITS, DEFAULT_OUT_Y_L),
+       LSM_CHANNELS(ACC_SCAN_Z, IIO_MOD_Z, IIO_LE,
+                                       DEFAULT_REALBITS, DEFAULT_OUT_Z_L),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+static const struct iio_chan_spec s2_channels[] =3D {
+       LSM_CHANNELS(ACC_SCAN_X, IIO_MOD_X, IIO_LE,
+                                       S2_REALBITS, DEFAULT_OUT_X_L),
+       LSM_CHANNELS(ACC_SCAN_Y, IIO_MOD_Y, IIO_LE,
+                                       S2_REALBITS, DEFAULT_OUT_Y_L),
+       LSM_CHANNELS(ACC_SCAN_Z, IIO_MOD_Z, IIO_LE,
+                                       S2_REALBITS, DEFAULT_OUT_Z_L),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+static const struct iio_chan_spec s5_channels[] =3D {
+       LSM_CHANNELS(ACC_SCAN_X, IIO_MOD_X, IIO_LE,
+                                       S5_REALBITS, DEFAULT_OUT_X_L),
+       LSM_CHANNELS(ACC_SCAN_Y, IIO_MOD_Y, IIO_LE,
+                                       S5_REALBITS, DEFAULT_OUT_Y_L),
+       LSM_CHANNELS(ACC_SCAN_Z, IIO_MOD_Z, IIO_LE,
+                                       S5_REALBITS, DEFAULT_OUT_Z_L),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static struct sensor {
+       u8 wai;
+       struct odr odr;
+       struct power pw;
+       struct fullscale fs;
+       struct data_out data;
+       struct data_ready_irq drdy_irq;
+       struct iio_chan_spec *ch;
+} sensor[] =3D {
+       {
+       .wai =3D S1_WAI_EXP,
+       .ch =3D (struct iio_chan_spec *)default_channels,
+       },
+       {
+       .wai =3D S2_WAI_EXP,
+       .ch =3D (struct iio_chan_spec *)s2_channels,
+       .odr =3D {
+               .odr_avl[0] =3D {
+                       .hz =3D S2_ODR_AVL_3HZ,
+                       .value =3D S2_ODR_AVL_3HZ_VALUE,
+               },
+               .odr_avl[1] =3D {
+                       .hz =3D S2_ODR_AVL_6HZ,
+                       .value =3D S2_ODR_AVL_6HZ_VALUE,
+               },
+               .odr_avl[2] =3D {
+                       .hz =3D S2_ODR_AVL_12HZ,
+                       .value =3D S2_ODR_AVL_12HZ_VALUE,
+               },
+               .odr_avl[3] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_25HZ,
+                       .value =3D DEFAULT_ODR_AVL_25HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[4] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_50HZ,
+                       .value =3D DEFAULT_ODR_AVL_50HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[5] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_100HZ,
+                       .value =3D DEFAULT_ODR_AVL_100HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[6] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_200HZ,
+                       .value =3D DEFAULT_ODR_AVL_200HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[7] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_400HZ,
+                       .value =3D DEFAULT_ODR_AVL_400HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[8] =3D {
+                       .hz =3D S2_ODR_AVL_800HZ,
+                       .value =3D S2_ODR_AVL_800HZ_VALUE,
+               },
+               .odr_avl[9] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_1600HZ,
+                       .value =3D DEFAULT_ODR_AVL_1600HZ_VALUE + 2*OFFSET_=
1,
+               },
+       },
+       .fs =3D {
+               .addr =3D S3_FS_ADDR,
+               .mask =3D S3_FS_MASK,
+               .num_bit =3D S3_FS_N_BIT,
+               .fs_avl[0] =3D {
+                       .num =3D DEFAULT_FS_AVL_2_NUM,
+                       .value =3D DEFAULT_FS_AVL_2_VALUE,
+                       .gain =3D S2_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] =3D {
+                       .num =3D DEFAULT_FS_AVL_4_NUM,
+                       .value =3D DEFAULT_FS_AVL_4_VALUE,
+                       .gain =3D S2_FS_AVL_4_GAIN,
+               },
+               .fs_avl[2] =3D {
+                       .num =3D S2_FS_AVL_6_NUM,
+                       .value =3D S2_FS_AVL_6_VALUE,
+                       .gain =3D S2_FS_AVL_6_GAIN,
+               },
+               .fs_avl[3] =3D {
+                       .num =3D DEFAULT_FS_AVL_8_NUM,
+                       .value =3D DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+                       .gain =3D S2_FS_AVL_8_GAIN,
+               },
+               .fs_avl[4] =3D {
+                       .num =3D DEFAULT_FS_AVL_16_NUM,
+                       .value =3D DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+                       .gain =3D S2_FS_AVL_16_GAIN,
+               },
+       },
+       .drdy_irq =3D {
+               .mask =3D S2_DRDY_IRQ_MASK,
+       },
+       .data =3D {
+               .bdu =3D {
+                       .addr =3D S2_BDU_ADDR,
+                       .mask =3D S2_BDU_MASK,
+               },
+       },
+       },
+       {
+       .wai =3D S3_WAI_EXP,
+       .ch =3D (struct iio_chan_spec *)default_channels,
+       .odr =3D {
+               .mask =3D S3_ODR_MASK,
+               .num_bit =3D S3_ODR_N_BIT,
+               .odr_avl[0] =3D {
+                       .hz =3D S3_ODR_AVL_40HZ,
+                       .value =3D S3_ODR_AVL_40HZ_VALUE,
+               },
+               .odr_avl[1] =3D {
+                       .hz =3D S3_ODR_AVL_160HZ,
+                       .value =3D S3_ODR_AVL_160HZ_VALUE,
+               },
+               .odr_avl[2] =3D {
+                       .hz =3D S3_ODR_AVL_640HZ,
+                       .value =3D S3_ODR_AVL_640HZ_VALUE,
+               },
+               .odr_avl[3] =3D {
+                       .hz =3D S3_ODR_AVL_2560HZ,
+                       .value =3D S3_ODR_AVL_2560HZ_VALUE,
+               },
+       },
+       .pw =3D {
+               .addr =3D S3_POWER_ADDR,
+               .mask =3D S3_POWER_MASK,
+               .num_bit =3D S3_POWER_N_BIT,
+               .value_on =3D S3_POWER_ON_VALUE,
+               .value_off =3D S3_POWER_OFF_VALUE,
+       },
+       .fs =3D {
+               .addr =3D S2_FS_ADDR,
+               .mask =3D S2_FS_MASK,
+               .num_bit =3D S2_FS_N_BIT,
+               .fs_avl[0] =3D {
+                       .num =3D S3_FS_AVL_2_NUM,
+                       .value =3D S3_FS_AVL_2_VALUE,
+                       .gain =3D S3_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] =3D {
+                       .num =3D S3_FS_AVL_6_NUM,
+                       .value =3D S3_FS_AVL_6_VALUE,
+                       .gain =3D S3_FS_AVL_6_GAIN,
+               },
+       },
+       .drdy_irq =3D {
+               .addr =3D S3_DRDY_IRQ_ADDR,
+               .mask =3D S3_DRDY_IRQ_MASK,
+       },
+       .data =3D {
+               .bdu =3D {
+                       .addr =3D S3_BDU_ADDR,
+                       .mask =3D S3_BDU_MASK,
+               },
+       },
+       },
+       {
+       .wai =3D S4_WAI_EXP,
+       .ch =3D (struct iio_chan_spec *)default_channels,
+       .odr =3D {
+               .mask =3D S4_ODR_MASK,
+               .num_bit =3D S4_ODR_N_BIT,
+               .odr_avl[0] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_50HZ,
+                       .value =3D S4_ODR_AVL_50HZ_VALUE,
+               },
+               .odr_avl[1] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_100HZ,
+                       .value =3D S4_ODR_AVL_100HZ_VALUE,
+               },
+               .odr_avl[2] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_400HZ,
+                       .value =3D S4_ODR_AVL_400HZ_VALUE,
+               },
+               .odr_avl[3] =3D {
+                       .hz =3D S4_ODR_AVL_1000HZ,
+                       .value =3D S4_ODR_AVL_1000HZ_VALUE,
+               },
+       },
+       .pw =3D {
+               .mask =3D S4_POWER_MASK,
+               .num_bit =3D S4_POWER_N_BIT,
+       },
+       .fs =3D {
+               .fs_avl[0] =3D {
+                       .num =3D S4_FS_AVL_2_NUM,
+                       .value =3D S4_FS_AVL_2_VALUE,
+                       .gain =3D S4_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] =3D {
+                       .num =3D S4_FS_AVL_4_NUM,
+                       .value =3D S4_FS_AVL_4_VALUE,
+                       .gain =3D S4_FS_AVL_4_GAIN,
+               },
+               .fs_avl[1] =3D {
+                       .num =3D S4_FS_AVL_8_NUM,
+                       .value =3D S4_FS_AVL_8_VALUE,
+                       .gain =3D S4_FS_AVL_8_GAIN,
+               },
+       },
+       },
+       {
+       .wai =3D S5_WAI_EXP,
+       .ch =3D (struct iio_chan_spec *)s5_channels,
+       .odr =3D {
+               .odr_avl[0] =3D {
+                       .hz =3D S5_ODR_AVL_3HZ,
+                       .value =3D S5_ODR_AVL_3HZ_VALUE,
+               },
+               .odr_avl[1] =3D {
+                       .hz =3D S5_ODR_AVL_6HZ,
+                       .value =3D S5_ODR_AVL_6HZ_VALUE,
+               },
+               .odr_avl[2] =3D {
+                       .hz =3D S5_ODR_AVL_12HZ,
+                       .value =3D S5_ODR_AVL_12HZ_VALUE,
+               },
+               .odr_avl[3] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_25HZ,
+                       .value =3D DEFAULT_ODR_AVL_25HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[4] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_50HZ,
+                       .value =3D DEFAULT_ODR_AVL_50HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[5] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_100HZ,
+                       .value =3D DEFAULT_ODR_AVL_100HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[6] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_400HZ,
+                       .value =3D DEFAULT_ODR_AVL_400HZ_VALUE,
+               },
+               .odr_avl[7] =3D {
+                       .hz =3D S5_ODR_AVL_800HZ,
+                       .value =3D S5_ODR_AVL_800HZ_VALUE - OFFSET_1,
+               },
+               .odr_avl[8] =3D {
+                       .hz =3D DEFAULT_ODR_AVL_1600HZ,
+                       .value =3D DEFAULT_ODR_AVL_1600HZ_VALUE + OFFSET_1,
+               },
+       },
+       .fs =3D {
+               .addr =3D S5_FS_ADDR,
+               .mask =3D S5_FS_MASK,
+               .num_bit =3D S5_FS_N_BIT,
+               .fs_avl[0] =3D {
+                       .num =3D DEFAULT_FS_AVL_2_NUM,
+                       .value =3D DEFAULT_FS_AVL_2_VALUE,
+                       .gain =3D S5_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] =3D {
+                       .num =3D DEFAULT_FS_AVL_4_NUM,
+                       .value =3D DEFAULT_FS_AVL_4_VALUE,
+                       .gain =3D S5_FS_AVL_4_GAIN,
+               },
+               .fs_avl[2] =3D {
+                       .num =3D S5_FS_AVL_6_NUM,
+                       .value =3D S5_FS_AVL_6_VALUE,
+                       .gain =3D S5_FS_AVL_6_GAIN,
+               },
+               .fs_avl[3] =3D {
+                       .num =3D DEFAULT_FS_AVL_8_NUM,
+                       .value =3D DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+                       .gain =3D S5_FS_AVL_8_GAIN,
+               },
+               .fs_avl[4] =3D {
+                       .num =3D DEFAULT_FS_AVL_16_NUM,
+                       .value =3D DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+                       .gain =3D S5_FS_AVL_16_GAIN,
+               },
+       },
+       .drdy_irq =3D {
+               .addr =3D S5_DRDY_IRQ_ADDR,
+               .mask =3D S5_DRDY_IRQ_MASK,
+               .ig1 =3D {
+                       .en_addr =3D S5_IG1_EN_ADDR,
+                       .en_mask =3D S5_IG1_EN_MASK,
+                       .latching_mask_addr =3D S5_IG1_LATCHING_MASK_ADDR,
+                       .latching_mask =3D S5_IG1_LATCHING_MASK,
+               },
+       },
+       .data =3D {
+               .bdu =3D {
+                       .addr =3D S5_BDU_ADDR,
+                       .mask =3D S5_BDU_MASK,
+               },
+       },
+       }
+};
+
+static struct acc_platform_data acc_default_pdata =3D {
+       .fullscale =3D DEFAULT_FS_AVL_2_NUM,
+       .sampling_frequency =3D DEFAULT_ODR_AVL_100HZ,
+};
+
+static int acc_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr=
,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err, j, pos;
+       u8 prev_data;
+       u8 new_data;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       pos =3D 7;
+       for (j =3D 128; j >=3D 0; j =3D j/2) {
+               if (mask/j > 0)
+                       break;
+               else
+                       pos--;
+       }
+
+       prev_data =3D adata->read_byte(adata, reg_addr);
+       if (prev_data < 0)
+               goto i2c_write_data_with_mask_error;
+       prev_data &=3D 0xff;
+       new_data =3D ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & =
mask));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+       if (err)
+               goto i2c_write_data_with_mask_error;
+
+i2c_write_data_with_mask_error:
+       return err;
+}
+
+static int match_odr(struct sensor *sensor, int odr,
+                                               struct odr_available *odr_o=
ut)
+{
+       int n, i, ret =3D -1;
+
+       n =3D ARRAY_SIZE(sensor->odr.odr_avl);
+       for (i =3D 0; i < n; i++)
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+
+       return ret;
+}
+
+static int match_fs(struct sensor *sensor, int fs,
+                                       struct fullscale_available *fs_out)
+{
+       int n, i, ret =3D -1;
+
+       n =3D ARRAY_SIZE(sensor->fs.fs_avl);
+       for (i =3D 0; i < n; i++)
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+
+       return ret;
+}
+
+int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct acc_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (sensor[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D acc_write_data_with_mask(indio_dev,
+                       sensor[adata->index].drdy_irq.ig1.en_addr,
+                       sensor[adata->index].drdy_irq.ig1.en_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto acc_set_dataready_irq_error;
+       }
+
+       if (sensor[adata->index].drdy_irq.ig1.latching_mask_addr > 0) {
+               err =3D acc_write_data_with_mask(indio_dev,
+                       sensor[adata->index].drdy_irq.ig1.latching_mask_add=
r,
+                       sensor[adata->index].drdy_irq.ig1.latching_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto acc_set_dataready_irq_error;
+       }
+
+       err =3D acc_write_data_with_mask(indio_dev,
+                       sensor[adata->index].drdy_irq.addr,
+                       sensor[adata->index].drdy_irq.mask, 1, (int)enable)=
;
+       if (err < 0)
+               goto acc_set_dataready_irq_error;
+
+       return 0;
+
+acc_set_dataready_irq_error:
+       return -EIO;
+}
+
+static int set_bdu(struct iio_dev *indio_dev, struct bdu *bdu, u8 value)
+{
+       int err;
+
+       err =3D acc_write_data_with_mask(indio_dev,
+                                       bdu->addr,
+                                       bdu->mask,
+                                       1,
+                                       value);
+
+       return err;
+}
+
+static int set_odr(struct iio_dev *indio_dev,
+                                       struct odr_available *odr_available=
)
+{
+       int err;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       if ((sensor[adata->index].odr.addr =3D=3D sensor[adata->index].pw.a=
ddr) &&
+                       (sensor[adata->index].odr.mask =3D=3D
+                               sensor[adata->index].pw.mask)) {
+               if (atomic_read(&adata->enabled) =3D=3D POWER_ON) {
+                       err =3D acc_write_data_with_mask(indio_dev,
+                                       sensor[adata->index].odr.addr,
+                                       sensor[adata->index].odr.mask,
+                                       sensor[adata->index].odr.num_bit,
+                                       odr_available->value);
+                       if (err < 0)
+                               goto set_odr_error;
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D acc_write_data_with_mask(indio_dev,
+                               sensor[adata->index].odr.addr,
+                               sensor[adata->index].odr.mask,
+                               sensor[adata->index].odr.num_bit,
+                               odr_available->value);
+               if (err < 0)
+                       goto set_odr_error;
+       }
+
+set_odr_error:
+       return err;
+}
+
+static int set_enable(struct iio_dev *indio_dev, int enable)
+{
+       int found, err;
+       u8 tmp_value;
+       struct odr_available *odr_out;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+       if (odr_out =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto odr_out_allocate_memory_error;
+       }
+
+       switch (enable) {
+       case POWER_ON:
+               found =3D 0;
+               tmp_value =3D sensor[adata->index].pw.value_on;
+               if ((sensor[adata->index].odr.addr =3D=3D
+                               sensor[adata->index].pw.addr) &&
+                       (sensor[adata->index].odr.mask =3D=3D
+                                       sensor[adata->index].pw.mask)) {
+                       err =3D match_odr(&sensor[adata->index],
+                                                       adata->odr, odr_out=
);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out->value;
+                       found =3D 1;
+               }
+               err =3D acc_write_data_with_mask(indio_dev,
+                               sensor[adata->index].pw.addr,
+                               sensor[adata->index].pw.mask,
+                               sensor[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               atomic_set(&adata->enabled, POWER_ON);
+               if (found =3D=3D 1)
+                       adata->odr =3D odr_out->hz;
+               break;
+       case POWER_OFF:
+               err =3D acc_write_data_with_mask(indio_dev,
+                               sensor[adata->index].pw.addr,
+                               sensor[adata->index].pw.mask,
+                               sensor[adata->index].pw.num_bit,
+                               sensor[adata->index].pw.value_off);
+               if (err < 0)
+                       goto set_enable_error;
+               atomic_set(&adata->enabled, POWER_OFF);
+               break;
+       default:
+               err =3D -1;
+               goto set_enable_error;
+       }
+
+set_enable_error:
+       kfree(odr_out);
+odr_out_allocate_memory_error:
+       return err;
+}
+
+static int set_fullscale(struct iio_dev *indio_dev,
+                                       struct fullscale_available *fs_avl)
+{
+       int err;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       err =3D acc_write_data_with_mask(indio_dev,
+                               sensor[adata->index].fs.addr,
+                               sensor[adata->index].fs.mask,
+                               sensor[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto set_fullscale_error;
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+set_fullscale_error:
+       pr_err("%s: failed to set new fullscale.\n", adata->name);
+       return err;
+}
+
+static int acc_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       int data_tmp;
+       u8 outdata[ACC_BYTE_FOR_CHANNEL];
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+               } else {
+                       err =3D atomic_read(&adata->enabled);
+                       if (!err) {
+                               err =3D -EHOSTDOWN;
+                       } else {
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ACC_BYTE_FOR_CHANNEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               if (ch->scan_type.endianness =3D=3D IIO_LE)
+                                       *val =3D (s32)(s16)(cpu_to_le16(
+                                       le16_to_cpu(((__le16 *)outdata)[0])=
))
+                                       >> ch->scan_type.shift;
+                               else
+                                       *val =3D (s32)(s16)(cpu_to_le16(
+                                       be16_to_cpu(((__be16 *)outdata)[0])=
))
+                                       >> ch->scan_type.shift;
+                       }
+               }
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               data_tmp =3D adata->gain*UG_TO_MS2;
+               *val =3D 0;
+               *val2 =3D data_tmp;
+               return IIO_VAL_INT_PLUS_NANO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       pr_err("%s: failed to read i2c raw data!\n", adata->name);
+       return err;
+}
+
+static int acc_check_irq(struct iio_dev *indio_dev)
+{
+       int err;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       if (adata->irq_data_ready <=3D 0)
+               err =3D -EINVAL;
+       else
+               err =3D 0;
+
+       return err;
+}
+
+static void register_channels(struct iio_dev *indio_dev)
+{
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->channels =3D sensor[adata->index].ch;
+       indio_dev->num_channels =3D ACC_NUMBER_ALL_CHANNELS;
+}
+
+static int validate_platform_data(struct iio_dev *indio_dev)
+{
+       int err;
+       struct odr_available *odr_out;
+       struct fullscale_available *fs_out;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+       if (odr_out =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto odr_out_allocate_memory_error;
+       }
+       fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL);
+       if (fs_out =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto fs_out_allocate_memory_error;
+       }
+       err =3D match_fs(&sensor[adata->index], adata->pdata->fullscale, fs=
_out);
+       if (err < 0) {
+               pr_err("%s: validate fullscale pdata failed!\n", adata->nam=
e);
+               goto validate_fullscale_error;
+       }
+       err =3D match_odr(&sensor[adata->index],
+                               adata->pdata->sampling_frequency, odr_out);
+       if (err < 0) {
+               pr_err("%s: validate sampling frequency pdata failed!\n",
+                                                               adata->name=
);
+               goto validate_sampling_error;
+       }
+       return err;
+
+validate_sampling_error:
+validate_fullscale_error:
+       kfree(fs_out);
+fs_out_allocate_memory_error:
+       kfree(odr_out);
+odr_out_allocate_memory_error:
+       return err;
+}
+
+static int set_platform_data(struct iio_dev *indio_dev)
+{
+       int err;
+       struct acc_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       adata->pdata =3D kmalloc(sizeof(struct acc_platform_data), GFP_KERN=
EL);
+       if (adata->pdata =3D=3D NULL) {
+               pr_err("%s: failed to allocate memory for pdata.\n",
+                                                               adata->name=
);
+               err =3D -ENOMEM;
+               goto pdata_malloc_error;
+       }
+       if (adata->client =3D=3D NULL) {
+               if (adata->spi->dev.platform_data !=3D NULL) {
+                       memcpy(adata->pdata, adata->spi->dev.platform_data,
+                                       sizeof(struct acc_platform_data));
+               } else {
+                       memcpy(adata->pdata, &acc_default_pdata,
+                                       sizeof(struct acc_platform_data));
+               }
+       } else if (adata->spi =3D=3D NULL) {
+               if (adata->client->dev.platform_data !=3D NULL) {
+                       memcpy(adata->pdata, adata->client->dev.platform_da=
ta,
+                                       sizeof(struct acc_platform_data));
+               } else {
+                       memcpy(adata->pdata, &acc_default_pdata,
+                                       sizeof(struct acc_platform_data));
+               }
+       }
+       return 0;
+
+pdata_malloc_error:
+       return err;
+}
+
+static void set_sensor_parameters(struct iio_dev *indio_dev)
+{
+       struct acc_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (!sensor[adata->index].odr.addr)
+               sensor[adata->index].odr.addr =3D DEFAULT_ODR_ADDR;
+       if (!sensor[adata->index].odr.mask)
+               sensor[adata->index].odr.mask =3D DEFAULT_ODR_MASK;
+       if (!sensor[adata->index].odr.num_bit)
+                       sensor[adata->index].odr.num_bit =3D DEFAULT_ODR_N_=
BIT;
+       if (!sensor[adata->index].pw.addr)
+               sensor[adata->index].pw.addr =3D DEFAULT_POWER_ADDR;
+       if (!sensor[adata->index].pw.mask)
+               sensor[adata->index].pw.mask =3D DEFAULT_POWER_MASK;
+       if (!sensor[adata->index].pw.num_bit)
+                       sensor[adata->index].pw.num_bit =3D DEFAULT_POWER_N=
_BIT;
+       if (!sensor[adata->index].pw.value_off)
+               sensor[adata->index].pw.value_off =3D DEFAULT_POWER_OFF_VAL=
UE;
+       if (!sensor[adata->index].pw.value_on)
+               sensor[adata->index].pw.value_on =3D DEFAULT_POWER_ON_VALUE=
;
+       if (!sensor[adata->index].odr.odr_avl[0].hz) {
+               sensor[adata->index].odr.odr_avl[0].hz =3D DEFAULT_ODR_AVL_=
1HZ;
+               sensor[adata->index].odr.odr_avl[0].value =3D
+                                               DEFAULT_ODR_AVL_1HZ_VALUE;
+               sensor[adata->index].odr.odr_avl[1].hz =3D DEFAULT_ODR_AVL_=
10HZ;
+               sensor[adata->index].odr.odr_avl[1].value =3D
+                                               DEFAULT_ODR_AVL_10HZ_VALUE;
+               sensor[adata->index].odr.odr_avl[2].hz =3D DEFAULT_ODR_AVL_=
25HZ;
+               sensor[adata->index].odr.odr_avl[2].value =3D
+                                               DEFAULT_ODR_AVL_25HZ_VALUE;
+               sensor[adata->index].odr.odr_avl[3].hz =3D DEFAULT_ODR_AVL_=
50HZ;
+               sensor[adata->index].odr.odr_avl[3].value =3D
+                                               DEFAULT_ODR_AVL_50HZ_VALUE;
+               sensor[adata->index].odr.odr_avl[4].hz =3D
+                                               DEFAULT_ODR_AVL_100HZ;
+               sensor[adata->index].odr.odr_avl[4].value =3D
+                                               DEFAULT_ODR_AVL_100HZ_VALUE=
;
+               sensor[adata->index].odr.odr_avl[5].hz =3D
+                                               DEFAULT_ODR_AVL_200HZ;
+               sensor[adata->index].odr.odr_avl[5].value =3D
+                                               DEFAULT_ODR_AVL_200HZ_VALUE=
;
+               sensor[adata->index].odr.odr_avl[6].hz =3D
+                                               DEFAULT_ODR_AVL_400HZ;
+               sensor[adata->index].odr.odr_avl[6].value =3D
+                                               DEFAULT_ODR_AVL_400HZ_VALUE=
;
+               sensor[adata->index].odr.odr_avl[7].hz =3D
+                                               DEFAULT_ODR_AVL_1600HZ;
+               sensor[adata->index].odr.odr_avl[7].value =3D
+                                               DEFAULT_ODR_AVL_1600HZ_VALU=
E;
+       }
+       if (!sensor[adata->index].fs.addr)
+               sensor[adata->index].fs.addr =3D DEFAULT_FS_ADDR;
+       if (!sensor[adata->index].fs.mask)
+               sensor[adata->index].fs.mask =3D DEFAULT_FS_MASK;
+       if (!sensor[adata->index].fs.num_bit)
+               sensor[adata->index].fs.num_bit =3D DEFAULT_FS_N_BIT;
+       if (!sensor[adata->index].fs.fs_avl[0].num) {
+               sensor[adata->index].fs.fs_avl[0].num =3D DEFAULT_FS_AVL_2_=
NUM;
+               sensor[adata->index].fs.fs_avl[0].value =3D
+                                               DEFAULT_FS_AVL_2_VALUE;
+               sensor[adata->index].fs.fs_avl[0].gain =3D
+                                               DEFAULT_FS_AVL_2_GAIN;
+               sensor[adata->index].fs.fs_avl[1].num =3D DEFAULT_FS_AVL_4_=
NUM;
+               sensor[adata->index].fs.fs_avl[1].value =3D
+                                               DEFAULT_FS_AVL_4_VALUE;
+               sensor[adata->index].fs.fs_avl[1].gain =3D
+                                               DEFAULT_FS_AVL_4_GAIN;
+               sensor[adata->index].fs.fs_avl[2].num =3D DEFAULT_FS_AVL_8_=
NUM;
+               sensor[adata->index].fs.fs_avl[2].value =3D
+                                               DEFAULT_FS_AVL_8_VALUE;
+               sensor[adata->index].fs.fs_avl[2].gain =3D
+                                               DEFAULT_FS_AVL_8_GAIN;
+               sensor[adata->index].fs.fs_avl[3].num =3D DEFAULT_FS_AVL_16=
_NUM;
+               sensor[adata->index].fs.fs_avl[3].value =3D
+                                               DEFAULT_FS_AVL_16_VALUE;
+               sensor[adata->index].fs.fs_avl[3].gain =3D
+                                               DEFAULT_FS_AVL_16_GAIN;
+       }
+       if (!sensor[adata->index].data.addr[X_AXIS][DATA_LOW])
+               sensor[adata->index].data.addr[X_AXIS][DATA_LOW] =3D
+                                                       (DEFAULT_OUT_X_L);
+       if (!sensor[adata->index].data.addr[X_AXIS][DATA_HIGH])
+               sensor[adata->index].data.addr[X_AXIS][DATA_HIGH] =3D
+                                                       DEFAULT_OUT_X_H;
+       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_LOW])
+               sensor[adata->index].data.addr[Y_AXIS][DATA_LOW] =3D
+                                                       (DEFAULT_OUT_Y_L);
+       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH])
+               sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH] =3D
+                                                       DEFAULT_OUT_Y_H;
+       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_LOW])
+               sensor[adata->index].data.addr[Z_AXIS][DATA_LOW] =3D
+                                                       (DEFAULT_OUT_Z_L);
+       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH])
+               sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH] =3D
+                                                       DEFAULT_OUT_Z_H;
+       if (!sensor[adata->index].drdy_irq.addr)
+               sensor[adata->index].drdy_irq.addr =3D DEFAULT_DRDY_IRQ_ADD=
R;
+       if (!sensor[adata->index].drdy_irq.mask)
+               sensor[adata->index].drdy_irq.mask =3D DEFAULT_DRDY_IRQ_MAS=
K;
+       if (!sensor[adata->index].data.bdu.addr)
+               sensor[adata->index].data.bdu.addr =3D DEFAULT_BDU_ADDR;
+       if (!sensor[adata->index].data.bdu.mask)
+               sensor[adata->index].data.bdu.mask =3D DEFAULT_BDU_MASK;
+       if (!sensor[adata->index].drdy_irq.ig1.en_addr)
+               sensor[adata->index].drdy_irq.ig1.en_addr =3D DEFAULT_IG1_E=
N_ADDR;
+       if (!sensor[adata->index].drdy_irq.ig1.en_mask)
+               sensor[adata->index].drdy_irq.ig1.en_mask =3D DEFAULT_IG1_E=
N_MASK;
+       if (!sensor[adata->index].drdy_irq.ig1.latching_mask_addr)
+               sensor[adata->index].drdy_irq.ig1.latching_mask_addr =3D
+                                               DEFAULT_IG1_LATCHING_MASK_A=
DDR;
+       if (!sensor[adata->index].drdy_irq.ig1.latching_mask)
+               sensor[adata->index].drdy_irq.ig1.latching_mask =3D
+                                               DEFAULT_IG1_LATCHING_MASK;
+}
+
+static int check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i, sensor_length, found;
+       struct acc_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       found =3D 0;
+       sensor_length =3D ARRAY_SIZE(sensor);
+       for (i =3D 0; i < sensor_length; i++) {
+               if (sensor[i].wai =3D=3D wai) {
+                       found =3D 1;
+                       break;
+               }
+       }
+       if (found !=3D 1)
+               goto check_device_error;
+       adata->index =3D i;
+       return i;
+
+check_device_error:
+       pr_err("%s: device not supported -> wai (0x%x).\n", adata->name, wa=
i);
+       return -ENODEV;
+}
+
+static int get_wai_device(struct iio_dev *indio_dev, u8 reg_addr, u8
*value)
+{
+       int ret;
+       u8 buf;
+       struct acc_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       buf =3D reg_addr;
+       ret =3D adata->read_byte(adata, reg_addr);
+       if (ret < 0)
+               goto read_byte_wai_error;
+       *value =3D ret;
+       return (int)ret;
+
+read_byte_wai_error:
+       pr_err("%s: failed to read wai register (0x%x).\n",
+                                                       adata->name, reg_ad=
dr);
+       return -1;
+}
+
+static ssize_t sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned long freq;
+       struct odr_available *odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoul(buf, 10, &freq);
+       if (err) {
+               pr_err("%s: input is not a number! (errn %d).\n",
+                                                       adata->name, err);
+               goto sysfs_set_sampling_frequency_error;
+       }
+
+       mutex_lock(&indio_dev->mlock);
+       odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+       if (odr_out =3D=3D NULL)
+               goto odr_out_allocate_memory_error;
+
+       err =3D match_odr(&sensor[adata->index], (int)freq, odr_out);
+       if (err < 0)
+               goto sysfs_set_sampling_frequency_error;
+       err =3D set_odr(indio_dev, odr_out);
+       if (err < 0) {
+               pr_err("%s: failed to set sampling frequency to %d.\n",
+                                                       adata->name, (int)f=
req);
+               goto sysfs_set_sampling_frequency_error;
+       }
+       adata->odr =3D odr_out->hz;
+       kfree(odr_out);
+
+odr_out_allocate_memory_error:
+       mutex_unlock(&indio_dev->mlock);
+sysfs_set_sampling_frequency_error:
+       return size;
+}
+
+static ssize_t sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       ssize_t ret;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D sprintf(buf, "%d\n", adata->odr);
+
+       return ret;
+}
+
+static ssize_t sysfs_set_enable(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned long en;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoul(buf, 10, &en);
+       if (err) {
+               pr_err("%s: input is not a number! (errn %d).\n",
+                                                       adata->name, err);
+               goto set_enable_error;
+       }
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D set_enable(indio_dev, (int)en);
+       if (err < 0)
+               pr_err("%s: failed to set enable to %d.\n",
+                                                       adata->name, (int)e=
n);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return size;
+}
+
+static ssize_t sysfs_get_enable(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       ssize_t ret;
+       int status;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       status =3D atomic_read(&adata->enabled);
+       ret =3D sprintf(buf, "%d\n", status);
+
+       return ret;
+}
+
+static ssize_t sysfs_get_fullscale(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       ssize_t ret;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D sprintf(buf, "%d\n", adata->fullscale);
+
+       return ret;
+}
+
+static ssize_t sysfs_set_fullscale(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned long fs;
+       struct fullscale_available *fs_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoul(buf, 10, &fs);
+       if (err) {
+               pr_err("%s: input is not a number! (errn %d).\n",
+                                                       adata->name, err);
+               goto set_fullscale_error;
+       }
+
+       mutex_lock(&indio_dev->mlock);
+       fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL);
+       if (fs_out =3D=3D NULL)
+               goto fs_out_allocate_memory_error;
+
+       err =3D match_fs(&sensor[adata->index], fs, fs_out);
+       if (err < 0) {
+               pr_err("%s: input is not a valid fullscale! (errn %d).\n",
+                                                       adata->name, err);
+               goto match_fullscale_error;
+       }
+       err =3D set_fullscale(indio_dev, fs_out);
+       if (err < 0) {
+               pr_err("%s: failed to set new fullscale. (errn %d).\n",
+                                                       adata->name, err);
+       }
+
+match_fullscale_error:
+       kfree(fs_out);
+fs_out_allocate_memory_error:
+       mutex_unlock(&indio_dev->mlock);
+set_fullscale_error:
+       return size;
+}
+
+static ssize_t sysfs_fullscale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, n, len;
+       char tmp[4];
+       char fullscale[30] =3D "";
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       n =3D ARRAY_SIZE(sensor[adata->index].fs.fs_avl);
+       for (i =3D 0; i < n; i++) {
+               if (sensor[adata->index].fs.fs_avl[i].num !=3D 0) {
+                       len =3D strlen(&fullscale[0]);
+                       sprintf(tmp, "%d ",
+                               sensor[adata->index].fs.fs_avl[i].num);
+                       strcpy(&fullscale[len], tmp);
+               } else
+                       break;
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       return sprintf(buf, "%s\n", fullscale);
+}
+
+static ssize_t sysfs_sampling_frequency_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, n, len;
+       char tmp[4];
+       char sampling_frequency[30] =3D "";
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       n =3D ARRAY_SIZE(sensor[adata->index].odr.odr_avl);
+       for (i =3D 0; i < n; i++) {
+               if (sensor[adata->index].odr.odr_avl[i].hz !=3D 0) {
+                       len =3D strlen(&sampling_frequency[0]);
+                       sprintf(tmp, "%d ",
+                               sensor[adata->index].odr.odr_avl[i].hz);
+                       strcpy(&sampling_frequency[len], tmp);
+               } else
+                       break;
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       return sprintf(buf, "%s\n", sampling_frequency);
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+                               sysfs_sampling_frequency_available, NULL , =
0);
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+                               sysfs_fullscale_available, NULL , 0);
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, sysfs_get_fullscale,
+                               sysfs_set_fullscale , 0);
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, sysfs_get_enable,
+                                               sysfs_set_enable , 0);
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
sysfs_get_sampling_frequency,
+                               sysfs_set_sampling_frequency);
+
+static struct attribute *acc_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_fullscale_available.dev_attr.attr,
+       &iio_dev_attr_fullscale.dev_attr.attr,
+       &iio_dev_attr_enable.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group acc_attribute_group =3D {
+       .attrs =3D acc_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &acc_attribute_group,
+       .read_raw =3D &acc_read_raw,
+};
+
+static int init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct odr_available *odr_out;
+       struct fullscale_available *fs_out;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+       if (odr_out =3D=3D NULL)
+               goto odr_out_allocate_memory_error;
+       fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL);
+       if (fs_out =3D=3D NULL)
+               goto fs_out_allocate_memory_error;
+
+       adata->fullscale =3D adata->pdata->fullscale;
+       adata->odr =3D adata->pdata->sampling_frequency;
+       set_enable(indio_dev, POWER_OFF);
+       match_fs(&sensor[adata->index], adata->fullscale, fs_out);
+       err =3D set_fullscale(indio_dev, fs_out);
+       if (err < 0)
+               goto init_error;
+       match_odr(&sensor[adata->index], adata->odr, odr_out);
+       err =3D set_odr(indio_dev, odr_out);
+       if (err < 0)
+               goto init_error;
+       err =3D set_bdu(indio_dev, &sensor[adata->index].data.bdu,
+                                               (u8)DEFAULT_POWER_ON_VALUE)=
;
+       if (err < 0)
+               goto init_error;
+       kfree(odr_out);
+       kfree(fs_out);
+
+       return 0;
+
+init_error:
+       kfree(fs_out);
+fs_out_allocate_memory_error:
+       kfree(odr_out);
+odr_out_allocate_memory_error:
+       return -EIO;
+}
+
+int acc_iio_default(struct iio_dev *indio_dev)
+{
+       int err;
+       u8 wai;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       mutex_init(&adata->slock);
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       err =3D get_wai_device(indio_dev, DEFAULT_WAI_ADDRESS, &wai);
+       if (err < 0)
+               goto get_wai_error;
+       err =3D check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto check_device_list_error;
+       set_sensor_parameters(indio_dev);
+       err =3D set_platform_data(indio_dev);
+       if (err < 0)
+               goto set_platform_data_error;
+       err =3D validate_platform_data(indio_dev);
+       if (err < 0)
+               goto validate_platform_data_error;
+       register_channels(indio_dev);
+
+       err =3D init_sensor(indio_dev);
+       if (err < 0)
+               goto init_sensor_error;
+
+       if (sensor[adata->index].wai =3D=3D S5_WAI_EXP)
+               adata->multiread_bit =3D 0;
+       else
+               adata->multiread_bit =3D 1;
+
+       err =3D acc_check_irq(indio_dev);
+       if (err < 0)
+               goto gpio_check_error;
+       err =3D acc_allocate_ring(indio_dev);
+       if (err < 0)
+               goto acc_allocate_ring_error;
+
+       err =3D acc_probe_trigger(indio_dev);
+       if (err < 0)
+               goto acc_probe_trigger_error;
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       pr_info("%s: probe end correctly.\n", adata->name);
+
+       return err;
+
+iio_device_register_error:
+       acc_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+       acc_deallocate_ring(indio_dev);
+acc_allocate_ring_error:
+gpio_check_error:
+init_sensor_error:
+validate_platform_data_error:
+       kfree(adata->pdata);
+set_platform_data_error:
+check_device_list_error:
+get_wai_error:
+       return err;
+}
+
+int acc_iio_remove(struct iio_dev *indio_dev)
+{
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       acc_remove_trigger(indio_dev);
+       acc_deallocate_ring(indio_dev);
+       kfree(adata->pdata);
+       iio_device_unregister(indio_dev);
+
+       return 0;
+}
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
new file mode 100644
index 0000000..f443e52
--- /dev/null
+++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
@@ -0,0 +1,124 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
+
+
+#define        ACC_I2C_MULTIREAD       0x80
+
+
+static inline s32 acc_i2c_read_byte(struct acc_data *adata, u8 reg_addr)
+{
+       return i2c_smbus_read_byte_data(adata->client, reg_addr);
+}
+
+static inline s32 acc_i2c_read_multiple_byte(struct acc_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit !=3D 0)
+               reg_addr |=3D ACC_I2C_MULTIREAD;
+       return i2c_smbus_read_i2c_block_data(adata->client,
+                                                       reg_addr, len, data=
);
+}
+
+static inline s32 acc_i2c_write_byte(struct acc_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(adata->client, reg_addr, data);
+}
+
+static int __devinit acc_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct acc_data *adata;
+       int err;
+
+       pr_info("%s: probe start.\n", client->name);
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->client =3D client;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D acc_i2c_read_byte;
+       adata->write_byte =3D acc_i2c_write_byte;
+       adata->read_multiple_byte =3D acc_i2c_read_multiple_byte;
+       adata->name =3D &client->name[0];
+       adata->irq_data_ready =3D &client->irq;
+
+       err =3D acc_iio_default(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit acc_i2c_remove(struct i2c_client *client)
+{
+       int err;
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+
+       err =3D acc_iio_remove(indio_dev);
+       iio_device_free(indio_dev);
+       return err;
+}
+
+static const struct i2c_device_id acc_id_table[] =3D {
+       { LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+       { LIS3DH_ACC_IIO_DEV_NAME, 0 },
+       { LSM330D_ACC_IIO_DEV_NAME, 0 },
+       { LSM330DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+       { LSM303D_ACC_IIO_DEV_NAME, 0 },
+       { LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+       { LIS3LV02DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+       { LSM330_ACC_IIO_DEV_NAME, 0 },
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, acc_id_table);
+
+static struct i2c_driver acc_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "STMicroelectronics i2c accelerometers",
+       },
+       .probe =3D acc_i2c_probe,
+       .remove =3D __devexit_p(acc_i2c_remove),
+       .id_table =3D acc_id_table,
+};
+
+module_i2c_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
new file mode 100644
index 0000000..f1ac211
--- /dev/null
+++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
@@ -0,0 +1,209 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+
+static inline s32 acc_spi_read_byte(struct acc_data *adata, u8 reg_addr)
+{
+       struct spi_message msg;
+       int err;
+       u8 res;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+                       .cs_change =3D 0,
+                       .delay_usecs =3D 10,
+               },
+               {
+                       .rx_buf =3D &res,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+                       .delay_usecs =3D 10,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx =3D reg_addr | ACC_SPI_READ;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(adata->spi, &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static inline s32 acc_spi_read_multiple_byte(struct acc_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+                       .cs_change =3D 0,
+                       .delay_usecs =3D 10,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+                       .cs_change =3D 0,
+                       .delay_usecs =3D 10,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       if (adata->multiread_bit !=3D 0)
+               tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx =3D reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(adata->spi, &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static inline s32 acc_spi_write_byte(struct acc_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2];
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 2,
+                       .cs_change =3D 0,
+                       .delay_usecs =3D 10,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       err =3D spi_sync(adata->spi, &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static int __devinit acc_spi_probe(struct spi_device *client)
+{
+       struct iio_dev *indio_dev;
+       struct acc_data *adata;
+       int err;
+
+       pr_info("%s: probe start.\n", client->modalias);
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->spi =3D client;
+       spi_set_drvdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->modalias;
+
+       adata->read_byte =3D acc_spi_read_byte;
+       adata->write_byte =3D acc_spi_write_byte;
+       adata->read_multiple_byte =3D acc_spi_read_multiple_byte;
+       adata->name =3D &client->modalias[0];
+       adata->irq_data_ready =3D &client->irq;
+
+       /* dummy read */
+       adata->read_byte(adata, 0x0f);
+
+       err =3D acc_iio_default(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit acc_spi_remove(struct spi_device *spi)
+{
+       int err;
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+
+       err =3D acc_iio_remove(indio_dev);
+       iio_device_free(indio_dev);
+
+       return 0;
+}
+
+static const struct spi_device_id acc_id_table[] =3D {
+       { LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+       { LIS3DH_ACC_IIO_DEV_NAME, 0 },
+       { LSM330D_ACC_IIO_DEV_NAME, 0 },
+       { LSM330DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+       { LSM303D_ACC_IIO_DEV_NAME, 0 },
+       { LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+       { LIS3LV02DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DL_ACC_IIO_DEV_NAME, 0 },
+       { LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+       { LSM330_ACC_IIO_DEV_NAME, 0 },
+       {},
+};
+
+MODULE_DEVICE_TABLE(spi, acc_id_table);
+
+static struct spi_driver acc_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "STMicroelectronics spi accelerometers",
+       },
+       .probe =3D acc_spi_probe,
+       .remove =3D __devexit_p(acc_spi_remove),
+       .id_table =3D acc_id_table,
+};
+
+module_spi_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
new file mode 100644
index 0000000..843af4c
--- /dev/null
+++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
@@ -0,0 +1,96 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
+
+
+static irqreturn_t acc_data_ready_trig_poll(int irq, void *trig)
+{
+       iio_trigger_poll(trig, iio_get_time_ns());
+       return IRQ_HANDLED;
+}
+
+static int iio_trig_acc_set_state(struct iio_trigger *trig, bool state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return acc_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops iio_acc_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &iio_trig_acc_set_state,
+};
+
+int acc_probe_trigger(struct iio_dev *indio_dev)
+{
+       int err;
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               pr_err("%s: failed to allocate iio trigger.\n", adata->name=
);
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(*adata->irq_data_ready,
+                       acc_data_ready_trig_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       "IRQ_data_ready",
+                       adata->trig);
+       if (err) {
+               pr_err("%s: failed to request threaded irq [%d].\n",
+                                       adata->name, *adata->irq_data_ready=
);
+               goto request_irq_error;
+       }
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &iio_acc_trigger_ops;
+
+       if (adata->client !=3D NULL)
+               adata->trig->dev.parent =3D &adata->client->dev;
+       else
+               adata->trig->dev.parent =3D &adata->spi->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               pr_err("%s: failed to register iio trigger.\n", adata->name=
);
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+       pr_info("%s: using [%s] trigger.\n", adata->name,
+                                                       adata->trig->name);
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(*adata->irq_data_ready, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+
+int acc_remove_trigger(struct iio_dev *indio_dev)
+{
+       struct acc_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(*adata->irq_data_ready, adata->trig);
+       iio_trigger_free(adata->trig);
+
+       return 0;
+}
--
1.7.0.4


 From 1fe8b75e0ec1197781c559935de23e116773c441 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 8 Oct 2012 17:08:43 +0200
Subject: [PATCH 2/2] add header file

---
  .../acc/STMicroelectronics_accelerometers_iio.h    |  116
++++++++++++++++++++
  1 files changed, 116 insertions(+), 0 deletions(-)
  create mode 100644
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h

diff --git
a/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
new file mode 100644
index 0000000..e6f18ad
--- /dev/null
+++ b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
@@ -0,0 +1,116 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Supported sensors:
+ * LSM303DLH
+ * LSM303DLHC
+ * LIS3DH
+ * LSM330D
+ * LSM330DL
+ * LSM330DLC
+ * LSM303D
+ * LSM9DS0
+ * LIS3LV02DL
+ * LIS331DLH
+ * LSM303DL
+ * LSM303DLM
+ * LSM330
+ */
+
+#include <linux/spi/spi.h>
+
+#ifndef ST_ACCELEROMETERS_IIO_ACC_H
+#define ST_ACCELEROMETERS_IIO_ACC_H
+
+#define LSM303DLH_ACC_IIO_DEV_NAME     "lsm303dlh_acc_iio"
+#define LSM303DLHC_ACC_IIO_DEV_NAME    "lsm303dlhc_acc_iio"
+#define LIS3DH_ACC_IIO_DEV_NAME                "lis3dh_acc_iio"
+#define LSM330D_ACC_IIO_DEV_NAME       "lsm330d_acc_iio"
+#define LSM330DL_ACC_IIO_DEV_NAME      "lsm330dl_acc_iio"
+#define LSM330DLC_ACC_IIO_DEV_NAME     "lsm330dlc_acc_iio"
+#define LSM303D_ACC_IIO_DEV_NAME       "lsm303d_acc_iio"
+#define LSM9DS0_ACC_IIO_DEV_NAME       "lsm9ds0_acc_iio"
+#define LIS3LV02DL_ACC_IIO_DEV_NAME    "lis3lv02dl_acc_iio"
+#define LSM303DL_ACC_IIO_DEV_NAME      "lsm303dl_acc_iio"
+#define LSM303DLM_ACC_IIO_DEV_NAME     "lsm303dlm_acc_iio"
+#define LSM330_ACC_IIO_DEV_NAME                "lsm330_acc_iio"
+
+#define ACC_NUMBER_ALL_CHANNELS                4
+#define ACC_NUMBER_DATA_CHANNELS       3
+#define ACC_SCAN_X                     0
+#define ACC_SCAN_Y                     1
+#define ACC_SCAN_Z                     2
+#define ACC_SCAN_TIMESTAMP             3
+#define ACC_BYTE_FOR_CHANNEL           2
+
+
+struct acc_platform_data {
+       int fullscale;
+       int sampling_frequency;
+};
+
+struct acc_data {
+       struct i2c_client *client;
+       struct spi_device *spi;
+       struct acc_platform_data *pdata;
+       char *name;
+
+       short index;
+
+       atomic_t enabled;
+       int fullscale;
+       int gain;
+       int odr;
+
+       int multiread_bit;
+       int (*read_byte) (struct acc_data *adata, u8 reg_addr);
+       int (*write_byte) (struct acc_data *adata, u8 reg_addr, u8 data);
+       int (*read_multiple_byte) (struct acc_data *adata, u8 reg_addr,
+                                                       int len, u8 *data);
+
+#ifdef CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO
+       struct iio_trigger *trig;
+#endif /* CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO */
+
+       int *irq_data_ready;
+       struct mutex slock;
+
+};
+
+int acc_iio_default(struct iio_dev *indio_dev);
+int acc_iio_remove(struct iio_dev *indio_dev);
+
+#ifdef CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO
+int acc_probe_trigger(struct iio_dev *indio_dev);
+int acc_remove_trigger(struct iio_dev *indio_dev);
+int acc_allocate_ring(struct iio_dev *indio_dev);
+void acc_deallocate_ring(struct iio_dev *indio_dev);
+int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+#else
+static inline int acc_probe_trigger(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline int acc_remove_trigger(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline int acc_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void acc_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO */
+
+#endif /* ST_ACCELEROMETERS_IIO_ACC_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-08 15:39 STMicroelectronics accelerometers driver Denis CIOCCA
@ 2012-10-08 19:14 ` Lars-Peter Clausen
  2012-10-08 19:50   ` Pavel Machek
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-08 19:14 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: linux-iio, Jonathan Cameron, burman.yan, pavel

On 10/08/2012 05:39 PM, Denis CIOCCA wrote:
> Hi everybody,
> 
> I submit to you my linux device driver to support the latest ST
> accelerometers. I want do submission to the linux community and I ask
> you suggestions and proposals.
> Thanks
> 
> Denis
> 

Hi just a quick and rough review. One general thing: It would be good if you
would namespace your function names, struct names, defines, etc. Currently some
of them aren't namespaced at all and some just use 'accel'. Maybe use
"st_accel" instead.

> 
>  From c965b7f522d858a48e3bbcc723cb2ff4c00397f4 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 8 Oct 2012 17:04:32 +0200
> Subject: [PATCH 1/2] add driver

You need a better patch description than this ;)

> 
> ---
>   .../STMicroelectronics_accelerometers_iio_buffer.c |  155 ++
>   .../STMicroelectronics_accelerometers_iio_core.c   | 1495
> ++++++++++++++++++++
>   .../STMicroelectronics_accelerometers_iio_i2c.c    |  124 ++
>   .../STMicroelectronics_accelerometers_iio_spi.c    |  209 +++
>   ...STMicroelectronics_accelerometers_iio_trigger.c |   96 ++
>   5 files changed, 2079 insertions(+), 0 deletions(-)
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c

the file names are a bit excessive in my opinion. How about
st_accel_{buffer,core,i2c,...}.c?

> 
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> new file mode 100644
> index 0000000..44e3f3d
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> @@ -0,0 +1,155 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +static int acc_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +       int len;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       len = adata->read_multiple_byte(adata,
> +               indio_dev->channels[ACC_SCAN_X].address,
> +               ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS, rx_array);
> +       if (len < 0)
> +               goto read_error;
> +
> +       return len;
> +
> +read_error:
> +       return -EIO;
> +}
> +
> +static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, n = 0;
> +       u8 *rx_array;
> +       u8 mask = 0x01;
> +       s16 *data = (s16 *)buf;
> +
> +       rx_array = kzalloc(ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS,
> +                                                               GFP_KERNEL);
> +       if (rx_array == NULL)
> +               return -ENOMEM;
> +       ret = acc_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +       for (i = 0; i < ACC_NUMBER_DATA_CHANNELS; i++) {
> +               if ((*indio_dev->active_scan_mask & mask) > 0) {
> +                       if (indio_dev->channels[i].scan_type.endianness ==
> +                                                               IIO_LE) {
> +                               data[n] = (s16)(cpu_to_le16(le16_to_cpu((
> +                               (__le16 *)rx_array)[i]))) >>
> +                               indio_dev->channels[i].scan_type.shift;
> +                               n++;
> +                       } else {
> +                               data[n] = (s16)(cpu_to_le16(be16_to_cpu((
> +                               (__be16 *)rx_array)[i]))) >>
> +                               indio_dev->channels[i].scan_type.shift;
> +                               n++;
> +                       }
> +               }
> +               mask = mask << 1;
> +       }


In buffered mode the samples should not be postprocessed. Userspace expects the
samples in the format as described by the scan_type. The buffer demuxing should
be handled by the IIO core. If you set up available_scan_masks correctly it
will automatically do the right thing.

> +       kfree(rx_array);
> +       return i*sizeof(data[0]);
> +}
> +
[...]
> +
> +int acc_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct iio_buffer *buffer;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       buffer = iio_kfifo_allocate(indio_dev);
> +       if (buffer == NULL) {
> +               ret = -ENOMEM;
> +               goto acc_configure_ring_error;
> +       }
> +       indio_dev->buffer = buffer;
> +       indio_dev->scan_timestamp = true;
> +       indio_dev->buffer->scan_timestamp = true;
> +       indio_dev->setup_ops = &acc_buf_setup_ops;
> +       indio_dev->pollfunc = iio_alloc_pollfunc(
> +                                               &iio_pollfunc_store_time,
> +                                               &acc_trigger_handler,
> +                                               IRQF_ONESHOT,
> +                                               indio_dev,
> +                                               "%s_consumer%d",
> +                                               indio_dev->name,
> +                                               indio_dev->id);
> +       if (indio_dev->pollfunc == NULL) {
> +               ret = -ENOMEM;
> +               goto iio_alloc_pollfunc_error;
> +       }
> +       indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
> +       ret = iio_buffer_register(indio_dev, indio_dev->channels,
> +                                               ACC_NUMBER_ALL_CHANNELS);
> +       if (ret < 0) {
> +               pr_err("%s: iio_buffer_register failed.\n", adata->name);
> +               goto iio_buffer_register_error;
> +       }
> +       pr_info("%s: allocate buffer -> done.\n", adata->name);


This looks like you could make use of the generic triggered_buffer code. Please
have a look at the different drivers which already make use of it.

> +       return 0;
> +
> +iio_buffer_register_error:
> +iio_alloc_pollfunc_error:
> +       iio_kfifo_free(buffer);
> +acc_configure_ring_error:
> +       return ret;
> +}
> +
> +void acc_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_dealloc_pollfunc(indio_dev->pollfunc);
> +       iio_kfifo_free(indio_dev->buffer);
> +       iio_buffer_unregister(indio_dev);
> +}
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> new file mode 100644
> index 0000000..f9ccbf6
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> @@ -0,0 +1,1495 @@
[...]
> +static struct sensor {
> +       u8 wai;
> +       struct odr odr;
> +       struct power pw;
> +       struct fullscale fs;
> +       struct data_out data;
> +       struct data_ready_irq drdy_irq;
> +       struct iio_chan_spec *ch;
> +} sensor[] = {
> +       {
> +       .wai = S1_WAI_EXP,
> +       .ch = (struct iio_chan_spec *)default_channels,
> +       },
> +       {
> +       .wai = S2_WAI_EXP,
> +       .ch = (struct iio_chan_spec *)s2_channels,
> +       .odr = {
> +               .odr_avl[0] = {

I'd use
 .odr_avl = {
	[0] = {
		...
	},
	[1] = {
		...

Makes things a bit more readable in my opinion.
	

> +                       .hz = S2_ODR_AVL_3HZ,
> +                       .value = S2_ODR_AVL_3HZ_VALUE,
> +               },
[...]
> +
> +static int acc_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err, j, pos;
> +       u8 prev_data;
> +       u8 new_data;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       pos = 7;
> +       for (j = 128; j >= 0; j = j/2) {

Missing spaces, please run checkpatch.pl before submitting a patch

> +               if (mask/j > 0)

Missing space

> +                       break;
> +               else
> +                       pos--;
> +       }
> +
> +       prev_data = adata->read_byte(adata, reg_addr);
> +       if (prev_data < 0)
> +               goto i2c_write_data_with_mask_error;
> +       prev_data &= 0xff;
> +       new_data = ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & mask));

Missing spaces

> +       err = adata->write_byte(adata, reg_addr, new_data);
> +       if (err)
> +               goto i2c_write_data_with_mask_error;

The label you jump to here is just on the next line...

> +
> +i2c_write_data_with_mask_error:
> +       return err;
> +}
> +
[...]


> +static int acc_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       int data_tmp;
> +       u8 outdata[ACC_BYTE_FOR_CHANNEL];
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +               } else {
> +                       err = atomic_read(&adata->enabled);
> +                       if (!err) {
> +                               err = -EHOSTDOWN;

"Host is down", I don't think this is best error message here. Also is there
any reason why you can't powerup/powerdown the device on demand?


> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ACC_BYTE_FOR_CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               if (ch->scan_type.endianness == IIO_LE)
> +                                       *val = (s32)(s16)(cpu_to_le16(
> +                                       le16_to_cpu(((__le16 *)outdata)[0])))
> +                                       >> ch->scan_type.shift;
> +                               else
> +                                       *val = (s32)(s16)(cpu_to_le16(
> +                                       be16_to_cpu(((__be16 *)outdata)[0])))
> +                                       >> ch->scan_type.shift;
> +                       }
> +               }
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               data_tmp = adata->gain*UG_TO_MS2;
> +               *val = 0;
> +               *val2 = data_tmp;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       pr_err("%s: failed to read i2c raw data!\n", adata->name);
> +       return err;
> +}
> +
> +static int acc_check_irq(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       if (adata->irq_data_ready <= 0)

This will always be true since you made irq_data_ready a pointer, doesn't the
compiler complain here?

> +               err = -EINVAL;
> +       else
> +               err = 0;
> +
> +       return err;
> +}
> +
> +
> +static int set_platform_data(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       adata->pdata = kmalloc(sizeof(struct acc_platform_data), GFP_KERNEL);
> +       if (adata->pdata == NULL) {
> +               pr_err("%s: failed to allocate memory for pdata.\n",
> +                                                               adata->name);
> +               err = -ENOMEM;
> +               goto pdata_malloc_error;
> +       }
> +       if (adata->client == NULL) {
> +               if (adata->spi->dev.platform_data != NULL) {
> +                       memcpy(adata->pdata, adata->spi->dev.platform_data,
> +                                       sizeof(struct acc_platform_data));
> +               } else {
> +                       memcpy(adata->pdata, &acc_default_pdata,
> +                                       sizeof(struct acc_platform_data));
> +               }
> +       } else if (adata->spi == NULL) {
> +               if (adata->client->dev.platform_data != NULL) {
> +                       memcpy(adata->pdata, adata->client->dev.platform_data,
> +                                       sizeof(struct acc_platform_data));
> +               } else {
> +                       memcpy(adata->pdata, &acc_default_pdata,
> +                                       sizeof(struct acc_platform_data));
> +               }
> +       }
> +       return 0;
> +
> +pdata_malloc_error:
> +       return err;
> +}

Is platform_data really used outside of probe? If not you could get rid of this.

> +
> +static void set_sensor_parameters(struct iio_dev *indio_dev)
> +{
> +       struct acc_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       if (!sensor[adata->index].odr.addr)
> +               sensor[adata->index].odr.addr = DEFAULT_ODR_ADDR;
> +       if (!sensor[adata->index].odr.mask)
> +               sensor[adata->index].odr.mask = DEFAULT_ODR_MASK;
> +       if (!sensor[adata->index].odr.num_bit)
> +                       sensor[adata->index].odr.num_bit = DEFAULT_ODR_N_BIT;
> +       if (!sensor[adata->index].pw.addr)
> +               sensor[adata->index].pw.addr = DEFAULT_POWER_ADDR;
> +       if (!sensor[adata->index].pw.mask)
> +               sensor[adata->index].pw.mask = DEFAULT_POWER_MASK;
> +       if (!sensor[adata->index].pw.num_bit)
> +                       sensor[adata->index].pw.num_bit = DEFAULT_POWER_N_BIT;
> +       if (!sensor[adata->index].pw.value_off)
> +               sensor[adata->index].pw.value_off = DEFAULT_POWER_OFF_VALUE;
> +       if (!sensor[adata->index].pw.value_on)
> +               sensor[adata->index].pw.value_on = DEFAULT_POWER_ON_VALUE;
> +       if (!sensor[adata->index].odr.odr_avl[0].hz) {
> +               sensor[adata->index].odr.odr_avl[0].hz = DEFAULT_ODR_AVL_1HZ;
> +               sensor[adata->index].odr.odr_avl[0].value =
> +                                               DEFAULT_ODR_AVL_1HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[1].hz = DEFAULT_ODR_AVL_10HZ;
> +               sensor[adata->index].odr.odr_avl[1].value =
> +                                               DEFAULT_ODR_AVL_10HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[2].hz = DEFAULT_ODR_AVL_25HZ;
> +               sensor[adata->index].odr.odr_avl[2].value =
> +                                               DEFAULT_ODR_AVL_25HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[3].hz = DEFAULT_ODR_AVL_50HZ;
> +               sensor[adata->index].odr.odr_avl[3].value =
> +                                               DEFAULT_ODR_AVL_50HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[4].hz =
> +                                               DEFAULT_ODR_AVL_100HZ;
> +               sensor[adata->index].odr.odr_avl[4].value =
> +                                               DEFAULT_ODR_AVL_100HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[5].hz =
> +                                               DEFAULT_ODR_AVL_200HZ;
> +               sensor[adata->index].odr.odr_avl[5].value =
> +                                               DEFAULT_ODR_AVL_200HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[6].hz =
> +                                               DEFAULT_ODR_AVL_400HZ;
> +               sensor[adata->index].odr.odr_avl[6].value =
> +                                               DEFAULT_ODR_AVL_400HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[7].hz =
> +                                               DEFAULT_ODR_AVL_1600HZ;
> +               sensor[adata->index].odr.odr_avl[7].value =
> +                                               DEFAULT_ODR_AVL_1600HZ_VALUE;
> +       }
> +       if (!sensor[adata->index].fs.addr)
> +               sensor[adata->index].fs.addr = DEFAULT_FS_ADDR;
> +       if (!sensor[adata->index].fs.mask)
> +               sensor[adata->index].fs.mask = DEFAULT_FS_MASK;
> +       if (!sensor[adata->index].fs.num_bit)
> +               sensor[adata->index].fs.num_bit = DEFAULT_FS_N_BIT;
> +       if (!sensor[adata->index].fs.fs_avl[0].num) {
> +               sensor[adata->index].fs.fs_avl[0].num = DEFAULT_FS_AVL_2_NUM;
> +               sensor[adata->index].fs.fs_avl[0].value =
> +                                               DEFAULT_FS_AVL_2_VALUE;
> +               sensor[adata->index].fs.fs_avl[0].gain =
> +                                               DEFAULT_FS_AVL_2_GAIN;
> +               sensor[adata->index].fs.fs_avl[1].num = DEFAULT_FS_AVL_4_NUM;
> +               sensor[adata->index].fs.fs_avl[1].value =
> +                                               DEFAULT_FS_AVL_4_VALUE;
> +               sensor[adata->index].fs.fs_avl[1].gain =
> +                                               DEFAULT_FS_AVL_4_GAIN;
> +               sensor[adata->index].fs.fs_avl[2].num = DEFAULT_FS_AVL_8_NUM;
> +               sensor[adata->index].fs.fs_avl[2].value =
> +                                               DEFAULT_FS_AVL_8_VALUE;
> +               sensor[adata->index].fs.fs_avl[2].gain =
> +                                               DEFAULT_FS_AVL_8_GAIN;
> +               sensor[adata->index].fs.fs_avl[3].num = DEFAULT_FS_AVL_16_NUM;
> +               sensor[adata->index].fs.fs_avl[3].value =
> +                                               DEFAULT_FS_AVL_16_VALUE;
> +               sensor[adata->index].fs.fs_avl[3].gain =
> +                                               DEFAULT_FS_AVL_16_GAIN;
> +       }
> +       if (!sensor[adata->index].data.addr[X_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[X_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_X_L);
> +       if (!sensor[adata->index].data.addr[X_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[X_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_X_H;
> +       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[Y_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_Y_L);
> +       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_Y_H;
> +       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[Z_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_Z_L);
> +       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_Z_H;
> +       if (!sensor[adata->index].drdy_irq.addr)
> +               sensor[adata->index].drdy_irq.addr = DEFAULT_DRDY_IRQ_ADDR;
> +       if (!sensor[adata->index].drdy_irq.mask)
> +               sensor[adata->index].drdy_irq.mask = DEFAULT_DRDY_IRQ_MASK;
> +       if (!sensor[adata->index].data.bdu.addr)
> +               sensor[adata->index].data.bdu.addr = DEFAULT_BDU_ADDR;
> +       if (!sensor[adata->index].data.bdu.mask)
> +               sensor[adata->index].data.bdu.mask = DEFAULT_BDU_MASK;
> +       if (!sensor[adata->index].drdy_irq.ig1.en_addr)
> +               sensor[adata->index].drdy_irq.ig1.en_addr = DEFAULT_IG1_EN_ADDR;
> +       if (!sensor[adata->index].drdy_irq.ig1.en_mask)
> +               sensor[adata->index].drdy_irq.ig1.en_mask = DEFAULT_IG1_EN_MASK;
> +       if (!sensor[adata->index].drdy_irq.ig1.latching_mask_addr)
> +               sensor[adata->index].drdy_irq.ig1.latching_mask_addr =
> +                                               DEFAULT_IG1_LATCHING_MASK_ADDR;
> +       if (!sensor[adata->index].drdy_irq.ig1.latching_mask)
> +               sensor[adata->index].drdy_irq.ig1.latching_mask =
> +                                               DEFAULT_IG1_LATCHING_MASK;

Any reason to not initialize the static sensor array directly with these
defaults? This function is idempotent, but still it would be nice to make
"sensor" const.

> +}
> +
[...]
> +static ssize_t sysfs_sampling_frequency_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, n, len;
> +       char tmp[4];
> +       char sampling_frequency[30] = "";
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       n = ARRAY_SIZE(sensor[adata->index].odr.odr_avl);
> +       for (i = 0; i < n; i++) {
> +               if (sensor[adata->index].odr.odr_avl[i].hz != 0) {
> +                       len = strlen(&sampling_frequency[0]);
> +                       sprintf(tmp, "%d ",
> +                               sensor[adata->index].odr.odr_avl[i].hz);
> +                       strcpy(&sampling_frequency[len], tmp);
> +               } else
> +                       break;
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       return sprintf(buf, "%s\n", sampling_frequency);

Ok, so first you sprintf your value into tmp, then you copy temp to
sampling_frequency and finally you copy sampling_frequency. You could just
sprintf directly into buf. Also try to use scnprintf with a limit of PAGE_SIZE

> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +                               sysfs_sampling_frequency_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
> +                               sysfs_fullscale_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, sysfs_get_fullscale,
> +                               sysfs_set_fullscale , 0);
> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, sysfs_get_enable,
> +                                               sysfs_set_enable , 0);
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> sysfs_get_sampling_frequency,
> +                               sysfs_set_sampling_frequency);

Any non-standard attributes need documentation.

> +int acc_iio_default(struct iio_dev *indio_dev)

I'd call this _probe instead of _default.

> +{
> +       int err;
> +       u8 wai;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       mutex_init(&adata->slock);
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->info = &acc_info;
> +
> +       err = get_wai_device(indio_dev, DEFAULT_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto get_wai_error;
> +       err = check_device_list(indio_dev, wai);
> +       if (err < 0)
> +               goto check_device_list_error;
> +       set_sensor_parameters(indio_dev);
> +       err = set_platform_data(indio_dev);
> +       if (err < 0)
> +               goto set_platform_data_error;
> +       err = validate_platform_data(indio_dev);
> +       if (err < 0)
> +               goto validate_platform_data_error;
> +       register_channels(indio_dev);
> +
> +       err = init_sensor(indio_dev);
> +       if (err < 0)
> +               goto init_sensor_error;
> +
> +       if (sensor[adata->index].wai == S5_WAI_EXP)
> +               adata->multiread_bit = 0;
> +       else
> +               adata->multiread_bit = 1;
> +
> +       err = acc_check_irq(indio_dev);
> +       if (err < 0)
> +               goto gpio_check_error;
> +       err = acc_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto acc_allocate_ring_error;
> +
> +       err = acc_probe_trigger(indio_dev);
> +       if (err < 0)
> +               goto acc_probe_trigger_error;
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       pr_info("%s: probe end correctly.\n", adata->name);
> +
> +       return err;
> +
> +iio_device_register_error:
> +       acc_remove_trigger(indio_dev);
> +acc_probe_trigger_error:
> +       acc_deallocate_ring(indio_dev);
> +acc_allocate_ring_error:
> +gpio_check_error:
> +init_sensor_error:
> +validate_platform_data_error:
> +       kfree(adata->pdata);
> +set_platform_data_error:
> +check_device_list_error:
> +get_wai_error:
> +       return err;
> +}
> +
> +int acc_iio_remove(struct iio_dev *indio_dev)
> +{
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       acc_remove_trigger(indio_dev);
> +       acc_deallocate_ring(indio_dev);
> +       kfree(adata->pdata);
> +       iio_device_unregister(indio_dev);
> +
> +       return 0;
> +}
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> new file mode 100644
> index 0000000..f443e52
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> @@ -0,0 +1,124 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +#define        ACC_I2C_MULTIREAD       0x80
> +
> +
> +static inline s32 acc_i2c_read_byte(struct acc_data *adata, u8 reg_addr)
> +{
> +       return i2c_smbus_read_byte_data(adata->client, reg_addr);
> +}
> +
> +static inline s32 acc_i2c_read_multiple_byte(struct acc_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       if (adata->multiread_bit != 0)
> +               reg_addr |= ACC_I2C_MULTIREAD;
> +       return i2c_smbus_read_i2c_block_data(adata->client,
> +                                                       reg_addr, len, data);
> +}
> +
> +static inline s32 acc_i2c_write_byte(struct acc_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       return i2c_smbus_write_byte_data(adata->client, reg_addr, data);
> +}
> +
> +static int __devinit acc_i2c_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct iio_dev *indio_dev;
> +       struct acc_data *adata;
> +       int err;
> +
> +       pr_info("%s: probe start.\n", client->name);

This line is just noise, I'd remove it

> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->client = client;
> +       i2c_set_clientdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->name;
> +
> +       adata->read_byte = acc_i2c_read_byte;
> +       adata->write_byte = acc_i2c_write_byte;
> +       adata->read_multiple_byte = acc_i2c_read_multiple_byte;
> +       adata->name = &client->name[0];

adata->name = client->name;

> +       adata->irq_data_ready = &client->irq;

Why do you take a pointer here?

> +
> +       err = acc_iio_default(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
[...]

No new line her
> +MODULE_DEVICE_TABLE(i2c, acc_id_table);
> +
> +static struct i2c_driver acc_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "STMicroelectronics i2c accelerometers",
> +       },
> +       .probe = acc_i2c_probe,
> +       .remove = __devexit_p(acc_i2c_remove),
> +       .id_table = acc_id_table,
> +};
> +

No new line her

> +module_i2c_driver(acc_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> new file mode 100644
> index 0000000..f1ac211
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> @@ -0,0 +1,209 @@
[...]
> +
> +static int __devinit acc_spi_probe(struct spi_device *client)
> +{
> +       struct iio_dev *indio_dev;
> +       struct acc_data *adata;
> +       int err;
> +
> +       pr_info("%s: probe start.\n", client->modalias);

That's just noise, I'd remove it.

> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->spi = client;
> +       spi_set_drvdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->modalias;
> +
> +       adata->read_byte = acc_spi_read_byte;
> +       adata->write_byte = acc_spi_write_byte;
> +       adata->read_multiple_byte = acc_spi_read_multiple_byte;
> +       adata->name = &client->modalias[0];

adata->name = client->modalias;

> +       adata->irq_data_ready = &client->irq;
> +
> +       /* dummy read */

It would be good if the comment mentioned why a dummy read is necessary.

> +       adata->read_byte(adata, 0x0f);
> +
> +       err = acc_iio_default(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit acc_spi_remove(struct spi_device *spi)
> +{
> +       int err;
> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +
> +       err = acc_iio_remove(indio_dev);

err is not used.

> +       iio_device_free(indio_dev);

Why not put the iio_device_free in acc_iio_remove?

> +
> +       return 0;
> +}
> +
[...]
> +

No newline here

> +MODULE_DEVICE_TABLE(spi, acc_id_table);
> +
> +static struct spi_driver acc_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "STMicroelectronics spi accelerometers",

Maybe use "st-accel" here instead

> +       },
> +       .probe = acc_spi_probe,
> +       .remove = __devexit_p(acc_spi_remove),
> +       .id_table = acc_id_table,
> +};
> +

No newline here

> +module_spi_driver(acc_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> new file mode 100644
> index 0000000..843af4c
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> @@ -0,0 +1,96 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +static irqreturn_t acc_data_ready_trig_poll(int irq, void *trig)
> +{
> +       iio_trigger_poll(trig, iio_get_time_ns());
> +       return IRQ_HANDLED;
> +}

This is just a open-coded iio_trigger_generic_data_rdy_poll, use it instead.

> +
> +static int iio_trig_acc_set_state(struct iio_trigger *trig, bool state)
> +{
> +       struct iio_dev *indio_dev = trig->private_data;
> +       return acc_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops iio_acc_trigger_ops = {
> +       .owner = THIS_MODULE,
> +       .set_trigger_state = &iio_trig_acc_set_state,
> +};
> +
> +int acc_probe_trigger(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +       if (adata->trig == NULL) {
> +               err = -ENOMEM;
> +               pr_err("%s: failed to allocate iio trigger.\n", adata->name);
> +               goto iio_trigger_alloc_error;
> +       }
> +
> +       err = request_threaded_irq(*adata->irq_data_ready,
> +                       acc_data_ready_trig_poll,
> +                       NULL,
> +                       IRQF_TRIGGER_RISING,
> +                       "IRQ_data_ready",
> +                       adata->trig);

request_irq. Your thread_fn is NULL. Also maybe use a better name, I'd
recommand trigger->name.

> +       if (err) {
> +               pr_err("%s: failed to request threaded irq [%d].\n",
> +                                       adata->name, *adata->irq_data_ready);
> +               goto request_irq_error;
> +       }
> +       adata->trig->private_data = indio_dev;
> +       adata->trig->ops = &iio_acc_trigger_ops;
> +
> +       if (adata->client != NULL)
> +               adata->trig->dev.parent = &adata->client->dev;
> +       else
> +               adata->trig->dev.parent = &adata->spi->dev;
> +
> +       err = iio_trigger_register(adata->trig);
> +       if (err < 0) {
> +               pr_err("%s: failed to register iio trigger.\n", adata->name);
> +               goto iio_trigger_register_error;
> +       }
> +       indio_dev->trig = adata->trig;
> +       pr_info("%s: using [%s] trigger.\n", adata->name,
> +                                                       adata->trig->name);
> +       return 0;
> +
> +iio_trigger_register_error:
> +       free_irq(*adata->irq_data_ready, adata->trig);
> +request_irq_error:
> +       iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +       return err;
> +}
[...]
> --
> 1.7.0.4
> 
> 
>  From 1fe8b75e0ec1197781c559935de23e116773c441 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 8 Oct 2012 17:08:43 +0200
> Subject: [PATCH 2/2] add header file
> 
> ---
>   .../acc/STMicroelectronics_accelerometers_iio.h    |  116
> ++++++++++++++++++++
>   1 files changed, 116 insertions(+), 0 deletions(-)
>   create mode 100644
> include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> 
> diff --git
> a/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> new file mode 100644
> index 0000000..e6f18ad
> --- /dev/null
> +++ b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> @@ -0,0 +1,116 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> [...]
> +
> +struct acc_platform_data {
> +       int fullscale;
> +       int sampling_frequency;
> +};
> +
> +struct acc_data {
> +       struct i2c_client *client;
> +       struct spi_device *spi;

Since a device will either be a spi or i2c device there is no need to have
pointers to both here. You can use a struct device and use to_i2c_client and
to_spi_device in the raw bus read/write callbacks. This will also help to clean
a few places in your driver where you do if(acc->client) .. else if (acc->spi)

> +       struct acc_platform_data *pdata;
> +       char *name;
> +
> +       short index;
> +
> +       atomic_t enabled;
> +       int fullscale;
> +       int gain;
> +       int odr;
> +
> +       int multiread_bit;
> +       int (*read_byte) (struct acc_data *adata, u8 reg_addr);
> +       int (*write_byte) (struct acc_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct acc_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +
> +#ifdef CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO
> +       struct iio_trigger *trig;
> +#endif /* CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO */
> +
> +       int *irq_data_ready;
> +       struct mutex slock;
> +
> +};
> +
[...]
> +
> +#endif /* ST_ACCELEROMETERS_IIO_ACC_H */

There is no need for most of this to be globally visible. Put everything except
for the platform data into a local header drivers/iio/accel. And put the header
containing the platform data in include/linux/platform_data/

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-08 19:14 ` Lars-Peter Clausen
@ 2012-10-08 19:50   ` Pavel Machek
  2012-10-08 20:33     ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Pavel Machek @ 2012-10-08 19:50 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Denis CIOCCA, linux-iio, Jonathan Cameron, burman.yan


> > drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> >   create mode 100644
> > drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> >   create mode 100644
> > drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> 
> the file names are a bit excessive in my opinion. How about
> st_accel_{buffer,core,i2c,...}.c?

drivers/iio/accel/st/iio_spi.c ?

								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-08 19:50   ` Pavel Machek
@ 2012-10-08 20:33     ` Lars-Peter Clausen
  2012-10-08 20:37       ` Jonathan Cameron
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-08 20:33 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Denis CIOCCA, linux-iio, Jonathan Cameron, burman.yan

On 10/08/2012 09:50 PM, Pavel Machek wrote:
> 
>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
>>>   create mode 100644
>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
>>>   create mode 100644
>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
>>
>> the file names are a bit excessive in my opinion. How about
>> st_accel_{buffer,core,i2c,...}.c?
> 
> drivers/iio/accel/st/iio_spi.c ?

Maybe. I'd still vote for st_accel though, since I think it is most
descriptive. Since trigger and buffer should go down to a few lines it probably
makes sense to put their content into the core file and I don't think we need a
extra directory for the driver then.

- Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-08 20:33     ` Lars-Peter Clausen
@ 2012-10-08 20:37       ` Jonathan Cameron
  2012-10-14 15:05         ` Denis Ciocca
  0 siblings, 1 reply; 42+ messages in thread
From: Jonathan Cameron @ 2012-10-08 20:37 UTC (permalink / raw)
  To: Lars-Peter Clausen, Pavel Machek
  Cc: Denis CIOCCA, linux-iio, Jonathan Cameron, burman.yan



Lars-Peter Clausen <lars@metafoo.de> wrote:

>On 10/08/2012 09:50 PM, Pavel Machek wrote:
>> 
>>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
>>>>   create mode 100644
>>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
>>>>   create mode 100644
>>>> drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
>>>
>>> the file names are a bit excessive in my opinion. How about
>>> st_accel_{buffer,core,i2c,...}.c?
>> 
>> drivers/iio/accel/st/iio_spi.c ?
>
>Maybe. I'd still vote for st_accel though, since I think it is most
>descriptive. Since trigger and buffer should go down to a few lines it
>probably
>makes sense to put their content into the core file and I don't think
>we need a
>extra directory for the driver then.
>
Agreed.  Either is an improvement but Lars' is more in keeping with other drivers.   Doesn't need its own dir. Whilst this one is large it isn't exceptionally so.

>- Lars

-- 
Sent from my Android phone with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-08 20:37       ` Jonathan Cameron
@ 2012-10-14 15:05         ` Denis Ciocca
  2012-10-14 19:08           ` Lars-Peter Clausen
  2012-10-16 17:51           ` Lars-Peter Clausen
  0 siblings, 2 replies; 42+ messages in thread
From: Denis Ciocca @ 2012-10-14 15:05 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lars-Peter Clausen, Pavel Machek, Denis CIOCCA, linux-iio,
	Jonathan Cameron, burman.yan

Hi guys,

according to your requests I have modified my driver. Only one things
about this function:

static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)

You tell to me:

>In buffered mode the samples should not be postprocessed. Userspace expects the
>samples in the format as described by the scan_type. The buffer demuxing should
>be handled by the IIO core. If you set up available_scan_masks correctly it
>will automatically do the right thing.

I agree with you for the postprocessed samples part, but I don't understand very
well the second one.
I saw the other drivers, if I check only the available_scan_masks variable
I don't know which channels are actives and which not.
I will read every time all data channels, but I choose which channel
put in the buffer.

Thanks,
Denis





>From 0917bcc1e22462db0469709d1020b5378d91959c Mon Sep 17 00:00:00 2001
From: userid <userid@MacBookAir.(none)>
Date: Sun, 14 Oct 2012 00:05:54 +0200
Subject: [PATCH] Add STMicroelectronics accelerometers driver to support I2C
 and SPI devices.

---
 drivers/iio/accel/Kconfig                    |   32 +
 drivers/iio/accel/Makefile                   |    6 +
 drivers/iio/accel/ST_accel_buffer.c          |  122 +++
 drivers/iio/accel/ST_accel_core.c            | 1335 ++++++++++++++++++++++++++
 drivers/iio/accel/ST_accel_i2c.c             |  165 ++++
 drivers/iio/accel/ST_accel_spi.c             |  236 +++++
 drivers/iio/accel/ST_accel_trigger.c         |   87 ++
 include/linux/iio/accel/ST_accel.h           |  120 +++
 include/linux/platform_data/ST_accel_pdata.h |   34 +
 9 files changed, 2137 insertions(+)
 create mode 100644 drivers/iio/accel/ST_accel_buffer.c
 create mode 100644 drivers/iio/accel/ST_accel_core.c
 create mode 100644 drivers/iio/accel/ST_accel_i2c.c
 create mode 100644 drivers/iio/accel/ST_accel_spi.c
 create mode 100644 drivers/iio/accel/ST_accel_trigger.c
 create mode 100644 include/linux/iio/accel/ST_accel.h
 create mode 100644 include/linux/platform_data/ST_accel_pdata.h

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..13cc44f 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,36 @@ config HID_SENSOR_ACCEL_3D
 	  Say yes here to build support for the HID SENSOR
 	  accelerometers 3D.

+config ST_ACCEL_3AXIS
+	tristate "STMicroelectronics accelerometers 3-Axis Driver"
+	depends on (I2C || SPI)
+	help
+	  Say yes here to build support for STMicroelectronics accelerometers.
+
+choice
+	prompt "Bus type"
+	depends on ST_ACCEL_3AXIS
+
+config ST_ACCEL_3AXIS_I2C
+	bool "support I2C bus connection"
+	depends on I2C && ST_ACCEL_3AXIS
+	help
+	  Say yes here to build I2C support for STMicroelectronics accelerometers.
+
+config ST_ACCEL_3AXIS_SPI
+	bool "support SPI bus connection"
+	depends on SPI && ST_ACCEL_3AXIS
+	help
+	Say yes here to build SPI support for STMicroelectronics accelerometers.
+
+endchoice
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+	tristate "support triggered buffer"
+	depends on ST_ACCEL_3AXIS
+	select IIO_TRIGGERED_BUFFER
+	select IIO_BUFFER
+	help
+	  Default trigger and buffer for STMicroelectronics accelerometers driver.
+
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..b797db4 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
 #

 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
+
+ST_accel-y := ST_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += ST_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += ST_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += ST_accel_trigger.o
ST_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += ST_accel.o
\ No newline at end of file
diff --git a/drivers/iio/accel/ST_accel_buffer.c
b/drivers/iio/accel/ST_accel_buffer.c
new file mode 100644
index 0000000..61ee836
--- /dev/null
+++ b/drivers/iio/accel/ST_accel_buffer.c
@@ -0,0 +1,122 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/ST_accel.h>
+
+
+#define ST_ACCEL_NUMBER_DATA_CHANNELS		3
+
+static int acc_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+	int len;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	len = adata->read_multiple_byte(adata,
+		indio_dev->channels[ST_ACC_SCAN_X].address,
+		ST_ACC_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
+		rx_array);
+	if (len < 0)
+		goto read_error;
+
+	return len;
+
+read_error:
+	return -EIO;
+}
+
+static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int ret, i, n = 0;
+	u8 *rx_array;
+	u8 mask = 0x01;
+	s16 *data = (s16 *)buf;
+	int scan_count = bitmap_weight(indio_dev->active_scan_mask,
+				       indio_dev->masklength);
+
+	rx_array = kzalloc(ST_ACC_BYTE_FOR_CHANNEL*
+				ST_ACCEL_NUMBER_DATA_CHANNELS, GFP_KERNEL);
+	if (rx_array == NULL)
+		return -ENOMEM;
+	ret = acc_read_all(indio_dev, rx_array);
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+		if ((*indio_dev->active_scan_mask & mask) > 0) {
+			if (indio_dev->channels[i].scan_type.endianness ==
+								IIO_LE) {
+				data[n] = (s16)(cpu_to_le16(le16_to_cpu((
+				(__le16 *)rx_array)[i])));
+				n++;
+			} else {
+				data[n] = (s16)(cpu_to_be16(be16_to_cpu((
+				(__be16 *)rx_array)[i])));
+				n++;
+			}
+		}
+		mask = mask << 1;
+	}
+	kfree(rx_array);
+	return i*sizeof(data[0]);
+}
+
+static irqreturn_t acc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	int len = 0;
+	char *data;
+
+	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (data == NULL)
+		goto done;
+	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+		len = acc_get_buffer_element(indio_dev, data);
+	else
+		goto done;
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
+	iio_push_to_buffer(indio_dev->buffer, data);
+	kfree(data);
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops acc_buf_setup_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+};
+
+int acc_allocate_ring(struct iio_dev *indio_dev)
+{
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+			&acc_trigger_handler, &acc_buf_setup_ops);
+}
+EXPORT_SYMBOL(acc_allocate_ring);
+
+void acc_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(acc_deallocate_ring);
diff --git a/drivers/iio/accel/ST_accel_core.c
b/drivers/iio/accel/ST_accel_core.c
new file mode 100644
index 0000000..5b30155
--- /dev/null
+++ b/drivers/iio/accel/ST_accel_core.c
@@ -0,0 +1,1335 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/ST_accel.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX	5
+#define ST_ACCEL_ODR_LIST_MAX		10
+#define UG_TO_MS2(x)			(x*9800)
+#define ST_ACCEL_ON			1
+#define ST_ACCEL_OFF			0
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_DEFAULT_OUT_X_L_ADDR		0x28
+#define ST_DEFAULT_OUT_X_H_ADDR		0x29
+#define ST_DEFAULT_OUT_Y_L_ADDR		0x2a
+#define ST_DEFAULT_OUT_Y_H_ADDR		0x2b
+#define ST_DEFAULT_OUT_Z_L_ADDR		0x2c
+#define ST_DEFAULT_OUT_Z_H_ADDR		0x2d
+#define ST_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_DEFAULT_POWER_OFF_VALUE	0x00
+
+/* ODR */
+#define ST_ACCEL_ODR_AVL_1HZ		1
+#define ST_ACCEL_ODR_AVL_3HZ		3
+#define ST_ACCEL_ODR_AVL_6HZ		6
+#define ST_ACCEL_ODR_AVL_10HZ		10
+#define ST_ACCEL_ODR_AVL_12HZ		12
+#define ST_ACCEL_ODR_AVL_25HZ		25
+#define ST_ACCEL_ODR_AVL_50HZ		50
+#define ST_ACCEL_ODR_AVL_100HZ		100
+#define ST_ACCEL_ODR_AVL_200HZ		200
+#define ST_ACCEL_ODR_AVL_400HZ		400
+#define ST_ACCEL_ODR_AVL_800HZ		800
+#define ST_ACCEL_ODR_AVL_1000HZ		1000
+#define ST_ACCEL_ODR_AVL_1600HZ		1600
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G		2
+#define ST_ACCEL_FS_AVL_4G		4
+#define ST_ACCEL_FS_AVL_6G		6
+#define ST_ACCEL_FS_AVL_8G		8
+#define ST_ACCEL_FS_AVL_16G		16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define S1_WAI_EXP			0x33
+#define S1_ODR_ADDR			0x20
+#define S1_ODR_MASK			0xf0
+#define S1_ODR_N_BIT			4
+#define S1_ODR_AVL_1HZ_VALUE		0x01
+#define S1_ODR_AVL_10HZ_VALUE		0x02
+#define S1_ODR_AVL_25HZ_VALUE		0x03
+#define S1_ODR_AVL_50HZ_VALUE		0x04
+#define S1_ODR_AVL_100HZ_VALUE		0x05
+#define S1_ODR_AVL_200HZ_VALUE		0x06
+#define S1_ODR_AVL_400HZ_VALUE		0x07
+#define S1_ODR_AVL_1600HZ_VALUE		0x08
+#define S1_FS_N_BIT			2
+#define S1_FS_ADDR			0x23
+#define S1_FS_MASK			0x30
+#define S1_FS_AVL_2_VALUE		0x00
+#define S1_FS_AVL_4_VALUE		0x01
+#define S1_FS_AVL_8_VALUE		0x02
+#define S1_FS_AVL_16_VALUE		0x03
+#define S1_FS_AVL_2_GAIN		1000
+#define S1_FS_AVL_4_GAIN		2000
+#define S1_FS_AVL_8_GAIN		4000
+#define S1_FS_AVL_16_GAIN		12000
+#define S1_REALBITS			12
+#define S1_BDU_ADDR			0x23
+#define S1_BDU_MASK			0x80
+#define S1_DRDY_IRQ_ADDR		0x22
+#define S1_DRDY_IRQ_MASK		0x10
+#define S1_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define S2_WAI_EXP			0x49
+#define S2_ODR_ADDR			0x20
+#define S2_ODR_MASK			0xf0
+#define S2_ODR_N_BIT			4
+#define S2_ODR_AVL_3HZ_VALUE		0x01
+#define S2_ODR_AVL_6HZ_VALUE		0x02
+#define S2_ODR_AVL_12HZ_VALUE		0x03
+#define S2_ODR_AVL_25HZ_VALUE		0x04
+#define S2_ODR_AVL_50HZ_VALUE		0x05
+#define S2_ODR_AVL_100HZ_VALUE		0x06
+#define S2_ODR_AVL_200HZ_VALUE		0x07
+#define S2_ODR_AVL_400HZ_VALUE		0x08
+#define S2_ODR_AVL_800HZ_VALUE		0x09
+#define S2_ODR_AVL_1600HZ_VALUE		0x0a
+#define S2_FS_N_BIT			3
+#define S2_FS_ADDR			0x21
+#define S2_FS_MASK			0x38
+#define S2_FS_AVL_2_VALUE		0X00
+#define S2_FS_AVL_4_VALUE		0X01
+#define S2_FS_AVL_6_VALUE		0x02
+#define S2_FS_AVL_8_VALUE		0x03
+#define S2_FS_AVL_16_VALUE		0x04
+#define S2_FS_AVL_2_GAIN		61
+#define S2_FS_AVL_4_GAIN		122
+#define S2_FS_AVL_6_GAIN		183
+#define S2_FS_AVL_8_GAIN		244
+#define S2_FS_AVL_16_GAIN		732
+#define S2_REALBITS			16
+#define S2_BDU_ADDR			0x20
+#define S2_BDU_MASK			0x08
+#define S2_DRDY_IRQ_ADDR		0x22
+#define S2_DRDY_IRQ_MASK		0x04
+#define S2_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define S3_WAI_EXP			0x3c
+#define S3_ODR_ADDR			0x20
+#define S3_ODR_MASK			0x18
+#define S3_ODR_N_BIT			2
+#define S3_ODR_AVL_50HZ_VALUE		0x00
+#define S3_ODR_AVL_100HZ_VALUE		0x01
+#define S3_ODR_AVL_400HZ_VALUE		0x02
+#define S3_ODR_AVL_1000HZ_VALUE		0x03
+#define S3_PW_ADDR			0x20
+#define S3_PW_MASK			0xe0
+#define S3_PW_N_BIT			3
+#define S3_PW_VALUE_ON			0x01
+#define S3_PW_VALUE_OFF			0x00
+#define S3_FS_N_BIT			2
+#define S3_FS_ADDR			0x23
+#define S3_FS_MASK			0x30
+#define S3_FS_AVL_2_VALUE		0X00
+#define S3_FS_AVL_4_VALUE		0X01
+#define S3_FS_AVL_8_VALUE		0x03
+#define S3_FS_AVL_2_GAIN		1000
+#define S3_FS_AVL_4_GAIN		2000
+#define S3_FS_AVL_8_GAIN		3900
+#define S3_REALBITS			12
+#define S3_BDU_ADDR			0x23
+#define S3_BDU_MASK			0x80
+#define S3_DRDY_IRQ_ADDR		0x22
+#define S3_DRDY_IRQ_MASK		0x02
+#define S3_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define S4_WAI_EXP			0xd4
+#define S4_ODR_ADDR			0x20
+#define S4_ODR_MASK			0xf0
+#define S4_ODR_N_BIT			4
+#define S4_ODR_AVL_3HZ_VALUE		0x01
+#define S4_ODR_AVL_6HZ_VALUE		0x02
+#define S4_ODR_AVL_12HZ_VALUE		0x03
+#define S4_ODR_AVL_25HZ_VALUE		0x04
+#define S4_ODR_AVL_50HZ_VALUE		0x05
+#define S4_ODR_AVL_100HZ_VALUE		0x06
+#define S4_ODR_AVL_200HZ_VALUE		0x07
+#define S4_ODR_AVL_400HZ_VALUE		0x08
+#define S4_ODR_AVL_800HZ_VALUE		0x09
+#define S4_ODR_AVL_1600HZ_VALUE		0x0a
+#define S4_FS_N_BIT			3
+#define S4_FS_ADDR			0x24
+#define S4_FS_MASK			0x38
+#define S4_FS_AVL_2_VALUE		0X00
+#define S4_FS_AVL_4_VALUE		0X01
+#define S4_FS_AVL_6_VALUE		0x02
+#define S4_FS_AVL_8_VALUE		0x03
+#define S4_FS_AVL_16_VALUE		0x04
+#define S2_FS_AVL_2_GAIN		61
+#define S2_FS_AVL_4_GAIN		122
+#define S2_FS_AVL_6_GAIN		183
+#define S2_FS_AVL_8_GAIN		244
+#define S2_FS_AVL_16_GAIN		732
+#define S4_REALBITS			12
+#define S4_BDU_ADDR			0x20
+#define S4_BDU_MASK			0x08
+#define S4_DRDY_IRQ_ADDR		0x23
+#define S4_DRDY_IRQ_MASK		0x80
+#define S4_MULTIREAD_BIT		false
+
+struct odr_available {
+	int hz;
+	u8 value;
+};
+
+struct odr {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct power {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct fullscale_available {
+	int num;
+	u8 value;
+	int gain;
+};
+
+struct fullscale {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
+};
+
+struct bdu {
+	u8 addr;
+	u8 mask;
+};
+
+struct interrupt_generator {
+	u8 en_addr;
+	u8 latch_mask_addr;
+	u8 en_mask;
+	u8 latching_mask;
+};
+
+struct data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct interrupt_generator ig1;
+};
+
+#define LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+	.type = IIO_ACCEL, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+static const struct iio_chan_spec default_channels[] = {
+	LSM_CHANNELS(ST_ACC_SCAN_X, IIO_MOD_X, IIO_LE,
+					S1_REALBITS, ST_DEFAULT_OUT_X_L_ADDR),
+	LSM_CHANNELS(ST_ACC_SCAN_Y, IIO_MOD_Y, IIO_LE,
+					S1_REALBITS, ST_DEFAULT_OUT_Y_L_ADDR),
+	LSM_CHANNELS(ST_ACC_SCAN_Z, IIO_MOD_Z, IIO_LE,
+					S1_REALBITS, ST_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec s2_channels[] = {
+	LSM_CHANNELS(ST_ACC_SCAN_X, IIO_MOD_X, IIO_LE,
+					S2_REALBITS, ST_DEFAULT_OUT_X_L_ADDR),
+	LSM_CHANNELS(ST_ACC_SCAN_Y, IIO_MOD_Y, IIO_LE,
+					S2_REALBITS, ST_DEFAULT_OUT_Y_L_ADDR),
+	LSM_CHANNELS(ST_ACC_SCAN_Z, IIO_MOD_Z, IIO_LE,
+					S2_REALBITS, ST_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+
+static const struct st_accel_sensors {
+	u8 wai;
+	struct iio_chan_spec *ch;
+	struct odr odr;
+	struct power pw;
+	struct fullscale fs;
+	struct bdu bdu;
+	struct data_ready_irq drdy_irq;
+	bool multi_read_bit;
+} st_accel_sensors[] = {
+	{
+	.wai = S1_WAI_EXP,
+	.ch = (struct iio_chan_spec *)default_channels,
+	.odr = {
+		.addr = S1_ODR_ADDR,
+		.mask = S1_ODR_MASK,
+		.num_bit = S1_ODR_N_BIT,
+		.odr_avl = {
+			[0] = {
+				.hz = ST_ACCEL_ODR_AVL_1HZ,
+				.value = S1_ODR_AVL_1HZ_VALUE,
+			},
+			[1] = {
+				.hz = ST_ACCEL_ODR_AVL_10HZ,
+				.value = S1_ODR_AVL_10HZ_VALUE,
+			},
+			[2] = {
+				.hz = ST_ACCEL_ODR_AVL_25HZ,
+				.value = S1_ODR_AVL_25HZ_VALUE,
+			},
+			[3] = {
+				.hz = ST_ACCEL_ODR_AVL_50HZ,
+				.value = S1_ODR_AVL_50HZ_VALUE,
+			},
+			[4] = {
+				.hz = ST_ACCEL_ODR_AVL_100HZ,
+				.value = S1_ODR_AVL_100HZ_VALUE,
+			},
+			[5] = {
+				.hz = ST_ACCEL_ODR_AVL_200HZ,
+				.value = S1_ODR_AVL_200HZ_VALUE,
+			},
+			[6] = {
+				.hz = ST_ACCEL_ODR_AVL_400HZ,
+				.value = S1_ODR_AVL_400HZ_VALUE,
+			},
+			[7] = {
+				.hz = ST_ACCEL_ODR_AVL_1600HZ,
+				.value = S1_ODR_AVL_1600HZ_VALUE,
+			},
+		},
+	},
+	.pw = {
+		.addr = S1_ODR_ADDR,
+		.mask = S1_ODR_MASK,
+		.num_bit = S1_ODR_N_BIT,
+		.value_off = ST_DEFAULT_POWER_OFF_VALUE,
+	},
+	.fs = {
+		.addr = S1_FS_ADDR,
+		.mask = S1_FS_MASK,
+		.num_bit = S1_FS_N_BIT,
+		.fs_avl = {
+			[0] = {
+				.num = ST_ACCEL_FS_AVL_2G,
+				.value = S1_FS_AVL_2_VALUE,
+				.gain = S1_FS_AVL_2_GAIN,
+			},
+			[1] = {
+				.num = ST_ACCEL_FS_AVL_4G,
+				.value = S1_FS_AVL_4_VALUE,
+				.gain = S1_FS_AVL_4_GAIN,
+			},
+			[2] = {
+				.num = ST_ACCEL_FS_AVL_8G,
+				.value = S1_FS_AVL_8_VALUE,
+				.gain = S1_FS_AVL_8_GAIN,
+			},
+			[3] = {
+				.num = ST_ACCEL_FS_AVL_16G,
+				.value = S1_FS_AVL_16_VALUE,
+				.gain = S1_FS_AVL_16_GAIN,
+			},
+		},
+	},
+	.bdu = {
+		.addr = S1_BDU_ADDR,
+		.mask = S1_BDU_MASK,
+	},
+	.drdy_irq = {
+		.addr = S1_DRDY_IRQ_ADDR,
+		.mask = S1_DRDY_IRQ_MASK,
+	},
+	.multi_read_bit = S1_MULTIREAD_BIT,
+	},
+	{
+	.wai = S2_WAI_EXP,
+	.ch = (struct iio_chan_spec *)s2_channels,
+	.odr = {
+		.addr = S2_ODR_ADDR,
+		.mask = S2_ODR_MASK,
+		.num_bit = S2_ODR_N_BIT,
+		.odr_avl = {
+			[0] = {
+				.hz = ST_ACCEL_ODR_AVL_3HZ,
+				.value = S2_ODR_AVL_3HZ_VALUE,
+			},
+			[1] = {
+				.hz = ST_ACCEL_ODR_AVL_6HZ,
+				.value = S2_ODR_AVL_6HZ_VALUE,
+			},
+			[2] = {
+				.hz = ST_ACCEL_ODR_AVL_12HZ,
+				.value = S2_ODR_AVL_12HZ_VALUE,
+			},
+			[3] = {
+				.hz = ST_ACCEL_ODR_AVL_25HZ,
+				.value = S2_ODR_AVL_25HZ_VALUE,
+			},
+			[4] = {
+				.hz = ST_ACCEL_ODR_AVL_50HZ,
+				.value = S2_ODR_AVL_50HZ_VALUE,
+			},
+			[5] = {
+				.hz = ST_ACCEL_ODR_AVL_100HZ,
+				.value = S2_ODR_AVL_100HZ_VALUE,
+			},
+			[6] = {
+				.hz = ST_ACCEL_ODR_AVL_200HZ,
+				.value = S2_ODR_AVL_200HZ_VALUE,
+			},
+			[7] = {
+				.hz = ST_ACCEL_ODR_AVL_400HZ,
+				.value = S2_ODR_AVL_400HZ_VALUE,
+			},
+			[8] = {
+				.hz = ST_ACCEL_ODR_AVL_800HZ,
+				.value = S2_ODR_AVL_800HZ_VALUE,
+			},
+			[9] = {
+				.hz = ST_ACCEL_ODR_AVL_1600HZ,
+				.value = S2_ODR_AVL_1600HZ_VALUE,
+			},
+		},
+	},
+	.pw = {
+		.addr = S2_ODR_ADDR,
+		.mask = S2_ODR_MASK,
+		.num_bit = S2_ODR_N_BIT,
+		.value_off = ST_DEFAULT_POWER_OFF_VALUE,
+	},
+	.fs = {
+		.addr = S2_FS_ADDR,
+		.mask = S2_FS_MASK,
+		.num_bit = S2_FS_N_BIT,
+		.fs_avl = {
+			[0] = {
+				.num = ST_ACCEL_FS_AVL_2G,
+				.value = S2_FS_AVL_2_VALUE,
+				.gain = S2_FS_AVL_2_GAIN,
+			},
+			[1] = {
+				.num = ST_ACCEL_FS_AVL_4G,
+				.value = S2_FS_AVL_4_VALUE,
+				.gain = S2_FS_AVL_4_GAIN,
+			},
+			[2] = {
+				.num = ST_ACCEL_FS_AVL_6G,
+				.value = S2_FS_AVL_6_VALUE,
+				.gain = S2_FS_AVL_6_GAIN,
+			},
+			[3] = {
+				.num = ST_ACCEL_FS_AVL_8G,
+				.value = S2_FS_AVL_8_VALUE,
+				.gain = S2_FS_AVL_8_GAIN,
+			},
+			[4] = {
+				.num = ST_ACCEL_FS_AVL_16G,
+				.value = S2_FS_AVL_16_VALUE,
+				.gain = S2_FS_AVL_16_GAIN,
+			},
+		},
+	},
+	.drdy_irq = {
+		.addr = S2_DRDY_IRQ_ADDR,
+		.mask = S2_DRDY_IRQ_MASK,
+	},
+	.bdu = {
+		.addr = S2_BDU_ADDR,
+		.mask = S2_BDU_MASK,
+	},
+	.multi_read_bit = S2_MULTIREAD_BIT,
+	},
+	{
+	.wai = S3_WAI_EXP,
+	.ch = (struct iio_chan_spec *)default_channels,
+	.odr = {
+		.addr = S3_ODR_ADDR,
+		.mask = S3_ODR_MASK,
+		.num_bit = S3_ODR_N_BIT,
+		.odr_avl = {
+			[0] = {
+				.hz = ST_ACCEL_ODR_AVL_50HZ,
+				.value = S3_ODR_AVL_50HZ_VALUE,
+			},
+			[1] = {
+				.hz = ST_ACCEL_ODR_AVL_100HZ,
+				.value = S3_ODR_AVL_100HZ_VALUE,
+			},
+			[2] = {
+				.hz = ST_ACCEL_ODR_AVL_400HZ,
+				.value = S3_ODR_AVL_400HZ_VALUE,
+			},
+			[3] = {
+				.hz = ST_ACCEL_ODR_AVL_1000HZ,
+				.value = S3_ODR_AVL_1000HZ_VALUE,
+			},
+		},
+	},
+	.pw = {
+		.addr = S3_PW_ADDR,
+		.mask = S3_PW_MASK,
+		.num_bit = S3_PW_N_BIT,
+		.value_on = S3_PW_VALUE_ON,
+		.value_off = S3_PW_VALUE_OFF,
+	},
+	.fs = {
+		.addr = S3_FS_ADDR,
+		.mask = S3_FS_MASK,
+		.num_bit = S3_FS_N_BIT,
+		.fs_avl = {
+			[0] = {
+				.num = ST_ACCEL_FS_AVL_2G,
+				.value = S3_FS_AVL_2_VALUE,
+				.gain = S3_FS_AVL_2_GAIN,
+			},
+			[1] = {
+				.num = ST_ACCEL_FS_AVL_4G,
+				.value = S3_FS_AVL_4_VALUE,
+				.gain = S3_FS_AVL_4_GAIN,
+			},
+			[2] = {
+				.num = ST_ACCEL_FS_AVL_8G,
+				.value = S3_FS_AVL_8_VALUE,
+				.gain = S3_FS_AVL_8_GAIN,
+			},
+		},
+	},
+	.bdu = {
+		.addr = S3_BDU_ADDR,
+		.mask = S3_BDU_MASK,
+	},
+	.drdy_irq = {
+		.addr = S3_DRDY_IRQ_ADDR,
+		.mask = S3_DRDY_IRQ_MASK,
+	},
+	.multi_read_bit = S3_MULTIREAD_BIT,
+	},
+	{
+	.wai = S4_WAI_EXP,
+	.ch = (struct iio_chan_spec *)s2_channels,
+	.odr = {
+		.addr = S4_ODR_ADDR,
+		.mask = S4_ODR_MASK,
+		.num_bit = S4_ODR_N_BIT,
+		.odr_avl = {
+			[0] = {
+				.hz = ST_ACCEL_ODR_AVL_3HZ,
+				.value = S4_ODR_AVL_3HZ_VALUE,
+			},
+			[1] = {
+				.hz = ST_ACCEL_ODR_AVL_6HZ,
+				.value = S4_ODR_AVL_6HZ_VALUE,
+			},
+			[2] = {
+				.hz = ST_ACCEL_ODR_AVL_12HZ,
+				.value = S4_ODR_AVL_12HZ_VALUE,
+			},
+			[3] = {
+				.hz = ST_ACCEL_ODR_AVL_25HZ,
+				.value = S4_ODR_AVL_25HZ_VALUE,
+			},
+			[4] = {
+				.hz = ST_ACCEL_ODR_AVL_50HZ,
+				.value = S4_ODR_AVL_50HZ_VALUE,
+			},
+			[5] = {
+				.hz = ST_ACCEL_ODR_AVL_100HZ,
+				.value = S4_ODR_AVL_100HZ_VALUE,
+			},
+			[6] = {
+				.hz = ST_ACCEL_ODR_AVL_200HZ,
+				.value = S4_ODR_AVL_200HZ_VALUE,
+			},
+			[7] = {
+				.hz = ST_ACCEL_ODR_AVL_400HZ,
+				.value = S4_ODR_AVL_400HZ_VALUE,
+			},
+			[8] = {
+				.hz = ST_ACCEL_ODR_AVL_800HZ,
+				.value = S4_ODR_AVL_800HZ_VALUE,
+			},
+			[9] = {
+				.hz = ST_ACCEL_ODR_AVL_1600HZ,
+				.value = S4_ODR_AVL_1600HZ_VALUE,
+			},
+		},
+	},
+	.pw = {
+		.addr = S4_ODR_ADDR,
+		.mask = S3_ODR_MASK,
+		.num_bit = S3_ODR_N_BIT,
+		.value_off = ST_DEFAULT_POWER_OFF_VALUE,
+	},
+	.fs = {
+		.addr = S4_FS_ADDR,
+		.mask = S4_FS_MASK,
+		.num_bit = S4_FS_N_BIT,
+		.fs_avl = {
+			[0] = {
+				.num = ST_ACCEL_FS_AVL_2G,
+				.value = S4_FS_AVL_2_VALUE,
+				.gain = S4_FS_AVL_2_GAIN,
+			},
+			[1] = {
+				.num = ST_ACCEL_FS_AVL_4G,
+				.value = S4_FS_AVL_4_VALUE,
+				.gain = S4_FS_AVL_4_GAIN,
+			},
+			[2] = {
+				.num = ST_ACCEL_FS_AVL_6G,
+				.value = S4_FS_AVL_6_VALUE,
+				.gain = S4_FS_AVL_6_GAIN,
+			},
+			[3] = {
+				.num = ST_ACCEL_FS_AVL_8G,
+				.value = S4_FS_AVL_8_VALUE,
+				.gain = S4_FS_AVL_8_GAIN,
+			},
+			[4] = {
+				.num = ST_ACCEL_FS_AVL_16G,
+				.value = S4_FS_AVL_16_VALUE,
+				.gain = S4_FS_AVL_16_GAIN,
+			},
+		},
+	},
+	.bdu = {
+		.addr = S4_BDU_ADDR,
+		.mask = S4_BDU_MASK,
+	},
+	.drdy_irq = {
+		.addr = S4_DRDY_IRQ_ADDR,
+		.mask = S4_DRDY_IRQ_MASK,
+	},
+	.multi_read_bit = S4_MULTIREAD_BIT,
+	},
+};
+
+static int acc_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+						u8 mask, short num_bit, u8 data)
+{
+	int err, j, pos;
+	u8 prev_data;
+	u8 new_data;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	pos = 7;
+	for (j = 128; j >= 0; j = j/2) {
+		if (mask/j > 0)
+			break;
+		else
+			pos--;
+	}
+
+	err = adata->read_byte(adata, reg_addr, &prev_data);
+	if (err < 0)
+		goto i2c_write_data_with_mask_error;
+
+	new_data = ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & mask));
+	err = adata->write_byte(adata, reg_addr, new_data);
+
+i2c_write_data_with_mask_error:
+	return err;
+}
+
+static int match_odr(const struct st_accel_sensors *sensor, int odr,
+						struct odr_available *odr_out)
+{
+	int n, i, ret = -1;
+
+	n = ARRAY_SIZE(sensor->odr.odr_avl);
+	for (i = 0; i < n; i++)
+		if (sensor->odr.odr_avl[i].hz == odr) {
+			odr_out->hz = sensor->odr.odr_avl[i].hz;
+			odr_out->value = sensor->odr.odr_avl[i].value;
+			ret = 0;
+			break;
+		}
+
+	return ret;
+}
+
+static int match_fs(const struct st_accel_sensors *sensor, int fs,
+					struct fullscale_available *fs_out)
+{
+	int n, i, ret = -1;
+
+	n = ARRAY_SIZE(sensor->fs.fs_avl);
+	for (i = 0; i < n; i++)
+		if (sensor->fs.fs_avl[i].num == fs) {
+			fs_out->num = sensor->fs.fs_avl[i].num;
+			fs_out->gain = sensor->fs.fs_avl[i].gain;
+			fs_out->value = sensor->fs.fs_avl[i].value;
+			ret = 0;
+			break;
+		}
+
+	return ret;
+}
+
+int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+	int err;
+	struct st_acc_data *adata;
+
+	adata = iio_priv(indio_dev);
+	if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+		err = acc_write_data_with_mask(indio_dev,
+			st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
+			st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
+			(int)enable);
+		if (err < 0)
+			goto acc_set_dataready_irq_error;
+	}
+
+	if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+		err = acc_write_data_with_mask(indio_dev,
+		st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
+		st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
+			(int)enable);
+		if (err < 0)
+			goto acc_set_dataready_irq_error;
+	}
+
+	err = acc_write_data_with_mask(indio_dev,
+		st_accel_sensors[adata->index].drdy_irq.addr,
+		st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
+	if (err < 0)
+		goto acc_set_dataready_irq_error;
+
+	return 0;
+
+acc_set_dataready_irq_error:
+	return -EIO;
+}
+EXPORT_SYMBOL(acc_set_dataready_irq);
+
+static int set_bdu(struct iio_dev *indio_dev, const struct bdu *bdu, u8 value)
+{
+	int err;
+
+	err = acc_write_data_with_mask(indio_dev,
+					bdu->addr,
+					bdu->mask,
+					1,
+					value);
+
+	return err;
+}
+
+static int set_odr(struct iio_dev *indio_dev,
+					struct odr_available *odr_available)
+{
+	int err;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	if ((st_accel_sensors[adata->index].odr.addr ==
+		st_accel_sensors[adata->index].pw.addr) &&
+			(st_accel_sensors[adata->index].odr.mask ==
+				st_accel_sensors[adata->index].pw.mask)) {
+		if (atomic_read(&adata->enabled) == ST_ACCEL_ON) {
+			err = acc_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].odr.addr,
+				st_accel_sensors[adata->index].odr.mask,
+				st_accel_sensors[adata->index].odr.num_bit,
+				odr_available->value);
+			if (err < 0)
+				goto set_odr_error;
+		} else {
+			adata->odr = odr_available->hz;
+			err = 0;
+		}
+	} else {
+		err = acc_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].odr.addr,
+				st_accel_sensors[adata->index].odr.mask,
+				st_accel_sensors[adata->index].odr.num_bit,
+				odr_available->value);
+		if (err < 0)
+			goto set_odr_error;
+	}
+
+set_odr_error:
+	return err;
+}
+
+static int set_enable(struct iio_dev *indio_dev, int enable)
+{
+	int found, err;
+	u8 tmp_value;
+	struct odr_available *odr_out;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
+	if (odr_out == NULL) {
+		err = -ENOMEM;
+		goto odr_out_allocate_memory_error;
+	}
+
+	switch (enable) {
+	case ST_ACCEL_ON:
+		found = 0;
+		tmp_value = st_accel_sensors[adata->index].pw.value_on;
+		if ((st_accel_sensors[adata->index].odr.addr ==
+				st_accel_sensors[adata->index].pw.addr) &&
+			(st_accel_sensors[adata->index].odr.mask ==
+				st_accel_sensors[adata->index].pw.mask)) {
+			err = match_odr(&st_accel_sensors[adata->index],
+							adata->odr, odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out->value;
+			found = 1;
+		}
+		err = acc_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].pw.addr,
+				st_accel_sensors[adata->index].pw.mask,
+				st_accel_sensors[adata->index].pw.num_bit,
+				tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		atomic_set(&adata->enabled, ST_ACCEL_ON);
+		if (found == 1)
+			adata->odr = odr_out->hz;
+		break;
+	case ST_ACCEL_OFF:
+		err = acc_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].pw.addr,
+				st_accel_sensors[adata->index].pw.mask,
+				st_accel_sensors[adata->index].pw.num_bit,
+				st_accel_sensors[adata->index].pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		atomic_set(&adata->enabled, ST_ACCEL_OFF);
+		break;
+	default:
+		err = -1;
+		goto set_enable_error;
+	}
+
+set_enable_error:
+	kfree(odr_out);
+odr_out_allocate_memory_error:
+	return err;
+}
+
+static int set_fullscale(struct iio_dev *indio_dev,
+					struct fullscale_available *fs_avl)
+{
+	int err;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	err = acc_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].fs.addr,
+				st_accel_sensors[adata->index].fs.mask,
+				st_accel_sensors[adata->index].fs.num_bit,
+				fs_avl->value);
+	if (err < 0)
+		goto set_fullscale_error;
+	adata->fullscale = fs_avl->num;
+	adata->gain = fs_avl->gain;
+	return err;
+
+set_fullscale_error:
+	pr_err("%s: failed to set new fullscale.\n", adata->name);
+	return err;
+}
+
+static int acc_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	int data_tmp;
+	u8 outdata[ST_ACC_BYTE_FOR_CHANNEL];
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+			err = -EBUSY;
+			goto read_error;
+		} else {
+			err = atomic_read(&adata->enabled);
+			if (!err) {
+				err = -EHOSTDOWN;
+				goto read_error;
+			} else {
+				err = adata->read_multiple_byte(adata,
+					ch->address, ST_ACC_BYTE_FOR_CHANNEL,
+					outdata);
+				if (err < 0)
+					goto read_error;
+
+				if (ch->scan_type.endianness == IIO_LE)
+					*val = (s32)(s16)(cpu_to_le16(
+					le16_to_cpu(((__le16 *)outdata)[0])))
+					>> ch->scan_type.shift;
+				else
+					*val = (s32)(s16)(cpu_to_le16(
+					be16_to_cpu(((__be16 *)outdata)[0])))
+					>> ch->scan_type.shift;
+			}
+		}
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		data_tmp = UG_TO_MS2(adata->gain);
+		*val = 0;
+		*val2 = data_tmp;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	return err;
+}
+
+static int acc_check_irq(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	if (*adata->irq_data_ready <= 0)
+		err = -EINVAL;
+	else
+		err = 0;
+
+	return err;
+}
+
+static void register_channels(struct iio_dev *indio_dev)
+{
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	indio_dev->channels = st_accel_sensors[adata->index].ch;
+	indio_dev->num_channels = ST_ACC_NUMBER_ALL_CHANNELS;
+}
+
+static void set_sensor_parameters(struct iio_dev *indio_dev)
+{
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+}
+
+static int check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+	int i, sensor_length, found;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	found = 0;
+	sensor_length = ARRAY_SIZE(st_accel_sensors);
+	for (i = 0; i < sensor_length; i++) {
+		if (st_accel_sensors[i].wai == wai) {
+			found = 1;
+			break;
+		}
+	}
+	if (found != 1)
+		goto check_device_error;
+	adata->index = i;
+	return i;
+
+check_device_error:
+	pr_err("%s: device not supported -> wai (0x%x).\n", adata->name, wai);
+	return -ENODEV;
+}
+
+static int get_wai_device(struct iio_dev *indio_dev, u8 reg_addr, u8 *value)
+{
+	int ret;
+	u8 buf;
+	struct st_acc_data *adata;
+
+	adata = iio_priv(indio_dev);
+	buf = reg_addr;
+	ret = adata->read_byte(adata, reg_addr, value);
+	if (ret < 0)
+		goto read_byte_wai_error;
+
+	return 0;
+
+read_byte_wai_error:
+	pr_err("%s: failed to read wai register (0x%x).\n",
+							adata->name, reg_addr);
+	return -EIO;
+}
+
+static ssize_t sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned long freq;
+	struct odr_available *odr_out;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	err = kstrtoul(buf, 10, &freq);
+	if (err) {
+		pr_err("%s: input is not a number! (errn %d).\n",
+							adata->name, err);
+		goto sysfs_set_sampling_frequency_error;
+	}
+
+	mutex_lock(&indio_dev->mlock);
+	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
+	if (odr_out == NULL)
+		goto odr_out_allocate_memory_error;
+
+	err = match_odr(&st_accel_sensors[adata->index], (int)freq, odr_out);
+	if (err < 0)
+		goto sysfs_set_sampling_frequency_error;
+	err = set_odr(indio_dev, odr_out);
+	if (err < 0) {
+		pr_err("%s: failed to set sampling frequency to %d.\n",
+							adata->name, (int)freq);
+		goto sysfs_set_sampling_frequency_error;
+	}
+	adata->odr = odr_out->hz;
+	kfree(odr_out);
+
+odr_out_allocate_memory_error:
+	mutex_unlock(&indio_dev->mlock);
+sysfs_set_sampling_frequency_error:
+	return size;
+}
+
+static ssize_t sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	ret = sprintf(buf, "%d\n", adata->odr);
+
+	return ret;
+}
+
+static ssize_t sysfs_set_enable(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned long en;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	err = kstrtoul(buf, 10, &en);
+	if (err) {
+		pr_err("%s: input is not a number! (errn %d).\n",
+							adata->name, err);
+		goto set_enable_error;
+	}
+
+	mutex_lock(&indio_dev->mlock);
+	err = set_enable(indio_dev, (int)en);
+	if (err < 0)
+		pr_err("%s: failed to set enable to %d.\n",
+							adata->name, (int)en);
+	mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+	return size;
+}
+
+static ssize_t sysfs_get_enable(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int status;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	status = atomic_read(&adata->enabled);
+	ret = sprintf(buf, "%d\n", status);
+
+	return ret;
+}
+
+static ssize_t sysfs_get_fullscale(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	ret = sprintf(buf, "%d\n", adata->fullscale);
+
+	return ret;
+}
+
+static ssize_t sysfs_set_fullscale(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned long fs;
+	struct fullscale_available *fs_out;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	err = kstrtoul(buf, 10, &fs);
+	if (err) {
+		pr_err("%s: input is not a number! (errn %d).\n",
+							adata->name, err);
+		goto set_fullscale_error;
+	}
+
+	mutex_lock(&indio_dev->mlock);
+	fs_out = kzalloc(sizeof(*fs_out), GFP_KERNEL);
+	if (fs_out == NULL)
+		goto fs_out_allocate_memory_error;
+
+	err = match_fs(&st_accel_sensors[adata->index], fs, fs_out);
+	if (err < 0) {
+		pr_err("%s: input is not a valid fullscale! (errn %d).\n",
+							adata->name, err);
+		goto match_fullscale_error;
+	}
+	err = set_fullscale(indio_dev, fs_out);
+	if (err < 0) {
+		pr_err("%s: failed to set new fullscale. (errn %d).\n",
+							adata->name, err);
+	}
+
+match_fullscale_error:
+	kfree(fs_out);
+fs_out_allocate_memory_error:
+	mutex_unlock(&indio_dev->mlock);
+set_fullscale_error:
+	return size;
+}
+
+static ssize_t sysfs_fullscale_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int i, n, len;
+	char tmp[4];
+	char fullscale[30] = "";
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	n = ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
+	for (i = 0; i < n; i++) {
+		if (st_accel_sensors[adata->index].fs.fs_avl[i].num != 0) {
+			len = strlen(&fullscale[0]);
+			sprintf(tmp, "%d ",
+			st_accel_sensors[adata->index].fs.fs_avl[i].num);
+			strcpy(&fullscale[len], tmp);
+		} else
+			break;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return sprintf(buf, "%s\n", fullscale);
+}
+
+static ssize_t sysfs_sampling_frequency_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int i, n, len;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	n = ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
+	for (i = 0; i < n; i++) {
+		if (st_accel_sensors[adata->index].odr.odr_avl[i].hz != 0) {
+			len = strlen(buf);
+			sprintf(buf+len, "%d ",
+			st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+		} else
+			break;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	len = strlen(buf);
+	return sprintf(buf+len, "\n");
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+				sysfs_sampling_frequency_available, NULL , 0);
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+				sysfs_fullscale_available, NULL , 0);
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, sysfs_get_fullscale,
+				sysfs_set_fullscale , 0);
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, sysfs_get_enable,
+						sysfs_set_enable , 0);
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, sysfs_get_sampling_frequency,
+				sysfs_set_sampling_frequency);
+
+static struct attribute *acc_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_fullscale_available.dev_attr.attr,
+	&iio_dev_attr_fullscale.dev_attr.attr,
+	&iio_dev_attr_enable.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group acc_attribute_group = {
+	.attrs = acc_attributes,
+};
+
+static const struct iio_info acc_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &acc_attribute_group,
+	.read_raw = &acc_read_raw,
+};
+
+static int init_sensor(struct iio_dev *indio_dev)
+{
+	int err;
+	struct odr_available *odr_out;
+	struct fullscale_available *fs_out;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
+	if (odr_out == NULL)
+		goto odr_out_allocate_memory_error;
+	fs_out = kzalloc(sizeof(*fs_out), GFP_KERNEL);
+	if (fs_out == NULL)
+		goto fs_out_allocate_memory_error;
+	set_enable(indio_dev, ST_ACCEL_OFF);
+	match_fs(&st_accel_sensors[adata->index], adata->fullscale, fs_out);
+	err = set_fullscale(indio_dev, fs_out);
+	if (err < 0)
+		goto init_error;
+	match_odr(&st_accel_sensors[adata->index], adata->odr, odr_out);
+	err = set_odr(indio_dev, odr_out);
+	if (err < 0)
+		goto init_error;
+	err = set_bdu(indio_dev,
+			&st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
+	if (err < 0)
+		goto init_error;
+	kfree(odr_out);
+	kfree(fs_out);
+
+	return 0;
+
+init_error:
+	kfree(fs_out);
+fs_out_allocate_memory_error:
+	kfree(odr_out);
+odr_out_allocate_memory_error:
+	return -EIO;
+}
+
+int acc_iio_probe(struct iio_dev *indio_dev)
+{
+	int err;
+	u8 wai;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	mutex_init(&adata->slock);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &acc_info;
+
+	err = get_wai_device(indio_dev, ST_DEFAULT_WAI_ADDRESS, &wai);
+	if (err < 0)
+		goto get_wai_error;
+	err = check_device_list(indio_dev, wai);
+	if (err < 0)
+		goto check_device_list_error;
+	set_sensor_parameters(indio_dev);
+	register_channels(indio_dev);
+	err = init_sensor(indio_dev);
+	if (err < 0)
+		goto init_sensor_error;
+
+	err = acc_check_irq(indio_dev);
+	if (err < 0)
+		goto gpio_check_error;
+	err = acc_allocate_ring(indio_dev);
+	if (err < 0)
+		goto acc_allocate_ring_error;
+
+	err = acc_probe_trigger(indio_dev);
+	if (err < 0)
+		goto acc_probe_trigger_error;
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto iio_device_register_error;
+
+	pr_info("%s: probe end correctly.\n", adata->name);
+
+	return err;
+
+iio_device_register_error:
+	acc_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+	acc_deallocate_ring(indio_dev);
+acc_allocate_ring_error:
+gpio_check_error:
+init_sensor_error:
+check_device_list_error:
+get_wai_error:
+	return err;
+}
+EXPORT_SYMBOL(acc_iio_probe);
+
+void acc_iio_remove(struct iio_dev *indio_dev)
+{
+	acc_remove_trigger(indio_dev);
+	acc_deallocate_ring(indio_dev);
+	iio_device_unregister(indio_dev);
+	iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(acc_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/ST_accel_i2c.c b/drivers/iio/accel/ST_accel_i2c.c
new file mode 100644
index 0000000..55bca3a
--- /dev/null
+++ b/drivers/iio/accel/ST_accel_i2c.c
@@ -0,0 +1,165 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/ST_accel.h>
+#include <linux/platform_data/ST_accel_pdata.h>
+
+
+#define ST_ACCEL_I2C_MULTIREAD			0x80
+
+static inline s32 acc_i2c_read_byte(struct st_acc_data *adata, u8 reg_addr,
+								u8 *res_byte)
+{
+	s32 err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
+	if (err < 0)
+		goto acc_i2c_read_byte_error;
+	*res_byte = err & 0xff;
+	return err;
+
+acc_i2c_read_byte_error:
+	return -EIO;
+}
+
+static inline s32 acc_i2c_read_multiple_byte(struct st_acc_data *adata,
+						u8 reg_addr, int len, u8 *data)
+{
+	s32 err;
+
+	if (adata->multiread_bit == true)
+		reg_addr |= ST_ACCEL_I2C_MULTIREAD;
+
+	err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+							reg_addr, len, data);
+	if (err < 0)
+		goto acc_i2c_read_multiple_byte_error;
+
+	return err;
+
+acc_i2c_read_multiple_byte_error:
+	return -EIO;
+}
+
+static inline s32 acc_i2c_write_byte(struct st_acc_data *adata,
+							u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+								reg_addr, data);
+}
+
+void set_platform_data(struct i2c_client *client, struct st_acc_data *adata)
+{
+	if (client->dev.platform_data == NULL) {
+		adata->fullscale = st_acc_default_pdata.fullscale;
+		adata->odr = st_acc_default_pdata.sampling_frequency;
+	} else {
+		adata->fullscale = ((struct st_acc_platform_data *)
+			(client->dev.platform_data))->fullscale;
+		adata->odr = ((struct st_acc_platform_data *)
+			(client->dev.platform_data))->sampling_frequency;
+	}
+}
+
+void set_trigger_parent(struct iio_dev *indio_dev)
+{
+	struct i2c_client *client;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	client = to_i2c_client(adata->dev);
+	adata->trig->dev.parent = &client->dev;
+}
+
+static int __devinit acc_i2c_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_acc_data *adata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*adata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	adata = iio_priv(indio_dev);
+	adata->dev = &client->dev;
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	adata->read_byte = acc_i2c_read_byte;
+	adata->write_byte = acc_i2c_write_byte;
+	adata->read_multiple_byte = acc_i2c_read_multiple_byte;
+	adata->set_trigger_parent = set_trigger_parent;
+	adata->name = client->name;
+	adata->irq_data_ready = &client->irq;
+
+	set_platform_data(client, adata);
+	err = acc_iio_probe(indio_dev);
+	if (err < 0)
+		goto acc_iio_default_error;
+
+	return 0;
+
+acc_iio_default_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int __devexit acc_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	acc_iio_remove(indio_dev);
+	return 0;
+}
+
+static const struct i2c_device_id acc_id_table[] = {
+	{ LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+	{ LIS3DH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330D_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330DL_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303D_ACC_IIO_DEV_NAME, 0 },
+	{ LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+	{ LIS331DLH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DL_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330_ACC_IIO_DEV_NAME, 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, acc_id_table);
+
+static struct i2c_driver acc_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "ST-accel-i2c",
+	},
+	.probe = acc_i2c_probe,
+	.remove = __devexit_p(acc_i2c_remove),
+	.id_table = acc_id_table,
+};
+module_i2c_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/ST_accel_spi.c b/drivers/iio/accel/ST_accel_spi.c
new file mode 100644
index 0000000..c918715
--- /dev/null
+++ b/drivers/iio/accel/ST_accel_spi.c
@@ -0,0 +1,236 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/ST_accel.h>
+#include <linux/platform_data/ST_accel_pdata.h>
+
+
+#define ACC_SPI_READ		0x80;
+#define ACC_SPI_MULTIREAD	0xc0
+
+static inline s32 acc_spi_read_byte(struct st_acc_data *adata, u8 reg_addr,
+								u8 *res_byte)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = &tx,
+			.bits_per_word = 8,
+			.len = 1,
+			.cs_change = 0,
+			.delay_usecs = 10,
+		},
+		{
+			.rx_buf = res_byte,
+			.bits_per_word = 8,
+			.len = 1,
+			.cs_change = 0,
+			.delay_usecs = 10,
+		}
+	};
+
+	mutex_lock(&adata->slock);
+	tx = reg_addr | ACC_SPI_READ;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+	mutex_unlock(&adata->slock);
+	if (err)
+		goto acc_spi_read_byte_error;
+
+
+	return err;
+
+acc_spi_read_byte_error:
+	return -EIO;
+}
+
+static inline s32 acc_spi_read_multiple_byte(struct st_acc_data *adata,
+						u8 reg_addr, int len, u8 *data)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = &tx,
+			.bits_per_word = 8,
+			.len = 1,
+			.cs_change = 0,
+			.delay_usecs = 10,
+		},
+		{
+			.rx_buf = data,
+			.bits_per_word = 8,
+			.len = len,
+			.cs_change = 0,
+			.delay_usecs = 10,
+		}
+	};
+
+	mutex_lock(&adata->slock);
+	if (adata->multiread_bit == true)
+		tx = reg_addr | ACC_SPI_MULTIREAD;
+	else
+		tx = reg_addr | ACC_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+	mutex_unlock(&adata->slock);
+	if (err)
+		goto acc_spi_read_multiple_byte_error;
+	return len;
+
+acc_spi_read_multiple_byte_error:
+	return -EIO;
+}
+
+static inline s32 acc_spi_write_byte(struct st_acc_data *adata,
+							u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx[2];
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 0,
+			.delay_usecs = 10,
+		}
+	};
+
+	mutex_lock(&adata->slock);
+	tx[0] = reg_addr;
+	tx[1] = data;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+	mutex_unlock(&adata->slock);
+
+	return err;
+}
+
+void set_platform_data(struct spi_device *client, struct st_acc_data *adata)
+{
+	if (client->dev.platform_data == NULL) {
+		adata->fullscale = st_acc_default_pdata.fullscale;
+		adata->odr = st_acc_default_pdata.sampling_frequency;
+	} else {
+		adata->fullscale = ((struct st_acc_platform_data *)
+			(client->dev.platform_data))->fullscale;
+		adata->odr = ((struct st_acc_platform_data *)
+			(client->dev.platform_data))->sampling_frequency;
+	}
+}
+
+void set_trigger_parent(struct iio_dev *indio_dev)
+{
+	struct spi_device *client;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	client = to_spi_device(adata->dev);
+	adata->trig->dev.parent = &client->dev;
+}
+
+static int __devinit acc_spi_probe(struct spi_device *client)
+{
+	struct iio_dev *indio_dev;
+	struct st_acc_data *adata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*adata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	adata = iio_priv(indio_dev);
+	adata->dev = &client->dev;
+	spi_set_drvdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->modalias;
+
+	adata->read_byte = acc_spi_read_byte;
+	adata->write_byte = acc_spi_write_byte;
+	adata->read_multiple_byte = acc_spi_read_multiple_byte;
+	adata->set_trigger_parent = set_trigger_parent;
+	adata->name = client->modalias;
+	adata->irq_data_ready = &client->irq;
+
+	set_platform_data(client, adata);
+	err = acc_iio_probe(indio_dev);
+	if (err < 0)
+		goto acc_iio_default_error;
+
+	return 0;
+
+acc_iio_default_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int __devexit acc_spi_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+	acc_iio_remove(indio_dev);
+	return 0;
+}
+
+static const struct spi_device_id acc_id_table[] = {
+	{ LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+	{ LIS3DH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330D_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330DL_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303D_ACC_IIO_DEV_NAME, 0 },
+	{ LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+	{ LIS331DLH_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DL_ACC_IIO_DEV_NAME, 0 },
+	{ LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+	{ LSM330_ACC_IIO_DEV_NAME, 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, acc_id_table);
+
+static struct spi_driver acc_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "ST-accel-spi",
+	},
+	.probe = acc_spi_probe,
+	.remove = __devexit_p(acc_spi_remove),
+	.id_table = acc_id_table,
+};
+module_spi_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/ST_accel_trigger.c
b/drivers/iio/accel/ST_accel_trigger.c
new file mode 100644
index 0000000..4375c31
--- /dev/null
+++ b/drivers/iio/accel/ST_accel_trigger.c
@@ -0,0 +1,87 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/ST_accel.h>
+
+
+static int iio_trig_acc_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = trig->private_data;
+	return acc_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops iio_acc_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &iio_trig_acc_set_state,
+};
+
+int acc_probe_trigger(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (adata->trig == NULL) {
+		err = -ENOMEM;
+		pr_err("%s: failed to allocate iio trigger.\n", adata->name);
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(*adata->irq_data_ready,
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			adata->trig->name,
+			adata->trig);
+	if (err) {
+		pr_err("%s: failed to request threaded irq [%d].\n",
+					adata->name, *adata->irq_data_ready);
+		goto request_irq_error;
+	}
+	adata->trig->private_data = indio_dev;
+	adata->trig->ops = &iio_acc_trigger_ops;
+
+	adata->set_trigger_parent(indio_dev);
+
+	err = iio_trigger_register(adata->trig);
+	if (err < 0) {
+		pr_err("%s: failed to register iio trigger.\n", adata->name);
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = adata->trig;
+	pr_info("%s: using [%s] trigger.\n", adata->name, adata->trig->name);
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(*adata->irq_data_ready, adata->trig);
+request_irq_error:
+	iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(acc_probe_trigger);
+
+void acc_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct st_acc_data *adata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(adata->trig);
+	free_irq(*adata->irq_data_ready, adata->trig);
+	iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(acc_remove_trigger);
diff --git a/include/linux/iio/accel/ST_accel.h
b/include/linux/iio/accel/ST_accel.h
new file mode 100644
index 0000000..20d1973
--- /dev/null
+++ b/include/linux/iio/accel/ST_accel.h
@@ -0,0 +1,120 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Supported sensors:
+ * LSM303DLH
+ * LSM303DLHC
+ * LIS3DH
+ * LSM330D
+ * LSM330DL
+ * LSM330DLC
+ * LSM303D
+ * LSM9DS0
+ * LIS331DLH
+ * LSM303DL
+ * LSM303DLM
+ * LSM330
+ */
+
+
+#ifndef ST_ACCELEROMETERS_IIO_ACC_H
+#define ST_ACCELEROMETERS_IIO_ACC_H
+
+#define LSM303DLH_ACC_IIO_DEV_NAME	"lsm303dlh_acc_iio"
+#define LSM303DLHC_ACC_IIO_DEV_NAME	"lsm303dlhc_acc_iio"
+#define LIS3DH_ACC_IIO_DEV_NAME		"lis3dh_acc_iio"
+#define LSM330D_ACC_IIO_DEV_NAME	"lsm330d_acc_iio"
+#define LSM330DL_ACC_IIO_DEV_NAME	"lsm330dl_acc_iio"
+#define LSM330DLC_ACC_IIO_DEV_NAME	"lsm330dlc_acc_iio"
+#define LSM303D_ACC_IIO_DEV_NAME	"lsm303d_iio"
+#define LSM9DS0_ACC_IIO_DEV_NAME	"lsm9ds0_iio"
+#define LIS331DLH_ACC_IIO_DEV_NAME	"lis331dlh_acc_iio"
+#define LSM303DL_ACC_IIO_DEV_NAME	"lsm303dl_acc_iio"
+#define LSM303DLM_ACC_IIO_DEV_NAME	"lsm303dlm_acc_iio"
+#define LSM330_ACC_IIO_DEV_NAME		"lsm330_acc_iio"
+
+#define ST_ACC_NUMBER_ALL_CHANNELS	4
+#define ST_ACC_BYTE_FOR_CHANNEL		2
+#define ST_ACC_SCAN_X			0
+#define ST_ACC_SCAN_Y			1
+#define ST_ACC_SCAN_Z			2
+
+/**
+ * struct st_acc_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @name: Name of the sensor in use.
+ * @enabled: Status of the sensor (0->off, 1->on).
+ * @index: Number used to point the sensor being used in the
+ *	st_accel_sensors struct.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [ug/LSB].
+ * @odr: Output data rate of the sensor.
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ * @set_trigger_parent: Function used to set the trigger parent.
+ * @trig: The trigger in use by the core driver.
+ * @irq_data_ready: IRQ number for data ready on INT1 pin.
+ * @slock: mutex for read and write operation.
+ */
+
+struct st_acc_data {
+	struct device *dev;
+	char *name;
+	atomic_t enabled;
+	short index;
+
+	int fullscale;
+	int gain;
+	int odr;
+
+	bool multiread_bit;
+	int (*read_byte) (struct st_acc_data *adata, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_acc_data *adata, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_acc_data *adata, u8 reg_addr,
+							int len, u8 *data);
+	void (*set_trigger_parent) (struct iio_dev *indio_dev);
+
+	struct iio_trigger *trig;
+	int *irq_data_ready;
+	struct mutex slock;
+};
+
+int acc_iio_probe(struct iio_dev *indio_dev);
+void acc_iio_remove(struct iio_dev *indio_dev);
+int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int acc_probe_trigger(struct iio_dev *indio_dev);
+void acc_remove_trigger(struct iio_dev *indio_dev);
+int acc_allocate_ring(struct iio_dev *indio_dev);
+void acc_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int acc_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void acc_remove_trigger(struct iio_dev *indio_dev)
+{
+	return;
+}
+static inline int acc_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void acc_deallocate_ring(struct iio_dev *indio_dev)
+{
+	return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCELEROMETERS_IIO_ACC_H */
diff --git a/include/linux/platform_data/ST_accel_pdata.h
b/include/linux/platform_data/ST_accel_pdata.h
new file mode 100644
index 0000000..51f7b2c
--- /dev/null
+++ b/include/linux/platform_data/ST_accel_pdata.h
@@ -0,0 +1,34 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+
+#ifndef ST_ACCELEROMETERS_PDATA_ACC_H
+#define ST_ACCELEROMETERS_PDATA_ACC_H
+
+#define DEFAULT_ACCEL_ODR_AVL_100HZ		100
+#define DEFAULT_ACCEL_FS_AVL_2G			2
+
+/**
+ * struct st_acc_platform_data - ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */
+
+struct st_acc_platform_data {
+	int fullscale;
+	int sampling_frequency;
+};
+
+static const struct st_acc_platform_data st_acc_default_pdata = {
+	.fullscale = DEFAULT_ACCEL_FS_AVL_2G,
+	.sampling_frequency = DEFAULT_ACCEL_ODR_AVL_100HZ,
+};
+
+#endif /* ST_ACCELEROMETERS_PDATA_ACC_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-14 15:05         ` Denis Ciocca
@ 2012-10-14 19:08           ` Lars-Peter Clausen
  2012-10-16 17:51           ` Lars-Peter Clausen
  1 sibling, 0 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-14 19:08 UTC (permalink / raw)
  To: Denis Ciocca
  Cc: Jonathan Cameron, Pavel Machek, Denis CIOCCA, linux-iio,
	Jonathan Cameron, burman.yan

On 10/14/2012 05:05 PM, Denis Ciocca wrote:
> Hi guys,
> 
> according to your requests I have modified my driver. Only one things
> about this function:
> 
> static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> 
> You tell to me:
> 
>> In buffered mode the samples should not be postprocessed. Userspace expects the
>> samples in the format as described by the scan_type. The buffer demuxing should
>> be handled by the IIO core. If you set up available_scan_masks correctly it
>> will automatically do the right thing.
> 
> I agree with you for the postprocessed samples part, but I don't understand very
> well the second one.
> I saw the other drivers, if I check only the available_scan_masks variable
> I don't know which channels are actives and which not.
> I will read every time all data channels, but I choose which channel
> put in the buffer.

You can tell the IIO core that you can only handle certain combinations of
channels which can be sampled at the same time. E.g. in you case there is only
one possible combination, which is that all channels are always sampled. If in
userspace only a subset of that list is selected the IIO core will take of only
passing those samples on to userspace which it actually requested.

Right now you do this by hand in your driver. But if you setup
scan_masks_available for you IIO device with only one scan mask which has all
channels set you can skip that step and just pass the buffer containing all
samples on to the IIO core and it will take care of picking those out which are
actually needed. There are a few examples of this, grep for
available_scan_masks to find them.

I now there are also some examples of IIO drivers which do what you did in your
driver, but these are older drivers which were written when the demuxing
infrastructure in the core was not in place. But the plan is to switch this
over as we continue to improve the existing drivers.

Will try take a look at the new patch tomorrow.

- Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-14 15:05         ` Denis Ciocca
  2012-10-14 19:08           ` Lars-Peter Clausen
@ 2012-10-16 17:51           ` Lars-Peter Clausen
  2012-10-22  9:31             ` Denis CIOCCA
  1 sibling, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-16 17:51 UTC (permalink / raw)
  To: Denis Ciocca
  Cc: Jonathan Cameron, Pavel Machek, Denis CIOCCA, linux-iio,
	Jonathan Cameron, burman.yan

On 10/14/2012 05:05 PM, Denis Ciocca wrote:
> Hi guys,
> 
> according to your requests I have modified my driver. Only one things
> about this function:

Hi,

you did not address all the issues addressed during the last review. Most
importantly the functions, structs, defines, etc. are still no namespaced.
Please add a st_acc_ prefix to your functions, etc. Add a st_acc_spi_
st_acc_i2c_ prefix to functions which are spi or i2c specific.

> 
> static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> 
> You tell to me:
> 
>> In buffered mode the samples should not be postprocessed. Userspace expects the
>> samples in the format as described by the scan_type. The buffer demuxing should
>> be handled by the IIO core. If you set up available_scan_masks correctly it
>> will automatically do the right thing.
> 
> I agree with you for the postprocessed samples part, but I don't understand very
> well the second one.
> I saw the other drivers, if I check only the available_scan_masks variable
> I don't know which channels are actives and which not.
> I will read every time all data channels, but I choose which channel
> put in the buffer.
> 
> Thanks,
> Denis
> 
> 
> 
> 
> 
> From 0917bcc1e22462db0469709d1020b5378d91959c Mon Sep 17 00:00:00 2001
> From: userid <userid@MacBookAir.(none)>

This needs a proper from tag. See here
http://git-scm.com/book/en/Customizing-Git-Git-Configuration how to setup
your name and email for git.

> Date: Sun, 14 Oct 2012 00:05:54 +0200
> Subject: [PATCH] Add STMicroelectronics accelerometers driver to support I2C
>  and SPI devices.

The title should start with the subsystem tag, in this case "iio:accel:"
Also no fullstop at the end of the title, I'd also not include the "to
support I2C  and SPI devices" to keep the title short.

This also needs a short description of what the patch does.

E.g. "This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports ..."

Also it is helpful for reviews to include a small changelog of what was
changed in this revision of the driver.

> 
> ---
>  drivers/iio/accel/Kconfig                    |   32 +
>  drivers/iio/accel/Makefile                   |    6 +
>  drivers/iio/accel/ST_accel_buffer.c          |  122 +++
>  drivers/iio/accel/ST_accel_core.c            | 1335 ++++++++++++++++++++++++++
>  drivers/iio/accel/ST_accel_i2c.c             |  165 ++++
>  drivers/iio/accel/ST_accel_spi.c             |  236 +++++
>  drivers/iio/accel/ST_accel_trigger.c         |   87 ++
>  include/linux/iio/accel/ST_accel.h           |  120 +++
>  include/linux/platform_data/ST_accel_pdata.h |   34 +
>  9 files changed, 2137 insertions(+)
>  create mode 100644 drivers/iio/accel/ST_accel_buffer.c
>  create mode 100644 drivers/iio/accel/ST_accel_core.c
>  create mode 100644 drivers/iio/accel/ST_accel_i2c.c
>  create mode 100644 drivers/iio/accel/ST_accel_spi.c
>  create mode 100644 drivers/iio/accel/ST_accel_trigger.c
>  create mode 100644 include/linux/iio/accel/ST_accel.h
>  create mode 100644 include/linux/platform_data/ST_accel_pdata.h
> 

Make the file names all lowercase

> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..13cc44f 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,36 @@ config HID_SENSOR_ACCEL_3D
>  	  Say yes here to build support for the HID SENSOR
>  	  accelerometers 3D.
> 
> +config ST_ACCEL_3AXIS
> +	tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +	depends on (I2C || SPI)
> +	help
> +	  Say yes here to build support for STMicroelectronics accelerometers.
> +
> +choice
> +	prompt "Bus type"
> +	depends on ST_ACCEL_3AXIS

I don't think there is a reason to make SPI and I2C support exclusive of
each other. The driver should work just fine if both are built in.

> +
> +config ST_ACCEL_3AXIS_I2C
> +	bool "support I2C bus connection"
> +	depends on I2C && ST_ACCEL_3AXIS
> +	help
> +	  Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +config ST_ACCEL_3AXIS_SPI
> +	bool "support SPI bus connection"
> +	depends on SPI && ST_ACCEL_3AXIS
> +	help
> +	Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +endchoice
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +	tristate "support triggered buffer"
> +	depends on ST_ACCEL_3AXIS
> +	select IIO_TRIGGERED_BUFFER
> +	select IIO_BUFFER
> +	help
> +	  Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>  endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..b797db4 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>  #
> 
>  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +ST_accel-y := ST_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += ST_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += ST_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += ST_accel_trigger.o
> ST_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += ST_accel.o
> \ No newline at end of file
> diff --git a/drivers/iio/accel/ST_accel_buffer.c
> b/drivers/iio/accel/ST_accel_buffer.c
> new file mode 100644
> index 0000000..61ee836
> --- /dev/null
> +++ b/drivers/iio/accel/ST_accel_buffer.c
> @@ -0,0 +1,122 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include <linux/iio/accel/ST_accel.h>
> +
> +
> +#define ST_ACCEL_NUMBER_DATA_CHANNELS		3
> +
> +static int acc_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +	int len;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	len = adata->read_multiple_byte(adata,
> +		indio_dev->channels[ST_ACC_SCAN_X].address,
> +		ST_ACC_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
> +		rx_array);
> +	if (len < 0)
> +		goto read_error;
> +
> +	return len;
> +
> +read_error:
> +	return -EIO;
> +}
> +
> +static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int ret, i, n = 0;
> +	u8 *rx_array;
> +	u8 mask = 0x01;
> +	s16 *data = (s16 *)buf;
> +	int scan_count = bitmap_weight(indio_dev->active_scan_mask,
> +				       indio_dev->masklength);
> +
> +	rx_array = kzalloc(ST_ACC_BYTE_FOR_CHANNEL*
> +				ST_ACCEL_NUMBER_DATA_CHANNELS, GFP_KERNEL);
> +	if (rx_array == NULL)
> +		return -ENOMEM;
> +	ret = acc_read_all(indio_dev, rx_array);
> +	if (ret < 0)
> +		return ret;
> +	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
> +		if ((*indio_dev->active_scan_mask & mask) > 0) {
> +			if (indio_dev->channels[i].scan_type.endianness ==
> +								IIO_LE) {
> +				data[n] = (s16)(cpu_to_le16(le16_to_cpu((
> +				(__le16 *)rx_array)[i])));
> +				n++;
> +			} else {
> +				data[n] = (s16)(cpu_to_be16(be16_to_cpu((
> +				(__be16 *)rx_array)[i])));
> +				n++;
> +			}

Don't do any endian conversion, userspace expects the data to be layed out
as described by the scan_type

> +		}
> +		mask = mask << 1;
> +	}
> +	kfree(rx_array);
> +	return i*sizeof(data[0]);
> +}
> +
> +static irqreturn_t acc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	int len = 0;
> +	char *data;
> +
> +	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +	if (data == NULL)
> +		goto done;
> +	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
> +		len = acc_get_buffer_element(indio_dev, data);
> +	else
> +		goto done;
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
> +	iio_push_to_buffer(indio_dev->buffer, data);
> +	kfree(data);
> +
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct iio_buffer_setup_ops acc_buf_setup_ops = {
> +	.preenable = &iio_sw_buffer_preenable,
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +};

This is just the default buffer_ops for triggered buffers. Just pass NULL as
the last parameter to iio_triggered_buffer_setup.

> +
> +int acc_allocate_ring(struct iio_dev *indio_dev)
> +{
> +	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +			&acc_trigger_handler, &acc_buf_setup_ops);
> +}
> +EXPORT_SYMBOL(acc_allocate_ring);
> +
> +void acc_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +	iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(acc_deallocate_ring);
> diff --git a/drivers/iio/accel/ST_accel_core.c
> b/drivers/iio/accel/ST_accel_core.c
> new file mode 100644
> index 0000000..5b30155
> --- /dev/null
> +++ b/drivers/iio/accel/ST_accel_core.c
> @@ -0,0 +1,1335 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include <linux/iio/accel/ST_accel.h>
> +
> +
[...]
> +
> +struct odr_available {
> +	int hz;

unsigned int some of the other structs memebers could also be made unsigned

> +	u8 value;
> +};
> +
> [...]
> +
> +/**
> + * struct st_accel_sensors - ST accel sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.

extra space

> + */
> +
> +static const struct st_accel_sensors {
> +	u8 wai;
> +	struct iio_chan_spec *ch;
> +	struct odr odr;
> +	struct power pw;
> +	struct fullscale fs;
> +	struct bdu bdu;
> +	struct data_ready_irq drdy_irq;
> +	bool multi_read_bit;
> +} st_accel_sensors[] = {
> +	{

An extra level of indention would be good after this line

> +	.wai = S1_WAI_EXP,
> +	.ch = (struct iio_chan_spec *)default_channels,
[...]
> }
> +
> +static int acc_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +						u8 mask, short num_bit, u8 data)
> +{
> +	int err, j, pos;
> +	u8 prev_data;
> +	u8 new_data;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	pos = 7;
> +	for (j = 128; j >= 0; j = j/2) {
> +		if (mask/j > 0)

Missing space around the "/"

> +			break;
> +		else

I'd remove the else

> +			pos--;
> +	}
> +
> +	err = adata->read_byte(adata, reg_addr, &prev_data);
> +	if (err < 0)
> +		goto i2c_write_data_with_mask_error;
> +
> +	new_data = ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & mask));
> +	err = adata->write_byte(adata, reg_addr, new_data);
> +
> +i2c_write_data_with_mask_error:

Since this is not i2c specific I'd remove the "i2c" from the label name

> +	return err;
> +}
> +
> +static int match_odr(const struct st_accel_sensors *sensor, int odr,
> +						struct odr_available *odr_out)
> +{
> +	int n, i, ret = -1;
> +
> +	n = ARRAY_SIZE(sensor->odr.odr_avl);
> +	for (i = 0; i < n; i++)

I'd put the ARRAY_SIZE in the for header, also brackets around the for body.

> +		if (sensor->odr.odr_avl[i].hz == odr) {
> +			odr_out->hz = sensor->odr.odr_avl[i].hz;
> +			odr_out->value = sensor->odr.odr_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +
> +	return ret;
> +}
> +
> +static int match_fs(const struct st_accel_sensors *sensor, int fs,
> +					struct fullscale_available *fs_out)
> +{
> +	int n, i, ret = -1;
> +
> +	n = ARRAY_SIZE(sensor->fs.fs_avl);
> +	for (i = 0; i < n; i++)

same here

> +		if (sensor->fs.fs_avl[i].num == fs) {
> +			fs_out->num = sensor->fs.fs_avl[i].num;
> +			fs_out->gain = sensor->fs.fs_avl[i].gain;
> +			fs_out->value = sensor->fs.fs_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +
> +	return ret;
> +}
> +
> +int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
> +{
> +	int err;
> +	struct st_acc_data *adata;
> +
> +	adata = iio_priv(indio_dev);
> +	if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
> +		err = acc_write_data_with_mask(indio_dev,
> +			st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
> +			st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto acc_set_dataready_irq_error;
> +	}
> +
> +	if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
> +		err = acc_write_data_with_mask(indio_dev,
> +		st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
> +		st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto acc_set_dataready_irq_error;
> +	}
> +
> +	err = acc_write_data_with_mask(indio_dev,
> +		st_accel_sensors[adata->index].drdy_irq.addr,
> +		st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
> +	if (err < 0)
> +		goto acc_set_dataready_irq_error;
> +
> +	return 0;
> +
> +acc_set_dataready_irq_error:
> +	return -EIO;

just pass on the error code in err.

> +}
> +EXPORT_SYMBOL(acc_set_dataready_irq);
> +
> +static int set_bdu(struct iio_dev *indio_dev, const struct bdu *bdu, u8 value)
> +{
> +	int err;
> +
> +	err = acc_write_data_with_mask(indio_dev,
> +					bdu->addr,
> +					bdu->mask,
> +					1,
> +					value);

I think this would fit all in one line without hitting the 80 char limit

> +
> +	return err;
> +}
> +
> +static int set_odr(struct iio_dev *indio_dev,
> +					struct odr_available *odr_available)
> +{
> +	int err;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	if ((st_accel_sensors[adata->index].odr.addr ==
> +		st_accel_sensors[adata->index].pw.addr) &&
> +			(st_accel_sensors[adata->index].odr.mask ==
> +				st_accel_sensors[adata->index].pw.mask)) {
> +		if (atomic_read(&adata->enabled) == ST_ACCEL_ON) {
> +			err = acc_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].odr.addr,
> +				st_accel_sensors[adata->index].odr.mask,
> +				st_accel_sensors[adata->index].odr.num_bit,
> +				odr_available->value);
> +			if (err < 0)
> +				goto set_odr_error;
> +		} else {
> +			adata->odr = odr_available->hz;
> +			err = 0;
> +		}
> +	} else {
> +		err = acc_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].odr.addr,
> +				st_accel_sensors[adata->index].odr.mask,
> +				st_accel_sensors[adata->index].odr.num_bit,
> +				odr_available->value);
> +		if (err < 0)
> +			goto set_odr_error;
> +	}
> +
> +set_odr_error:
> +	return err;
> +}
> +
> +static int set_enable(struct iio_dev *indio_dev, int enable)
> +{
> +	int found, err;
> +	u8 tmp_value;
> +	struct odr_available *odr_out;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
> +	if (odr_out == NULL) {
> +		err = -ENOMEM;
> +		goto odr_out_allocate_memory_error;
> +	}

Allocate odr_out on the stack

> +
> +	switch (enable) {
> +	case ST_ACCEL_ON:
> +		found = 0;
> +		tmp_value = st_accel_sensors[adata->index].pw.value_on;
> +		if ((st_accel_sensors[adata->index].odr.addr ==
> +				st_accel_sensors[adata->index].pw.addr) &&
> +			(st_accel_sensors[adata->index].odr.mask ==
> +				st_accel_sensors[adata->index].pw.mask)) {
> +			err = match_odr(&st_accel_sensors[adata->index],
> +							adata->odr, odr_out);
> +			if (err < 0)
> +				goto set_enable_error;
> +			tmp_value = odr_out->value;
> +			found = 1;
> +		}
> +		err = acc_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].pw.addr,
> +				st_accel_sensors[adata->index].pw.mask,
> +				st_accel_sensors[adata->index].pw.num_bit,
> +				tmp_value);
> +		if (err < 0)
> +			goto set_enable_error;
> +		atomic_set(&adata->enabled, ST_ACCEL_ON);
> +		if (found == 1)
> +			adata->odr = odr_out->hz;
> +		break;
> +	case ST_ACCEL_OFF:
> +		err = acc_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].pw.addr,
> +				st_accel_sensors[adata->index].pw.mask,
> +				st_accel_sensors[adata->index].pw.num_bit,
> +				st_accel_sensors[adata->index].pw.value_off);
> +		if (err < 0)
> +			goto set_enable_error;
> +		atomic_set(&adata->enabled, ST_ACCEL_OFF);
> +		break;
> +	default:
> +		err = -1;

Use a proper error value e.g. -EINVAL

> +		goto set_enable_error;
> +	}
> +
> +set_enable_error:
> +	kfree(odr_out);
> +odr_out_allocate_memory_error:
> +	return err;
> +}
> +
> [...]
> +
> +static int acc_read_raw(struct iio_dev *indio_dev,
> +			struct iio_chan_spec const *ch, int *val,
> +							int *val2, long mask)
> +{
> +	int err;
> +	int data_tmp;
> +	u8 outdata[ST_ACC_BYTE_FOR_CHANNEL];
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +			err = -EBUSY;
> +			goto read_error;
> +		} else {
> +			err = atomic_read(&adata->enabled);
> +			if (!err) {
> +				err = -EHOSTDOWN;

EHOSTDOWN is not a appropricate error in this case. This is not a socket!

> +				goto read_error;
> +			} else {
> +				err = adata->read_multiple_byte(adata,
> +					ch->address, ST_ACC_BYTE_FOR_CHANNEL,
> +					outdata);
> +				if (err < 0)
> +					goto read_error;
> +
> +				if (ch->scan_type.endianness == IIO_LE)
> +					*val = (s32)(s16)(cpu_to_le16(
> +					le16_to_cpu(((__le16 *)outdata)[0])))
> +					>> ch->scan_type.shift;
> +				else
> +					*val = (s32)(s16)(cpu_to_le16(
> +					be16_to_cpu(((__be16 *)outdata)[0])))
> +					>> ch->scan_type.shift;

All these casts look kind of crazy.

> +			}
> +		}
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		data_tmp = UG_TO_MS2(adata->gain);
> +		*val = 0;
> +		*val2 = data_tmp;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +read_error:
> +	return err;
> +}
> +
> +static int acc_check_irq(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	if (*adata->irq_data_ready <= 0)
> +		err = -EINVAL;
> +	else
> +		err = 0;
> +

There is onlu one caller of this function, just inline the code int that
function

> +	return err;
> +}
> +
> +static void register_channels(struct iio_dev *indio_dev)
> +{
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	indio_dev->channels = st_accel_sensors[adata->index].ch;
> +	indio_dev->num_channels = ST_ACC_NUMBER_ALL_CHANNELS;

Same here

> +}
> +
> +static void set_sensor_parameters(struct iio_dev *indio_dev)
> +{
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;

Same here

> +}
> +
> +static int check_device_list(struct iio_dev *indio_dev, u8 wai)
> +{
> +	int i, sensor_length, found;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	found = 0;
> +	sensor_length = ARRAY_SIZE(st_accel_sensors);
> +	for (i = 0; i < sensor_length; i++) {

I'd just put the ARRAY_SIZE(...) in the for header and get rid of the extra
variable.

> +		if (st_accel_sensors[i].wai == wai) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +	if (found != 1)
> +		goto check_device_error;
> +	adata->index = i;
> +	return i;
> +
> +check_device_error:
> +	pr_err("%s: device not supported -> wai (0x%x).\n", adata->name, wai);

dev_err. As a rule of thumb don't use the pr_ functions in device drivers.

> +	return -ENODEV;
> +}
> +
> +static int get_wai_device(struct iio_dev *indio_dev, u8 reg_addr, u8 *value)
> +{
> +	int ret;
> +	u8 buf;
> +	struct st_acc_data *adata;
> +
> +	adata = iio_priv(indio_dev);
> +	buf = reg_addr;
> +	ret = adata->read_byte(adata, reg_addr, value);
> +	if (ret < 0)
> +		goto read_byte_wai_error;
> +
> +	return 0;
> +
> +read_byte_wai_error:
> +	pr_err("%s: failed to read wai register (0x%x).\n",
> +							adata->name, reg_addr);

dev_err, there are a few more pr_errs throughout the driver, those should be
fixed up as well. Useing dev_err will also allow you to remove the name
field from the st_acc_data struct

> +	return -EIO;
> +}
> +
> +static ssize_t sysfs_set_sampling_frequency(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	int err;
> +	unsigned long freq;
> +	struct odr_available *odr_out;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	err = kstrtoul(buf, 10, &freq);

use kstrtoint instead of casting freq to int later on

> +	if (err) {
> +		pr_err("%s: input is not a number! (errn %d).\n",
> +							adata->name, err);

This message is just noise

> +		goto sysfs_set_sampling_frequency_error;
> +	}
> +
> +	mutex_lock(&indio_dev->mlock);
> +	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);

Allocate odr_out on the stack

> +	if (odr_out == NULL)
> +		goto odr_out_allocate_memory_error;
> +
> +	err = match_odr(&st_accel_sensors[adata->index], (int)freq, odr_out);
> +	if (err < 0)
> +		goto sysfs_set_sampling_frequency_error;
> +	err = set_odr(indio_dev, odr_out);
> +	if (err < 0) {
> +		pr_err("%s: failed to set sampling frequency to %d.\n",
> +							adata->name, (int)freq);
> +		goto sysfs_set_sampling_frequency_error;
> +	}
> +	adata->odr = odr_out->hz;
> +	kfree(odr_out);
> +
> +odr_out_allocate_memory_error:
> +	mutex_unlock(&indio_dev->mlock);
> +sysfs_set_sampling_frequency_error:
> +	return size;
> +}
> +
> +static ssize_t sysfs_get_sampling_frequency(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	ssize_t ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	ret = sprintf(buf, "%d\n", adata->odr);
> +
> +	return ret;

I'd just do return sprintf(...)

> +}
> +
> +static ssize_t sysfs_set_enable(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	int err;
> +	unsigned long en;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +



> +	err = kstrtoul(buf, 10, &en);
> +	if (err) {
> +		pr_err("%s: input is not a number! (errn %d).\n",
> +							adata->name, err);
> +		goto set_enable_error;
> +	}

strtobool

> +
> +	mutex_lock(&indio_dev->mlock);
> +	err = set_enable(indio_dev, (int)en);
> +	if (err < 0)
> +		pr_err("%s: failed to set enable to %d.\n",
> +							adata->name, (int)en);
> +	mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +	return size;
> +}
> +
> +static ssize_t sysfs_get_enable(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	ssize_t ret;
> +	int status;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	status = atomic_read(&adata->enabled);
> +	ret = sprintf(buf, "%d\n", status);

I'd just do return sprintf(...)

> +
> +	return ret;
> +}
> +
> +static ssize_t sysfs_get_fullscale(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	ssize_t ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	ret = sprintf(buf, "%d\n", adata->fullscale);

same here

> +
> +	return ret;
> +}
> +
> +static ssize_t sysfs_set_fullscale(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	int err;
> +	unsigned long fs;
> +	struct fullscale_available *fs_out;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	err = kstrtoul(buf, 10, &fs);
> +	if (err) {
> +		pr_err("%s: input is not a number! (errn %d).\n",
> +							adata->name, err);

This is just noise, the caller will be notified by the error code that it's
input was invalid.

> +		goto set_fullscale_error;
> +	}
> +
> +	mutex_lock(&indio_dev->mlock);
> +	fs_out = kzalloc(sizeof(*fs_out), GFP_KERNEL);

Allocate fs_out on the stack

> +	if (fs_out == NULL)
> +		goto fs_out_allocate_memory_error;
> +
> +	err = match_fs(&st_accel_sensors[adata->index], fs, fs_out);
> +	if (err < 0) {
> +		pr_err("%s: input is not a valid fullscale! (errn %d).\n",
> +							adata->name, err);

This too

> +		goto match_fullscale_error;
> +	}
> +	err = set_fullscale(indio_dev, fs_out);
> +	if (err < 0) {
> +		pr_err("%s: failed to set new fullscale. (errn %d).\n",
> +							adata->name, err);
> +	}
> +
> +match_fullscale_error:
> +	kfree(fs_out);
> +fs_out_allocate_memory_error:
> +	mutex_unlock(&indio_dev->mlock);
> +set_fullscale_error:
> +	return size;
> +}
> +
> +static ssize_t sysfs_fullscale_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, n, len;
> +	char tmp[4];
> +	char fullscale[30] = "";
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	n = ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
> +	for (i = 0; i < n; i++) {
> +		if (st_accel_sensors[adata->index].fs.fs_avl[i].num != 0) {
> +			len = strlen(&fullscale[0]);
> +			sprintf(tmp, "%d ",
> +			st_accel_sensors[adata->index].fs.fs_avl[i].num);
> +			strcpy(&fullscale[len], tmp);
> +		} else
> +			break;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return sprintf(buf, "%s\n", fullscale);

This function does still the crazy sprintf, strcpy, sprintf combo

In general the same comments as to sysfs_sampling_frequency_available apply
here.

> +}
> +
> +static ssize_t sysfs_sampling_frequency_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, n, len;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	n = ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
> +	for (i = 0; i < n; i++) {
> +		if (st_accel_sensors[adata->index].odr.odr_avl[i].hz != 0) {

I'd rewrite this as

		if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
			break;
		...


> +			len = strlen(buf);
> +			sprintf(buf+len, "%d ",
> +			st_accel_sensors[adata->index].odr.odr_avl[i].hz);

sprintf returns the number of characters written, so you don't need to run
strlen each loop interation. Also use scnprint with a limit of PAGE_SIZE
this avoids the (theoretical) buffer overflow. Your code could look
something like this:

len += scnprintf(buf+len, PAGE_SIZE, ...)

> +		} else
> +			break;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	len = strlen(buf);
> +	return sprintf(buf+len, "\n");

This will return 1 instead of the actuall string length. I'd rewrite this as

	buf[len] = "\n"
	return len;

This will replace the last space in the string with a newline, so you won't
end up with a trailing space.


> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +				sysfs_sampling_frequency_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
> +				sysfs_fullscale_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, sysfs_get_fullscale,
> +				sysfs_set_fullscale , 0);
> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, sysfs_get_enable,
> +						sysfs_set_enable , 0);
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, sysfs_get_sampling_frequency,
> +				sysfs_set_sampling_frequency);
> +
> +static struct attribute *acc_attributes[] = {
> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +	&iio_dev_attr_fullscale_available.dev_attr.attr,
> +	&iio_dev_attr_fullscale.dev_attr.attr,
> +	&iio_dev_attr_enable.dev_attr.attr,
> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
> +	NULL,
> +};

Non standard attributes need to be documented.

> +
> +static int init_sensor(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct odr_available *odr_out;
> +	struct fullscale_available *fs_out;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
> +	if (odr_out == NULL)
> +		goto odr_out_allocate_memory_error;
> +	fs_out = kzalloc(sizeof(*fs_out), GFP_KERNEL);
> +	if (fs_out == NULL)
> +		goto fs_out_allocate_memory_error;
> +	set_enable(indio_dev, ST_ACCEL_OFF);
> +	match_fs(&st_accel_sensors[adata->index], adata->fullscale, fs_out);
> +	err = set_fullscale(indio_dev, fs_out);
> +	if (err < 0)
> +		goto init_error;
> +	match_odr(&st_accel_sensors[adata->index], adata->odr, odr_out);
> +	err = set_odr(indio_dev, odr_out);
> +	if (err < 0)
> +		goto init_error;
> +	err = set_bdu(indio_dev,
> +			&st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
> +	if (err < 0)
> +		goto init_error;
> +	kfree(odr_out);
> +	kfree(fs_out);

Allocate both odr_out and fs_out on the stack.

Also this function could use a few newlines.

> +
> +	return 0;
> +
> +init_error:
> +	kfree(fs_out);
> +fs_out_allocate_memory_error:
> +	kfree(odr_out);
> +odr_out_allocate_memory_error:
> +	return -EIO;
> +}
> +
> +int acc_iio_probe(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	u8 wai;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	mutex_init(&adata->slock);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &acc_info;
> +
> +	err = get_wai_device(indio_dev, ST_DEFAULT_WAI_ADDRESS, &wai);
> +	if (err < 0)
> +		goto get_wai_error;

Missing newline

> +	err = check_device_list(indio_dev, wai);
> +	if (err < 0)
> +		goto check_device_list_error;

Missing newline

> +	set_sensor_parameters(indio_dev);
> +	register_channels(indio_dev);

Missing newline

> +	err = init_sensor(indio_dev);
> +	if (err < 0)
> +		goto init_sensor_error;
> +
> +	err = acc_check_irq(indio_dev);
> +	if (err < 0)
> +		goto gpio_check_error;

Missing newline

> +	err = acc_allocate_ring(indio_dev);
> +	if (err < 0)
> +		goto acc_allocate_ring_error;
> +
> +	err = acc_probe_trigger(indio_dev);
> +	if (err < 0)
> +		goto acc_probe_trigger_error;
> +
> +	err = iio_device_register(indio_dev);
> +	if (err)
> +		goto iio_device_register_error;
> +
> +	pr_info("%s: probe end correctly.\n", adata->name);

This is just noise. Imagine every driver doing this you'd end up with quite
a few lines of "Drivers has probed correctly" in your bootlog.

> +
> +	return err;
> +
> +iio_device_register_error:
> +	acc_remove_trigger(indio_dev);
> +acc_probe_trigger_error:
> +	acc_deallocate_ring(indio_dev);
> +acc_allocate_ring_error:
> +gpio_check_error:
> +init_sensor_error:
> +check_device_list_error:
> +get_wai_error:

No need for all these labels

> +	return err;
> +}
> +EXPORT_SYMBOL(acc_iio_probe);
> +
> +void acc_iio_remove(struct iio_dev *indio_dev)
> +{
> +	acc_remove_trigger(indio_dev);
> +	acc_deallocate_ring(indio_dev);
> +	iio_device_unregister(indio_dev);
> +	iio_device_free(indio_dev);

Rule of thumb: First iio_device_unregister then everything else and
iio_device_free last

> +}
> +EXPORT_SYMBOL(acc_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/ST_accel_i2c.c b/drivers/iio/accel/ST_accel_i2c.c
> new file mode 100644
> index 0000000..55bca3a
> --- /dev/null
> +++ b/drivers/iio/accel/ST_accel_i2c.c
> @@ -0,0 +1,165 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/ST_accel.h>
> +#include <linux/platform_data/ST_accel_pdata.h>
> +
> +
> +#define ST_ACCEL_I2C_MULTIREAD			0x80
> +
> +static inline s32 acc_i2c_read_byte(struct st_acc_data *adata, u8 reg_addr,
> +								u8 *res_byte)


This function obviously can not be inlined since it is used as a callback
and use int for the return type

> +{
> +	s32 err;
> +
> +	err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +	if (err < 0)
> +		goto acc_i2c_read_byte_error;
> +	*res_byte = err & 0xff;
> +	return err;
> +
> +acc_i2c_read_byte_error:
> +	return -EIO;
> +}
> +
> +static inline s32 acc_i2c_read_multiple_byte(struct st_acc_data *adata,
> +						u8 reg_addr, int len, u8 *data)

same here

> +{
> +	s32 err;
> +
> +	if (adata->multiread_bit == true)
> +		reg_addr |= ST_ACCEL_I2C_MULTIREAD;
> +
> +	err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +							reg_addr, len, data);
> +	if (err < 0)
> +		goto acc_i2c_read_multiple_byte_error;
> +
> +	return err;
> +
> +acc_i2c_read_multiple_byte_error:
> +	return -EIO;
> +}
> +
> +static inline s32 acc_i2c_write_byte(struct st_acc_data *adata,
> +							u8 reg_addr, u8 data)

same here

> +{
> +	return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
> +								reg_addr, data);
> +}
> +
[...]
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/ST_accel_spi.c b/drivers/iio/accel/ST_accel_spi.c
> new file mode 100644
> index 0000000..c918715
> --- /dev/null
> +++ b/drivers/iio/accel/ST_accel_spi.c
> @@ -0,0 +1,236 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/ST_accel.h>
> +#include <linux/platform_data/ST_accel_pdata.h>
> +
> +
> +#define ACC_SPI_READ		0x80;
> +#define ACC_SPI_MULTIREAD	0xc0
> +
> +static inline s32 acc_spi_read_byte(struct st_acc_data *adata, u8 reg_addr,
> +								u8 *res_byte)

This function obviously can not be inlined since it is used as a callback
and use int for the return type

> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = &tx,
> +			.bits_per_word = 8,
> +			.len = 1,
> +			.cs_change = 0,
> +			.delay_usecs = 10,
> +		},
> +		{
> +			.rx_buf = res_byte,
> +			.bits_per_word = 8,
> +			.len = 1,
> +			.cs_change = 0,
> +			.delay_usecs = 10,
> +		}
> +	};
> +
> +	mutex_lock(&adata->slock);
> +	tx = reg_addr | ACC_SPI_READ;
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +	mutex_unlock(&adata->slock);
> +	if (err)
> +		goto acc_spi_read_byte_error;
> +
> +

Rule of thumb: You should never have multiple consecutive newlines

> +	return err;
> +
> +acc_spi_read_byte_error:
> +	return -EIO;
> +}
> +
> +static inline s32 acc_spi_read_multiple_byte(struct st_acc_data *adata,
> +						u8 reg_addr, int len, u8 *data)

same here

> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = &tx,
> +			.bits_per_word = 8,
> +			.len = 1,
> +			.cs_change = 0,
> +			.delay_usecs = 10,
> +		},
> +		{
> +			.rx_buf = data,
> +			.bits_per_word = 8,
> +			.len = len,
> +			.cs_change = 0,
> +			.delay_usecs = 10,
> +		}
> +	};
> +
> +	mutex_lock(&adata->slock);
> +	if (adata->multiread_bit == true)
> +		tx = reg_addr | ACC_SPI_MULTIREAD;
> +	else
> +		tx = reg_addr | ACC_SPI_READ;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +	mutex_unlock(&adata->slock);
> +	if (err)
> +		goto acc_spi_read_multiple_byte_error;
> +	return len;
> +
> +acc_spi_read_multiple_byte_error:
> +	return -EIO;
> +}
> +
> +static inline s32 acc_spi_write_byte(struct st_acc_data *adata,
> +							u8 reg_addr, u8 data)

same here

> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx[2];
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 0,

Usually if cs_change is 0 it is not explicitly initalized.

> +			.delay_usecs = 10,
> +		}
> +	};
> +
> +	mutex_lock(&adata->slock);
> +	tx[0] = reg_addr;
> +	tx[1] = data;
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +	mutex_unlock(&adata->slock);
> +
> +	return err;
> +}
> +
> +void set_platform_data(struct spi_device *client, struct st_acc_data *adata)
> +{
> +	if (client->dev.platform_data == NULL) {
> +		adata->fullscale = st_acc_default_pdata.fullscale;
> +		adata->odr = st_acc_default_pdata.sampling_frequency;
> +	} else {
> +		adata->fullscale = ((struct st_acc_platform_data *)
> +			(client->dev.platform_data))->fullscale;
> +		adata->odr = ((struct st_acc_platform_data *)
> +			(client->dev.platform_data))->sampling_frequency;
> +	}
> +}

I'd move this to the generic part of the driver, it's the same for both SPI
and I2C. Also instead of the casting create a helper variale and use that.

struct st_acc_platform_data *pdata = adata->dev->platform_data;
adata->fullscale = pdata->fullscale;
...

> +
> +void set_trigger_parent(struct iio_dev *indio_dev)
> +{
> +	struct spi_device *client;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	client = to_spi_device(adata->dev);
> +	adata->trig->dev.parent = &client->dev;
> +}

This should not be necessary. You convert your device to a SPI device and
then read the device again from the spi device. So in the end adata->dev and
&client->dev are the same. There is no need for this callback, just do
adata->trig->dev.parent = ata->dev; in the generic code.

> +
> +static int __devinit acc_spi_probe(struct spi_device *client)
> +{
> +	struct iio_dev *indio_dev;
> +	struct st_acc_data *adata;
> +	int err;
> +
> +	indio_dev = iio_device_alloc(sizeof(*adata));
> +	if (indio_dev == NULL) {
> +		err = -ENOMEM;
> +		goto iio_device_alloc_error;
> +	}
> +
> +	adata = iio_priv(indio_dev);
> +	adata->dev = &client->dev;
> +	spi_set_drvdata(client, indio_dev);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->name = client->modalias;
> +
> +	adata->read_byte = acc_spi_read_byte;
> +	adata->write_byte = acc_spi_write_byte;
> +	adata->read_multiple_byte = acc_spi_read_multiple_byte;
> +	adata->set_trigger_parent = set_trigger_parent;
> +	adata->name = client->modalias;
> +	adata->irq_data_ready = &client->irq;
> +
> +	set_platform_data(client, adata);
> +	err = acc_iio_probe(indio_dev);
> +	if (err < 0)
> +		goto acc_iio_default_error;
> +
> +	return 0;
> +
> +acc_iio_default_error:
> +	iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +	return err;
> +}
> +
> +static int __devexit acc_spi_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +
> +	acc_iio_remove(indio_dev);
> +	return 0;
> +}
> +
> +static const struct spi_device_id acc_id_table[] = {
> +	{ LSM303DLH_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
> +	{ LIS3DH_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM330D_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM330DL_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM330DLC_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM303D_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM9DS0_ACC_IIO_DEV_NAME, 0 },
> +	{ LIS331DLH_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM303DL_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM303DLM_ACC_IIO_DEV_NAME, 0 },
> +	{ LSM330_ACC_IIO_DEV_NAME, 0 },
> +	{},

Since the second field won't be used in the driver you can just leave the it
blank,
e.g. { LSM330_ACC_IIO_DEV_NAME },

> +};
> +MODULE_DEVICE_TABLE(spi, acc_id_table);
> +
> +static struct spi_driver acc_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "ST-accel-spi",
> +	},
> +	.probe = acc_spi_probe,
> +	.remove = __devexit_p(acc_spi_remove),
> +	.id_table = acc_id_table,
> +};
> +module_spi_driver(acc_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/ST_accel_trigger.c
> b/drivers/iio/accel/ST_accel_trigger.c
> new file mode 100644
> index 0000000..4375c31
> --- /dev/null
> +++ b/drivers/iio/accel/ST_accel_trigger.c
> @@ -0,0 +1,87 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/ST_accel.h>
> +
> +
> +static int iio_trig_acc_set_state(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *indio_dev = trig->private_data;
> +	return acc_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops iio_acc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &iio_trig_acc_set_state,
> +};
> +
> +int acc_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +	if (adata->trig == NULL) {
> +		err = -ENOMEM;
> +		pr_err("%s: failed to allocate iio trigger.\n", adata->name);
> +		goto iio_trigger_alloc_error;
> +	}
> +
> +	err = request_threaded_irq(*adata->irq_data_ready,
> +			iio_trigger_generic_data_rdy_poll,
> +			NULL,
> +			IRQF_TRIGGER_RISING,
> +			adata->trig->name,
> +			adata->trig);
> +	if (err) {
> +		pr_err("%s: failed to request threaded irq [%d].\n",
> +					adata->name, *adata->irq_data_ready);
> +		goto request_irq_error;
> +	}
> +	adata->trig->private_data = indio_dev;
> +	adata->trig->ops = &iio_acc_trigger_ops;
> +
> +	adata->set_trigger_parent(indio_dev);
> +
> +	err = iio_trigger_register(adata->trig);
> +	if (err < 0) {
> +		pr_err("%s: failed to register iio trigger.\n", adata->name);
> +		goto iio_trigger_register_error;
> +	}
> +	indio_dev->trig = adata->trig;
> +	pr_info("%s: using [%s] trigger.\n", adata->name, adata->trig->name);
> +	return 0;
> +
> +iio_trigger_register_error:
> +	free_irq(*adata->irq_data_ready, adata->trig);
> +request_irq_error:
> +	iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(acc_probe_trigger);
> +
> +void acc_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	struct st_acc_data *adata = iio_priv(indio_dev);
> +
> +	iio_trigger_unregister(adata->trig);
> +	free_irq(*adata->irq_data_ready, adata->trig);
> +	iio_trigger_free(adata->trig);
> +}
> +EXPORT_SYMBOL(acc_remove_trigger);
> diff --git a/include/linux/iio/accel/ST_accel.h
> b/include/linux/iio/accel/ST_accel.h
> new file mode 100644
> index 0000000..20d1973
> --- /dev/null
> +++ b/include/linux/iio/accel/ST_accel.h
> @@ -0,0 +1,120 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> +/*
> + * Supported sensors:
> + * LSM303DLH
> + * LSM303DLHC
> + * LIS3DH
> + * LSM330D
> + * LSM330DL
> + * LSM330DLC
> + * LSM303D
> + * LSM9DS0
> + * LIS331DLH
> + * LSM303DL
> + * LSM303DLM
> + * LSM330
> + */
> +
> +
> +#ifndef ST_ACCELEROMETERS_IIO_ACC_H
> +#define ST_ACCELEROMETERS_IIO_ACC_H
> +
> +#define LSM303DLH_ACC_IIO_DEV_NAME	"lsm303dlh_acc_iio"
> +#define LSM303DLHC_ACC_IIO_DEV_NAME	"lsm303dlhc_acc_iio"
> +#define LIS3DH_ACC_IIO_DEV_NAME		"lis3dh_acc_iio"
> +#define LSM330D_ACC_IIO_DEV_NAME	"lsm330d_acc_iio"
> +#define LSM330DL_ACC_IIO_DEV_NAME	"lsm330dl_acc_iio"
> +#define LSM330DLC_ACC_IIO_DEV_NAME	"lsm330dlc_acc_iio"
> +#define LSM303D_ACC_IIO_DEV_NAME	"lsm303d_iio"
> +#define LSM9DS0_ACC_IIO_DEV_NAME	"lsm9ds0_iio"
> +#define LIS331DLH_ACC_IIO_DEV_NAME	"lis331dlh_acc_iio"
> +#define LSM303DL_ACC_IIO_DEV_NAME	"lsm303dl_acc_iio"
> +#define LSM303DLM_ACC_IIO_DEV_NAME	"lsm303dlm_acc_iio"
> +#define LSM330_ACC_IIO_DEV_NAME		"lsm330_acc_iio"

The acc_iio prefixes on the driver names should not be necessary

> +
> +#define ST_ACC_NUMBER_ALL_CHANNELS	4
> +#define ST_ACC_BYTE_FOR_CHANNEL		2
> +#define ST_ACC_SCAN_X			0
> +#define ST_ACC_SCAN_Y			1
> +#define ST_ACC_SCAN_Z			2
> +
> +/**
> + * struct st_acc_data - ST accel device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @name: Name of the sensor in use.
> + * @enabled: Status of the sensor (0->off, 1->on).
> + * @index: Number used to point the sensor being used in the
> + *	st_accel_sensors struct.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [ug/LSB].
> + * @odr: Output data rate of the sensor.
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + * @set_trigger_parent: Function used to set the trigger parent.
> + * @trig: The trigger in use by the core driver.
> + * @irq_data_ready: IRQ number for data ready on INT1 pin.
> + * @slock: mutex for read and write operation.
> + */
> +
> +struct st_acc_data {
> +	struct device *dev;
> +	char *name;
> +	atomic_t enabled;

Why does this have to be atomic?

> +	short index;
> +
> +	int fullscale;
> +	int gain;
> +	int odr;
> +
> +	bool multiread_bit;
> +	int (*read_byte) (struct st_acc_data *adata, u8 reg_addr, u8 *res_byte);
> +	int (*write_byte) (struct st_acc_data *adata, u8 reg_addr, u8 data);
> +	int (*read_multiple_byte) (struct st_acc_data *adata, u8 reg_addr,
> +							int len, u8 *data);
> +	void (*set_trigger_parent) (struct iio_dev *indio_dev);
> +
> +	struct iio_trigger *trig;
> +	int *irq_data_ready;

Why does this have to be a pointer?

> +	struct mutex slock;
> +};
> +
> +int acc_iio_probe(struct iio_dev *indio_dev);
> +void acc_iio_remove(struct iio_dev *indio_dev);
> +int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int acc_probe_trigger(struct iio_dev *indio_dev);
> +void acc_remove_trigger(struct iio_dev *indio_dev);
> +int acc_allocate_ring(struct iio_dev *indio_dev);
> +void acc_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int acc_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +static inline void acc_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	return;
> +}
> +static inline int acc_allocate_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +static inline void acc_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +	return;
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCELEROMETERS_IIO_ACC_H */
> diff --git a/include/linux/platform_data/ST_accel_pdata.h
> b/include/linux/platform_data/ST_accel_pdata.h
> new file mode 100644
> index 0000000..51f7b2c
> --- /dev/null
> +++ b/include/linux/platform_data/ST_accel_pdata.h
> @@ -0,0 +1,34 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +
> +#ifndef ST_ACCELEROMETERS_PDATA_ACC_H
> +#define ST_ACCELEROMETERS_PDATA_ACC_H
> +
> +#define DEFAULT_ACCEL_ODR_AVL_100HZ		100
> +#define DEFAULT_ACCEL_FS_AVL_2G			2
> +
> +/**
> + * struct st_acc_platform_data - ST accel device platform data
> + * @fullscale: Value of fullscale used for the sensor.
> + * @sampling_frequency: Value of sampling frequency used for the sensor.
> + */
> +
> +struct st_acc_platform_data {
> +	int fullscale;
> +	int sampling_frequency;
> +};
> +
> +static const struct st_acc_platform_data st_acc_default_pdata = {
> +	.fullscale = DEFAULT_ACCEL_FS_AVL_2G,
> +	.sampling_frequency = DEFAULT_ACCEL_ODR_AVL_100HZ,
> +};

This one should clearly not be defined in the header.

> +
> +#endif /* ST_ACCELEROMETERS_PDATA_ACC_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-16 17:51           ` Lars-Peter Clausen
@ 2012-10-22  9:31             ` Denis CIOCCA
  2012-10-22 18:07               ` Jonathan Cameron
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-22  9:31 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Denis Ciocca, Jonathan Cameron, Pavel Machek, linux-iio,
	Jonathan Cameron, burman.yan

Hi guys,

according to Lars-Peter requests, I have modified my driver. (I hope I
have checked everything).
I have only one things about read_all function on the buffer source file:
I think the best solution is reading every time of all output registers,
these are three-axial accelerometers, the overhead for support the power
on/off of single axis is too much big for the purpose (because if I
don't power off the single axis the data-ready doesn't reset). What do
you think about that?

Thanks

Denis



 From f2f556001170e8698a1bbf0146ed2d11e75436b1 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  drivers/iio/accel/Kconfig                    |   37 +
  drivers/iio/accel/Makefile                   |    6 +
  drivers/iio/accel/st_accel_buffer.c          |  103 ++
  drivers/iio/accel/st_accel_core.c            | 1301
++++++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c             |  139 +++
  drivers/iio/accel/st_accel_spi.c             |  199 ++++
  drivers/iio/accel/st_accel_trigger.c         |   84 ++
  include/linux/iio/accel/st_accel.h           |  119 +++
  include/linux/platform_data/st_accel_pdata.h |   27 +
  9 files changed, 2015 insertions(+), 0 deletions(-)
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h
  create mode 100644 include/linux/platform_data/st_accel_pdata.h

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics accelerometers:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelerometers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelerometers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
+
+st_accel-y := st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..e6e4b0d
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,103 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_NUMBER_DATA_CHANNELS          3
+
+static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+       int len;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       len = adata->read_multiple_byte(adata,
+               indio_dev->channels[ST_ACCEL_SCAN_X].address,
+               ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
+               rx_array);
+       if (len < 0)
+               goto read_error;
+
+       return len;
+
+read_error:
+       return -EIO;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int ret, i, n = 0;
+       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
+       s16 *data = (s16 *)buf;
+
+       ret = st_accel_read_all(indio_dev, rx_array);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       data[n] = (s16)(((s16)(rx_array[2*i+1]) << 8)
+                                                       | rx_array[2*i]);
+                       n++;
+               }
+       }
+
+       return n*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       int len = 0;
+       char *data;
+
+       data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (data == NULL)
+               goto done;
+       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+               len = st_accel_get_buffer_element(indio_dev, data);
+       else
+               goto done;
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
+       iio_push_to_buffer(indio_dev->buffer, data);
+       kfree(data);
+
+done:
+       iio_trigger_notify_done(indio_dev->trig);
+       return IRQ_HANDLED;
+}
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       indio_dev->scan_timestamp = true;
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+                       &st_accel_trigger_handler, NULL);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..6295970
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1301 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/platform_data/st_accel_pdata.h>
+
+
+#define UG_TO_MS2(x)                           (x*9800)
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ON                            1
+#define ST_ACCEL_OFF                           0
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+
+/* ODR */
+#define ST_ACCEL_ODR_AVL_1HZ                   1
+#define ST_ACCEL_ODR_AVL_3HZ                   3
+#define ST_ACCEL_ODR_AVL_6HZ                   6
+#define ST_ACCEL_ODR_AVL_10HZ                  10
+#define ST_ACCEL_ODR_AVL_12HZ                  12
+#define ST_ACCEL_ODR_AVL_25HZ                  25
+#define ST_ACCEL_ODR_AVL_50HZ                  50
+#define ST_ACCEL_ODR_AVL_100HZ                 100
+#define ST_ACCEL_ODR_AVL_200HZ                 200
+#define ST_ACCEL_ODR_AVL_400HZ                 400
+#define ST_ACCEL_ODR_AVL_800HZ                 800
+#define ST_ACCEL_ODR_AVL_1000HZ                        1000
+#define ST_ACCEL_ODR_AVL_1600HZ                        1600
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               1000
+#define ST_ACCEL_1_FS_AVL_4_GAIN               2000
+#define ST_ACCEL_1_FS_AVL_8_GAIN               4000
+#define ST_ACCEL_1_FS_AVL_16_GAIN              12000
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               61
+#define ST_ACCEL_2_FS_AVL_4_GAIN               122
+#define ST_ACCEL_2_FS_AVL_6_GAIN               183
+#define ST_ACCEL_2_FS_AVL_8_GAIN               244
+#define ST_ACCEL_2_FS_AVL_16_GAIN              732
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_PW_VALUE_ON                 0x01
+#define ST_ACCEL_3_PW_VALUE_OFF                        0x00
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               1000
+#define ST_ACCEL_3_FS_AVL_4_GAIN               2000
+#define ST_ACCEL_3_FS_AVL_8_GAIN               3900
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               61
+#define ST_ACCEL_4_FS_AVL_4_GAIN               122
+#define ST_ACCEL_4_FS_AVL_6_GAIN               183
+#define ST_ACCEL_4_FS_AVL_8_GAIN               244
+#define ST_ACCEL_4_FS_AVL_16_GAIN              732
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type = IIO_ACCEL, \
+       .modified = 1, \
+       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index = index, \
+       .channel2 = mod, \
+       .address = addr, \
+       .scan_type = { \
+               .sign = 's', \
+               .realbits = bits, \
+               .shift = 16 - bits, \
+               .storagebits = 16, \
+               .endianness = endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] = {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] = {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_accel_platform_data st_accel_default_pdata = {
+       .fullscale = ST_ACCEL_FS_AVL_2G,
+       .sampling_frequency = ST_ACCEL_ODR_AVL_100HZ,
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ *
+ */
+
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] = {
+       {
+               .wai = ST_ACCEL_1_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr = {
+                       .addr = ST_ACCEL_1_ODR_ADDR,
+                       .mask = ST_ACCEL_1_ODR_MASK,
+                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl = {
+                               [0] = {
+                                       .hz = ST_ACCEL_ODR_AVL_1HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_1HZ_VAL,
+                               },
+                               [1] = {
+                                       .hz = ST_ACCEL_ODR_AVL_10HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_10HZ_VAL,
+                               },
+                               [2] = {
+                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_25HZ_VAL,
+                               },
+                               [3] = {
+                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_50HZ_VAL,
+                               },
+                               [4] = {
+                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_100HZ_VAL,
+                               },
+                               [5] = {
+                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_200HZ_VAL,
+                               },
+                               [6] = {
+                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_400HZ_VAL,
+                               },
+                               [7] = {
+                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL,
+                               },
+                       },
+               },
+               .pw = {
+                       .addr = ST_ACCEL_1_ODR_ADDR,
+                       .mask = ST_ACCEL_1_ODR_MASK,
+                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
+                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .fs = {
+                       .addr = ST_ACCEL_1_FS_ADDR,
+                       .mask = ST_ACCEL_1_FS_MASK,
+                       .num_bit = ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_ACCEL_FS_AVL_2G,
+                                       .value = ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain = ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_ACCEL_FS_AVL_4G,
+                                       .value = ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain = ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_ACCEL_FS_AVL_8G,
+                                       .value = ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain = ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] = {
+                                       .num = ST_ACCEL_FS_AVL_16G,
+                                       .value = ST_ACCEL_1_FS_AVL_16_VAL,
+                                       .gain = ST_ACCEL_1_FS_AVL_16_GAIN,
+                               },
+                       },
+               },
+               .bdu = {
+                       .addr = ST_ACCEL_1_BDU_ADDR,
+                       .mask = ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq = {
+                       .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask = ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
+               },
+               {
+               .wai = ST_ACCEL_2_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr = {
+                       .addr = ST_ACCEL_2_ODR_ADDR,
+                       .mask = ST_ACCEL_2_ODR_MASK,
+                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl = {
+                               [0] = {
+                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_3HZ_VAL,
+                               },
+                               [1] = {
+                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_6HZ_VAL,
+                               },
+                               [2] = {
+                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_12HZ_VAL,
+                               },
+                               [3] = {
+                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_25HZ_VAL,
+                               },
+                               [4] = {
+                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_50HZ_VAL,
+                               },
+                               [5] = {
+                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_100HZ_VAL,
+                               },
+                               [6] = {
+                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_200HZ_VAL,
+                               },
+                               [7] = {
+                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_400HZ_VAL,
+                               },
+                               [8] = {
+                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_800HZ_VAL,
+                               },
+                               [9] = {
+                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL,
+                               },
+                       },
+               },
+               .pw = {
+                       .addr = ST_ACCEL_2_ODR_ADDR,
+                       .mask = ST_ACCEL_2_ODR_MASK,
+                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
+                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .fs = {
+                       .addr = ST_ACCEL_2_FS_ADDR,
+                       .mask = ST_ACCEL_2_FS_MASK,
+                       .num_bit = ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_ACCEL_FS_AVL_2G,
+                                       .value = ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain = ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_ACCEL_FS_AVL_4G,
+                                       .value = ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain = ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_ACCEL_FS_AVL_6G,
+                                       .value = ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain = ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] = {
+                                       .num = ST_ACCEL_FS_AVL_8G,
+                                       .value = ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain = ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] = {
+                                       .num = ST_ACCEL_FS_AVL_16G,
+                                       .value = ST_ACCEL_2_FS_AVL_16_VAL,
+                                       .gain = ST_ACCEL_2_FS_AVL_16_GAIN,
+                               },
+                       },
+               },
+               .drdy_irq = {
+                       .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask = ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu = {
+                       .addr = ST_ACCEL_2_BDU_ADDR,
+                       .mask = ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
+               },
+               {
+               .wai = ST_ACCEL_3_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr = {
+                       .addr = ST_ACCEL_3_ODR_ADDR,
+                       .mask = ST_ACCEL_3_ODR_MASK,
+                       .num_bit = ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl = {
+                               [0] = {
+                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
+                                       .value = ST_ACCEL_3_ODR_AVL_50HZ_VAL,
+                               },
+                               [1] = {
+                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
+                                       .value = ST_ACCEL_3_ODR_AVL_100HZ_VAL,
+                               },
+                               [2] = {
+                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
+                                       .value = ST_ACCEL_3_ODR_AVL_400HZ_VAL,
+                               },
+                               [3] = {
+                                       .hz = ST_ACCEL_ODR_AVL_1000HZ,
+                                       .value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL,
+                               },
+                       },
+               },
+               .pw = {
+                       .addr = ST_ACCEL_3_PW_ADDR,
+                       .mask = ST_ACCEL_3_PW_MASK,
+                       .num_bit = ST_ACCEL_3_PW_N_BIT,
+                       .value_on = ST_ACCEL_3_PW_VALUE_ON,
+                       .value_off = ST_ACCEL_3_PW_VALUE_OFF,
+               },
+               .fs = {
+                       .addr = ST_ACCEL_3_FS_ADDR,
+                       .mask = ST_ACCEL_3_FS_MASK,
+                       .num_bit = ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_ACCEL_FS_AVL_2G,
+                                       .value = ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain = ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_ACCEL_FS_AVL_4G,
+                                       .value = ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain = ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_ACCEL_FS_AVL_8G,
+                                       .value = ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain = ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu = {
+                       .addr = ST_ACCEL_3_BDU_ADDR,
+                       .mask = ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq = {
+                       .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask = ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
+               },
+               {
+               .wai = ST_ACCEL_4_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr = {
+                       .addr = ST_ACCEL_4_ODR_ADDR,
+                       .mask = ST_ACCEL_4_ODR_MASK,
+                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl = {
+                               [0] = {
+                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_3HZ_VAL,
+                               },
+                               [1] = {
+                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_6HZ_VAL,
+                               },
+                               [2] = {
+                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_12HZ_VAL,
+                               },
+                               [3] = {
+                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_25HZ_VAL,
+                               },
+                               [4] = {
+                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_50HZ_VAL,
+                               },
+                               [5] = {
+                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_100HZ_VAL,
+                               },
+                               [6] = {
+                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_200HZ_VAL,
+                               },
+                               [7] = {
+                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_400HZ_VAL,
+                               },
+                               [8] = {
+                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_800HZ_VAL,
+                               },
+                               [9] = {
+                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL,
+                               },
+                       },
+               },
+               .pw = {
+                       .addr = ST_ACCEL_4_ODR_ADDR,
+                       .mask = ST_ACCEL_4_ODR_MASK,
+                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
+                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .fs = {
+                       .addr = ST_ACCEL_4_FS_ADDR,
+                       .mask = ST_ACCEL_4_FS_MASK,
+                       .num_bit = ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_ACCEL_FS_AVL_2G,
+                                       .value = ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_ACCEL_FS_AVL_4G,
+                                       .value = ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain = ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_ACCEL_FS_AVL_6G,
+                                       .value = ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] = {
+                                       .num = ST_ACCEL_FS_AVL_8G,
+                                       .value = ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain = ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] = {
+                                       .num = ST_ACCEL_FS_AVL_16G,
+                                       .value = ST_ACCEL_4_FS_AVL_16_VAL,
+                                       .gain = ST_ACCEL_4_FS_AVL_16_GAIN,
+                               },
+                       },
+               },
+               .bdu = {
+                       .addr = ST_ACCEL_4_BDU_ADDR,
+                       .mask = ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq = {
+                       .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask = ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 = {
+                               .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask = ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 data)
+{
+       int err, j, pos;
+       u8 prev_data;
+       u8 new_data;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       pos = 8 - num_bit;
+       for (j = 128; j >= 0; j = j/2) {
+               if (mask / j > 0)
+                       break;
+               else
+                       pos--;
+       }
+
+       err = adata->read_byte(adata, reg_addr, &prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
+       err = adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret = -1;
+
+       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz == odr) {
+                       odr_out->hz = sensor->odr.odr_avl[i].hz;
+                       odr_out->value = sensor->odr.odr_avl[i].value;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_out)
+{
+       int i, ret = -1;
+
+       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num == fs) {
+                       fs_out->num = sensor->fs.fs_avl[i].num;
+                       fs_out->gain = sensor->fs.fs_avl[i].gain;
+                       fs_out->value = sensor->fs.fs_avl[i].value;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata = iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err = st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+               err = st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err = st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
+       if (err < 0)
+               goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, u8 value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
+                                                               1, value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_available)
+{
+       int err;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr ==
+               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask ==
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled == (bool)ST_ACCEL_ON) {
+                       err = st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+                       if (err < 0)
+                               goto sc_accel_set_odr_error;
+               } else {
+                       adata->odr = odr_available->hz;
+                       err = 0;
+               }
+       } else {
+               err = st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               if (err < 0)
+                       goto sc_accel_set_odr_error;
+       }
+
+sc_accel_set_odr_error:
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, int enable)
+{
+       int err;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       switch (enable) {
+       case ST_ACCEL_ON:
+               found = false;
+               tmp_value = st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr ==
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask ==
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err = st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value = odr_out.value;
+                       found = true;
+               }
+               err = st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled = (bool)ST_ACCEL_ON;
+               if (found)
+                       adata->odr = odr_out.hz;
+               break;
+       case ST_ACCEL_OFF:
+               err = st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled = (bool)ST_ACCEL_OFF;
+               break;
+       default:
+               err = -EINVAL;
+               goto set_enable_error;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl)
+{
+       int err;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       err = st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale = fs_avl->num;
+       adata->gain = fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mask)
+{
+       int err;
+       int data_tmp;
+       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+                       err = -EBUSY;
+                       goto read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err = -EIO;
+                               goto read_error;
+                       } else {
+                               err = adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val = ((s16)(((s16)(outdata[1]) << 8)
+                                       | outdata[0])) >> ch->scan_type.shift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               data_tmp = UG_TO_MS2(adata->gain);
+               *val = 0;
+               *val2 = data_tmp;
+               return IIO_VAL_INT_PLUS_NANO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       bool found;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       found = false;
+       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai == wai) {
+                       found = true;
+                       break;
+               }
+       }
+       if (!found)
+               goto check_device_error;
+
+       adata->index = i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       ret = adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       err = kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err = st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err = st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set sampling frequency to %d.\n", freq);
+               goto st_accel_sysfs_set_sampling_frequency_error;
+       }
+       adata->odr = odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_enable(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int err;
+       bool en;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+       err = strtobool(buf, &en);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err = st_accel_set_enable(indio_dev, (int)en);
+       if (err < 0)
+               dev_err(&indio_dev->dev,
+                               "failed to set enable to %d.\n", (int)en);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_enable(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)adata->enabled);
+}
+
+static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->fullscale);
+}
+
+static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int err;
+       unsigned int fs;
+       struct st_accel_fullscale_available fs_out;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       err = kstrtoint(buf, 10, &fs);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err = st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_out);
+       if (err < 0)
+               goto match_fullscale_error;
+
+       err = st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set new fullscale. (errn %d).\n", err);
+       }
+
+match_fullscale_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len = 0;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
+                                                                       i++) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
+                       break;
+
+               len += sprintf(buf+len, "%d ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].num);
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       len--;
+       len += sprintf(buf+len, "\n");
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len = 0;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
+                                                                       i++) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
+                       break;
+
+               len += sprintf(buf+len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       len--;
+       len += sprintf(buf+len, "\n");
+       return len;
+}
+
+/**
+ * IIO_DEVICE_ATTR - sampling_frequency_available
+ * @read: show all frequency available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale_available
+ * @read: show all fullscale available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+               st_accel_sysfs_fullscale_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale
+ * @read: show the current fullscale of the sensor.
+ * @write: store the current fullscale of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0);
+
+/**
+ * IIO_DEVICE_ATTR - enable
+ * @read: show the current status of the sensor.
+ * @write: power on/off the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
st_accel_sysfs_get_enable,
+               st_accel_sysfs_set_enable , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequency);
+
+static struct attribute *st_accel_attributes[] = {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_fullscale_available.dev_attr.attr,
+       &iio_dev_attr_fullscale.dev_attr.attr,
+       &iio_dev_attr_enable.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group = {
+       .attrs = st_accel_attributes,
+};
+
+static const struct iio_info acc_info = {
+       .driver_module = THIS_MODULE,
+       .attrs = &st_accel_attribute_group,
+       .read_raw = &st_accel_read_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       err = st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err = st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_out);
+       err = st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err = st_accel_set_bdu(indio_dev,
+                       &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
+       if (err < 0)
+               goto init_error;
+
+       return 0;
+
+init_error:
+       dev_err(&indio_dev->dev, "failed to init sensor data.");
+       return -EIO;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev)
+{
+       int err;
+       u8 wai;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+       struct st_accel_platform_data *pdata;
+
+       mutex_init(&adata->slock);
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &acc_info;
+
+       err = st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err = st_accel_check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+       indio_dev->channels = st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
+       pdata = adata->dev->platform_data;
+       if (pdata == NULL) {
+               adata->fullscale = st_accel_default_pdata.fullscale;
+               adata->odr = st_accel_default_pdata.sampling_frequency;
+       } else {
+               adata->fullscale = pdata->fullscale;
+               adata->odr = pdata->sampling_frequency;
+       }
+
+       err = st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err = st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (*adata->irq_data_ready > 0) {
+               err = st_accel_probe_trigger(indio_dev);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err = iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..7cfeb4c
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,139 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+       *res_byte = err & 0xff;
+       return err;
+
+st_accel_i2c_read_byte_error:
+       return -EIO;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *data)
+{
+       int err;
+
+       if (adata->multiread_bit == true)
+               reg_addr |= ST_ACCEL_I2C_MULTIREAD;
+
+       err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data);
+       if (err < 0)
+               goto st_accel_i2c_read_multiple_byte_error;
+
+       return err;
+
+st_accel_i2c_read_multiple_byte_error:
+       return -EIO;
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 data)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, data);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev = iio_device_alloc(sizeof(*adata));
+       if (indio_dev == NULL) {
+               err = -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata = iio_priv(indio_dev);
+       adata->dev = &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = client->name;
+
+       adata->read_byte = st_accel_i2c_read_byte;
+       adata->write_byte = st_accel_i2c_write_byte;
+       adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
+       adata->irq_data_ready = &client->irq;
+
+       err = st_accel_iio_probe(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+       st_accel_iio_remove(indio_dev);
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] = {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "st-accel-i2c",
+       },
+       .probe = st_accel_i2c_probe,
+       .remove = __devexit_p(st_accel_i2c_remove),
+       .id_table = st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..40279bd
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,199 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] = {
+               {
+                       .tx_buf = &tx,
+                       .bits_per_word = 8,
+                       .len = 1,
+               },
+               {
+                       .rx_buf = res_byte,
+                       .bits_per_word = 8,
+                       .len = 1,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx = reg_addr | ACC_SPI_READ;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err = spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+       if (err)
+               goto acc_spi_read_byte_error;
+
+       return err;
+
+acc_spi_read_byte_error:
+       return -EIO;
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *data)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] = {
+               {
+                       .tx_buf = &tx,
+                       .bits_per_word = 8,
+                       .len = 1,
+               },
+               {
+                       .rx_buf = data,
+                       .bits_per_word = 8,
+                       .len = len,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       if (adata->multiread_bit == true)
+               tx = reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx = reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err = spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+       if (err)
+               goto acc_spi_read_multiple_byte_error;
+       return len;
+
+acc_spi_read_multiple_byte_error:
+       return -EIO;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 data)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2];
+
+       struct spi_transfer xfers[] = {
+               {
+                       .tx_buf = tx,
+                       .bits_per_word = 8,
+                       .len = 2,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx[0] = reg_addr;
+       tx[1] = data;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       err = spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *client)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev = iio_device_alloc(sizeof(*adata));
+       if (indio_dev == NULL) {
+               err = -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata = iio_priv(indio_dev);
+       adata->dev = &client->dev;
+       spi_set_drvdata(client, indio_dev);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = client->modalias;
+
+       adata->read_byte = st_accel_spi_read_byte;
+       adata->write_byte = st_accel_spi_write_byte;
+       adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
+       adata->irq_data_ready = &client->irq;
+
+       err = st_accel_iio_probe(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+       st_accel_iio_remove(indio_dev);
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] = {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "st-accel-spi",
+       },
+       .probe = st_accel_spi_probe,
+       .remove = __devexit_p(st_accel_spi_remove),
+       .id_table = st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..7b57b7f
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,84 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev = trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops = {
+       .owner = THIS_MODULE,
+       .set_trigger_state = &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig == NULL) {
+               err = -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+               goto iio_trigger_alloc_error;
+       }
+
+       err = request_threaded_irq(*adata->irq_data_ready,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data = indio_dev;
+       adata->trig->ops = &st_accel_trigger_ops;
+       adata->trig->dev.parent = adata->dev;
+
+       err = iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig = adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(*adata->irq_data_ready, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev)
+{
+       struct st_accel_data *adata = iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(*adata->irq_data_ready, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..1386488
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,119 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Supported sensors:
+ * LSM303DLH
+ * LSM303DLHC
+ * LIS3DH
+ * LSM330D
+ * LSM330DL
+ * LSM330DLC
+ * LSM303D
+ * LSM9DS0
+ * LIS331DLH
+ * LSM303DL
+ * LSM303DLM
+ * LSM330
+ */
+
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @name: Name of the sensor in use.
+ * @enabled: Status of the sensor (0->off, 1->on).
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [ug/LSB].
+ * @odr: Output data rate of the sensor.
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ * @set_trigger_parent: Function used to set the trigger parent.
+ * @trig: The trigger in use by the core driver.
+ * @irq_data_ready: IRQ number for data ready on INT1 pin.
+ * @slock: mutex for read and write operation.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       bool enabled;
+       short index;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       bool multiread_bit;
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byte);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                       int len, u8 *data);
+
+       struct iio_trigger *trig;
+       int *irq_data_ready;
+       struct mutex slock;
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev);
+void st_accel_iio_remove(struct iio_dev *indio_dev);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev);
+void st_accel_remove_trigger(struct iio_dev *indio_dev);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
diff --git a/include/linux/platform_data/st_accel_pdata.h
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
--- /dev/null
+++ b/include/linux/platform_data/st_accel_pdata.h
@@ -0,0 +1,27 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+
+#ifndef ST_ACCEL_PDATA_H
+#define ST_ACCEL_PDATA_H
+
+
+/**
+ * struct st_accel_platform_data - ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */
+
+struct st_accel_platform_data {
+       int fullscale;
+       int sampling_frequency;
+};
+
+#endif /* ST_ACCEL_PDATA_H */
--
1.7.0.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-22  9:31             ` Denis CIOCCA
@ 2012-10-22 18:07               ` Jonathan Cameron
  2012-10-22 19:37                 ` Denis Ciocca
  2012-10-24 12:44                 ` Denis CIOCCA
  0 siblings, 2 replies; 42+ messages in thread
From: Jonathan Cameron @ 2012-10-22 18:07 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Lars-Peter Clausen, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/22/2012 10:31 AM, Denis CIOCCA wrote:
> Hi guys,
> 
> according to Lars-Peter requests, I have modified my driver. (I hope I
> have checked everything).
> I have only one things about read_all function on the buffer source file:
> I think the best solution is reading every time of all output registers,
> these are three-axial accelerometers, the overhead for support the power
> on/off of single axis is too much big for the purpose (because if I
> don't power off the single axis the data-ready doesn't reset). What do
> you think about that?
For the non buffered case - reading is slow anyway so if anyone cares
they will be doing buffered reads.  For buffered reads this isn't going
to change often (and there is a lot of cost associated with bringing the buffer
up and down anyway) so a little cost in disabling the channel is minor
compared to the cost of hammering the bus unecessarily - particularly with
good old slow i2c.

So I'd be inclined to turn on only channels we care about.  Do you have
an estimate of how long it will take to turn one on for a single read?

> 
> Thanks
> 
> Denis
> 
> 
> 
>  From f2f556001170e8698a1bbf0146ed2d11e75436b1 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
> 
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>   drivers/iio/accel/Kconfig                    |   37 +
>   drivers/iio/accel/Makefile                   |    6 +
>   drivers/iio/accel/st_accel_buffer.c          |  103 ++
>   drivers/iio/accel/st_accel_core.c            | 1301
> ++++++++++++++++++++++++++
>   drivers/iio/accel/st_accel_i2c.c             |  139 +++
>   drivers/iio/accel/st_accel_spi.c             |  199 ++++
>   drivers/iio/accel/st_accel_trigger.c         |   84 ++
>   include/linux/iio/accel/st_accel.h           |  119 +++
>   include/linux/platform_data/st_accel_pdata.h |   27 +
>   9 files changed, 2015 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/iio/accel/st_accel_buffer.c
>   create mode 100644 drivers/iio/accel/st_accel_core.c
>   create mode 100644 drivers/iio/accel/st_accel_i2c.c
>   create mode 100644 drivers/iio/accel/st_accel_spi.c
>   create mode 100644 drivers/iio/accel/st_accel_trigger.c
>   create mode 100644 include/linux/iio/accel/st_accel.h
>   create mode 100644 include/linux/platform_data/st_accel_pdata.h
> 
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..d65e66a 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>           Say yes here to build support for the HID SENSOR
>           accelerometers 3D.
> 
> +config ST_ACCEL_3AXIS
> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +       depends on (I2C || SPI) && SYSFS
> +       help
> +         Say yes here to build support for STMicroelectronics accelerometers:
> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_accel.
> +
> +config ST_ACCEL_3AXIS_I2C
> +       tristate "support I2C bus connection"
> +       depends on ST_ACCEL_3AXIS && I2C
> +       help
> +         Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_i2c.
> +
> +config ST_ACCEL_3AXIS_SPI
> +       tristate "support SPI bus connection"
> +       depends on ST_ACCEL_3AXIS && SPI_MASTER
> +       help
> +         Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_spi.
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +       tristate "support triggered buffer"
> +       depends on ST_ACCEL_3AXIS
> +       select IIO_TRIGGERED_BUFFER
> +       select IIO_BUFFER
> +       help
> +         Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>   endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..1541236 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>   #
> 
>   obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +st_accel-y := st_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
> st_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..e6e4b0d
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_buffer.c
> @@ -0,0 +1,103 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_NUMBER_DATA_CHANNELS          3
> +
> +static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +       int len;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       len = adata->read_multiple_byte(adata,
> +               indio_dev->channels[ST_ACCEL_SCAN_X].address,
> +               ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
> +               rx_array);
> +       if (len < 0)
> +               goto read_error;
> +
> +       return len;
> +
> +read_error:
> +       return -EIO;
> +}
> +
> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, n = 0;
> +       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       s16 *data = (s16 *)buf;
> +
> +       ret = st_accel_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
> +               if (test_bit(i, indio_dev->active_scan_mask)) {
> +                       data[n] = (s16)(((s16)(rx_array[2*i+1]) << 8)
> +                                                       | rx_array[2*i]);
> +                       n++;
> +               }
> +       }
> +
> +       return n*sizeof(data[0]);
> +}
> +
> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       int len = 0;
> +       char *data;
> +
> +       data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +       if (data == NULL)
> +               goto done;
> +       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
> +               len = st_accel_get_buffer_element(indio_dev, data);
> +       else
> +               goto done;
> +       if (indio_dev->scan_timestamp)
> +               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
> +       iio_push_to_buffer(indio_dev->buffer, data);
> +       kfree(data);
> +
> +done:
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}
> +
> +int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       indio_dev->scan_timestamp = true;
> +       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +                       &st_accel_trigger_handler, NULL);
> +}
> +EXPORT_SYMBOL(st_accel_allocate_ring);
> +
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_deallocate_ring);
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..6295970
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1301 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +#include <linux/platform_data/st_accel_pdata.h>
> +
> +
> +#define UG_TO_MS2(x)                           (x*9800)
> +#define ST_ACCEL_FULLSCALE_AVL_MAX             5
> +#define ST_ACCEL_ODR_LIST_MAX                  10
> +#define ST_ACCEL_ON                            1
> +#define ST_ACCEL_OFF                           0
> +
> +/* DEFAULT VALUE FOR SENSORS */
> +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
> +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
> +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
> +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
> +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
> +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
> +#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
> +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
> +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
> +#define ST_ACCEL_DEFAULT_12_REALBITS           12
> +#define ST_ACCEL_DEFAULT_16_REALBITS           16
> +
> +/* ODR */
> +#define ST_ACCEL_ODR_AVL_1HZ                   1
> +#define ST_ACCEL_ODR_AVL_3HZ                   3
> +#define ST_ACCEL_ODR_AVL_6HZ                   6
> +#define ST_ACCEL_ODR_AVL_10HZ                  10
> +#define ST_ACCEL_ODR_AVL_12HZ                  12
> +#define ST_ACCEL_ODR_AVL_25HZ                  25
> +#define ST_ACCEL_ODR_AVL_50HZ                  50
> +#define ST_ACCEL_ODR_AVL_100HZ                 100
> +#define ST_ACCEL_ODR_AVL_200HZ                 200
> +#define ST_ACCEL_ODR_AVL_400HZ                 400
> +#define ST_ACCEL_ODR_AVL_800HZ                 800
> +#define ST_ACCEL_ODR_AVL_1000HZ                        1000
> +#define ST_ACCEL_ODR_AVL_1600HZ                        1600
> +
> +/* FULLSCALE */
> +#define ST_ACCEL_FS_AVL_2G                     2
> +#define ST_ACCEL_FS_AVL_4G                     4
> +#define ST_ACCEL_FS_AVL_6G                     6
> +#define ST_ACCEL_FS_AVL_8G                     8
> +#define ST_ACCEL_FS_AVL_16G                    16
> +
> +/* CUSTOM VALUES FOR SENSOR 1 */
> +#define ST_ACCEL_1_WAI_EXP                     0x33
> +#define ST_ACCEL_1_ODR_ADDR                    0x20
> +#define ST_ACCEL_1_ODR_MASK                    0xf0
> +#define ST_ACCEL_1_ODR_N_BIT                   4
> +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
> +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
> +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
> +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
> +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
> +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
> +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
> +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
> +#define ST_ACCEL_1_FS_N_BIT                    2
> +#define ST_ACCEL_1_FS_ADDR                     0x23
> +#define ST_ACCEL_1_FS_MASK                     0x30
> +#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
> +#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
> +#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
> +#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
> +#define ST_ACCEL_1_FS_AVL_2_GAIN               1000
> +#define ST_ACCEL_1_FS_AVL_4_GAIN               2000
> +#define ST_ACCEL_1_FS_AVL_8_GAIN               4000
> +#define ST_ACCEL_1_FS_AVL_16_GAIN              12000
> +#define ST_ACCEL_1_BDU_ADDR                    0x23
> +#define ST_ACCEL_1_BDU_MASK                    0x80
> +#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
> +#define ST_ACCEL_1_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 2 */
> +#define ST_ACCEL_2_WAI_EXP                     0x49
> +#define ST_ACCEL_2_ODR_ADDR                    0x20
> +#define ST_ACCEL_2_ODR_MASK                    0xf0
> +#define ST_ACCEL_2_ODR_N_BIT                   4
> +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_2_FS_N_BIT                    3
> +#define ST_ACCEL_2_FS_ADDR                     0x21
> +#define ST_ACCEL_2_FS_MASK                     0x38
> +#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_2_FS_AVL_2_GAIN               61
> +#define ST_ACCEL_2_FS_AVL_4_GAIN               122
> +#define ST_ACCEL_2_FS_AVL_6_GAIN               183
> +#define ST_ACCEL_2_FS_AVL_8_GAIN               244
> +#define ST_ACCEL_2_FS_AVL_16_GAIN              732
> +#define ST_ACCEL_2_BDU_ADDR                    0x20
> +#define ST_ACCEL_2_BDU_MASK                    0x08
> +#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
> +#define ST_ACCEL_2_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 3 */
> +#define ST_ACCEL_3_WAI_EXP                     0x32
> +#define ST_ACCEL_3_ODR_ADDR                    0x20
> +#define ST_ACCEL_3_ODR_MASK                    0x18
> +#define ST_ACCEL_3_ODR_N_BIT                   2
> +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
> +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
> +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
> +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
> +#define ST_ACCEL_3_PW_ADDR                     0x20
> +#define ST_ACCEL_3_PW_MASK                     0xe0
> +#define ST_ACCEL_3_PW_N_BIT                    3
> +#define ST_ACCEL_3_PW_VALUE_ON                 0x01
> +#define ST_ACCEL_3_PW_VALUE_OFF                        0x00
> +#define ST_ACCEL_3_FS_N_BIT                    2
> +#define ST_ACCEL_3_FS_ADDR                     0x23
> +#define ST_ACCEL_3_FS_MASK                     0x30
> +#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_3_FS_AVL_2_GAIN               1000
> +#define ST_ACCEL_3_FS_AVL_4_GAIN               2000
> +#define ST_ACCEL_3_FS_AVL_8_GAIN               3900
> +#define ST_ACCEL_3_BDU_ADDR                    0x23
> +#define ST_ACCEL_3_BDU_MASK                    0x80
> +#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
> +#define ST_ACCEL_3_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 4 */
> +#define ST_ACCEL_4_WAI_EXP                     0x40
> +#define ST_ACCEL_4_ODR_ADDR                    0x20
> +#define ST_ACCEL_4_ODR_MASK                    0xf0
> +#define ST_ACCEL_4_ODR_N_BIT                   4
> +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_4_FS_N_BIT                    3
> +#define ST_ACCEL_4_FS_ADDR                     0x24
> +#define ST_ACCEL_4_FS_MASK                     0x38
> +#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_4_FS_AVL_2_GAIN               61
> +#define ST_ACCEL_4_FS_AVL_4_GAIN               122
> +#define ST_ACCEL_4_FS_AVL_6_GAIN               183
> +#define ST_ACCEL_4_FS_AVL_8_GAIN               244
> +#define ST_ACCEL_4_FS_AVL_16_GAIN              732
> +#define ST_ACCEL_4_BDU_ADDR                    0x20
> +#define ST_ACCEL_4_BDU_MASK                    0x08
> +#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
> +#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
> +#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
> +#define ST_ACCEL_4_IG1_EN_MASK                 0x08
> +#define ST_ACCEL_4_MULTIREAD_BIT               false
> +
> +struct st_accel_odr_available {
> +       unsigned int hz;
> +       u8 value;
> +};
> +
> +struct st_accel_odr {
> +       u8 addr;
> +       u8 mask;
> +       short num_bit;
> +       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
> +};
> +
> +struct st_accel_power {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       u8 value_off;
> +       u8 value_on;
> +};
> +
> +struct st_accel_fullscale_available {
> +       unsigned int num;
> +       u8 value;
> +       unsigned int gain;
> +};
> +
> +struct st_accel_fullscale {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_accel_bdu {
> +       u8 addr;
> +       u8 mask;
> +};
> +
> +struct st_accel_interrupt_generator {
> +       u8 en_addr;
> +       u8 latch_mask_addr;
> +       u8 en_mask;
> +       u8 latching_mask;
> +};
> +
> +struct st_accel_data_ready_irq {
> +       u8 addr;
> +       u8 mask;
> +       struct st_accel_interrupt_generator ig1;
> +};
> +
> +#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
> +{ \
> +       .type = IIO_ACCEL, \
> +       .modified = 1, \
> +       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +       .scan_index = index, \
> +       .channel2 = mod, \
> +       .address = addr, \
> +       .scan_type = { \
> +               .sign = 's', \
> +               .realbits = bits, \
> +               .shift = 16 - bits, \
> +               .storagebits = 16, \
> +               .endianness = endian, \
> +       }, \
> +}
> +
> +static const struct iio_chan_spec st_accel_12bit_channels[] = {
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +static const struct iio_chan_spec st_accel_16bit_channels[] = {
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +static const struct st_accel_platform_data st_accel_default_pdata = {
> +       .fullscale = ST_ACCEL_FS_AVL_2G,
> +       .sampling_frequency = ST_ACCEL_ODR_AVL_100HZ,
> +};
> +
> +/**
> + * struct st_accel_sensors - ST accel sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + *
> + */
> +
> +static const struct st_accel_sensors {
> +       u8 wai;
> +       struct iio_chan_spec *ch;
> +       struct st_accel_odr odr;
> +       struct st_accel_power pw;
> +       struct st_accel_fullscale fs;
> +       struct st_accel_bdu bdu;
> +       struct st_accel_data_ready_irq drdy_irq;
> +       bool multi_read_bit;
> +} st_accel_sensors[] = {
> +       {
> +               .wai = ST_ACCEL_1_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .odr_avl = {
> +                               [0] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_1HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_1HZ_VAL,
> +                               },
> +                               [1] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_10HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_10HZ_VAL,
> +                               },
> +                               [2] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_25HZ_VAL,
> +                               },
> +                               [3] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_50HZ_VAL,
> +                               },
> +                               [4] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_100HZ_VAL,
> +                               },
> +                               [5] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_200HZ_VAL,
> +                               },
> +                               [6] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_400HZ_VAL,
> +                               },
> +                               [7] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
> +                                       .value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL,
> +                               },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_1_FS_ADDR,
> +                       .mask = ST_ACCEL_1_FS_MASK,
> +                       .num_bit = ST_ACCEL_1_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_1_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_1_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_1_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_8_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_1_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_1_BDU_ADDR,
> +                       .mask = ST_ACCEL_1_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_1_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
> +               },
> +               {
> +               .wai = ST_ACCEL_2_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .odr_avl = {
> +                               [0] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_3HZ_VAL,
> +                               },
> +                               [1] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_6HZ_VAL,
> +                               },
> +                               [2] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_12HZ_VAL,
> +                               },
> +                               [3] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_25HZ_VAL,
> +                               },
> +                               [4] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_50HZ_VAL,
> +                               },
> +                               [5] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_100HZ_VAL,
> +                               },
> +                               [6] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_200HZ_VAL,
> +                               },
> +                               [7] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_400HZ_VAL,
> +                               },
> +                               [8] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_800HZ_VAL,
> +                               },
> +                               [9] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
> +                                       .value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL,
> +                               },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_2_FS_ADDR,
> +                       .mask = ST_ACCEL_2_FS_MASK,
> +                       .num_bit = ST_ACCEL_2_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_2_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_2_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_2_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_2_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_2_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_2_DRDY_IRQ_MASK,
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_2_BDU_ADDR,
> +                       .mask = ST_ACCEL_2_BDU_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
> +               },
> +               {
> +               .wai = ST_ACCEL_3_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_3_ODR_ADDR,
> +                       .mask = ST_ACCEL_3_ODR_MASK,
> +                       .num_bit = ST_ACCEL_3_ODR_N_BIT,
> +                       .odr_avl = {
> +                               [0] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
> +                                       .value = ST_ACCEL_3_ODR_AVL_50HZ_VAL,
> +                               },
> +                               [1] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
> +                                       .value = ST_ACCEL_3_ODR_AVL_100HZ_VAL,
> +                               },
> +                               [2] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
> +                                       .value = ST_ACCEL_3_ODR_AVL_400HZ_VAL,
> +                               },
> +                               [3] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_1000HZ,
> +                                       .value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL,
> +                               },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_3_PW_ADDR,
> +                       .mask = ST_ACCEL_3_PW_MASK,
> +                       .num_bit = ST_ACCEL_3_PW_N_BIT,
> +                       .value_on = ST_ACCEL_3_PW_VALUE_ON,
> +                       .value_off = ST_ACCEL_3_PW_VALUE_OFF,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_3_FS_ADDR,
> +                       .mask = ST_ACCEL_3_FS_MASK,
> +                       .num_bit = ST_ACCEL_3_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_3_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_3_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_3_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_8_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_3_BDU_ADDR,
> +                       .mask = ST_ACCEL_3_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_3_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
> +               },
> +               {
> +               .wai = ST_ACCEL_4_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .odr_avl = {
> +                               [0] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_3HZ_VAL,
> +                               },
> +                               [1] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_6HZ_VAL,
> +                               },
> +                               [2] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_12HZ_VAL,
> +                               },
> +                               [3] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_25HZ_VAL,
> +                               },
> +                               [4] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_50HZ_VAL,
> +                               },
> +                               [5] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_100HZ_VAL,
> +                               },
> +                               [6] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_200HZ_VAL,
> +                               },
> +                               [7] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_400HZ_VAL,
> +                               },
> +                               [8] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_800HZ_VAL,
> +                               },
> +                               [9] = {
> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
> +                                       .value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL,
> +                               },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_4_FS_ADDR,
> +                       .mask = ST_ACCEL_4_FS_MASK,
> +                       .num_bit = ST_ACCEL_4_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_4_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_4_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_4_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_4_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_4_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_4_BDU_ADDR,
> +                       .mask = ST_ACCEL_4_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_4_DRDY_IRQ_MASK,
> +                       .ig1 = {
> +                               .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
> +                               .en_mask = ST_ACCEL_4_IG1_EN_MASK,
> +                       },
> +               },
> +               .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
> +       },
> +};
> +
> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
> reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err, j, pos;
> +       u8 prev_data;
> +       u8 new_data;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       pos = 8 - num_bit;
> +       for (j = 128; j >= 0; j = j/2) {
> +               if (mask / j > 0)
> +                       break;
> +               else
> +                       pos--;
> +       }
> +
> +       err = adata->read_byte(adata, reg_addr, &prev_data);
> +       if (err < 0)
> +               goto st_accel_write_data_with_mask_error;
> +
> +       new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
> +       err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_accel_write_data_with_mask_error:
> +       return err;
> +}
> +
> +static int st_accel_match_odr(const struct st_accel_sensors *sensor,
> +               unsigned int odr, struct st_accel_odr_available *odr_out)
> +{
> +       int i, ret = -1;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
> +               if (sensor->odr.odr_avl[i].hz == odr) {
> +                       odr_out->hz = sensor->odr.odr_avl[i].hz;
> +                       odr_out->value = sensor->odr.odr_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int st_accel_match_fs(const struct st_accel_sensors *sensor,
> +               unsigned int fs, struct st_accel_fullscale_available *fs_out)
> +{
> +       int i, ret = -1;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].num == fs) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
> +{
> +       int err;
> +       struct st_accel_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_accel_set_dataready_irq_error;
> +       }
> +
> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
> +               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_accel_set_dataready_irq_error;
> +       }
> +
> +       err = st_accel_write_data_with_mask(indio_dev,
> +               st_accel_sensors[adata->index].drdy_irq.addr,
> +               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
> +       if (err < 0)
> +               goto st_accel_set_dataready_irq_error;
> +
> +st_accel_set_dataready_irq_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_set_dataready_irq);
> +
> +static int st_accel_set_bdu(struct iio_dev *indio_dev,
> +                               const struct st_accel_bdu *bdu, u8 value)
> +{
> +       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
> +                                                               1, value);
> +}
> +
> +static int st_accel_set_odr(struct iio_dev *indio_dev,
> +                               struct st_accel_odr_available *odr_available)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       if ((st_accel_sensors[adata->index].odr.addr ==
> +               st_accel_sensors[adata->index].pw.addr) &&
> +                       (st_accel_sensors[adata->index].odr.mask ==
> +                               st_accel_sensors[adata->index].pw.mask)) {
> +               if (adata->enabled == (bool)ST_ACCEL_ON) {
> +                       err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].odr.addr,
> +                               st_accel_sensors[adata->index].odr.mask,
> +                               st_accel_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +                       if (err < 0)
> +                               goto sc_accel_set_odr_error;
> +               } else {
> +                       adata->odr = odr_available->hz;
> +                       err = 0;
> +               }
> +       } else {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].odr.addr,
> +                               st_accel_sensors[adata->index].odr.mask,
> +                               st_accel_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +               if (err < 0)
> +                       goto sc_accel_set_odr_error;
> +       }
> +
> +sc_accel_set_odr_error:
> +       return err;
> +}
> +
> +static int st_accel_set_enable(struct iio_dev *indio_dev, int enable)
> +{
> +       int err;
> +       bool found;
> +       u8 tmp_value;
> +       struct st_accel_odr_available odr_out;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       switch (enable) {
> +       case ST_ACCEL_ON:
> +               found = false;
> +               tmp_value = st_accel_sensors[adata->index].pw.value_on;
> +               if ((st_accel_sensors[adata->index].odr.addr ==
> +                               st_accel_sensors[adata->index].pw.addr) &&
> +                       (st_accel_sensors[adata->index].odr.mask ==
> +                               st_accel_sensors[adata->index].pw.mask)) {
> +                       err = st_accel_match_odr(
> +                               &st_accel_sensors[adata->index], adata->odr,
> +                                       &odr_out);
> +                       if (err < 0)
> +                               goto set_enable_error;
> +                       tmp_value = odr_out.value;
> +                       found = true;
> +               }
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].pw.addr,
> +                               st_accel_sensors[adata->index].pw.mask,
> +                               st_accel_sensors[adata->index].pw.num_bit,
> +                               tmp_value);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = (bool)ST_ACCEL_ON;
> +               if (found)
> +                       adata->odr = odr_out.hz;
> +               break;
> +       case ST_ACCEL_OFF:
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].pw.addr,
> +                               st_accel_sensors[adata->index].pw.mask,
> +                               st_accel_sensors[adata->index].pw.num_bit,
> +                               st_accel_sensors[adata->index].pw.value_off);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = (bool)ST_ACCEL_OFF;
> +               break;
> +       default:
> +               err = -EINVAL;
> +               goto set_enable_error;
> +       }
> +
> +set_enable_error:
> +       return err;
> +}
> +
> +static int st_accel_set_fullscale(struct iio_dev *indio_dev,
> +                               struct st_accel_fullscale_available *fs_avl)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].fs.addr,
> +                               st_accel_sensors[adata->index].fs.mask,
> +                               st_accel_sensors[adata->index].fs.num_bit,
> +                               fs_avl->value);
> +       if (err < 0)
> +               goto st_accel_set_fullscale_error;
> +
> +       adata->fullscale = fs_avl->num;
> +       adata->gain = fs_avl->gain;
> +       return err;
> +
> +st_accel_set_fullscale_error:
> +       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +       return err;
> +}
> +
> +static int st_accel_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       int data_tmp;
> +       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&indio_dev->mlock);
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +                       goto read_error;
> +               } else {
> +                       if (!adata->enabled) {
> +                               err = -EIO;
> +                               goto read_error;
> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               *val = ((s16)(((s16)(outdata[1]) << 8)
> +                                       | outdata[0])) >> ch->scan_type.shift;
> +                       }
> +               }
> +               mutex_unlock(&indio_dev->mlock);
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               data_tmp = UG_TO_MS2(adata->gain);
> +               *val = 0;
> +               *val2 = data_tmp;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       mutex_unlock(&indio_dev->mlock);
> +       return err;
> +}
> +
> +static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
> +{
> +       int i;
> +       bool found;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       found = false;
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
> +               if (st_accel_sensors[i].wai == wai) {
> +                       found = true;
> +                       break;
> +               }
> +       }
> +       if (!found)
> +               goto check_device_error;
> +
> +       adata->index = i;
> +
> +       return i;
> +
> +check_device_error:
> +       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
> +       return -ENODEV;
> +}
> +
> +static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
> +                                                               u8 *value)
> +{
> +       int ret;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       ret = adata->read_byte(adata, reg_addr, value);
> +       if (ret < 0)
> +               goto read_byte_wai_error;
> +
> +       return 0;
> +
> +read_byte_wai_error:
> +       dev_err(&indio_dev->dev,
> +                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
> +       return -EIO;
> +}
> +
> +static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int freq;
> +       struct st_accel_odr_available odr_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &freq);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_match_odr(&st_accel_sensors[adata->index],
> +                                                               freq, &odr_out);
> +       if (err < 0)
> +               goto st_accel_sysfs_set_sampling_frequency_error;
> +
> +       err = st_accel_set_odr(indio_dev, &odr_out);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev,
> +                       "failed to set sampling frequency to %d.\n", freq);
> +               goto st_accel_sysfs_set_sampling_frequency_error;
> +       }
> +       adata->odr = odr_out.hz;
> +
> +st_accel_sysfs_set_sampling_frequency_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->odr);
> +}
> +
> +static ssize_t st_accel_sysfs_set_enable(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       bool en;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> +       err = strtobool(buf, &en);
> +       if (err < 0)
> +               goto set_enable_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_set_enable(indio_dev, (int)en);
> +       if (err < 0)
> +               dev_err(&indio_dev->dev,
> +                               "failed to set enable to %d.\n", (int)en);
> +       mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_enable(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", (int)adata->enabled);
> +}
> +
> +static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->fullscale);
> +}
> +
> +static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int fs;
> +       struct st_accel_fullscale_available fs_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &fs);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_out);
> +       if (err < 0)
> +               goto match_fullscale_error;
> +
> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev,
> +                       "failed to set new fullscale. (errn %d).\n", err);
> +       }
> +
> +match_fullscale_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "%d ",
> +                       st_accel_sensors[adata->index].fs.fs_avl[i].num);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       len--;
> +       len += sprintf(buf+len, "\n");
> +       return len;
> +}
> +
> +static ssize_t st_accel_sysfs_sampling_frequency_available(struct
> device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "%d ",
> +                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       len--;
> +       len += sprintf(buf+len, "\n");
> +       return len;
> +}
> +
> +/**
> + * IIO_DEVICE_ATTR - sampling_frequency_available
> + * @read: show all frequency available of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +               st_accel_sysfs_sampling_frequency_available, NULL , 0);
> +
> +/**
> + * IIO_DEVICE_ATTR - fullscale_available
> + * @read: show all fullscale available of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
> +               st_accel_sysfs_fullscale_available, NULL , 0);
> +
> +/**
> + * IIO_DEVICE_ATTR - fullscale
> + * @read: show the current fullscale of the sensor.
> + * @write: store the current fullscale of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
> +               st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0);
> +
> +/**
> + * IIO_DEVICE_ATTR - enable
> + * @read: show the current status of the sensor.
> + * @write: power on/off the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
> st_accel_sysfs_get_enable,
> +               st_accel_sysfs_set_enable , 0);
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +                       st_accel_sysfs_get_sampling_frequency,
> +                                       st_accel_sysfs_set_sampling_frequency);
> +
> +static struct attribute *st_accel_attributes[] = {
> +       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +       &iio_dev_attr_fullscale_available.dev_attr.attr,
> +       &iio_dev_attr_fullscale.dev_attr.attr,
> +       &iio_dev_attr_enable.dev_attr.attr,
> +       &iio_dev_attr_sampling_frequency.dev_attr.attr,
> +       NULL,
> +};
> +
> +static const struct attribute_group st_accel_attribute_group = {
> +       .attrs = st_accel_attributes,
> +};
> +
> +static const struct iio_info acc_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &st_accel_attribute_group,
> +       .read_raw = &st_accel_read_raw,
> +};
> +
> +static int st_accel_init_sensor(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_accel_odr_available odr_out;
> +       struct st_accel_fullscale_available fs_out;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
> +       if (err < 0)
> +               goto init_error;
> +
> +       st_accel_match_fs(&st_accel_sensors[adata->index],
> +                                               adata->fullscale, &fs_out);
> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
> +       if (err < 0)
> +               goto init_error;
> +
> +       st_accel_match_odr(&st_accel_sensors[adata->index],
> +                                                       adata->odr, &odr_out);
> +       err = st_accel_set_odr(indio_dev, &odr_out);
> +       if (err < 0)
> +               goto init_error;
> +
> +       err = st_accel_set_bdu(indio_dev,
> +                       &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
> +       if (err < 0)
> +               goto init_error;
> +
> +       return 0;
> +
> +init_error:
> +       dev_err(&indio_dev->dev, "failed to init sensor data.");
> +       return -EIO;
> +}
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       u8 wai;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +       struct st_accel_platform_data *pdata;
> +
> +       mutex_init(&adata->slock);
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->info = &acc_info;
> +
> +       err = st_accel_get_wai_device(indio_dev,
> +                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_check_device_list(indio_dev, wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
> +       indio_dev->channels = st_accel_sensors[adata->index].ch;
> +       indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
> +       pdata = adata->dev->platform_data;
> +       if (pdata == NULL) {
> +               adata->fullscale = st_accel_default_pdata.fullscale;
> +               adata->odr = st_accel_default_pdata.sampling_frequency;
> +       } else {
> +               adata->fullscale = pdata->fullscale;
> +               adata->odr = pdata->sampling_frequency;
> +       }
> +
> +       err = st_accel_init_sensor(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       if (*adata->irq_data_ready > 0) {
> +               err = st_accel_probe_trigger(indio_dev);
> +               if (err < 0)
> +                       goto acc_probe_trigger_error;
> +       }
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       return err;
> +
> +iio_device_register_error:
> +       st_accel_remove_trigger(indio_dev);
> +acc_probe_trigger_error:
> +       st_accel_deallocate_ring(indio_dev);
> +st_accel_iio_probe_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_iio_probe);
> +
> +void st_accel_iio_remove(struct iio_dev *indio_dev)
> +{
> +       iio_device_unregister(indio_dev);
> +       st_accel_remove_trigger(indio_dev);
> +       st_accel_deallocate_ring(indio_dev);
> +       iio_device_free(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_i2c.c
> b/drivers/iio/accel/st_accel_i2c.c
> new file mode 100644
> index 0000000..7cfeb4c
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -0,0 +1,139 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_I2C_MULTIREAD                 0x80
> +
> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       int err;
> +
> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +       if (err < 0)
> +               goto st_accel_i2c_read_byte_error;
> +       *res_byte = err & 0xff;
> +       return err;
> +
> +st_accel_i2c_read_byte_error:
> +       return -EIO;
> +}
> +
> +static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       int err;
> +
> +       if (adata->multiread_bit == true)
> +               reg_addr |= ST_ACCEL_I2C_MULTIREAD;
> +
> +       err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +                                                       reg_addr, len, data);
> +       if (err < 0)
> +               goto st_accel_i2c_read_multiple_byte_error;
> +
> +       return err;
> +
> +st_accel_i2c_read_multiple_byte_error:
> +       return -EIO;
> +}
> +
> +static int st_accel_i2c_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
> +                                                               reg_addr, data);
> +}
> +
> +static int __devinit st_accel_i2c_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_accel_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &client->dev;
> +       i2c_set_clientdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->name;
> +
> +       adata->read_byte = st_accel_i2c_read_byte;
> +       adata->write_byte = st_accel_i2c_write_byte;
> +       adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
> +       adata->irq_data_ready = &client->irq;
> +
> +       err = st_accel_iio_probe(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit st_accel_i2c_remove(struct i2c_client *client)
> +{
> +       struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +
> +       st_accel_iio_remove(indio_dev);
> +       return 0;
> +}
> +
> +static const struct i2c_device_id st_accel_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
> +
> +static struct i2c_driver st_accel_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-accel-i2c",
> +       },
> +       .probe = st_accel_i2c_probe,
> +       .remove = __devexit_p(st_accel_i2c_remove),
> +       .id_table = st_accel_id_table,
> +};
> +module_i2c_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_spi.c
> b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..40279bd
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,199 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ           0x80;
> +#define ACC_SPI_MULTIREAD      0xc0
> +
> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = res_byte,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       tx = reg_addr | ACC_SPI_READ;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);
> +       if (err)
> +               goto acc_spi_read_byte_error;
> +
> +       return err;
> +
> +acc_spi_read_byte_error:
> +       return -EIO;
> +}
> +
> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = data,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       if (adata->multiread_bit == true)
> +               tx = reg_addr | ACC_SPI_MULTIREAD;
> +       else
> +               tx = reg_addr | ACC_SPI_READ;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);
> +       if (err)
> +               goto acc_spi_read_multiple_byte_error;
> +       return len;
> +
> +acc_spi_read_multiple_byte_error:
> +       return -EIO;
> +}
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx[2];
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = tx,
> +                       .bits_per_word = 8,
> +                       .len = 2,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       tx[0] = reg_addr;
> +       tx[1] = data;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);
> +
> +       return err;
> +}
> +
> +static int __devinit st_accel_spi_probe(struct spi_device *client)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_accel_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &client->dev;
> +       spi_set_drvdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->modalias;
> +
> +       adata->read_byte = st_accel_spi_read_byte;
> +       adata->write_byte = st_accel_spi_write_byte;
> +       adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
> +       adata->irq_data_ready = &client->irq;
> +
> +       err = st_accel_iio_probe(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit st_accel_spi_remove(struct spi_device *spi)
> +{
> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +
> +       st_accel_iio_remove(indio_dev);
> +       return 0;
> +}
> +
> +static const struct spi_device_id st_accel_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(spi, st_accel_id_table);
> +
> +static struct spi_driver st_accel_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-accel-spi",
> +       },
> +       .probe = st_accel_spi_probe,
> +       .remove = __devexit_p(st_accel_spi_remove),
> +       .id_table = st_accel_id_table,
> +};
> +module_spi_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_trigger.c
> b/drivers/iio/accel/st_accel_trigger.c
> new file mode 100644
> index 0000000..7b57b7f
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_trigger.c
> @@ -0,0 +1,84 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
> state)
> +{
> +       struct iio_dev *indio_dev = trig->private_data;
> +       return st_accel_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops st_accel_trigger_ops = {
> +       .owner = THIS_MODULE,
> +       .set_trigger_state = &st_accel_trig_acc_set_state,
> +};
> +
> +int st_accel_probe_trigger(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +       if (adata->trig == NULL) {
> +               err = -ENOMEM;
> +               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +               goto iio_trigger_alloc_error;
> +       }
> +
> +       err = request_threaded_irq(*adata->irq_data_ready,
> +                       iio_trigger_generic_data_rdy_poll,
> +                       NULL,
> +                       IRQF_TRIGGER_RISING,
> +                       adata->trig->name,
> +                       adata->trig);
> +       if (err)
> +               goto request_irq_error;
> +
> +       adata->trig->private_data = indio_dev;
> +       adata->trig->ops = &st_accel_trigger_ops;
> +       adata->trig->dev.parent = adata->dev;
> +
> +       err = iio_trigger_register(adata->trig);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +               goto iio_trigger_register_error;
> +       }
> +       indio_dev->trig = adata->trig;
> +
> +       return 0;
> +
> +iio_trigger_register_error:
> +       free_irq(*adata->irq_data_ready, adata->trig);
> +request_irq_error:
> +       iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_probe_trigger);
> +
> +void st_accel_remove_trigger(struct iio_dev *indio_dev)
> +{
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       iio_trigger_unregister(adata->trig);
> +       free_irq(*adata->irq_data_ready, adata->trig);
> +       iio_trigger_free(adata->trig);
> +}
> +EXPORT_SYMBOL(st_accel_remove_trigger);
> diff --git a/include/linux/iio/accel/st_accel.h
> b/include/linux/iio/accel/st_accel.h
> new file mode 100644
> index 0000000..1386488
> --- /dev/null
> +++ b/include/linux/iio/accel/st_accel.h
> @@ -0,0 +1,119 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> +/*
> + * Supported sensors:
> + * LSM303DLH
> + * LSM303DLHC
> + * LIS3DH
> + * LSM330D
> + * LSM330DL
> + * LSM330DLC
> + * LSM303D
> + * LSM9DS0
> + * LIS331DLH
> + * LSM303DL
> + * LSM303DLM
> + * LSM330
> + */
> +
> +
> +#ifndef ST_ACCEL_H
> +#define ST_ACCEL_H
> +
> +#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
> +#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
> +#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
> +#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
> +#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
> +#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
> +#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
> +#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
> +#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
> +#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
> +#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
> +#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
> +
> +#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
> +#define ST_ACCEL_BYTE_FOR_CHANNEL      2
> +#define ST_ACCEL_SCAN_X                        0
> +#define ST_ACCEL_SCAN_Y                        1
> +#define ST_ACCEL_SCAN_Z                        2
> +
> +/**
> + * struct st_accel_data - ST accel device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @name: Name of the sensor in use.
> + * @enabled: Status of the sensor (0->off, 1->on).
> + * @index: Number used to point the sensor being used in the
> + *     st_accel_sensors struct.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [ug/LSB].
> + * @odr: Output data rate of the sensor.
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + * @set_trigger_parent: Function used to set the trigger parent.
> + * @trig: The trigger in use by the core driver.
> + * @irq_data_ready: IRQ number for data ready on INT1 pin.
> + * @slock: mutex for read and write operation.
> + */
> +
> +struct st_accel_data {
> +       struct device *dev;
> +       bool enabled;
> +       short index;
> +
> +       unsigned int fullscale;
> +       unsigned int gain;
> +       unsigned int odr;
> +
> +       bool multiread_bit;
> +       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                               u8 *res_byte);
> +       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +
> +       struct iio_trigger *trig;
> +       int *irq_data_ready;
> +       struct mutex slock;
> +};
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev);
> +void st_accel_iio_remove(struct iio_dev *indio_dev);
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_accel_probe_trigger(struct iio_dev *indio_dev);
> +void st_accel_remove_trigger(struct iio_dev *indio_dev);
> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev)
> +{
> +       return;
> +}
> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       return;
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCEL_H */
> diff --git a/include/linux/platform_data/st_accel_pdata.h
> b/include/linux/platform_data/st_accel_pdata.h
> new file mode 100644
> index 0000000..416489b
> --- /dev/null
> +++ b/include/linux/platform_data/st_accel_pdata.h
> @@ -0,0 +1,27 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +
> +#ifndef ST_ACCEL_PDATA_H
> +#define ST_ACCEL_PDATA_H
> +
> +
> +/**
> + * struct st_accel_platform_data - ST accel device platform data
> + * @fullscale: Value of fullscale used for the sensor.
> + * @sampling_frequency: Value of sampling frequency used for the sensor.
> + */
> +
> +struct st_accel_platform_data {
> +       int fullscale;
> +       int sampling_frequency;
> +};
> +
> +#endif /* ST_ACCEL_PDATA_H */
> --
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-22 18:07               ` Jonathan Cameron
@ 2012-10-22 19:37                 ` Denis Ciocca
  2012-10-24 12:44                 ` Denis CIOCCA
  1 sibling, 0 replies; 42+ messages in thread
From: Denis Ciocca @ 2012-10-22 19:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis CIOCCA, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/22/2012 08:07 PM, Jonathan Cameron wrote:
> On 10/22/2012 10:31 AM, Denis CIOCCA wrote:
>> Hi guys,
>>
>> according to Lars-Peter requests, I have modified my driver. (I hope I
>> have checked everything).
>> I have only one things about read_all function on the buffer source file:
>> I think the best solution is reading every time of all output registers,
>> these are three-axial accelerometers, the overhead for support the power
>> on/off of single axis is too much big for the purpose (because if I
>> don't power off the single axis the data-ready doesn't reset). What do
>> you think about that?
> For the non buffered case - reading is slow anyway so if anyone cares
> they will be doing buffered reads.
In the non buffered case is done by reading only the selected registers.

> For buffered reads this isn't going
> to change often (and there is a lot of cost associated with bringing the buffer
> up and down anyway) so a little cost in disabling the channel is minor
> compared to the cost of hammering the bus unecessarily - particularly with
> good old slow i2c.
For the buffered case you are right.

> So I'd be inclined to turn on only channels we care about.  Do you have
> an estimate of how long it will take to turn one on for a single read?
>
For a single read, I think is not the best solution activate one channel 
and then deactivate it when the reading is ended. The time of turn on 
the channel depends on device, tomorrow morning I check these details on 
our datasheets. The only benefit in this kind of approach is the power 
consumption of the devices.


Denis









>>
>>
>>
>>   From f2f556001170e8698a1bbf0146ed2d11e75436b1 Mon Sep 17 00:00:00 2001
>> From: Denis Ciocca <denis.ciocca@st.com>
>> Date: Mon, 22 Oct 2012 11:17:27 +0200
>> Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
>>
>> This patch adds generic accelerometer driver for STMicroelectronics
>> accelerometers, currently it supports:
>> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
>> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
>>
>> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
>> ---
>>    drivers/iio/accel/Kconfig                    |   37 +
>>    drivers/iio/accel/Makefile                   |    6 +
>>    drivers/iio/accel/st_accel_buffer.c          |  103 ++
>>    drivers/iio/accel/st_accel_core.c            | 1301
>> ++++++++++++++++++++++++++
>>    drivers/iio/accel/st_accel_i2c.c             |  139 +++
>>    drivers/iio/accel/st_accel_spi.c             |  199 ++++
>>    drivers/iio/accel/st_accel_trigger.c         |   84 ++
>>    include/linux/iio/accel/st_accel.h           |  119 +++
>>    include/linux/platform_data/st_accel_pdata.h |   27 +
>>    9 files changed, 2015 insertions(+), 0 deletions(-)
>>    create mode 100644 drivers/iio/accel/st_accel_buffer.c
>>    create mode 100644 drivers/iio/accel/st_accel_core.c
>>    create mode 100644 drivers/iio/accel/st_accel_i2c.c
>>    create mode 100644 drivers/iio/accel/st_accel_spi.c
>>    create mode 100644 drivers/iio/accel/st_accel_trigger.c
>>    create mode 100644 include/linux/iio/accel/st_accel.h
>>    create mode 100644 include/linux/platform_data/st_accel_pdata.h
>>
>> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
>> index b2510c4..d65e66a 100644
>> --- a/drivers/iio/accel/Kconfig
>> +++ b/drivers/iio/accel/Kconfig
>> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>>            Say yes here to build support for the HID SENSOR
>>            accelerometers 3D.
>>
>> +config ST_ACCEL_3AXIS
>> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
>> +       depends on (I2C || SPI) && SYSFS
>> +       help
>> +         Say yes here to build support for STMicroelectronics accelerometers:
>> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
>> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
>> +
>> +         This driver can also be built as a module. If so, the module
>> +         will be called st_accel.
>> +
>> +config ST_ACCEL_3AXIS_I2C
>> +       tristate "support I2C bus connection"
>> +       depends on ST_ACCEL_3AXIS && I2C
>> +       help
>> +         Say yes here to build I2C support for STMicroelectronics accelerometers.
>> +
>> +         To compile this driver as a module, choose M here: the
>> +         module will be called st_accel_i2c.
>> +
>> +config ST_ACCEL_3AXIS_SPI
>> +       tristate "support SPI bus connection"
>> +       depends on ST_ACCEL_3AXIS && SPI_MASTER
>> +       help
>> +         Say yes here to build SPI support for STMicroelectronics accelerometers.
>> +
>> +         To compile this driver as a module, choose M here: the
>> +         module will be called st_accel_spi.
>> +
>> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
>> +       tristate "support triggered buffer"
>> +       depends on ST_ACCEL_3AXIS
>> +       select IIO_TRIGGERED_BUFFER
>> +       select IIO_BUFFER
>> +       help
>> +         Default trigger and buffer for STMicroelectronics accelerometers driver.
>> +
>>    endmenu
>> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
>> index 5bc6855..1541236 100644
>> --- a/drivers/iio/accel/Makefile
>> +++ b/drivers/iio/accel/Makefile
>> @@ -3,3 +3,9 @@
>>    #
>>
>>    obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
>> +
>> +st_accel-y := st_accel_core.o
>> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
>> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
>> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
>> st_accel_buffer.o
>> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
>> diff --git a/drivers/iio/accel/st_accel_buffer.c
>> b/drivers/iio/accel/st_accel_buffer.c
>> new file mode 100644
>> index 0000000..e6e4b0d
>> --- /dev/null
>> +++ b/drivers/iio/accel/st_accel_buffer.c
>> @@ -0,0 +1,103 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/stat.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/byteorder/generic.h>
>> +#include <linux/i2c.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +
>> +#include <linux/iio/accel/st_accel.h>
>> +
>> +
>> +#define ST_ACCEL_NUMBER_DATA_CHANNELS          3
>> +
>> +static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
>> +{
>> +       int len;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       len = adata->read_multiple_byte(adata,
>> +               indio_dev->channels[ST_ACCEL_SCAN_X].address,
>> +               ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
>> +               rx_array);
>> +       if (len < 0)
>> +               goto read_error;
>> +
>> +       return len;
>> +
>> +read_error:
>> +       return -EIO;
>> +}
>> +
>> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
>> +{
>> +       int ret, i, n = 0;
>> +       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
>> +       s16 *data = (s16 *)buf;
>> +
>> +       ret = st_accel_read_all(indio_dev, rx_array);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
>> +               if (test_bit(i, indio_dev->active_scan_mask)) {
>> +                       data[n] = (s16)(((s16)(rx_array[2*i+1]) << 8)
>> +                                                       | rx_array[2*i]);
>> +                       n++;
>> +               }
>> +       }
>> +
>> +       return n*sizeof(data[0]);
>> +}
>> +
>> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
>> +{
>> +       struct iio_poll_func *pf = p;
>> +       struct iio_dev *indio_dev = pf->indio_dev;
>> +       int len = 0;
>> +       char *data;
>> +
>> +       data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
>> +       if (data == NULL)
>> +               goto done;
>> +       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
>> +               len = st_accel_get_buffer_element(indio_dev, data);
>> +       else
>> +               goto done;
>> +       if (indio_dev->scan_timestamp)
>> +               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
>> +       iio_push_to_buffer(indio_dev->buffer, data);
>> +       kfree(data);
>> +
>> +done:
>> +       iio_trigger_notify_done(indio_dev->trig);
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +int st_accel_allocate_ring(struct iio_dev *indio_dev)
>> +{
>> +       indio_dev->scan_timestamp = true;
>> +       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
>> +                       &st_accel_trigger_handler, NULL);
>> +}
>> +EXPORT_SYMBOL(st_accel_allocate_ring);
>> +
>> +void st_accel_deallocate_ring(struct iio_dev *indio_dev)
>> +{
>> +       iio_triggered_buffer_cleanup(indio_dev);
>> +}
>> +EXPORT_SYMBOL(st_accel_deallocate_ring);
>> diff --git a/drivers/iio/accel/st_accel_core.c
>> b/drivers/iio/accel/st_accel_core.c
>> new file mode 100644
>> index 0000000..6295970
>> --- /dev/null
>> +++ b/drivers/iio/accel/st_accel_core.c
>> @@ -0,0 +1,1301 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/errno.h>
>> +#include <linux/types.h>
>> +#include <linux/mutex.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/i2c.h>
>> +#include <linux/gpio.h>
>> +#include <linux/irq.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/buffer.h>
>> +
>> +#include <linux/iio/accel/st_accel.h>
>> +#include <linux/platform_data/st_accel_pdata.h>
>> +
>> +
>> +#define UG_TO_MS2(x)                           (x*9800)
>> +#define ST_ACCEL_FULLSCALE_AVL_MAX             5
>> +#define ST_ACCEL_ODR_LIST_MAX                  10
>> +#define ST_ACCEL_ON                            1
>> +#define ST_ACCEL_OFF                           0
>> +
>> +/* DEFAULT VALUE FOR SENSORS */
>> +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
>> +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
>> +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
>> +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
>> +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
>> +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
>> +#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
>> +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
>> +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
>> +#define ST_ACCEL_DEFAULT_12_REALBITS           12
>> +#define ST_ACCEL_DEFAULT_16_REALBITS           16
>> +
>> +/* ODR */
>> +#define ST_ACCEL_ODR_AVL_1HZ                   1
>> +#define ST_ACCEL_ODR_AVL_3HZ                   3
>> +#define ST_ACCEL_ODR_AVL_6HZ                   6
>> +#define ST_ACCEL_ODR_AVL_10HZ                  10
>> +#define ST_ACCEL_ODR_AVL_12HZ                  12
>> +#define ST_ACCEL_ODR_AVL_25HZ                  25
>> +#define ST_ACCEL_ODR_AVL_50HZ                  50
>> +#define ST_ACCEL_ODR_AVL_100HZ                 100
>> +#define ST_ACCEL_ODR_AVL_200HZ                 200
>> +#define ST_ACCEL_ODR_AVL_400HZ                 400
>> +#define ST_ACCEL_ODR_AVL_800HZ                 800
>> +#define ST_ACCEL_ODR_AVL_1000HZ                        1000
>> +#define ST_ACCEL_ODR_AVL_1600HZ                        1600
>> +
>> +/* FULLSCALE */
>> +#define ST_ACCEL_FS_AVL_2G                     2
>> +#define ST_ACCEL_FS_AVL_4G                     4
>> +#define ST_ACCEL_FS_AVL_6G                     6
>> +#define ST_ACCEL_FS_AVL_8G                     8
>> +#define ST_ACCEL_FS_AVL_16G                    16
>> +
>> +/* CUSTOM VALUES FOR SENSOR 1 */
>> +#define ST_ACCEL_1_WAI_EXP                     0x33
>> +#define ST_ACCEL_1_ODR_ADDR                    0x20
>> +#define ST_ACCEL_1_ODR_MASK                    0xf0
>> +#define ST_ACCEL_1_ODR_N_BIT                   4
>> +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
>> +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
>> +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
>> +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
>> +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
>> +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
>> +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
>> +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
>> +#define ST_ACCEL_1_FS_N_BIT                    2
>> +#define ST_ACCEL_1_FS_ADDR                     0x23
>> +#define ST_ACCEL_1_FS_MASK                     0x30
>> +#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
>> +#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
>> +#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
>> +#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
>> +#define ST_ACCEL_1_FS_AVL_2_GAIN               1000
>> +#define ST_ACCEL_1_FS_AVL_4_GAIN               2000
>> +#define ST_ACCEL_1_FS_AVL_8_GAIN               4000
>> +#define ST_ACCEL_1_FS_AVL_16_GAIN              12000
>> +#define ST_ACCEL_1_BDU_ADDR                    0x23
>> +#define ST_ACCEL_1_BDU_MASK                    0x80
>> +#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
>> +#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
>> +#define ST_ACCEL_1_MULTIREAD_BIT               true
>> +
>> +/* CUSTOM VALUES FOR SENSOR 2 */
>> +#define ST_ACCEL_2_WAI_EXP                     0x49
>> +#define ST_ACCEL_2_ODR_ADDR                    0x20
>> +#define ST_ACCEL_2_ODR_MASK                    0xf0
>> +#define ST_ACCEL_2_ODR_N_BIT                   4
>> +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
>> +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
>> +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
>> +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
>> +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
>> +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
>> +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
>> +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
>> +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
>> +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
>> +#define ST_ACCEL_2_FS_N_BIT                    3
>> +#define ST_ACCEL_2_FS_ADDR                     0x21
>> +#define ST_ACCEL_2_FS_MASK                     0x38
>> +#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
>> +#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
>> +#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
>> +#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
>> +#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
>> +#define ST_ACCEL_2_FS_AVL_2_GAIN               61
>> +#define ST_ACCEL_2_FS_AVL_4_GAIN               122
>> +#define ST_ACCEL_2_FS_AVL_6_GAIN               183
>> +#define ST_ACCEL_2_FS_AVL_8_GAIN               244
>> +#define ST_ACCEL_2_FS_AVL_16_GAIN              732
>> +#define ST_ACCEL_2_BDU_ADDR                    0x20
>> +#define ST_ACCEL_2_BDU_MASK                    0x08
>> +#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
>> +#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
>> +#define ST_ACCEL_2_MULTIREAD_BIT               true
>> +
>> +/* CUSTOM VALUES FOR SENSOR 3 */
>> +#define ST_ACCEL_3_WAI_EXP                     0x32
>> +#define ST_ACCEL_3_ODR_ADDR                    0x20
>> +#define ST_ACCEL_3_ODR_MASK                    0x18
>> +#define ST_ACCEL_3_ODR_N_BIT                   2
>> +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
>> +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
>> +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
>> +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
>> +#define ST_ACCEL_3_PW_ADDR                     0x20
>> +#define ST_ACCEL_3_PW_MASK                     0xe0
>> +#define ST_ACCEL_3_PW_N_BIT                    3
>> +#define ST_ACCEL_3_PW_VALUE_ON                 0x01
>> +#define ST_ACCEL_3_PW_VALUE_OFF                        0x00
>> +#define ST_ACCEL_3_FS_N_BIT                    2
>> +#define ST_ACCEL_3_FS_ADDR                     0x23
>> +#define ST_ACCEL_3_FS_MASK                     0x30
>> +#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
>> +#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
>> +#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
>> +#define ST_ACCEL_3_FS_AVL_2_GAIN               1000
>> +#define ST_ACCEL_3_FS_AVL_4_GAIN               2000
>> +#define ST_ACCEL_3_FS_AVL_8_GAIN               3900
>> +#define ST_ACCEL_3_BDU_ADDR                    0x23
>> +#define ST_ACCEL_3_BDU_MASK                    0x80
>> +#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
>> +#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
>> +#define ST_ACCEL_3_MULTIREAD_BIT               true
>> +
>> +/* CUSTOM VALUES FOR SENSOR 4 */
>> +#define ST_ACCEL_4_WAI_EXP                     0x40
>> +#define ST_ACCEL_4_ODR_ADDR                    0x20
>> +#define ST_ACCEL_4_ODR_MASK                    0xf0
>> +#define ST_ACCEL_4_ODR_N_BIT                   4
>> +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
>> +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
>> +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
>> +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
>> +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
>> +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
>> +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
>> +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
>> +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
>> +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
>> +#define ST_ACCEL_4_FS_N_BIT                    3
>> +#define ST_ACCEL_4_FS_ADDR                     0x24
>> +#define ST_ACCEL_4_FS_MASK                     0x38
>> +#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
>> +#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
>> +#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
>> +#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
>> +#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
>> +#define ST_ACCEL_4_FS_AVL_2_GAIN               61
>> +#define ST_ACCEL_4_FS_AVL_4_GAIN               122
>> +#define ST_ACCEL_4_FS_AVL_6_GAIN               183
>> +#define ST_ACCEL_4_FS_AVL_8_GAIN               244
>> +#define ST_ACCEL_4_FS_AVL_16_GAIN              732
>> +#define ST_ACCEL_4_BDU_ADDR                    0x20
>> +#define ST_ACCEL_4_BDU_MASK                    0x08
>> +#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
>> +#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
>> +#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
>> +#define ST_ACCEL_4_IG1_EN_MASK                 0x08
>> +#define ST_ACCEL_4_MULTIREAD_BIT               false
>> +
>> +struct st_accel_odr_available {
>> +       unsigned int hz;
>> +       u8 value;
>> +};
>> +
>> +struct st_accel_odr {
>> +       u8 addr;
>> +       u8 mask;
>> +       short num_bit;
>> +       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
>> +};
>> +
>> +struct st_accel_power {
>> +       u8 addr;
>> +       u8 mask;
>> +       unsigned short num_bit;
>> +       u8 value_off;
>> +       u8 value_on;
>> +};
>> +
>> +struct st_accel_fullscale_available {
>> +       unsigned int num;
>> +       u8 value;
>> +       unsigned int gain;
>> +};
>> +
>> +struct st_accel_fullscale {
>> +       u8 addr;
>> +       u8 mask;
>> +       unsigned short num_bit;
>> +       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
>> +};
>> +
>> +struct st_accel_bdu {
>> +       u8 addr;
>> +       u8 mask;
>> +};
>> +
>> +struct st_accel_interrupt_generator {
>> +       u8 en_addr;
>> +       u8 latch_mask_addr;
>> +       u8 en_mask;
>> +       u8 latching_mask;
>> +};
>> +
>> +struct st_accel_data_ready_irq {
>> +       u8 addr;
>> +       u8 mask;
>> +       struct st_accel_interrupt_generator ig1;
>> +};
>> +
>> +#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
>> +{ \
>> +       .type = IIO_ACCEL, \
>> +       .modified = 1, \
>> +       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
>> +                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
>> +       .scan_index = index, \
>> +       .channel2 = mod, \
>> +       .address = addr, \
>> +       .scan_type = { \
>> +               .sign = 's', \
>> +               .realbits = bits, \
>> +               .shift = 16 - bits, \
>> +               .storagebits = 16, \
>> +               .endianness = endian, \
>> +       }, \
>> +}
>> +
>> +static const struct iio_chan_spec st_accel_12bit_channels[] = {
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
>> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
>> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
>> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
>> +       IIO_CHAN_SOFT_TIMESTAMP(3)
>> +};
>> +
>> +static const struct iio_chan_spec st_accel_16bit_channels[] = {
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
>> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
>> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
>> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
>> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
>> +       IIO_CHAN_SOFT_TIMESTAMP(3)
>> +};
>> +
>> +static const struct st_accel_platform_data st_accel_default_pdata = {
>> +       .fullscale = ST_ACCEL_FS_AVL_2G,
>> +       .sampling_frequency = ST_ACCEL_ODR_AVL_100HZ,
>> +};
>> +
>> +/**
>> + * struct st_accel_sensors - ST accel sensors list
>> + * @wai: Contents of WhoAmI register.
>> + * @ch: IIO channels for the sensor.
>> + * @odr: Output data rate register and odr list available.
>> + * @pw: Power register of the sensor.
>> + * @fs: Full scale register and fs list available.
>> + * @bdu: Block data update register.
>> + * @drdy_irq: Data ready register of the sensor.
>> + * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
>> + *
>> + */
>> +
>> +static const struct st_accel_sensors {
>> +       u8 wai;
>> +       struct iio_chan_spec *ch;
>> +       struct st_accel_odr odr;
>> +       struct st_accel_power pw;
>> +       struct st_accel_fullscale fs;
>> +       struct st_accel_bdu bdu;
>> +       struct st_accel_data_ready_irq drdy_irq;
>> +       bool multi_read_bit;
>> +} st_accel_sensors[] = {
>> +       {
>> +               .wai = ST_ACCEL_1_WAI_EXP,
>> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
>> +               .odr = {
>> +                       .addr = ST_ACCEL_1_ODR_ADDR,
>> +                       .mask = ST_ACCEL_1_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
>> +                       .odr_avl = {
>> +                               [0] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_1HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_1HZ_VAL,
>> +                               },
>> +                               [1] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_10HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_10HZ_VAL,
>> +                               },
>> +                               [2] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_25HZ_VAL,
>> +                               },
>> +                               [3] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_50HZ_VAL,
>> +                               },
>> +                               [4] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_100HZ_VAL,
>> +                               },
>> +                               [5] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_200HZ_VAL,
>> +                               },
>> +                               [6] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_400HZ_VAL,
>> +                               },
>> +                               [7] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
>> +                                       .value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL,
>> +                               },
>> +                       },
>> +               },
>> +               .pw = {
>> +                       .addr = ST_ACCEL_1_ODR_ADDR,
>> +                       .mask = ST_ACCEL_1_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
>> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
>> +               },
>> +               .fs = {
>> +                       .addr = ST_ACCEL_1_FS_ADDR,
>> +                       .mask = ST_ACCEL_1_FS_MASK,
>> +                       .num_bit = ST_ACCEL_1_FS_N_BIT,
>> +                       .fs_avl = {
>> +                               [0] = {
>> +                                       .num = ST_ACCEL_FS_AVL_2G,
>> +                                       .value = ST_ACCEL_1_FS_AVL_2_VAL,
>> +                                       .gain = ST_ACCEL_1_FS_AVL_2_GAIN,
>> +                               },
>> +                               [1] = {
>> +                                       .num = ST_ACCEL_FS_AVL_4G,
>> +                                       .value = ST_ACCEL_1_FS_AVL_4_VAL,
>> +                                       .gain = ST_ACCEL_1_FS_AVL_4_GAIN,
>> +                               },
>> +                               [2] = {
>> +                                       .num = ST_ACCEL_FS_AVL_8G,
>> +                                       .value = ST_ACCEL_1_FS_AVL_8_VAL,
>> +                                       .gain = ST_ACCEL_1_FS_AVL_8_GAIN,
>> +                               },
>> +                               [3] = {
>> +                                       .num = ST_ACCEL_FS_AVL_16G,
>> +                                       .value = ST_ACCEL_1_FS_AVL_16_VAL,
>> +                                       .gain = ST_ACCEL_1_FS_AVL_16_GAIN,
>> +                               },
>> +                       },
>> +               },
>> +               .bdu = {
>> +                       .addr = ST_ACCEL_1_BDU_ADDR,
>> +                       .mask = ST_ACCEL_1_BDU_MASK,
>> +               },
>> +               .drdy_irq = {
>> +                       .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
>> +                       .mask = ST_ACCEL_1_DRDY_IRQ_MASK,
>> +               },
>> +               .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
>> +               },
>> +               {
>> +               .wai = ST_ACCEL_2_WAI_EXP,
>> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
>> +               .odr = {
>> +                       .addr = ST_ACCEL_2_ODR_ADDR,
>> +                       .mask = ST_ACCEL_2_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
>> +                       .odr_avl = {
>> +                               [0] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_3HZ_VAL,
>> +                               },
>> +                               [1] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_6HZ_VAL,
>> +                               },
>> +                               [2] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_12HZ_VAL,
>> +                               },
>> +                               [3] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_25HZ_VAL,
>> +                               },
>> +                               [4] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_50HZ_VAL,
>> +                               },
>> +                               [5] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_100HZ_VAL,
>> +                               },
>> +                               [6] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_200HZ_VAL,
>> +                               },
>> +                               [7] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_400HZ_VAL,
>> +                               },
>> +                               [8] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_800HZ_VAL,
>> +                               },
>> +                               [9] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
>> +                                       .value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL,
>> +                               },
>> +                       },
>> +               },
>> +               .pw = {
>> +                       .addr = ST_ACCEL_2_ODR_ADDR,
>> +                       .mask = ST_ACCEL_2_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
>> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
>> +               },
>> +               .fs = {
>> +                       .addr = ST_ACCEL_2_FS_ADDR,
>> +                       .mask = ST_ACCEL_2_FS_MASK,
>> +                       .num_bit = ST_ACCEL_2_FS_N_BIT,
>> +                       .fs_avl = {
>> +                               [0] = {
>> +                                       .num = ST_ACCEL_FS_AVL_2G,
>> +                                       .value = ST_ACCEL_2_FS_AVL_2_VAL,
>> +                                       .gain = ST_ACCEL_2_FS_AVL_2_GAIN,
>> +                               },
>> +                               [1] = {
>> +                                       .num = ST_ACCEL_FS_AVL_4G,
>> +                                       .value = ST_ACCEL_2_FS_AVL_4_VAL,
>> +                                       .gain = ST_ACCEL_2_FS_AVL_4_GAIN,
>> +                               },
>> +                               [2] = {
>> +                                       .num = ST_ACCEL_FS_AVL_6G,
>> +                                       .value = ST_ACCEL_2_FS_AVL_6_VAL,
>> +                                       .gain = ST_ACCEL_2_FS_AVL_6_GAIN,
>> +                               },
>> +                               [3] = {
>> +                                       .num = ST_ACCEL_FS_AVL_8G,
>> +                                       .value = ST_ACCEL_2_FS_AVL_8_VAL,
>> +                                       .gain = ST_ACCEL_2_FS_AVL_8_GAIN,
>> +                               },
>> +                               [4] = {
>> +                                       .num = ST_ACCEL_FS_AVL_16G,
>> +                                       .value = ST_ACCEL_2_FS_AVL_16_VAL,
>> +                                       .gain = ST_ACCEL_2_FS_AVL_16_GAIN,
>> +                               },
>> +                       },
>> +               },
>> +               .drdy_irq = {
>> +                       .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
>> +                       .mask = ST_ACCEL_2_DRDY_IRQ_MASK,
>> +               },
>> +               .bdu = {
>> +                       .addr = ST_ACCEL_2_BDU_ADDR,
>> +                       .mask = ST_ACCEL_2_BDU_MASK,
>> +               },
>> +               .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
>> +               },
>> +               {
>> +               .wai = ST_ACCEL_3_WAI_EXP,
>> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
>> +               .odr = {
>> +                       .addr = ST_ACCEL_3_ODR_ADDR,
>> +                       .mask = ST_ACCEL_3_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_3_ODR_N_BIT,
>> +                       .odr_avl = {
>> +                               [0] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
>> +                                       .value = ST_ACCEL_3_ODR_AVL_50HZ_VAL,
>> +                               },
>> +                               [1] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
>> +                                       .value = ST_ACCEL_3_ODR_AVL_100HZ_VAL,
>> +                               },
>> +                               [2] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
>> +                                       .value = ST_ACCEL_3_ODR_AVL_400HZ_VAL,
>> +                               },
>> +                               [3] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_1000HZ,
>> +                                       .value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL,
>> +                               },
>> +                       },
>> +               },
>> +               .pw = {
>> +                       .addr = ST_ACCEL_3_PW_ADDR,
>> +                       .mask = ST_ACCEL_3_PW_MASK,
>> +                       .num_bit = ST_ACCEL_3_PW_N_BIT,
>> +                       .value_on = ST_ACCEL_3_PW_VALUE_ON,
>> +                       .value_off = ST_ACCEL_3_PW_VALUE_OFF,
>> +               },
>> +               .fs = {
>> +                       .addr = ST_ACCEL_3_FS_ADDR,
>> +                       .mask = ST_ACCEL_3_FS_MASK,
>> +                       .num_bit = ST_ACCEL_3_FS_N_BIT,
>> +                       .fs_avl = {
>> +                               [0] = {
>> +                                       .num = ST_ACCEL_FS_AVL_2G,
>> +                                       .value = ST_ACCEL_3_FS_AVL_2_VAL,
>> +                                       .gain = ST_ACCEL_3_FS_AVL_2_GAIN,
>> +                               },
>> +                               [1] = {
>> +                                       .num = ST_ACCEL_FS_AVL_4G,
>> +                                       .value = ST_ACCEL_3_FS_AVL_4_VAL,
>> +                                       .gain = ST_ACCEL_3_FS_AVL_4_GAIN,
>> +                               },
>> +                               [2] = {
>> +                                       .num = ST_ACCEL_FS_AVL_8G,
>> +                                       .value = ST_ACCEL_3_FS_AVL_8_VAL,
>> +                                       .gain = ST_ACCEL_3_FS_AVL_8_GAIN,
>> +                               },
>> +                       },
>> +               },
>> +               .bdu = {
>> +                       .addr = ST_ACCEL_3_BDU_ADDR,
>> +                       .mask = ST_ACCEL_3_BDU_MASK,
>> +               },
>> +               .drdy_irq = {
>> +                       .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
>> +                       .mask = ST_ACCEL_3_DRDY_IRQ_MASK,
>> +               },
>> +               .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
>> +               },
>> +               {
>> +               .wai = ST_ACCEL_4_WAI_EXP,
>> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
>> +               .odr = {
>> +                       .addr = ST_ACCEL_4_ODR_ADDR,
>> +                       .mask = ST_ACCEL_4_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
>> +                       .odr_avl = {
>> +                               [0] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_3HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_3HZ_VAL,
>> +                               },
>> +                               [1] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_6HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_6HZ_VAL,
>> +                               },
>> +                               [2] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_12HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_12HZ_VAL,
>> +                               },
>> +                               [3] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_25HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_25HZ_VAL,
>> +                               },
>> +                               [4] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_50HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_50HZ_VAL,
>> +                               },
>> +                               [5] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_100HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_100HZ_VAL,
>> +                               },
>> +                               [6] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_200HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_200HZ_VAL,
>> +                               },
>> +                               [7] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_400HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_400HZ_VAL,
>> +                               },
>> +                               [8] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_800HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_800HZ_VAL,
>> +                               },
>> +                               [9] = {
>> +                                       .hz = ST_ACCEL_ODR_AVL_1600HZ,
>> +                                       .value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL,
>> +                               },
>> +                       },
>> +               },
>> +               .pw = {
>> +                       .addr = ST_ACCEL_4_ODR_ADDR,
>> +                       .mask = ST_ACCEL_4_ODR_MASK,
>> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
>> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
>> +               },
>> +               .fs = {
>> +                       .addr = ST_ACCEL_4_FS_ADDR,
>> +                       .mask = ST_ACCEL_4_FS_MASK,
>> +                       .num_bit = ST_ACCEL_4_FS_N_BIT,
>> +                       .fs_avl = {
>> +                               [0] = {
>> +                                       .num = ST_ACCEL_FS_AVL_2G,
>> +                                       .value = ST_ACCEL_4_FS_AVL_2_VAL,
>> +                                       .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
>> +                               },
>> +                               [1] = {
>> +                                       .num = ST_ACCEL_FS_AVL_4G,
>> +                                       .value = ST_ACCEL_4_FS_AVL_4_VAL,
>> +                                       .gain = ST_ACCEL_4_FS_AVL_4_GAIN,
>> +                               },
>> +                               [2] = {
>> +                                       .num = ST_ACCEL_FS_AVL_6G,
>> +                                       .value = ST_ACCEL_4_FS_AVL_6_VAL,
>> +                                       .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
>> +                               },
>> +                               [3] = {
>> +                                       .num = ST_ACCEL_FS_AVL_8G,
>> +                                       .value = ST_ACCEL_4_FS_AVL_8_VAL,
>> +                                       .gain = ST_ACCEL_4_FS_AVL_8_GAIN,
>> +                               },
>> +                               [4] = {
>> +                                       .num = ST_ACCEL_FS_AVL_16G,
>> +                                       .value = ST_ACCEL_4_FS_AVL_16_VAL,
>> +                                       .gain = ST_ACCEL_4_FS_AVL_16_GAIN,
>> +                               },
>> +                       },
>> +               },
>> +               .bdu = {
>> +                       .addr = ST_ACCEL_4_BDU_ADDR,
>> +                       .mask = ST_ACCEL_4_BDU_MASK,
>> +               },
>> +               .drdy_irq = {
>> +                       .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
>> +                       .mask = ST_ACCEL_4_DRDY_IRQ_MASK,
>> +                       .ig1 = {
>> +                               .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
>> +                               .en_mask = ST_ACCEL_4_IG1_EN_MASK,
>> +                       },
>> +               },
>> +               .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
>> +       },
>> +};
>> +
>> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
>> reg_addr,
>> +                                               u8 mask, short num_bit, u8 data)
>> +{
>> +       int err, j, pos;
>> +       u8 prev_data;
>> +       u8 new_data;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       pos = 8 - num_bit;
>> +       for (j = 128; j >= 0; j = j/2) {
>> +               if (mask / j > 0)
>> +                       break;
>> +               else
>> +                       pos--;
>> +       }
>> +
>> +       err = adata->read_byte(adata, reg_addr, &prev_data);
>> +       if (err < 0)
>> +               goto st_accel_write_data_with_mask_error;
>> +
>> +       new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
>> +       err = adata->write_byte(adata, reg_addr, new_data);
>> +
>> +st_accel_write_data_with_mask_error:
>> +       return err;
>> +}
>> +
>> +static int st_accel_match_odr(const struct st_accel_sensors *sensor,
>> +               unsigned int odr, struct st_accel_odr_available *odr_out)
>> +{
>> +       int i, ret = -1;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
>> +               if (sensor->odr.odr_avl[i].hz == odr) {
>> +                       odr_out->hz = sensor->odr.odr_avl[i].hz;
>> +                       odr_out->value = sensor->odr.odr_avl[i].value;
>> +                       ret = 0;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static int st_accel_match_fs(const struct st_accel_sensors *sensor,
>> +               unsigned int fs, struct st_accel_fullscale_available *fs_out)
>> +{
>> +       int i, ret = -1;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
>> +               if (sensor->fs.fs_avl[i].num == fs) {
>> +                       fs_out->num = sensor->fs.fs_avl[i].num;
>> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
>> +                       fs_out->value = sensor->fs.fs_avl[i].value;
>> +                       ret = 0;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
>> +{
>> +       int err;
>> +       struct st_accel_data *adata;
>> +
>> +       adata = iio_priv(indio_dev);
>> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
>> +               err = st_accel_write_data_with_mask(indio_dev,
>> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
>> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
>> +                       (int)enable);
>> +               if (err < 0)
>> +                       goto st_accel_set_dataready_irq_error;
>> +       }
>> +
>> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
>> +               err = st_accel_write_data_with_mask(indio_dev,
>> +               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
>> +               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
>> +                       (int)enable);
>> +               if (err < 0)
>> +                       goto st_accel_set_dataready_irq_error;
>> +       }
>> +
>> +       err = st_accel_write_data_with_mask(indio_dev,
>> +               st_accel_sensors[adata->index].drdy_irq.addr,
>> +               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
>> +       if (err < 0)
>> +               goto st_accel_set_dataready_irq_error;
>> +
>> +st_accel_set_dataready_irq_error:
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(st_accel_set_dataready_irq);
>> +
>> +static int st_accel_set_bdu(struct iio_dev *indio_dev,
>> +                               const struct st_accel_bdu *bdu, u8 value)
>> +{
>> +       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
>> +                                                               1, value);
>> +}
>> +
>> +static int st_accel_set_odr(struct iio_dev *indio_dev,
>> +                               struct st_accel_odr_available *odr_available)
>> +{
>> +       int err;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       if ((st_accel_sensors[adata->index].odr.addr ==
>> +               st_accel_sensors[adata->index].pw.addr) &&
>> +                       (st_accel_sensors[adata->index].odr.mask ==
>> +                               st_accel_sensors[adata->index].pw.mask)) {
>> +               if (adata->enabled == (bool)ST_ACCEL_ON) {
>> +                       err = st_accel_write_data_with_mask(indio_dev,
>> +                               st_accel_sensors[adata->index].odr.addr,
>> +                               st_accel_sensors[adata->index].odr.mask,
>> +                               st_accel_sensors[adata->index].odr.num_bit,
>> +                               odr_available->value);
>> +                       if (err < 0)
>> +                               goto sc_accel_set_odr_error;
>> +               } else {
>> +                       adata->odr = odr_available->hz;
>> +                       err = 0;
>> +               }
>> +       } else {
>> +               err = st_accel_write_data_with_mask(indio_dev,
>> +                               st_accel_sensors[adata->index].odr.addr,
>> +                               st_accel_sensors[adata->index].odr.mask,
>> +                               st_accel_sensors[adata->index].odr.num_bit,
>> +                               odr_available->value);
>> +               if (err < 0)
>> +                       goto sc_accel_set_odr_error;
>> +       }
>> +
>> +sc_accel_set_odr_error:
>> +       return err;
>> +}
>> +
>> +static int st_accel_set_enable(struct iio_dev *indio_dev, int enable)
>> +{
>> +       int err;
>> +       bool found;
>> +       u8 tmp_value;
>> +       struct st_accel_odr_available odr_out;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       switch (enable) {
>> +       case ST_ACCEL_ON:
>> +               found = false;
>> +               tmp_value = st_accel_sensors[adata->index].pw.value_on;
>> +               if ((st_accel_sensors[adata->index].odr.addr ==
>> +                               st_accel_sensors[adata->index].pw.addr) &&
>> +                       (st_accel_sensors[adata->index].odr.mask ==
>> +                               st_accel_sensors[adata->index].pw.mask)) {
>> +                       err = st_accel_match_odr(
>> +                               &st_accel_sensors[adata->index], adata->odr,
>> +                                       &odr_out);
>> +                       if (err < 0)
>> +                               goto set_enable_error;
>> +                       tmp_value = odr_out.value;
>> +                       found = true;
>> +               }
>> +               err = st_accel_write_data_with_mask(indio_dev,
>> +                               st_accel_sensors[adata->index].pw.addr,
>> +                               st_accel_sensors[adata->index].pw.mask,
>> +                               st_accel_sensors[adata->index].pw.num_bit,
>> +                               tmp_value);
>> +               if (err < 0)
>> +                       goto set_enable_error;
>> +               adata->enabled = (bool)ST_ACCEL_ON;
>> +               if (found)
>> +                       adata->odr = odr_out.hz;
>> +               break;
>> +       case ST_ACCEL_OFF:
>> +               err = st_accel_write_data_with_mask(indio_dev,
>> +                               st_accel_sensors[adata->index].pw.addr,
>> +                               st_accel_sensors[adata->index].pw.mask,
>> +                               st_accel_sensors[adata->index].pw.num_bit,
>> +                               st_accel_sensors[adata->index].pw.value_off);
>> +               if (err < 0)
>> +                       goto set_enable_error;
>> +               adata->enabled = (bool)ST_ACCEL_OFF;
>> +               break;
>> +       default:
>> +               err = -EINVAL;
>> +               goto set_enable_error;
>> +       }
>> +
>> +set_enable_error:
>> +       return err;
>> +}
>> +
>> +static int st_accel_set_fullscale(struct iio_dev *indio_dev,
>> +                               struct st_accel_fullscale_available *fs_avl)
>> +{
>> +       int err;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       err = st_accel_write_data_with_mask(indio_dev,
>> +                               st_accel_sensors[adata->index].fs.addr,
>> +                               st_accel_sensors[adata->index].fs.mask,
>> +                               st_accel_sensors[adata->index].fs.num_bit,
>> +                               fs_avl->value);
>> +       if (err < 0)
>> +               goto st_accel_set_fullscale_error;
>> +
>> +       adata->fullscale = fs_avl->num;
>> +       adata->gain = fs_avl->gain;
>> +       return err;
>> +
>> +st_accel_set_fullscale_error:
>> +       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
>> +       return err;
>> +}
>> +
>> +static int st_accel_read_raw(struct iio_dev *indio_dev,
>> +                       struct iio_chan_spec const *ch, int *val,
>> +                                                       int *val2, long mask)
>> +{
>> +       int err;
>> +       int data_tmp;
>> +       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       switch (mask) {
>> +       case IIO_CHAN_INFO_RAW:
>> +               mutex_lock(&indio_dev->mlock);
>> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
>> +                       err = -EBUSY;
>> +                       goto read_error;
>> +               } else {
>> +                       if (!adata->enabled) {
>> +                               err = -EIO;
>> +                               goto read_error;
>> +                       } else {
>> +                               err = adata->read_multiple_byte(adata,
>> +                                       ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
>> +                                       outdata);
>> +                               if (err < 0)
>> +                                       goto read_error;
>> +
>> +                               *val = ((s16)(((s16)(outdata[1]) << 8)
>> +                                       | outdata[0])) >> ch->scan_type.shift;
>> +                       }
>> +               }
>> +               mutex_unlock(&indio_dev->mlock);
>> +               return IIO_VAL_INT;
>> +       case IIO_CHAN_INFO_SCALE:
>> +               data_tmp = UG_TO_MS2(adata->gain);
>> +               *val = 0;
>> +               *val2 = data_tmp;
>> +               return IIO_VAL_INT_PLUS_NANO;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +read_error:
>> +       mutex_unlock(&indio_dev->mlock);
>> +       return err;
>> +}
>> +
>> +static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
>> +{
>> +       int i;
>> +       bool found;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       found = false;
>> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
>> +               if (st_accel_sensors[i].wai == wai) {
>> +                       found = true;
>> +                       break;
>> +               }
>> +       }
>> +       if (!found)
>> +               goto check_device_error;
>> +
>> +       adata->index = i;
>> +
>> +       return i;
>> +
>> +check_device_error:
>> +       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
>> +       return -ENODEV;
>> +}
>> +
>> +static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
>> +                                                               u8 *value)
>> +{
>> +       int ret;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       ret = adata->read_byte(adata, reg_addr, value);
>> +       if (ret < 0)
>> +               goto read_byte_wai_error;
>> +
>> +       return 0;
>> +
>> +read_byte_wai_error:
>> +       dev_err(&indio_dev->dev,
>> +                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
>> +       return -EIO;
>> +}
>> +
>> +static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
>> +               struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> +       int err;
>> +       unsigned int freq;
>> +       struct st_accel_odr_available odr_out;
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       err = kstrtoint(buf, 10, &freq);
>> +       if (err < 0)
>> +               goto conversion_error;
>> +
>> +       mutex_lock(&indio_dev->mlock);
>> +       err = st_accel_match_odr(&st_accel_sensors[adata->index],
>> +                                                               freq, &odr_out);
>> +       if (err < 0)
>> +               goto st_accel_sysfs_set_sampling_frequency_error;
>> +
>> +       err = st_accel_set_odr(indio_dev, &odr_out);
>> +       if (err < 0) {
>> +               dev_err(&indio_dev->dev,
>> +                       "failed to set sampling frequency to %d.\n", freq);
>> +               goto st_accel_sysfs_set_sampling_frequency_error;
>> +       }
>> +       adata->odr = odr_out.hz;
>> +
>> +st_accel_sysfs_set_sampling_frequency_error:
>> +       mutex_unlock(&indio_dev->mlock);
>> +conversion_error:
>> +       return size;
>> +}
>> +
>> +static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       return sprintf(buf, "%d\n", adata->odr);
>> +}
>> +
>> +static ssize_t st_accel_sysfs_set_enable(struct device *dev,
>> +               struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> +       int err;
>> +       bool en;
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +
>> +       err = strtobool(buf, &en);
>> +       if (err < 0)
>> +               goto set_enable_error;
>> +
>> +       mutex_lock(&indio_dev->mlock);
>> +       err = st_accel_set_enable(indio_dev, (int)en);
>> +       if (err < 0)
>> +               dev_err(&indio_dev->dev,
>> +                               "failed to set enable to %d.\n", (int)en);
>> +       mutex_unlock(&indio_dev->mlock);
>> +
>> +set_enable_error:
>> +       return size;
>> +}
>> +
>> +static ssize_t st_accel_sysfs_get_enable(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       return sprintf(buf, "%d\n", (int)adata->enabled);
>> +}
>> +
>> +static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       return sprintf(buf, "%d\n", adata->fullscale);
>> +}
>> +
>> +static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
>> +               struct device_attribute *attr, const char *buf, size_t size)
>> +{
>> +       int err;
>> +       unsigned int fs;
>> +       struct st_accel_fullscale_available fs_out;
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       err = kstrtoint(buf, 10, &fs);
>> +       if (err < 0)
>> +               goto conversion_error;
>> +
>> +       mutex_lock(&indio_dev->mlock);
>> +       err = st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_out);
>> +       if (err < 0)
>> +               goto match_fullscale_error;
>> +
>> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
>> +       if (err < 0) {
>> +               dev_err(&indio_dev->dev,
>> +                       "failed to set new fullscale. (errn %d).\n", err);
>> +       }
>> +
>> +match_fullscale_error:
>> +       mutex_unlock(&indio_dev->mlock);
>> +conversion_error:
>> +       return size;
>> +}
>> +
>> +static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       int i, len = 0;
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       mutex_lock(&indio_dev->mlock);
>> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
>> +                                                                       i++) {
>> +               if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
>> +                       break;
>> +
>> +               len += sprintf(buf+len, "%d ",
>> +                       st_accel_sensors[adata->index].fs.fs_avl[i].num);
>> +       }
>> +       mutex_unlock(&indio_dev->mlock);
>> +
>> +       len--;
>> +       len += sprintf(buf+len, "\n");
>> +       return len;
>> +}
>> +
>> +static ssize_t st_accel_sysfs_sampling_frequency_available(struct
>> device *dev,
>> +                               struct device_attribute *attr, char *buf)
>> +{
>> +       int i, len = 0;
>> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       mutex_lock(&indio_dev->mlock);
>> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
>> +                                                                       i++) {
>> +               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
>> +                       break;
>> +
>> +               len += sprintf(buf+len, "%d ",
>> +                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
>> +       }
>> +       mutex_unlock(&indio_dev->mlock);
>> +
>> +       len--;
>> +       len += sprintf(buf+len, "\n");
>> +       return len;
>> +}
>> +
>> +/**
>> + * IIO_DEVICE_ATTR - sampling_frequency_available
>> + * @read: show all frequency available of the sensor.
>> + *
>> + */
>> +
>> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
>> +               st_accel_sysfs_sampling_frequency_available, NULL , 0);
>> +
>> +/**
>> + * IIO_DEVICE_ATTR - fullscale_available
>> + * @read: show all fullscale available of the sensor.
>> + *
>> + */
>> +
>> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
>> +               st_accel_sysfs_fullscale_available, NULL , 0);
>> +
>> +/**
>> + * IIO_DEVICE_ATTR - fullscale
>> + * @read: show the current fullscale of the sensor.
>> + * @write: store the current fullscale of the sensor.
>> + *
>> + */
>> +
>> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
>> +               st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0);
>> +
>> +/**
>> + * IIO_DEVICE_ATTR - enable
>> + * @read: show the current status of the sensor.
>> + * @write: power on/off the sensor.
>> + *
>> + */
>> +
>> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
>> st_accel_sysfs_get_enable,
>> +               st_accel_sysfs_set_enable , 0);
>> +
>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>> +                       st_accel_sysfs_get_sampling_frequency,
>> +                                       st_accel_sysfs_set_sampling_frequency);
>> +
>> +static struct attribute *st_accel_attributes[] = {
>> +       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
>> +       &iio_dev_attr_fullscale_available.dev_attr.attr,
>> +       &iio_dev_attr_fullscale.dev_attr.attr,
>> +       &iio_dev_attr_enable.dev_attr.attr,
>> +       &iio_dev_attr_sampling_frequency.dev_attr.attr,
>> +       NULL,
>> +};
>> +
>> +static const struct attribute_group st_accel_attribute_group = {
>> +       .attrs = st_accel_attributes,
>> +};
>> +
>> +static const struct iio_info acc_info = {
>> +       .driver_module = THIS_MODULE,
>> +       .attrs = &st_accel_attribute_group,
>> +       .read_raw = &st_accel_read_raw,
>> +};
>> +
>> +static int st_accel_init_sensor(struct iio_dev *indio_dev)
>> +{
>> +       int err;
>> +       struct st_accel_odr_available odr_out;
>> +       struct st_accel_fullscale_available fs_out;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       err = st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
>> +       if (err < 0)
>> +               goto init_error;
>> +
>> +       st_accel_match_fs(&st_accel_sensors[adata->index],
>> +                                               adata->fullscale, &fs_out);
>> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
>> +       if (err < 0)
>> +               goto init_error;
>> +
>> +       st_accel_match_odr(&st_accel_sensors[adata->index],
>> +                                                       adata->odr, &odr_out);
>> +       err = st_accel_set_odr(indio_dev, &odr_out);
>> +       if (err < 0)
>> +               goto init_error;
>> +
>> +       err = st_accel_set_bdu(indio_dev,
>> +                       &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
>> +       if (err < 0)
>> +               goto init_error;
>> +
>> +       return 0;
>> +
>> +init_error:
>> +       dev_err(&indio_dev->dev, "failed to init sensor data.");
>> +       return -EIO;
>> +}
>> +
>> +int st_accel_iio_probe(struct iio_dev *indio_dev)
>> +{
>> +       int err;
>> +       u8 wai;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +       struct st_accel_platform_data *pdata;
>> +
>> +       mutex_init(&adata->slock);
>> +       indio_dev->modes = INDIO_DIRECT_MODE;
>> +       indio_dev->info = &acc_info;
>> +
>> +       err = st_accel_get_wai_device(indio_dev,
>> +                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
>> +       if (err < 0)
>> +               goto st_accel_iio_probe_error;
>> +
>> +       err = st_accel_check_device_list(indio_dev, wai);
>> +       if (err < 0)
>> +               goto st_accel_iio_probe_error;
>> +
>> +       adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
>> +       indio_dev->channels = st_accel_sensors[adata->index].ch;
>> +       indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
>> +       pdata = adata->dev->platform_data;
>> +       if (pdata == NULL) {
>> +               adata->fullscale = st_accel_default_pdata.fullscale;
>> +               adata->odr = st_accel_default_pdata.sampling_frequency;
>> +       } else {
>> +               adata->fullscale = pdata->fullscale;
>> +               adata->odr = pdata->sampling_frequency;
>> +       }
>> +
>> +       err = st_accel_init_sensor(indio_dev);
>> +       if (err < 0)
>> +               goto st_accel_iio_probe_error;
>> +
>> +       err = st_accel_allocate_ring(indio_dev);
>> +       if (err < 0)
>> +               goto st_accel_iio_probe_error;
>> +
>> +       if (*adata->irq_data_ready > 0) {
>> +               err = st_accel_probe_trigger(indio_dev);
>> +               if (err < 0)
>> +                       goto acc_probe_trigger_error;
>> +       }
>> +
>> +       err = iio_device_register(indio_dev);
>> +       if (err)
>> +               goto iio_device_register_error;
>> +
>> +       return err;
>> +
>> +iio_device_register_error:
>> +       st_accel_remove_trigger(indio_dev);
>> +acc_probe_trigger_error:
>> +       st_accel_deallocate_ring(indio_dev);
>> +st_accel_iio_probe_error:
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(st_accel_iio_probe);
>> +
>> +void st_accel_iio_remove(struct iio_dev *indio_dev)
>> +{
>> +       iio_device_unregister(indio_dev);
>> +       st_accel_remove_trigger(indio_dev);
>> +       st_accel_deallocate_ring(indio_dev);
>> +       iio_device_free(indio_dev);
>> +}
>> +EXPORT_SYMBOL(st_accel_iio_remove);
>> +
>> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/accel/st_accel_i2c.c
>> b/drivers/iio/accel/st_accel_i2c.c
>> new file mode 100644
>> index 0000000..7cfeb4c
>> --- /dev/null
>> +++ b/drivers/iio/accel/st_accel_i2c.c
>> @@ -0,0 +1,139 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/i2c.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +
>> +#include <linux/iio/accel/st_accel.h>
>> +
>> +
>> +#define ST_ACCEL_I2C_MULTIREAD                 0x80
>> +
>> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
>> +                                               u8 reg_addr, u8 *res_byte)
>> +{
>> +       int err;
>> +
>> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
>> +       if (err < 0)
>> +               goto st_accel_i2c_read_byte_error;
>> +       *res_byte = err & 0xff;
>> +       return err;
>> +
>> +st_accel_i2c_read_byte_error:
>> +       return -EIO;
>> +}
>> +
>> +static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
>> +                                               u8 reg_addr, int len, u8 *data)
>> +{
>> +       int err;
>> +
>> +       if (adata->multiread_bit == true)
>> +               reg_addr |= ST_ACCEL_I2C_MULTIREAD;
>> +
>> +       err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
>> +                                                       reg_addr, len, data);
>> +       if (err < 0)
>> +               goto st_accel_i2c_read_multiple_byte_error;
>> +
>> +       return err;
>> +
>> +st_accel_i2c_read_multiple_byte_error:
>> +       return -EIO;
>> +}
>> +
>> +static int st_accel_i2c_write_byte(struct st_accel_data *adata,
>> +                                                       u8 reg_addr, u8 data)
>> +{
>> +       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
>> +                                                               reg_addr, data);
>> +}
>> +
>> +static int __devinit st_accel_i2c_probe(struct i2c_client *client,
>> +                               const struct i2c_device_id *id)
>> +{
>> +       struct iio_dev *indio_dev;
>> +       struct st_accel_data *adata;
>> +       int err;
>> +
>> +       indio_dev = iio_device_alloc(sizeof(*adata));
>> +       if (indio_dev == NULL) {
>> +               err = -ENOMEM;
>> +               goto iio_device_alloc_error;
>> +       }
>> +
>> +       adata = iio_priv(indio_dev);
>> +       adata->dev = &client->dev;
>> +       i2c_set_clientdata(client, indio_dev);
>> +
>> +       indio_dev->dev.parent = &client->dev;
>> +       indio_dev->name = client->name;
>> +
>> +       adata->read_byte = st_accel_i2c_read_byte;
>> +       adata->write_byte = st_accel_i2c_write_byte;
>> +       adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
>> +       adata->irq_data_ready = &client->irq;
>> +
>> +       err = st_accel_iio_probe(indio_dev);
>> +       if (err < 0)
>> +               goto acc_iio_default_error;
>> +
>> +       return 0;
>> +
>> +acc_iio_default_error:
>> +       iio_device_free(indio_dev);
>> +iio_device_alloc_error:
>> +       return err;
>> +}
>> +
>> +static int __devexit st_accel_i2c_remove(struct i2c_client *client)
>> +{
>> +       struct iio_dev *indio_dev = i2c_get_clientdata(client);
>> +
>> +       st_accel_iio_remove(indio_dev);
>> +       return 0;
>> +}
>> +
>> +static const struct i2c_device_id st_accel_id_table[] = {
>> +       { LSM303DLH_ACCEL_DEV_NAME },
>> +       { LSM303DLHC_ACCEL_DEV_NAME },
>> +       { LIS3DH_ACCEL_DEV_NAME },
>> +       { LSM330D_ACCEL_DEV_NAME },
>> +       { LSM330DL_ACCEL_DEV_NAME },
>> +       { LSM330DLC_ACCEL_DEV_NAME },
>> +       { LSM303D_ACCEL_DEV_NAME },
>> +       { LSM9DS0_ACCEL_DEV_NAME },
>> +       { LIS331DLH_ACCEL_DEV_NAME },
>> +       { LSM303DL_ACCEL_DEV_NAME },
>> +       { LSM303DLM_ACCEL_DEV_NAME },
>> +       { LSM330_ACCEL_DEV_NAME },
>> +       {},
>> +};
>> +MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
>> +
>> +static struct i2c_driver st_accel_driver = {
>> +       .driver = {
>> +               .owner = THIS_MODULE,
>> +               .name = "st-accel-i2c",
>> +       },
>> +       .probe = st_accel_i2c_probe,
>> +       .remove = __devexit_p(st_accel_i2c_remove),
>> +       .id_table = st_accel_id_table,
>> +};
>> +module_i2c_driver(st_accel_driver);
>> +
>> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/accel/st_accel_spi.c
>> b/drivers/iio/accel/st_accel_spi.c
>> new file mode 100644
>> index 0000000..40279bd
>> --- /dev/null
>> +++ b/drivers/iio/accel/st_accel_spi.c
>> @@ -0,0 +1,199 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +
>> +#include <linux/iio/accel/st_accel.h>
>> +
>> +
>> +#define ACC_SPI_READ           0x80;
>> +#define ACC_SPI_MULTIREAD      0xc0
>> +
>> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
>> +                                               u8 reg_addr, u8 *res_byte)
>> +{
>> +       struct spi_message msg;
>> +       int err;
>> +       u8 tx;
>> +
>> +       struct spi_transfer xfers[] = {
>> +               {
>> +                       .tx_buf = &tx,
>> +                       .bits_per_word = 8,
>> +                       .len = 1,
>> +               },
>> +               {
>> +                       .rx_buf = res_byte,
>> +                       .bits_per_word = 8,
>> +                       .len = 1,
>> +               }
>> +       };
>> +
>> +       mutex_lock(&adata->slock);
>> +       tx = reg_addr | ACC_SPI_READ;
>> +       spi_message_init(&msg);
>> +       spi_message_add_tail(&xfers[0], &msg);
>> +       spi_message_add_tail(&xfers[1], &msg);
>> +       err = spi_sync(to_spi_device(adata->dev), &msg);
>> +       mutex_unlock(&adata->slock);
>> +       if (err)
>> +               goto acc_spi_read_byte_error;
>> +
>> +       return err;
>> +
>> +acc_spi_read_byte_error:
>> +       return -EIO;
>> +}
>> +
>> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
>> +                                               u8 reg_addr, int len, u8 *data)
>> +{
>> +       struct spi_message msg;
>> +       int err;
>> +       u8 tx;
>> +
>> +       struct spi_transfer xfers[] = {
>> +               {
>> +                       .tx_buf = &tx,
>> +                       .bits_per_word = 8,
>> +                       .len = 1,
>> +               },
>> +               {
>> +                       .rx_buf = data,
>> +                       .bits_per_word = 8,
>> +                       .len = len,
>> +               }
>> +       };
>> +
>> +       mutex_lock(&adata->slock);
>> +       if (adata->multiread_bit == true)
>> +               tx = reg_addr | ACC_SPI_MULTIREAD;
>> +       else
>> +               tx = reg_addr | ACC_SPI_READ;
>> +
>> +       spi_message_init(&msg);
>> +       spi_message_add_tail(&xfers[0], &msg);
>> +       spi_message_add_tail(&xfers[1], &msg);
>> +       err = spi_sync(to_spi_device(adata->dev), &msg);
>> +       mutex_unlock(&adata->slock);
>> +       if (err)
>> +               goto acc_spi_read_multiple_byte_error;
>> +       return len;
>> +
>> +acc_spi_read_multiple_byte_error:
>> +       return -EIO;
>> +}
>> +
>> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
>> +                                                       u8 reg_addr, u8 data)
>> +{
>> +       struct spi_message msg;
>> +       int err;
>> +       u8 tx[2];
>> +
>> +       struct spi_transfer xfers[] = {
>> +               {
>> +                       .tx_buf = tx,
>> +                       .bits_per_word = 8,
>> +                       .len = 2,
>> +               }
>> +       };
>> +
>> +       mutex_lock(&adata->slock);
>> +       tx[0] = reg_addr;
>> +       tx[1] = data;
>> +       spi_message_init(&msg);
>> +       spi_message_add_tail(&xfers[0], &msg);
>> +       err = spi_sync(to_spi_device(adata->dev), &msg);
>> +       mutex_unlock(&adata->slock);
>> +
>> +       return err;
>> +}
>> +
>> +static int __devinit st_accel_spi_probe(struct spi_device *client)
>> +{
>> +       struct iio_dev *indio_dev;
>> +       struct st_accel_data *adata;
>> +       int err;
>> +
>> +       indio_dev = iio_device_alloc(sizeof(*adata));
>> +       if (indio_dev == NULL) {
>> +               err = -ENOMEM;
>> +               goto iio_device_alloc_error;
>> +       }
>> +
>> +       adata = iio_priv(indio_dev);
>> +       adata->dev = &client->dev;
>> +       spi_set_drvdata(client, indio_dev);
>> +
>> +       indio_dev->dev.parent = &client->dev;
>> +       indio_dev->name = client->modalias;
>> +
>> +       adata->read_byte = st_accel_spi_read_byte;
>> +       adata->write_byte = st_accel_spi_write_byte;
>> +       adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
>> +       adata->irq_data_ready = &client->irq;
>> +
>> +       err = st_accel_iio_probe(indio_dev);
>> +       if (err < 0)
>> +               goto acc_iio_default_error;
>> +
>> +       return 0;
>> +
>> +acc_iio_default_error:
>> +       iio_device_free(indio_dev);
>> +iio_device_alloc_error:
>> +       return err;
>> +}
>> +
>> +static int __devexit st_accel_spi_remove(struct spi_device *spi)
>> +{
>> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
>> +
>> +       st_accel_iio_remove(indio_dev);
>> +       return 0;
>> +}
>> +
>> +static const struct spi_device_id st_accel_id_table[] = {
>> +       { LSM303DLH_ACCEL_DEV_NAME },
>> +       { LSM303DLHC_ACCEL_DEV_NAME },
>> +       { LIS3DH_ACCEL_DEV_NAME },
>> +       { LSM330D_ACCEL_DEV_NAME },
>> +       { LSM330DL_ACCEL_DEV_NAME },
>> +       { LSM330DLC_ACCEL_DEV_NAME },
>> +       { LSM303D_ACCEL_DEV_NAME },
>> +       { LSM9DS0_ACCEL_DEV_NAME },
>> +       { LIS331DLH_ACCEL_DEV_NAME },
>> +       { LSM303DL_ACCEL_DEV_NAME },
>> +       { LSM303DLM_ACCEL_DEV_NAME },
>> +       { LSM330_ACCEL_DEV_NAME },
>> +       {},
>> +};
>> +MODULE_DEVICE_TABLE(spi, st_accel_id_table);
>> +
>> +static struct spi_driver st_accel_driver = {
>> +       .driver = {
>> +               .owner = THIS_MODULE,
>> +               .name = "st-accel-spi",
>> +       },
>> +       .probe = st_accel_spi_probe,
>> +       .remove = __devexit_p(st_accel_spi_remove),
>> +       .id_table = st_accel_id_table,
>> +};
>> +module_spi_driver(st_accel_driver);
>> +
>> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/accel/st_accel_trigger.c
>> b/drivers/iio/accel/st_accel_trigger.c
>> new file mode 100644
>> index 0000000..7b57b7f
>> --- /dev/null
>> +++ b/drivers/iio/accel/st_accel_trigger.c
>> @@ -0,0 +1,84 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/stat.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/i2c.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +
>> +#include <linux/iio/accel/st_accel.h>
>> +
>> +
>> +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
>> state)
>> +{
>> +       struct iio_dev *indio_dev = trig->private_data;
>> +       return st_accel_set_dataready_irq(indio_dev, state);
>> +}
>> +
>> +static const struct iio_trigger_ops st_accel_trigger_ops = {
>> +       .owner = THIS_MODULE,
>> +       .set_trigger_state = &st_accel_trig_acc_set_state,
>> +};
>> +
>> +int st_accel_probe_trigger(struct iio_dev *indio_dev)
>> +{
>> +       int err;
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
>> +       if (adata->trig == NULL) {
>> +               err = -ENOMEM;
>> +               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
>> +               goto iio_trigger_alloc_error;
>> +       }
>> +
>> +       err = request_threaded_irq(*adata->irq_data_ready,
>> +                       iio_trigger_generic_data_rdy_poll,
>> +                       NULL,
>> +                       IRQF_TRIGGER_RISING,
>> +                       adata->trig->name,
>> +                       adata->trig);
>> +       if (err)
>> +               goto request_irq_error;
>> +
>> +       adata->trig->private_data = indio_dev;
>> +       adata->trig->ops = &st_accel_trigger_ops;
>> +       adata->trig->dev.parent = adata->dev;
>> +
>> +       err = iio_trigger_register(adata->trig);
>> +       if (err < 0) {
>> +               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
>> +               goto iio_trigger_register_error;
>> +       }
>> +       indio_dev->trig = adata->trig;
>> +
>> +       return 0;
>> +
>> +iio_trigger_register_error:
>> +       free_irq(*adata->irq_data_ready, adata->trig);
>> +request_irq_error:
>> +       iio_trigger_free(adata->trig);
>> +iio_trigger_alloc_error:
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(st_accel_probe_trigger);
>> +
>> +void st_accel_remove_trigger(struct iio_dev *indio_dev)
>> +{
>> +       struct st_accel_data *adata = iio_priv(indio_dev);
>> +
>> +       iio_trigger_unregister(adata->trig);
>> +       free_irq(*adata->irq_data_ready, adata->trig);
>> +       iio_trigger_free(adata->trig);
>> +}
>> +EXPORT_SYMBOL(st_accel_remove_trigger);
>> diff --git a/include/linux/iio/accel/st_accel.h
>> b/include/linux/iio/accel/st_accel.h
>> new file mode 100644
>> index 0000000..1386488
>> --- /dev/null
>> +++ b/include/linux/iio/accel/st_accel.h
>> @@ -0,0 +1,119 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + * v. 1.0.0
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +/*
>> + * Supported sensors:
>> + * LSM303DLH
>> + * LSM303DLHC
>> + * LIS3DH
>> + * LSM330D
>> + * LSM330DL
>> + * LSM330DLC
>> + * LSM303D
>> + * LSM9DS0
>> + * LIS331DLH
>> + * LSM303DL
>> + * LSM303DLM
>> + * LSM330
>> + */
>> +
>> +
>> +#ifndef ST_ACCEL_H
>> +#define ST_ACCEL_H
>> +
>> +#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
>> +#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
>> +#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
>> +#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
>> +#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
>> +#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
>> +#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
>> +#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
>> +#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
>> +#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
>> +#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
>> +#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
>> +
>> +#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
>> +#define ST_ACCEL_BYTE_FOR_CHANNEL      2
>> +#define ST_ACCEL_SCAN_X                        0
>> +#define ST_ACCEL_SCAN_Y                        1
>> +#define ST_ACCEL_SCAN_Z                        2
>> +
>> +/**
>> + * struct st_accel_data - ST accel device status
>> + * @dev: Pointer to instance of struct device (I2C or SPI).
>> + * @name: Name of the sensor in use.
>> + * @enabled: Status of the sensor (0->off, 1->on).
>> + * @index: Number used to point the sensor being used in the
>> + *     st_accel_sensors struct.
>> + * @fullscale: Maximum range of measure by the sensor.
>> + * @gain: Sensitivity of the sensor [ug/LSB].
>> + * @odr: Output data rate of the sensor.
>> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
>> + * @read_byte: Function used to read one byte.
>> + * @write_byte: Function used to write one byte.
>> + * @read_multiple_byte: Function used to read multiple byte.
>> + * @set_trigger_parent: Function used to set the trigger parent.
>> + * @trig: The trigger in use by the core driver.
>> + * @irq_data_ready: IRQ number for data ready on INT1 pin.
>> + * @slock: mutex for read and write operation.
>> + */
>> +
>> +struct st_accel_data {
>> +       struct device *dev;
>> +       bool enabled;
>> +       short index;
>> +
>> +       unsigned int fullscale;
>> +       unsigned int gain;
>> +       unsigned int odr;
>> +
>> +       bool multiread_bit;
>> +       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
>> +                                                               u8 *res_byte);
>> +       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
>> +       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
>> +                                                       int len, u8 *data);
>> +
>> +       struct iio_trigger *trig;
>> +       int *irq_data_ready;
>> +       struct mutex slock;
>> +};
>> +
>> +int st_accel_iio_probe(struct iio_dev *indio_dev);
>> +void st_accel_iio_remove(struct iio_dev *indio_dev);
>> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
>> +
>> +#ifdef CONFIG_IIO_BUFFER
>> +int st_accel_probe_trigger(struct iio_dev *indio_dev);
>> +void st_accel_remove_trigger(struct iio_dev *indio_dev);
>> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
>> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
>> +#else /* CONFIG_IIO_BUFFER */
>> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev)
>> +{
>> +       return 0;
>> +}
>> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev)
>> +{
>> +       return;
>> +}
>> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
>> +{
>> +       return 0;
>> +}
>> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
>> +{
>> +       return;
>> +}
>> +#endif /* CONFIG_IIO_BUFFER */
>> +
>> +#endif /* ST_ACCEL_H */
>> diff --git a/include/linux/platform_data/st_accel_pdata.h
>> b/include/linux/platform_data/st_accel_pdata.h
>> new file mode 100644
>> index 0000000..416489b
>> --- /dev/null
>> +++ b/include/linux/platform_data/st_accel_pdata.h
>> @@ -0,0 +1,27 @@
>> +/*
>> + * STMicroelectronics accelerometers driver
>> + *
>> + * Copyright 2012 STMicroelectronics Inc.
>> + *
>> + * Denis Ciocca <denis.ciocca@st.com>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +
>> +#ifndef ST_ACCEL_PDATA_H
>> +#define ST_ACCEL_PDATA_H
>> +
>> +
>> +/**
>> + * struct st_accel_platform_data - ST accel device platform data
>> + * @fullscale: Value of fullscale used for the sensor.
>> + * @sampling_frequency: Value of sampling frequency used for the sensor.
>> + */
>> +
>> +struct st_accel_platform_data {
>> +       int fullscale;
>> +       int sampling_frequency;
>> +};
>> +
>> +#endif /* ST_ACCEL_PDATA_H */
>> --
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-22 18:07               ` Jonathan Cameron
  2012-10-22 19:37                 ` Denis Ciocca
@ 2012-10-24 12:44                 ` Denis CIOCCA
  2012-10-26 12:10                   ` Lars-Peter Clausen
  1 sibling, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-24 12:44 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lars-Peter Clausen, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan,


> For the non buffered case - reading is slow anyway so if anyone cares
> they will be doing buffered reads.  For buffered reads this isn't going
> to change often (and there is a lot of cost associated with bringing the =
buffer
> up and down anyway) so a little cost in disabling the channel is minor
> compared to the cost of hammering the bus unecessarily - particularly wit=
h
> good old slow i2c.
>
> So I'd be inclined to turn on only channels we care about.  Do you have
> an estimate of how long it will take to turn one on for a single read?

I have checked what you tell me about power on/off of the axis. For the
buffered case I have modify the driver to power on and off the single
axis. What you think about my driver now? It is necessary other modify?
Thanks

Denis


(If you want I send this patch by git because I don't know if this is
the better way)



 From b34e061a37655da5ab9bacde4d9a38bb9d3cf69f Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 1/2] iio:accel: Add STMicroelectronics accelerometers drive=
r

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  drivers/iio/accel/Kconfig                    |   37 +
  drivers/iio/accel/Makefile                   |    6 +
  drivers/iio/accel/st_accel_buffer.c          |  166 ++++
  drivers/iio/accel/st_accel_core.c            | 1342
++++++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c             |  139 +++
  drivers/iio/accel/st_accel_spi.c             |  199 ++++
  drivers/iio/accel/st_accel_trigger.c         |   84 ++
  include/linux/iio/accel/st_accel.h           |  122 +++
  include/linux/platform_data/st_accel_pdata.h |   27 +
  9 files changed, 2122 insertions(+), 0 deletions(-)
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h
  create mode 100644 include/linux/platform_data/st_accel_pdata.h

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..600ecc6
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,166 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+
+static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+       int len =3D 0, i, n =3D 0;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, rx_array=
);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       rx_array);
+               } else {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       rx_array[2] =3D rx_array[4];
+                       rx_array[3] =3D rx_array[5];
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       rx_array);
+               break;
+       default:
+               break;
+       }
+
+       return len;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int ret, i, scan_count;
+       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS=
];
+       s16 *data =3D (s16 *)buf;
+
+       ret =3D st_accel_read_all(indio_dev, rx_array);
+       if (ret < 0)
+               return ret;
+
+       scan_count =3D bitmap_weight(indio_dev->active_scan_mask,
+                                                       indio_dev->maskleng=
th);
+
+       for (i =3D 0; i < scan_count; i++)
+               data[i] =3D (s16)(((s16)(rx_array[2*i+1]) << 8) | rx_array[=
2*i]);
+
+       return i*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       int len =3D 0;
+       char *data;
+
+       data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (data =3D=3D NULL)
+               goto done;
+       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklengt=
h))
+               len =3D st_accel_get_buffer_element(indio_dev, data);
+       else
+               goto done;
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =3D pf->time=
stamp;
+       iio_push_to_buffer(indio_dev->buffer, data);
+       kfree(data);
+
+done:
+       iio_trigger_notify_done(indio_dev->trig);
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       indio_dev->scan_timestamp =3D true;
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..97ea1b7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1342 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/platform_data/st_accel_pdata.h>
+
+
+#define UG_TO_MS2(x)                           (x*9800)
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ON                            1
+#define ST_ACCEL_OFF                           0
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* ODR */
+#define ST_ACCEL_ODR_AVL_1HZ                   1
+#define ST_ACCEL_ODR_AVL_3HZ                   3
+#define ST_ACCEL_ODR_AVL_6HZ                   6
+#define ST_ACCEL_ODR_AVL_10HZ                  10
+#define ST_ACCEL_ODR_AVL_12HZ                  12
+#define ST_ACCEL_ODR_AVL_25HZ                  25
+#define ST_ACCEL_ODR_AVL_50HZ                  50
+#define ST_ACCEL_ODR_AVL_100HZ                 100
+#define ST_ACCEL_ODR_AVL_200HZ                 200
+#define ST_ACCEL_ODR_AVL_400HZ                 400
+#define ST_ACCEL_ODR_AVL_800HZ                 800
+#define ST_ACCEL_ODR_AVL_1000HZ                        1000
+#define ST_ACCEL_ODR_AVL_1600HZ                        1600
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               1000
+#define ST_ACCEL_1_FS_AVL_4_GAIN               2000
+#define ST_ACCEL_1_FS_AVL_8_GAIN               4000
+#define ST_ACCEL_1_FS_AVL_16_GAIN              12000
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               61
+#define ST_ACCEL_2_FS_AVL_4_GAIN               122
+#define ST_ACCEL_2_FS_AVL_6_GAIN               183
+#define ST_ACCEL_2_FS_AVL_8_GAIN               244
+#define ST_ACCEL_2_FS_AVL_16_GAIN              732
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_PW_VALUE_ON                 0x01
+#define ST_ACCEL_3_PW_VALUE_OFF                        0x00
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               1000
+#define ST_ACCEL_3_FS_AVL_4_GAIN               2000
+#define ST_ACCEL_3_FS_AVL_8_GAIN               3900
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               61
+#define ST_ACCEL_4_FS_AVL_4_GAIN               122
+#define ST_ACCEL_4_FS_AVL_6_GAIN               183
+#define ST_ACCEL_4_FS_AVL_8_GAIN               244
+#define ST_ACCEL_4_FS_AVL_16_GAIN              732
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_accel_platform_data st_accel_default_pdata =3D {
+       .fullscale =3D ST_ACCEL_FS_AVL_2G,
+       .sampling_frequency =3D ST_ACCEL_ODR_AVL_100HZ,
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @ multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ *
+ */
+
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_1HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_10HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_10HZ_=
VAL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+               },
+               {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_3HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_6HZ_V=
AL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_12HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [8] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_800HZ=
_VAL,
+                               },
+                               [9] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+               },
+               {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1000HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_1000H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_3_PW_VALUE_ON,
+                       .value_off =3D ST_ACCEL_3_PW_VALUE_OFF,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+               },
+               {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_3HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_6HZ_V=
AL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_12HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [8] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_800HZ=
_VAL,
+                               },
+                               [9] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+               },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err, j, pos;
+       u8 prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       pos =3D 8 - num_bit;
+       for (j =3D 128; j >=3D 0; j =3D j/2) {
+               if (mask / j > 0)
+                       break;
+               else
+                       pos--;
+       }
+
+       err =3D adata->read_byte(adata, reg_addr, &prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((prev_data & (~mask)) | ((data << pos) & mask));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -1;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -1;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+       if (err < 0)
+               goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, u8 value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D (bool)ST_ACCEL_ON) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+                       if (err < 0)
+                               goto sc_accel_set_odr_error;
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               if (err < 0)
+                       goto sc_accel_set_odr_error;
+       }
+
+sc_accel_set_odr_error:
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, int enable)
+{
+       int err;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (enable) {
+       case ST_ACCEL_ON:
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D (bool)ST_ACCEL_ON;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+               break;
+       case ST_ACCEL_OFF:
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D (bool)ST_ACCEL_OFF;
+               break;
+       default:
+               err =3D -EINVAL;
+               goto set_enable_error;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto read_error;
+                       } else {
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)(((s16)(outdata[1]) << 8)
+                                       | outdata[0])) >> ch->scan_type.shi=
ft;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D UG_TO_MS2(adata->gain);
+               return IIO_VAL_INT_PLUS_NANO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       bool found;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       found =3D false;
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai) {
+                       found =3D true;
+                       break;
+               }
+       }
+       if (!found)
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set sampling frequency to %d.\n", freq);
+               goto st_accel_sysfs_set_sampling_frequency_error;
+       }
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_enable(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool en;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &en);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, (int)en);
+       if (err < 0)
+               dev_err(&indio_dev->dev,
+                               "failed to set enable to %d.\n", (int)en);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_enable(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)adata->enabled);
+}
+
+static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->fullscale);
+}
+
+static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int fs;
+       struct st_accel_fullscale_available fs_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &fs);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_=
out);
+       if (err < 0)
+               goto match_fullscale_error;
+
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set new fullscale. (errn %d).\n", err);
+       }
+
+match_fullscale_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "%d ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].num);
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       len--;
+       len +=3D sprintf(buf+len, "\n");
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf+len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+
+       len--;
+       len +=3D sprintf(buf+len, "\n");
+       return len;
+}
+
+/**
+ * IIO_DEVICE_ATTR - sampling_frequency_available
+ * @read: show all frequency available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale_available
+ * @read: show all fullscale available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+               st_accel_sysfs_fullscale_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale
+ * @read: show the current fullscale of the sensor.
+ * @write: store the current fullscale of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale =
, 0);
+
+/**
+ * IIO_DEVICE_ATTR - enable
+ * @read: show the current status of the sensor.
+ * @write: power on/off the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
st_accel_sysfs_get_enable,
+               st_accel_sysfs_set_enable , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_fullscale_available.dev_attr.attr,
+       &iio_dev_attr_fullscale.dev_attr.attr,
+       &iio_dev_attr_enable.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                       &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_O=
N);
+       if (err < 0)
+               goto init_error;
+
+       return 0;
+
+init_error:
+       dev_err(&indio_dev->dev, "failed to init sensor data.");
+       return -EIO;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev)
+{
+       int err;
+       u8 wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+       struct st_accel_platform_data *pdata;
+
+       mutex_init(&adata->slock);
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai)=
;
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+       pdata =3D adata->dev->platform_data;
+       if (pdata =3D=3D NULL) {
+               adata->fullscale =3D st_accel_default_pdata.fullscale;
+               adata->odr =3D st_accel_default_pdata.sampling_frequency;
+       } else {
+               adata->fullscale =3D pdata->fullscale;
+               adata->odr =3D pdata->sampling_frequency;
+       }
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (*adata->irq_data_ready > 0) {
+               err =3D st_accel_probe_trigger(indio_dev);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..7cfeb4c
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,139 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+       *res_byte =3D err & 0xff;
+       return err;
+
+st_accel_i2c_read_byte_error:
+       return -EIO;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       int err;
+
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       err =3D i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+       if (err < 0)
+               goto st_accel_i2c_read_multiple_byte_error;
+
+       return err;
+
+st_accel_i2c_read_multiple_byte_error:
+       return -EIO;
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+       adata->irq_data_ready =3D &client->irq;
+
+       err =3D st_accel_iio_probe(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+
+       st_accel_iio_remove(indio_dev);
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D __devexit_p(st_accel_i2c_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..40279bd
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,199 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D res_byte,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx =3D reg_addr | ACC_SPI_READ;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+       if (err)
+               goto acc_spi_read_byte_error;
+
+       return err;
+
+acc_spi_read_byte_error:
+       return -EIO;
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       if (adata->multiread_bit =3D=3D true)
+               tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx =3D reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+       if (err)
+               goto acc_spi_read_multiple_byte_error;
+       return len;
+
+acc_spi_read_multiple_byte_error:
+       return -EIO;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2];
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 2,
+               }
+       };
+
+       mutex_lock(&adata->slock);
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *client)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       spi_set_drvdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+       adata->irq_data_ready =3D &client->irq;
+
+       err =3D st_accel_iio_probe(indio_dev);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+
+       st_accel_iio_remove(indio_dev);
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D __devexit_p(st_accel_spi_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..7b57b7f
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,84 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(*adata->irq_data_ready,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(*adata->irq_data_ready, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(*adata->irq_data_ready, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..557d8cf
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,122 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Supported sensors:
+ * LSM303DLH
+ * LSM303DLHC
+ * LIS3DH
+ * LSM330D
+ * LSM330DL
+ * LSM330DLC
+ * LSM303D
+ * LSM9DS0
+ * LIS331DLH
+ * LSM303DL
+ * LSM303DLM
+ * LSM330
+ *
+ */
+
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @name: Name of the sensor in use.
+ * @enabled: Status of the sensor (0->off, 1->on).
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [ug/LSB].
+ * @odr: Output data rate of the sensor.
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ * @trig: The trigger in use by the core driver.
+ * @irq_data_ready: IRQ number for data ready on INT1 pin.
+ * @slock: mutex for read and write operation.
+ *
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       bool enabled;
+       short index;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       bool multiread_bit;
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+
+       struct iio_trigger *trig;
+       int *irq_data_ready;
+       struct mutex slock;
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev);
+void st_accel_iio_remove(struct iio_dev *indio_dev);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev);
+void st_accel_remove_trigger(struct iio_dev *indio_dev);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
diff --git a/include/linux/platform_data/st_accel_pdata.h
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
--- /dev/null
+++ b/include/linux/platform_data/st_accel_pdata.h
@@ -0,0 +1,27 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+
+#ifndef ST_ACCEL_PDATA_H
+#define ST_ACCEL_PDATA_H
+
+
+/**
+ * struct st_accel_platform_data - ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */
+
+struct st_accel_platform_data {
+       int fullscale;
+       int sampling_frequency;
+};
+
+#endif /* ST_ACCEL_PDATA_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-24 12:44                 ` Denis CIOCCA
@ 2012-10-26 12:10                   ` Lars-Peter Clausen
  2012-10-29  8:55                     ` Denis CIOCCA
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-26 12:10 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/24/2012 02:44 PM, Denis CIOCCA wrote:
> Hi Jonathan,
> 
> 
>> For the non buffered case - reading is slow anyway so if anyone cares
>> they will be doing buffered reads.  For buffered reads this isn't going
>> to change often (and there is a lot of cost associated with bringing the buffer
>> up and down anyway) so a little cost in disabling the channel is minor
>> compared to the cost of hammering the bus unecessarily - particularly with
>> good old slow i2c.
>>
>> So I'd be inclined to turn on only channels we care about.  Do you have
>> an estimate of how long it will take to turn one on for a single read?
> 
> I have checked what you tell me about power on/off of the axis. For the
> buffered case I have modify the driver to power on and off the single
> axis. What you think about my driver now? It is necessary other modify?

We'll need at least another revision, since you did not address all comments
from my last review. But I guess 2-3 more revision are more realistic.

> Thanks
> 
> Denis
> 
> 
> (If you want I send this patch by git because I don't know if this is
> the better way)

For the final version it's better to use git send-email. Your mailclient for
example seems to replace tabs with spaces and also insert a extra newline
occasionally. This is not nice, but, well, does not really impact review.

> 
> 
> 
>  From b34e061a37655da5ab9bacde4d9a38bb9d3cf69f Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH 1/2] iio:accel: Add STMicroelectronics accelerometers driver
> 
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>   drivers/iio/accel/Kconfig                    |   37 +
>   drivers/iio/accel/Makefile                   |    6 +
>   drivers/iio/accel/st_accel_buffer.c          |  166 ++++
>   drivers/iio/accel/st_accel_core.c            | 1342
> ++++++++++++++++++++++++++
>   drivers/iio/accel/st_accel_i2c.c             |  139 +++
>   drivers/iio/accel/st_accel_spi.c             |  199 ++++
>   drivers/iio/accel/st_accel_trigger.c         |   84 ++
>   include/linux/iio/accel/st_accel.h           |  122 +++
>   include/linux/platform_data/st_accel_pdata.h |   27 +
>   9 files changed, 2122 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/iio/accel/st_accel_buffer.c
>   create mode 100644 drivers/iio/accel/st_accel_core.c
>   create mode 100644 drivers/iio/accel/st_accel_i2c.c
>   create mode 100644 drivers/iio/accel/st_accel_spi.c
>   create mode 100644 drivers/iio/accel/st_accel_trigger.c
>   create mode 100644 include/linux/iio/accel/st_accel.h
>   create mode 100644 include/linux/platform_data/st_accel_pdata.h
> 
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..d65e66a 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>           Say yes here to build support for the HID SENSOR
>           accelerometers 3D.
> 
> +config ST_ACCEL_3AXIS
> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +       depends on (I2C || SPI) && SYSFS
> +       help
> +         Say yes here to build support for STMicroelectronics accelerometers:
> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_accel.
> +
> +config ST_ACCEL_3AXIS_I2C
> +       tristate "support I2C bus connection"
> +       depends on ST_ACCEL_3AXIS && I2C
> +       help
> +         Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_i2c.
> +
> +config ST_ACCEL_3AXIS_SPI
> +       tristate "support SPI bus connection"
> +       depends on ST_ACCEL_3AXIS && SPI_MASTER
> +       help
> +         Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_spi.
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +       tristate "support triggered buffer"
> +       depends on ST_ACCEL_3AXIS
> +       select IIO_TRIGGERED_BUFFER
> +       select IIO_BUFFER
> +       help
> +         Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>   endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..1541236 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>   #
> 
>   obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +st_accel-y := st_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
> st_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..600ecc6
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_buffer.c
> @@ -0,0 +1,166 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
> +
> +
> +static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +       int len = 0, i, n = 0;
> +       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
> +               if (test_bit(i, indio_dev->active_scan_mask)) {
> +                       reg_addr[n] = indio_dev->channels[i].address;
> +                       n++;
> +               }
> +       }
> +       switch (n) {
> +       case 1:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_ACCEL_BYTE_FOR_CHANNEL, rx_array);
> +               break;
> +       case 2:
> +               if ((reg_addr[1] - reg_addr[0]) == ST_ACCEL_BYTE_FOR_CHANNEL) {
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
> +                                       rx_array);
> +               } else {
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                               ST_ACCEL_BYTE_FOR_CHANNEL*
> +                               ST_ACCEL_NUMBER_DATA_CHANNELS,
> +                               rx_array);
> +                       rx_array[2] = rx_array[4];
> +                       rx_array[3] = rx_array[5];
> +                       len = ST_ACCEL_BYTE_FOR_CHANNEL*n;
> +               }
> +               break;
> +       case 3:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
> +                       rx_array);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return len;
> +}
> +
> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, scan_count;
> +       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       s16 *data = (s16 *)buf;
> +
> +       ret = st_accel_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +
> +       scan_count = bitmap_weight(indio_dev->active_scan_mask,
> +                                                       indio_dev->masklength);
> +
> +       for (i = 0; i < scan_count; i++)
> +               data[i] = (s16)(((s16)(rx_array[2*i+1]) << 8) | rx_array[2*i]);

There is no need to perform endian conversion

> +
> +       return i*sizeof(data[0]);
> +}
> +
> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       int len = 0;
> +       char *data;
> +
> +       data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +       if (data == NULL)
> +               goto done;

You could preallocate data in your postenable callback, so you don't have to
do this each time the trigger handler is called.

> +       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))

The bitmap will never be empty at this point

> +               len = st_accel_get_buffer_element(indio_dev, data);
> +       else
> +               goto done;
> +       if (indio_dev->scan_timestamp)
> +               *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp;
> +       iio_push_to_buffer(indio_dev->buffer, data);
> +       kfree(data);
> +
> +done:
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}
> +
> +static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int err, i;
> +       u8 active_bit = 0x00;
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
> +               if (test_bit(i, indio_dev->active_scan_mask))
> +                       active_bit |= (1 << i);
> +
> +       err = st_accel_set_axis_enable(indio_dev, active_bit);
> +       if (err < 0)
> +               goto st_accel_buffer_postenable_error;
> +
> +       err = iio_triggered_buffer_postenable(indio_dev);
> +
> +st_accel_buffer_postenable_error:
> +       return err;
> +}
> +
> +static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int err;
> +
> +       err = iio_triggered_buffer_predisable(indio_dev);
> +       if (err < 0)
> +               goto st_accel_buffer_predisable_error;
> +
> +       err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +
> +st_accel_buffer_predisable_error:
> +       return err;
> +}
> +
> +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
> +       .preenable = &iio_sw_buffer_preenable,
> +       .postenable = &st_accel_buffer_postenable,
> +       .predisable = &st_accel_buffer_predisable,
> +};
> +
> +int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       indio_dev->scan_timestamp = true;
> +       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +                       &st_accel_trigger_handler, &st_accel_buffer_setup_ops);
> +}
> +EXPORT_SYMBOL(st_accel_allocate_ring);
> +
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_deallocate_ring);
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..97ea1b7
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1342 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +#include <linux/platform_data/st_accel_pdata.h>
> +
[...]

> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
> reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err, j, pos;
> +       u8 prev_data;
> +       u8 new_data;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       pos = 8 - num_bit;
> +       for (j = 128; j >= 0; j = j/2) {
> +               if (mask / j > 0)
> +                       break;
> +               else
> +                       pos--;
> +       }

is this pos = __ffs(mask)? Or can't you just pass in pos directly instead of
num_bit? I think that would make the code much more straight forward.

> +
> +       err = adata->read_byte(adata, reg_addr, &prev_data);
> +       if (err < 0)
> +               goto st_accel_write_data_with_mask_error;
> +
> +       new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
> +       err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_accel_write_data_with_mask_error:
> +       return err;
> +}
> +
> +static int st_accel_match_odr(const struct st_accel_sensors *sensor,
> +               unsigned int odr, struct st_accel_odr_available *odr_out)
> +{
> +       int i, ret = -1;

Since you pass it on to userspace this needs to be a proper error code, e.g.
EINVAL

> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
> +               if (sensor->odr.odr_avl[i].hz == odr) {
> +                       odr_out->hz = sensor->odr.odr_avl[i].hz;
> +                       odr_out->value = sensor->odr.odr_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int st_accel_match_fs(const struct st_accel_sensors *sensor,
> +               unsigned int fs, struct st_accel_fullscale_available *fs_out)
> +{
> +       int i, ret = -1;

Same here

> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].num == fs) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
[...]
> +
> +static int st_accel_set_enable(struct iio_dev *indio_dev, int enable)

I'd just let this take a bool instead of an int, lets you avoid some casts.
ST_ACCEL_{ON,OFF} is kind of your custom bool, just use true and false instead

> +{
[...]
> +
> +static int st_accel_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&indio_dev->mlock);
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +                       goto read_error;
> +               } else {
> +                       if (!adata->enabled) {
> +                               err = -EIO;
> +                               goto read_error;
> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               *val = ((s16)(((s16)(outdata[1]) << 8)
> +                                       | outdata[0])) >> ch->scan_type.shift;
> +                       }
> +               }
> +               mutex_unlock(&indio_dev->mlock);
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = UG_TO_MS2(adata->gain);

There is no a generic macro for G to MS2 in iio called IIO_G_TO_M_S_2. I
think you can also do the conversion of G to m/s**2 at compile time when you
initilize the gain attributes of the st_accel_fullscale strutcs

> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       mutex_unlock(&indio_dev->mlock);
> +       return err;
> +}
> +
> +static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
> +{
> +       int i;
> +       bool found;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       found = false;
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
> +               if (st_accel_sensors[i].wai == wai) {
> +                       found = true;
> +                       break;
> +               }
> +       }
> +       if (!found)

You can just do 'i != ARRAY_SIZE(st_accel_sensors)' instead of 'found'.

> +               goto check_device_error;
> +
> +       adata->index = i;
> +
> +       return i;
> +
> +check_device_error:
> +       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
> +       return -ENODEV;
> +}
[...]
[...]
> +static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->fullscale);
> +}
> +
> +static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int fs;
> +       struct st_accel_fullscale_available fs_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &fs);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_out);
> +       if (err < 0)
> +               goto match_fullscale_error;
> +
> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev,
> +                       "failed to set new fullscale. (errn %d).\n", err);
> +       }
> +
> +match_fullscale_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return size;
> +}

I don't think you need the fullscale attribute. This is just the same as the
scale attribute just in a different representation, as far as I can see.

> +
> +static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "%d ",
> +                       st_accel_sensors[adata->index].fs.fs_avl[i].num);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       len--;
> +       len += sprintf(buf+len, "\n");

just buf[len-1] = '\n'; would be simpler

> +       return len;
> +}
> +
> +static ssize_t st_accel_sysfs_sampling_frequency_available(struct
> device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "%d ",
> +                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       len--;
> +       len += sprintf(buf+len, "\n");

just buf[len-1] = '\n'; would be simpler

> +       return len;
> +}
> +
> +/**
> + * IIO_DEVICE_ATTR - sampling_frequency_available
> + * @read: show all frequency available of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +               st_accel_sysfs_sampling_frequency_available, NULL , 0);
> +
> +/**
> + * IIO_DEVICE_ATTR - fullscale_available
> + * @read: show all fullscale available of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
> +               st_accel_sysfs_fullscale_available, NULL , 0);
> +
> +/**
> + * IIO_DEVICE_ATTR - fullscale
> + * @read: show the current fullscale of the sensor.
> + * @write: store the current fullscale of the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
> +               st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0);
> +

You need to document the non-standard sysfs files in a text document in
Documentation/ABI/testing/sysfs-bus-iio-accel-st. See the other files in
that folder for an example

> +/**
> + * IIO_DEVICE_ATTR - enable
> + * @read: show the current status of the sensor.
> + * @write: power on/off the sensor.
> + *
> + */
> +
> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
> st_accel_sysfs_get_enable,
> +               st_accel_sysfs_set_enable , 0);
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +                       st_accel_sysfs_get_sampling_frequency,
> +                                       st_accel_sysfs_set_sampling_frequency);
> +

I still don't get why you need this. Can't you just power-up and down the
device on demand?
> +
[...]
> +int st_accel_iio_probe(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       u8 wai;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +       struct st_accel_platform_data *pdata;
> +
> +       mutex_init(&adata->slock);
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->info = &acc_info;
> +
> +       err = st_accel_get_wai_device(indio_dev,
> +                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_check_device_list(indio_dev, wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
> +       indio_dev->channels = st_accel_sensors[adata->index].ch;
> +       indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
> +       pdata = adata->dev->platform_data;

How about if (!pdata)
		pdata = &st_accel_default_pdata;

> +       if (pdata == NULL) {
> +               adata->fullscale = st_accel_default_pdata.fullscale;
> +               adata->odr = st_accel_default_pdata.sampling_frequency;
> +       } else {
> +               adata->fullscale = pdata->fullscale;
> +               adata->odr = pdata->sampling_frequency;
> +       }
> +
> +       err = st_accel_init_sensor(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       if (*adata->irq_data_ready > 0) {
> +               err = st_accel_probe_trigger(indio_dev);
> +               if (err < 0)
> +                       goto acc_probe_trigger_error;
> +       }
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       return err;
> +
> +iio_device_register_error:
> +       st_accel_remove_trigger(indio_dev);
> +acc_probe_trigger_error:
> +       st_accel_deallocate_ring(indio_dev);
> +st_accel_iio_probe_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_iio_probe);
> +
> +void st_accel_iio_remove(struct iio_dev *indio_dev)
> +{
> +       iio_device_unregister(indio_dev);
> +       st_accel_remove_trigger(indio_dev);
> +       st_accel_deallocate_ring(indio_dev);
> +       iio_device_free(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_i2c.c
> b/drivers/iio/accel/st_accel_i2c.c
> new file mode 100644
> index 0000000..7cfeb4c
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -0,0 +1,139 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_I2C_MULTIREAD                 0x80
> +
> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       int err;
> +
> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +       if (err < 0)
> +               goto st_accel_i2c_read_byte_error;
> +       *res_byte = err & 0xff;
> +       return err;

Shouldn't this be return 0?

> +
> +st_accel_i2c_read_byte_error:
> +       return -EIO;

Just return err

> +}
> +
> +static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       int err;
> +
> +       if (adata->multiread_bit == true)
> +               reg_addr |= ST_ACCEL_I2C_MULTIREAD;
> +
> +       err = i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +                                                       reg_addr, len, data);
> +       if (err < 0)
> +               goto st_accel_i2c_read_multiple_byte_error;
> +
> +       return err;
> +
> +st_accel_i2c_read_multiple_byte_error:
> +       return -EIO;

Just return err. Also makes the code much simpler since you don't need the
goto and could just do a  return i2c_smbus_read_i2c_block_data(...)

> +}
> +
[...]
> diff --git a/drivers/iio/accel/st_accel_spi.c
> b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..40279bd
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,199 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ           0x80;
> +#define ACC_SPI_MULTIREAD      0xc0
> +
> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = res_byte,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       tx = reg_addr | ACC_SPI_READ;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);

Why does the spi code require locking and the i2c does not? Everything here
seems to be on the stack, and the SPI bus has internal locking, so I don't
think it is necessary to take slock.

> +       if (err)
> +               goto acc_spi_read_byte_error;
> +
> +       return err;
> +
> +acc_spi_read_byte_error:
> +       return -EIO;

Just returne err.

> +}
> +
> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = data,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       if (adata->multiread_bit == true)
> +               tx = reg_addr | ACC_SPI_MULTIREAD;
> +       else
> +               tx = reg_addr | ACC_SPI_READ;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);

Same thing about locking as above

> +       if (err)
> +               goto acc_spi_read_multiple_byte_error;
> +       return len;
> +
> +acc_spi_read_multiple_byte_error:
> +       return -EIO;

Just return err

> +}
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx[2];
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = tx,
> +                       .bits_per_word = 8,
> +                       .len = 2,
> +               }
> +       };
> +
> +       mutex_lock(&adata->slock);
> +       tx[0] = reg_addr;
> +       tx[1] = data;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->slock);

Again, is the lock necessary?

> +
> +       return err;
> +}
> +
[...]
> diff --git a/drivers/iio/accel/st_accel_trigger.c
> b/drivers/iio/accel/st_accel_trigger.c
[...]
> diff --git a/include/linux/iio/accel/st_accel.h
> b/include/linux/iio/accel/st_accel.h
> new file mode 100644
> index 0000000..557d8cf
> --- /dev/null
> +++ b/include/linux/iio/accel/st_accel.h
> @@ -0,0 +1,122 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> +/*
> + * Supported sensors:
> + * LSM303DLH
> + * LSM303DLHC
> + * LIS3DH
> + * LSM330D
> + * LSM330DL
> + * LSM330DLC
> + * LSM303D
> + * LSM9DS0
> + * LIS331DLH
> + * LSM303DL
> + * LSM303DLM
> + * LSM330
> + *
> + */
> +
> +
> +#ifndef ST_ACCEL_H
> +#define ST_ACCEL_H
> +
> +#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
> +#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
> +#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
> +#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
> +#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
> +#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
> +#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
> +#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
> +#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
> +#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
> +#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
> +#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
> +
> +#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
> +#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
> +#define ST_ACCEL_BYTE_FOR_CHANNEL      2
> +#define ST_ACCEL_SCAN_X                        0
> +#define ST_ACCEL_SCAN_Y                        1
> +#define ST_ACCEL_SCAN_Z                        2
> +
> +/**
> + * struct st_accel_data - ST accel device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @name: Name of the sensor in use.
> + * @enabled: Status of the sensor (0->off, 1->on).
> + * @index: Number used to point the sensor being used in the
> + *     st_accel_sensors struct.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [ug/LSB].
> + * @odr: Output data rate of the sensor.
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + * @trig: The trigger in use by the core driver.
> + * @irq_data_ready: IRQ number for data ready on INT1 pin.
> + * @slock: mutex for read and write operation.
> + *
> + */
> +
> +struct st_accel_data {
> +       struct device *dev;
> +       bool enabled;
> +       short index;
> +
> +       unsigned int fullscale;
> +       unsigned int gain;
> +       unsigned int odr;
> +
> +       bool multiread_bit;
> +       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                               u8 *res_byte);
> +       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +
> +       struct iio_trigger *trig;
> +       int *irq_data_ready;

I still don't get why this has to be a pointer...

> +       struct mutex slock;
> +};
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev);
> +void st_accel_iio_remove(struct iio_dev *indio_dev);
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_accel_probe_trigger(struct iio_dev *indio_dev);
> +void st_accel_remove_trigger(struct iio_dev *indio_dev);
> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev)
> +{
> +       return;
> +}
> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       return;
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCEL_H */
> diff --git a/include/linux/platform_data/st_accel_pdata.h
> b/include/linux/platform_data/st_accel_pdata.h
> new file mode 100644
> index 0000000..416489b
> --- /dev/null
> +++ b/include/linux/platform_data/st_accel_pdata.h
> @@ -0,0 +1,27 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +
> +#ifndef ST_ACCEL_PDATA_H
> +#define ST_ACCEL_PDATA_H
> +
> +
> +/**
> + * struct st_accel_platform_data - ST accel device platform data
> + * @fullscale: Value of fullscale used for the sensor.
> + * @sampling_frequency: Value of sampling frequency used for the sensor.
> + */
> +
> +struct st_accel_platform_data {
> +       int fullscale;
> +       int sampling_frequency;
> +};
> +
> +#endif /* ST_ACCEL_PDATA_H */
> --
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-26 12:10                   ` Lars-Peter Clausen
@ 2012-10-29  8:55                     ` Denis CIOCCA
  2012-10-29  9:13                       ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-29  8:55 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Lars-Peter,

I have modified the source code to apply your code review, now I would 
tell you if I attach the only new patch or the full code, as you wish.

Also I have some question for you:


> There is no a generic macro for G to MS2 in iio called IIO_G_TO_M_S_2. I
> think you can also do the conversion of G to m/s**2 at compile time when you
> initilize the gain attributes of the st_accel_fullscale strutcs
>
I don't find IIO_G_TO_M_S_2 in the framework code, but I added this 
macro in my source code. It is exatly?


> I don't think you need the fullscale attribute. This is just the same as the
> scale attribute just in a different representation, as far as I can see.
>
I think this is more useful to user because if you want change the 
fullscale you can see immediately the current fullscale without apply 
conversion manually.


> I still don't get why you need this. Can't you just power-up and down the
> device on demand?
The boot time is too different from one sensor to another, this 
introduce much delay in the single read.


> I still don't get why this has to be a pointer...
I don't understand if your point is use a pointer or use a variable or 
it is not necessary.


Thanks and best regards

Denis

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-29  8:55                     ` Denis CIOCCA
@ 2012-10-29  9:13                       ` Lars-Peter Clausen
  2012-10-29 10:24                         ` Denis CIOCCA
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-29  9:13 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi,

On 10/29/2012 09:55 AM, Denis CIOCCA wrote:
> Hi Lars-Peter,
> 
> I have modified the source code to apply your code review, now I would 
> tell you if I attach the only new patch or the full code, as you wish.

There is no attachment attached to this mail.

> 
> Also I have some question for you:
> 
> 
>> There is no a generic macro for G to MS2 in iio called IIO_G_TO_M_S_2. I
>> think you can also do the conversion of G to m/s**2 at compile time when you
>> initilize the gain attributes of the st_accel_fullscale strutcs
>>
> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this 
> macro in my source code. It is exatly?

It's in the latest IIO tree and also in staging/staging-next. The definition is

+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
	/ 18000000ULL)

> 
> 
>> I don't think you need the fullscale attribute. This is just the same as the
>> scale attribute just in a different representation, as far as I can see.
>>
> I think this is more useful to user because if you want change the 
> fullscale you can see immediately the current fullscale without apply 
> conversion manually.
> 

I understand your reasoning and I also think that changing the scale factor
by entering the floating point lsb resolution is rather tedious and don't
like this. On the other hand having two attributes for the same information
and more importantly doing things different from every other driver in the
IIO framework is kind of a no-go. So we have two possible solutions, both
are far from optimal, but in my opinion the first one is the lesser of the
two evils.

And hopefully the user or end-user will not have to manually navigate sysfs
but will rather use some nice abstraction which hides these details.

> 
>> I still don't get why you need this. Can't you just power-up and down the
>> device on demand?
> The boot time is too different from one sensor to another, this 
> introduce much delay in the single read.

Ok, fair enough. But what power-up times are we talking about exactly. In
the order of ms or more in the order of up to seconds?

Secondly we have similar attributes for other drivers, primarily DACs. But
the attribute is called 'powerdown' instead of 'enable'. For consistency
within the IIO framework it would be good if you could use that name (and
its semantics) as well.

> 
> 
>> I still don't get why this has to be a pointer...
> I don't understand if your point is use a pointer or use a variable or 
> it is not necessary.
> 

A plain integer should be enough. But you could also get rid of it
completely since it is only used in the probe and remove callbacks, by
adding a parameter to these functions and pass client->irq from the I2C and
SPI probe and remove functions/

- Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-29  9:13                       ` Lars-Peter Clausen
@ 2012-10-29 10:24                         ` Denis CIOCCA
  2012-10-29 10:30                           ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-29 10:24 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan


> There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?


>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>> macro in my source code. It is exatly?
>
> It's in the latest IIO tree and also in staging/staging-next. The definition is
>
> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
> 	/ 18000000ULL)

Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?



>>> I don't think you need the fullscale attribute. This is just the same as the
>>> scale attribute just in a different representation, as far as I can see.
>>>
>> I think this is more useful to user because if you want change the
>> fullscale you can see immediately the current fullscale without apply
>> conversion manually.
>>
>
> I understand your reasoning and I also think that changing the scale factor
> by entering the floating point lsb resolution is rather tedious and don't
> like this. On the other hand having two attributes for the same information
> and more importantly doing things different from every other driver in the
> IIO framework is kind of a no-go. So we have two possible solutions, both
> are far from optimal, but in my opinion the first one is the lesser of the
> two evils.
>
> And hopefully the user or end-user will not have to manually navigate sysfs
> but will rather use some nice abstraction which hides these details.
I check other drivers for that and then do cross-check with you.


>>> I still don't get why you need this. Can't you just power-up and down the
>>> device on demand?
>> The boot time is too different from one sensor to another, this
>> introduce much delay in the single read.
>
> Ok, fair enough. But what power-up times are we talking about exactly. In
> the order of ms or more in the order of up to seconds?
The power-up time depends by ODR and by device. For example in some 
devices, you can lose the first 3-4 samples before it is ready, for most 
application this is too big.


> Secondly we have similar attributes for other drivers, primarily DACs. But
> the attribute is called 'powerdown' instead of 'enable'. For consistency
> within the IIO framework it would be good if you could use that name (and
> its semantics) as well.
Ok this is not a problem.


>>> I still don't get why this has to be a pointer...
>> I don't understand if your point is use a pointer or use a variable or
>> it is not necessary.
>>
>
> A plain integer should be enough. But you could also get rid of it
> completely since it is only used in the probe and remove callbacks, by
> adding a parameter to these functions and pass client->irq from the I2C and
> SPI probe and remove functions
Ok the second one is very good solution.



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-29 10:24                         ` Denis CIOCCA
@ 2012-10-29 10:30                           ` Lars-Peter Clausen
  2012-10-29 10:38                             ` Denis CIOCCA
  2012-10-31 14:27                             ` Denis CIOCCA
  0 siblings, 2 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-29 10:30 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/29/2012 11:24 AM, Denis CIOCCA wrote:
> 
>> There is no attachment attached to this mail.
> my was a question. You prefer I attach all source code or the last modified?
> 
> 
>>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>>> macro in my source code. It is exatly?
>>
>> It's in the latest IIO tree and also in staging/staging-next. The definition is
>>
>> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
>> 	/ 18000000ULL)
> 
> Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
> 

Sorry, too early in the morning, copied the wrong line.

+#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)

Btw. the full patch can be found here:
 http://comments.gmane.org/gmane.linux.kernel.iio/5777

- Lars


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-29 10:30                           ` Lars-Peter Clausen
@ 2012-10-29 10:38                             ` Denis CIOCCA
  2012-10-31 14:27                             ` Denis CIOCCA
  1 sibling, 0 replies; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-29 10:38 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/29/2012 11:30 AM, Lars-Peter Clausen wrote:
> On 10/29/2012 11:24 AM, Denis CIOCCA wrote:
>>
>>> There is no attachment attached to this mail.
>> my was a question. You prefer I attach all source code or the last modified?
>>
>>
>>>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>>>> macro in my source code. It is exatly?
>>>
>>> It's in the latest IIO tree and also in staging/staging-next. The definition is
>>>
>>> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
>>> 	/ 18000000ULL)
>>
>> Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
>>
>
> Sorry, too early in the morning, copied the wrong line.
>
> +#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>
> Btw. the full patch can be found here:
>   http://comments.gmane.org/gmane.linux.kernel.iio/5777
>
> - Lars
>

Ok thanks.
;)

Denis

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-29 10:30                           ` Lars-Peter Clausen
  2012-10-29 10:38                             ` Denis CIOCCA
@ 2012-10-31 14:27                             ` Denis CIOCCA
  2012-10-31 16:40                               ` Lars-Peter Clausen
  1 sibling, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-10-31 14:27 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Lars-Peter,

I'm trying modified the driver to change the fullscale attribute and use 
the in_accel_*_scale attribute.
My question is that:

- the in_accel_*_scale attribute rappresent the conversion value from 
raw data sensor to m/s2, if I use only this value, how can I know what 
is the maximum full scale associated to the value?

- how can I view a list of available values? I have to create scale 
attribute on sysfs? If the last point is true, it is so redundant what I 
did before?


Thanks,

Denis


On 10/29/2012 11:30 AM, Lars-Peter Clausen wrote:
> On 10/29/2012 11:24 AM, Denis CIOCCA wrote:
>>
>>> There is no attachment attached to this mail.
>> my was a question. You prefer I attach all source code or the last modified?
>>
>>
>>>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>>>> macro in my source code. It is exatly?
>>>
>>> It's in the latest IIO tree and also in staging/staging-next. The definition is
>>>
>>> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
>>> 	/ 18000000ULL)
>>
>> Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
>>
>
> Sorry, too early in the morning, copied the wrong line.
>
> +#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>
> Btw. the full patch can be found here:
>   http://comments.gmane.org/gmane.linux.kernel.iio/5777
>
> - Lars
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-31 14:27                             ` Denis CIOCCA
@ 2012-10-31 16:40                               ` Lars-Peter Clausen
  2012-10-31 20:33                                 ` Jonathan Cameron
  2012-11-04 10:09                                 ` Denis Ciocca
  0 siblings, 2 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-10-31 16:40 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi,

On 10/31/2012 03:27 PM, Denis CIOCCA wrote:
> Hi Lars-Peter,
> 
> I'm trying modified the driver to change the fullscale attribute and use 
> the in_accel_*_scale attribute.
> My question is that:
> 
> - the in_accel_*_scale attribute rappresent the conversion value from 
> raw data sensor to m/s2, if I use only this value, how can I know what 
> is the maximum full scale associated to the value?

Unfortunately there is currently no way in IIO to specify minimum or maximum
values for a given channel. But you are welcome to come up with one, I think
this will be useful for other drivers as well.

> 
> - how can I view a list of available values? I have to create scale 
> attribute on sysfs? If the last point is true, it is so redundant what I 
> did before?

You should add a scale_available attribute. Some ADC drivers implement this,
you could use them as an example.

- Lars

> 
> 
> Thanks,
> 
> Denis
> 
> 
> On 10/29/2012 11:30 AM, Lars-Peter Clausen wrote:
>> On 10/29/2012 11:24 AM, Denis CIOCCA wrote:
>>>
>>>> There is no attachment attached to this mail.
>>> my was a question. You prefer I attach all source code or the last modified?
>>>
>>>
>>>>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>>>>> macro in my source code. It is exatly?
>>>>
>>>> It's in the latest IIO tree and also in staging/staging-next. The definition is
>>>>
>>>> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
>>>> 	/ 18000000ULL)
>>>
>>> Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
>>>
>>
>> Sorry, too early in the morning, copied the wrong line.
>>
>> +#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>>
>> Btw. the full patch can be found here:
>>   http://comments.gmane.org/gmane.linux.kernel.iio/5777
>>
>> - Lars
>>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-31 16:40                               ` Lars-Peter Clausen
@ 2012-10-31 20:33                                 ` Jonathan Cameron
  2012-11-04 10:09                                 ` Denis Ciocca
  1 sibling, 0 replies; 42+ messages in thread
From: Jonathan Cameron @ 2012-10-31 20:33 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Denis CIOCCA, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 10/31/2012 04:40 PM, Lars-Peter Clausen wrote:
> Hi,
> 
> On 10/31/2012 03:27 PM, Denis CIOCCA wrote:
>> Hi Lars-Peter,
>>
>> I'm trying modified the driver to change the fullscale attribute and use 
>> the in_accel_*_scale attribute.
>> My question is that:
>>
>> - the in_accel_*_scale attribute rappresent the conversion value from 
>> raw data sensor to m/s2, if I use only this value, how can I know what 
>> is the maximum full scale associated to the value?
> 
> Unfortunately there is currently no way in IIO to specify minimum or maximum
> values for a given channel. But you are welcome to come up with one, I think
> this will be useful for other drivers as well.
My personal preference would be an additional (for now optional) callback
alongside read_raw and write_raw called something like available.  This would
be used by additional xxxxx_available attributes to describe the possible
values for info_mask element.  Exactly how to do this cleanly is a small
'implemention detail' which means this will probably be rather more
complex than this implies :)

Interdependent info_mask elements will be 'interesting' (say a scale
attribute for which the possible values change with the sampling frequency or
visa-versa).

I've been meaning to look at this for a while, but if anyone else fancies
giving it a go, feel free!
> 
>>
>> - how can I view a list of available values? I have to create scale 
>> attribute on sysfs? If the last point is true, it is so redundant what I 
>> did before?
> 
> You should add a scale_available attribute. Some ADC drivers implement this,
> you could use them as an example.
> 
> - Lars
> 
>>
>>
>> Thanks,
>>
>> Denis
>>
>>
>> On 10/29/2012 11:30 AM, Lars-Peter Clausen wrote:
>>> On 10/29/2012 11:24 AM, Denis CIOCCA wrote:
>>>>
>>>>> There is no attachment attached to this mail.
>>>> my was a question. You prefer I attach all source code or the last modified?
>>>>
>>>>
>>>>>> I don't find IIO_G_TO_M_S_2 in the framework code, but I added this
>>>>>> macro in my source code. It is exatly?
>>>>>
>>>>> It's in the latest IIO tree and also in staging/staging-next. The definition is
>>>>>
>>>>> +#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \
>>>>> 	/ 18000000ULL)
>>>>
>>>> Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
>>>>
>>>
>>> Sorry, too early in the morning, copied the wrong line.
>>>
>>> +#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
>>>
>>> Btw. the full patch can be found here:
>>>   http://comments.gmane.org/gmane.linux.kernel.iio/5777
>>>
>>> - Lars
>>>
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-10-31 16:40                               ` Lars-Peter Clausen
  2012-10-31 20:33                                 ` Jonathan Cameron
@ 2012-11-04 10:09                                 ` Denis Ciocca
  2012-11-05 21:28                                   ` Jonathan Cameron
  1 sibling, 1 reply; 42+ messages in thread
From: Denis Ciocca @ 2012-11-04 10:09 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Denis CIOCCA, Jonathan Cameron, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi everybody,

I have modify the driver in according to your previous arrangements. I
don't know if I use the best solution (I think not), but now I don't
have time to read and write code for the framework because my work
doesn't allow me.

Thanks

Denis


>From 348bcf19b2bb400bae0af5a5e444f772bb5c4f97 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 Documentation/ABI/testing/sysfs-bus-iio-accel-st |   26 +
 drivers/iio/accel/Kconfig                        |   37 +
 drivers/iio/accel/Makefile                       |    6 +
 drivers/iio/accel/st_accel_buffer.c              |  176 +++
 drivers/iio/accel/st_accel_core.c                | 1291 ++++++++++++++++++++++
 drivers/iio/accel/st_accel_i2c.c                 |  129 +++
 drivers/iio/accel/st_accel_spi.c                 |  188 ++++
 drivers/iio/accel/st_accel_trigger.c             |   84 ++
 include/linux/iio/accel/st_accel.h               |  121 ++
 include/linux/platform_data/st_accel_pdata.h     |   27 +
 10 files changed, 2085 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
 create mode 100644 drivers/iio/accel/st_accel_buffer.c
 create mode 100644 drivers/iio/accel/st_accel_core.c
 create mode 100644 drivers/iio/accel/st_accel_i2c.c
 create mode 100644 drivers/iio/accel/st_accel_spi.c
 create mode 100644 drivers/iio/accel/st_accel_trigger.c
 create mode 100644 include/linux/iio/accel/st_accel.h
 create mode 100644 include/linux/platform_data/st_accel_pdata.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..e86c1b4
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,26 @@
+What:		/sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion:	3.7.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns either '1' or '0'.
+		'1' means that the device in question is off.
+		'0' means that the devices in question is on.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sampling_frequency_available
+KernelVersion:	3.7.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns the sampling frequency available for the
+		device in question. This values are known in datasheet with
+		ODR [output data rate]. The units measurement is Hz.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
+KernelVersion:	3.7.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns the scale values available for the device in
+		question.
+		Writing one of this values to one of in_accel_*_scale attribute,
+		the full scale of the sensor in question change, the
+		value is related by this relation:
+		[in_accel_scale_available] = [sensitivity]*[conversion_to_ms^2].
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
 	  Say yes here to build support for the HID SENSOR
 	  accelerometers 3D.

+config ST_ACCEL_3AXIS
+	tristate "STMicroelectronics accelerometers 3-Axis Driver"
+	depends on (I2C || SPI) && SYSFS
+	help
+	  Say yes here to build support for STMicroelectronics accelerometers:
+	  LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
+	  LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+	tristate "support I2C bus connection"
+	depends on ST_ACCEL_3AXIS && I2C
+	help
+	  Say yes here to build I2C support for STMicroelectronics accelerometers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+	tristate "support SPI bus connection"
+	depends on ST_ACCEL_3AXIS && SPI_MASTER
+	help
+	  Say yes here to build SPI support for STMicroelectronics accelerometers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+	tristate "support triggered buffer"
+	depends on ST_ACCEL_3AXIS
+	select IIO_TRIGGERED_BUFFER
+	select IIO_BUFFER
+	help
+	  Default trigger and buffer for STMicroelectronics accelerometers driver.
+
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
 #

 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
+
+st_accel-y := st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..bcf0fe2
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,176 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS		0x07
+
+static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+	int len = 0, i, n = 0;
+	u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			reg_addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = adata->read_multiple_byte(adata, reg_addr[0],
+					ST_ACCEL_BYTE_FOR_CHANNEL, rx_array);
+		break;
+	case 2:
+		if ((reg_addr[1] - reg_addr[0]) == ST_ACCEL_BYTE_FOR_CHANNEL) {
+			len = adata->read_multiple_byte(adata, reg_addr[0],
+					ST_ACCEL_BYTE_FOR_CHANNEL*n,
+					rx_array);
+		} else {
+			len = adata->read_multiple_byte(adata, reg_addr[0],
+				ST_ACCEL_BYTE_FOR_CHANNEL*
+				ST_ACCEL_NUMBER_DATA_CHANNELS,
+				rx_array);
+			rx_array[2] = rx_array[4];
+			rx_array[3] = rx_array[5];
+			len = ST_ACCEL_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = adata->read_multiple_byte(adata, reg_addr[0],
+			ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
+			rx_array);
+		break;
+	default:
+		break;
+	}
+
+	return len;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int ret, i, scan_count;
+	u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
+	s16 *data = (s16 *)buf;
+
+	ret = st_accel_read_all(indio_dev, rx_array);
+	if (ret < 0)
+		return ret;
+
+	scan_count = bitmap_weight(indio_dev->active_scan_mask,
+							indio_dev->masklength);
+
+	for (i = 0; i < scan_count; i++)
+		data[i] = le16_to_cpup((__le16 *)&(rx_array[i*2]));
+
+	return i*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+	int len = 0;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	len = st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)adata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int err, i;
+	u8 active_bit = 0x00;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (adata->buffer_data == NULL) {
+		err = -ENOMEM;
+		goto allocate_memory_error;
+	}
+
+	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+		if (test_bit(i, indio_dev->active_scan_mask))
+			active_bit |= (1 << i);
+
+	err = st_accel_set_axis_enable(indio_dev, active_bit);
+	if (err < 0)
+		goto st_accel_buffer_postenable_error;
+
+	err = iio_triggered_buffer_postenable(indio_dev);
+
+	return err;
+
+allocate_memory_error:
+	kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+	return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+	if (err < 0)
+		goto st_accel_buffer_predisable_error;
+
+	err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
+	if (err < 0)
+		goto st_accel_buffer_predisable_error;
+
+	kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &st_accel_buffer_postenable,
+	.predisable = &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+	indio_dev->scan_timestamp = true;
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+			&st_accel_trigger_handler, &st_accel_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..9697a2b
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1291 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/platform_data/st_accel_pdata.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX		5
+#define ST_ACCEL_ODR_LIST_MAX			10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS		0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR		0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR		0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR		0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR		0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR		0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR		0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE		0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS		12
+#define ST_ACCEL_DEFAULT_16_REALBITS		16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR		0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK		0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT		3
+
+/* ODR */
+#define ST_ACCEL_ODR_AVL_1HZ			1
+#define ST_ACCEL_ODR_AVL_3HZ			3
+#define ST_ACCEL_ODR_AVL_6HZ			6
+#define ST_ACCEL_ODR_AVL_10HZ			10
+#define ST_ACCEL_ODR_AVL_12HZ			12
+#define ST_ACCEL_ODR_AVL_25HZ			25
+#define ST_ACCEL_ODR_AVL_50HZ			50
+#define ST_ACCEL_ODR_AVL_100HZ			100
+#define ST_ACCEL_ODR_AVL_200HZ			200
+#define ST_ACCEL_ODR_AVL_400HZ			400
+#define ST_ACCEL_ODR_AVL_800HZ			800
+#define ST_ACCEL_ODR_AVL_1000HZ			1000
+#define ST_ACCEL_ODR_AVL_1600HZ			1600
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G			2
+#define ST_ACCEL_FS_AVL_4G			4
+#define ST_ACCEL_FS_AVL_6G			6
+#define ST_ACCEL_FS_AVL_8G			8
+#define ST_ACCEL_FS_AVL_16G			16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP			0x33
+#define ST_ACCEL_1_ODR_ADDR			0x20
+#define ST_ACCEL_1_ODR_MASK			0xf0
+#define ST_ACCEL_1_ODR_N_BIT			4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL		0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL		0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL		0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL		0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL		0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL		0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL		0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL		0x08
+#define ST_ACCEL_1_FS_N_BIT			2
+#define ST_ACCEL_1_FS_ADDR			0x23
+#define ST_ACCEL_1_FS_MASK			0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL			0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL			0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL			0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL		0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR			0x23
+#define ST_ACCEL_1_BDU_MASK			0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR		0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK		0x10
+#define ST_ACCEL_1_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP			0x49
+#define ST_ACCEL_2_ODR_ADDR			0x20
+#define ST_ACCEL_2_ODR_MASK			0xf0
+#define ST_ACCEL_2_ODR_N_BIT			4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL		0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL		0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL		0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL		0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL		0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL		0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL		0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL		0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL		0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL		0x0a
+#define ST_ACCEL_2_FS_N_BIT			3
+#define ST_ACCEL_2_FS_ADDR			0x21
+#define ST_ACCEL_2_FS_MASK			0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL			0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL			0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL			0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL			0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL		0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR			0x20
+#define ST_ACCEL_2_BDU_MASK			0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR		0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK		0x04
+#define ST_ACCEL_2_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP			0x32
+#define ST_ACCEL_3_ODR_ADDR			0x20
+#define ST_ACCEL_3_ODR_MASK			0x18
+#define ST_ACCEL_3_ODR_N_BIT			2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL		0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL		0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL		0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL		0x03
+#define ST_ACCEL_3_PW_ADDR			0x20
+#define ST_ACCEL_3_PW_MASK			0xe0
+#define ST_ACCEL_3_PW_N_BIT			3
+#define ST_ACCEL_3_FS_N_BIT			2
+#define ST_ACCEL_3_FS_ADDR			0x23
+#define ST_ACCEL_3_FS_MASK			0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL			0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL			0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL			0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR			0x23
+#define ST_ACCEL_3_BDU_MASK			0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR		0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK		0x02
+#define ST_ACCEL_3_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP			0x40
+#define ST_ACCEL_4_ODR_ADDR			0x20
+#define ST_ACCEL_4_ODR_MASK			0xf0
+#define ST_ACCEL_4_ODR_N_BIT			4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL		0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL		0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL		0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL		0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL		0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL		0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL		0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL		0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL		0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL		0x0a
+#define ST_ACCEL_4_FS_N_BIT			3
+#define ST_ACCEL_4_FS_ADDR			0x24
+#define ST_ACCEL_4_FS_MASK			0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL			0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL			0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL			0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL			0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL		0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR			0x20
+#define ST_ACCEL_4_BDU_MASK			0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR		0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK		0x80
+#define ST_ACCEL_4_IG1_EN_ADDR			0x23
+#define ST_ACCEL_4_IG1_EN_MASK			0x08
+#define ST_ACCEL_4_MULTIREAD_BIT		false
+
+struct st_accel_odr_available {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_accel_odr {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_accel_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_accel_fullscale_available {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+};
+
+struct st_accel_fullscale {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
+};
+
+struct st_accel_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+	u8 en_addr;
+	u8 latch_mask_addr;
+	u8 en_mask;
+	u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+	.type = IIO_ACCEL, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] = {
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] = {
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_accel_platform_data st_accel_default_pdata = {
+	.fullscale = ST_ACCEL_FS_AVL_2G,
+	.sampling_frequency = ST_ACCEL_ODR_AVL_100HZ,
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ *
+ */
+
+static const struct st_accel_sensors {
+	u8 wai;
+	struct iio_chan_spec *ch;
+	struct st_accel_odr odr;
+	struct st_accel_power pw;
+	struct st_accel_axis enable_axis;
+	struct st_accel_fullscale fs;
+	struct st_accel_bdu bdu;
+	struct st_accel_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+} st_accel_sensors[] = {
+	{
+		.wai = ST_ACCEL_1_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_1_ODR_ADDR,
+			.mask = ST_ACCEL_1_ODR_MASK,
+			.num_bit = ST_ACCEL_1_ODR_N_BIT,
+			.odr_avl = {
+				[0] = {
+					.hz = ST_ACCEL_ODR_AVL_1HZ,
+					.value = ST_ACCEL_1_ODR_AVL_1HZ_VAL,
+				},
+				[1] = {
+					.hz = ST_ACCEL_ODR_AVL_10HZ,
+					.value = ST_ACCEL_1_ODR_AVL_10HZ_VAL,
+				},
+				[2] = {
+					.hz = ST_ACCEL_ODR_AVL_25HZ,
+					.value = ST_ACCEL_1_ODR_AVL_25HZ_VAL,
+				},
+				[3] = {
+					.hz = ST_ACCEL_ODR_AVL_50HZ,
+					.value = ST_ACCEL_1_ODR_AVL_50HZ_VAL,
+				},
+				[4] = {
+					.hz = ST_ACCEL_ODR_AVL_100HZ,
+					.value = ST_ACCEL_1_ODR_AVL_100HZ_VAL,
+				},
+				[5] = {
+					.hz = ST_ACCEL_ODR_AVL_200HZ,
+					.value = ST_ACCEL_1_ODR_AVL_200HZ_VAL,
+				},
+				[6] = {
+					.hz = ST_ACCEL_ODR_AVL_400HZ,
+					.value = ST_ACCEL_1_ODR_AVL_400HZ_VAL,
+				},
+				[7] = {
+					.hz = ST_ACCEL_ODR_AVL_1600HZ,
+					.value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL,
+				},
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_1_ODR_ADDR,
+			.mask = ST_ACCEL_1_ODR_MASK,
+			.num_bit = ST_ACCEL_1_ODR_N_BIT,
+			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
+			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_1_FS_ADDR,
+			.mask = ST_ACCEL_1_FS_MASK,
+			.num_bit = ST_ACCEL_1_FS_N_BIT,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_1_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_1_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_1_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = ST_ACCEL_1_FS_AVL_16_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_1_BDU_ADDR,
+			.mask = ST_ACCEL_1_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_1_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
+	},
+	{
+		.wai = ST_ACCEL_2_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_2_ODR_ADDR,
+			.mask = ST_ACCEL_2_ODR_MASK,
+			.num_bit = ST_ACCEL_2_ODR_N_BIT,
+			.odr_avl = {
+				[0] = {
+					.hz = ST_ACCEL_ODR_AVL_3HZ,
+					.value = ST_ACCEL_2_ODR_AVL_3HZ_VAL,
+				},
+				[1] = {
+					.hz = ST_ACCEL_ODR_AVL_6HZ,
+					.value = ST_ACCEL_2_ODR_AVL_6HZ_VAL,
+				},
+				[2] = {
+					.hz = ST_ACCEL_ODR_AVL_12HZ,
+					.value = ST_ACCEL_2_ODR_AVL_12HZ_VAL,
+				},
+				[3] = {
+					.hz = ST_ACCEL_ODR_AVL_25HZ,
+					.value = ST_ACCEL_2_ODR_AVL_25HZ_VAL,
+				},
+				[4] = {
+					.hz = ST_ACCEL_ODR_AVL_50HZ,
+					.value = ST_ACCEL_2_ODR_AVL_50HZ_VAL,
+				},
+				[5] = {
+					.hz = ST_ACCEL_ODR_AVL_100HZ,
+					.value = ST_ACCEL_2_ODR_AVL_100HZ_VAL,
+				},
+				[6] = {
+					.hz = ST_ACCEL_ODR_AVL_200HZ,
+					.value = ST_ACCEL_2_ODR_AVL_200HZ_VAL,
+				},
+				[7] = {
+					.hz = ST_ACCEL_ODR_AVL_400HZ,
+					.value = ST_ACCEL_2_ODR_AVL_400HZ_VAL,
+				},
+				[8] = {
+					.hz = ST_ACCEL_ODR_AVL_800HZ,
+					.value = ST_ACCEL_2_ODR_AVL_800HZ_VAL,
+				},
+				[9] = {
+					.hz = ST_ACCEL_ODR_AVL_1600HZ,
+					.value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL,
+				},
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_2_ODR_ADDR,
+			.mask = ST_ACCEL_2_ODR_MASK,
+			.num_bit = ST_ACCEL_2_ODR_N_BIT,
+			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
+			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_2_FS_ADDR,
+			.mask = ST_ACCEL_2_FS_MASK,
+			.num_bit = ST_ACCEL_2_FS_N_BIT,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_2_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_2_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_6G,
+					.value = ST_ACCEL_2_FS_AVL_6_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_6_GAIN,
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_2_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
+				},
+				[4] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = ST_ACCEL_2_FS_AVL_16_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_16_GAIN,
+				},
+			},
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_2_DRDY_IRQ_MASK,
+		},
+		.bdu = {
+			.addr = ST_ACCEL_2_BDU_ADDR,
+			.mask = ST_ACCEL_2_BDU_MASK,
+		},
+		.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
+	},
+	{
+		.wai = ST_ACCEL_3_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_3_ODR_ADDR,
+			.mask = ST_ACCEL_3_ODR_MASK,
+			.num_bit = ST_ACCEL_3_ODR_N_BIT,
+			.odr_avl = {
+				[0] = {
+					.hz = ST_ACCEL_ODR_AVL_50HZ,
+					.value = ST_ACCEL_3_ODR_AVL_50HZ_VAL,
+				},
+				[1] = {
+					.hz = ST_ACCEL_ODR_AVL_100HZ,
+					.value = ST_ACCEL_3_ODR_AVL_100HZ_VAL,
+				},
+				[2] = {
+					.hz = ST_ACCEL_ODR_AVL_400HZ,
+					.value = ST_ACCEL_3_ODR_AVL_400HZ_VAL,
+				},
+				[3] = {
+					.hz = ST_ACCEL_ODR_AVL_1000HZ,
+					.value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL,
+				},
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_3_PW_ADDR,
+			.mask = ST_ACCEL_3_PW_MASK,
+			.num_bit = ST_ACCEL_3_PW_N_BIT,
+			.value_on = ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
+			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_3_FS_ADDR,
+			.mask = ST_ACCEL_3_FS_MASK,
+			.num_bit = ST_ACCEL_3_FS_N_BIT,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_3_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_3_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_3_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_3_BDU_ADDR,
+			.mask = ST_ACCEL_3_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_3_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
+	},
+	{
+		.wai = ST_ACCEL_4_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_4_ODR_ADDR,
+			.mask = ST_ACCEL_4_ODR_MASK,
+			.num_bit = ST_ACCEL_4_ODR_N_BIT,
+			.odr_avl = {
+				[0] = {
+					.hz = ST_ACCEL_ODR_AVL_3HZ,
+					.value = ST_ACCEL_4_ODR_AVL_3HZ_VAL,
+				},
+				[1] = {
+					.hz = ST_ACCEL_ODR_AVL_6HZ,
+					.value = ST_ACCEL_4_ODR_AVL_6HZ_VAL,
+				},
+				[2] = {
+					.hz = ST_ACCEL_ODR_AVL_12HZ,
+					.value = ST_ACCEL_4_ODR_AVL_12HZ_VAL,
+				},
+				[3] = {
+					.hz = ST_ACCEL_ODR_AVL_25HZ,
+					.value = ST_ACCEL_4_ODR_AVL_25HZ_VAL,
+				},
+				[4] = {
+					.hz = ST_ACCEL_ODR_AVL_50HZ,
+					.value = ST_ACCEL_4_ODR_AVL_50HZ_VAL,
+				},
+				[5] = {
+					.hz = ST_ACCEL_ODR_AVL_100HZ,
+					.value = ST_ACCEL_4_ODR_AVL_100HZ_VAL,
+				},
+				[6] = {
+					.hz = ST_ACCEL_ODR_AVL_200HZ,
+					.value = ST_ACCEL_4_ODR_AVL_200HZ_VAL,
+				},
+				[7] = {
+					.hz = ST_ACCEL_ODR_AVL_400HZ,
+					.value = ST_ACCEL_4_ODR_AVL_400HZ_VAL,
+				},
+				[8] = {
+					.hz = ST_ACCEL_ODR_AVL_800HZ,
+					.value = ST_ACCEL_4_ODR_AVL_800HZ_VAL,
+				},
+				[9] = {
+					.hz = ST_ACCEL_ODR_AVL_1600HZ,
+					.value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL,
+				},
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_4_ODR_ADDR,
+			.mask = ST_ACCEL_4_ODR_MASK,
+			.num_bit = ST_ACCEL_4_ODR_N_BIT,
+			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
+			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_4_FS_ADDR,
+			.mask = ST_ACCEL_4_FS_MASK,
+			.num_bit = ST_ACCEL_4_FS_N_BIT,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_4_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_4_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_4_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_6G,
+					.value = ST_ACCEL_4_FS_AVL_6_VAL,
+					.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_4_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_4_FS_AVL_8_GAIN,
+				},
+				[4] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = ST_ACCEL_4_FS_AVL_16_VAL,
+					.gain = ST_ACCEL_4_FS_AVL_16_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_4_BDU_ADDR,
+			.mask = ST_ACCEL_4_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_4_DRDY_IRQ_MASK,
+			.ig1 = {
+				.en_addr = ST_ACCEL_4_IG1_EN_ADDR,
+				.en_mask = ST_ACCEL_4_IG1_EN_MASK,
+			},
+		},
+		.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
+	},
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr,
+						u8 mask, short num_bit, u8 data)
+{
+	int err;
+	u8 prev_data;
+	u8 new_data;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = adata->read_byte(adata, reg_addr, &prev_data);
+	if (err < 0)
+		goto st_accel_write_data_with_mask_error;
+
+	new_data = ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+	return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+		unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+		if (sensor->odr.odr_avl[i].hz == odr) {
+			odr_out->hz = sensor->odr.odr_avl[i].hz;
+			odr_out->value = sensor->odr.odr_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+		unsigned int fs, struct st_accel_fullscale_available *fs_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+		if (sensor->fs.fs_avl[i].num == fs) {
+			fs_out->num = sensor->fs.fs_avl[i].num;
+			fs_out->gain = sensor->fs.fs_avl[i].gain;
+			fs_out->value = sensor->fs.fs_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+			int scale, struct st_accel_fullscale_available *fs_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+		if (sensor->fs.fs_avl[i].gain == scale) {
+			fs_out->num = sensor->fs.fs_avl[i].num;
+			fs_out->gain = sensor->fs.fs_avl[i].gain;
+			fs_out->value = sensor->fs.fs_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+	int err;
+	struct st_accel_data *adata;
+
+	adata = iio_priv(indio_dev);
+	if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+		err = st_accel_write_data_with_mask(indio_dev,
+			st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
+			st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
+			(int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+		err = st_accel_write_data_with_mask(indio_dev,
+		st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
+		st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
+			(int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	err = st_accel_write_data_with_mask(indio_dev,
+		st_accel_sensors[adata->index].drdy_irq.addr,
+		st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
+	if (err < 0)
+		goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+				const struct st_accel_bdu *bdu, bool value)
+{
+	return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
+								1, (u8)value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+				struct st_accel_odr_available *odr_available)
+{
+	int err;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	if ((st_accel_sensors[adata->index].odr.addr ==
+		st_accel_sensors[adata->index].pw.addr) &&
+			(st_accel_sensors[adata->index].odr.mask ==
+				st_accel_sensors[adata->index].pw.mask)) {
+		if (adata->enabled == true) {
+			err = st_accel_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].odr.addr,
+				st_accel_sensors[adata->index].odr.mask,
+				st_accel_sensors[adata->index].odr.num_bit,
+				odr_available->value);
+			if (err < 0)
+				goto sc_accel_set_odr_error;
+		} else {
+			adata->odr = odr_available->hz;
+			err = 0;
+		}
+	} else {
+		err = st_accel_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].odr.addr,
+				st_accel_sensors[adata->index].odr.mask,
+				st_accel_sensors[adata->index].odr.num_bit,
+				odr_available->value);
+		if (err < 0)
+			goto sc_accel_set_odr_error;
+	}
+
+sc_accel_set_odr_error:
+	return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+	int err;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = st_accel_write_data_with_mask(indio_dev,
+			st_accel_sensors[adata->index].enable_axis.addr,
+			st_accel_sensors[adata->index].enable_axis.mask,
+			ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+	return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	int err;
+	bool found;
+	u8 tmp_value;
+	struct st_accel_odr_available odr_out;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	switch (enable) {
+	case true:
+		found = false;
+		tmp_value = st_accel_sensors[adata->index].pw.value_on;
+		if ((st_accel_sensors[adata->index].odr.addr ==
+				st_accel_sensors[adata->index].pw.addr) &&
+			(st_accel_sensors[adata->index].odr.mask ==
+				st_accel_sensors[adata->index].pw.mask)) {
+			err = st_accel_match_odr(
+				&st_accel_sensors[adata->index], adata->odr,
+					&odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_accel_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].pw.addr,
+				st_accel_sensors[adata->index].pw.mask,
+				st_accel_sensors[adata->index].pw.num_bit,
+				tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		adata->enabled = true;
+		if (found)
+			adata->odr = odr_out.hz;
+		break;
+	case false:
+		err = st_accel_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].pw.addr,
+				st_accel_sensors[adata->index].pw.mask,
+				st_accel_sensors[adata->index].pw.num_bit,
+				st_accel_sensors[adata->index].pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		adata->enabled = false;
+		break;
+	default:
+		err = -EINVAL;
+		goto set_enable_error;
+	}
+
+set_enable_error:
+	return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+				struct st_accel_fullscale_available *fs_avl)
+{
+	int err;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = st_accel_write_data_with_mask(indio_dev,
+				st_accel_sensors[adata->index].fs.addr,
+				st_accel_sensors[adata->index].fs.mask,
+				st_accel_sensors[adata->index].fs.num_bit,
+				fs_avl->value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	adata->fullscale = fs_avl->num;
+	adata->gain = fs_avl->gain;
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+		if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+			err = -EBUSY;
+			goto read_error;
+		} else {
+			if (!adata->enabled) {
+				err = -EIO;
+				goto read_error;
+			} else {
+				err = adata->read_multiple_byte(adata,
+					ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
+					outdata);
+				if (err < 0)
+					goto read_error;
+
+				*val = ((s16)(((s16)(outdata[1]) << 8)
+					| outdata[0])) >> ch->scan_type.shift;
+			}
+		}
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = adata->gain;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	mutex_unlock(&indio_dev->mlock);
+	return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	int err;
+	struct st_accel_fullscale_available fs_out;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_accel_match_scale(&st_accel_sensors[adata->index],
+								val2, &fs_out);
+		if (err < 0)
+			goto write_error;
+
+		err = st_accel_set_fullscale(indio_dev, &fs_out);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+write_error:
+	return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+	int i;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+		if (st_accel_sensors[i].wai == wai)
+			break;
+	}
+	if (i == ARRAY_SIZE(st_accel_sensors))
+		goto check_device_error;
+
+	adata->index = i;
+
+	return i;
+
+check_device_error:
+	dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
+	return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+								u8 *value)
+{
+	int ret;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	ret = adata->read_byte(adata, reg_addr, value);
+	if (ret < 0)
+		goto read_byte_wai_error;
+
+	return 0;
+
+read_byte_wai_error:
+	dev_err(&indio_dev->dev,
+			"failed to read WhoAmI (register 0x%x).\n", reg_addr);
+	return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned int freq;
+	struct st_accel_odr_available odr_out;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = kstrtoint(buf, 10, &freq);
+	if (err < 0)
+		goto conversion_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_accel_match_odr(&st_accel_sensors[adata->index],
+								freq, &odr_out);
+	if (err < 0)
+		goto st_accel_sysfs_set_sampling_frequency_error;
+
+	err = st_accel_set_odr(indio_dev, &odr_out);
+	if (err < 0) {
+		dev_err(&indio_dev->dev,
+			"failed to set sampling frequency to %d.\n", freq);
+		goto st_accel_sysfs_set_sampling_frequency_error;
+	}
+	adata->odr = odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+	mutex_unlock(&indio_dev->mlock);
+conversion_error:
+	return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	bool powerdown;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	err = strtobool(buf, &powerdown);
+	if (err < 0)
+		goto set_enable_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_accel_set_enable(indio_dev, ~powerdown);
+	if (err < 0)
+		dev_err(&indio_dev->dev,
+			"failed to set powerdown to %d.\n", (int)(powerdown));
+	mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+	return size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int i, len = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
+									i++) {
+		if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
+			break;
+
+		len += sprintf(buf+len, "0.%06u ",
+			st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len-1] = '\n';
+
+	return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int i, len = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
+									i++) {
+		if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
+			break;
+
+		len += sprintf(buf+len, "%d ",
+			st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len-1] = '\n';
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+		st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+				st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+		st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			st_accel_sysfs_get_sampling_frequency,
+					st_accel_sysfs_set_sampling_frequency);
+
+static struct attribute *st_accel_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+	&iio_dev_attr_powerdown.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group = {
+	.attrs = st_accel_attributes,
+};
+
+static const struct iio_info acc_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &st_accel_attribute_group,
+	.read_raw = &st_accel_read_raw,
+	.write_raw = &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_accel_odr_available odr_out;
+	struct st_accel_fullscale_available fs_out;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	err = st_accel_set_enable(indio_dev, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
+	if (err < 0)
+		goto init_error;
+
+	st_accel_match_fs(&st_accel_sensors[adata->index],
+						adata->fullscale, &fs_out);
+	err = st_accel_set_fullscale(indio_dev, &fs_out);
+	if (err < 0)
+		goto init_error;
+
+	st_accel_match_odr(&st_accel_sensors[adata->index],
+							adata->odr, &odr_out);
+	err = st_accel_set_odr(indio_dev, &odr_out);
+	if (err < 0)
+		goto init_error;
+
+	err = st_accel_set_bdu(indio_dev,
+				&st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+	return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+	int err;
+	u8 wai;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+	struct st_accel_platform_data *pdata;
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &acc_info;
+
+	err = st_accel_get_wai_device(indio_dev,
+					ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
+	if (err < 0)
+		goto st_accel_iio_probe_error;
+
+	err = st_accel_check_device_list(indio_dev, wai);
+	if (err < 0)
+		goto st_accel_iio_probe_error;
+
+	adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+	indio_dev->channels = st_accel_sensors[adata->index].ch;
+	indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
+	pdata = adata->dev->platform_data;
+	if (!pdata)
+		pdata = (struct st_accel_platform_data *)
+							&st_accel_default_pdata;
+
+	adata->fullscale = pdata->fullscale;
+	adata->odr = pdata->sampling_frequency;
+
+	err = st_accel_init_sensor(indio_dev);
+	if (err < 0)
+		goto st_accel_iio_probe_error;
+
+	err = st_accel_allocate_ring(indio_dev);
+	if (err < 0)
+		goto st_accel_iio_probe_error;
+
+	if (irq > 0) {
+		err = st_accel_probe_trigger(indio_dev, irq);
+		if (err < 0)
+			goto acc_probe_trigger_error;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto iio_device_register_error;
+
+	return err;
+
+iio_device_register_error:
+	st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+	st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+	return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+	iio_device_unregister(indio_dev);
+	st_accel_remove_trigger(indio_dev, irq);
+	st_accel_deallocate_ring(indio_dev);
+	iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..a9fec2e
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,129 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_I2C_MULTIREAD			0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+						u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+						u8 reg_addr, int len, u8 *data)
+{
+	if (adata->multiread_bit == true)
+		reg_addr |= ST_ACCEL_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+							reg_addr, len, data);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+							u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+								reg_addr, data);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_accel_data *adata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*adata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	adata = iio_priv(indio_dev);
+	adata->dev = &client->dev;
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	adata->read_byte = st_accel_i2c_read_byte;
+	adata->write_byte = st_accel_i2c_write_byte;
+	adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
+
+	err = st_accel_iio_probe(indio_dev, client->irq);
+	if (err < 0)
+		goto acc_iio_default_error;
+
+	return 0;
+
+acc_iio_default_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int __devexit st_accel_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	st_accel_iio_remove(indio_dev, client->irq);
+	return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] = {
+	{ LSM303DLH_ACCEL_DEV_NAME },
+	{ LSM303DLHC_ACCEL_DEV_NAME },
+	{ LIS3DH_ACCEL_DEV_NAME },
+	{ LSM330D_ACCEL_DEV_NAME },
+	{ LSM330DL_ACCEL_DEV_NAME },
+	{ LSM330DLC_ACCEL_DEV_NAME },
+	{ LSM303D_ACCEL_DEV_NAME },
+	{ LSM9DS0_ACCEL_DEV_NAME },
+	{ LIS331DLH_ACCEL_DEV_NAME },
+	{ LSM303DL_ACCEL_DEV_NAME },
+	{ LSM303DLM_ACCEL_DEV_NAME },
+	{ LSM330_ACCEL_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-accel-i2c",
+	},
+	.probe = st_accel_i2c_probe,
+	.remove = __devexit_p(st_accel_i2c_remove),
+	.id_table = st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..26b8ff5
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,188 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ		0x80;
+#define ACC_SPI_MULTIREAD	0xc0
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+						u8 reg_addr, u8 *res_byte)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = &tx,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = res_byte,
+			.bits_per_word = 8,
+			.len = 1,
+		}
+	};
+
+	tx = reg_addr | ACC_SPI_READ;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+
+	return err;
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+						u8 reg_addr, int len, u8 *data)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = &tx,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = data,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	if (adata->multiread_bit == true)
+		tx = reg_addr | ACC_SPI_MULTIREAD;
+	else
+		tx = reg_addr | ACC_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+	if (err)
+		goto acc_spi_read_multiple_byte_error;
+
+	return len;
+
+acc_spi_read_multiple_byte_error:
+	return err;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+							u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+	u8 tx[2];
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tx,
+			.bits_per_word = 8,
+			.len = 2,
+		}
+	};
+
+	tx[0] = reg_addr;
+	tx[1] = data;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	err = spi_sync(to_spi_device(adata->dev), &msg);
+
+	return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct st_accel_data *adata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*adata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	adata = iio_priv(indio_dev);
+	adata->dev = &spi->dev;
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	adata->read_byte = st_accel_spi_read_byte;
+	adata->write_byte = st_accel_spi_write_byte;
+	adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
+
+	err = st_accel_iio_probe(indio_dev, spi->irq);
+	if (err < 0)
+		goto acc_iio_default_error;
+
+	return 0;
+
+acc_iio_default_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int __devexit st_accel_spi_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+	st_accel_iio_remove(indio_dev, spi->irq);
+	return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] = {
+	{ LSM303DLH_ACCEL_DEV_NAME },
+	{ LSM303DLHC_ACCEL_DEV_NAME },
+	{ LIS3DH_ACCEL_DEV_NAME },
+	{ LSM330D_ACCEL_DEV_NAME },
+	{ LSM330DL_ACCEL_DEV_NAME },
+	{ LSM330DLC_ACCEL_DEV_NAME },
+	{ LSM303D_ACCEL_DEV_NAME },
+	{ LSM9DS0_ACCEL_DEV_NAME },
+	{ LIS331DLH_ACCEL_DEV_NAME },
+	{ LSM303DL_ACCEL_DEV_NAME },
+	{ LSM303DLM_ACCEL_DEV_NAME },
+	{ LSM330_ACCEL_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-accel-spi",
+	},
+	.probe = st_accel_spi_probe,
+	.remove = __devexit_p(st_accel_spi_remove),
+	.id_table = st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..8bc970c
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,84 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = trig->private_data;
+	return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+	int err;
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (adata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(irq,
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			adata->trig->name,
+			adata->trig);
+	if (err)
+		goto request_irq_error;
+
+	adata->trig->private_data = indio_dev;
+	adata->trig->ops = &st_accel_trigger_ops;
+	adata->trig->dev.parent = adata->dev;
+
+	err = iio_trigger_register(adata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = adata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(irq, adata->trig);
+request_irq_error:
+	iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+	struct st_accel_data *adata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(adata->trig);
+	free_irq(irq, adata->trig);
+	iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..1efd29e
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,121 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Supported sensors:
+ * LSM303DLH
+ * LSM303DLHC
+ * LIS3DH
+ * LSM330D
+ * LSM330DL
+ * LSM330DLC
+ * LSM303D
+ * LSM9DS0
+ * LIS331DLH
+ * LSM303DL
+ * LSM303DLM
+ * LSM330
+ *
+ */
+
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME	"lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME	"lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME		"lis3dh"
+#define LSM330D_ACCEL_DEV_NAME		"lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME		"lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME	"lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME		"lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME		"lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME	"lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME		"lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME	"lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME		"lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS	4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS	3
+#define ST_ACCEL_BYTE_FOR_CHANNEL	2
+#define ST_ACCEL_SCAN_X			0
+#define ST_ACCEL_SCAN_Y			1
+#define ST_ACCEL_SCAN_Z			2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *	st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [ms^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ *
+ */
+
+struct st_accel_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int fullscale;
+	unsigned int gain;
+	unsigned int odr;
+
+	int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+								u8 *res_byte);
+	int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
+							int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+	return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
diff --git a/include/linux/platform_data/st_accel_pdata.h
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
--- /dev/null
+++ b/include/linux/platform_data/st_accel_pdata.h
@@ -0,0 +1,27 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+
+#ifndef ST_ACCEL_PDATA_H
+#define ST_ACCEL_PDATA_H
+
+
+/**
+ * struct st_accel_platform_data - ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */
+
+struct st_accel_platform_data {
+	int fullscale;
+	int sampling_frequency;
+};
+
+#endif /* ST_ACCEL_PDATA_H */
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-04 10:09                                 ` Denis Ciocca
@ 2012-11-05 21:28                                   ` Jonathan Cameron
  2012-11-06 11:11                                     ` Denis CIOCCA
  2012-11-06 11:14                                     ` Denis CIOCCA
  0 siblings, 2 replies; 42+ messages in thread
From: Jonathan Cameron @ 2012-11-05 21:28 UTC (permalink / raw)
  To: Denis Ciocca
  Cc: Lars-Peter Clausen, Denis CIOCCA, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/04/2012 10:09 AM, Denis Ciocca wrote:
> Hi everybody,
>
> I have modify the driver in according to your previous arrangements. I
> don't know if I use the best solution (I think not), but now I don't
> have time to read and write code for the framework because my work
> doesn't allow me.

Thanks for your work so far on this and I fully understand that work
can and often takes one away from contributing.

I have reviewed the code as it stands so that if you or anyone else
wants to continue in the future the pointers are there.

It's a pretty clean driver now with all the big stuff sorted.
I hope we do manage to get the last few bits cleaned up.

>
> Thanks
>
> Denis
>
>
> From 348bcf19b2bb400bae0af5a5e444f772bb5c4f97 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
>
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
>
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>  Documentation/ABI/testing/sysfs-bus-iio-accel-st |   26 +
>  drivers/iio/accel/Kconfig                        |   37 +
>  drivers/iio/accel/Makefile                       |    6 +
>  drivers/iio/accel/st_accel_buffer.c              |  176 +++
>  drivers/iio/accel/st_accel_core.c                | 1291 ++++++++++++++++++++++
>  drivers/iio/accel/st_accel_i2c.c                 |  129 +++
>  drivers/iio/accel/st_accel_spi.c                 |  188 ++++
>  drivers/iio/accel/st_accel_trigger.c             |   84 ++
>  include/linux/iio/accel/st_accel.h               |  121 ++
>  include/linux/platform_data/st_accel_pdata.h     |   27 +
>  10 files changed, 2085 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
>  create mode 100644 drivers/iio/accel/st_accel_buffer.c
>  create mode 100644 drivers/iio/accel/st_accel_core.c
>  create mode 100644 drivers/iio/accel/st_accel_i2c.c
>  create mode 100644 drivers/iio/accel/st_accel_spi.c
>  create mode 100644 drivers/iio/accel/st_accel_trigger.c
>  create mode 100644 include/linux/iio/accel/st_accel.h
>  create mode 100644 include/linux/platform_data/st_accel_pdata.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> new file mode 100644
> index 0000000..e86c1b4
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> @@ -0,0 +1,26 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/powerdown
> +KernelVersion:	3.7.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns either '1' or '0'.
> +		'1' means that the device in question is off.
> +		'0' means that the devices in question is on.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/sampling_frequency_available
> +KernelVersion:	3.7.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the sampling frequency available for the
> +		device in question. This values are known in datasheet with
> +		ODR [output data rate]. The units measurement is Hz.
> +
This is standard (even if it's not used anywhere else) so should be in sysfs-bus-iio.
(same for sampling_frequency_available)

> +What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
> +KernelVersion:	3.7.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the scale values available for the device in
> +		question.
> +		Writing one of this values to one of in_accel_*_scale attribute,
> +		the full scale of the sensor in question change, the
> +		value is related by this relation:
> +		[in_accel_scale_available] = [sensitivity]*[conversion_to_ms^2].
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..d65e66a 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>  	  Say yes here to build support for the HID SENSOR
>  	  accelerometers 3D.
>
> +config ST_ACCEL_3AXIS
> +	tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +	depends on (I2C || SPI) && SYSFS
> +	help
> +	  Say yes here to build support for STMicroelectronics accelerometers:
> +	  LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +	  LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called st_accel.

> +
> +config ST_ACCEL_3AXIS_I2C
> +	tristate "support I2C bus connection"
> +	depends on ST_ACCEL_3AXIS && I2C
> +	help
> +	  Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_accel_i2c.
> +
> +config ST_ACCEL_3AXIS_SPI
> +	tristate "support SPI bus connection"
> +	depends on ST_ACCEL_3AXIS && SPI_MASTER
> +	help
> +	  Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_accel_spi.
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +	tristate "support triggered buffer"
> +	depends on ST_ACCEL_3AXIS
> +	select IIO_TRIGGERED_BUFFER
> +	select IIO_BUFFER
> +	help
> +	  Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>  endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..1541236 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>  #
>
>  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +st_accel-y := st_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
> st_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..bcf0fe2
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_buffer.c
> @@ -0,0 +1,176 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_ENABLE_ALL_CHANNELS		0x07
> +
> +static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +	int len = 0, i, n = 0;
> +	u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
> +		if (test_bit(i, indio_dev->active_scan_mask)) {
> +			reg_addr[n] = indio_dev->channels[i].address;
> +			n++;
> +		}
> +	}
> +	switch (n) {
> +	case 1:
> +		len = adata->read_multiple_byte(adata, reg_addr[0],
> +					ST_ACCEL_BYTE_FOR_CHANNEL, rx_array);
> +		break;
> +	case 2:
> +		if ((reg_addr[1] - reg_addr[0]) == ST_ACCEL_BYTE_FOR_CHANNEL) {
> +			len = adata->read_multiple_byte(adata, reg_addr[0],
> +					ST_ACCEL_BYTE_FOR_CHANNEL*n,
> +					rx_array);
> +		} else {
> +			len = adata->read_multiple_byte(adata, reg_addr[0],
> +				ST_ACCEL_BYTE_FOR_CHANNEL*
> +				ST_ACCEL_NUMBER_DATA_CHANNELS,
> +				rx_array);
> +			rx_array[2] = rx_array[4];
> +			rx_array[3] = rx_array[5];
> +			len = ST_ACCEL_BYTE_FOR_CHANNEL*n;
> +		}
> +		break;
> +	case 3:
> +		len = adata->read_multiple_byte(adata, reg_addr[0],
> +			ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
> +			rx_array);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return len;
> +}
> +
> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int ret, i, scan_count;
> +	u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
> +	s16 *data = (s16 *)buf;
> +
> +	ret = st_accel_read_all(indio_dev, rx_array);
> +	if (ret < 0)
> +		return ret;
> +
> +	scan_count = bitmap_weight(indio_dev->active_scan_mask,
> +							indio_dev->masklength);
> +
> +	for (i = 0; i < scan_count; i++)
> +		data[i] = le16_to_cpup((__le16 *)&(rx_array[i*2]));
As Lars pointed out there is no need to do this.  Just specify these
as little endian channels and let userspace handle it.
> +
> +	return i*sizeof(data[0]);
> +}
> +
> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
> +{
> +	int len = 0;
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	len = st_accel_get_buffer_element(indio_dev, adata->buffer_data);
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)adata->buffer_data +
> +				ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +	iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	int err, i;
> +	u8 active_bit = 0x00;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +	if (adata->buffer_data == NULL) {
> +		err = -ENOMEM;
> +		goto allocate_memory_error;
> +	}
> +
> +	for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
> +		if (test_bit(i, indio_dev->active_scan_mask))
> +			active_bit |= (1 << i);
> +
> +	err = st_accel_set_axis_enable(indio_dev, active_bit);
> +	if (err < 0)
> +		goto st_accel_buffer_postenable_error;
> +
> +	err = iio_triggered_buffer_postenable(indio_dev);
> +
> +	return err;
> +
> +allocate_memory_error:
> +	kfree(adata->buffer_data);
> +st_accel_buffer_postenable_error:
> +	return err;
> +}
> +
> +static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = iio_triggered_buffer_predisable(indio_dev);
> +	if (err < 0)
> +		goto st_accel_buffer_predisable_error;
> +
> +	err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +	if (err < 0)
> +		goto st_accel_buffer_predisable_error;
> +
> +	kfree(adata->buffer_data);
> +
> +st_accel_buffer_predisable_error:
> +	return err;
> +}
> +
> +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
> +	.preenable = &iio_sw_buffer_preenable,
> +	.postenable = &st_accel_buffer_postenable,
> +	.predisable = &st_accel_buffer_predisable,
> +};
> +
> +int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +	indio_dev->scan_timestamp = true;
> +	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +			&st_accel_trigger_handler, &st_accel_buffer_setup_ops);
> +}
> +EXPORT_SYMBOL(st_accel_allocate_ring);
> +
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +	iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_deallocate_ring);
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..9697a2b
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1291 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +#include <linux/platform_data/st_accel_pdata.h>
> +
> +
> +#define ST_ACCEL_FULLSCALE_AVL_MAX		5
> +#define ST_ACCEL_ODR_LIST_MAX			10
> +#define ST_ACCEL_ENABLE_ALL_CHANNELS		0x07
> +
> +/* DEFAULT VALUE FOR SENSORS */
> +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR		0x28
> +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR		0x29
> +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR		0x2a
> +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR		0x2b
> +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR		0x2c
> +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR		0x2d
> +#define ST_ACCEL_DEFAULT_WAI_ADDRESS		0x0f
> +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE		0x01
> +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE	0x00
> +#define ST_ACCEL_DEFAULT_12_REALBITS		12
> +#define ST_ACCEL_DEFAULT_16_REALBITS		16
> +#define ST_ACCEL_DEFAULT_AXIS_ADDR		0x20
> +#define ST_ACCEL_DEFAULT_AXIS_MASK		0x07
> +#define ST_ACCEL_DEFAULT_AXIS_N_BIT		3
> +
> +/* ODR */
> +#define ST_ACCEL_ODR_AVL_1HZ			1
> +#define ST_ACCEL_ODR_AVL_3HZ			3
> +#define ST_ACCEL_ODR_AVL_6HZ			6
> +#define ST_ACCEL_ODR_AVL_10HZ			10
> +#define ST_ACCEL_ODR_AVL_12HZ			12
> +#define ST_ACCEL_ODR_AVL_25HZ			25
> +#define ST_ACCEL_ODR_AVL_50HZ			50
> +#define ST_ACCEL_ODR_AVL_100HZ			100
> +#define ST_ACCEL_ODR_AVL_200HZ			200
> +#define ST_ACCEL_ODR_AVL_400HZ			400
> +#define ST_ACCEL_ODR_AVL_800HZ			800
> +#define ST_ACCEL_ODR_AVL_1000HZ			1000
> +#define ST_ACCEL_ODR_AVL_1600HZ			1600
> +
> +/* FULLSCALE */
> +#define ST_ACCEL_FS_AVL_2G			2
> +#define ST_ACCEL_FS_AVL_4G			4
> +#define ST_ACCEL_FS_AVL_6G			6
> +#define ST_ACCEL_FS_AVL_8G			8
> +#define ST_ACCEL_FS_AVL_16G			16
> +
> +/* CUSTOM VALUES FOR SENSOR 1 */
> +#define ST_ACCEL_1_WAI_EXP			0x33
> +#define ST_ACCEL_1_ODR_ADDR			0x20
> +#define ST_ACCEL_1_ODR_MASK			0xf0
> +#define ST_ACCEL_1_ODR_N_BIT			4
> +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL		0x01
> +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL		0x02
> +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL		0x03
> +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL		0x04
> +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL		0x05
> +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL		0x06
> +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL		0x07
> +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL		0x08
> +#define ST_ACCEL_1_FS_N_BIT			2
> +#define ST_ACCEL_1_FS_ADDR			0x23
> +#define ST_ACCEL_1_FS_MASK			0x30
> +#define ST_ACCEL_1_FS_AVL_2_VAL			0x00
> +#define ST_ACCEL_1_FS_AVL_4_VAL			0x01
> +#define ST_ACCEL_1_FS_AVL_8_VAL			0x02
> +#define ST_ACCEL_1_FS_AVL_16_VAL		0x03
> +#define ST_ACCEL_1_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_1_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_1_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(4000)
> +#define ST_ACCEL_1_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(12000)
> +#define ST_ACCEL_1_BDU_ADDR			0x23
> +#define ST_ACCEL_1_BDU_MASK			0x80
> +#define ST_ACCEL_1_DRDY_IRQ_ADDR		0x22
> +#define ST_ACCEL_1_DRDY_IRQ_MASK		0x10
> +#define ST_ACCEL_1_MULTIREAD_BIT		true
> +
> +/* CUSTOM VALUES FOR SENSOR 2 */
> +#define ST_ACCEL_2_WAI_EXP			0x49
> +#define ST_ACCEL_2_ODR_ADDR			0x20
> +#define ST_ACCEL_2_ODR_MASK			0xf0
> +#define ST_ACCEL_2_ODR_N_BIT			4
> +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL		0x01
> +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL		0x02
> +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL		0x03
> +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL		0x04
> +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL		0x05
> +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL		0x06
> +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL		0x07
> +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL		0x08
> +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL		0x09
> +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL		0x0a
> +#define ST_ACCEL_2_FS_N_BIT			3
> +#define ST_ACCEL_2_FS_ADDR			0x21
> +#define ST_ACCEL_2_FS_MASK			0x38
> +#define ST_ACCEL_2_FS_AVL_2_VAL			0X00
> +#define ST_ACCEL_2_FS_AVL_4_VAL			0X01
> +#define ST_ACCEL_2_FS_AVL_6_VAL			0x02
> +#define ST_ACCEL_2_FS_AVL_8_VAL			0x03
> +#define ST_ACCEL_2_FS_AVL_16_VAL		0x04
> +#define ST_ACCEL_2_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_2_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_2_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_2_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_2_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_2_BDU_ADDR			0x20
> +#define ST_ACCEL_2_BDU_MASK			0x08
> +#define ST_ACCEL_2_DRDY_IRQ_ADDR		0x22
> +#define ST_ACCEL_2_DRDY_IRQ_MASK		0x04
> +#define ST_ACCEL_2_MULTIREAD_BIT		true
> +
> +/* CUSTOM VALUES FOR SENSOR 3 */
> +#define ST_ACCEL_3_WAI_EXP			0x32
> +#define ST_ACCEL_3_ODR_ADDR			0x20
> +#define ST_ACCEL_3_ODR_MASK			0x18
> +#define ST_ACCEL_3_ODR_N_BIT			2
> +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL		0x00
> +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL		0x01
> +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL		0x02
> +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL		0x03
> +#define ST_ACCEL_3_PW_ADDR			0x20
> +#define ST_ACCEL_3_PW_MASK			0xe0
> +#define ST_ACCEL_3_PW_N_BIT			3
> +#define ST_ACCEL_3_FS_N_BIT			2
> +#define ST_ACCEL_3_FS_ADDR			0x23
> +#define ST_ACCEL_3_FS_MASK			0x30
> +#define ST_ACCEL_3_FS_AVL_2_VAL			0X00
> +#define ST_ACCEL_3_FS_AVL_4_VAL			0X01
> +#define ST_ACCEL_3_FS_AVL_8_VAL			0x03
> +#define ST_ACCEL_3_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_3_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_3_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(3900)
> +#define ST_ACCEL_3_BDU_ADDR			0x23
> +#define ST_ACCEL_3_BDU_MASK			0x80
> +#define ST_ACCEL_3_DRDY_IRQ_ADDR		0x22
> +#define ST_ACCEL_3_DRDY_IRQ_MASK		0x02
> +#define ST_ACCEL_3_MULTIREAD_BIT		true
> +
> +/* CUSTOM VALUES FOR SENSOR 4 */
> +#define ST_ACCEL_4_WAI_EXP			0x40
> +#define ST_ACCEL_4_ODR_ADDR			0x20
> +#define ST_ACCEL_4_ODR_MASK			0xf0
> +#define ST_ACCEL_4_ODR_N_BIT			4
> +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL		0x01
> +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL		0x02
> +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL		0x03
> +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL		0x04
> +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL		0x05
> +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL		0x06
> +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL		0x07
> +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL		0x08
> +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL		0x09
> +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL		0x0a
> +#define ST_ACCEL_4_FS_N_BIT			3
> +#define ST_ACCEL_4_FS_ADDR			0x24
> +#define ST_ACCEL_4_FS_MASK			0x38
> +#define ST_ACCEL_4_FS_AVL_2_VAL			0X00
> +#define ST_ACCEL_4_FS_AVL_4_VAL			0X01
> +#define ST_ACCEL_4_FS_AVL_6_VAL			0x02
> +#define ST_ACCEL_4_FS_AVL_8_VAL			0x03
> +#define ST_ACCEL_4_FS_AVL_16_VAL		0x04
> +#define ST_ACCEL_4_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_4_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_4_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_4_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_4_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_4_BDU_ADDR			0x20
> +#define ST_ACCEL_4_BDU_MASK			0x08
> +#define ST_ACCEL_4_DRDY_IRQ_ADDR		0x23
> +#define ST_ACCEL_4_DRDY_IRQ_MASK		0x80
> +#define ST_ACCEL_4_IG1_EN_ADDR			0x23
> +#define ST_ACCEL_4_IG1_EN_MASK			0x08
> +#define ST_ACCEL_4_MULTIREAD_BIT		false
> +
> +struct st_accel_odr_available {
> +	unsigned int hz;
> +	u8 value;
> +};
> +
> +struct st_accel_odr {
> +	u8 addr;
> +	u8 mask;
> +	short num_bit;
> +	struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
> +};
> +
> +struct st_accel_power {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	u8 value_off;
> +	u8 value_on;
> +};
> +
> +struct st_accel_axis {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_accel_fullscale_available {
> +	unsigned int num;
> +	u8 value;
> +	unsigned int gain;
> +};
> +
> +struct st_accel_fullscale {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_accel_bdu {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_accel_interrupt_generator {
> +	u8 en_addr;
> +	u8 latch_mask_addr;
> +	u8 en_mask;
> +	u8 latching_mask;
> +};
> +
> +struct st_accel_data_ready_irq {
> +	u8 addr;
> +	u8 mask;
> +	struct st_accel_interrupt_generator ig1;
> +};
> +
> +#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
> +{ \
> +	.type = IIO_ACCEL, \
> +	.modified = 1, \
> +	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +	.scan_index = index, \
> +	.channel2 = mod, \
> +	.address = addr, \
> +	.scan_type = { \
> +		.sign = 's', \
> +		.realbits = bits, \
> +		.shift = 16 - bits, \
> +		.storagebits = 16, \
> +		.endianness = endian, \
> +	}, \
> +}
> +
> +static const struct iio_chan_spec st_accel_12bit_channels[] = {
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +		ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +	IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +static const struct iio_chan_spec st_accel_16bit_channels[] = {
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +	ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +		ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +	IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +static const struct st_accel_platform_data st_accel_default_pdata = {
> +	.fullscale = ST_ACCEL_FS_AVL_2G,
> +	.sampling_frequency = ST_ACCEL_ODR_AVL_100HZ,
> +};
> +
> +/**
> + * struct st_accel_sensors - ST accel sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + *
> + */
Excess white line here and above the */
> +
> +static const struct st_accel_sensors {
> +	u8 wai;
> +	struct iio_chan_spec *ch;
> +	struct st_accel_odr odr;
> +	struct st_accel_power pw;
> +	struct st_accel_axis enable_axis;
> +	struct st_accel_fullscale fs;
> +	struct st_accel_bdu bdu;
> +	struct st_accel_data_ready_irq drdy_irq;
> +	bool multi_read_bit;
> +} st_accel_sensors[] = {
> +	{
> +		.wai = ST_ACCEL_1_WAI_EXP,
> +		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +		.odr = {
> +			.addr = ST_ACCEL_1_ODR_ADDR,
> +			.mask = ST_ACCEL_1_ODR_MASK,
> +			.num_bit = ST_ACCEL_1_ODR_N_BIT,
> +			.odr_avl = {
  While sometimes c99 assignment helps with clarity - sometimes it just bloats the code.
  Personally I'd do these as
  .odr_avl = {{ST_ACCEL_ODR_AVL_1HZ, ST_ACCEL_1_ODR_AVL_1HZ_VAL},
  	      {...}
etc

or for that matter take the view that the defines are largely pointless as they
are only used here and
just do
     .odr_avl = {{1, 0x01},
     	      	 {10, 0x02},
etc.
Or maybe if it fits on the line
   .odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest... Not sure.

> +				[0] = {
> +					.hz = ST_ACCEL_ODR_AVL_1HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_1HZ_VAL,
> +				},
> +				[1] = {
> +					.hz = ST_ACCEL_ODR_AVL_10HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_10HZ_VAL,
> +				},
> +				[2] = {
> +					.hz = ST_ACCEL_ODR_AVL_25HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_25HZ_VAL,
> +				},
> +				[3] = {
> +					.hz = ST_ACCEL_ODR_AVL_50HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_50HZ_VAL,
> +				},
> +				[4] = {
> +					.hz = ST_ACCEL_ODR_AVL_100HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_100HZ_VAL,
> +				},
> +				[5] = {
> +					.hz = ST_ACCEL_ODR_AVL_200HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_200HZ_VAL,
> +				},
> +				[6] = {
> +					.hz = ST_ACCEL_ODR_AVL_400HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_400HZ_VAL,
> +				},
> +				[7] = {
> +					.hz = ST_ACCEL_ODR_AVL_1600HZ,
> +					.value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL,
> +				},
> +			},
> +		},
> +		.pw = {
> +			.addr = ST_ACCEL_1_ODR_ADDR,
> +			.mask = ST_ACCEL_1_ODR_MASK,
> +			.num_bit = ST_ACCEL_1_ODR_N_BIT,
> +			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +		},
> +		.enable_axis = {
> +			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +		},
> +		.fs = {
> +			.addr = ST_ACCEL_1_FS_ADDR,
> +			.mask = ST_ACCEL_1_FS_MASK,
> +			.num_bit = ST_ACCEL_1_FS_N_BIT,
> +			.fs_avl = {
> +				[0] = {
> +					.num = ST_ACCEL_FS_AVL_2G,
> +					.value = ST_ACCEL_1_FS_AVL_2_VAL,
> +					.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
> +				},
> +				[1] = {
> +					.num = ST_ACCEL_FS_AVL_4G,
> +					.value = ST_ACCEL_1_FS_AVL_4_VAL,
> +					.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
> +				},
> +				[2] = {
> +					.num = ST_ACCEL_FS_AVL_8G,
> +					.value = ST_ACCEL_1_FS_AVL_8_VAL,
> +					.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
> +				},
> +				[3] = {
> +					.num = ST_ACCEL_FS_AVL_16G,
> +					.value = ST_ACCEL_1_FS_AVL_16_VAL,
> +					.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
> +				},
> +			},
> +		},
> +		.bdu = {
> +			.addr = ST_ACCEL_1_BDU_ADDR,
> +			.mask = ST_ACCEL_1_BDU_MASK,
> +		},
> +		.drdy_irq = {
> +			.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
> +			.mask = ST_ACCEL_1_DRDY_IRQ_MASK,
> +		},
> +		.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
> +	},
> +	{
> +		.wai = ST_ACCEL_2_WAI_EXP,
> +		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +		.odr = {
> +			.addr = ST_ACCEL_2_ODR_ADDR,
> +			.mask = ST_ACCEL_2_ODR_MASK,
> +			.num_bit = ST_ACCEL_2_ODR_N_BIT,
> +			.odr_avl = {
> +				[0] = {
> +					.hz = ST_ACCEL_ODR_AVL_3HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_3HZ_VAL,
> +				},
> +				[1] = {
> +					.hz = ST_ACCEL_ODR_AVL_6HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_6HZ_VAL,
> +				},
> +				[2] = {
> +					.hz = ST_ACCEL_ODR_AVL_12HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_12HZ_VAL,
> +				},
> +				[3] = {
> +					.hz = ST_ACCEL_ODR_AVL_25HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_25HZ_VAL,
> +				},
> +				[4] = {
> +					.hz = ST_ACCEL_ODR_AVL_50HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_50HZ_VAL,
> +				},
> +				[5] = {
> +					.hz = ST_ACCEL_ODR_AVL_100HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_100HZ_VAL,
> +				},
> +				[6] = {
> +					.hz = ST_ACCEL_ODR_AVL_200HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_200HZ_VAL,
> +				},
> +				[7] = {
> +					.hz = ST_ACCEL_ODR_AVL_400HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_400HZ_VAL,
> +				},
> +				[8] = {
> +					.hz = ST_ACCEL_ODR_AVL_800HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_800HZ_VAL,
> +				},
> +				[9] = {
> +					.hz = ST_ACCEL_ODR_AVL_1600HZ,
> +					.value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL,
> +				},
> +			},
> +		},
> +		.pw = {
> +			.addr = ST_ACCEL_2_ODR_ADDR,
> +			.mask = ST_ACCEL_2_ODR_MASK,
> +			.num_bit = ST_ACCEL_2_ODR_N_BIT,
> +			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +		},
> +		.enable_axis = {
> +			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +		},
> +		.fs = {
> +			.addr = ST_ACCEL_2_FS_ADDR,
> +			.mask = ST_ACCEL_2_FS_MASK,
> +			.num_bit = ST_ACCEL_2_FS_N_BIT,
> +			.fs_avl = {
> +				[0] = {
> +					.num = ST_ACCEL_FS_AVL_2G,
> +					.value = ST_ACCEL_2_FS_AVL_2_VAL,
> +					.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
> +				},
> +				[1] = {
> +					.num = ST_ACCEL_FS_AVL_4G,
> +					.value = ST_ACCEL_2_FS_AVL_4_VAL,
> +					.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
> +				},
> +				[2] = {
> +					.num = ST_ACCEL_FS_AVL_6G,
> +					.value = ST_ACCEL_2_FS_AVL_6_VAL,
> +					.gain = ST_ACCEL_2_FS_AVL_6_GAIN,
> +				},
> +				[3] = {
> +					.num = ST_ACCEL_FS_AVL_8G,
> +					.value = ST_ACCEL_2_FS_AVL_8_VAL,
> +					.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
> +				},
> +				[4] = {
> +					.num = ST_ACCEL_FS_AVL_16G,
> +					.value = ST_ACCEL_2_FS_AVL_16_VAL,
> +					.gain = ST_ACCEL_2_FS_AVL_16_GAIN,
> +				},
> +			},
> +		},
> +		.drdy_irq = {
> +			.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
> +			.mask = ST_ACCEL_2_DRDY_IRQ_MASK,
> +		},
> +		.bdu = {
> +			.addr = ST_ACCEL_2_BDU_ADDR,
> +			.mask = ST_ACCEL_2_BDU_MASK,
> +		},
> +		.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
> +	},
> +	{
> +		.wai = ST_ACCEL_3_WAI_EXP,
> +		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +		.odr = {
> +			.addr = ST_ACCEL_3_ODR_ADDR,
> +			.mask = ST_ACCEL_3_ODR_MASK,
> +			.num_bit = ST_ACCEL_3_ODR_N_BIT,
> +			.odr_avl = {
> +				[0] = {
> +					.hz = ST_ACCEL_ODR_AVL_50HZ,
> +					.value = ST_ACCEL_3_ODR_AVL_50HZ_VAL,
> +				},
> +				[1] = {
> +					.hz = ST_ACCEL_ODR_AVL_100HZ,
> +					.value = ST_ACCEL_3_ODR_AVL_100HZ_VAL,
> +				},
> +				[2] = {
> +					.hz = ST_ACCEL_ODR_AVL_400HZ,
> +					.value = ST_ACCEL_3_ODR_AVL_400HZ_VAL,
> +				},
> +				[3] = {
> +					.hz = ST_ACCEL_ODR_AVL_1000HZ,
> +					.value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL,
> +				},
> +			},
> +		},
> +		.pw = {
> +			.addr = ST_ACCEL_3_PW_ADDR,
> +			.mask = ST_ACCEL_3_PW_MASK,
> +			.num_bit = ST_ACCEL_3_PW_N_BIT,
> +			.value_on = ST_ACCEL_DEFAULT_POWER_ON_VALUE,
> +			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +		},
> +		.enable_axis = {
> +			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +		},
> +		.fs = {
> +			.addr = ST_ACCEL_3_FS_ADDR,
> +			.mask = ST_ACCEL_3_FS_MASK,
> +			.num_bit = ST_ACCEL_3_FS_N_BIT,
> +			.fs_avl = {
> +				[0] = {
> +					.num = ST_ACCEL_FS_AVL_2G,
> +					.value = ST_ACCEL_3_FS_AVL_2_VAL,
> +					.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
> +				},
> +				[1] = {
> +					.num = ST_ACCEL_FS_AVL_4G,
> +					.value = ST_ACCEL_3_FS_AVL_4_VAL,
> +					.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
> +				},
> +				[2] = {
> +					.num = ST_ACCEL_FS_AVL_8G,
> +					.value = ST_ACCEL_3_FS_AVL_8_VAL,
> +					.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
> +				},
> +			},
> +		},
> +		.bdu = {
> +			.addr = ST_ACCEL_3_BDU_ADDR,
> +			.mask = ST_ACCEL_3_BDU_MASK,
> +		},
> +		.drdy_irq = {
> +			.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
> +			.mask = ST_ACCEL_3_DRDY_IRQ_MASK,
> +		},
> +		.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
> +	},
> +	{
> +		.wai = ST_ACCEL_4_WAI_EXP,
> +		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +		.odr = {
> +			.addr = ST_ACCEL_4_ODR_ADDR,
> +			.mask = ST_ACCEL_4_ODR_MASK,
> +			.num_bit = ST_ACCEL_4_ODR_N_BIT,
> +			.odr_avl = {
> +				[0] = {
> +					.hz = ST_ACCEL_ODR_AVL_3HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_3HZ_VAL,
> +				},
> +				[1] = {
> +					.hz = ST_ACCEL_ODR_AVL_6HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_6HZ_VAL,
> +				},
> +				[2] = {
> +					.hz = ST_ACCEL_ODR_AVL_12HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_12HZ_VAL,
> +				},
> +				[3] = {
> +					.hz = ST_ACCEL_ODR_AVL_25HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_25HZ_VAL,
> +				},
> +				[4] = {
> +					.hz = ST_ACCEL_ODR_AVL_50HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_50HZ_VAL,
> +				},
> +				[5] = {
> +					.hz = ST_ACCEL_ODR_AVL_100HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_100HZ_VAL,
> +				},
> +				[6] = {
> +					.hz = ST_ACCEL_ODR_AVL_200HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_200HZ_VAL,
> +				},
> +				[7] = {
> +					.hz = ST_ACCEL_ODR_AVL_400HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_400HZ_VAL,
> +				},
> +				[8] = {
> +					.hz = ST_ACCEL_ODR_AVL_800HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_800HZ_VAL,
> +				},
> +				[9] = {
> +					.hz = ST_ACCEL_ODR_AVL_1600HZ,
> +					.value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL,
> +				},
> +			},
> +		},
> +		.pw = {
> +			.addr = ST_ACCEL_4_ODR_ADDR,
> +			.mask = ST_ACCEL_4_ODR_MASK,
> +			.num_bit = ST_ACCEL_4_ODR_N_BIT,
> +			.value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +		},
> +		.enable_axis = {
> +			.addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +			.mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +		},
> +		.fs = {
> +			.addr = ST_ACCEL_4_FS_ADDR,
> +			.mask = ST_ACCEL_4_FS_MASK,
> +			.num_bit = ST_ACCEL_4_FS_N_BIT,
> +			.fs_avl = {
> +				[0] = {
> +					.num = ST_ACCEL_FS_AVL_2G,
> +					.value = ST_ACCEL_4_FS_AVL_2_VAL,
> +					.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
> +				},
> +				[1] = {
> +					.num = ST_ACCEL_FS_AVL_4G,
> +					.value = ST_ACCEL_4_FS_AVL_4_VAL,
> +					.gain = ST_ACCEL_4_FS_AVL_4_GAIN,
> +				},
> +				[2] = {
> +					.num = ST_ACCEL_FS_AVL_6G,
> +					.value = ST_ACCEL_4_FS_AVL_6_VAL,
> +					.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
> +				},
> +				[3] = {
> +					.num = ST_ACCEL_FS_AVL_8G,
> +					.value = ST_ACCEL_4_FS_AVL_8_VAL,
> +					.gain = ST_ACCEL_4_FS_AVL_8_GAIN,
> +				},
> +				[4] = {
> +					.num = ST_ACCEL_FS_AVL_16G,
> +					.value = ST_ACCEL_4_FS_AVL_16_VAL,
> +					.gain = ST_ACCEL_4_FS_AVL_16_GAIN,
> +				},
> +			},
> +		},
> +		.bdu = {
> +			.addr = ST_ACCEL_4_BDU_ADDR,
> +			.mask = ST_ACCEL_4_BDU_MASK,
> +		},
> +		.drdy_irq = {
> +			.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
> +			.mask = ST_ACCEL_4_DRDY_IRQ_MASK,
> +			.ig1 = {
> +				.en_addr = ST_ACCEL_4_IG1_EN_ADDR,
> +				.en_mask = ST_ACCEL_4_IG1_EN_MASK,
> +			},
> +		},
> +		.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
> +	},
> +};
> +
> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev,
> u8 reg_addr,
> +						u8 mask, short num_bit, u8 data)
> +{
> +	int err;
> +	u8 prev_data;
> +	u8 new_data;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = adata->read_byte(adata, reg_addr, &prev_data);
> +	if (err < 0)
> +		goto st_accel_write_data_with_mask_error;
> +
> +	new_data = ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +	err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_accel_write_data_with_mask_error:
> +	return err;
> +}
> +
> +static int st_accel_match_odr(const struct st_accel_sensors *sensor,
> +		unsigned int odr, struct st_accel_odr_available *odr_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
> +		if (sensor->odr.odr_avl[i].hz == odr) {
> +			odr_out->hz = sensor->odr.odr_avl[i].hz;
> +			odr_out->value = sensor->odr.odr_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int st_accel_match_fs(const struct st_accel_sensors *sensor,
> +		unsigned int fs, struct st_accel_fullscale_available *fs_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +		if (sensor->fs.fs_avl[i].num == fs) {
> +			fs_out->num = sensor->fs.fs_avl[i].num;
> +			fs_out->gain = sensor->fs.fs_avl[i].gain;
> +			fs_out->value = sensor->fs.fs_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int st_accel_match_scale(const struct st_accel_sensors *sensor,
> +			int scale, struct st_accel_fullscale_available *fs_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +		if (sensor->fs.fs_avl[i].gain == scale) {
> +			fs_out->num = sensor->fs.fs_avl[i].num;
> +			fs_out->gain = sensor->fs.fs_avl[i].gain;
> +			fs_out->value = sensor->fs.fs_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
> +{
> +	int err;
> +	struct st_accel_data *adata;
> +
> +	adata = iio_priv(indio_dev);
> +	if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
> +		err = st_accel_write_data_with_mask(indio_dev,
> +			st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
> +			st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
> +		err = st_accel_write_data_with_mask(indio_dev,
> +		st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
> +		st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	err = st_accel_write_data_with_mask(indio_dev,
> +		st_accel_sensors[adata->index].drdy_irq.addr,
> +		st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
> +	if (err < 0)
> +		goto st_accel_set_dataready_irq_error;
> +
> +st_accel_set_dataready_irq_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_accel_set_dataready_irq);
> +
> +static int st_accel_set_bdu(struct iio_dev *indio_dev,
> +				const struct st_accel_bdu *bdu, bool value)
> +{
> +	return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
> +								1, (u8)value);
> +}
> +
> +static int st_accel_set_odr(struct iio_dev *indio_dev,
> +				struct st_accel_odr_available *odr_available)
> +{
> +	int err;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	if ((st_accel_sensors[adata->index].odr.addr ==
> +		st_accel_sensors[adata->index].pw.addr) &&
Alignment is rather messed up here I think...
> +			(st_accel_sensors[adata->index].odr.mask ==
> +				st_accel_sensors[adata->index].pw.mask)) {
> +		if (adata->enabled == true) {
> +			err = st_accel_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].odr.addr,
> +				st_accel_sensors[adata->index].odr.mask,
> +				st_accel_sensors[adata->index].odr.num_bit,
> +				odr_available->value);
> +			if (err < 0)
> +				goto sc_accel_set_odr_error;
> +		} else {
> +			adata->odr = odr_available->hz;
> +			err = 0;
> +		}
> +	} else {
> +		err = st_accel_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].odr.addr,
> +				st_accel_sensors[adata->index].odr.mask,
> +				st_accel_sensors[adata->index].odr.num_bit,
> +				odr_available->value);
> +		if (err < 0)
> +			goto sc_accel_set_odr_error;
> +	}
> +
> +sc_accel_set_odr_error:
> +	return err;
> +}
> +
> +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
> +{
> +	int err;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = st_accel_write_data_with_mask(indio_dev,
> +			st_accel_sensors[adata->index].enable_axis.addr,
> +			st_accel_sensors[adata->index].enable_axis.mask,
> +			ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
> +
> +	return err;
> +}
> +
> +static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
> +{
> +	int err;
> +	bool found;
> +	u8 tmp_value;
> +	struct st_accel_odr_available odr_out;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
switch on a boolean seems overly complex. I'd just do
and if / else pair
> +	switch (enable) {
> +	case true:
> +		found = false;
> +		tmp_value = st_accel_sensors[adata->index].pw.value_on;
> +		if ((st_accel_sensors[adata->index].odr.addr ==
> +				st_accel_sensors[adata->index].pw.addr) &&
> +			(st_accel_sensors[adata->index].odr.mask ==
> +				st_accel_sensors[adata->index].pw.mask)) {
> +			err = st_accel_match_odr(
> +				&st_accel_sensors[adata->index], adata->odr,
> +					&odr_out);
> +			if (err < 0)
> +				goto set_enable_error;
> +			tmp_value = odr_out.value;
> +			found = true;
> +		}
> +		err = st_accel_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].pw.addr,
> +				st_accel_sensors[adata->index].pw.mask,
> +				st_accel_sensors[adata->index].pw.num_bit,
> +				tmp_value);
> +		if (err < 0)
> +			goto set_enable_error;
> +		adata->enabled = true;
> +		if (found)
> +			adata->odr = odr_out.hz;
> +		break;
> +	case false:
> +		err = st_accel_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].pw.addr,
> +				st_accel_sensors[adata->index].pw.mask,
> +				st_accel_sensors[adata->index].pw.num_bit,
> +				st_accel_sensors[adata->index].pw.value_off);
> +		if (err < 0)
> +			goto set_enable_error;
> +		adata->enabled = false;
> +		break;
> +	default:
default on a boolean switch?
> +		err = -EINVAL;
> +		goto set_enable_error;
> +	}
> +
> +set_enable_error:
> +	return err;
> +}
> +
> +static int st_accel_set_fullscale(struct iio_dev *indio_dev,
> +				struct st_accel_fullscale_available *fs_avl)
> +{
> +	int err;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = st_accel_write_data_with_mask(indio_dev,
> +				st_accel_sensors[adata->index].fs.addr,
> +				st_accel_sensors[adata->index].fs.mask,
> +				st_accel_sensors[adata->index].fs.num_bit,
> +				fs_avl->value);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	adata->fullscale = fs_avl->num;
> +	adata->gain = fs_avl->gain;
> +	return err;
> +
> +st_accel_set_fullscale_error:
> +	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +	return err;
> +}
> +
> +static int st_accel_read_raw(struct iio_dev *indio_dev,
> +			struct iio_chan_spec const *ch, int *val,
> +							int *val2, long mask)
> +{
> +	int err;
> +	u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&indio_dev->mlock);
> +		if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +			err = -EBUSY;
> +			goto read_error;
> +		} else {
> +			if (!adata->enabled) {
> +				err = -EIO;
> +				goto read_error;
> +			} else {
> +				err = adata->read_multiple_byte(adata,
> +					ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
> +					outdata);
> +				if (err < 0)
> +					goto read_error;
> +
> +				*val = ((s16)(((s16)(outdata[1]) << 8)
> +					| outdata[0])) >> ch->scan_type.shift;
Why not standard endian conversion functions?
> +			}
> +		}
> +		mutex_unlock(&indio_dev->mlock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = adata->gain;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +read_error:
> +	mutex_unlock(&indio_dev->mlock);
> +	return err;
> +}
> +
> +static int st_accel_write_raw(struct iio_dev *indio_dev,
> +		struct iio_chan_spec const *chan, int val, int val2, long mask)
> +{
> +	int err;
> +	struct st_accel_fullscale_available fs_out;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		err = st_accel_match_scale(&st_accel_sensors[adata->index],
> +								val2, &fs_out);
> +		if (err < 0)
> +			goto write_error;
> +
> +		err = st_accel_set_fullscale(indio_dev, &fs_out);
> +		break;
> +	default:
> +		err = -EINVAL;
> +	}
> +
> +write_error:
> +	return err;
> +}
> +
> +static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
> +{
> +	int i;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
> +		if (st_accel_sensors[i].wai == wai)
> +			break;
> +	}
> +	if (i == ARRAY_SIZE(st_accel_sensors))
> +		goto check_device_error;
> +
> +	adata->index = i;
> +
> +	return i;
> +
> +check_device_error:
> +	dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
> +	return -ENODEV;
> +}
> +
> +static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
> +								u8 *value)
> +{
> +	int ret;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	ret = adata->read_byte(adata, reg_addr, value);
> +	if (ret < 0)
> +		goto read_byte_wai_error;
> +
> +	return 0;
> +
> +read_byte_wai_error:
> +	dev_err(&indio_dev->dev,
> +			"failed to read WhoAmI (register 0x%x).\n", reg_addr);
> +	return -EIO;
> +}
> +
> +static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	int err;
> +	unsigned int freq;
> +	struct st_accel_odr_available odr_out;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = kstrtoint(buf, 10, &freq);
> +	if (err < 0)
> +		goto conversion_error;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	err = st_accel_match_odr(&st_accel_sensors[adata->index],
> +								freq, &odr_out);
> +	if (err < 0)
> +		goto st_accel_sysfs_set_sampling_frequency_error;
> +
> +	err = st_accel_set_odr(indio_dev, &odr_out);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev,
> +			"failed to set sampling frequency to %d.\n", freq);
> +		goto st_accel_sysfs_set_sampling_frequency_error;
> +	}
> +	adata->odr = odr_out.hz;
> +
> +st_accel_sysfs_set_sampling_frequency_error:
> +	mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +	return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%d\n", adata->odr);
> +}
> +
> +static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	int err;
> +	bool powerdown;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> +	err = strtobool(buf, &powerdown);
> +	if (err < 0)
> +		goto set_enable_error;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	err = st_accel_set_enable(indio_dev, ~powerdown);
> +	if (err < 0)
> +		dev_err(&indio_dev->dev,
> +			"failed to set powerdown to %d.\n", (int)(powerdown));
> +	mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +	return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	return sprintf(buf, "%d\n", (int)(!adata->enabled));
> +}
> +
> +static ssize_t st_accel_sysfs_scale_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, len = 0;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
> +									i++) {
> +		if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
> +			break;
> +
> +		len += sprintf(buf+len, "0.%06u ",
> +			st_accel_sensors[adata->index].fs.fs_avl[i].gain);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len-1] = '\n';
> +
> +	return len;
> +}
> +
> +static ssize_t st_accel_sysfs_sampling_frequency_available(struct device *dev,
> +				struct device_attribute *attr, char *buf)
> +{
> +	int i, len = 0;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
> +									i++) {
> +		if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
> +			break;
> +
> +		len += sprintf(buf+len, "%d ",
> +			st_accel_sensors[adata->index].odr.odr_avl[i].hz);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
spaces around -
> +	buf[len-1] = '\n';
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +		st_accel_sysfs_sampling_frequency_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
> +				st_accel_sysfs_scale_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
> +		st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown , 0);
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +			st_accel_sysfs_get_sampling_frequency,
> +					st_accel_sysfs_set_sampling_frequency);
> +
> +static struct attribute *st_accel_attributes[] = {
> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
> +	&iio_dev_attr_powerdown.dev_attr.attr,
> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group st_accel_attribute_group = {
> +	.attrs = st_accel_attributes,
> +};
> +
> +static const struct iio_info acc_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &st_accel_attribute_group,
> +	.read_raw = &st_accel_read_raw,
> +	.write_raw = &st_accel_write_raw,
> +};
> +
> +static int st_accel_init_sensor(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct st_accel_odr_available odr_out;
> +	struct st_accel_fullscale_available fs_out;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	err = st_accel_set_enable(indio_dev, false);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +	if (err < 0)
> +		goto init_error;
> +
> +	st_accel_match_fs(&st_accel_sensors[adata->index],
> +						adata->fullscale, &fs_out);
> +	err = st_accel_set_fullscale(indio_dev, &fs_out);
> +	if (err < 0)
> +		goto init_error;
> +
> +	st_accel_match_odr(&st_accel_sensors[adata->index],
> +							adata->odr, &odr_out);
> +	err = st_accel_set_odr(indio_dev, &odr_out);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_accel_set_bdu(indio_dev,
> +				&st_accel_sensors[adata->index].bdu, true);
> +
> +init_error:
> +	return err;
> +}
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
> +{
> +	int err;
> +	u8 wai;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +	struct st_accel_platform_data *pdata;
> +
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &acc_info;
> +
> +	err = st_accel_get_wai_device(indio_dev,
> +					ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
> +	if (err < 0)
> +		goto st_accel_iio_probe_error;
> +
> +	err = st_accel_check_device_list(indio_dev, wai);
> +	if (err < 0)
> +		goto st_accel_iio_probe_error;
> +
> +	adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
> +	indio_dev->channels = st_accel_sensors[adata->index].ch;
> +	indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
> +	pdata = adata->dev->platform_data;
> +	if (!pdata)
> +		pdata = (struct st_accel_platform_data *)
> +							&st_accel_default_pdata;
> +
> +	adata->fullscale = pdata->fullscale;
> +	adata->odr = pdata->sampling_frequency;
> +
> +	err = st_accel_init_sensor(indio_dev);
> +	if (err < 0)
> +		goto st_accel_iio_probe_error;
> +
> +	err = st_accel_allocate_ring(indio_dev);
> +	if (err < 0)
> +		goto st_accel_iio_probe_error;
> +
> +	if (irq > 0) {
> +		err = st_accel_probe_trigger(indio_dev, irq);
> +		if (err < 0)
> +			goto acc_probe_trigger_error;
> +	}
> +
> +	err = iio_device_register(indio_dev);
> +	if (err)
> +		goto iio_device_register_error;
> +
> +	return err;
> +
> +iio_device_register_error:
> +	st_accel_remove_trigger(indio_dev, irq);
> +acc_probe_trigger_error:
> +	st_accel_deallocate_ring(indio_dev);
> +st_accel_iio_probe_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_accel_iio_probe);
> +
> +void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
> +{
> +	iio_device_unregister(indio_dev);
> +	st_accel_remove_trigger(indio_dev, irq);
> +	st_accel_deallocate_ring(indio_dev);
> +	iio_device_free(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
> new file mode 100644
> index 0000000..a9fec2e
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -0,0 +1,129 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
1 line is enought here.
> +
> +#define ST_ACCEL_I2C_MULTIREAD			0x80
> +
> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
> +						u8 reg_addr, u8 *res_byte)
> +{
> +	int err;
> +
> +	err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +	if (err < 0)
> +		goto st_accel_i2c_read_byte_error;
> +
> +	*res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +	return err;
> +}
> +
> +static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
> +						u8 reg_addr, int len, u8 *data)
> +{
> +	if (adata->multiread_bit == true)
> +		reg_addr |= ST_ACCEL_I2C_MULTIREAD;
> +
> +	return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +							reg_addr, len, data);
> +}
> +
> +static int st_accel_i2c_write_byte(struct st_accel_data *adata,
> +							u8 reg_addr, u8 data)
> +{
> +	return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
> +								reg_addr, data);
> +}
> +
> +static int __devinit st_accel_i2c_probe(struct i2c_client *client,
> +				const struct i2c_device_id *id)
> +{
> +	struct iio_dev *indio_dev;
> +	struct st_accel_data *adata;
> +	int err;
> +
> +	indio_dev = iio_device_alloc(sizeof(*adata));
> +	if (indio_dev == NULL) {
> +		err = -ENOMEM;
> +		goto iio_device_alloc_error;
> +	}
> +
> +	adata = iio_priv(indio_dev);
> +	adata->dev = &client->dev;
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->name = client->name;
> +
> +	adata->read_byte = st_accel_i2c_read_byte;
> +	adata->write_byte = st_accel_i2c_write_byte;
> +	adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
> +
> +	err = st_accel_iio_probe(indio_dev, client->irq);
> +	if (err < 0)
> +		goto acc_iio_default_error;
> +
> +	return 0;
> +
> +acc_iio_default_error:
> +	iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +	return err;
> +}
> +
> +static int __devexit st_accel_i2c_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +
> +	st_accel_iio_remove(indio_dev, client->irq);
blank line here would be good.
> +	return 0;
> +}
> +
> +static const struct i2c_device_id st_accel_id_table[] = {
> +	{ LSM303DLH_ACCEL_DEV_NAME },
> +	{ LSM303DLHC_ACCEL_DEV_NAME },
> +	{ LIS3DH_ACCEL_DEV_NAME },
> +	{ LSM330D_ACCEL_DEV_NAME },
> +	{ LSM330DL_ACCEL_DEV_NAME },
> +	{ LSM330DLC_ACCEL_DEV_NAME },
> +	{ LSM303D_ACCEL_DEV_NAME },
> +	{ LSM9DS0_ACCEL_DEV_NAME },
> +	{ LIS331DLH_ACCEL_DEV_NAME },
> +	{ LSM303DL_ACCEL_DEV_NAME },
> +	{ LSM303DLM_ACCEL_DEV_NAME },
> +	{ LSM330_ACCEL_DEV_NAME },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
> +
> +static struct i2c_driver st_accel_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "st-accel-i2c",
> +	},
> +	.probe = st_accel_i2c_probe,
> +	.remove = __devexit_p(st_accel_i2c_remove),
> +	.id_table = st_accel_id_table,
> +};
> +module_i2c_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..26b8ff5
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,188 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ		0x80;
> +#define ACC_SPI_MULTIREAD	0xc0
> +
> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
> +						u8 reg_addr, u8 *res_byte)
> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = &tx,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
}, {
> +		{
> +			.rx_buf = res_byte,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		}
> +	};
> +
> +	tx = reg_addr | ACC_SPI_READ;
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +
> +	return err;
> +}
> +
> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
> +						u8 reg_addr, int len, u8 *data)
> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = &tx,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
}, {

> +		{
> +			.rx_buf = data,
> +			.bits_per_word = 8,
> +			.len = len,
> +		}
> +	};
> +
> +	if (adata->multiread_bit == true)
> +		tx = reg_addr | ACC_SPI_MULTIREAD;
> +	else
> +		tx = reg_addr | ACC_SPI_READ;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +	if (err)
> +		goto acc_spi_read_multiple_byte_error;
> +
> +	return len;
> +
> +acc_spi_read_multiple_byte_error:
> +	return err;
> +}
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +							u8 reg_addr, u8 data)
> +{
> +	struct spi_message msg;
> +	int err;
> +	u8 tx[2];
> +
No benefit to array of structures so just have one of them.
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +		}
> +	};
> +
> +	tx[0] = reg_addr;
> +	tx[1] = data;
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	err = spi_sync(to_spi_device(adata->dev), &msg);
> +
> +	return err;
> +}
> +
> +static int __devinit st_accel_spi_probe(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev;
> +	struct st_accel_data *adata;
> +	int err;
> +
> +	indio_dev = iio_device_alloc(sizeof(*adata));
> +	if (indio_dev == NULL) {
> +		err = -ENOMEM;
> +		goto iio_device_alloc_error;
> +	}
> +
> +	adata = iio_priv(indio_dev);
> +	adata->dev = &spi->dev;
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi->modalias;
> +
> +	adata->read_byte = st_accel_spi_read_byte;
> +	adata->write_byte = st_accel_spi_write_byte;
> +	adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
> +
> +	err = st_accel_iio_probe(indio_dev, spi->irq);
> +	if (err < 0)
> +		goto acc_iio_default_error;
> +
> +	return 0;
> +
> +acc_iio_default_error:
> +	iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +	return err;
> +}
> +
> +static int __devexit st_accel_spi_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +
> +	st_accel_iio_remove(indio_dev, spi->irq);
Additional blank line would be good here.
> +	return 0;
> +}
> +
> +static const struct spi_device_id st_accel_id_table[] = {
> +	{ LSM303DLH_ACCEL_DEV_NAME },
> +	{ LSM303DLHC_ACCEL_DEV_NAME },
> +	{ LIS3DH_ACCEL_DEV_NAME },
> +	{ LSM330D_ACCEL_DEV_NAME },
> +	{ LSM330DL_ACCEL_DEV_NAME },
> +	{ LSM330DLC_ACCEL_DEV_NAME },
> +	{ LSM303D_ACCEL_DEV_NAME },
> +	{ LSM9DS0_ACCEL_DEV_NAME },
> +	{ LIS331DLH_ACCEL_DEV_NAME },
> +	{ LSM303DL_ACCEL_DEV_NAME },
> +	{ LSM303DLM_ACCEL_DEV_NAME },
> +	{ LSM330_ACCEL_DEV_NAME },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(spi, st_accel_id_table);
> +
> +static struct spi_driver st_accel_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "st-accel-spi",
> +	},
> +	.probe = st_accel_spi_probe,
> +	.remove = __devexit_p(st_accel_spi_remove),
> +	.id_table = st_accel_id_table,
> +};
> +module_spi_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_trigger.c
> b/drivers/iio/accel/st_accel_trigger.c
> new file mode 100644
> index 0000000..8bc970c
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_trigger.c
> @@ -0,0 +1,84 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
Excess blank lines.
> +
> +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *indio_dev = trig->private_data;
> +	return st_accel_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops st_accel_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &st_accel_trig_acc_set_state,
> +};
> +
> +int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +	int err;
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +	if (adata->trig == NULL) {
> +		err = -ENOMEM;
> +		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +		goto iio_trigger_alloc_error;
> +	}
> +
> +	err = request_threaded_irq(irq,
> +			iio_trigger_generic_data_rdy_poll,
> +			NULL,
> +			IRQF_TRIGGER_RISING,
> +			adata->trig->name,
> +			adata->trig);
> +	if (err)
> +		goto request_irq_error;
> +
> +	adata->trig->private_data = indio_dev;
> +	adata->trig->ops = &st_accel_trigger_ops;
> +	adata->trig->dev.parent = adata->dev;
> +
> +	err = iio_trigger_register(adata->trig);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +		goto iio_trigger_register_error;
> +	}
> +	indio_dev->trig = adata->trig;
> +
> +	return 0;
> +
> +iio_trigger_register_error:
> +	free_irq(irq, adata->trig);
> +request_irq_error:
> +	iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_accel_probe_trigger);
> +
> +void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +	struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +	iio_trigger_unregister(adata->trig);
> +	free_irq(irq, adata->trig);
> +	iio_trigger_free(adata->trig);
> +}
> +EXPORT_SYMBOL(st_accel_remove_trigger);
> diff --git a/include/linux/iio/accel/st_accel.h
> b/include/linux/iio/accel/st_accel.h
> new file mode 100644
> index 0000000..1efd29e
> --- /dev/null
> +++ b/include/linux/iio/accel/st_accel.h
> @@ -0,0 +1,121 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
The supported sensors don't really need to be listed here as they
are apparent elsewhere in the driver. Documentation like this
just tends to get out of date.
> +/*
> + * Supported sensors:
> + * LSM303DLH
> + * LSM303DLHC
> + * LIS3DH
> + * LSM330D
> + * LSM330DL
> + * LSM330DLC
> + * LSM303D
> + * LSM9DS0
> + * LIS331DLH
> + * LSM303DL
> + * LSM303DLM
> + * LSM330
> + *
> + */
> +
> +
> +#ifndef ST_ACCEL_H
> +#define ST_ACCEL_H
> +
> +#define LSM303DLH_ACCEL_DEV_NAME	"lsm303dlh_accel"
> +#define LSM303DLHC_ACCEL_DEV_NAME	"lsm303dlhc_accel"
> +#define LIS3DH_ACCEL_DEV_NAME		"lis3dh"
> +#define LSM330D_ACCEL_DEV_NAME		"lsm330d_accel"
> +#define LSM330DL_ACCEL_DEV_NAME		"lsm330dl_accel"
> +#define LSM330DLC_ACCEL_DEV_NAME	"lsm330dlc_accel"
> +#define LSM303D_ACCEL_DEV_NAME		"lsm303d"
> +#define LSM9DS0_ACCEL_DEV_NAME		"lsm9ds0"
> +#define LIS331DLH_ACCEL_DEV_NAME	"lis331dlh"
> +#define LSM303DL_ACCEL_DEV_NAME		"lsm303dl_accel"
> +#define LSM303DLM_ACCEL_DEV_NAME	"lsm303dlm_accel"
> +#define LSM330_ACCEL_DEV_NAME		"lsm330_accel"
> +
> +#define ST_ACCEL_NUMBER_ALL_CHANNELS	4
> +#define ST_ACCEL_NUMBER_DATA_CHANNELS	3
> +#define ST_ACCEL_BYTE_FOR_CHANNEL	2
> +#define ST_ACCEL_SCAN_X			0
> +#define ST_ACCEL_SCAN_Y			1
> +#define ST_ACCEL_SCAN_Z			2
> +
> +/**
> + * struct st_accel_data - ST accel device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the
> + *	st_accel_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [ms^2/LSB].
> + * @odr: Output data rate of the sensor [Hz].
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + *
Unecessary blank line.
> + */
> +
> +struct st_accel_data {
> +	struct device *dev;
> +	struct iio_trigger *trig;
> +
> +	bool enabled;
> +	bool multiread_bit;
> +
> +	short index;
> +
> +	char *buffer_data;
> +
> +	unsigned int fullscale;
> +	unsigned int gain;
> +	unsigned int odr;
> +
> +	int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
> +								u8 *res_byte);
> +	int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
> +	int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
> +							int len, u8 *data);
> +};
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
> +void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
> +void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +	return 0;
> +}
> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +	return;
> +}
> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +	return;
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCEL_H */
> diff --git a/include/linux/platform_data/st_accel_pdata.h
> b/include/linux/platform_data/st_accel_pdata.h
> new file mode 100644
> index 0000000..416489b
> --- /dev/null
> +++ b/include/linux/platform_data/st_accel_pdata.h
> @@ -0,0 +1,27 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
The general principal that it rarely makes sense to have
more than one blank line applies here.
> +
> +
> +#ifndef ST_ACCEL_PDATA_H
> +#define ST_ACCEL_PDATA_H
> +
> +
> +/**
> + * struct st_accel_platform_data - ST accel device platform data
> + * @fullscale: Value of fullscale used for the sensor.
> + * @sampling_frequency: Value of sampling frequency used for the sensor.
> + */
> +
> +struct st_accel_platform_data {
> +	int fullscale;
> +	int sampling_frequency;
> +};

What is the purpose of having these as platform data?

> +
> +#endif /* ST_ACCEL_PDATA_H */
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-05 21:28                                   ` Jonathan Cameron
@ 2012-11-06 11:11                                     ` Denis CIOCCA
  2012-11-12 17:10                                       ` Denis CIOCCA
  2012-11-06 11:14                                     ` Denis CIOCCA
  1 sibling, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-06 11:11 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan,

Thanks for your support!
I have applied your comments, only two point I would check better.



>> +} st_accel_sensors[] = {
>> +     {
>> +             .wai = ST_ACCEL_1_WAI_EXP,
>> +             .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
>> +             .odr = {
>> +                     .addr = ST_ACCEL_1_ODR_ADDR,
>> +                     .mask = ST_ACCEL_1_ODR_MASK,
>> +                     .num_bit = ST_ACCEL_1_ODR_N_BIT,
>> +                     .odr_avl = {
>    While sometimes c99 assignment helps with clarity - sometimes it just bloats the code.
>    Personally I'd do these as
>    .odr_avl = {{ST_ACCEL_ODR_AVL_1HZ, ST_ACCEL_1_ODR_AVL_1HZ_VAL},
>                {...}
> etc
>
> or for that matter take the view that the defines are largely pointless as they
> are only used here and
> just do
>       .odr_avl = {{1, 0x01},
>                   {10, 0x02},
> etc.
> Or maybe if it fits on the line
>     .odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest... Not sure.
>

In my first version I have used about this compact approach but 
Lars-Peter tell to me to use this style code:
 >I'd use
 > .odr_avl = {
 >        [0] = {
 >                ...
 >        },
 >        [1] = {
 >                ...
 >
 >Makes things a bit more readable in my opinion.

Ok for me it is not a problem but you must tell me one definitive style 
please. I think this style is more cleanly, though are necessary more rows.



>> +/**
>> + * struct st_accel_platform_data - ST accel device platform data
>> + * @fullscale: Value of fullscale used for the sensor.
>> + * @sampling_frequency: Value of sampling frequency used for the sensor.
>> + */
>> +
>> +struct st_accel_platform_data {
>> +     int fullscale;
>> +     int sampling_frequency;
>> +};
>
> What is the purpose of having these as platform data?
>

For now the purpose is to set only the initial values for fullscale and 
sampling_frequency but I think it's good thing include the mapping of 
axis. You think it's necessary to remove this for now?



Thanks,

Denis










^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-05 21:28                                   ` Jonathan Cameron
  2012-11-06 11:11                                     ` Denis CIOCCA
@ 2012-11-06 11:14                                     ` Denis CIOCCA
  1 sibling, 0 replies; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-06 11:14 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

sorry the patch...

Denis

 From 89a783207e72709d2aa137ab805102b8675e2fe8 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
  drivers/iio/accel/Kconfig                        |   37 +
  drivers/iio/accel/Makefile                       |    6 +
  drivers/iio/accel/st_accel_buffer.c              |  176 +++
  drivers/iio/accel/st_accel_core.c                | 1276
++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c                 |  128 +++
  drivers/iio/accel/st_accel_spi.c                 |  186 ++++
  drivers/iio/accel/st_accel_trigger.c             |   83 ++
  include/linux/iio/accel/st_accel.h               |  102 ++
  include/linux/platform_data/st_accel_pdata.h     |   25 +
  10 files changed, 2026 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h
  create mode 100644 include/linux/platform_data/st_accel_pdata.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..af5d333
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,176 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+       int len =3D 0, i, n =3D 0;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, rx_array=
);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       rx_array);
+               } else {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       rx_array[2] =3D rx_array[4];
+                       rx_array[3] =3D rx_array[5];
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       rx_array);
+               break;
+       default:
+               break;
+       }
+
+       return len;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int ret, i, scan_count;
+       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS=
];
+       s16 *data =3D (s16 *)buf;
+
+       ret =3D st_accel_read_all(indio_dev, rx_array);
+       if (ret < 0)
+               return ret;
+
+       scan_count =3D bitmap_weight(indio_dev->active_scan_mask,
+                                                       indio_dev->maskleng=
th);
+
+       for (i =3D 0; i < scan_count; i++)
+               memcpy(data+i, &rx_array[i*2], sizeof(s16));
+
+       return i*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       int len =3D 0;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       indio_dev->scan_timestamp =3D true;
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..8a3ec5a
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1276 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/platform_data/st_accel_pdata.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* ODR */
+#define ST_ACCEL_ODR_AVL_1HZ                   1
+#define ST_ACCEL_ODR_AVL_3HZ                   3
+#define ST_ACCEL_ODR_AVL_6HZ                   6
+#define ST_ACCEL_ODR_AVL_10HZ                  10
+#define ST_ACCEL_ODR_AVL_12HZ                  12
+#define ST_ACCEL_ODR_AVL_25HZ                  25
+#define ST_ACCEL_ODR_AVL_50HZ                  50
+#define ST_ACCEL_ODR_AVL_100HZ                 100
+#define ST_ACCEL_ODR_AVL_200HZ                 200
+#define ST_ACCEL_ODR_AVL_400HZ                 400
+#define ST_ACCEL_ODR_AVL_800HZ                 800
+#define ST_ACCEL_ODR_AVL_1000HZ                        1000
+#define ST_ACCEL_ODR_AVL_1600HZ                        1600
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_accel_platform_data st_accel_default_pdata =3D {
+       .fullscale =3D ST_ACCEL_FS_AVL_2G,
+       .sampling_frequency =3D ST_ACCEL_ODR_AVL_100HZ,
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_1HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_10HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_10HZ_=
VAL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_1_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_3HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_6HZ_V=
AL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_12HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [8] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_800HZ=
_VAL,
+                               },
+                               [9] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_2_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1000HZ,
+                                       .value =3D ST_ACCEL_3_ODR_AVL_1000H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               [0] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_3HZ_V=
AL,
+                               },
+                               [1] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_6HZ_V=
AL,
+                               },
+                               [2] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_12HZ_=
VAL,
+                               },
+                               [3] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_25HZ_=
VAL,
+                               },
+                               [4] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_50HZ_=
VAL,
+                               },
+                               [5] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_100HZ=
_VAL,
+                               },
+                               [6] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_200HZ=
_VAL,
+                               },
+                               [7] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_400HZ=
_VAL,
+                               },
+                               [8] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_800HZ=
_VAL,
+                               },
+                               [9] =3D {
+                                       .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+                                       .value =3D ST_ACCEL_4_ODR_AVL_1600H=
Z_VAL,
+                               },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err;
+       u8 prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D adata->read_byte(adata, reg_addr, &prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask=
));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+                       int scale, struct st_accel_fullscale_available *fs_=
out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, bool value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+               (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto read_error;
+                       } else {
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)cpu_to_le16p((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_accel_match_scale(&st_accel_sensors[adata->index=
],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set sampling frequency to %d.\n", freq);
+               goto st_accel_sysfs_set_sampling_frequency_error;
+       }
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, ~powerdown);
+       if (err < 0)
+               dev_err(&indio_dev->dev,
+                       "failed to set powerdown to %d.\n", (int)(powerdown=
));
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+                               st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown =
, 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+       .write_raw =3D &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                               &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+       struct st_accel_platform_data *pdata;
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai)=
;
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+       pdata =3D adata->dev->platform_data;
+       if (!pdata)
+               pdata =3D (struct st_accel_platform_data *)
+                                                       &st_accel_default_p=
data;
+
+       adata->fullscale =3D pdata->fullscale;
+       adata->odr =3D pdata->sampling_frequency;
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_accel_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev, irq);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..f4680a2
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_accel_i2c_read_byte_error:
+       return err;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_accel_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D __devexit_p(st_accel_i2c_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..1f7f167
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,186 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D res_byte,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               }
+       };
+
+       tx =3D reg_addr | ACC_SPI_READ;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+       return err;
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       if (adata->multiread_bit =3D=3D true)
+               tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx =3D reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_multiple_byte_error;
+
+       return len;
+
+acc_spi_read_multiple_byte_error:
+       return err;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2];
+
+       struct spi_transfer xfers =3D {
+               .tx_buf =3D tx,
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_accel_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D __devexit_p(st_accel_spi_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..68813f7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..c5a0edc
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [ms^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
irq)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
diff --git a/include/linux/platform_data/st_accel_pdata.h
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..8434ca3
--- /dev/null
+++ b/include/linux/platform_data/st_accel_pdata.h
@@ -0,0 +1,25 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_PDATA_H
+#define ST_ACCEL_PDATA_H
+
+/**
+ * struct st_accel_platform_data - ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */
+
+struct st_accel_platform_data {
+       int fullscale;
+       int sampling_frequency;
+};
+
+#endif /* ST_ACCEL_PDATA_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-06 11:11                                     ` Denis CIOCCA
@ 2012-11-12 17:10                                       ` Denis CIOCCA
  2012-11-12 18:48                                         ` Jonathan Cameron
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-12 17:10 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan,

can you please provide me one direction to modify the part in question=20
about the style of struct, in such a way I can do some modifications.

Thanks,

Denis Ciocca



On 11/06/2012 12:11 PM, Denis Ciocca wrote:
> Hi Jonathan,
>
> Thanks for your support!
> I have applied your comments, only two point I would check better.
>
>
>
>>> +} st_accel_sensors[] =3D {
>>> +     {
>>> +             .wai =3D ST_ACCEL_1_WAI_EXP,
>>> +             .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
>>> +             .odr =3D {
>>> +                     .addr =3D ST_ACCEL_1_ODR_ADDR,
>>> +                     .mask =3D ST_ACCEL_1_ODR_MASK,
>>> +                     .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
>>> +                     .odr_avl =3D {
>>    While sometimes c99 assignment helps with clarity - sometimes it
>> just bloats the code.
>>    Personally I'd do these as
>>    .odr_avl =3D {{ST_ACCEL_ODR_AVL_1HZ, ST_ACCEL_1_ODR_AVL_1HZ_VAL},
>>                {...}
>> etc
>>
>> or for that matter take the view that the defines are largely
>> pointless as they
>> are only used here and
>> just do
>>       .odr_avl =3D {{1, 0x01},
>>                   {10, 0x02},
>> etc.
>> Or maybe if it fits on the line
>>     .odr_avl =3D {{ .hz =3D 1, .value =3D 0x01 } might be the clearest..=
.
>> Not sure.
>>
>
> In my first version I have used about this compact approach but
> Lars-Peter tell to me to use this style code:
>  >I'd use
>  > .odr_avl =3D {
>  >        [0] =3D {
>  >                ...
>  >        },
>  >        [1] =3D {
>  >                ...
>  >
>  >Makes things a bit more readable in my opinion.
>
> Ok for me it is not a problem but you must tell me one definitive style
> please. I think this style is more cleanly, though are necessary more row=
s.
>
>
>
>>> +/**
>>> + * struct st_accel_platform_data - ST accel device platform data
>>> + * @fullscale: Value of fullscale used for the sensor.
>>> + * @sampling_frequency: Value of sampling frequency used for the
>>> sensor.
>>> + */
>>> +
>>> +struct st_accel_platform_data {
>>> +     int fullscale;
>>> +     int sampling_frequency;
>>> +};
>>
>> What is the purpose of having these as platform data?
>>
>
> For now the purpose is to set only the initial values for fullscale and
> sampling_frequency but I think it's good thing include the mapping of
> axis. You think it's necessary to remove this for now?
>
>
>
> Thanks,
>
> Denis
>
>
>
>
>
>
>
>
>
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-12 17:10                                       ` Denis CIOCCA
@ 2012-11-12 18:48                                         ` Jonathan Cameron
  2012-11-13 15:38                                           ` Denis CIOCCA
  0 siblings, 1 reply; 42+ messages in thread
From: Jonathan Cameron @ 2012-11-12 18:48 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/12/2012 05:10 PM, Denis CIOCCA wrote:
> Hi Jonathan,
> 
> can you please provide me one direction to modify the part in question 
> about the style of struct, in such a way I can do some modifications.
> 
Sorry, I haven't caught up with my emails for a few days!

Anyhow, quick reply below.
> Thanks,
> 
> Denis Ciocca
> 
> 
> 
> On 11/06/2012 12:11 PM, Denis Ciocca wrote:
>> Hi Jonathan,
>>
>> Thanks for your support!
>> I have applied your comments, only two point I would check better.
>>
>>
>>
>>>> +} st_accel_sensors[] = {
>>>> +     {
>>>> +             .wai = ST_ACCEL_1_WAI_EXP,
>>>> +             .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
>>>> +             .odr = {
>>>> +                     .addr = ST_ACCEL_1_ODR_ADDR,
>>>> +                     .mask = ST_ACCEL_1_ODR_MASK,
>>>> +                     .num_bit = ST_ACCEL_1_ODR_N_BIT,
>>>> +                     .odr_avl = {
>>>    While sometimes c99 assignment helps with clarity - sometimes it
>>> just bloats the code.
>>>    Personally I'd do these as
>>>    .odr_avl = {{ST_ACCEL_ODR_AVL_1HZ, ST_ACCEL_1_ODR_AVL_1HZ_VAL},
>>>                {...}
>>> etc
>>>
>>> or for that matter take the view that the defines are largely
>>> pointless as they
>>> are only used here and
>>> just do
>>>       .odr_avl = {{1, 0x01},
>>>                   {10, 0x02},
>>> etc.
>>> Or maybe if it fits on the line
>>>     .odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest...
>>> Not sure.
>>>
>>
>> In my first version I have used about this compact approach but
>> Lars-Peter tell to me to use this style code:
>>  >I'd use
>>  > .odr_avl = {
>>  >        [0] = {
>>  >                ...
>>  >        },
>>  >        [1] = {
>>  >                ...
>>  >
>>  >Makes things a bit more readable in my opinion.
>>
>> Ok for me it is not a problem but you must tell me one definitive style
>> please. I think this style is more cleanly, though are necessary more rows.

The indexing gives no benefit - it's not relevant to the use of this
array as far as I can tell. Now we had index value pairs, then it would make
sense, but we don't.

My personal view is go for whatever gives the best combination of readability to
code compactness.  So here, where what we have is effectively key value pairs,
I'd just have them as an array.  Elsewhere, when you have general elements of
a structure then c99 style assignment is clearest and allows the structure
to sometimes be extended without needing to be careful about the ordering etc.

To a certain extent I don't care, I just thought for key value pairs there
was an awful lot of code here and it actually damaged readability rather
than enhancing it.

Hence have a think about it and go with whatever you think is clearest.

>
>
>>
>>
>>>> +/**
>>>> + * struct st_accel_platform_data - ST accel device platform data
>>>> + * @fullscale: Value of fullscale used for the sensor.
>>>> + * @sampling_frequency: Value of sampling frequency used for the
>>>> sensor.
>>>> + */
>>>> +
>>>> +struct st_accel_platform_data {
>>>> +     int fullscale;
>>>> +     int sampling_frequency;
>>>> +};
>>>
>>> What is the purpose of having these as platform data?
>>>
>>
>> For now the purpose is to set only the initial values for fullscale and
>> sampling_frequency but I think it's good thing include the mapping of
>> axis. You think it's necessary to remove this for now?
I'd not bother with it.  Just put a sensible default in the driver and
if anything else is wanted leave it up to userspace once the driver
is loaded.

Axis alignment is an interesting one.  So far we have it for just one
driver and that was to maintain feature parity with an existing driver
when we pulled it into the kernel.

It is often rather arbitary. The case where it matters is usually for
human input in which case it can be handled in the mapping code
(if using iio and the input bridge driver).

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-12 18:48                                         ` Jonathan Cameron
@ 2012-11-13 15:38                                           ` Denis CIOCCA
  2012-11-18 13:20                                             ` Jonathan Cameron
  2012-11-19 13:00                                             ` STMicroelectronics accelerometers driver Lars-Peter Clausen
  0 siblings, 2 replies; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-13 15:38 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan,

I have reviewed my code and I applied your stylish comment to decrease
the lines number of the code.
For now I also deleted the platform_data, I will do this in a future patch.

I attached to you the new patch, if the code is ok I will send this with
git.

Thanks,

Denis




 From 8bb6479aa8814ad4b82c359a0aaea99b04a7ecef Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
  drivers/iio/accel/Kconfig                        |   37 +
  drivers/iio/accel/Makefile                       |    6 +
  drivers/iio/accel/st_accel_buffer.c              |  176 ++++
  drivers/iio/accel/st_accel_core.c                | 1154
++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c                 |  128 +++
  drivers/iio/accel/st_accel_spi.c                 |  186 ++++
  drivers/iio/accel/st_accel_trigger.c             |   83 ++
  include/linux/iio/accel/st_accel.h               |  102 ++
  9 files changed, 1879 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..af5d333
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,176 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+       int len =3D 0, i, n =3D 0;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, rx_array=
);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       rx_array);
+               } else {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       rx_array[2] =3D rx_array[4];
+                       rx_array[3] =3D rx_array[5];
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       rx_array);
+               break;
+       default:
+               break;
+       }
+
+       return len;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int ret, i, scan_count;
+       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS=
];
+       s16 *data =3D (s16 *)buf;
+
+       ret =3D st_accel_read_all(indio_dev, rx_array);
+       if (ret < 0)
+               return ret;
+
+       scan_count =3D bitmap_weight(indio_dev->active_scan_mask,
+                                                       indio_dev->maskleng=
th);
+
+       for (i =3D 0; i < scan_count; i++)
+               memcpy(data+i, &rx_array[i*2], sizeof(s16));
+
+       return i*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       int len =3D 0;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       indio_dev->scan_timestamp =3D true;
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..0a787df
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1154 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
+                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
+                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err;
+       u8 prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D adata->read_byte(adata, reg_addr, &prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask=
));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+                       int scale, struct st_accel_fullscale_available *fs_=
out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, bool value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+               (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto read_error;
+                       } else {
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)cpu_to_le16p((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_accel_match_scale(&st_accel_sensors[adata->index=
],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to set sampling frequency to %d.\n", freq);
+               goto st_accel_sysfs_set_sampling_frequency_error;
+       }
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, !powerdown);
+       if (err < 0)
+               dev_err(&indio_dev->dev,
+                       "failed to set powerdown to %d.\n", (int)(powerdown=
));
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+                               st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown =
, 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+       .write_raw =3D &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                               &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai)=
;
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+
+       adata->fullscale =3D ST_ACCEL_FS_AVL_2G;
+       adata->odr =3D 100;
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_accel_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev, irq);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..f4680a2
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_accel_i2c_read_byte_error:
+       return err;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_accel_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D __devexit_p(st_accel_i2c_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..1f7f167
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,186 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D res_byte,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               }
+       };
+
+       tx =3D reg_addr | ACC_SPI_READ;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+       return err;
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       if (adata->multiread_bit =3D=3D true)
+               tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx =3D reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_multiple_byte_error;
+
+       return len;
+
+acc_spi_read_multiple_byte_error:
+       return err;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2];
+
+       struct spi_transfer xfers =3D {
+               .tx_buf =3D tx,
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int __devexit st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_accel_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D __devexit_p(st_accel_spi_remove),
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..68813f7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..9e0abd6
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
irq)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-13 15:38                                           ` Denis CIOCCA
@ 2012-11-18 13:20                                             ` Jonathan Cameron
  2012-11-23 16:10                                               ` Denis CIOCCA
  2012-11-19 13:00                                             ` STMicroelectronics accelerometers driver Lars-Peter Clausen
  1 sibling, 1 reply; 42+ messages in thread
From: Jonathan Cameron @ 2012-11-18 13:20 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/13/2012 03:38 PM, Denis CIOCCA wrote:
> Hi Jonathan,
>
> I have reviewed my code and I applied your stylish comment to decrease
> the lines number of the code.
> For now I also deleted the platform_data, I will do this in a future patch.
>
> I attached to you the new patch, if the code is ok I will send this with
> git.
>
> Thanks,
>
> Denis
>
Hi Denis,

I'm sorry but I'd missed one big issue with the spi support before.
For spi dma requirements mean that the buffers MUST sit in their
own cachelines or you will sometimes randomly corrupt nearby memory.

The code to allow individual channels to be disabled for buffering
turned out more complex than I expected.  Perhaps with hindsight
grabbing them all and letting the core handle demuxing the data
flow would have been cleaner.  Never mind though. What you have
is correct and a valid solution so lets stay with it!

Couple of other nitpicks inline.

Jonathan
>
>
>
>  From 8bb6479aa8814ad4b82c359a0aaea99b04a7ecef Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
>
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
>
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>   Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
>   drivers/iio/accel/Kconfig                        |   37 +
>   drivers/iio/accel/Makefile                       |    6 +
>   drivers/iio/accel/st_accel_buffer.c              |  176 ++++
>   drivers/iio/accel/st_accel_core.c                | 1154
> ++++++++++++++++++++++
>   drivers/iio/accel/st_accel_i2c.c                 |  128 +++
>   drivers/iio/accel/st_accel_spi.c                 |  186 ++++
>   drivers/iio/accel/st_accel_trigger.c             |   83 ++
>   include/linux/iio/accel/st_accel.h               |  102 ++
>   9 files changed, 1879 insertions(+), 0 deletions(-)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
>   create mode 100644 drivers/iio/accel/st_accel_buffer.c
>   create mode 100644 drivers/iio/accel/st_accel_core.c
>   create mode 100644 drivers/iio/accel/st_accel_i2c.c
>   create mode 100644 drivers/iio/accel/st_accel_spi.c
>   create mode 100644 drivers/iio/accel/st_accel_trigger.c
>   create mode 100644 include/linux/iio/accel/st_accel.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> new file mode 100644
> index 0000000..f426c02
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> @@ -0,0 +1,7 @@
> +What:          /sys/bus/iio/devices/iio:deviceX/powerdown
> +KernelVersion: 3.7.0
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               Reading returns either '1' or '0'.
> +               '1' means that the device in question is off.
> +               '0' means that the devices in question is on.
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..d65e66a 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>           Say yes here to build support for the HID SENSOR
>           accelerometers 3D.
>
> +config ST_ACCEL_3AXIS
> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +       depends on (I2C || SPI) && SYSFS
> +       help
> +         Say yes here to build support for STMicroelectronics accelerometers:
> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_accel.
> +
> +config ST_ACCEL_3AXIS_I2C
> +       tristate "support I2C bus connection"
> +       depends on ST_ACCEL_3AXIS && I2C
> +       help
> +         Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_i2c.
> +
> +config ST_ACCEL_3AXIS_SPI
> +       tristate "support SPI bus connection"
> +       depends on ST_ACCEL_3AXIS && SPI_MASTER
> +       help
> +         Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_spi.
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +       tristate "support triggered buffer"
> +       depends on ST_ACCEL_3AXIS
> +       select IIO_TRIGGERED_BUFFER
> +       select IIO_BUFFER
> +       help
> +         Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>   endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..1541236 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>   #
>
>   obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +st_accel-y := st_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
> st_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..af5d333
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_buffer.c
> @@ -0,0 +1,176 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
> +
This probably wants renaming as it isn't reading all any more.
It's also only called from one place, I'd squish the code in there
and drop this additional function.
> +static int st_accel_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +       int len = 0, i, n = 0;
> +       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
> +               if (test_bit(i, indio_dev->active_scan_mask)) {
> +                       reg_addr[n] = indio_dev->channels[i].address;
Normally this would be set up in the update_scan_mode callback - but given
the complexity here I suppose it doesn't really matter.
> +                       n++;
> +               }
> +       }
> +       switch (n) {
> +       case 1:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_ACCEL_BYTE_FOR_CHANNEL, rx_array);
> +               break;
> +       case 2:
> +               if ((reg_addr[1] - reg_addr[0]) == ST_ACCEL_BYTE_FOR_CHANNEL) {
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
> +                                       rx_array);
> +               } else {
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                               ST_ACCEL_BYTE_FOR_CHANNEL*
> +                               ST_ACCEL_NUMBER_DATA_CHANNELS,
> +                               rx_array);
> +                       rx_array[2] = rx_array[4];
> +                       rx_array[3] = rx_array[5];
> +                       len = ST_ACCEL_BYTE_FOR_CHANNEL*n;
> +               }
Ouch.  This complexity might argue in favour of always reading everything
then letting the core demux handle pulling the requested data out of the
stream.  I hadn't realised this complexity would occur if one turned individual
channels off.  Maybe handle the one channel and the 3 channel. Let the core
code deal with the two (just a case of seeing up the appropriate available_scan_masks.  See how the adc/max1363 does it
for example).

Maybe just leaving it as you have here is best at this stage though!

> +               break;
> +       case 3:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS,
> +                       rx_array);
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return len;
> +}
> +
> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, scan_count;
> +       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       s16 *data = (s16 *)buf;
> +
> +       ret = st_accel_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +
> +       scan_count = bitmap_weight(indio_dev->active_scan_mask,
> +                                                       indio_dev->masklength);
> +
> +       for (i = 0; i < scan_count; i++)
> +               memcpy(data+i, &rx_array[i*2], sizeof(s16));
I think you now only actually have the correct channels correctly packed.
Hence why the copy?
> +
> +       return i*sizeof(data[0]);
> +}
> +
> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
> +{
> +       int len = 0;
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       len = st_accel_get_buffer_element(indio_dev, adata->buffer_data);
> +       if (indio_dev->scan_timestamp)
> +               *(s64 *)((u8 *)adata->buffer_data +
> +                               ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +       iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
Sorry, iio has changed in the mean time.
Now iio_push_to_buffers(indio_dev, adata->buffer_data);

> +       iio_trigger_notify_done(indio_dev->trig);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int err, i;
> +       u8 active_bit = 0x00;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +       if (adata->buffer_data == NULL) {
> +               err = -ENOMEM;
> +               goto allocate_memory_error;
> +       }
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
> +               if (test_bit(i, indio_dev->active_scan_mask))
> +                       active_bit |= (1 << i);
> +
> +       err = st_accel_set_axis_enable(indio_dev, active_bit);
> +       if (err < 0)
> +               goto st_accel_buffer_postenable_error;
> +
> +       err = iio_triggered_buffer_postenable(indio_dev);
> +
> +       return err;
> +
> +allocate_memory_error:
> +       kfree(adata->buffer_data);
> +st_accel_buffer_postenable_error:
> +       return err;
> +}
> +
> +static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = iio_triggered_buffer_predisable(indio_dev);
> +       if (err < 0)
> +               goto st_accel_buffer_predisable_error;
> +
> +       err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +       if (err < 0)
> +               goto st_accel_buffer_predisable_error;
> +
> +       kfree(adata->buffer_data);
> +
> +st_accel_buffer_predisable_error:
> +       return err;
> +}
> +
> +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
> +       .preenable = &iio_sw_buffer_preenable,
> +       .postenable = &st_accel_buffer_postenable,
> +       .predisable = &st_accel_buffer_predisable,
> +};
> +
> +int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       indio_dev->scan_timestamp = true;
Why?  That should be entirely up to userspace.  Sometimes we want the timestamp
sometimes it is pointless overhead?

> +       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +                       &st_accel_trigger_handler, &st_accel_buffer_setup_ops);
> +}
> +EXPORT_SYMBOL(st_accel_allocate_ring);
> +
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_deallocate_ring);
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..0a787df
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1154 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ST_ACCEL_FULLSCALE_AVL_MAX             5
> +#define ST_ACCEL_ODR_LIST_MAX                  10
> +#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
> +
> +/* DEFAULT VALUE FOR SENSORS */
> +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
> +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
> +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
> +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
> +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
> +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
> +#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
> +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
> +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
> +#define ST_ACCEL_DEFAULT_12_REALBITS           12
> +#define ST_ACCEL_DEFAULT_16_REALBITS           16
> +#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
> +#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
> +#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
> +
> +/* FULLSCALE */
> +#define ST_ACCEL_FS_AVL_2G                     2
> +#define ST_ACCEL_FS_AVL_4G                     4
> +#define ST_ACCEL_FS_AVL_6G                     6
> +#define ST_ACCEL_FS_AVL_8G                     8
> +#define ST_ACCEL_FS_AVL_16G                    16
> +
> +/* CUSTOM VALUES FOR SENSOR 1 */
> +#define ST_ACCEL_1_WAI_EXP                     0x33
> +#define ST_ACCEL_1_ODR_ADDR                    0x20
> +#define ST_ACCEL_1_ODR_MASK                    0xf0
> +#define ST_ACCEL_1_ODR_N_BIT                   4
> +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
> +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
> +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
> +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
> +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
> +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
> +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
> +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
> +#define ST_ACCEL_1_FS_N_BIT                    2
> +#define ST_ACCEL_1_FS_ADDR                     0x23
> +#define ST_ACCEL_1_FS_MASK                     0x30
> +#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
> +#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
> +#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
> +#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
> +#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
> +#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
> +#define ST_ACCEL_1_BDU_ADDR                    0x23
> +#define ST_ACCEL_1_BDU_MASK                    0x80
> +#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
> +#define ST_ACCEL_1_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 2 */
> +#define ST_ACCEL_2_WAI_EXP                     0x49
> +#define ST_ACCEL_2_ODR_ADDR                    0x20
> +#define ST_ACCEL_2_ODR_MASK                    0xf0
> +#define ST_ACCEL_2_ODR_N_BIT                   4
> +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_2_FS_N_BIT                    3
> +#define ST_ACCEL_2_FS_ADDR                     0x21
> +#define ST_ACCEL_2_FS_MASK                     0x38
> +#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_2_BDU_ADDR                    0x20
> +#define ST_ACCEL_2_BDU_MASK                    0x08
> +#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
> +#define ST_ACCEL_2_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 3 */
> +#define ST_ACCEL_3_WAI_EXP                     0x32
> +#define ST_ACCEL_3_ODR_ADDR                    0x20
> +#define ST_ACCEL_3_ODR_MASK                    0x18
> +#define ST_ACCEL_3_ODR_N_BIT                   2
> +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
> +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
> +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
> +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
> +#define ST_ACCEL_3_PW_ADDR                     0x20
> +#define ST_ACCEL_3_PW_MASK                     0xe0
> +#define ST_ACCEL_3_PW_N_BIT                    3
> +#define ST_ACCEL_3_FS_N_BIT                    2
> +#define ST_ACCEL_3_FS_ADDR                     0x23
> +#define ST_ACCEL_3_FS_MASK                     0x30
> +#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
> +#define ST_ACCEL_3_BDU_ADDR                    0x23
> +#define ST_ACCEL_3_BDU_MASK                    0x80
> +#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
> +#define ST_ACCEL_3_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 4 */
> +#define ST_ACCEL_4_WAI_EXP                     0x40
> +#define ST_ACCEL_4_ODR_ADDR                    0x20
> +#define ST_ACCEL_4_ODR_MASK                    0xf0
> +#define ST_ACCEL_4_ODR_N_BIT                   4
> +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_4_FS_N_BIT                    3
> +#define ST_ACCEL_4_FS_ADDR                     0x24
> +#define ST_ACCEL_4_FS_MASK                     0x38
> +#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_4_BDU_ADDR                    0x20
> +#define ST_ACCEL_4_BDU_MASK                    0x08
> +#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
> +#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
> +#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
> +#define ST_ACCEL_4_IG1_EN_MASK                 0x08
> +#define ST_ACCEL_4_MULTIREAD_BIT               false
> +
> +struct st_accel_odr_available {
> +       unsigned int hz;
> +       u8 value;
> +};
> +
> +struct st_accel_odr {
> +       u8 addr;
> +       u8 mask;
> +       short num_bit;
> +       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
> +};
> +
> +struct st_accel_power {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       u8 value_off;
> +       u8 value_on;
> +};
> +
> +struct st_accel_axis {
> +       u8 addr;
> +       u8 mask;
> +};
> +
> +struct st_accel_fullscale_available {
> +       unsigned int num;
> +       u8 value;
> +       unsigned int gain;
> +};
> +
> +struct st_accel_fullscale {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_accel_bdu {
> +       u8 addr;
> +       u8 mask;
> +};
> +
> +struct st_accel_interrupt_generator {
> +       u8 en_addr;
> +       u8 latch_mask_addr;
> +       u8 en_mask;
> +       u8 latching_mask;
> +};
> +
> +struct st_accel_data_ready_irq {
> +       u8 addr;
> +       u8 mask;
> +       struct st_accel_interrupt_generator ig1;
> +};
> +
> +#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
> +{ \
> +       .type = IIO_ACCEL, \
> +       .modified = 1, \
> +       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +       .scan_index = index, \
> +       .channel2 = mod, \
> +       .address = addr, \
> +       .scan_type = { \
> +               .sign = 's', \
> +               .realbits = bits, \
> +               .shift = 16 - bits, \
> +               .storagebits = 16, \
> +               .endianness = endian, \
> +       }, \
> +}
> +
> +static const struct iio_chan_spec st_accel_12bit_channels[] = {
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +static const struct iio_chan_spec st_accel_16bit_channels[] = {
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
> +       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +/**
> + * struct st_accel_sensors - ST accel sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + */
> +static const struct st_accel_sensors {
> +       u8 wai;
> +       struct iio_chan_spec *ch;
> +       struct st_accel_odr odr;
> +       struct st_accel_power pw;
> +       struct st_accel_axis enable_axis;
> +       struct st_accel_fullscale fs;
> +       struct st_accel_bdu bdu;
> +       struct st_accel_data_ready_irq drdy_irq;
> +       bool multi_read_bit;
> +} st_accel_sensors[] = {
> +       {
> +               .wai = ST_ACCEL_1_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
> +                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
> +                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
> +                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +                       .mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_1_FS_ADDR,
> +                       .mask = ST_ACCEL_1_FS_MASK,
> +                       .num_bit = ST_ACCEL_1_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_1_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_1_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_1_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_8_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_1_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_1_BDU_ADDR,
> +                       .mask = ST_ACCEL_1_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_1_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_2_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
> +                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
> +                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
> +                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
> +                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
> +                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +                       .mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_2_FS_ADDR,
> +                       .mask = ST_ACCEL_2_FS_MASK,
> +                       .num_bit = ST_ACCEL_2_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_2_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_2_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_2_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_2_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_2_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_2_DRDY_IRQ_MASK,
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_2_BDU_ADDR,
> +                       .mask = ST_ACCEL_2_BDU_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_3_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_3_ODR_ADDR,
> +                       .mask = ST_ACCEL_3_ODR_MASK,
> +                       .num_bit = ST_ACCEL_3_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
> +                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
> +                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_3_PW_ADDR,
> +                       .mask = ST_ACCEL_3_PW_MASK,
> +                       .num_bit = ST_ACCEL_3_PW_N_BIT,
> +                       .value_on = ST_ACCEL_DEFAULT_POWER_ON_VALUE,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +                       .mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_3_FS_ADDR,
> +                       .mask = ST_ACCEL_3_FS_MASK,
> +                       .num_bit = ST_ACCEL_3_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_3_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_3_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_3_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_8_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_3_BDU_ADDR,
> +                       .mask = ST_ACCEL_3_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_3_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_4_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
> +                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
> +                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
> +                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
> +                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
> +                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_ACCEL_DEFAULT_AXIS_ADDR,
> +                       .mask = ST_ACCEL_DEFAULT_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_4_FS_ADDR,
> +                       .mask = ST_ACCEL_4_FS_MASK,
> +                       .num_bit = ST_ACCEL_4_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_4_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_4_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_4_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_4_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_4_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_4_BDU_ADDR,
> +                       .mask = ST_ACCEL_4_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_4_DRDY_IRQ_MASK,
> +                       .ig1 = {
> +                               .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
> +                               .en_mask = ST_ACCEL_4_IG1_EN_MASK,
> +                       },
> +               },
> +               .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
> +       },
> +};
> +
> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
> reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err;
> +       u8 prev_data;
> +       u8 new_data;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = adata->read_byte(adata, reg_addr, &prev_data);
> +       if (err < 0)
> +               goto st_accel_write_data_with_mask_error;
> +
> +       new_data = ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +       err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_accel_write_data_with_mask_error:
> +       return err;
> +}
> +
> +static int st_accel_match_odr(const struct st_accel_sensors *sensor,
> +               unsigned int odr, struct st_accel_odr_available *odr_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
> +               if (sensor->odr.odr_avl[i].hz == odr) {
> +                       odr_out->hz = sensor->odr.odr_avl[i].hz;
> +                       odr_out->value = sensor->odr.odr_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int st_accel_match_fs(const struct st_accel_sensors *sensor,
> +               unsigned int fs, struct st_accel_fullscale_available *fs_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].num == fs) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +static int st_accel_match_scale(const struct st_accel_sensors *sensor,
> +                       int scale, struct st_accel_fullscale_available *fs_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].gain == scale) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
> +{
> +       int err;
> +       struct st_accel_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
> +                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_accel_set_dataready_irq_error;
> +       }
> +
> +       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
> +               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_accel_set_dataready_irq_error;
> +       }
> +
> +       err = st_accel_write_data_with_mask(indio_dev,
> +               st_accel_sensors[adata->index].drdy_irq.addr,
> +               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
> +
> +st_accel_set_dataready_irq_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_set_dataready_irq);
> +
> +static int st_accel_set_bdu(struct iio_dev *indio_dev,
> +                               const struct st_accel_bdu *bdu, bool value)
> +{
> +       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
> +                                                               1, (u8)value);
> +}
> +
> +static int st_accel_set_odr(struct iio_dev *indio_dev,
> +                               struct st_accel_odr_available *odr_available)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       if ((st_accel_sensors[adata->index].odr.addr ==
> +               st_accel_sensors[adata->index].pw.addr) &&
> +               (st_accel_sensors[adata->index].odr.mask ==
> +                               st_accel_sensors[adata->index].pw.mask)) {
> +               if (adata->enabled == true) {
> +                       err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].odr.addr,
> +                               st_accel_sensors[adata->index].odr.mask,
> +                               st_accel_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +               } else {
> +                       adata->odr = odr_available->hz;
> +                       err = 0;
> +               }
> +       } else {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].odr.addr,
> +                               st_accel_sensors[adata->index].odr.mask,
> +                               st_accel_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +       }
> +
> +       return err;
> +}
> +
> +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_write_data_with_mask(indio_dev,
> +                       st_accel_sensors[adata->index].enable_axis.addr,
> +                       st_accel_sensors[adata->index].enable_axis.mask,
> +                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
> +
> +       return err;
> +}
> +
> +static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
> +{
> +       int err = -EINVAL;
> +       bool found;
> +       u8 tmp_value;
> +       struct st_accel_odr_available odr_out;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       if (enable) {
> +               found = false;
> +               tmp_value = st_accel_sensors[adata->index].pw.value_on;
> +               if ((st_accel_sensors[adata->index].odr.addr ==
> +                               st_accel_sensors[adata->index].pw.addr) &&
> +                       (st_accel_sensors[adata->index].odr.mask ==
> +                               st_accel_sensors[adata->index].pw.mask)) {
> +                       err = st_accel_match_odr(
> +                               &st_accel_sensors[adata->index], adata->odr,
> +                                       &odr_out);
> +                       if (err < 0)
> +                               goto set_enable_error;
> +                       tmp_value = odr_out.value;
> +                       found = true;
> +               }
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].pw.addr,
> +                               st_accel_sensors[adata->index].pw.mask,
> +                               st_accel_sensors[adata->index].pw.num_bit,
> +                               tmp_value);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = true;
> +               if (found)
> +                       adata->odr = odr_out.hz;
> +       } else {
> +               err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].pw.addr,
> +                               st_accel_sensors[adata->index].pw.mask,
> +                               st_accel_sensors[adata->index].pw.num_bit,
> +                               st_accel_sensors[adata->index].pw.value_off);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = false;
> +       }
> +
> +set_enable_error:
> +       return err;
> +}
> +
> +static int st_accel_set_fullscale(struct iio_dev *indio_dev,
> +                               struct st_accel_fullscale_available *fs_avl)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_write_data_with_mask(indio_dev,
> +                               st_accel_sensors[adata->index].fs.addr,
> +                               st_accel_sensors[adata->index].fs.mask,
> +                               st_accel_sensors[adata->index].fs.num_bit,
> +                               fs_avl->value);
> +       if (err < 0)
> +               goto st_accel_set_fullscale_error;
> +
> +       adata->fullscale = fs_avl->num;
> +       adata->gain = fs_avl->gain;
> +       return err;
> +
> +st_accel_set_fullscale_error:
> +       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +       return err;
> +}
> +
> +static int st_accel_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
Outdata will need to be in it's own cacheline.  Either allocate
it dynamically here, or put it in adata and enforce the alignment.
This requirement is always there for spi drivers because they use
the buffer directly in dma (whereas i2c always copies it).

> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&indio_dev->mlock);
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +                       goto read_error;
> +               } else {
> +                       if (!adata->enabled) {
> +                               err = -EIO;
> +                               goto read_error;
> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               *val = ((s16)cpu_to_le16p((u16 *)outdata))
> +                                                       >> ch->scan_type.shift;
Going from cpu to le16 seems a little odd.  Surely le16_to_cpu?

> +                       }
> +               }
> +               mutex_unlock(&indio_dev->mlock);
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = adata->gain;
> +               return IIO_VAL_INT_PLUS_MICRO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       mutex_unlock(&indio_dev->mlock);
> +       return err;
> +}
> +
> +static int st_accel_write_raw(struct iio_dev *indio_dev,
> +               struct iio_chan_spec const *chan, int val, int val2, long mask)
> +{
> +       int err;
> +       struct st_accel_fullscale_available fs_out;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SCALE:
> +               err = st_accel_match_scale(&st_accel_sensors[adata->index],
> +                                                               val2, &fs_out);
> +               if (err < 0)
> +                       goto write_error;
> +
> +               err = st_accel_set_fullscale(indio_dev, &fs_out);
> +               break;
> +       default:
> +               err = -EINVAL;
> +       }
> +
> +write_error:
> +       return err;
> +}
> +
> +static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
> +{
> +       int i;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
> +               if (st_accel_sensors[i].wai == wai)
> +                       break;
> +       }
> +       if (i == ARRAY_SIZE(st_accel_sensors))
> +               goto check_device_error;
> +
> +       adata->index = i;
> +
> +       return i;
> +
> +check_device_error:
> +       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
> +       return -ENODEV;
> +}
> +
> +static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
> +                                                               u8 *value)
> +{
> +       int ret;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       ret = adata->read_byte(adata, reg_addr, value);
> +       if (ret < 0)
> +               goto read_byte_wai_error;
> +
> +       return 0;
> +
> +read_byte_wai_error:
> +       dev_err(&indio_dev->dev,
> +                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
> +       return -EIO;
> +}
> +
> +static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int freq;
> +       struct st_accel_odr_available odr_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &freq);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_match_odr(&st_accel_sensors[adata->index],
> +                                                               freq, &odr_out);
> +       if (err < 0)
> +               goto st_accel_sysfs_set_sampling_frequency_error;
> +
> +       err = st_accel_set_odr(indio_dev, &odr_out);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev,
> +                       "failed to set sampling frequency to %d.\n", freq);
> +               goto st_accel_sysfs_set_sampling_frequency_error;
> +       }
> +       adata->odr = odr_out.hz;
> +
> +st_accel_sysfs_set_sampling_frequency_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->odr);
> +}
> +
> +static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       bool powerdown;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> +       err = strtobool(buf, &powerdown);
> +       if (err < 0)
> +               goto set_enable_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_set_enable(indio_dev, !powerdown);
> +       if (err < 0)
> +               dev_err(&indio_dev->dev,
> +                       "failed to set powerdown to %d.\n", (int)(powerdown));
> +       mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", (int)(!adata->enabled));
> +}
> +
> +static ssize_t st_accel_sysfs_scale_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "0.%06u ",
> +                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +       buf[len-1] = '\n';
> +
> +       return len;
> +}
> +
> +static ssize_t st_accel_sysfs_sampling_frequency_available(struct
> device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
> +                                                                       i++) {
> +               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
> +                       break;
> +
> +               len += sprintf(buf + len, "%d ",
> +                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +       buf[len - 1] = '\n';
> +
> +       return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +               st_accel_sysfs_sampling_frequency_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
> +                               st_accel_sysfs_scale_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
> +               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown , 0);
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +                       st_accel_sysfs_get_sampling_frequency,
> +                                       st_accel_sysfs_set_sampling_frequency);
> +
> +static struct attribute *st_accel_attributes[] = {
> +       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
> +       &iio_dev_attr_powerdown.dev_attr.attr,
> +       &iio_dev_attr_sampling_frequency.dev_attr.attr,
> +       NULL,
> +};
> +
> +static const struct attribute_group st_accel_attribute_group = {
> +       .attrs = st_accel_attributes,
> +};
> +
> +static const struct iio_info acc_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &st_accel_attribute_group,
> +       .read_raw = &st_accel_read_raw,
> +       .write_raw = &st_accel_write_raw,
> +};
> +
> +static int st_accel_init_sensor(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_accel_odr_available odr_out;
> +       struct st_accel_fullscale_available fs_out;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_set_enable(indio_dev, false);
> +       if (err < 0)
> +               goto init_error;
> +
> +       err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +       if (err < 0)
> +               goto init_error;
> +
> +       st_accel_match_fs(&st_accel_sensors[adata->index],
> +                                               adata->fullscale, &fs_out);
> +       err = st_accel_set_fullscale(indio_dev, &fs_out);
> +       if (err < 0)
> +               goto init_error;
> +
> +       st_accel_match_odr(&st_accel_sensors[adata->index],
> +                                                       adata->odr, &odr_out);
> +       err = st_accel_set_odr(indio_dev, &odr_out);
> +       if (err < 0)
> +               goto init_error;
> +
> +       err = st_accel_set_bdu(indio_dev,
> +                               &st_accel_sensors[adata->index].bdu, true);
> +
> +init_error:
> +       return err;
> +}
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
> +{
> +       int err;
> +       u8 wai;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->info = &acc_info;
> +
> +       err = st_accel_get_wai_device(indio_dev,
> +                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_check_device_list(indio_dev, wai);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
> +       indio_dev->channels = st_accel_sensors[adata->index].ch;
> +       indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
> +
> +       adata->fullscale = ST_ACCEL_FS_AVL_2G;
> +       adata->odr = 100;
> +
> +       err = st_accel_init_sensor(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       err = st_accel_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto st_accel_iio_probe_error;
> +
> +       if (irq > 0) {
> +               err = st_accel_probe_trigger(indio_dev, irq);
> +               if (err < 0)
> +                       goto acc_probe_trigger_error;
> +       }
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       return err;
> +
> +iio_device_register_error:
> +       st_accel_remove_trigger(indio_dev, irq);
> +acc_probe_trigger_error:
> +       st_accel_deallocate_ring(indio_dev);
> +st_accel_iio_probe_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_iio_probe);
> +
> +void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
> +{
> +       iio_device_unregister(indio_dev);
> +       st_accel_remove_trigger(indio_dev, irq);
> +       st_accel_deallocate_ring(indio_dev);
> +       iio_device_free(indio_dev);
> +}
> +EXPORT_SYMBOL(st_accel_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_i2c.c
> b/drivers/iio/accel/st_accel_i2c.c
> new file mode 100644
> index 0000000..f4680a2
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -0,0 +1,128 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +#define ST_ACCEL_I2C_MULTIREAD                 0x80
> +
> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       int err;
> +
> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +       if (err < 0)
> +               goto st_accel_i2c_read_byte_error;
> +
> +       *res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +       return err;
> +}
> +
> +static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       if (adata->multiread_bit == true)
> +               reg_addr |= ST_ACCEL_I2C_MULTIREAD;
> +
> +       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +                                                       reg_addr, len, data);
> +}
> +
> +static int st_accel_i2c_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
> +                                                               reg_addr, data);
> +}
> +
> +static int __devinit st_accel_i2c_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_accel_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &client->dev;
> +       i2c_set_clientdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->name;
> +
> +       adata->read_byte = st_accel_i2c_read_byte;
> +       adata->write_byte = st_accel_i2c_write_byte;
> +       adata->read_multiple_byte = st_accel_i2c_read_multiple_byte;
> +
> +       err = st_accel_iio_probe(indio_dev, client->irq);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit st_accel_i2c_remove(struct i2c_client *client)
> +{
> +       struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +       st_accel_iio_remove(indio_dev, client->irq);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id st_accel_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
> +
> +static struct i2c_driver st_accel_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-accel-i2c",
> +       },
> +       .probe = st_accel_i2c_probe,
> +       .remove = __devexit_p(st_accel_i2c_remove),
> +       .id_table = st_accel_id_table,
> +};
> +module_i2c_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_spi.c
> b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..1f7f167
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,186 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ           0x80;
> +#define ACC_SPI_MULTIREAD      0xc0
> +
> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = res_byte,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               }
> +       };
> +
> +       tx = reg_addr | ACC_SPI_READ;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
Little tidy up, but could a single util function called by both
the read_multiple below and this one reduce code repitition?
> +
> +       return err;
> +}
> +
> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = data,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       if (adata->multiread_bit == true)
> +               tx = reg_addr | ACC_SPI_MULTIREAD;
> +       else
> +               tx = reg_addr | ACC_SPI_READ;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       if (err)
> +               goto acc_spi_read_multiple_byte_error;
> +
> +       return len;
> +
> +acc_spi_read_multiple_byte_error:
> +       return err;
> +}
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx[2];
We may have a cacheline issue here for some spi controllers.
As they often use dma one needs to ensure the buffers are in
different cachelines from anything else.  Usual trick
is to dynamically allocate them either separately with a kmalloc
or in the adata structure using appropriate alignment forcing.

 Basically any buffers being used in spi transfers must be in a different
cacheline from the rest of the code (no problem with tx and rx being in
the same line as far as I know).
> +
> +       struct spi_transfer xfers = {
> +               .tx_buf = tx,
> +               .bits_per_word = 8,
> +               .len = 2,
> +       };
> +
> +       tx[0] = reg_addr;
> +       tx[1] = data;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers, &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +
> +       return err;
> +}
> +
> +static int __devinit st_accel_spi_probe(struct spi_device *spi)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_accel_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &spi->dev;
> +       spi_set_drvdata(spi, indio_dev);
> +
> +       indio_dev->dev.parent = &spi->dev;
> +       indio_dev->name = spi->modalias;
> +
> +       adata->read_byte = st_accel_spi_read_byte;
> +       adata->write_byte = st_accel_spi_write_byte;
> +       adata->read_multiple_byte = st_accel_spi_read_multiple_byte;
> +
> +       err = st_accel_iio_probe(indio_dev, spi->irq);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit st_accel_spi_remove(struct spi_device *spi)
> +{
> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +       st_accel_iio_remove(indio_dev, spi->irq);
> +
> +       return 0;
> +}
> +
> +static const struct spi_device_id st_accel_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(spi, st_accel_id_table);
> +
> +static struct spi_driver st_accel_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-accel-spi",
> +       },
> +       .probe = st_accel_spi_probe,
> +       .remove = __devexit_p(st_accel_spi_remove),
> +       .id_table = st_accel_id_table,
> +};
> +module_spi_driver(st_accel_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_trigger.c
> b/drivers/iio/accel/st_accel_trigger.c
> new file mode 100644
> index 0000000..68813f7
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_trigger.c
> @@ -0,0 +1,83 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
> state)
> +{
> +       struct iio_dev *indio_dev = trig->private_data;
> +       return st_accel_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops st_accel_trigger_ops = {
> +       .owner = THIS_MODULE,
> +       .set_trigger_state = &st_accel_trig_acc_set_state,
> +};
> +
> +int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +       if (adata->trig == NULL) {
> +               err = -ENOMEM;
> +               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +               goto iio_trigger_alloc_error;
> +       }
> +
> +       err = request_threaded_irq(irq,
> +                       iio_trigger_generic_data_rdy_poll,
> +                       NULL,
> +                       IRQF_TRIGGER_RISING,
> +                       adata->trig->name,
> +                       adata->trig);
> +       if (err)
> +               goto request_irq_error;
> +
> +       adata->trig->private_data = indio_dev;
> +       adata->trig->ops = &st_accel_trigger_ops;
> +       adata->trig->dev.parent = adata->dev;
> +
> +       err = iio_trigger_register(adata->trig);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +               goto iio_trigger_register_error;
> +       }
> +       indio_dev->trig = adata->trig;
> +
> +       return 0;
> +
> +iio_trigger_register_error:
> +       free_irq(irq, adata->trig);
> +request_irq_error:
> +       iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_probe_trigger);
> +
> +void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       iio_trigger_unregister(adata->trig);
> +       free_irq(irq, adata->trig);
> +       iio_trigger_free(adata->trig);
> +}
> +EXPORT_SYMBOL(st_accel_remove_trigger);
> diff --git a/include/linux/iio/accel/st_accel.h
> b/include/linux/iio/accel/st_accel.h
> new file mode 100644
> index 0000000..9e0abd6
> --- /dev/null
> +++ b/include/linux/iio/accel/st_accel.h
> @@ -0,0 +1,102 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_ACCEL_H
> +#define ST_ACCEL_H
> +
> +#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
> +#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
> +#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
> +#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
> +#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
> +#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
> +#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
> +#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
> +#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
> +#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
> +#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
> +#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
> +
> +#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
> +#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
> +#define ST_ACCEL_BYTE_FOR_CHANNEL      2
> +#define ST_ACCEL_SCAN_X                        0
> +#define ST_ACCEL_SCAN_Y                        1
> +#define ST_ACCEL_SCAN_Z                        2
> +
> +/**
> + * struct st_accel_data - ST accel device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the
> + *     st_accel_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [m/s^2/LSB].
> + * @odr: Output data rate of the sensor [Hz].
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + */
> +
> +struct st_accel_data {
> +       struct device *dev;
> +       struct iio_trigger *trig;
> +
> +       bool enabled;
> +       bool multiread_bit;
> +
> +       short index;
> +
> +       char *buffer_data;
> +
> +       unsigned int fullscale;
> +       unsigned int gain;
> +       unsigned int odr;
> +
> +       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                               u8 *res_byte);
> +       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +};
> +
> +int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
> +void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
> +int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
> +void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
> irq)
> +{
> +       return 0;
> +}
> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
> int irq)
> +{
> +       return;
> +}
> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       return;
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCEL_H */
> --
> 1.7.0.4
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-13 15:38                                           ` Denis CIOCCA
  2012-11-18 13:20                                             ` Jonathan Cameron
@ 2012-11-19 13:00                                             ` Lars-Peter Clausen
  1 sibling, 0 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-11-19 13:00 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/13/2012 04:38 PM, Denis CIOCCA wrote:
> Hi Jonathan,
> 
> I have reviewed my code and I applied your stylish comment to decrease
> the lines number of the code.
> For now I also deleted the platform_data, I will do this in a future patch.
> 
> I attached to you the new patch, if the code is ok I will send this with
> git.
> 
> Thanks,
> 
> Denis
> 
> 
> 
> 
>  From 8bb6479aa8814ad4b82c359a0aaea99b04a7ecef Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
> 
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>

Looks pretty good to me, just a few minor issues.

[...]
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..af5d333
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_buffer.c
[...]
> +
> +static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, scan_count;
> +       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS];
> +       s16 *data = (s16 *)buf;
> +
> +       ret = st_accel_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +
> +       scan_count = bitmap_weight(indio_dev->active_scan_mask,
> +                                                       indio_dev->masklength);
> +
> +       for (i = 0; i < scan_count; i++)
> +               memcpy(data+i, &rx_array[i*2], sizeof(s16));

I think you can just pass in 'data' directly to st_accel_read_all. Just use
st_accel_read_all at all call sites and remove st_accel_get_buffer_element
completely.

> +
> +       return i*sizeof(data[0]);
> +}
> +
[...]
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..0a787df
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1154 @@
[...]
> +
> +static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int freq;
> +       struct st_accel_odr_available odr_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &freq);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_match_odr(&st_accel_sensors[adata->index],
> +                                                               freq, &odr_out);
> +       if (err < 0)
> +               goto st_accel_sysfs_set_sampling_frequency_error;

It may make sense to return an error in this case to tell userspace that the
desired frequency could not be set.

> +
> +       err = st_accel_set_odr(indio_dev, &odr_out);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev,
> +                       "failed to set sampling frequency to %d.\n", freq);
> +               goto st_accel_sysfs_set_sampling_frequency_error;

same here. and maybe remove the dev_err(...)

> +       }
> +       adata->odr = odr_out.hz;
> +
> +st_accel_sysfs_set_sampling_frequency_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return size;
> +}
> +
> +static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->odr);
> +}
> +
> +static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       bool powerdown;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> +       err = strtobool(buf, &powerdown);
> +       if (err < 0)
> +               goto set_enable_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_accel_set_enable(indio_dev, !powerdown);
> +       if (err < 0)
> +               dev_err(&indio_dev->dev,
> +                       "failed to set powerdown to %d.\n", (int)(powerdown));

Same here, return an error instead of the dev_err.

> +       mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +       return size;
> +}
> +

[...]
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/accel/st_accel_i2c.c
> b/drivers/iio/accel/st_accel_i2c.c
> new file mode 100644
> index 0000000..f4680a2
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -0,0 +1,128 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +#define ST_ACCEL_I2C_MULTIREAD                 0x80
> +
> +static int st_accel_i2c_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       int err;
> +
> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +       if (err < 0)
> +               goto st_accel_i2c_read_byte_error;
> +
> +       *res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +       return err;

Shouldn't this return 0 in case of success?

> +}
> +

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-18 13:20                                             ` Jonathan Cameron
@ 2012-11-23 16:10                                               ` Denis CIOCCA
  2012-11-24 16:23                                                 ` Jonathan Cameron
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-23 16:10 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan and Lars-Peter,

I have modified the source code but I am not sure if the spi dma code
management is correctly because this part is new for me.

About the complexity of channels management in the buffer code, I think
the code is not too difficult and it is possible leave this code as now.
Are you agree?


I attach the patch.

Best regards,

Denis



 From 6d4f08e97ffdf56ab17feb0884f2043be3d6759e Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 3/3] iio:accel: Add STMicroelectronics accelerometers drive=
r

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
  drivers/iio/accel/Kconfig                        |   37 +
  drivers/iio/accel/Makefile                       |    6 +
  drivers/iio/accel/st_accel_buffer.c              |  179 ++++
  drivers/iio/accel/st_accel_core.c                | 1159
++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c                 |  128 +++
  drivers/iio/accel/st_accel_spi.c                 |  178 ++++
  drivers/iio/accel/st_accel_trigger.c             |   83 ++
  include/linux/iio/accel/st_accel.h               |  102 ++
  9 files changed, 1879 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..fdc7f85
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,179 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int i, n, len;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, buf);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       buf);
+               } else {
+                       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS];
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       if (len < 0)
+                               goto read_data_channels_error;
+
+                       for (i =3D 0; i < n * ST_ACCEL_NUMBER_DATA_CHANNELS=
;
+                                                                       i++=
) {
+                               if (i < n)
+                                       buf[i] =3D rx_array[i];
+                               else
+                                       buf[i] =3D rx_array[n + i];
+                       }
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       buf);
+               break;
+       default:
+               len =3D -EINVAL;
+               goto read_data_channels_error;
+       }
+       if (len !=3D ST_ACCEL_BYTE_FOR_CHANNEL*n) {
+               len =3D -EIO;
+               goto read_data_channels_error;
+       }
+
+       return len*ST_ACCEL_BYTE_FOR_CHANNEL;
+
+read_data_channels_error:
+       return len;
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       int len;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+       if (len < 0)
+               goto st_accel_get_buffer_element_error;
+
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffers(indio_dev->buffer, adata->buffer_data);
+
+st_accel_get_buffer_element_error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..98c3ead
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1159 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
+                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
+                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err;
+       u8 prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D adata->read_byte(adata, reg_addr, &prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask=
));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+                       int scale, struct st_accel_fullscale_available *fs_=
out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, bool value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+               (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 *outdata;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto device_read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto device_read_error;
+                       } else {
+                               outdata =3D kmalloc(sizeof(*outdata*
+                                       ST_ACCEL_BYTE_FOR_CHANNEL), GFP_KER=
NEL);
+                               if (outdata =3D=3D NULL) {
+                                       err =3D -ENOMEM;
+                                       goto device_read_error;
+                               }
+
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)le16_to_cpup((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               kfree(outdata);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       kfree(outdata);
+device_read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_accel_match_scale(&st_accel_sensors[adata->index=
],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, !powerdown);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+                               st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown =
, 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+       .write_raw =3D &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                               &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai)=
;
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+
+       adata->fullscale =3D ST_ACCEL_FS_AVL_2G;
+       adata->odr =3D 100;
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_accel_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev, irq);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..87b50e1
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_accel_i2c_read_byte_error:
+       return err < 0 ? err : 0;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_accel_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D st_accel_i2c_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..fbc199d
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,178 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D &tx,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       if ((adata->multiread_bit =3D=3D true) && (len > 1))
+               tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               tx =3D reg_addr | ACC_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_error;
+
+       return len;
+
+acc_spi_read_error:
+       return err;
+}
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       return st_accel_spi_read(adata, reg_addr, 1, res_byte);
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       return st_accel_spi_read(adata, reg_addr, len, data);
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 tx[2], *data_write;
+
+       struct spi_transfer xfers =3D {
+               .tx_buf =3D tx,
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       data_write =3D kmalloc(sizeof(*data_write), GFP_KERNEL);
+       if (data_write =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto alloc_memory_error;
+       }
+       *data_write =3D data;
+
+       tx[0] =3D reg_addr;
+       tx[1] =3D *data_write;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       kfree(data_write);
+
+alloc_memory_error:
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_accel_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D st_accel_spi_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..68813f7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..9e0abd6
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
irq)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-23 16:10                                               ` Denis CIOCCA
@ 2012-11-24 16:23                                                 ` Jonathan Cameron
  2012-11-26 16:57                                                   ` Denis CIOCCA
                                                                     ` (2 more replies)
  0 siblings, 3 replies; 42+ messages in thread
From: Jonathan Cameron @ 2012-11-24 16:23 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/23/2012 04:10 PM, Denis CIOCCA wrote:
> Hi Jonathan and Lars-Peter,
>
> I have modified the source code but I am not sure if the spi dma code
> management is correctly because this part is new for me.
Comments on that below. It's much simpler than what you have done.
>
> About the complexity of channels management in the buffer code, I think
> the code is not too difficult and it is possible leave this code as now.
> Are you agree?
Yeah, that bit is fine.
>
>
> I attach the patch.
>
> Best regards,
>
> Denis
>
>

...
> diff --git a/drivers/iio/accel/st_accel_spi.c
> b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..fbc199d
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,178 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ           0x80;
> +#define ACC_SPI_MULTIREAD      0xc0
> +
> +static int st_accel_spi_read(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
This is on the stack. Needs to be on the heap to ensure alignment
and either it has to be on it's own or alignment to a cacheline
must be enforced.

u8 *tx = kmalloc(sizeof(tx), GFP_KERNEL);

Also note that data must also be dynamically allocated (from a quick
look it is).
> +       u8 tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = &tx,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = data,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       if ((adata->multiread_bit == true) && (len > 1))
> +               tx = reg_addr | ACC_SPI_MULTIREAD;
> +       else
> +               tx = reg_addr | ACC_SPI_READ;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       if (err)
> +               goto acc_spi_read_error;
> +
> +       return len;
> +
> +acc_spi_read_error:
> +       return err;
> +}
> +
> +static int st_accel_spi_read_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       return st_accel_spi_read(adata, reg_addr, 1, res_byte);
> +}
> +
> +static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       return st_accel_spi_read(adata, reg_addr, len, data);
> +}
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 tx[2], *data_write;
> +

Errr.  Not sure what you are up to. It's simply a case of allocating
tx on the heap, not the stack.  This is true for anything that goes
in tx_buf or rx_buf of an spi_transfer structure;


struct spi_transfer xfers = {
       .bits_per_word = 8,
       .len = 2,
};
u8 *tx;

tx = kmalloc(sizeof(u8)*2);
tx[0] = reg_addr;
tx[1] = data_write;
xfers.tx_buf = tx;
...


kfree(tx);
}
> +       struct spi_transfer xfers = {
> +               .tx_buf = tx,
> +               .bits_per_word = 8,
> +               .len = 2,
> +       };
> +
> +       data_write = kmalloc(sizeof(*data_write), GFP_KERNEL);
> +       if (data_write == NULL) {
> +               err = -ENOMEM;
> +               goto alloc_memory_error;
> +       }
> +       *data_write = data;
> +
> +       tx[0] = reg_addr;
> +       tx[1] = *data_write;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers, &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       kfree(data_write);
> +
> +alloc_memory_error:
> +       return err;
> +}
> +
...

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-24 16:23                                                 ` Jonathan Cameron
@ 2012-11-26 16:57                                                   ` Denis CIOCCA
  2012-11-27 11:52                                                   ` Denis CIOCCA
  2012-11-27 15:36                                                   ` STMicroelectronics gyroscopes driver Denis CIOCCA
  2 siblings, 0 replies; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-26 16:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Jonathan,

> Comments on that below. It's much simpler than what you have done.

sorry but I don't had understand very well what was the problem!
Now, maybe, I have done the modification in the correct way.

Best regards,

Denis




 From c9c33e9cda2b6e23fb46f9f15eacf96babe729f8 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 3/3] iio:accel: Add STMicroelectronics accelerometers drive=
r

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
  drivers/iio/accel/Kconfig                        |   37 +
  drivers/iio/accel/Makefile                       |    6 +
  drivers/iio/accel/st_accel_buffer.c              |  177 ++++
  drivers/iio/accel/st_accel_core.c                | 1171
++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c                 |  128 +++
  drivers/iio/accel/st_accel_spi.c                 |  182 ++++
  drivers/iio/accel/st_accel_trigger.c             |   83 ++
  include/linux/iio/accel/st_accel.h               |  102 ++
  9 files changed, 1893 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..d089f89
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,177 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int i, n, len;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, buf);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       buf);
+               } else {
+                       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS];
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       if (len < 0)
+                               goto read_data_channels_error;
+
+                       for (i =3D 0; i < n * ST_ACCEL_NUMBER_DATA_CHANNELS=
;
+                                                                       i++=
) {
+                               if (i < n)
+                                       buf[i] =3D rx_array[i];
+                               else
+                                       buf[i] =3D rx_array[n + i];
+                       }
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       buf);
+               break;
+       default:
+               len =3D -EINVAL;
+               goto read_data_channels_error;
+       }
+       if (len !=3D ST_ACCEL_BYTE_FOR_CHANNEL*n) {
+               len =3D -EIO;
+               goto read_data_channels_error;
+       }
+
+read_data_channels_error:
+       return len;
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       int len;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+       if (len < 0)
+               goto st_accel_get_buffer_element_error;
+
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffers(indio_dev->buffer, adata->buffer_data);
+
+st_accel_get_buffer_element_error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..932cd96
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1171 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
+                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
+                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err;
+       u8 *prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       prev_data =3D kmalloc(sizeof(prev_data), GFP_KERNEL);
+       if (prev_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto st_accel_write_data_with_mask_error;
+       }
+
+       err =3D adata->read_byte(adata, reg_addr, prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((*prev_data & (~mask)) | ((data << __ffs(mask)) & mas=
k));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+                       int scale, struct st_accel_fullscale_available *fs_=
out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, bool value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+               (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 *outdata;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto device_read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto device_read_error;
+                       } else {
+                               outdata =3D kmalloc(sizeof(*outdata*
+                                       ST_ACCEL_BYTE_FOR_CHANNEL), GFP_KER=
NEL);
+                               if (outdata =3D=3D NULL) {
+                                       err =3D -ENOMEM;
+                                       goto device_read_error;
+                               }
+
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)le16_to_cpup((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               kfree(outdata);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       kfree(outdata);
+device_read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_accel_match_scale(&st_accel_sensors[adata->index=
],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, !powerdown);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+                               st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown =
, 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+       .write_raw =3D &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                               &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 *wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       wai =3D kmalloc(sizeof(wai), GFP_KERNEL);
+       if (wai =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto st_accel_iio_probe_error;
+       }
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, *wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+
+       adata->fullscale =3D ST_ACCEL_FS_AVL_2G;
+       adata->odr =3D 100;
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_accel_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev, irq);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..87b50e1
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_accel_i2c_read_byte_error:
+       return err < 0 ? err : 0;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_accel_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D st_accel_i2c_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..51a00cc
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,182 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       tx =3D kmalloc(sizeof(tx), GFP_KERNEL);
+       if (tx =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto acc_spi_read_error;
+       }
+
+       if ((adata->multiread_bit =3D=3D true) && (len > 1))
+               *tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               *tx =3D reg_addr | ACC_SPI_READ;
+
+       xfers[0].tx_buf =3D tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_error;
+
+       return len;
+
+acc_spi_read_error:
+       return err;
+}
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       return st_accel_spi_read(adata, reg_addr, 1, res_byte);
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       return st_accel_spi_read(adata, reg_addr, len, data);
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers =3D {
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       tx =3D kmalloc(sizeof(tx)*2, GFP_KERNEL);
+       if (tx =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto alloc_memory_error;
+       }
+
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       xfers.tx_buf =3D tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+alloc_memory_error:
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_accel_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D st_accel_spi_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..68813f7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..9e0abd6
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
irq)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-24 16:23                                                 ` Jonathan Cameron
  2012-11-26 16:57                                                   ` Denis CIOCCA
@ 2012-11-27 11:52                                                   ` Denis CIOCCA
  2012-11-29  9:46                                                     ` Lars-Peter Clausen
  2012-11-27 15:36                                                   ` STMicroelectronics gyroscopes driver Denis CIOCCA
  2 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-27 11:52 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi everybody,

I attach the new modified patch with corrections about DMA using SPI.
Best regards,

Denis




 From 1b3c4eb307c1083cafa3dba6f1777f8438f4a6ad Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 3/3] iio:accel: Add STMicroelectronics accelerometers drive=
r

This patch adds generic accelerometer driver for STMicroelectronics
accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
  drivers/iio/accel/Kconfig                        |   37 +
  drivers/iio/accel/Makefile                       |    6 +
  drivers/iio/accel/st_accel_buffer.c              |  177 ++++
  drivers/iio/accel/st_accel_core.c                | 1171
++++++++++++++++++++++
  drivers/iio/accel/st_accel_i2c.c                 |  128 +++
  drivers/iio/accel/st_accel_spi.c                 |  182 ++++
  drivers/iio/accel/st_accel_trigger.c             |   83 ++
  include/linux/iio/accel/st_accel.h               |  102 ++
  9 files changed, 1893 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 drivers/iio/accel/st_accel_buffer.c
  create mode 100644 drivers/iio/accel/st_accel_core.c
  create mode 100644 drivers/iio/accel/st_accel_i2c.c
  create mode 100644 drivers/iio/accel/st_accel_spi.c
  create mode 100644 drivers/iio/accel/st_accel_trigger.c
  create mode 100644 include/linux/iio/accel/st_accel.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..d65e66a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3AXIS
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_ACCEL_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_ACCEL_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_ACCEL_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..1541236 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+
+st_accel-y :=3D st_accel_core.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o
+obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel_buffer.c
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..e8419be
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,177 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int i, n, len;
+       u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS];
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL, buf);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_ACCEL_BYTE_FOR_CHANNEL*n,
+                                       buf);
+               } else {
+                       u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS];
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_ACCEL_BYTE_FOR_CHANNEL*
+                               ST_ACCEL_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       if (len < 0)
+                               goto read_data_channels_error;
+
+                       for (i =3D 0; i < n * ST_ACCEL_NUMBER_DATA_CHANNELS=
;
+                                                                       i++=
) {
+                               if (i < n)
+                                       buf[i] =3D rx_array[i];
+                               else
+                                       buf[i] =3D rx_array[n + i];
+                       }
+                       len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+                       buf);
+               break;
+       default:
+               len =3D -EINVAL;
+               goto read_data_channels_error;
+       }
+       if (len !=3D ST_ACCEL_BYTE_FOR_CHANNEL*n) {
+               len =3D -EIO;
+               goto read_data_channels_error;
+       }
+
+read_data_channels_error:
+       return len;
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p)
+{
+       int len;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_accel_get_buffer_element(indio_dev, adata->buffer_data);
+       if (len < 0)
+               goto st_accel_get_buffer_element_error;
+
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);
+
+st_accel_get_buffer_element_error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_accel_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(adata->buffer_data);
+st_accel_buffer_postenable_error:
+       return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto st_accel_buffer_predisable_error;
+
+       kfree(adata->buffer_data);
+
+st_accel_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_accel_buffer_postenable,
+       .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+                       &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
diff --git a/drivers/iio/accel/st_accel_core.c
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..791161e
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,1171 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ST_ACCEL_FULLSCALE_AVL_MAX             5
+#define ST_ACCEL_ODR_LIST_MAX                  10
+#define ST_ACCEL_ENABLE_ALL_CHANNELS           0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR          0x2a
+#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR          0x2b
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR          0x2c
+#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR          0x2d
+#define ST_ACCEL_DEFAULT_WAI_ADDRESS           0x0f
+#define ST_ACCEL_DEFAULT_POWER_ON_VALUE                0x01
+#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE       0x00
+#define ST_ACCEL_DEFAULT_12_REALBITS           12
+#define ST_ACCEL_DEFAULT_16_REALBITS           16
+#define ST_ACCEL_DEFAULT_AXIS_ADDR             0x20
+#define ST_ACCEL_DEFAULT_AXIS_MASK             0x07
+#define ST_ACCEL_DEFAULT_AXIS_N_BIT            3
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+struct st_accel_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_accel_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX];
+};
+
+struct st_accel_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_accel_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_accel_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_M=
AX];
+};
+
+struct st_accel_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_accel_interrupt_generator ig1;
+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type =3D IIO_ACCEL, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+       ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_accel_sensors - ST accel sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_accel_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_accel_odr odr;
+       struct st_accel_power pw;
+       struct st_accel_axis enable_axis;
+       struct st_accel_fullscale fs;
+       struct st_accel_bdu bdu;
+       struct st_accel_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
+                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_ACCEL_DEFAULT_POWER_ON_VALUE,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
+                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+                       .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 =
data)
+{
+       int err;
+       u8 *prev_data;
+       u8 new_data;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       prev_data =3D kmalloc(sizeof(*prev_data), GFP_KERNEL);
+       if (prev_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto st_accel_write_data_with_mask_error;
+       }
+
+       err =3D adata->read_byte(adata, reg_addr, prev_data);
+       if (err < 0)
+               goto st_accel_write_data_with_mask_error;
+
+       new_data =3D ((*prev_data & (~mask)) | ((data << __ffs(mask)) & mas=
k));
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+       return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+               unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+               unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+                       int scale, struct st_accel_fullscale_available *fs_=
out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_accel_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+                       st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+               st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_accel_set_dataready_irq_error;
+       }
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+               st_accel_sensors[adata->index].drdy_irq.addr,
+               st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+
+st_accel_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_accel_bdu *bdu, bool value)
+{
+       return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+                               struct st_accel_odr_available *odr_availabl=
e)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+               st_accel_sensors[adata->index].pw.addr) &&
+               (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].odr.addr,
+                               st_accel_sensors[adata->index].odr.mask,
+                               st_accel_sensors[adata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                       st_accel_sensors[adata->index].enable_axis.addr,
+                       st_accel_sensors[adata->index].enable_axis.mask,
+                       ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+               if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+                               st_accel_sensors[adata->index].pw.addr) &&
+                       (st_accel_sensors[adata->index].odr.mask =3D=3D
+                               st_accel_sensors[adata->index].pw.mask)) {
+                       err =3D st_accel_match_odr(
+                               &st_accel_sensors[adata->index], adata->odr=
,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].pw.addr,
+                               st_accel_sensors[adata->index].pw.mask,
+                               st_accel_sensors[adata->index].pw.num_bit,
+                               st_accel_sensors[adata->index].pw.value_off=
);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_accel_fullscale_available *fs_avl=
)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_write_data_with_mask(indio_dev,
+                               st_accel_sensors[adata->index].fs.addr,
+                               st_accel_sensors[adata->index].fs.mask,
+                               st_accel_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_accel_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_accel_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 *outdata;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto device_read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto device_read_error;
+                       } else {
+                               outdata =3D kmalloc(sizeof(*outdata*
+                                       ST_ACCEL_BYTE_FOR_CHANNEL), GFP_KER=
NEL);
+                               if (outdata =3D=3D NULL) {
+                                       err =3D -ENOMEM;
+                                       goto device_read_error;
+                               }
+
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)le16_to_cpup((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               kfree(outdata);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       kfree(outdata);
+device_read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_accel_match_scale(&st_accel_sensors[adata->index=
],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+       return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_accel_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_accel_sysfs_set_sampling_frequency_error;
+
+       adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_accel_set_enable(indio_dev, !powerdown);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=
_avl);
+                                                                       i++=
) {
+               if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+                               st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown =
, 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_accel_sysfs_get_sampling_frequency,
+                                       st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+       .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_accel_attribute_group,
+       .read_raw =3D &st_accel_read_raw,
+       .write_raw =3D &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_accel_odr_available odr_out;
+       struct st_accel_fullscale_available fs_out;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_fs(&st_accel_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_accel_match_odr(&st_accel_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_accel_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err =3D st_accel_set_bdu(indio_dev,
+                               &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 *wai;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+       indio_dev->info =3D &acc_info;
+
+       wai =3D kmalloc(sizeof(*wai), GFP_KERNEL);
+       if (wai =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto st_accel_iio_probe_error;
+       }
+
+       err =3D st_accel_get_wai_device(indio_dev,
+                                       ST_ACCEL_DEFAULT_WAI_ADDRESS, wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_check_device_list(indio_dev, *wai);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+       indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+
+       adata->fullscale =3D ST_ACCEL_FS_AVL_2G;
+       adata->odr =3D 100;
+
+       err =3D st_accel_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       err =3D st_accel_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_accel_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_accel_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_accel_remove_trigger(indio_dev, irq);
+       st_accel_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..87b50e1
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+#define ST_ACCEL_I2C_MULTIREAD                 0x80
+
+static int st_accel_i2c_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_accel_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_accel_i2c_read_byte_error:
+       return err < 0 ? err : 0;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_accel_i2c_read_byte;
+       adata->write_byte =3D st_accel_i2c_write_byte;
+       adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_accel_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-i2c",
+       },
+       .probe =3D st_accel_i2c_probe,
+       .remove =3D st_accel_i2c_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..6df1440
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,182 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+
+#define ACC_SPI_READ           0x80;
+#define ACC_SPI_MULTIREAD      0xc0
+
+static int st_accel_spi_read(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D data,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       tx =3D kmalloc(sizeof(*tx), GFP_KERNEL);
+       if (tx =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto acc_spi_read_error;
+       }
+
+       if ((adata->multiread_bit =3D=3D true) && (len > 1))
+               *tx =3D reg_addr | ACC_SPI_MULTIREAD;
+       else
+               *tx =3D reg_addr | ACC_SPI_READ;
+
+       xfers[0].tx_buf =3D tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_error;
+
+       return len;
+
+acc_spi_read_error:
+       return err;
+}
+
+static int st_accel_spi_read_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       return st_accel_spi_read(adata, reg_addr, 1, res_byte);
+}
+
+static int st_accel_spi_read_multiple_byte(struct st_accel_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       return st_accel_spi_read(adata, reg_addr, len, data);
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers =3D {
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       tx =3D kmalloc(sizeof(*tx)*2, GFP_KERNEL);
+       if (tx =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto alloc_memory_error;
+       }
+
+       tx[0] =3D reg_addr;
+       tx[1] =3D data;
+       xfers.tx_buf =3D tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+
+alloc_memory_error:
+       return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_accel_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       adata->read_byte =3D st_accel_spi_read_byte;
+       adata->write_byte =3D st_accel_spi_write_byte;
+       adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte;
+
+       err =3D st_accel_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_accel_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_accel_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-accel-spi",
+       },
+       .probe =3D st_accel_spi_probe,
+       .remove =3D st_accel_spi_remove,
+       .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_trigger.c
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..68813f7
--- /dev/null
+++ b/drivers/iio/accel/st_accel_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+
+static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_accel_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
diff --git a/include/linux/iio/accel/st_accel.h
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..9e0abd6
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#define LSM303DLH_ACCEL_DEV_NAME       "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME      "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME          "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME         "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME       "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME         "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME         "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME       "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME       "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME          "lsm330_accel"
+
+#define ST_ACCEL_NUMBER_ALL_CHANNELS   4
+#define ST_ACCEL_NUMBER_DATA_CHANNELS  3
+#define ST_ACCEL_BYTE_FOR_CHANNEL      2
+#define ST_ACCEL_SCAN_X                        0
+#define ST_ACCEL_SCAN_Y                        1
+#define ST_ACCEL_SCAN_Z                        2
+
+/**
+ * struct st_accel_data - ST accel device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_accel_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_accel_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_accel_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+       int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+                                                       int len, u8 *data);
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
irq)
+{
+       return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* STMicroelectronics gyroscopes driver.
  2012-11-24 16:23                                                 ` Jonathan Cameron
  2012-11-26 16:57                                                   ` Denis CIOCCA
  2012-11-27 11:52                                                   ` Denis CIOCCA
@ 2012-11-27 15:36                                                   ` Denis CIOCCA
  2012-11-29  9:51                                                     ` Lars-Peter Clausen
  2 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-27 15:36 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Denis Ciocca, Lars-Peter Clausen, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi guys,

based on the driver for accelerometers, I wrote the driver for all our
last mems gyroscopes.
Best regards,

Denis


 From 6634b1b6f2cb2887dbc00a802cc365fb26ce7be0 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Tue, 27 Nov 2012 16:20:40 +0100
Subject: [PATCH] iio:gyro: Add STMicroelectronics gyroscopes driver

This patch adds generic gyroscope driver for STMicroelectronics
gyroscopes, currently it supports:
L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DLC, LSM9DS0,
L3G4IS, LSM330
---
  drivers/iio/gyro/Kconfig           |   37 ++
  drivers/iio/gyro/Makefile          |    6 +
  drivers/iio/gyro/st_gyro_buffer.c  |  177 +++++++
  drivers/iio/gyro/st_gyro_core.c    |  929
++++++++++++++++++++++++++++++++++++
  drivers/iio/gyro/st_gyro_i2c.c     |  125 +++++
  drivers/iio/gyro/st_gyro_spi.c     |  179 +++++++
  drivers/iio/gyro/st_gyro_trigger.c |   83 ++++
  include/linux/iio/gyro/st_gyro.h   |   99 ++++
  8 files changed, 1635 insertions(+), 0 deletions(-)
  create mode 100644 drivers/iio/gyro/st_gyro_buffer.c
  create mode 100644 drivers/iio/gyro/st_gyro_core.c
  create mode 100644 drivers/iio/gyro/st_gyro_i2c.c
  create mode 100644 drivers/iio/gyro/st_gyro_spi.c
  create mode 100644 drivers/iio/gyro/st_gyro_trigger.c
  create mode 100644 include/linux/iio/gyro/st_gyro.h

diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 21e27e2..d105242 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -13,4 +13,41 @@ config HID_SENSOR_GYRO_3D
          Say yes here to build support for the HID SENSOR
          Gyroscope 3D.

+config ST_GYRO_3AXIS
+       tristate "STMicroelectronics gyroscopes 3-Axis Driver"
+       depends on (I2C || SPI) && SYSFS
+       help
+         Say yes here to build support for STMicroelectronics gyroscopes:
+         L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DL, LSM330DLC,
+         LSM9DS0, L3G4IS, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_gyro.
+
+config ST_GYRO_3AXIS_I2C
+       tristate "support I2C bus connection"
+       depends on ST_GYRO_3AXIS && I2C
+       help
+         Say yes here to build I2C support for STMicroelectronics gyroscopes.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_gyro_i2c.
+
+config ST_GYRO_3AXIS_SPI
+       tristate "support SPI bus connection"
+       depends on ST_GYRO_3AXIS && SPI_MASTER
+       help
+         Say yes here to build SPI support for STMicroelectronics gyroscopes.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_gyro_spi.
+
+config ST_GYRO_3AXIS_TRIGGERED_BUFFER
+       tristate "support triggered buffer"
+       depends on ST_GYRO_3AXIS
+       select IIO_TRIGGERED_BUFFER
+       select IIO_BUFFER
+       help
+         Default trigger and buffer for STMicroelectronics gyroscopes driver.
+
  endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 8a895d9..d8ad80b 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -3,3 +3,9 @@
  #

  obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
+
+st_gyro-y := st_gyro_core.o
+obj-$(CONFIG_ST_GYRO_3AXIS_I2C) += st_gyro_i2c.o
+obj-$(CONFIG_ST_GYRO_3AXIS_SPI) += st_gyro_spi.o
+obj-$(CONFIG_ST_GYRO_3AXIS_TRIGGERED_BUFFER) += st_gyro_trigger.o
st_gyro_buffer.o
+obj-$(CONFIG_ST_GYRO_3AXIS) += st_gyro.o
diff --git a/drivers/iio/gyro/st_gyro_buffer.c
b/drivers/iio/gyro/st_gyro_buffer.c
new file mode 100644
index 0000000..3ae3b62
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_buffer.c
@@ -0,0 +1,177 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+
+
+#define ST_GYRO_ENABLE_ALL_CHANNELS            0x07
+
+static int st_gyro_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+       int i, n, len;
+       u8 reg_addr[ST_GYRO_NUMBER_DATA_CHANNELS];
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       for (i = 0; i < ST_GYRO_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] = indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len = gdata->read_multiple_byte(gdata, reg_addr[0],
+                                       ST_GYRO_BYTE_FOR_CHANNEL, buf);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) == ST_GYRO_BYTE_FOR_CHANNEL) {
+                       len = gdata->read_multiple_byte(gdata, reg_addr[0],
+                                       ST_GYRO_BYTE_FOR_CHANNEL*n,
+                                       buf);
+               } else {
+                       u8 rx_array[ST_GYRO_BYTE_FOR_CHANNEL*
+                               ST_GYRO_NUMBER_DATA_CHANNELS];
+                       len = gdata->read_multiple_byte(gdata, reg_addr[0],
+                               ST_GYRO_BYTE_FOR_CHANNEL*
+                               ST_GYRO_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       if (len < 0)
+                               goto read_data_channels_error;
+
+                       for (i = 0; i < n * ST_GYRO_NUMBER_DATA_CHANNELS;
+                                                                       i++) {
+                               if (i < n)
+                                       buf[i] = rx_array[i];
+                               else
+                                       buf[i] = rx_array[n + i];
+                       }
+                       len = ST_GYRO_BYTE_FOR_CHANNEL*n;
+               }
+               break;
+       case 3:
+               len = gdata->read_multiple_byte(gdata, reg_addr[0],
+                       ST_GYRO_BYTE_FOR_CHANNEL*ST_GYRO_NUMBER_DATA_CHANNELS,
+                       buf);
+               break;
+       default:
+               len = -EINVAL;
+               goto read_data_channels_error;
+       }
+       if (len != ST_GYRO_BYTE_FOR_CHANNEL*n) {
+               len = -EIO;
+               goto read_data_channels_error;
+       }
+
+read_data_channels_error:
+       return len;
+}
+
+static irqreturn_t st_gyro_trigger_handler(int irq, void *p)
+{
+       int len;
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       len = st_gyro_get_buffer_element(indio_dev, gdata->buffer_data);
+       if (len < 0)
+               goto st_gyro_get_buffer_element_error;
+
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)gdata->buffer_data +
+                               ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+       iio_push_to_buffer(indio_dev->buffer, gdata->buffer_data);
+
+st_gyro_get_buffer_element_error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit = 0x00;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (gdata->buffer_data == NULL) {
+               err = -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i = 0; i < ST_GYRO_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |= (1 << i);
+
+       err = st_gyro_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_gyro_buffer_postenable_error;
+
+       err = iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+allocate_memory_error:
+       kfree(gdata->buffer_data);
+st_gyro_buffer_postenable_error:
+       return err;
+}
+
+static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       err = iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_gyro_buffer_predisable_error;
+
+       err = st_gyro_set_axis_enable(indio_dev, ST_GYRO_ENABLE_ALL_CHANNELS);
+       if (err < 0)
+               goto st_gyro_buffer_predisable_error;
+
+       kfree(gdata->buffer_data);
+
+st_gyro_buffer_predisable_error:
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
+       .preenable = &iio_sw_buffer_preenable,
+       .postenable = &st_gyro_buffer_postenable,
+       .predisable = &st_gyro_buffer_predisable,
+};
+
+int st_gyro_allocate_ring(struct iio_dev *indio_dev)
+{
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+                       &st_gyro_trigger_handler, &st_gyro_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_gyro_allocate_ring);
+
+void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_gyro_deallocate_ring);
diff --git a/drivers/iio/gyro/st_gyro_core.c
b/drivers/iio/gyro/st_gyro_core.c
new file mode 100644
index 0000000..c2215af
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -0,0 +1,929 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+
+
+#define ST_GYRO_FULLSCALE_AVL_MAX              3
+#define ST_GYRO_ODR_LIST_MAX                   4
+#define ST_GYRO_ENABLE_ALL_CHANNELS            0x07
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_GYRO_DEFAULT_OUT_X_L_ADDR           0x28
+#define ST_GYRO_DEFAULT_OUT_X_H_ADDR           0x29
+#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR           0x2a
+#define ST_GYRO_DEFAULT_OUT_Y_H_ADDR           0x2b
+#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR           0x2c
+#define ST_GYRO_DEFAULT_OUT_Z_H_ADDR           0x2d
+#define ST_GYRO_DEFAULT_WAI_ADDRESS            0x0f
+#define ST_GYRO_DEFAULT_POWER_ON_VALUE         0x01
+#define ST_GYRO_DEFAULT_POWER_OFF_VALUE                0x00
+#define ST_GYRO_DEFAULT_16_REALBITS            16
+#define ST_GYRO_DEFAULT_AXIS_ADDR              0x20
+#define ST_GYRO_DEFAULT_AXIS_MASK              0x07
+#define ST_GYRO_DEFAULT_AXIS_N_BIT             3
+
+/* FULLSCALE */
+#define ST_GYRO_FS_AVL_250DPS                  250
+#define ST_GYRO_FS_AVL_500DPS                  500
+#define ST_GYRO_FS_AVL_2000DPS                 2000
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_GYRO_1_WAI_EXP                      0xd3
+#define ST_GYRO_1_ODR_ADDR                     0x20
+#define ST_GYRO_1_ODR_MASK                     0xc0
+#define ST_GYRO_1_ODR_N_BIT                    2
+#define ST_GYRO_1_ODR_AVL_100HZ_VAL            0x00
+#define ST_GYRO_1_ODR_AVL_200HZ_VAL            0x01
+#define ST_GYRO_1_ODR_AVL_400HZ_VAL            0x02
+#define ST_GYRO_1_ODR_AVL_800HZ_VAL            0x03
+#define ST_GYRO_1_PW_ADDR                      0x20
+#define ST_GYRO_1_PW_MASK                      0x08
+#define ST_GYRO_1_PW_N_BIT                     1
+#define ST_GYRO_1_FS_N_BIT                     2
+#define ST_GYRO_1_FS_ADDR                      0x23
+#define ST_GYRO_1_FS_MASK                      0x30
+#define ST_GYRO_1_FS_AVL_250_VAL               0x00
+#define ST_GYRO_1_FS_AVL_500_VAL               0x01
+#define ST_GYRO_1_FS_AVL_2000_VAL              0x02
+#define ST_GYRO_1_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_1_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_1_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_1_BDU_ADDR                     0x23
+#define ST_GYRO_1_BDU_MASK                     0x80
+#define ST_GYRO_1_DRDY_IRQ_ADDR                        0x22
+#define ST_GYRO_1_DRDY_IRQ_MASK                        0x08
+#define ST_GYRO_1_MULTIREAD_BIT                        true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_GYRO_2_WAI_EXP                      0xd4
+#define ST_GYRO_2_ODR_ADDR                     0x20
+#define ST_GYRO_2_ODR_MASK                     0xc0
+#define ST_GYRO_2_ODR_N_BIT                    2
+#define ST_GYRO_2_ODR_AVL_95HZ_VAL             0x00
+#define ST_GYRO_2_ODR_AVL_190HZ_VAL            0x01
+#define ST_GYRO_2_ODR_AVL_380HZ_VAL            0x02
+#define ST_GYRO_2_ODR_AVL_760HZ_VAL            0x03
+#define ST_GYRO_2_PW_ADDR                      0x20
+#define ST_GYRO_2_PW_MASK                      0x08
+#define ST_GYRO_2_PW_N_BIT                     1
+#define ST_GYRO_2_FS_N_BIT                     2
+#define ST_GYRO_2_FS_ADDR                      0x23
+#define ST_GYRO_2_FS_MASK                      0x30
+#define ST_GYRO_2_FS_AVL_250_VAL               0x00
+#define ST_GYRO_2_FS_AVL_500_VAL               0x01
+#define ST_GYRO_2_FS_AVL_2000_VAL              0x02
+#define ST_GYRO_2_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_2_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_2_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_2_BDU_ADDR                     0x23
+#define ST_GYRO_2_BDU_MASK                     0x80
+#define ST_GYRO_2_DRDY_IRQ_ADDR                        0x22
+#define ST_GYRO_2_DRDY_IRQ_MASK                        0x08
+#define ST_GYRO_2_MULTIREAD_BIT                        true
+
+struct st_gyro_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_gyro_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_gyro_odr_available odr_avl[ST_GYRO_ODR_LIST_MAX];
+};
+
+struct st_gyro_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_gyro_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_gyro_fullscale_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_gyro_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_gyro_fullscale_available fs_avl[ST_GYRO_FULLSCALE_AVL_MAX];
+};
+
+struct st_gyro_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_gyro_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_gyro_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_gyro_interrupt_generator ig1;
+};
+
+#define ST_GYRO_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+       .type = IIO_ANGL_VEL, \
+       .modified = 1, \
+       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index = index, \
+       .channel2 = mod, \
+       .address = addr, \
+       .scan_type = { \
+               .sign = 's', \
+               .realbits = bits, \
+               .shift = 16 - bits, \
+               .storagebits = 16, \
+               .endianness = endian, \
+       }, \
+}
+
+static const struct iio_chan_spec st_gyro_16bit_channels[] = {
+       ST_GYRO_LSM_CHANNELS(ST_GYRO_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_GYRO_DEFAULT_16_REALBITS, ST_GYRO_DEFAULT_OUT_X_L_ADDR),
+       ST_GYRO_LSM_CHANNELS(ST_GYRO_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_GYRO_DEFAULT_16_REALBITS, ST_GYRO_DEFAULT_OUT_Y_L_ADDR),
+       ST_GYRO_LSM_CHANNELS(ST_GYRO_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_GYRO_DEFAULT_16_REALBITS, ST_GYRO_DEFAULT_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+/**
+ * struct st_gyro_sensors - ST gyro sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+static const struct st_gyro_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_gyro_odr odr;
+       struct st_gyro_power pw;
+       struct st_gyro_axis enable_axis;
+       struct st_gyro_fullscale fs;
+       struct st_gyro_bdu bdu;
+       struct st_gyro_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+} st_gyro_sensors[] = {
+       {
+               .wai = ST_GYRO_1_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
+               .odr = {
+                       .addr = ST_GYRO_1_ODR_ADDR,
+                       .mask = ST_GYRO_1_ODR_MASK,
+                       .num_bit = ST_GYRO_1_ODR_N_BIT,
+                       .odr_avl = {
+                               { 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
+                       },
+               },
+               .pw = {
+                       .addr = ST_GYRO_1_PW_ADDR,
+                       .mask = ST_GYRO_1_PW_MASK,
+                       .num_bit = ST_GYRO_1_PW_N_BIT,
+                       .value_on = ST_GYRO_DEFAULT_POWER_ON_VALUE,
+                       .value_off = ST_GYRO_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis = {
+                       .addr = ST_GYRO_DEFAULT_AXIS_ADDR,
+                       .mask = ST_GYRO_DEFAULT_AXIS_MASK,
+               },
+               .fs = {
+                       .addr = ST_GYRO_1_FS_ADDR,
+                       .mask = ST_GYRO_1_FS_MASK,
+                       .num_bit = ST_GYRO_1_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_GYRO_FS_AVL_250DPS,
+                                       .value = ST_GYRO_1_FS_AVL_250_VAL,
+                                       .gain = ST_GYRO_1_FS_AVL_250_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_GYRO_FS_AVL_500DPS,
+                                       .value = ST_GYRO_1_FS_AVL_500_VAL,
+                                       .gain = ST_GYRO_1_FS_AVL_500_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_GYRO_FS_AVL_2000DPS,
+                                       .value = ST_GYRO_1_FS_AVL_2000_VAL,
+                                       .gain = ST_GYRO_1_FS_AVL_2000_GAIN,
+                               },
+                       },
+               },
+               .bdu = {
+                       .addr = ST_GYRO_1_BDU_ADDR,
+                       .mask = ST_GYRO_1_BDU_MASK,
+               },
+               .drdy_irq = {
+                       .addr = ST_GYRO_1_DRDY_IRQ_ADDR,
+                       .mask = ST_GYRO_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
+       },
+       {
+               .wai = ST_GYRO_2_WAI_EXP,
+               .ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
+               .odr = {
+                       .addr = ST_GYRO_2_ODR_ADDR,
+                       .mask = ST_GYRO_2_ODR_MASK,
+                       .num_bit = ST_GYRO_2_ODR_N_BIT,
+                       .odr_avl = {
+                               { 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
+                               { 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
+                               { 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
+                               { 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
+                       },
+               },
+               .pw = {
+                       .addr = ST_GYRO_2_PW_ADDR,
+                       .mask = ST_GYRO_2_PW_MASK,
+                       .num_bit = ST_GYRO_2_PW_N_BIT,
+                       .value_on = ST_GYRO_DEFAULT_POWER_ON_VALUE,
+                       .value_off = ST_GYRO_DEFAULT_POWER_OFF_VALUE,
+               },
+               .enable_axis = {
+                       .addr = ST_GYRO_DEFAULT_AXIS_ADDR,
+                       .mask = ST_GYRO_DEFAULT_AXIS_MASK,
+               },
+               .fs = {
+                       .addr = ST_GYRO_2_FS_ADDR,
+                       .mask = ST_GYRO_2_FS_MASK,
+                       .num_bit = ST_GYRO_2_FS_N_BIT,
+                       .fs_avl = {
+                               [0] = {
+                                       .num = ST_GYRO_FS_AVL_250DPS,
+                                       .value = ST_GYRO_2_FS_AVL_250_VAL,
+                                       .gain = ST_GYRO_2_FS_AVL_250_GAIN,
+                               },
+                               [1] = {
+                                       .num = ST_GYRO_FS_AVL_500DPS,
+                                       .value = ST_GYRO_2_FS_AVL_500_VAL,
+                                       .gain = ST_GYRO_2_FS_AVL_500_GAIN,
+                               },
+                               [2] = {
+                                       .num = ST_GYRO_FS_AVL_2000DPS,
+                                       .value = ST_GYRO_2_FS_AVL_2000_VAL,
+                                       .gain = ST_GYRO_2_FS_AVL_2000_GAIN,
+                               },
+                       },
+               },
+               .bdu = {
+                       .addr = ST_GYRO_2_BDU_ADDR,
+                       .mask = ST_GYRO_2_BDU_MASK,
+               },
+               .drdy_irq = {
+                       .addr = ST_GYRO_2_DRDY_IRQ_ADDR,
+                       .mask = ST_GYRO_2_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
+       },
+};
+
+static int st_gyro_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,
+                                               u8 mask, short num_bit, u8 data)
+{
+       int err;
+       u8 *prev_data;
+       u8 new_data;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       prev_data = kmalloc(sizeof(*prev_data), GFP_KERNEL);
+       if (prev_data == NULL) {
+               err = -ENOMEM;
+               goto st_gyro_write_data_with_mask_error;
+       }
+
+       err = gdata->read_byte(gdata, reg_addr, prev_data);
+       if (err < 0)
+               goto st_gyro_write_data_with_mask_error;
+
+       new_data = ((*prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
+       err = gdata->write_byte(gdata, reg_addr, new_data);
+
+st_gyro_write_data_with_mask_error:
+       return err;
+}
+
+static int st_gyro_match_odr(const struct st_gyro_sensors *sensor,
+               unsigned int odr, struct st_gyro_odr_available *odr_out)
+{
+       int i, ret = -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz == odr) {
+                       odr_out->hz = sensor->odr.odr_avl[i].hz;
+                       odr_out->value = sensor->odr.odr_avl[i].value;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_gyro_match_fs(const struct st_gyro_sensors *sensor,
+               unsigned int fs, struct st_gyro_fullscale_available *fs_out)
+{
+       int i, ret = -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num == fs) {
+                       fs_out->num = sensor->fs.fs_avl[i].num;
+                       fs_out->gain = sensor->fs.fs_avl[i].gain;
+                       fs_out->value = sensor->fs.fs_avl[i].value;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int st_gyro_match_scale(const struct st_gyro_sensors *sensor,
+                       int scale, struct st_gyro_fullscale_available *fs_out)
+{
+       int i, ret = -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain == scale) {
+                       fs_out->num = sensor->fs.fs_avl[i].num;
+                       fs_out->gain = sensor->fs.fs_avl[i].gain;
+                       fs_out->value = sensor->fs.fs_avl[i].value;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_gyro_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_gyro_data *gdata;
+
+       gdata = iio_priv(indio_dev);
+       if (st_gyro_sensors[gdata->index].drdy_irq.ig1.en_addr > 0) {
+               err = st_gyro_write_data_with_mask(indio_dev,
+                       st_gyro_sensors[gdata->index].drdy_irq.ig1.en_addr,
+                       st_gyro_sensors[gdata->index].drdy_irq.ig1.en_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_gyro_set_dataready_irq_error;
+       }
+
+       if (st_gyro_sensors[gdata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+               err = st_gyro_write_data_with_mask(indio_dev,
+               st_gyro_sensors[gdata->index].drdy_irq.ig1.latch_mask_addr,
+               st_gyro_sensors[gdata->index].drdy_irq.ig1.latching_mask, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_gyro_set_dataready_irq_error;
+       }
+
+       err = st_gyro_write_data_with_mask(indio_dev,
+               st_gyro_sensors[gdata->index].drdy_irq.addr,
+               st_gyro_sensors[gdata->index].drdy_irq.mask, 1, (int)enable);
+
+st_gyro_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_gyro_set_dataready_irq);
+
+static int st_gyro_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_gyro_bdu *bdu, bool value)
+{
+       return st_gyro_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
+                                                               1, (u8)value);
+}
+
+static int st_gyro_set_odr(struct iio_dev *indio_dev,
+                               struct st_gyro_odr_available *odr_available)
+{
+       int err;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       if ((st_gyro_sensors[gdata->index].odr.addr ==
+               st_gyro_sensors[gdata->index].pw.addr) &&
+               (st_gyro_sensors[gdata->index].odr.mask ==
+                               st_gyro_sensors[gdata->index].pw.mask)) {
+               if (gdata->enabled == true) {
+                       err = st_gyro_write_data_with_mask(indio_dev,
+                               st_gyro_sensors[gdata->index].odr.addr,
+                               st_gyro_sensors[gdata->index].odr.mask,
+                               st_gyro_sensors[gdata->index].odr.num_bit,
+                               odr_available->value);
+               } else {
+                       gdata->odr = odr_available->hz;
+                       err = 0;
+               }
+       } else {
+               err = st_gyro_write_data_with_mask(indio_dev,
+                               st_gyro_sensors[gdata->index].odr.addr,
+                               st_gyro_sensors[gdata->index].odr.mask,
+                               st_gyro_sensors[gdata->index].odr.num_bit,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_gyro_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       err = st_gyro_write_data_with_mask(indio_dev,
+                       st_gyro_sensors[gdata->index].enable_axis.addr,
+                       st_gyro_sensors[gdata->index].enable_axis.mask,
+                       ST_GYRO_DEFAULT_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+
+static int st_gyro_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err = -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_gyro_odr_available odr_out;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       if (enable) {
+               found = false;
+               tmp_value = st_gyro_sensors[gdata->index].pw.value_on;
+               if ((st_gyro_sensors[gdata->index].odr.addr ==
+                               st_gyro_sensors[gdata->index].pw.addr) &&
+                       (st_gyro_sensors[gdata->index].odr.mask ==
+                               st_gyro_sensors[gdata->index].pw.mask)) {
+                       err = st_gyro_match_odr(
+                               &st_gyro_sensors[gdata->index], gdata->odr,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value = odr_out.value;
+                       found = true;
+               }
+               err = st_gyro_write_data_with_mask(indio_dev,
+                               st_gyro_sensors[gdata->index].pw.addr,
+                               st_gyro_sensors[gdata->index].pw.mask,
+                               st_gyro_sensors[gdata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               gdata->enabled = true;
+               if (found)
+                       gdata->odr = odr_out.hz;
+       } else {
+               err = st_gyro_write_data_with_mask(indio_dev,
+                               st_gyro_sensors[gdata->index].pw.addr,
+                               st_gyro_sensors[gdata->index].pw.mask,
+                               st_gyro_sensors[gdata->index].pw.num_bit,
+                               st_gyro_sensors[gdata->index].pw.value_off);
+               if (err < 0)
+                       goto set_enable_error;
+               gdata->enabled = false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_gyro_set_fullscale(struct iio_dev *indio_dev,
+                               struct st_gyro_fullscale_available *fs_avl)
+{
+       int err;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       err = st_gyro_write_data_with_mask(indio_dev,
+                               st_gyro_sensors[gdata->index].fs.addr,
+                               st_gyro_sensors[gdata->index].fs.mask,
+                               st_gyro_sensors[gdata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_gyro_set_fullscale_error;
+
+       gdata->fullscale = fs_avl->num;
+       gdata->gain = fs_avl->gain;
+       return err;
+
+st_gyro_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_gyro_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mask)
+{
+       int err;
+       u8 *outdata;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+                       err = -EBUSY;
+                       goto device_read_error;
+               } else {
+                       if (!gdata->enabled) {
+                               err = -EIO;
+                               goto device_read_error;
+                       } else {
+                               outdata = kmalloc(sizeof(*outdata*
+                                       ST_GYRO_BYTE_FOR_CHANNEL), GFP_KERNEL);
+                               if (outdata == NULL) {
+                                       err = -ENOMEM;
+                                       goto device_read_error;
+                               }
+
+                               err = gdata->read_multiple_byte(gdata,
+                                       ch->address, ST_GYRO_BYTE_FOR_CHANNEL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val = ((s16)le16_to_cpup((u16 *)outdata))
+                                                       >> ch->scan_type.shift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               kfree(outdata);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val = 0;
+               *val2 = gdata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       kfree(outdata);
+device_read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_gyro_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+       int err;
+       struct st_gyro_fullscale_available fs_out;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err = st_gyro_match_scale(&st_gyro_sensors[gdata->index],
+                                                               val2, &fs_out);
+               if (err < 0)
+                       goto write_error;
+
+               err = st_gyro_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static int st_gyro_check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       for (i = 0; i < ARRAY_SIZE(st_gyro_sensors); i++) {
+               if (st_gyro_sensors[i].wai == wai)
+                       break;
+       }
+       if (i == ARRAY_SIZE(st_gyro_sensors))
+               goto check_device_error;
+
+       gdata->index = i;
+
+       return i;
+
+check_device_error:
+       dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
+       return -ENODEV;
+}
+
+static int st_gyro_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       ret = gdata->read_byte(gdata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
+       return -EIO;
+}
+
+static ssize_t st_gyro_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int err;
+       unsigned int freq;
+       struct st_gyro_odr_available odr_out;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       err = kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err = st_gyro_match_odr(&st_gyro_sensors[gdata->index],
+                                                               freq, &odr_out);
+       if (err < 0)
+               goto st_gyro_sysfs_set_sampling_frequency_error;
+
+       err = st_gyro_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_gyro_sysfs_set_sampling_frequency_error;
+
+       gdata->odr = odr_out.hz;
+
+st_gyro_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_gyro_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", gdata->odr);
+}
+
+static ssize_t st_gyro_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+       err = strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err = st_gyro_set_enable(indio_dev, !powerdown);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_gyro_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!gdata->enabled));
+}
+
+static ssize_t st_gyro_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len = 0;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i = 0; i < ARRAY_SIZE(st_gyro_sensors[gdata->index].fs.fs_avl);
+                                                                       i++) {
+               if (st_gyro_sensors[gdata->index].fs.fs_avl[i].num == 0)
+                       break;
+
+               len += sprintf(buf+len, "0.%06u ",
+                       st_gyro_sensors[gdata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] = '\n';
+
+       return len;
+}
+
+static ssize_t st_gyro_sysfs_sampling_frequency_available(struct device
*dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len = 0;
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i = 0; i < ARRAY_SIZE(st_gyro_sensors[gdata->index].odr.odr_avl);
+                                                                       i++) {
+               if (st_gyro_sensors[gdata->index].odr.odr_avl[i].hz == 0)
+                       break;
+
+               len += sprintf(buf + len, "%d ",
+                       st_gyro_sensors[gdata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_gyro_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_anglvel_scale_available, S_IRUGO,
+                               st_gyro_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_gyro_sysfs_get_powerdown, st_gyro_sysfs_set_powerdown , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_gyro_sysfs_get_sampling_frequency,
+                                       st_gyro_sysfs_set_sampling_frequency);
+
+static struct attribute *st_gyro_attributes[] = {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_gyro_attribute_group = {
+       .attrs = st_gyro_attributes,
+};
+
+static const struct iio_info acc_info = {
+       .driver_module = THIS_MODULE,
+       .attrs = &st_gyro_attribute_group,
+       .read_raw = &st_gyro_read_raw,
+       .write_raw = &st_gyro_write_raw,
+};
+
+static int st_gyro_init_sensor(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_gyro_odr_available odr_out;
+       struct st_gyro_fullscale_available fs_out;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       err = st_gyro_set_enable(indio_dev, false);
+       if (err < 0)
+               goto init_error;
+
+       err = st_gyro_set_axis_enable(indio_dev, ST_GYRO_ENABLE_ALL_CHANNELS);
+       if (err < 0)
+               goto init_error;
+
+       st_gyro_match_fs(&st_gyro_sensors[gdata->index],
+                                               gdata->fullscale, &fs_out);
+       err = st_gyro_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto init_error;
+
+       st_gyro_match_odr(&st_gyro_sensors[gdata->index],
+                                                       gdata->odr, &odr_out);
+       err = st_gyro_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto init_error;
+
+       err = st_gyro_set_bdu(indio_dev,
+                               &st_gyro_sensors[gdata->index].bdu, true);
+
+init_error:
+       return err;
+}
+
+int st_gyro_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 *wai;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &acc_info;
+
+       wai = kmalloc(sizeof(*wai), GFP_KERNEL);
+       if (wai == NULL) {
+               err = -ENOMEM;
+               goto st_gyro_iio_probe_error;
+       }
+
+       err = st_gyro_get_wai_device(indio_dev,
+                                       ST_GYRO_DEFAULT_WAI_ADDRESS, wai);
+       if (err < 0)
+               goto st_gyro_iio_probe_error;
+
+       err = st_gyro_check_device_list(indio_dev, *wai);
+       if (err < 0)
+               goto st_gyro_iio_probe_error;
+
+       gdata->multiread_bit = st_gyro_sensors[gdata->index].multi_read_bit;
+       indio_dev->channels = st_gyro_sensors[gdata->index].ch;
+       indio_dev->num_channels = ST_GYRO_NUMBER_ALL_CHANNELS;
+
+       gdata->fullscale = ST_GYRO_FS_AVL_2000DPS;
+       gdata->odr = 100;
+
+       err = st_gyro_init_sensor(indio_dev);
+       if (err < 0)
+               goto st_gyro_iio_probe_error;
+
+       err = st_gyro_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_gyro_iio_probe_error;
+
+       if (irq > 0) {
+               err = st_gyro_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err = iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_gyro_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_gyro_deallocate_ring(indio_dev);
+st_gyro_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_gyro_iio_probe);
+
+void st_gyro_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_gyro_remove_trigger(indio_dev, irq);
+       st_gyro_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_gyro_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c
new file mode 100644
index 0000000..d63ab8e
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -0,0 +1,125 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+
+#define ST_GYRO_I2C_MULTIREAD                  0x80
+
+static int st_gyro_i2c_read_byte(struct st_gyro_data *gdata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err = i2c_smbus_read_byte_data(to_i2c_client(gdata->dev), reg_addr);
+       if (err < 0)
+               goto st_gyro_i2c_read_byte_error;
+
+       *res_byte = err & 0xff;
+
+st_gyro_i2c_read_byte_error:
+       return err < 0 ? err : 0;
+}
+
+static int st_gyro_i2c_read_multiple_byte(struct st_gyro_data *gdata,
+                                               u8 reg_addr, int len, u8 *data)
+{
+       if (gdata->multiread_bit == true)
+               reg_addr |= ST_GYRO_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(gdata->dev),
+                                                       reg_addr, len, data);
+}
+
+static int st_gyro_i2c_write_byte(struct st_gyro_data *gdata,
+                                                       u8 reg_addr, u8 data)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(gdata->dev),
+                                                               reg_addr, data);
+}
+
+static int __devinit st_gyro_i2c_probe(struct i2c_client *client,
+                                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_gyro_data *gdata;
+       int err;
+
+       indio_dev = iio_device_alloc(sizeof(*gdata));
+       if (indio_dev == NULL) {
+               err = -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       gdata = iio_priv(indio_dev);
+       gdata->dev = &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->name = client->name;
+
+       gdata->read_byte = st_gyro_i2c_read_byte;
+       gdata->write_byte = st_gyro_i2c_write_byte;
+       gdata->read_multiple_byte = st_gyro_i2c_read_multiple_byte;
+
+       err = st_gyro_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_gyro_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+       st_gyro_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_gyro_id_table[] = {
+       { L3G4200D_GYRO_DEV_NAME },
+       { LSM330DL_GYRO_DEV_NAME },
+       { L3GD20_GYRO_DEV_NAME },
+       { L3GD20H_GYRO_DEV_NAME },
+       { LSM330D_GYRO_DEV_NAME },
+       { LSM330DLC_GYRO_DEV_NAME },
+       { LSM9DS0_GYRO_DEV_NAME },
+       { L3G4IS_GYRO_DEV_NAME },
+       { LSM330_GYRO_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
+
+static struct i2c_driver st_gyro_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "st-gyro-i2c",
+       },
+       .probe = st_gyro_i2c_probe,
+       .remove = st_gyro_i2c_remove,
+       .id_table = st_gyro_id_table,
+};
+module_i2c_driver(st_gyro_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
new file mode 100644
index 0000000..8823f35
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -0,0 +1,179 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+
+
+#define GYRO_SPI_READ          0x80;
+#define GYRO_SPI_MULTIREAD     0xc0
+
+static int st_gyro_spi_read(struct st_gyro_data *gdata,
+                                               u8 reg_addr, int len, u8 *data)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers[] = {
+               {
+                       .bits_per_word = 8,
+                       .len = 1,
+               },
+               {
+                       .rx_buf = data,
+                       .bits_per_word = 8,
+                       .len = len,
+               }
+       };
+
+       tx = kmalloc(sizeof(*tx), GFP_KERNEL);
+       if (tx == NULL) {
+               err = -ENOMEM;
+               goto acc_spi_read_error;
+       }
+
+       if ((gdata->multiread_bit == true) && (len > 1))
+               *tx = reg_addr | GYRO_SPI_MULTIREAD;
+       else
+               *tx = reg_addr | GYRO_SPI_READ;
+
+       xfers[0].tx_buf = tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err = spi_sync(to_spi_device(gdata->dev), &msg);
+       if (err)
+               goto acc_spi_read_error;
+
+       return len;
+
+acc_spi_read_error:
+       return err;
+}
+
+static int st_gyro_spi_read_byte(struct st_gyro_data *gdata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       return st_gyro_spi_read(gdata, reg_addr, 1, res_byte);
+}
+
+static int st_gyro_spi_read_multiple_byte(struct st_gyro_data *gdata,
+                                               u8 reg_addr, int len, u8 *data)
+{
+       return st_gyro_spi_read(gdata, reg_addr, len, data);
+}
+
+static int st_gyro_spi_write_byte(struct st_gyro_data *gdata,
+                                                       u8 reg_addr, u8 data)
+{
+       struct spi_message msg;
+       int err;
+       u8 *tx;
+
+       struct spi_transfer xfers = {
+               .bits_per_word = 8,
+               .len = 2,
+       };
+
+       tx = kmalloc(sizeof(*tx)*2, GFP_KERNEL);
+       if (tx == NULL) {
+               err = -ENOMEM;
+               goto alloc_memory_error;
+       }
+
+       tx[0] = reg_addr;
+       tx[1] = data;
+       xfers.tx_buf = tx;
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err = spi_sync(to_spi_device(gdata->dev), &msg);
+
+alloc_memory_error:
+       return err;
+}
+
+static int __devinit st_gyro_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_gyro_data *gdata;
+       int err;
+
+       indio_dev = iio_device_alloc(sizeof(*gdata));
+       if (indio_dev == NULL) {
+               err = -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       gdata = iio_priv(indio_dev);
+       gdata->dev = &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent = &spi->dev;
+       indio_dev->name = spi->modalias;
+
+       gdata->read_byte = st_gyro_spi_read_byte;
+       gdata->write_byte = st_gyro_spi_write_byte;
+       gdata->read_multiple_byte = st_gyro_spi_read_multiple_byte;
+
+       err = st_gyro_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_gyro_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+       st_gyro_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_gyro_id_table[] = {
+       { L3G4200D_GYRO_DEV_NAME },
+       { LSM330DL_GYRO_DEV_NAME },
+       { L3GD20_GYRO_DEV_NAME },
+       { L3GD20H_GYRO_DEV_NAME },
+       { LSM330D_GYRO_DEV_NAME },
+       { LSM330DLC_GYRO_DEV_NAME },
+       { LSM9DS0_GYRO_DEV_NAME },
+       { L3G4IS_GYRO_DEV_NAME },
+       { LSM330_GYRO_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
+
+static struct spi_driver st_gyro_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "st-gyro-spi",
+       },
+       .probe = st_gyro_spi_probe,
+       .remove = st_gyro_spi_remove,
+       .id_table = st_gyro_id_table,
+};
+module_spi_driver(st_gyro_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_trigger.c
b/drivers/iio/gyro/st_gyro_trigger.c
new file mode 100644
index 0000000..e072533
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+
+static int st_gyro_trig_acc_set_state(struct iio_trigger *trig, bool state)
+{
+       struct iio_dev *indio_dev = trig->private_data;
+       return st_gyro_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_gyro_trigger_ops = {
+       .owner = THIS_MODULE,
+       .set_trigger_state = &st_gyro_trig_acc_set_state,
+};
+
+int st_gyro_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       gdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (gdata->trig == NULL) {
+               err = -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+               goto iio_trigger_alloc_error;
+       }
+
+       err = request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       gdata->trig->name,
+                       gdata->trig);
+       if (err)
+               goto request_irq_error;
+
+       gdata->trig->private_data = indio_dev;
+       gdata->trig->ops = &st_gyro_trigger_ops;
+       gdata->trig->dev.parent = gdata->dev;
+
+       err = iio_trigger_register(gdata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig = gdata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, gdata->trig);
+request_irq_error:
+       iio_trigger_free(gdata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_gyro_probe_trigger);
+
+void st_gyro_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_gyro_data *gdata = iio_priv(indio_dev);
+
+       iio_trigger_unregister(gdata->trig);
+       free_irq(irq, gdata->trig);
+       iio_trigger_free(gdata->trig);
+}
+EXPORT_SYMBOL(st_gyro_remove_trigger);
diff --git a/include/linux/iio/gyro/st_gyro.h
b/include/linux/iio/gyro/st_gyro.h
new file mode 100644
index 0000000..81dc86e
--- /dev/null
+++ b/include/linux/iio/gyro/st_gyro.h
@@ -0,0 +1,99 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_GYRO_H
+#define ST_GYRO_H
+
+#define L3G4200D_GYRO_DEV_NAME         "l3g4200d"
+#define LSM330DL_GYRO_DEV_NAME         "lsm330dl_gyro"
+#define L3GD20_GYRO_DEV_NAME           "l3gd20"
+#define L3GD20H_GYRO_DEV_NAME          "l3gd20h"
+#define LSM330D_GYRO_DEV_NAME          "lsm330d_gyro"
+#define LSM330DLC_GYRO_DEV_NAME                "lsm330dlc_gyro"
+#define LSM9DS0_GYRO_DEV_NAME          "lsm9ds0_gyro"
+#define L3G4IS_GYRO_DEV_NAME           "l3g4is_ui"
+#define LSM330_GYRO_DEV_NAME           "lsm330_gyro"
+
+#define ST_GYRO_NUMBER_ALL_CHANNELS    4
+#define ST_GYRO_NUMBER_DATA_CHANNELS   3
+#define ST_GYRO_BYTE_FOR_CHANNEL       2
+#define ST_GYRO_SCAN_X                 0
+#define ST_GYRO_SCAN_Y                 1
+#define ST_GYRO_SCAN_Z                 2
+
+/**
+ * struct st_gyro_data - ST gyro device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_gyro_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_gyro_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       int (*read_byte) (struct st_gyro_data *gdata, u8 reg_addr,
+                                                               u8 *res_byte);
+       int (*write_byte) (struct st_gyro_data *gdata, u8 reg_addr, u8 data);
+       int (*read_multiple_byte) (struct st_gyro_data *gdata, u8 reg_addr,
+                                                       int len, u8 *data);
+};
+
+int st_gyro_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_gyro_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_gyro_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_gyro_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_gyro_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_gyro_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_gyro_allocate_ring(struct iio_dev *indio_dev);
+void st_gyro_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_gyro_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       return 0;
+}
+static inline void st_gyro_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_gyro_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
+{
+       return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_GYRO_H */
--
1.7.0.4


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics accelerometers driver.
  2012-11-27 11:52                                                   ` Denis CIOCCA
@ 2012-11-29  9:46                                                     ` Lars-Peter Clausen
  0 siblings, 0 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-11-29  9:46 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/27/2012 12:52 PM, Denis CIOCCA wrote:
> Hi everybody,
> 
> I attach the new modified patch with corrections about DMA using SPI.
> Best regards,
> 
> Denis
> 

Hi,

I think the driver looks fine now mostly fine. You need to watch your
kmallocs though, you introduced quite a few memory leaks in the latest
revisions of the driver.

And your e-mail client still replaces tabs with spaces as well as adding
newlines to overlong lines.

Either fix your E-Mail client or just use git send-email[1] which does
pretty good job and is straight forward to use. E.g. what I use to send out
IIO patches is:

git send-email --to "Jonathan Cameron <jic23@cam.ac.uk>" \
    --cc linux-iio@vger.kernel.org --cc drivers@analog.com \
    *.patch

[1] http://www.kernel.org/pub/software/scm/git/docs/git-send-email.html

> 
> 
> 
>  From 1b3c4eb307c1083cafa3dba6f1777f8438f4a6ad Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Mon, 22 Oct 2012 11:17:27 +0200
> Subject: [PATCH 3/3] iio:accel: Add STMicroelectronics accelerometers driver
> 
> This patch adds generic accelerometer driver for STMicroelectronics
> accelerometers, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>   Documentation/ABI/testing/sysfs-bus-iio-accel-st |    7 +
>   drivers/iio/accel/Kconfig                        |   37 +
>   drivers/iio/accel/Makefile                       |    6 +
>   drivers/iio/accel/st_accel_buffer.c              |  177 ++++
>   drivers/iio/accel/st_accel_core.c                | 1171
> ++++++++++++++++++++++
>   drivers/iio/accel/st_accel_i2c.c                 |  128 +++
>   drivers/iio/accel/st_accel_spi.c                 |  182 ++++
>   drivers/iio/accel/st_accel_trigger.c             |   83 ++
>   include/linux/iio/accel/st_accel.h               |  102 ++
>   9 files changed, 1893 insertions(+), 0 deletions(-)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
>   create mode 100644 drivers/iio/accel/st_accel_buffer.c
>   create mode 100644 drivers/iio/accel/st_accel_core.c
>   create mode 100644 drivers/iio/accel/st_accel_i2c.c
>   create mode 100644 drivers/iio/accel/st_accel_spi.c
>   create mode 100644 drivers/iio/accel/st_accel_trigger.c
>   create mode 100644 include/linux/iio/accel/st_accel.h
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> new file mode 100644
> index 0000000..f426c02
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> @@ -0,0 +1,7 @@
> +What:          /sys/bus/iio/devices/iio:deviceX/powerdown
> +KernelVersion: 3.7.0
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               Reading returns either '1' or '0'.
> +               '1' means that the device in question is off.
> +               '0' means that the devices in question is on.
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..d65e66a 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D
>           Say yes here to build support for the HID SENSOR
>           accelerometers 3D.
> 
> +config ST_ACCEL_3AXIS
> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +       depends on (I2C || SPI) && SYSFS

You use SPI here and SPI_MASTER down below, it would be more consistent to
use SPI_MASTER in both places.

> +       help
> +         Say yes here to build support for STMicroelectronics accelerometers:
> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_accel.
> +
> +config ST_ACCEL_3AXIS_I2C
> +       tristate "support I2C bus connection"
> +       depends on ST_ACCEL_3AXIS && I2C
> +       help
> +         Say yes here to build I2C support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_i2c.
> +
> +config ST_ACCEL_3AXIS_SPI
> +       tristate "support SPI bus connection"
> +       depends on ST_ACCEL_3AXIS && SPI_MASTER
> +       help
> +         Say yes here to build SPI support for STMicroelectronics accelerometers.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called st_accel_spi.
> +
> +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
> +       tristate "support triggered buffer"
> +       depends on ST_ACCEL_3AXIS
> +       select IIO_TRIGGERED_BUFFER
> +       select IIO_BUFFER
> +       help
> +         Default trigger and buffer for STMicroelectronics accelerometers driver.
> +
>   endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..1541236 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,9 @@
>   #
> 
>   obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +
> +st_accel-y := st_accel_core.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
> st_accel_buffer.o
> +obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel_buffer.c
> b/drivers/iio/accel/st_accel_buffer.c
> new file mode 100644
> index 0000000..e8419be
> --- /dev/null
[...]
> +
> +static irqreturn_t st_accel_trigger_handler(int irq, void *p)
> +{
> +       int len;
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       len = st_accel_get_buffer_element(indio_dev, adata->buffer_data);
> +       if (len < 0)
> +               goto st_accel_get_buffer_element_error;
> +
> +       if (indio_dev->scan_timestamp)
> +               *(s64 *)((u8 *)adata->buffer_data +
> +                               ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +       iio_push_to_buffer(indio_dev->buffer, adata->buffer_data);

I think Jonathan mentioned this before. The interface has slightly changed.
It is iio_push_to_buffers(indio_dev, adata->buffer_data) now

> +
> +st_accel_get_buffer_element_error:
> +       iio_trigger_notify_done(indio_dev->trig);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int err, i;
> +       u8 active_bit = 0x00;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +       if (adata->buffer_data == NULL) {
> +               err = -ENOMEM;
> +               goto allocate_memory_error;

I think you go the labels wrong here. You jump to kfree if the allocation
fails...

> +       }
> +
> +       for (i = 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
> +               if (test_bit(i, indio_dev->active_scan_mask))
> +                       active_bit |= (1 << i);
> +
> +       err = st_accel_set_axis_enable(indio_dev, active_bit);
> +       if (err < 0)
> +               goto st_accel_buffer_postenable_error;

... but skip the kfree if the allocation succeeded.

> +
> +       err = iio_triggered_buffer_postenable(indio_dev);
> +
> +       return err;
> +
> +allocate_memory_error:
> +       kfree(adata->buffer_data);
> +st_accel_buffer_postenable_error:
> +       return err;
> +}
> +
> +static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       err = iio_triggered_buffer_predisable(indio_dev);
> +       if (err < 0)
> +               goto st_accel_buffer_predisable_error;
> +
> +       err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
> +       if (err < 0)
> +               goto st_accel_buffer_predisable_error;

I think you should still free the buffer

> +
> +       kfree(adata->buffer_data);
> +
> +st_accel_buffer_predisable_error:
> +       return err;
> +}
> +
[...]
> diff --git a/drivers/iio/accel/st_accel_core.c
> b/drivers/iio/accel/st_accel_core.c
> new file mode 100644
> index 0000000..791161e
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -0,0 +1,1171 @@
[...]
> +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
> reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err;
> +       u8 *prev_data;
> +       u8 new_data;
> +       struct st_accel_data *adata = iio_priv(indio_dev);
> +
> +       prev_data = kmalloc(sizeof(*prev_data), GFP_KERNEL);

Is this ever freed? I think it may make sense to use a global buffer in your
st_accel_data struct instead of allocation a new one for each read. If you
do this make sure that you protect the use of the buffer with a mutex.

> +       if (prev_data == NULL) {
> +               err = -ENOMEM;
> +               goto st_accel_write_data_with_mask_error;
> +       }
> +
> +       err = adata->read_byte(adata, reg_addr, prev_data);
> +       if (err < 0)
> +               goto st_accel_write_data_with_mask_error;
> +
> +       new_data = ((*prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +       err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_accel_write_data_with_mask_error:
> +       return err;
> +}

[...]
> b/drivers/iio/accel/st_accel_spi.c
> new file mode 100644
> index 0000000..6df1440
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -0,0 +1,182 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/accel/st_accel.h>
> +
> +
> +#define ACC_SPI_READ           0x80;
> +#define ACC_SPI_MULTIREAD      0xc0
> +
> +static int st_accel_spi_read(struct st_accel_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 *tx;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = data,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       tx = kmalloc(sizeof(*tx), GFP_KERNEL);

This is never freed. Again it may make sense to make this buffer a field in
your st_accel_data struct, so you don't have to allocate it for each
transfer. Take a look at how other IIO drivers handle this. The keyword here
is ____cacheline_aligned.

> +       if (tx == NULL) {
> +               err = -ENOMEM;
> +               goto acc_spi_read_error;
> +       }
> +
> +       if ((adata->multiread_bit == true) && (len > 1))
> +               *tx = reg_addr | ACC_SPI_MULTIREAD;
> +       else
> +               *tx = reg_addr | ACC_SPI_READ;
> +
> +       xfers[0].tx_buf = tx;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       if (err)
> +               goto acc_spi_read_error;
> +
> +       return len;
> +
> +acc_spi_read_error:
> +       return err;
> +}
> +
> +
> +static int st_accel_spi_write_byte(struct st_accel_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +       u8 *tx;
> +
> +       struct spi_transfer xfers = {
> +               .bits_per_word = 8,
> +               .len = 2,
> +       };
> +
> +       tx = kmalloc(sizeof(*tx)*2, GFP_KERNEL);

Same here

> +       if (tx == NULL) {
> +               err = -ENOMEM;
> +               goto alloc_memory_error;
> +       }
> +
> +       tx[0] = reg_addr;
> +       tx[1] = data;
> +       xfers.tx_buf = tx;
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers, &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +
> +alloc_memory_error:
> +       return err;
> +}
[...]


> diff --git a/include/linux/iio/accel/st_accel.h
> b/include/linux/iio/accel/st_accel.h
> new file mode 100644
> index 0000000..9e0abd6
> --- /dev/null
> +++ b/include/linux/iio/accel/st_accel.h
[...]
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_accel_probe_trigger(struct iio_dev *indio_dev, int irq);
> +void st_accel_remove_trigger(struct iio_dev *indio_dev, int irq);
> +int st_accel_allocate_ring(struct iio_dev *indio_dev);
> +void st_accel_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_accel_probe_trigger(struct iio_dev *indio_dev, int
> irq)
> +{
> +       return 0;
> +}
> +static inline void st_accel_remove_trigger(struct iio_dev *indio_dev,
> int irq)
> +{
> +       return;
> +}
> +static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       return;

You don't need the return here.

> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_ACCEL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics gyroscopes driver.
  2012-11-27 15:36                                                   ` STMicroelectronics gyroscopes driver Denis CIOCCA
@ 2012-11-29  9:51                                                     ` Lars-Peter Clausen
  2012-11-30  9:13                                                       ` Denis CIOCCA
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-11-29  9:51 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/27/2012 04:36 PM, Denis CIOCCA wrote:
> Hi guys,
> 
> based on the driver for accelerometers, I wrote the driver for all our
> last mems gyroscopes.
> Best regards,
> 
> Denis
> 

How different are the gyros from the accelerometers? On a first quick glance
the looks as if you took the accelerometer driver and did a search and
replace on "accel" with "gyro". Can both be supported by the same driver?

- Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: STMicroelectronics gyroscopes driver.
  2012-11-29  9:51                                                     ` Lars-Peter Clausen
@ 2012-11-30  9:13                                                       ` Denis CIOCCA
  2012-11-30 10:36                                                         ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-11-30  9:13 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Lars,

yes you have right, the main difference between accelerometers driver and this driver is the "find and replace". :)
Yes, for now it is possible use the same driver for both devices, but in the next patch I will add some functionality, for example the FIFO hardware used in the gyro, ecc.
What do you suggest?

In the future I will write also the drivers for the magnetometers and pressure sensors, I think the magnetometers driver is very similar to the accelerometers and magnetometers drivers, you suggest me to use the same code?
How it is possible to do this?
We can add a STMicroelectronics folder in the IIO tree to support all sensors?

Thanks
Best Regards,

Denis


-----Original Message-----
From: Lars-Peter Clausen [mailto:lars@metafoo.de] 
Sent: Thursday, November 29, 2012 10:51 AM
To: Denis CIOCCA
Cc: Jonathan Cameron; Denis Ciocca; Jonathan Cameron; Pavel Machek; linux-iio@vger.kernel.org; burman.yan@gmail.com
Subject: Re: STMicroelectronics gyroscopes driver.

On 11/27/2012 04:36 PM, Denis CIOCCA wrote:
> Hi guys,
> 
> based on the driver for accelerometers, I wrote the driver for all our 
> last mems gyroscopes.
> Best regards,
> 
> Denis
> 

How different are the gyros from the accelerometers? On a first quick glance the looks as if you took the accelerometer driver and did a search and replace on "accel" with "gyro". Can both be supported by the same driver?

- Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics gyroscopes driver.
  2012-11-30  9:13                                                       ` Denis CIOCCA
@ 2012-11-30 10:36                                                         ` Lars-Peter Clausen
  2012-11-30 13:06                                                           ` Jonathan Cameron
  0 siblings, 1 reply; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-11-30 10:36 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/30/2012 10:13 AM, Denis CIOCCA wrote:
> Hi Lars,
> 
> yes you have right, the main difference between accelerometers driver and this driver is the "find and replace". :)
> Yes, for now it is possible use the same driver for both devices, but in the next patch I will add some functionality, for example the FIFO hardware used in the gyro, ecc.
> What do you suggest?
> 
> In the future I will write also the drivers for the magnetometers and pressure sensors, I think the magnetometers driver is very similar to the accelerometers and magnetometers drivers, you suggest me to use the same code?
> How it is possible to do this?

If the different device categories share some common bits and add custom
bits on top of it, it may make sense to have a common base library module
which implements the common bits and than have one driver per device type
which uses the common library and implements the device type specific bits
on top of it.

- Lars

> We can add a STMicroelectronics folder in the IIO tree to support all sensors?
> 
> Thanks
> Best Regards,
> 
> Denis
> 
> 
> -----Original Message-----
> From: Lars-Peter Clausen [mailto:lars@metafoo.de] 
> Sent: Thursday, November 29, 2012 10:51 AM
> To: Denis CIOCCA
> Cc: Jonathan Cameron; Denis Ciocca; Jonathan Cameron; Pavel Machek; linux-iio@vger.kernel.org; burman.yan@gmail.com
> Subject: Re: STMicroelectronics gyroscopes driver.
> 
> On 11/27/2012 04:36 PM, Denis CIOCCA wrote:
>> Hi guys,
>>
>> based on the driver for accelerometers, I wrote the driver for all our 
>> last mems gyroscopes.
>> Best regards,
>>
>> Denis
>>
> 
> How different are the gyros from the accelerometers? On a first quick glance the looks as if you took the accelerometer driver and did a search and replace on "accel" with "gyro". Can both be supported by the same driver?
> 
> - Lars

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics gyroscopes driver.
  2012-11-30 10:36                                                         ` Lars-Peter Clausen
@ 2012-11-30 13:06                                                           ` Jonathan Cameron
  2012-12-03 16:40                                                             ` STMicroelectronics driver Denis CIOCCA
  0 siblings, 1 reply; 42+ messages in thread
From: Jonathan Cameron @ 2012-11-30 13:06 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Denis CIOCCA, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 11/30/2012 10:36 AM, Lars-Peter Clausen wrote:
> On 11/30/2012 10:13 AM, Denis CIOCCA wrote:
>> Hi Lars,
>>
>> yes you have right, the main difference between accelerometers driver and this driver is the "find and replace". :)
>> Yes, for now it is possible use the same driver for both devices, but in the next patch I will add some functionality, for example the FIFO hardware used in the gyro, ecc.
>> What do you suggest?
>>
>> In the future I will write also the drivers for the magnetometers and pressure sensors, I think the magnetometers driver is very similar to the accelerometers and magnetometers drivers, you suggest me to use the same code?
>> How it is possible to do this?
> 
> If the different device categories share some common bits and add custom
> bits on top of it, it may make sense to have a common base library module
> which implements the common bits and than have one driver per device type
> which uses the common library and implements the device type specific bits
> on top of it.
> 
> - Lars
> 
>> We can add a STMicroelectronics folder in the IIO tree to support all sensors?
We have the common directory for this sort of usage. If it makes sense stick an appropriate
folder under there (e.g. if there are a number of files).

Unless you want to hold it back, I'd suggest we merge the accelerometer driver as
is and then you do the common code stuff by refactoring that driver in tree.

If you prefer you can even merge the gyroscope driver as well then refactor them both.
Sometimes the easiest way to argue for a common library is to provide clear examples
from existing drivers in the tree.

At the end of the day I like common libraries if they cut down the code repitition
but don't make the code significantly harder to follow!

Jonathan

>>
>> Thanks
>> Best Regards,
>>
>> Denis
>>
>>
>> -----Original Message-----
>> From: Lars-Peter Clausen [mailto:lars@metafoo.de] 
>> Sent: Thursday, November 29, 2012 10:51 AM
>> To: Denis CIOCCA
>> Cc: Jonathan Cameron; Denis Ciocca; Jonathan Cameron; Pavel Machek; linux-iio@vger.kernel.org; burman.yan@gmail.com
>> Subject: Re: STMicroelectronics gyroscopes driver.
>>
>> On 11/27/2012 04:36 PM, Denis CIOCCA wrote:
>>> Hi guys,
>>>
>>> based on the driver for accelerometers, I wrote the driver for all our 
>>> last mems gyroscopes.
>>> Best regards,
>>>
>>> Denis
>>>
>>
>> How different are the gyros from the accelerometers? On a first quick glance the looks as if you took the accelerometer driver and did a search and replace on "accel" with "gyro". Can both be supported by the same driver?
>>
>> - Lars
> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* STMicroelectronics driver
  2012-11-30 13:06                                                           ` Jonathan Cameron
@ 2012-12-03 16:40                                                             ` Denis CIOCCA
  2012-12-03 19:01                                                               ` Lars-Peter Clausen
  0 siblings, 1 reply; 42+ messages in thread
From: Denis CIOCCA @ 2012-12-03 16:40 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lars-Peter Clausen, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

Hi Lars-Peter & Jonathan,

in according to your previous emails, I have modified the accelerometers
driver to support all the sensors without code repetition.
In order to follow my company requests I need to submit at least the IIO
accelerometers driver in next kernel release.
I'll do my best to finalise enclosed solution on time for next release,
otherwise I have to submit Accelometer driver first and then patch it
for gyroscope and magnetometer.
Thanks for your feedback.

Denis



 From 5b63bcd8934ab3b18edf71c71e7671a70922f043 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@st.com>
Date: Tue, 27 Nov 2012 16:20:40 +0100
Subject: [PATCH] iio:common: Add STMicroelectronics common driver

This patch adds generic common code driver for STMicroelectronics
accelerometers, gyroscopes, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LIS331DLH, LSM303DL, LSM303DLM,
L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DLC, LSM9DS0,
L3G4IS, LSM330
---
  Documentation/ABI/testing/sysfs-bus-iio-accel-st   |    7 +
  Documentation/ABI/testing/sysfs-bus-iio-gyro-st    |    7 +
  drivers/iio/accel/Kconfig                          |   14 +
  drivers/iio/accel/Makefile                         |    1 +
  drivers/iio/accel/st_accel.c                       |  493 +++++++++++++++=
+
  drivers/iio/common/Kconfig                         |    1 +
  drivers/iio/common/Makefile                        |    1 +
  drivers/iio/common/st-sensors/Kconfig              |   17 +
  drivers/iio/common/st-sensors/Makefile             |    6 +
  drivers/iio/common/st-sensors/st_sensors.h         |  259 +++++++++
  drivers/iio/common/st-sensors/st_sensors_buffer.c  |  178 ++++++
  drivers/iio/common/st-sensors/st_sensors_core.c    |  589
++++++++++++++++++++
  drivers/iio/common/st-sensors/st_sensors_i2c.c     |  137 +++++
  drivers/iio/common/st-sensors/st_sensors_spi.c     |  183 ++++++
  drivers/iio/common/st-sensors/st_sensors_trigger.c |   83 +++
  drivers/iio/gyro/Kconfig                           |   14 +
  drivers/iio/gyro/Makefile                          |    1 +
  drivers/iio/gyro/st_gyro.c                         |  252 +++++++++
  18 files changed, 2243 insertions(+), 0 deletions(-)
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-gyro-st
  create mode 100644 drivers/iio/accel/st_accel.c
  create mode 100644 drivers/iio/common/st-sensors/Kconfig
  create mode 100644 drivers/iio/common/st-sensors/Makefile
  create mode 100644 drivers/iio/common/st-sensors/st_sensors.h
  create mode 100644 drivers/iio/common/st-sensors/st_sensors_buffer.c
  create mode 100644 drivers/iio/common/st-sensors/st_sensors_core.c
  create mode 100644 drivers/iio/common/st-sensors/st_sensors_i2c.c
  create mode 100644 drivers/iio/common/st-sensors/st_sensors_spi.c
  create mode 100644 drivers/iio/common/st-sensors/st_sensors_trigger.c
  create mode 100644 drivers/iio/gyro/st_gyro.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
b/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
new file mode 100644
index 0000000..f426c02
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
@@ -0,0 +1,7 @@
+What:          /sys/bus/iio/devices/iio:deviceX/powerdown
+KernelVersion: 3.7.0
+Contact:       linux-iio@vger.kernel.org
+Description:
+               Reading returns either '1' or '0'.
+               '1' means that the device in question is off.
+               '0' means that the devices in question is on.
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b2510c4..6e8a955 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -13,4 +13,18 @@ config HID_SENSOR_ACCEL_3D
          Say yes here to build support for the HID SENSOR
          accelerometers 3D.

+config ST_ACCEL_3D
+       tristate "STMicroelectronics accelerometers 3-Axis Driver"
+       depends on (I2C || SPI_MASTER) && SYSFS
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       select ST_SENSORS_IIO_COMMON
+       help
+         Say yes here to build support for STMicroelectronics acceleromete=
rs:
+         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_accel.
+
  endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..d3ce19a 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,4 @@
  #

  obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o
+obj-$(CONFIG_ST_ACCEL_3D) +=3D st_accel.o
diff --git a/drivers/iio/accel/st_accel.c b/drivers/iio/accel/st_accel.c
new file mode 100644
index 0000000..02c9ff8
--- /dev/null
+++ b/drivers/iio/accel/st_accel.c
@@ -0,0 +1,493 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include "../common/st-sensors/st_sensors.h"
+
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G                     2
+#define ST_ACCEL_FS_AVL_4G                     4
+#define ST_ACCEL_FS_AVL_6G                     6
+#define ST_ACCEL_FS_AVL_8G                     8
+#define ST_ACCEL_FS_AVL_16G                    16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP                     0x33
+#define ST_ACCEL_1_ODR_ADDR                    0x20
+#define ST_ACCEL_1_ODR_MASK                    0xf0
+#define ST_ACCEL_1_ODR_N_BIT                   4
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
+#define ST_ACCEL_1_FS_N_BIT                    2
+#define ST_ACCEL_1_FS_ADDR                     0x23
+#define ST_ACCEL_1_FS_MASK                     0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR                    0x23
+#define ST_ACCEL_1_BDU_MASK                    0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
+#define ST_ACCEL_1_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP                     0x49
+#define ST_ACCEL_2_ODR_ADDR                    0x20
+#define ST_ACCEL_2_ODR_MASK                    0xf0
+#define ST_ACCEL_2_ODR_N_BIT                   4
+#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_2_FS_N_BIT                    3
+#define ST_ACCEL_2_FS_ADDR                     0x21
+#define ST_ACCEL_2_FS_MASK                     0x38
+#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_2_BDU_ADDR                    0x20
+#define ST_ACCEL_2_BDU_MASK                    0x08
+#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
+#define ST_ACCEL_2_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP                     0x32
+#define ST_ACCEL_3_ODR_ADDR                    0x20
+#define ST_ACCEL_3_ODR_MASK                    0x18
+#define ST_ACCEL_3_ODR_N_BIT                   2
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
+#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
+#define ST_ACCEL_3_PW_ADDR                     0x20
+#define ST_ACCEL_3_PW_MASK                     0xe0
+#define ST_ACCEL_3_PW_N_BIT                    3
+#define ST_ACCEL_3_FS_N_BIT                    2
+#define ST_ACCEL_3_FS_ADDR                     0x23
+#define ST_ACCEL_3_FS_MASK                     0x30
+#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_3_BDU_ADDR                    0x23
+#define ST_ACCEL_3_BDU_MASK                    0x80
+#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
+#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
+#define ST_ACCEL_3_MULTIREAD_BIT               true
+
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP                     0x40
+#define ST_ACCEL_4_ODR_ADDR                    0x20
+#define ST_ACCEL_4_ODR_MASK                    0xf0
+#define ST_ACCEL_4_ODR_N_BIT                   4
+#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
+#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
+#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
+#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
+#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
+#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
+#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
+#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
+#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
+#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
+#define ST_ACCEL_4_FS_N_BIT                    3
+#define ST_ACCEL_4_FS_ADDR                     0x24
+#define ST_ACCEL_4_FS_MASK                     0x38
+#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
+#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
+#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
+#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
+#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
+#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_4_BDU_ADDR                    0x20
+#define ST_ACCEL_4_BDU_MASK                    0x08
+#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
+#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
+#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
+#define ST_ACCEL_4_IG1_EN_MASK                 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT               false
+
+const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+const struct st_sensors st_accel_sensors[] =3D {
+       {
+               .wai =3D ST_ACCEL_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_1_ODR_ADDR,
+                       .mask =3D ST_ACCEL_1_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_1_FS_ADDR,
+                       .mask =3D ST_ACCEL_1_FS_MASK,
+                       .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_1_BDU_ADDR,
+                       .mask =3D ST_ACCEL_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
+                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_2_ODR_ADDR,
+                       .mask =3D ST_ACCEL_2_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_2_FS_ADDR,
+                       .mask =3D ST_ACCEL_2_FS_MASK,
+                       .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_2_BDU_ADDR,
+                       .mask =3D ST_ACCEL_2_BDU_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_3_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_3_ODR_ADDR,
+                       .mask =3D ST_ACCEL_3_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_3_PW_ADDR,
+                       .mask =3D ST_ACCEL_3_PW_MASK,
+                       .num_bit =3D ST_ACCEL_3_PW_N_BIT,
+                       .value_on =3D ST_SENSORS_DEF_POWER_ON_VALUE,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_3_FS_ADDR,
+                       .mask =3D ST_ACCEL_3_FS_MASK,
+                       .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_3_BDU_ADDR,
+                       .mask =3D ST_ACCEL_3_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_ACCEL_4_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
+                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
+                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
+                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
+                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
+                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
+                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_ACCEL_4_ODR_ADDR,
+                       .mask =3D ST_ACCEL_4_ODR_MASK,
+                       .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_ACCEL_4_FS_ADDR,
+                       .mask =3D ST_ACCEL_4_FS_MASK,
+                       .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_2G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_4G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_6G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+                               },
+                               [3] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_8G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,
+                               },
+                               [4] =3D {
+                                       .num =3D ST_ACCEL_FS_AVL_16G,
+                                       .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+                                       .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_ACCEL_4_BDU_ADDR,
+                       .mask =3D ST_ACCEL_4_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+                       .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+                       .ig1 =3D {
+                               .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+                               .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+                       },
+               },
+               .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+       },
+};
+
+static int st_accel_check_device_list(struct st_sensors_data *adata, u8
wai)
+{
+       int i;
+
+       for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+               if (st_accel_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_accel_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       return -ENODEV;
+}
+
+int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_accel_check_device_list(adata, wai);
+       if (err < 0)
+               goto init_error;
+
+       adata->st_sensors =3D (const struct st_sensors *)st_accel_sensors;
+
+init_error:
+       return err;
+}
+EXPORT_SYMBOL(st_accel_init_sensor);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..315632f 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
  #

  source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st-sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..f876ca5 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
  #

  obj-y +=3D hid-sensors/
+obj-y +=3D st-sensors/
diff --git a/drivers/iio/common/st-sensors/Kconfig
b/drivers/iio/common/st-sensors/Kconfig
new file mode 100644
index 0000000..5e17159
--- /dev/null
+++ b/drivers/iio/common/st-sensors/Kconfig
@@ -0,0 +1,17 @@
+#
+# STMicroelectronics sensors common modules
+#
+menu "STMicroelectronics Sensors IIO Common"
+
+config ST_SENSORS_IIO_COMMON
+       tristate "Common modules for all ST Sensors IIO drivers"
+       depends on (I2C || SPI_MASTER) && SYSFS
+       select IIO_TRIGGER if IIO_BUFFER
+       help
+         Say yes here to build support for HID sensor to use
+         HID sensor common processing for attributes and IIO triggers.
+         There are many attributes which can be shared among multiple
+         HID sensor drivers, this module contains processing for those
+         attributes.
+
+endmenu
diff --git a/drivers/iio/common/st-sensors/Makefile
b/drivers/iio/common/st-sensors/Makefile
new file mode 100644
index 0000000..5c517ff
--- /dev/null
+++ b/drivers/iio/common/st-sensors/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the STMicroelectronics sensors common modules.
+#
+
+obj-$(CONFIG_ST_SENSORS_IIO_COMMON) +=3D st-sensors-iio-common.o
+st-sensors-iio-common-y :=3D st_sensors_core.o st_sensors_i2c.o
st_sensors_spi.o st_sensors_trigger.o st_sensors_buffer.o
diff --git a/drivers/iio/common/st-sensors/st_sensors.h
b/drivers/iio/common/st-sensors/st_sensors.h
new file mode 100644
index 0000000..5daadfa
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors.h
@@ -0,0 +1,259 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#define LSM303DLH_ACCEL_DEV_NAME               "lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME              "lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME                  "lis3dh"
+#define LSM330D_ACCEL_DEV_NAME                 "lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME                        "lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME               "lsm330dlc_accel"
+#define LSM303D_ACCEL_DEV_NAME                 "lsm303d"
+#define LSM9DS0_ACCEL_DEV_NAME                 "lsm9ds0"
+#define LIS331DLH_ACCEL_DEV_NAME               "lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME                        "lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME               "lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME                  "lsm330_accel"
+
+#define L3G4200D_GYRO_DEV_NAME                 "l3g4200d"
+#define LSM330DL_GYRO_DEV_NAME                 "lsm330dl_gyro"
+#define L3GD20_GYRO_DEV_NAME                   "l3gd20"
+#define L3GD20H_GYRO_DEV_NAME                  "l3gd20h"
+#define LSM330D_GYRO_DEV_NAME                  "lsm330d_gyro"
+#define LSM330DLC_GYRO_DEV_NAME                        "lsm330dlc_gyro"
+#define LSM9DS0_GYRO_DEV_NAME                  "lsm9ds0_gyro"
+#define L3G4IS_GYRO_DEV_NAME                   "l3g4is_ui"
+#define LSM330_GYRO_DEV_NAME                   "lsm330_gyro"
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS         4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS                3
+#define ST_SENSORS_BYTE_4CHANNEL               2
+#define ST_SENSORS_SCAN_X                      0
+#define ST_SENSORS_SCAN_Y                      1
+#define ST_SENSORS_SCAN_Z                      2
+#define ST_SENSORS_TX_MAX_LENGHT               2
+#define ST_SENSORS_RX_MAX_LENGHT               6
+#define ST_SENSORS_FULLSCALE_AVL_MAX           5
+#define ST_SENSORS_ODR_LIST_MAX                        10
+#define ST_SENSORS_ENABLE_ALL_CHANNELS         0x07
+
+/* DEFAULT VALUES FOR SENSORS */
+#define ST_SENSORS_DEF_OUT_X_L_ADDR            0x28
+#define ST_SENSORS_DEF_OUT_X_H_ADDR            0x29
+#define ST_SENSORS_DEF_OUT_Y_L_ADDR            0x2a
+#define ST_SENSORS_DEF_OUT_Y_H_ADDR            0x2b
+#define ST_SENSORS_DEF_OUT_Z_L_ADDR            0x2c
+#define ST_SENSORS_DEF_OUT_Z_H_ADDR            0x2d
+#define ST_SENSORS_DEF_WAI_ADDRESS             0x0f
+#define ST_SENSORS_DEF_POWER_ON_VALUE          0x01
+#define ST_SENSORS_DEF_POWER_OFF_VALUE         0x00
+#define ST_SENSORS_DEF_12_REALBITS             12
+#define ST_SENSORS_DEF_16_REALBITS             16
+#define ST_SENSORS_DEF_AXIS_ADDR               0x20
+#define ST_SENSORS_DEF_AXIS_MASK               0x07
+#define ST_SENSORS_DEF_AXIS_N_BIT              3
+
+#define ST_LSM_CHANNELS(sensor_type, index, mod, endian, bits, addr) \
+{ \
+       .type =3D sensor_type, \
+       .modified =3D 1, \
+       .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+       .scan_index =3D index, \
+       .channel2 =3D mod, \
+       .address =3D addr, \
+       .scan_type =3D { \
+               .sign =3D 's', \
+               .realbits =3D bits, \
+               .shift =3D 16 - bits, \
+               .storagebits =3D 16, \
+               .endianness =3D endian, \
+       }, \
+}
+
+/**
+ * struct st_sensors_data - ST sensors device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the
+ *     st_sensors_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the
sensors.
+ *     This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *     This buffer is used to avoid DMA not-aligned issue.
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+
+struct st_sensors_data {
+       struct device *dev;
+       struct iio_trigger *trig;
+       const struct st_sensors *st_sensors;
+
+       bool enabled;
+       bool multiread_bit;
+
+       short index;
+
+       char *buffer_data;
+
+       unsigned int fullscale;
+       unsigned int gain;
+       unsigned int odr;
+
+       struct mutex buf_lock;
+       u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+       u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned;
+
+       int (*read_byte) (struct st_sensors_data *adata, u8 reg_addr,
+                                                               u8 *res_byt=
e);
+       int (*write_byte) (struct st_sensors_data *adata, u8 reg_addr, u8 d=
ata);
+       int (*read_multiple_byte) (struct st_sensors_data *adata, u8 reg_ad=
dr,
+                                                       int len, u8 *data);
+};
+
+struct st_sensors_odr_available {
+       unsigned int hz;
+       u8 value;
+};
+
+struct st_sensors_odr {
+       u8 addr;
+       u8 mask;
+       short num_bit;
+       struct st_sensors_odr_available odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensors_power {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       u8 value_off;
+       u8 value_on;
+};
+
+struct st_sensors_axis {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_sensors_fs_available {
+       unsigned int num;
+       u8 value;
+       unsigned int gain;
+};
+
+struct st_sensors_fullscale {
+       u8 addr;
+       u8 mask;
+       unsigned short num_bit;
+       struct st_sensors_fs_available fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX]=
;
+};
+
+struct st_sensors_bdu {
+       u8 addr;
+       u8 mask;
+};
+
+struct st_sensors_interrupt_generator {
+       u8 en_addr;
+       u8 latch_mask_addr;
+       u8 en_mask;
+       u8 latching_mask;
+};
+
+struct st_sensors_data_ready_irq {
+       u8 addr;
+       u8 mask;
+       struct st_sensors_interrupt_generator ig1;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+
+struct st_sensors {
+       u8 wai;
+       struct iio_chan_spec *ch;
+       struct st_sensors_odr odr;
+       struct st_sensors_power pw;
+       struct st_sensors_axis enable_axis;
+       struct st_sensors_fullscale fs;
+       struct st_sensors_bdu bdu;
+       struct st_sensors_data_ready_irq drdy_irq;
+       bool multi_read_bit;
+};
+
+int st_sensors_iio_probe(struct iio_dev *indio_dev, int irq);
+void st_sensors_iio_remove(struct iio_dev *indio_dev, int irq);
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+#ifdef CONFIG_ST_ACCEL_3D
+int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai);
+#else /* CONFIG_ST_ACCEL_3D */
+static inline int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_ST_ACCEL_3D */
+
+#ifdef CONFIG_ST_GYRO_3D
+int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai);
+#else /* CONFIG_ST_GYRO_3D */
+static inline int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_ST_GYRO_3D */
+
+#ifdef CONFIG_IIO_BUFFER
+int st_sensors_probe_trigger(struct iio_dev *indio_dev, int irq);
+void st_sensors_remove_trigger(struct iio_dev *indio_dev, int irq);
+int st_sensors_allocate_ring(struct iio_dev *indio_dev);
+void st_sensors_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_sensors_probe_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return 0;
+}
+static inline void st_sensors_remove_trigger(struct iio_dev *indio_dev,
int irq)
+{
+       return;
+}
+static inline int st_sensors_allocate_ring(struct iio_dev *indio_dev)
+{
+       return 0;
+}
+static inline void st_sensors_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_SENSORS_H */
diff --git a/drivers/iio/common/st-sensors/st_sensors_buffer.c
b/drivers/iio/common/st-sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..b8d1cc3
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors_buffer.c
@@ -0,0 +1,178 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "st_sensors.h"
+
+
+#define ST_SENSORS_ENABLE_ALL_CHANNELS         0x07
+
+static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8
*buf)
+{
+       int i, n, len;
+       u8 reg_addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       for (i =3D 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+               if (test_bit(i, indio_dev->active_scan_mask)) {
+                       reg_addr[n] =3D indio_dev->channels[i].address;
+                       n++;
+               }
+       }
+       switch (n) {
+       case 1:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                                       ST_SENSORS_BYTE_4CHANNEL, buf);
+               break;
+       case 2:
+               if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_SENSORS_BYTE_4CHA=
NNEL) {
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                                       ST_SENSORS_BYTE_4CHANNEL*n,
+                                       buf);
+               } else {
+                       u8 rx_array[ST_SENSORS_BYTE_4CHANNEL*
+                               ST_SENSORS_NUMBER_DATA_CHANNELS];
+                       len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+                               ST_SENSORS_BYTE_4CHANNEL*
+                               ST_SENSORS_NUMBER_DATA_CHANNELS,
+                               rx_array);
+                       if (len < 0)
+                               goto read_data_channels_error;
+
+                       for (i =3D 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNE=
LS;
+                                                                       i++=
) {
+                               if (i < n)
+                                       buf[i] =3D rx_array[i];
+                               else
+                                       buf[i] =3D rx_array[n + i];
+                       }
+                       len =3D ST_SENSORS_BYTE_4CHANNEL*n;
+               }
+               break;
+       case 3:
+               len =3D adata->read_multiple_byte(adata, reg_addr[0],
+                       ST_SENSORS_BYTE_4CHANNEL*
+                       ST_SENSORS_NUMBER_DATA_CHANNELS,
+                       buf);
+               break;
+       default:
+               len =3D -EINVAL;
+               goto read_data_channels_error;
+       }
+       if (len !=3D ST_SENSORS_BYTE_4CHANNEL*n) {
+               len =3D -EIO;
+               goto read_data_channels_error;
+       }
+
+read_data_channels_error:
+       return len;
+}
+
+static irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+       int len;
+       struct iio_poll_func *pf =3D p;
+       struct iio_dev *indio_dev =3D pf->indio_dev;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       len =3D st_sensors_get_buffer_element(indio_dev, adata->buffer_data=
);
+       if (len < 0)
+               goto st_sensors_get_buffer_element_error;
+
+       if (indio_dev->scan_timestamp)
+               *(s64 *)((u8 *)adata->buffer_data +
+                               ALIGN(len, sizeof(s64))) =3D pf->timestamp;
+
+       iio_push_to_buffers(indio_dev, adata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int st_sensors_buffer_postenable(struct iio_dev *indio_dev)
+{
+       int err, i;
+       u8 active_bit =3D 0x00;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       adata->buffer_data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+       if (adata->buffer_data =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto allocate_memory_error;
+       }
+
+       for (i =3D 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++)
+               if (test_bit(i, indio_dev->active_scan_mask))
+                       active_bit |=3D (1 << i);
+
+       err =3D st_sensors_set_axis_enable(indio_dev, active_bit);
+       if (err < 0)
+               goto st_sensors_buffer_postenable_error;
+
+       err =3D iio_triggered_buffer_postenable(indio_dev);
+
+       return err;
+
+st_sensors_buffer_postenable_error:
+       kfree(adata->buffer_data);
+allocate_memory_error:
+       return err;
+}
+
+static int st_sensors_buffer_predisable(struct iio_dev *indio_dev)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D iio_triggered_buffer_predisable(indio_dev);
+       if (err < 0)
+               goto st_sensors_buffer_predisable_error;
+
+       err =3D st_sensors_set_axis_enable(indio_dev,
+                                               ST_SENSORS_ENABLE_ALL_CHANN=
ELS);
+       if (err < 0)
+               goto st_sensors_buffer_predisable_error;
+
+st_sensors_buffer_predisable_error:
+       kfree(adata->buffer_data);
+       return err;
+}
+
+static const struct iio_buffer_setup_ops st_sensors_buffer_setup_ops =3D {
+       .preenable =3D &iio_sw_buffer_preenable,
+       .postenable =3D &st_sensors_buffer_postenable,
+       .predisable =3D &st_sensors_buffer_predisable,
+};
+
+int st_sensors_allocate_ring(struct iio_dev *indio_dev)
+{
+       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+               &st_sensors_trigger_handler, &st_sensors_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_sensors_allocate_ring);
+
+void st_sensors_deallocate_ring(struct iio_dev *indio_dev)
+{
+       iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_ring);
diff --git a/drivers/iio/common/st-sensors/st_sensors_core.c
b/drivers/iio/common/st-sensors/st_sensors_core.c
new file mode 100644
index 0000000..9b9e951
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors_core.c
@@ -0,0 +1,589 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include "st_sensors.h"
+
+
+static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+                               u8 reg_addr, u8 mask, short num_bit, u8 dat=
a)
+{
+       int err;
+       u8 new_data;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D adata->read_byte(adata, reg_addr, &new_data);
+       if (err < 0)
+               goto st_sensors_write_data_with_mask_error;
+
+       new_data =3D ((new_data & (~mask)) | ((data << __ffs(mask)) & mask)=
);
+       err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+       return err;
+}
+
+int st_sensors_match_odr(const struct st_sensors *sensor,
+               unsigned int odr, struct st_sensors_odr_available *odr_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+               if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+                       odr_out->hz =3D sensor->odr.odr_avl[i].hz;
+                       odr_out->value =3D sensor->odr.odr_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_sensors_match_fs(const struct st_sensors *sensor,
+                       unsigned int fs, struct st_sensors_fs_available *fs=
_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_sensors_match_scale(const struct st_sensors *sensor,
+                       int scale, struct st_sensors_fs_available *fs_out)
+{
+       int i, ret =3D -EINVAL;
+
+       for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+               if (sensor->fs.fs_avl[i].gain =3D=3D scale) {
+                       fs_out->num =3D sensor->fs.fs_avl[i].num;
+                       fs_out->gain =3D sensor->fs.fs_avl[i].gain;
+                       fs_out->value =3D sensor->fs.fs_avl[i].value;
+                       ret =3D 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+       int err;
+       struct st_sensors_data *adata;
+
+       adata =3D iio_priv(indio_dev);
+       if (adata->st_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err =3D st_sensors_write_data_with_mask(indio_dev,
+                       adata->st_sensors[adata->index].drdy_irq.ig1.en_add=
r,
+                       adata->st_sensors[adata->index].drdy_irq.ig1.en_mas=
k, 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_sensors_set_dataready_irq_error;
+       }
+
+       if (adata->st_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > =
0) {
+               err =3D st_sensors_write_data_with_mask(indio_dev,
+               adata->st_sensors[adata->index].drdy_irq.ig1.latch_mask_add=
r,
+               adata->st_sensors[adata->index].drdy_irq.ig1.latching_mask,=
 1,
+                       (int)enable);
+               if (err < 0)
+                       goto st_sensors_set_dataready_irq_error;
+       }
+
+       err =3D st_sensors_write_data_with_mask(indio_dev,
+               adata->st_sensors[adata->index].drdy_irq.addr,
+               adata->st_sensors[adata->index].drdy_irq.mask, 1, (int)enab=
le);
+
+st_sensors_set_dataready_irq_error:
+       return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+static int st_sensors_set_bdu(struct iio_dev *indio_dev,
+                               const struct st_sensors_bdu *bdu, bool valu=
e)
+{
+       return st_sensors_write_data_with_mask(indio_dev, bdu->addr, bdu->m=
ask,
+                                                               1, (u8)valu=
e);
+}
+
+static int st_sensors_set_odr(struct iio_dev *indio_dev,
+                               struct st_sensors_odr_available *odr_availa=
ble)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       if ((adata->st_sensors[adata->index].odr.addr =3D=3D
+               adata->st_sensors[adata->index].pw.addr) &&
+               (adata->st_sensors[adata->index].odr.mask =3D=3D
+                               adata->st_sensors[adata->index].pw.mask)) {
+               if (adata->enabled =3D=3D true) {
+                       err =3D st_sensors_write_data_with_mask(indio_dev,
+                               adata->st_sensors[adata->index].odr.addr,
+                               adata->st_sensors[adata->index].odr.mask,
+                               adata->st_sensors[adata->index].odr.num_bit=
,
+                               odr_available->value);
+               } else {
+                       adata->odr =3D odr_available->hz;
+                       err =3D 0;
+               }
+       } else {
+               err =3D st_sensors_write_data_with_mask(indio_dev,
+                               adata->st_sensors[adata->index].odr.addr,
+                               adata->st_sensors[adata->index].odr.mask,
+                               adata->st_sensors[adata->index].odr.num_bit=
,
+                               odr_available->value);
+       }
+
+       return err;
+}
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_sensors_write_data_with_mask(indio_dev,
+                       adata->st_sensors[adata->index].enable_axis.addr,
+                       adata->st_sensors[adata->index].enable_axis.mask,
+                       ST_SENSORS_DEF_AXIS_N_BIT, axis_enable);
+
+       return err;
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+static int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+       int err =3D -EINVAL;
+       bool found;
+       u8 tmp_value;
+       struct st_sensors_odr_available odr_out;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       if (enable) {
+               found =3D false;
+               tmp_value =3D adata->st_sensors[adata->index].pw.value_on;
+               if ((adata->st_sensors[adata->index].odr.addr =3D=3D
+                               adata->st_sensors[adata->index].pw.addr) &&
+                       (adata->st_sensors[adata->index].odr.mask =3D=3D
+                               adata->st_sensors[adata->index].pw.mask)) {
+                       err =3D st_sensors_match_odr(
+                               &adata->st_sensors[adata->index], adata->od=
r,
+                                       &odr_out);
+                       if (err < 0)
+                               goto set_enable_error;
+                       tmp_value =3D odr_out.value;
+                       found =3D true;
+               }
+               err =3D st_sensors_write_data_with_mask(indio_dev,
+                               adata->st_sensors[adata->index].pw.addr,
+                               adata->st_sensors[adata->index].pw.mask,
+                               adata->st_sensors[adata->index].pw.num_bit,
+                               tmp_value);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D true;
+               if (found)
+                       adata->odr =3D odr_out.hz;
+       } else {
+               err =3D st_sensors_write_data_with_mask(indio_dev,
+                               adata->st_sensors[adata->index].pw.addr,
+                               adata->st_sensors[adata->index].pw.mask,
+                               adata->st_sensors[adata->index].pw.num_bit,
+                               adata->st_sensors[adata->index].pw.value_of=
f);
+               if (err < 0)
+                       goto set_enable_error;
+               adata->enabled =3D false;
+       }
+
+set_enable_error:
+       return err;
+}
+
+static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+                                       struct st_sensors_fs_available *fs_=
avl)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_sensors_write_data_with_mask(indio_dev,
+                               adata->st_sensors[adata->index].fs.addr,
+                               adata->st_sensors[adata->index].fs.mask,
+                               adata->st_sensors[adata->index].fs.num_bit,
+                               fs_avl->value);
+       if (err < 0)
+               goto st_sensors_set_fullscale_error;
+
+       adata->fullscale =3D fs_avl->num;
+       adata->gain =3D fs_avl->gain;
+       return err;
+
+st_sensors_set_fullscale_error:
+       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+       return err;
+}
+
+static int st_sensors_read_raw(struct iio_dev *indio_dev,
+                       struct iio_chan_spec const *ch, int *val,
+                                                       int *val2, long mas=
k)
+{
+       int err;
+       u8 outdata[ST_SENSORS_BYTE_4CHANNEL];
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               mutex_lock(&indio_dev->mlock);
+               if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+                       err =3D -EBUSY;
+                       goto read_error;
+               } else {
+                       if (!adata->enabled) {
+                               err =3D -EIO;
+                               goto read_error;
+                       } else {
+                               err =3D adata->read_multiple_byte(adata,
+                                       ch->address, ST_SENSORS_BYTE_4CHANN=
EL,
+                                       outdata);
+                               if (err < 0)
+                                       goto read_error;
+
+                               *val =3D ((s16)le16_to_cpup((u16 *)outdata)=
)
+                                                       >> ch->scan_type.sh=
ift;
+                       }
+               }
+               mutex_unlock(&indio_dev->mlock);
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val =3D 0;
+               *val2 =3D adata->gain;
+               return IIO_VAL_INT_PLUS_MICRO;
+       default:
+               return -EINVAL;
+       }
+
+read_error:
+       mutex_unlock(&indio_dev->mlock);
+       return err;
+}
+
+static int st_sensors_write_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int val, int val2, long m=
ask)
+{
+       int err;
+       struct st_sensors_fs_available fs_out;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               err =3D st_sensors_match_scale(&adata->st_sensors[adata->in=
dex],
+                                                               val2, &fs_o=
ut);
+               if (err < 0)
+                       goto write_error;
+
+               err =3D st_sensors_set_fullscale(indio_dev, &fs_out);
+               break;
+       default:
+               err =3D -EINVAL;
+       }
+
+write_error:
+       return err;
+}
+
+static ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       unsigned int freq;
+       struct st_sensors_odr_available odr_out;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D kstrtoint(buf, 10, &freq);
+       if (err < 0)
+               goto conversion_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_sensors_match_odr(&adata->st_sensors[adata->index],
+                                                               freq, &odr_=
out);
+       if (err < 0)
+               goto st_sensors_sysfs_set_sampling_frequency_error;
+
+       err =3D st_sensors_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_sensors_sysfs_set_sampling_frequency_error;
+
+       adata->odr =3D odr_out.hz;
+
+st_sensors_sysfs_set_sampling_frequency_error:
+       mutex_unlock(&indio_dev->mlock);
+conversion_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_sensors_sysfs_set_powerdown(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size=
)
+{
+       int err;
+       bool powerdown;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+       err =3D strtobool(buf, &powerdown);
+       if (err < 0)
+               goto set_enable_error;
+
+       mutex_lock(&indio_dev->mlock);
+       err =3D st_sensors_set_enable(indio_dev, !powerdown);
+       mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+       return err < 0 ? err : size;
+}
+
+static ssize_t st_sensors_sysfs_get_powerdown(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_sensors_sysfs_scale_available(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(adata->st_sensors[adata->index].fs.fs_=
avl);
+                                                                       i++=
) {
+               if (adata->st_sensors[adata->index].fs.fs_avl[i].num =3D=3D=
 0)
+                       break;
+
+               len +=3D sprintf(buf+len, "0.%06u ",
+                       adata->st_sensors[adata->index].fs.fs_avl[i].gain);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len-1] =3D '\n';
+
+       return len;
+}
+
+static ssize_t st_sensors_sysfs_sampling_frequency_available(struct
device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int i, len =3D 0;
+       struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       for (i =3D 0; i < ARRAY_SIZE(adata->st_sensors[adata->index].odr.od=
r_avl);
+                                                                       i++=
) {
+               if (adata->st_sensors[adata->index].odr.odr_avl[i].hz =3D=
=3D 0)
+                       break;
+
+               len +=3D sprintf(buf + len, "%d ",
+                       adata->st_sensors[adata->index].odr.odr_avl[i].hz);
+       }
+       mutex_unlock(&indio_dev->mlock);
+       buf[len - 1] =3D '\n';
+
+       return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+               st_sensors_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_sensors_scale_available, S_IRUGO,
+                               st_sensors_sysfs_scale_available, NULL , 0)=
;
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+               st_sensors_sysfs_get_powerdown,
+                                       st_sensors_sysfs_set_powerdown , 0)=
;
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+                       st_sensors_sysfs_get_sampling_frequency,
+                               st_sensors_sysfs_set_sampling_frequency);
+
+static struct attribute *st_sensor_attributes[] =3D {
+       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+       &iio_dev_attr_in_sensors_scale_available.dev_attr.attr,
+       &iio_dev_attr_powerdown.dev_attr.attr,
+       &iio_dev_attr_sampling_frequency.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group st_sensor_attribute_group =3D {
+       .attrs =3D st_sensor_attributes,
+};
+
+static const struct iio_info sensor_info =3D {
+       .driver_module =3D THIS_MODULE,
+       .attrs =3D &st_sensor_attribute_group,
+       .read_raw =3D &st_sensors_read_raw,
+       .write_raw =3D &st_sensors_write_raw,
+};
+
+static int st_sensors_get_wai_device(struct iio_dev *indio_dev, u8
reg_addr,
+                                                               u8 *value)
+{
+       int ret;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       ret =3D adata->read_byte(adata, reg_addr, value);
+       if (ret < 0)
+               goto read_byte_wai_error;
+
+       return 0;
+
+read_byte_wai_error:
+       dev_err(&indio_dev->dev,
+                       "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+       return -EIO;
+}
+
+static int st_init_sensor(struct iio_dev *indio_dev, int wai)
+{
+       int err;
+
+       err =3D st_accel_init_sensor(indio_dev, wai);
+       if (err < 0)
+               err =3D st_gyro_init_sensor(indio_dev, wai);
+
+       if (err < 0)
+               dev_err(&indio_dev->dev,
+                               "device not supported -> wai (0x%x).\n", wa=
i);
+
+       return err;
+}
+
+int st_sensors_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       u8 wai;
+       struct st_sensors_odr_available odr_out;
+       struct st_sensors_fs_available fs_out;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       indio_dev->modes =3D INDIO_DIRECT_MODE;
+
+       err =3D st_sensors_get_wai_device(indio_dev,
+                                       ST_SENSORS_DEF_WAI_ADDRESS, &wai);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       err =3D st_init_sensor(indio_dev, wai);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       indio_dev->info =3D &sensor_info;
+       adata->multiread_bit =3D adata->st_sensors[adata->index].multi_read=
_bit;
+       indio_dev->channels =3D adata->st_sensors[adata->index].ch;
+       indio_dev->num_channels =3D ST_SENSORS_NUMBER_ALL_CHANNELS;
+
+       adata->fullscale =3D adata->st_sensors[adata->index].fs.fs_avl[0].n=
um;
+       adata->odr =3D adata->st_sensors[adata->index].odr.odr_avl[0].hz;
+
+       err =3D st_sensors_set_enable(indio_dev, false);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       err =3D st_sensors_set_axis_enable(indio_dev,
+                                               ST_SENSORS_ENABLE_ALL_CHANN=
ELS);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       st_sensors_match_fs(&adata->st_sensors[adata->index],
+                                               adata->fullscale, &fs_out);
+       err =3D st_sensors_set_fullscale(indio_dev, &fs_out);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       st_sensors_match_odr(&adata->st_sensors[adata->index],
+                                                       adata->odr, &odr_ou=
t);
+       err =3D st_sensors_set_odr(indio_dev, &odr_out);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       err =3D st_sensors_set_bdu(indio_dev,
+                               &adata->st_sensors[adata->index].bdu, true)=
;
+
+       err =3D st_sensors_allocate_ring(indio_dev);
+       if (err < 0)
+               goto st_sensors_iio_probe_error;
+
+       if (irq > 0) {
+               err =3D st_sensors_probe_trigger(indio_dev, irq);
+               if (err < 0)
+                       goto acc_probe_trigger_error;
+       }
+
+       err =3D iio_device_register(indio_dev);
+       if (err)
+               goto iio_device_register_error;
+
+       return err;
+
+iio_device_register_error:
+       st_sensors_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+       st_sensors_deallocate_ring(indio_dev);
+st_sensors_iio_probe_error:
+       return err;
+}
+EXPORT_SYMBOL(st_sensors_iio_probe);
+
+void st_sensors_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+       iio_device_unregister(indio_dev);
+       st_sensors_remove_trigger(indio_dev, irq);
+       st_sensors_deallocate_ring(indio_dev);
+       iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_sensors_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics sensors core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st-sensors/st_sensors_i2c.c
b/drivers/iio/common/st-sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..2d2d5ee
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors_i2c.c
@@ -0,0 +1,137 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include "st_sensors.h"
+
+#define ST_SENSORS_I2C_MULTIREAD               0x80
+
+static int st_sensors_i2c_read_byte(struct st_sensors_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       int err;
+
+       err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+       if (err < 0)
+               goto st_sensors_i2c_read_byte_error;
+
+       *res_byte =3D err & 0xff;
+
+st_sensors_i2c_read_byte_error:
+       return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(struct st_sensors_data *adata=
,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       if (adata->multiread_bit =3D=3D true)
+               reg_addr |=3D ST_SENSORS_I2C_MULTIREAD;
+
+       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+                                                       reg_addr, len, data=
);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensors_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+                                                               reg_addr, d=
ata);
+}
+
+static int __devinit st_sensors_i2c_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct iio_dev *indio_dev;
+       struct st_sensors_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &client->dev;
+       i2c_set_clientdata(client, indio_dev);
+
+       indio_dev->dev.parent =3D &client->dev;
+       indio_dev->name =3D client->name;
+
+       adata->read_byte =3D st_sensors_i2c_read_byte;
+       adata->write_byte =3D st_sensors_i2c_write_byte;
+       adata->read_multiple_byte =3D st_sensors_i2c_read_multiple_byte;
+
+       err =3D st_sensors_iio_probe(indio_dev, client->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_sensors_i2c_remove(struct i2c_client *client)
+{
+       struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+       st_sensors_iio_remove(indio_dev, client->irq);
+
+       return 0;
+}
+
+static const struct i2c_device_id st_sensors_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       { L3G4200D_GYRO_DEV_NAME },
+       { LSM330DL_GYRO_DEV_NAME },
+       { L3GD20_GYRO_DEV_NAME },
+       { L3GD20H_GYRO_DEV_NAME },
+       { LSM330D_GYRO_DEV_NAME },
+       { LSM330DLC_GYRO_DEV_NAME },
+       { LSM9DS0_GYRO_DEV_NAME },
+       { L3G4IS_GYRO_DEV_NAME },
+       { LSM330_GYRO_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, st_sensors_id_table);
+
+static struct i2c_driver st_sensors_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-sensors-i2c",
+       },
+       .probe =3D st_sensors_i2c_probe,
+       .remove =3D st_sensors_i2c_remove,
+       .id_table =3D st_sensors_id_table,
+};
+module_i2c_driver(st_sensors_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st-sensors/st_sensors_spi.c
b/drivers/iio/common/st-sensors/st_sensors_spi.c
new file mode 100644
index 0000000..1df0c80
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors_spi.c
@@ -0,0 +1,183 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include "st_sensors.h"
+
+
+#define ST_SENSORS_SPI_READ            0x80;
+#define ST_SENSORS_SPI_MULTIREAD       0xc0
+
+static int st_sensors_spi_read(struct st_sensors_data *adata,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       struct spi_message msg;
+       int err;
+
+       struct spi_transfer xfers[] =3D {
+               {
+                       .tx_buf =3D adata->tx_buf,
+                       .bits_per_word =3D 8,
+                       .len =3D 1,
+               },
+               {
+                       .rx_buf =3D adata->rx_buf,
+                       .bits_per_word =3D 8,
+                       .len =3D len,
+               }
+       };
+
+       mutex_lock(&adata->buf_lock);
+       if ((adata->multiread_bit =3D=3D true) && (len > 1))
+               adata->tx_buf[0] =3D reg_addr | ST_SENSORS_SPI_MULTIREAD;
+       else
+               adata->tx_buf[0] =3D reg_addr | ST_SENSORS_SPI_READ;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers[0], &msg);
+       spi_message_add_tail(&xfers[1], &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       if (err)
+               goto acc_spi_read_error;
+
+       memcpy(data, adata->rx_buf, len*sizeof(u8));
+       mutex_unlock(&adata->buf_lock);
+       return len;
+
+acc_spi_read_error:
+       return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensors_data *adata,
+                                               u8 reg_addr, u8 *res_byte)
+{
+       return st_sensors_spi_read(adata, reg_addr, 1, res_byte);
+}
+
+static int st_sensors_spi_read_multiple_byte(struct st_sensors_data *adata=
,
+                                               u8 reg_addr, int len, u8 *d=
ata)
+{
+       return st_sensors_spi_read(adata, reg_addr, len, data);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensors_data *adata,
+                                                       u8 reg_addr, u8 dat=
a)
+{
+       struct spi_message msg;
+       int err;
+
+       struct spi_transfer xfers =3D {
+               .tx_buf =3D adata->tx_buf,
+               .bits_per_word =3D 8,
+               .len =3D 2,
+       };
+
+       mutex_lock(&adata->buf_lock);
+       adata->tx_buf[0] =3D reg_addr;
+       adata->tx_buf[1] =3D data;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfers, &msg);
+       err =3D spi_sync(to_spi_device(adata->dev), &msg);
+       mutex_unlock(&adata->buf_lock);
+
+       return err;
+}
+
+static int __devinit st_sensors_spi_probe(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev;
+       struct st_sensors_data *adata;
+       int err;
+
+       indio_dev =3D iio_device_alloc(sizeof(*adata));
+       if (indio_dev =3D=3D NULL) {
+               err =3D -ENOMEM;
+               goto iio_device_alloc_error;
+       }
+
+       adata =3D iio_priv(indio_dev);
+       adata->dev =3D &spi->dev;
+       spi_set_drvdata(spi, indio_dev);
+
+       indio_dev->dev.parent =3D &spi->dev;
+       indio_dev->name =3D spi->modalias;
+
+       mutex_init(&adata->buf_lock);
+       adata->read_byte =3D st_sensors_spi_read_byte;
+       adata->write_byte =3D st_sensors_spi_write_byte;
+       adata->read_multiple_byte =3D st_sensors_spi_read_multiple_byte;
+
+       err =3D st_sensors_iio_probe(indio_dev, spi->irq);
+       if (err < 0)
+               goto acc_iio_default_error;
+
+       return 0;
+
+acc_iio_default_error:
+       iio_device_free(indio_dev);
+iio_device_alloc_error:
+       return err;
+}
+
+static int st_sensors_spi_remove(struct spi_device *spi)
+{
+       struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+       st_sensors_iio_remove(indio_dev, spi->irq);
+
+       return 0;
+}
+
+static const struct spi_device_id st_sensors_id_table[] =3D {
+       { LSM303DLH_ACCEL_DEV_NAME },
+       { LSM303DLHC_ACCEL_DEV_NAME },
+       { LIS3DH_ACCEL_DEV_NAME },
+       { LSM330D_ACCEL_DEV_NAME },
+       { LSM330DL_ACCEL_DEV_NAME },
+       { LSM330DLC_ACCEL_DEV_NAME },
+       { LSM303D_ACCEL_DEV_NAME },
+       { LSM9DS0_ACCEL_DEV_NAME },
+       { LIS331DLH_ACCEL_DEV_NAME },
+       { LSM303DL_ACCEL_DEV_NAME },
+       { LSM303DLM_ACCEL_DEV_NAME },
+       { LSM330_ACCEL_DEV_NAME },
+       { L3G4200D_GYRO_DEV_NAME },
+       { LSM330DL_GYRO_DEV_NAME },
+       { L3GD20_GYRO_DEV_NAME },
+       { L3GD20H_GYRO_DEV_NAME },
+       { LSM330D_GYRO_DEV_NAME },
+       { LSM330DLC_GYRO_DEV_NAME },
+       { LSM9DS0_GYRO_DEV_NAME },
+       { L3G4IS_GYRO_DEV_NAME },
+       { LSM330_GYRO_DEV_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(spi, st_sensors_id_table);
+
+static struct spi_driver st_sensors_driver =3D {
+       .driver =3D {
+               .owner =3D THIS_MODULE,
+               .name =3D "st-sensors-spi",
+       },
+       .probe =3D st_sensors_spi_probe,
+       .remove =3D st_sensors_spi_remove,
+       .id_table =3D st_sensors_id_table,
+};
+module_spi_driver(st_sensors_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st-sensors/st_sensors_trigger.c
b/drivers/iio/common/st-sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..37dadc2
--- /dev/null
+++ b/drivers/iio/common/st-sensors/st_sensors_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics sensors driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include "st_sensors.h"
+
+static int st_sensors_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+       struct iio_dev *indio_dev =3D trig->private_data;
+       return st_sensors_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_sensors_trigger_ops =3D {
+       .owner =3D THIS_MODULE,
+       .set_trigger_state =3D &st_sensors_trig_acc_set_state,
+};
+
+int st_sensors_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+       if (adata->trig =3D=3D NULL) {
+               err =3D -ENOMEM;
+               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+               goto iio_trigger_alloc_error;
+       }
+
+       err =3D request_threaded_irq(irq,
+                       iio_trigger_generic_data_rdy_poll,
+                       NULL,
+                       IRQF_TRIGGER_RISING,
+                       adata->trig->name,
+                       adata->trig);
+       if (err)
+               goto request_irq_error;
+
+       adata->trig->private_data =3D indio_dev;
+       adata->trig->ops =3D &st_sensors_trigger_ops;
+       adata->trig->dev.parent =3D adata->dev;
+
+       err =3D iio_trigger_register(adata->trig);
+       if (err < 0) {
+               dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+               goto iio_trigger_register_error;
+       }
+       indio_dev->trig =3D adata->trig;
+
+       return 0;
+
+iio_trigger_register_error:
+       free_irq(irq, adata->trig);
+request_irq_error:
+       iio_trigger_free(adata->trig);
+iio_trigger_alloc_error:
+       return err;
+}
+EXPORT_SYMBOL(st_sensors_probe_trigger);
+
+void st_sensors_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       iio_trigger_unregister(adata->trig);
+       free_irq(irq, adata->trig);
+       iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_sensors_remove_trigger);
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 21e27e2..3e8b746 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -13,4 +13,18 @@ config HID_SENSOR_GYRO_3D
          Say yes here to build support for the HID SENSOR
          Gyroscope 3D.

+config ST_GYRO_3D
+       tristate "STMicroelectronics gyroscopes 3-Axis Driver"
+       depends on (I2C || SPI_MASTER) && SYSFS
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+       select ST_SENSORS_IIO_COMMON
+       help
+         Say yes here to build support for STMicroelectronics gyroscopes:
+         L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DL, LSM330DLC=
,
+         LSM9DS0, L3G4IS, LSM330.
+
+         This driver can also be built as a module. If so, the module
+         will be called st_gyro.
+
  endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 8a895d9..6a0a0d1 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -3,3 +3,4 @@
  #

  obj-$(CONFIG_HID_SENSOR_GYRO_3D) +=3D hid-sensor-gyro-3d.o
+obj-$(CONFIG_ST_GYRO_3D) +=3D st_gyro.o
diff --git a/drivers/iio/gyro/st_gyro.c b/drivers/iio/gyro/st_gyro.c
new file mode 100644
index 0000000..a483f19
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro.c
@@ -0,0 +1,252 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include "../common/st-sensors/st_sensors.h"
+
+
+/* FULLSCALE */
+#define ST_GYRO_FS_AVL_250DPS                  250
+#define ST_GYRO_FS_AVL_500DPS                  500
+#define ST_GYRO_FS_AVL_2000DPS                 2000
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_GYRO_1_WAI_EXP                      0xd3
+#define ST_GYRO_1_ODR_ADDR                     0x20
+#define ST_GYRO_1_ODR_MASK                     0xc0
+#define ST_GYRO_1_ODR_N_BIT                    2
+#define ST_GYRO_1_ODR_AVL_100HZ_VAL            0x00
+#define ST_GYRO_1_ODR_AVL_200HZ_VAL            0x01
+#define ST_GYRO_1_ODR_AVL_400HZ_VAL            0x02
+#define ST_GYRO_1_ODR_AVL_800HZ_VAL            0x03
+#define ST_GYRO_1_PW_ADDR                      0x20
+#define ST_GYRO_1_PW_MASK                      0x08
+#define ST_GYRO_1_PW_N_BIT                     1
+#define ST_GYRO_1_FS_N_BIT                     2
+#define ST_GYRO_1_FS_ADDR                      0x23
+#define ST_GYRO_1_FS_MASK                      0x30
+#define ST_GYRO_1_FS_AVL_250_VAL               0x00
+#define ST_GYRO_1_FS_AVL_500_VAL               0x01
+#define ST_GYRO_1_FS_AVL_2000_VAL              0x02
+#define ST_GYRO_1_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_1_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_1_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_1_BDU_ADDR                     0x23
+#define ST_GYRO_1_BDU_MASK                     0x80
+#define ST_GYRO_1_DRDY_IRQ_ADDR                        0x22
+#define ST_GYRO_1_DRDY_IRQ_MASK                        0x08
+#define ST_GYRO_1_MULTIREAD_BIT                        true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_GYRO_2_WAI_EXP                      0xd4
+#define ST_GYRO_2_ODR_ADDR                     0x20
+#define ST_GYRO_2_ODR_MASK                     0xc0
+#define ST_GYRO_2_ODR_N_BIT                    2
+#define ST_GYRO_2_ODR_AVL_95HZ_VAL             0x00
+#define ST_GYRO_2_ODR_AVL_190HZ_VAL            0x01
+#define ST_GYRO_2_ODR_AVL_380HZ_VAL            0x02
+#define ST_GYRO_2_ODR_AVL_760HZ_VAL            0x03
+#define ST_GYRO_2_PW_ADDR                      0x20
+#define ST_GYRO_2_PW_MASK                      0x08
+#define ST_GYRO_2_PW_N_BIT                     1
+#define ST_GYRO_2_FS_N_BIT                     2
+#define ST_GYRO_2_FS_ADDR                      0x23
+#define ST_GYRO_2_FS_MASK                      0x30
+#define ST_GYRO_2_FS_AVL_250_VAL               0x00
+#define ST_GYRO_2_FS_AVL_500_VAL               0x01
+#define ST_GYRO_2_FS_AVL_2000_VAL              0x02
+#define ST_GYRO_2_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_2_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_2_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_2_BDU_ADDR                     0x23
+#define ST_GYRO_2_BDU_MASK                     0x80
+#define ST_GYRO_2_DRDY_IRQ_ADDR                        0x22
+#define ST_GYRO_2_DRDY_IRQ_MASK                        0x08
+#define ST_GYRO_2_MULTIREAD_BIT                        true
+
+const struct iio_chan_spec st_gyro_16bit_channels[] =3D {
+       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
+       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
+       IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+const struct st_sensors st_gyro_sensors[] =3D {
+       {
+               .wai =3D ST_GYRO_1_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_gyro_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_GYRO_1_ODR_ADDR,
+                       .mask =3D ST_GYRO_1_ODR_MASK,
+                       .num_bit =3D ST_GYRO_1_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
+                               { 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
+                               { 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
+                               { 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_GYRO_1_PW_ADDR,
+                       .mask =3D ST_GYRO_1_PW_MASK,
+                       .num_bit =3D ST_GYRO_1_PW_N_BIT,
+                       .value_on =3D ST_SENSORS_DEF_POWER_ON_VALUE,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_GYRO_1_FS_ADDR,
+                       .mask =3D ST_GYRO_1_FS_MASK,
+                       .num_bit =3D ST_GYRO_1_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_250DPS,
+                                       .value =3D ST_GYRO_1_FS_AVL_250_VAL=
,
+                                       .gain =3D ST_GYRO_1_FS_AVL_250_GAIN=
,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_500DPS,
+                                       .value =3D ST_GYRO_1_FS_AVL_500_VAL=
,
+                                       .gain =3D ST_GYRO_1_FS_AVL_500_GAIN=
,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_2000DPS,
+                                       .value =3D ST_GYRO_1_FS_AVL_2000_VA=
L,
+                                       .gain =3D ST_GYRO_1_FS_AVL_2000_GAI=
N,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_GYRO_1_BDU_ADDR,
+                       .mask =3D ST_GYRO_1_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_GYRO_1_DRDY_IRQ_ADDR,
+                       .mask =3D ST_GYRO_1_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_GYRO_1_MULTIREAD_BIT,
+       },
+       {
+               .wai =3D ST_GYRO_2_WAI_EXP,
+               .ch =3D (struct iio_chan_spec *)st_gyro_16bit_channels,
+               .odr =3D {
+                       .addr =3D ST_GYRO_2_ODR_ADDR,
+                       .mask =3D ST_GYRO_2_ODR_MASK,
+                       .num_bit =3D ST_GYRO_2_ODR_N_BIT,
+                       .odr_avl =3D {
+                               { 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
+                               { 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
+                               { 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
+                               { 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
+                       },
+               },
+               .pw =3D {
+                       .addr =3D ST_GYRO_2_PW_ADDR,
+                       .mask =3D ST_GYRO_2_PW_MASK,
+                       .num_bit =3D ST_GYRO_2_PW_N_BIT,
+                       .value_on =3D ST_SENSORS_DEF_POWER_ON_VALUE,
+                       .value_off =3D ST_SENSORS_DEF_POWER_OFF_VALUE,
+               },
+               .enable_axis =3D {
+                       .addr =3D ST_SENSORS_DEF_AXIS_ADDR,
+                       .mask =3D ST_SENSORS_DEF_AXIS_MASK,
+               },
+               .fs =3D {
+                       .addr =3D ST_GYRO_2_FS_ADDR,
+                       .mask =3D ST_GYRO_2_FS_MASK,
+                       .num_bit =3D ST_GYRO_2_FS_N_BIT,
+                       .fs_avl =3D {
+                               [0] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_250DPS,
+                                       .value =3D ST_GYRO_2_FS_AVL_250_VAL=
,
+                                       .gain =3D ST_GYRO_2_FS_AVL_250_GAIN=
,
+                               },
+                               [1] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_500DPS,
+                                       .value =3D ST_GYRO_2_FS_AVL_500_VAL=
,
+                                       .gain =3D ST_GYRO_2_FS_AVL_500_GAIN=
,
+                               },
+                               [2] =3D {
+                                       .num =3D ST_GYRO_FS_AVL_2000DPS,
+                                       .value =3D ST_GYRO_2_FS_AVL_2000_VA=
L,
+                                       .gain =3D ST_GYRO_2_FS_AVL_2000_GAI=
N,
+                               },
+                       },
+               },
+               .bdu =3D {
+                       .addr =3D ST_GYRO_2_BDU_ADDR,
+                       .mask =3D ST_GYRO_2_BDU_MASK,
+               },
+               .drdy_irq =3D {
+                       .addr =3D ST_GYRO_2_DRDY_IRQ_ADDR,
+                       .mask =3D ST_GYRO_2_DRDY_IRQ_MASK,
+               },
+               .multi_read_bit =3D ST_GYRO_2_MULTIREAD_BIT,
+       },
+};
+
+static int st_gyro_check_device_list(struct st_sensors_data *adata, u8 wai=
)
+{
+       int i;
+
+       for (i =3D 0; i < ARRAY_SIZE(st_gyro_sensors); i++) {
+               if (st_gyro_sensors[i].wai =3D=3D wai)
+                       break;
+       }
+       if (i =3D=3D ARRAY_SIZE(st_gyro_sensors))
+               goto check_device_error;
+
+       adata->index =3D i;
+
+       return i;
+
+check_device_error:
+       return -ENODEV;
+}
+
+int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai)
+{
+       int err;
+       struct st_sensors_data *adata =3D iio_priv(indio_dev);
+
+       err =3D st_gyro_check_device_list(adata, wai);
+       if (err < 0)
+               goto init_error;
+
+       adata->st_sensors =3D (const struct st_sensors *)st_gyro_sensors;
+
+init_error:
+       return err;
+}
+EXPORT_SYMBOL(st_gyro_init_sensor);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
+MODULE_LICENSE("GPL v2");
--
1.7.0.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: STMicroelectronics driver
  2012-12-03 16:40                                                             ` STMicroelectronics driver Denis CIOCCA
@ 2012-12-03 19:01                                                               ` Lars-Peter Clausen
  0 siblings, 0 replies; 42+ messages in thread
From: Lars-Peter Clausen @ 2012-12-03 19:01 UTC (permalink / raw)
  To: Denis CIOCCA
  Cc: Jonathan Cameron, Denis Ciocca, Jonathan Cameron, Pavel Machek,
	linux-iio, burman.yan

On 12/03/2012 05:40 PM, Denis CIOCCA wrote:
> Hi Lars-Peter & Jonathan,
> 
> in according to your previous emails, I have modified the accelerometers
> driver to support all the sensors without code repetition.
> In order to follow my company requests I need to submit at least the IIO
> accelerometers driver in next kernel release.
> I'll do my best to finalise enclosed solution on time for next release,
> otherwise I have to submit Accelometer driver first and then patch it
> for gyroscope and magnetometer.
> Thanks for your feedback.
> 
> Denis
> 
> 

Hi,

I haven't looked at the patch yet, will do that tomorrow, but there is still
the formating issue that all tabs have been replaced with spaces, etc.

- Lars

> 
>  From 5b63bcd8934ab3b18edf71c71e7671a70922f043 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@st.com>
> Date: Tue, 27 Nov 2012 16:20:40 +0100
> Subject: [PATCH] iio:common: Add STMicroelectronics common driver
> 
> This patch adds generic common code driver for STMicroelectronics
> accelerometers, gyroscopes, currently it supports:
> LSM303DLH, LSM303DLHC, LIS3DH, LIS331DLH, LSM303DL, LSM303DLM,
> L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DLC, LSM9DS0,
> L3G4IS, LSM330
> ---
>   Documentation/ABI/testing/sysfs-bus-iio-accel-st   |    7 +
>   Documentation/ABI/testing/sysfs-bus-iio-gyro-st    |    7 +
>   drivers/iio/accel/Kconfig                          |   14 +
>   drivers/iio/accel/Makefile                         |    1 +
>   drivers/iio/accel/st_accel.c                       |  493 ++++++++++++++++
>   drivers/iio/common/Kconfig                         |    1 +
>   drivers/iio/common/Makefile                        |    1 +
>   drivers/iio/common/st-sensors/Kconfig              |   17 +
>   drivers/iio/common/st-sensors/Makefile             |    6 +
>   drivers/iio/common/st-sensors/st_sensors.h         |  259 +++++++++
>   drivers/iio/common/st-sensors/st_sensors_buffer.c  |  178 ++++++
>   drivers/iio/common/st-sensors/st_sensors_core.c    |  589
> ++++++++++++++++++++
>   drivers/iio/common/st-sensors/st_sensors_i2c.c     |  137 +++++
>   drivers/iio/common/st-sensors/st_sensors_spi.c     |  183 ++++++
>   drivers/iio/common/st-sensors/st_sensors_trigger.c |   83 +++
>   drivers/iio/gyro/Kconfig                           |   14 +
>   drivers/iio/gyro/Makefile                          |    1 +
>   drivers/iio/gyro/st_gyro.c                         |  252 +++++++++
>   18 files changed, 2243 insertions(+), 0 deletions(-)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-gyro-st
>   create mode 100644 drivers/iio/accel/st_accel.c
>   create mode 100644 drivers/iio/common/st-sensors/Kconfig
>   create mode 100644 drivers/iio/common/st-sensors/Makefile
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors.h
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors_buffer.c
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors_core.c
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors_i2c.c
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors_spi.c
>   create mode 100644 drivers/iio/common/st-sensors/st_sensors_trigger.c
>   create mode 100644 drivers/iio/gyro/st_gyro.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> new file mode 100644
> index 0000000..f426c02
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-accel-st
> @@ -0,0 +1,7 @@
> +What:          /sys/bus/iio/devices/iio:deviceX/powerdown
> +KernelVersion: 3.7.0
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               Reading returns either '1' or '0'.
> +               '1' means that the device in question is off.
> +               '0' means that the devices in question is on.
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
> b/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
> new file mode 100644
> index 0000000..f426c02
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-gyro-st
> @@ -0,0 +1,7 @@
> +What:          /sys/bus/iio/devices/iio:deviceX/powerdown
> +KernelVersion: 3.7.0
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               Reading returns either '1' or '0'.
> +               '1' means that the device in question is off.
> +               '0' means that the devices in question is on.
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index b2510c4..6e8a955 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -13,4 +13,18 @@ config HID_SENSOR_ACCEL_3D
>           Say yes here to build support for the HID SENSOR
>           accelerometers 3D.
> 
> +config ST_ACCEL_3D
> +       tristate "STMicroelectronics accelerometers 3-Axis Driver"
> +       depends on (I2C || SPI_MASTER) && SYSFS
> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       select ST_SENSORS_IIO_COMMON
> +       help
> +         Say yes here to build support for STMicroelectronics accelerometers:
> +         LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D,
> +         LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_accel.
> +
>   endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 5bc6855..d3ce19a 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -3,3 +3,4 @@
>   #
> 
>   obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
> +obj-$(CONFIG_ST_ACCEL_3D) += st_accel.o
> diff --git a/drivers/iio/accel/st_accel.c b/drivers/iio/accel/st_accel.c
> new file mode 100644
> index 0000000..02c9ff8
> --- /dev/null
> +++ b/drivers/iio/accel/st_accel.c
> @@ -0,0 +1,493 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include "../common/st-sensors/st_sensors.h"
> +
> +
> +/* FULLSCALE */
> +#define ST_ACCEL_FS_AVL_2G                     2
> +#define ST_ACCEL_FS_AVL_4G                     4
> +#define ST_ACCEL_FS_AVL_6G                     6
> +#define ST_ACCEL_FS_AVL_8G                     8
> +#define ST_ACCEL_FS_AVL_16G                    16
> +
> +/* CUSTOM VALUES FOR SENSOR 1 */
> +#define ST_ACCEL_1_WAI_EXP                     0x33
> +#define ST_ACCEL_1_ODR_ADDR                    0x20
> +#define ST_ACCEL_1_ODR_MASK                    0xf0
> +#define ST_ACCEL_1_ODR_N_BIT                   4
> +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL             0x01
> +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL            0x02
> +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL            0x03
> +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL            0x04
> +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL           0x05
> +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL           0x06
> +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL           0x07
> +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL          0x08
> +#define ST_ACCEL_1_FS_N_BIT                    2
> +#define ST_ACCEL_1_FS_ADDR                     0x23
> +#define ST_ACCEL_1_FS_MASK                     0x30
> +#define ST_ACCEL_1_FS_AVL_2_VAL                        0x00
> +#define ST_ACCEL_1_FS_AVL_4_VAL                        0x01
> +#define ST_ACCEL_1_FS_AVL_8_VAL                        0x02
> +#define ST_ACCEL_1_FS_AVL_16_VAL               0x03
> +#define ST_ACCEL_1_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_1_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_1_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(4000)
> +#define ST_ACCEL_1_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(12000)
> +#define ST_ACCEL_1_BDU_ADDR                    0x23
> +#define ST_ACCEL_1_BDU_MASK                    0x80
> +#define ST_ACCEL_1_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_1_DRDY_IRQ_MASK               0x10
> +#define ST_ACCEL_1_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 2 */
> +#define ST_ACCEL_2_WAI_EXP                     0x49
> +#define ST_ACCEL_2_ODR_ADDR                    0x20
> +#define ST_ACCEL_2_ODR_MASK                    0xf0
> +#define ST_ACCEL_2_ODR_N_BIT                   4
> +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_2_FS_N_BIT                    3
> +#define ST_ACCEL_2_FS_ADDR                     0x21
> +#define ST_ACCEL_2_FS_MASK                     0x38
> +#define ST_ACCEL_2_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_2_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_2_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_2_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_2_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_2_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_2_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_2_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_2_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_2_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_2_BDU_ADDR                    0x20
> +#define ST_ACCEL_2_BDU_MASK                    0x08
> +#define ST_ACCEL_2_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_2_DRDY_IRQ_MASK               0x04
> +#define ST_ACCEL_2_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 3 */
> +#define ST_ACCEL_3_WAI_EXP                     0x32
> +#define ST_ACCEL_3_ODR_ADDR                    0x20
> +#define ST_ACCEL_3_ODR_MASK                    0x18
> +#define ST_ACCEL_3_ODR_N_BIT                   2
> +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL            0x00
> +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL           0x01
> +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL           0x02
> +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL          0x03
> +#define ST_ACCEL_3_PW_ADDR                     0x20
> +#define ST_ACCEL_3_PW_MASK                     0xe0
> +#define ST_ACCEL_3_PW_N_BIT                    3
> +#define ST_ACCEL_3_FS_N_BIT                    2
> +#define ST_ACCEL_3_FS_ADDR                     0x23
> +#define ST_ACCEL_3_FS_MASK                     0x30
> +#define ST_ACCEL_3_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_3_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_3_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_3_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(1000)
> +#define ST_ACCEL_3_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(2000)
> +#define ST_ACCEL_3_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(3900)
> +#define ST_ACCEL_3_BDU_ADDR                    0x23
> +#define ST_ACCEL_3_BDU_MASK                    0x80
> +#define ST_ACCEL_3_DRDY_IRQ_ADDR               0x22
> +#define ST_ACCEL_3_DRDY_IRQ_MASK               0x02
> +#define ST_ACCEL_3_MULTIREAD_BIT               true
> +
> +/* CUSTOM VALUES FOR SENSOR 4 */
> +#define ST_ACCEL_4_WAI_EXP                     0x40
> +#define ST_ACCEL_4_ODR_ADDR                    0x20
> +#define ST_ACCEL_4_ODR_MASK                    0xf0
> +#define ST_ACCEL_4_ODR_N_BIT                   4
> +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL             0x01
> +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL             0x02
> +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL            0x03
> +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL            0x04
> +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL            0x05
> +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL           0x06
> +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL           0x07
> +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL           0x08
> +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL           0x09
> +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL          0x0a
> +#define ST_ACCEL_4_FS_N_BIT                    3
> +#define ST_ACCEL_4_FS_ADDR                     0x24
> +#define ST_ACCEL_4_FS_MASK                     0x38
> +#define ST_ACCEL_4_FS_AVL_2_VAL                        0X00
> +#define ST_ACCEL_4_FS_AVL_4_VAL                        0X01
> +#define ST_ACCEL_4_FS_AVL_6_VAL                        0x02
> +#define ST_ACCEL_4_FS_AVL_8_VAL                        0x03
> +#define ST_ACCEL_4_FS_AVL_16_VAL               0x04
> +#define ST_ACCEL_4_FS_AVL_2_GAIN               IIO_G_TO_M_S_2(61)
> +#define ST_ACCEL_4_FS_AVL_4_GAIN               IIO_G_TO_M_S_2(122)
> +#define ST_ACCEL_4_FS_AVL_6_GAIN               IIO_G_TO_M_S_2(183)
> +#define ST_ACCEL_4_FS_AVL_8_GAIN               IIO_G_TO_M_S_2(244)
> +#define ST_ACCEL_4_FS_AVL_16_GAIN              IIO_G_TO_M_S_2(732)
> +#define ST_ACCEL_4_BDU_ADDR                    0x20
> +#define ST_ACCEL_4_BDU_MASK                    0x08
> +#define ST_ACCEL_4_DRDY_IRQ_ADDR               0x23
> +#define ST_ACCEL_4_DRDY_IRQ_MASK               0x80
> +#define ST_ACCEL_4_IG1_EN_ADDR                 0x23
> +#define ST_ACCEL_4_IG1_EN_MASK                 0x08
> +#define ST_ACCEL_4_MULTIREAD_BIT               false
> +
> +const struct iio_chan_spec st_accel_12bit_channels[] = {
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_SENSORS_DEF_12_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +const struct iio_chan_spec st_accel_16bit_channels[] = {
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +const struct st_sensors st_accel_sensors[] = {
> +       {
> +               .wai = ST_ACCEL_1_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
> +                               { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
> +                               { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
> +                               { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_1_ODR_ADDR,
> +                       .mask = ST_ACCEL_1_ODR_MASK,
> +                       .num_bit = ST_ACCEL_1_ODR_N_BIT,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_1_FS_ADDR,
> +                       .mask = ST_ACCEL_1_FS_MASK,
> +                       .num_bit = ST_ACCEL_1_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_1_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_1_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_1_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_8_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_1_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_1_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_1_BDU_ADDR,
> +                       .mask = ST_ACCEL_1_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_1_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_2_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 3, ST_ACCEL_2_ODR_AVL_3HZ_VAL, },
> +                               { 6, ST_ACCEL_2_ODR_AVL_6HZ_VAL, },
> +                               { 12, ST_ACCEL_2_ODR_AVL_12HZ_VAL, },
> +                               { 25, ST_ACCEL_2_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_2_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
> +                               { 800, ST_ACCEL_2_ODR_AVL_800HZ_VAL, },
> +                               { 1600, ST_ACCEL_2_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_2_ODR_ADDR,
> +                       .mask = ST_ACCEL_2_ODR_MASK,
> +                       .num_bit = ST_ACCEL_2_ODR_N_BIT,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_2_FS_ADDR,
> +                       .mask = ST_ACCEL_2_FS_MASK,
> +                       .num_bit = ST_ACCEL_2_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_2_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_2_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_2_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_2_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_2_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_2_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_2_DRDY_IRQ_MASK,
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_2_BDU_ADDR,
> +                       .mask = ST_ACCEL_2_BDU_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_3_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_3_ODR_ADDR,
> +                       .mask = ST_ACCEL_3_ODR_MASK,
> +                       .num_bit = ST_ACCEL_3_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
> +                               { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
> +                               { 1000, ST_ACCEL_3_ODR_AVL_1000HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_3_PW_ADDR,
> +                       .mask = ST_ACCEL_3_PW_MASK,
> +                       .num_bit = ST_ACCEL_3_PW_N_BIT,
> +                       .value_on = ST_SENSORS_DEF_POWER_ON_VALUE,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_3_FS_ADDR,
> +                       .mask = ST_ACCEL_3_FS_MASK,
> +                       .num_bit = ST_ACCEL_3_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_3_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_3_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_3_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_3_FS_AVL_8_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_3_BDU_ADDR,
> +                       .mask = ST_ACCEL_3_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_3_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_ACCEL_4_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_accel_16bit_channels,
> +               .odr = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 3, ST_ACCEL_4_ODR_AVL_3HZ_VAL },
> +                               { 6, ST_ACCEL_4_ODR_AVL_6HZ_VAL, },
> +                               { 12, ST_ACCEL_4_ODR_AVL_12HZ_VAL, },
> +                               { 25, ST_ACCEL_4_ODR_AVL_25HZ_VAL, },
> +                               { 50, ST_ACCEL_4_ODR_AVL_50HZ_VAL, },
> +                               { 100, ST_ACCEL_4_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_ACCEL_4_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_ACCEL_4_ODR_AVL_400HZ_VAL, },
> +                               { 800, ST_ACCEL_4_ODR_AVL_800HZ_VAL, },
> +                               { 1600, ST_ACCEL_4_ODR_AVL_1600HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_ACCEL_4_ODR_ADDR,
> +                       .mask = ST_ACCEL_4_ODR_MASK,
> +                       .num_bit = ST_ACCEL_4_ODR_N_BIT,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_ACCEL_4_FS_ADDR,
> +                       .mask = ST_ACCEL_4_FS_MASK,
> +                       .num_bit = ST_ACCEL_4_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_ACCEL_FS_AVL_2G,
> +                                       .value = ST_ACCEL_4_FS_AVL_2_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_ACCEL_FS_AVL_4G,
> +                                       .value = ST_ACCEL_4_FS_AVL_4_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_4_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_ACCEL_FS_AVL_6G,
> +                                       .value = ST_ACCEL_4_FS_AVL_6_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
> +                               },
> +                               [3] = {
> +                                       .num = ST_ACCEL_FS_AVL_8G,
> +                                       .value = ST_ACCEL_4_FS_AVL_8_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_8_GAIN,
> +                               },
> +                               [4] = {
> +                                       .num = ST_ACCEL_FS_AVL_16G,
> +                                       .value = ST_ACCEL_4_FS_AVL_16_VAL,
> +                                       .gain = ST_ACCEL_4_FS_AVL_16_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_ACCEL_4_BDU_ADDR,
> +                       .mask = ST_ACCEL_4_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
> +                       .mask = ST_ACCEL_4_DRDY_IRQ_MASK,
> +                       .ig1 = {
> +                               .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
> +                               .en_mask = ST_ACCEL_4_IG1_EN_MASK,
> +                       },
> +               },
> +               .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
> +       },
> +};
> +
> +static int st_accel_check_device_list(struct st_sensors_data *adata, u8
> wai)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
> +               if (st_accel_sensors[i].wai == wai)
> +                       break;
> +       }
> +       if (i == ARRAY_SIZE(st_accel_sensors))
> +               goto check_device_error;
> +
> +       adata->index = i;
> +
> +       return i;
> +
> +check_device_error:
> +       return -ENODEV;
> +}
> +
> +int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = st_accel_check_device_list(adata, wai);
> +       if (err < 0)
> +               goto init_error;
> +
> +       adata->st_sensors = (const struct st_sensors *)st_accel_sensors;
> +
> +init_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_accel_init_sensor);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index ed45ee5..315632f 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,3 +3,4 @@
>   #
> 
>   source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/st-sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 8158400..f876ca5 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,3 +7,4 @@
>   #
> 
>   obj-y += hid-sensors/
> +obj-y += st-sensors/
> diff --git a/drivers/iio/common/st-sensors/Kconfig
> b/drivers/iio/common/st-sensors/Kconfig
> new file mode 100644
> index 0000000..5e17159
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/Kconfig
> @@ -0,0 +1,17 @@
> +#
> +# STMicroelectronics sensors common modules
> +#
> +menu "STMicroelectronics Sensors IIO Common"
> +
> +config ST_SENSORS_IIO_COMMON
> +       tristate "Common modules for all ST Sensors IIO drivers"
> +       depends on (I2C || SPI_MASTER) && SYSFS
> +       select IIO_TRIGGER if IIO_BUFFER
> +       help
> +         Say yes here to build support for HID sensor to use
> +         HID sensor common processing for attributes and IIO triggers.
> +         There are many attributes which can be shared among multiple
> +         HID sensor drivers, this module contains processing for those
> +         attributes.
> +
> +endmenu
> diff --git a/drivers/iio/common/st-sensors/Makefile
> b/drivers/iio/common/st-sensors/Makefile
> new file mode 100644
> index 0000000..5c517ff
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the STMicroelectronics sensors common modules.
> +#
> +
> +obj-$(CONFIG_ST_SENSORS_IIO_COMMON) += st-sensors-iio-common.o
> +st-sensors-iio-common-y := st_sensors_core.o st_sensors_i2c.o
> st_sensors_spi.o st_sensors_trigger.o st_sensors_buffer.o
> diff --git a/drivers/iio/common/st-sensors/st_sensors.h
> b/drivers/iio/common/st-sensors/st_sensors.h
> new file mode 100644
> index 0000000..5daadfa
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors.h
> @@ -0,0 +1,259 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_H
> +#define ST_SENSORS_H
> +
> +#define LSM303DLH_ACCEL_DEV_NAME               "lsm303dlh_accel"
> +#define LSM303DLHC_ACCEL_DEV_NAME              "lsm303dlhc_accel"
> +#define LIS3DH_ACCEL_DEV_NAME                  "lis3dh"
> +#define LSM330D_ACCEL_DEV_NAME                 "lsm330d_accel"
> +#define LSM330DL_ACCEL_DEV_NAME                        "lsm330dl_accel"
> +#define LSM330DLC_ACCEL_DEV_NAME               "lsm330dlc_accel"
> +#define LSM303D_ACCEL_DEV_NAME                 "lsm303d"
> +#define LSM9DS0_ACCEL_DEV_NAME                 "lsm9ds0"
> +#define LIS331DLH_ACCEL_DEV_NAME               "lis331dlh"
> +#define LSM303DL_ACCEL_DEV_NAME                        "lsm303dl_accel"
> +#define LSM303DLM_ACCEL_DEV_NAME               "lsm303dlm_accel"
> +#define LSM330_ACCEL_DEV_NAME                  "lsm330_accel"
> +
> +#define L3G4200D_GYRO_DEV_NAME                 "l3g4200d"
> +#define LSM330DL_GYRO_DEV_NAME                 "lsm330dl_gyro"
> +#define L3GD20_GYRO_DEV_NAME                   "l3gd20"
> +#define L3GD20H_GYRO_DEV_NAME                  "l3gd20h"
> +#define LSM330D_GYRO_DEV_NAME                  "lsm330d_gyro"
> +#define LSM330DLC_GYRO_DEV_NAME                        "lsm330dlc_gyro"
> +#define LSM9DS0_GYRO_DEV_NAME                  "lsm9ds0_gyro"
> +#define L3G4IS_GYRO_DEV_NAME                   "l3g4is_ui"
> +#define LSM330_GYRO_DEV_NAME                   "lsm330_gyro"
> +
> +#define ST_SENSORS_NUMBER_ALL_CHANNELS         4
> +#define ST_SENSORS_NUMBER_DATA_CHANNELS                3
> +#define ST_SENSORS_BYTE_4CHANNEL               2
> +#define ST_SENSORS_SCAN_X                      0
> +#define ST_SENSORS_SCAN_Y                      1
> +#define ST_SENSORS_SCAN_Z                      2
> +#define ST_SENSORS_TX_MAX_LENGHT               2
> +#define ST_SENSORS_RX_MAX_LENGHT               6
> +#define ST_SENSORS_FULLSCALE_AVL_MAX           5
> +#define ST_SENSORS_ODR_LIST_MAX                        10
> +#define ST_SENSORS_ENABLE_ALL_CHANNELS         0x07
> +
> +/* DEFAULT VALUES FOR SENSORS */
> +#define ST_SENSORS_DEF_OUT_X_L_ADDR            0x28
> +#define ST_SENSORS_DEF_OUT_X_H_ADDR            0x29
> +#define ST_SENSORS_DEF_OUT_Y_L_ADDR            0x2a
> +#define ST_SENSORS_DEF_OUT_Y_H_ADDR            0x2b
> +#define ST_SENSORS_DEF_OUT_Z_L_ADDR            0x2c
> +#define ST_SENSORS_DEF_OUT_Z_H_ADDR            0x2d
> +#define ST_SENSORS_DEF_WAI_ADDRESS             0x0f
> +#define ST_SENSORS_DEF_POWER_ON_VALUE          0x01
> +#define ST_SENSORS_DEF_POWER_OFF_VALUE         0x00
> +#define ST_SENSORS_DEF_12_REALBITS             12
> +#define ST_SENSORS_DEF_16_REALBITS             16
> +#define ST_SENSORS_DEF_AXIS_ADDR               0x20
> +#define ST_SENSORS_DEF_AXIS_MASK               0x07
> +#define ST_SENSORS_DEF_AXIS_N_BIT              3
> +
> +#define ST_LSM_CHANNELS(sensor_type, index, mod, endian, bits, addr) \
> +{ \
> +       .type = sensor_type, \
> +       .modified = 1, \
> +       .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +                                       IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +       .scan_index = index, \
> +       .channel2 = mod, \
> +       .address = addr, \
> +       .scan_type = { \
> +               .sign = 's', \
> +               .realbits = bits, \
> +               .shift = 16 - bits, \
> +               .storagebits = 16, \
> +               .endianness = endian, \
> +       }, \
> +}
> +
> +/**
> + * struct st_sensors_data - ST sensors device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the
> + *     st_sensors_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [m/s^2/LSB].
> + * @odr: Output data rate of the sensor [Hz].
> + * @buf_lock: Mutex to protect rx and tx buffers.
> + * @tx_buf: Buffer used by SPI transfer function to send data to the
> sensors.
> + *     This buffer is used to avoid DMA not-aligned issue.
> + * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
> + *     This buffer is used to avoid DMA not-aligned issue.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + */
> +
> +struct st_sensors_data {
> +       struct device *dev;
> +       struct iio_trigger *trig;
> +       const struct st_sensors *st_sensors;
> +
> +       bool enabled;
> +       bool multiread_bit;
> +
> +       short index;
> +
> +       char *buffer_data;
> +
> +       unsigned int fullscale;
> +       unsigned int gain;
> +       unsigned int odr;
> +
> +       struct mutex buf_lock;
> +       u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
> +       u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned;
> +
> +       int (*read_byte) (struct st_sensors_data *adata, u8 reg_addr,
> +                                                               u8 *res_byte);
> +       int (*write_byte) (struct st_sensors_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct st_sensors_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +};
> +
> +struct st_sensors_odr_available {
> +       unsigned int hz;
> +       u8 value;
> +};
> +
> +struct st_sensors_odr {
> +       u8 addr;
> +       u8 mask;
> +       short num_bit;
> +       struct st_sensors_odr_available odr_avl[ST_SENSORS_ODR_LIST_MAX];
> +};
> +
> +struct st_sensors_power {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       u8 value_off;
> +       u8 value_on;
> +};
> +
> +struct st_sensors_axis {
> +       u8 addr;
> +       u8 mask;
> +};
> +
> +struct st_sensors_fs_available {
> +       unsigned int num;
> +       u8 value;
> +       unsigned int gain;
> +};
> +
> +struct st_sensors_fullscale {
> +       u8 addr;
> +       u8 mask;
> +       unsigned short num_bit;
> +       struct st_sensors_fs_available fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_sensors_bdu {
> +       u8 addr;
> +       u8 mask;
> +};
> +
> +struct st_sensors_interrupt_generator {
> +       u8 en_addr;
> +       u8 latch_mask_addr;
> +       u8 en_mask;
> +       u8 latching_mask;
> +};
> +
> +struct st_sensors_data_ready_irq {
> +       u8 addr;
> +       u8 mask;
> +       struct st_sensors_interrupt_generator ig1;
> +};
> +
> +/**
> + * struct st_sensors - ST sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + */
> +
> +struct st_sensors {
> +       u8 wai;
> +       struct iio_chan_spec *ch;
> +       struct st_sensors_odr odr;
> +       struct st_sensors_power pw;
> +       struct st_sensors_axis enable_axis;
> +       struct st_sensors_fullscale fs;
> +       struct st_sensors_bdu bdu;
> +       struct st_sensors_data_ready_irq drdy_irq;
> +       bool multi_read_bit;
> +};
> +
> +int st_sensors_iio_probe(struct iio_dev *indio_dev, int irq);
> +void st_sensors_iio_remove(struct iio_dev *indio_dev, int irq);
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
> +
> +#ifdef CONFIG_ST_ACCEL_3D
> +int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai);
> +#else /* CONFIG_ST_ACCEL_3D */
> +static inline int st_accel_init_sensor(struct iio_dev *indio_dev, u8 wai)
> +{
> +       return -ENODEV;
> +}
> +#endif /* CONFIG_ST_ACCEL_3D */
> +
> +#ifdef CONFIG_ST_GYRO_3D
> +int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai);
> +#else /* CONFIG_ST_GYRO_3D */
> +static inline int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai)
> +{
> +       return -ENODEV;
> +}
> +#endif /* CONFIG_ST_GYRO_3D */
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_sensors_probe_trigger(struct iio_dev *indio_dev, int irq);
> +void st_sensors_remove_trigger(struct iio_dev *indio_dev, int irq);
> +int st_sensors_allocate_ring(struct iio_dev *indio_dev);
> +void st_sensors_deallocate_ring(struct iio_dev *indio_dev);
> +#else /* CONFIG_IIO_BUFFER */
> +static inline int st_sensors_probe_trigger(struct iio_dev *indio_dev,
> int irq)
> +{
> +       return 0;
> +}
> +static inline void st_sensors_remove_trigger(struct iio_dev *indio_dev,
> int irq)
> +{
> +       return;
> +}
> +static inline int st_sensors_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return 0;
> +}
> +static inline void st_sensors_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +}
> +#endif /* CONFIG_IIO_BUFFER */
> +
> +#endif /* ST_SENSORS_H */
> diff --git a/drivers/iio/common/st-sensors/st_sensors_buffer.c
> b/drivers/iio/common/st-sensors/st_sensors_buffer.c
> new file mode 100644
> index 0000000..b8d1cc3
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors_buffer.c
> @@ -0,0 +1,178 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include "st_sensors.h"
> +
> +
> +#define ST_SENSORS_ENABLE_ALL_CHANNELS         0x07
> +
> +static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8
> *buf)
> +{
> +       int i, n, len;
> +       u8 reg_addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
> +               if (test_bit(i, indio_dev->active_scan_mask)) {
> +                       reg_addr[n] = indio_dev->channels[i].address;
> +                       n++;
> +               }
> +       }
> +       switch (n) {
> +       case 1:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_SENSORS_BYTE_4CHANNEL, buf);
> +               break;
> +       case 2:
> +               if ((reg_addr[1] - reg_addr[0]) == ST_SENSORS_BYTE_4CHANNEL) {
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                                       ST_SENSORS_BYTE_4CHANNEL*n,
> +                                       buf);
> +               } else {
> +                       u8 rx_array[ST_SENSORS_BYTE_4CHANNEL*
> +                               ST_SENSORS_NUMBER_DATA_CHANNELS];
> +                       len = adata->read_multiple_byte(adata, reg_addr[0],
> +                               ST_SENSORS_BYTE_4CHANNEL*
> +                               ST_SENSORS_NUMBER_DATA_CHANNELS,
> +                               rx_array);
> +                       if (len < 0)
> +                               goto read_data_channels_error;
> +
> +                       for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
> +                                                                       i++) {
> +                               if (i < n)
> +                                       buf[i] = rx_array[i];
> +                               else
> +                                       buf[i] = rx_array[n + i];
> +                       }
> +                       len = ST_SENSORS_BYTE_4CHANNEL*n;
> +               }
> +               break;
> +       case 3:
> +               len = adata->read_multiple_byte(adata, reg_addr[0],
> +                       ST_SENSORS_BYTE_4CHANNEL*
> +                       ST_SENSORS_NUMBER_DATA_CHANNELS,
> +                       buf);
> +               break;
> +       default:
> +               len = -EINVAL;
> +               goto read_data_channels_error;
> +       }
> +       if (len != ST_SENSORS_BYTE_4CHANNEL*n) {
> +               len = -EIO;
> +               goto read_data_channels_error;
> +       }
> +
> +read_data_channels_error:
> +       return len;
> +}
> +
> +static irqreturn_t st_sensors_trigger_handler(int irq, void *p)
> +{
> +       int len;
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       len = st_sensors_get_buffer_element(indio_dev, adata->buffer_data);
> +       if (len < 0)
> +               goto st_sensors_get_buffer_element_error;
> +
> +       if (indio_dev->scan_timestamp)
> +               *(s64 *)((u8 *)adata->buffer_data +
> +                               ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +       iio_push_to_buffers(indio_dev, adata->buffer_data);
> +
> +st_sensors_get_buffer_element_error:
> +       iio_trigger_notify_done(indio_dev->trig);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int st_sensors_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +       int err, i;
> +       u8 active_bit = 0x00;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> +       if (adata->buffer_data == NULL) {
> +               err = -ENOMEM;
> +               goto allocate_memory_error;
> +       }
> +
> +       for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++)
> +               if (test_bit(i, indio_dev->active_scan_mask))
> +                       active_bit |= (1 << i);
> +
> +       err = st_sensors_set_axis_enable(indio_dev, active_bit);
> +       if (err < 0)
> +               goto st_sensors_buffer_postenable_error;
> +
> +       err = iio_triggered_buffer_postenable(indio_dev);
> +
> +       return err;
> +
> +st_sensors_buffer_postenable_error:
> +       kfree(adata->buffer_data);
> +allocate_memory_error:
> +       return err;
> +}
> +
> +static int st_sensors_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = iio_triggered_buffer_predisable(indio_dev);
> +       if (err < 0)
> +               goto st_sensors_buffer_predisable_error;
> +
> +       err = st_sensors_set_axis_enable(indio_dev,
> +                                               ST_SENSORS_ENABLE_ALL_CHANNELS);
> +       if (err < 0)
> +               goto st_sensors_buffer_predisable_error;
> +
> +st_sensors_buffer_predisable_error:
> +       kfree(adata->buffer_data);
> +       return err;
> +}
> +
> +static const struct iio_buffer_setup_ops st_sensors_buffer_setup_ops = {
> +       .preenable = &iio_sw_buffer_preenable,
> +       .postenable = &st_sensors_buffer_postenable,
> +       .predisable = &st_sensors_buffer_predisable,
> +};
> +
> +int st_sensors_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +               &st_sensors_trigger_handler, &st_sensors_buffer_setup_ops);
> +}
> +EXPORT_SYMBOL(st_sensors_allocate_ring);
> +
> +void st_sensors_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_triggered_buffer_cleanup(indio_dev);
> +}
> +EXPORT_SYMBOL(st_sensors_deallocate_ring);
> diff --git a/drivers/iio/common/st-sensors/st_sensors_core.c
> b/drivers/iio/common/st-sensors/st_sensors_core.c
> new file mode 100644
> index 0000000..9b9e951
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors_core.c
> @@ -0,0 +1,589 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include "st_sensors.h"
> +
> +
> +static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
> +                               u8 reg_addr, u8 mask, short num_bit, u8 data)
> +{
> +       int err;
> +       u8 new_data;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = adata->read_byte(adata, reg_addr, &new_data);
> +       if (err < 0)
> +               goto st_sensors_write_data_with_mask_error;
> +
> +       new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +       err = adata->write_byte(adata, reg_addr, new_data);
> +
> +st_sensors_write_data_with_mask_error:
> +       return err;
> +}
> +
> +int st_sensors_match_odr(const struct st_sensors *sensor,
> +               unsigned int odr, struct st_sensors_odr_available *odr_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
> +               if (sensor->odr.odr_avl[i].hz == odr) {
> +                       odr_out->hz = sensor->odr.odr_avl[i].hz;
> +                       odr_out->value = sensor->odr.odr_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +int st_sensors_match_fs(const struct st_sensors *sensor,
> +                       unsigned int fs, struct st_sensors_fs_available *fs_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].num == fs) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +int st_sensors_match_scale(const struct st_sensors *sensor,
> +                       int scale, struct st_sensors_fs_available *fs_out)
> +{
> +       int i, ret = -EINVAL;
> +
> +       for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
> +               if (sensor->fs.fs_avl[i].gain == scale) {
> +                       fs_out->num = sensor->fs.fs_avl[i].num;
> +                       fs_out->gain = sensor->fs.fs_avl[i].gain;
> +                       fs_out->value = sensor->fs.fs_avl[i].value;
> +                       ret = 0;
> +                       break;
> +               }
> +       }
> +
> +       return ret;
> +}
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
> +{
> +       int err;
> +       struct st_sensors_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       if (adata->st_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
> +               err = st_sensors_write_data_with_mask(indio_dev,
> +                       adata->st_sensors[adata->index].drdy_irq.ig1.en_addr,
> +                       adata->st_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_sensors_set_dataready_irq_error;
> +       }
> +
> +       if (adata->st_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
> +               err = st_sensors_write_data_with_mask(indio_dev,
> +               adata->st_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
> +               adata->st_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
> +                       (int)enable);
> +               if (err < 0)
> +                       goto st_sensors_set_dataready_irq_error;
> +       }
> +
> +       err = st_sensors_write_data_with_mask(indio_dev,
> +               adata->st_sensors[adata->index].drdy_irq.addr,
> +               adata->st_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
> +
> +st_sensors_set_dataready_irq_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_dataready_irq);
> +
> +static int st_sensors_set_bdu(struct iio_dev *indio_dev,
> +                               const struct st_sensors_bdu *bdu, bool value)
> +{
> +       return st_sensors_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
> +                                                               1, (u8)value);
> +}
> +
> +static int st_sensors_set_odr(struct iio_dev *indio_dev,
> +                               struct st_sensors_odr_available *odr_available)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       if ((adata->st_sensors[adata->index].odr.addr ==
> +               adata->st_sensors[adata->index].pw.addr) &&
> +               (adata->st_sensors[adata->index].odr.mask ==
> +                               adata->st_sensors[adata->index].pw.mask)) {
> +               if (adata->enabled == true) {
> +                       err = st_sensors_write_data_with_mask(indio_dev,
> +                               adata->st_sensors[adata->index].odr.addr,
> +                               adata->st_sensors[adata->index].odr.mask,
> +                               adata->st_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +               } else {
> +                       adata->odr = odr_available->hz;
> +                       err = 0;
> +               }
> +       } else {
> +               err = st_sensors_write_data_with_mask(indio_dev,
> +                               adata->st_sensors[adata->index].odr.addr,
> +                               adata->st_sensors[adata->index].odr.mask,
> +                               adata->st_sensors[adata->index].odr.num_bit,
> +                               odr_available->value);
> +       }
> +
> +       return err;
> +}
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = st_sensors_write_data_with_mask(indio_dev,
> +                       adata->st_sensors[adata->index].enable_axis.addr,
> +                       adata->st_sensors[adata->index].enable_axis.mask,
> +                       ST_SENSORS_DEF_AXIS_N_BIT, axis_enable);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_axis_enable);
> +
> +static int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
> +{
> +       int err = -EINVAL;
> +       bool found;
> +       u8 tmp_value;
> +       struct st_sensors_odr_available odr_out;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       if (enable) {
> +               found = false;
> +               tmp_value = adata->st_sensors[adata->index].pw.value_on;
> +               if ((adata->st_sensors[adata->index].odr.addr ==
> +                               adata->st_sensors[adata->index].pw.addr) &&
> +                       (adata->st_sensors[adata->index].odr.mask ==
> +                               adata->st_sensors[adata->index].pw.mask)) {
> +                       err = st_sensors_match_odr(
> +                               &adata->st_sensors[adata->index], adata->odr,
> +                                       &odr_out);
> +                       if (err < 0)
> +                               goto set_enable_error;
> +                       tmp_value = odr_out.value;
> +                       found = true;
> +               }
> +               err = st_sensors_write_data_with_mask(indio_dev,
> +                               adata->st_sensors[adata->index].pw.addr,
> +                               adata->st_sensors[adata->index].pw.mask,
> +                               adata->st_sensors[adata->index].pw.num_bit,
> +                               tmp_value);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = true;
> +               if (found)
> +                       adata->odr = odr_out.hz;
> +       } else {
> +               err = st_sensors_write_data_with_mask(indio_dev,
> +                               adata->st_sensors[adata->index].pw.addr,
> +                               adata->st_sensors[adata->index].pw.mask,
> +                               adata->st_sensors[adata->index].pw.num_bit,
> +                               adata->st_sensors[adata->index].pw.value_off);
> +               if (err < 0)
> +                       goto set_enable_error;
> +               adata->enabled = false;
> +       }
> +
> +set_enable_error:
> +       return err;
> +}
> +
> +static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +                                       struct st_sensors_fs_available *fs_avl)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = st_sensors_write_data_with_mask(indio_dev,
> +                               adata->st_sensors[adata->index].fs.addr,
> +                               adata->st_sensors[adata->index].fs.mask,
> +                               adata->st_sensors[adata->index].fs.num_bit,
> +                               fs_avl->value);
> +       if (err < 0)
> +               goto st_sensors_set_fullscale_error;
> +
> +       adata->fullscale = fs_avl->num;
> +       adata->gain = fs_avl->gain;
> +       return err;
> +
> +st_sensors_set_fullscale_error:
> +       dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +       return err;
> +}
> +
> +static int st_sensors_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       u8 outdata[ST_SENSORS_BYTE_4CHANNEL];
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&indio_dev->mlock);
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +                       goto read_error;
> +               } else {
> +                       if (!adata->enabled) {
> +                               err = -EIO;
> +                               goto read_error;
> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ST_SENSORS_BYTE_4CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               *val = ((s16)le16_to_cpup((u16 *)outdata))
> +                                                       >> ch->scan_type.shift;
> +                       }
> +               }
> +               mutex_unlock(&indio_dev->mlock);
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               *val = 0;
> +               *val2 = adata->gain;
> +               return IIO_VAL_INT_PLUS_MICRO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       mutex_unlock(&indio_dev->mlock);
> +       return err;
> +}
> +
> +static int st_sensors_write_raw(struct iio_dev *indio_dev,
> +               struct iio_chan_spec const *chan, int val, int val2, long mask)
> +{
> +       int err;
> +       struct st_sensors_fs_available fs_out;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SCALE:
> +               err = st_sensors_match_scale(&adata->st_sensors[adata->index],
> +                                                               val2, &fs_out);
> +               if (err < 0)
> +                       goto write_error;
> +
> +               err = st_sensors_set_fullscale(indio_dev, &fs_out);
> +               break;
> +       default:
> +               err = -EINVAL;
> +       }
> +
> +write_error:
> +       return err;
> +}
> +
> +static ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       unsigned int freq;
> +       struct st_sensors_odr_available odr_out;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = kstrtoint(buf, 10, &freq);
> +       if (err < 0)
> +               goto conversion_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_sensors_match_odr(&adata->st_sensors[adata->index],
> +                                                               freq, &odr_out);
> +       if (err < 0)
> +               goto st_sensors_sysfs_set_sampling_frequency_error;
> +
> +       err = st_sensors_set_odr(indio_dev, &odr_out);
> +       if (err < 0)
> +               goto st_sensors_sysfs_set_sampling_frequency_error;
> +
> +       adata->odr = odr_out.hz;
> +
> +st_sensors_sysfs_set_sampling_frequency_error:
> +       mutex_unlock(&indio_dev->mlock);
> +conversion_error:
> +       return err < 0 ? err : size;
> +}
> +
> +static ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", adata->odr);
> +}
> +
> +static ssize_t st_sensors_sysfs_set_powerdown(struct device *dev,
> +               struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       int err;
> +       bool powerdown;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +
> +       err = strtobool(buf, &powerdown);
> +       if (err < 0)
> +               goto set_enable_error;
> +
> +       mutex_lock(&indio_dev->mlock);
> +       err = st_sensors_set_enable(indio_dev, !powerdown);
> +       mutex_unlock(&indio_dev->mlock);
> +
> +set_enable_error:
> +       return err < 0 ? err : size;
> +}
> +
> +static ssize_t st_sensors_sysfs_get_powerdown(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       return sprintf(buf, "%d\n", (int)(!adata->enabled));
> +}
> +
> +static ssize_t st_sensors_sysfs_scale_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(adata->st_sensors[adata->index].fs.fs_avl);
> +                                                                       i++) {
> +               if (adata->st_sensors[adata->index].fs.fs_avl[i].num == 0)
> +                       break;
> +
> +               len += sprintf(buf+len, "0.%06u ",
> +                       adata->st_sensors[adata->index].fs.fs_avl[i].gain);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +       buf[len-1] = '\n';
> +
> +       return len;
> +}
> +
> +static ssize_t st_sensors_sysfs_sampling_frequency_available(struct
> device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, len = 0;
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       for (i = 0; i < ARRAY_SIZE(adata->st_sensors[adata->index].odr.odr_avl);
> +                                                                       i++) {
> +               if (adata->st_sensors[adata->index].odr.odr_avl[i].hz == 0)
> +                       break;
> +
> +               len += sprintf(buf + len, "%d ",
> +                       adata->st_sensors[adata->index].odr.odr_avl[i].hz);
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +       buf[len - 1] = '\n';
> +
> +       return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +               st_sensors_sysfs_sampling_frequency_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(in_sensors_scale_available, S_IRUGO,
> +                               st_sensors_sysfs_scale_available, NULL , 0);
> +
> +static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
> +               st_sensors_sysfs_get_powerdown,
> +                                       st_sensors_sysfs_set_powerdown , 0);
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +                       st_sensors_sysfs_get_sampling_frequency,
> +                               st_sensors_sysfs_set_sampling_frequency);
> +
> +static struct attribute *st_sensor_attributes[] = {
> +       &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +       &iio_dev_attr_in_sensors_scale_available.dev_attr.attr,
> +       &iio_dev_attr_powerdown.dev_attr.attr,
> +       &iio_dev_attr_sampling_frequency.dev_attr.attr,
> +       NULL,
> +};
> +
> +static const struct attribute_group st_sensor_attribute_group = {
> +       .attrs = st_sensor_attributes,
> +};
> +
> +static const struct iio_info sensor_info = {
> +       .driver_module = THIS_MODULE,
> +       .attrs = &st_sensor_attribute_group,
> +       .read_raw = &st_sensors_read_raw,
> +       .write_raw = &st_sensors_write_raw,
> +};
> +
> +static int st_sensors_get_wai_device(struct iio_dev *indio_dev, u8
> reg_addr,
> +                                                               u8 *value)
> +{
> +       int ret;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       ret = adata->read_byte(adata, reg_addr, value);
> +       if (ret < 0)
> +               goto read_byte_wai_error;
> +
> +       return 0;
> +
> +read_byte_wai_error:
> +       dev_err(&indio_dev->dev,
> +                       "failed to read WhoAmI (register 0x%x).\n", reg_addr);
> +       return -EIO;
> +}
> +
> +static int st_init_sensor(struct iio_dev *indio_dev, int wai)
> +{
> +       int err;
> +
> +       err = st_accel_init_sensor(indio_dev, wai);
> +       if (err < 0)
> +               err = st_gyro_init_sensor(indio_dev, wai);
> +
> +       if (err < 0)
> +               dev_err(&indio_dev->dev,
> +                               "device not supported -> wai (0x%x).\n", wai);
> +
> +       return err;
> +}
> +
> +int st_sensors_iio_probe(struct iio_dev *indio_dev, int irq)
> +{
> +       int err;
> +       u8 wai;
> +       struct st_sensors_odr_available odr_out;
> +       struct st_sensors_fs_available fs_out;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +       err = st_sensors_get_wai_device(indio_dev,
> +                                       ST_SENSORS_DEF_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       err = st_init_sensor(indio_dev, wai);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       indio_dev->info = &sensor_info;
> +       adata->multiread_bit = adata->st_sensors[adata->index].multi_read_bit;
> +       indio_dev->channels = adata->st_sensors[adata->index].ch;
> +       indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
> +
> +       adata->fullscale = adata->st_sensors[adata->index].fs.fs_avl[0].num;
> +       adata->odr = adata->st_sensors[adata->index].odr.odr_avl[0].hz;
> +
> +       err = st_sensors_set_enable(indio_dev, false);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       err = st_sensors_set_axis_enable(indio_dev,
> +                                               ST_SENSORS_ENABLE_ALL_CHANNELS);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       st_sensors_match_fs(&adata->st_sensors[adata->index],
> +                                               adata->fullscale, &fs_out);
> +       err = st_sensors_set_fullscale(indio_dev, &fs_out);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       st_sensors_match_odr(&adata->st_sensors[adata->index],
> +                                                       adata->odr, &odr_out);
> +       err = st_sensors_set_odr(indio_dev, &odr_out);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       err = st_sensors_set_bdu(indio_dev,
> +                               &adata->st_sensors[adata->index].bdu, true);
> +
> +       err = st_sensors_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto st_sensors_iio_probe_error;
> +
> +       if (irq > 0) {
> +               err = st_sensors_probe_trigger(indio_dev, irq);
> +               if (err < 0)
> +                       goto acc_probe_trigger_error;
> +       }
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       return err;
> +
> +iio_device_register_error:
> +       st_sensors_remove_trigger(indio_dev, irq);
> +acc_probe_trigger_error:
> +       st_sensors_deallocate_ring(indio_dev);
> +st_sensors_iio_probe_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_sensors_iio_probe);
> +
> +void st_sensors_iio_remove(struct iio_dev *indio_dev, int irq)
> +{
> +       iio_device_unregister(indio_dev);
> +       st_sensors_remove_trigger(indio_dev, irq);
> +       st_sensors_deallocate_ring(indio_dev);
> +       iio_device_free(indio_dev);
> +}
> +EXPORT_SYMBOL(st_sensors_iio_remove);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics sensors core driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st-sensors/st_sensors_i2c.c
> b/drivers/iio/common/st-sensors/st_sensors_i2c.c
> new file mode 100644
> index 0000000..2d2d5ee
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors_i2c.c
> @@ -0,0 +1,137 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include "st_sensors.h"
> +
> +#define ST_SENSORS_I2C_MULTIREAD               0x80
> +
> +static int st_sensors_i2c_read_byte(struct st_sensors_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       int err;
> +
> +       err = i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_addr);
> +       if (err < 0)
> +               goto st_sensors_i2c_read_byte_error;
> +
> +       *res_byte = err & 0xff;
> +
> +st_sensors_i2c_read_byte_error:
> +       return err < 0 ? err : 0;
> +}
> +
> +static int st_sensors_i2c_read_multiple_byte(struct st_sensors_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       if (adata->multiread_bit == true)
> +               reg_addr |= ST_SENSORS_I2C_MULTIREAD;
> +
> +       return i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
> +                                                       reg_addr, len, data);
> +}
> +
> +static int st_sensors_i2c_write_byte(struct st_sensors_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
> +                                                               reg_addr, data);
> +}
> +
> +static int __devinit st_sensors_i2c_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_sensors_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &client->dev;
> +       i2c_set_clientdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->name;
> +
> +       adata->read_byte = st_sensors_i2c_read_byte;
> +       adata->write_byte = st_sensors_i2c_write_byte;
> +       adata->read_multiple_byte = st_sensors_i2c_read_multiple_byte;
> +
> +       err = st_sensors_iio_probe(indio_dev, client->irq);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int st_sensors_i2c_remove(struct i2c_client *client)
> +{
> +       struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +       st_sensors_iio_remove(indio_dev, client->irq);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id st_sensors_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       { L3G4200D_GYRO_DEV_NAME },
> +       { LSM330DL_GYRO_DEV_NAME },
> +       { L3GD20_GYRO_DEV_NAME },
> +       { L3GD20H_GYRO_DEV_NAME },
> +       { LSM330D_GYRO_DEV_NAME },
> +       { LSM330DLC_GYRO_DEV_NAME },
> +       { LSM9DS0_GYRO_DEV_NAME },
> +       { L3G4IS_GYRO_DEV_NAME },
> +       { LSM330_GYRO_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(i2c, st_sensors_id_table);
> +
> +static struct i2c_driver st_sensors_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-sensors-i2c",
> +       },
> +       .probe = st_sensors_i2c_probe,
> +       .remove = st_sensors_i2c_remove,
> +       .id_table = st_sensors_id_table,
> +};
> +module_i2c_driver(st_sensors_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics sensors i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st-sensors/st_sensors_spi.c
> b/drivers/iio/common/st-sensors/st_sensors_spi.c
> new file mode 100644
> index 0000000..1df0c80
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors_spi.c
> @@ -0,0 +1,183 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include "st_sensors.h"
> +
> +
> +#define ST_SENSORS_SPI_READ            0x80;
> +#define ST_SENSORS_SPI_MULTIREAD       0xc0
> +
> +static int st_sensors_spi_read(struct st_sensors_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       struct spi_message msg;
> +       int err;
> +
> +       struct spi_transfer xfers[] = {
> +               {
> +                       .tx_buf = adata->tx_buf,
> +                       .bits_per_word = 8,
> +                       .len = 1,
> +               },
> +               {
> +                       .rx_buf = adata->rx_buf,
> +                       .bits_per_word = 8,
> +                       .len = len,
> +               }
> +       };
> +
> +       mutex_lock(&adata->buf_lock);
> +       if ((adata->multiread_bit == true) && (len > 1))
> +               adata->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
> +       else
> +               adata->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers[0], &msg);
> +       spi_message_add_tail(&xfers[1], &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       if (err)
> +               goto acc_spi_read_error;
> +
> +       memcpy(data, adata->rx_buf, len*sizeof(u8));
> +       mutex_unlock(&adata->buf_lock);
> +       return len;
> +
> +acc_spi_read_error:
> +       return err;
> +}
> +
> +static int st_sensors_spi_read_byte(struct st_sensors_data *adata,
> +                                               u8 reg_addr, u8 *res_byte)
> +{
> +       return st_sensors_spi_read(adata, reg_addr, 1, res_byte);
> +}
> +
> +static int st_sensors_spi_read_multiple_byte(struct st_sensors_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       return st_sensors_spi_read(adata, reg_addr, len, data);
> +}
> +
> +static int st_sensors_spi_write_byte(struct st_sensors_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       struct spi_message msg;
> +       int err;
> +
> +       struct spi_transfer xfers = {
> +               .tx_buf = adata->tx_buf,
> +               .bits_per_word = 8,
> +               .len = 2,
> +       };
> +
> +       mutex_lock(&adata->buf_lock);
> +       adata->tx_buf[0] = reg_addr;
> +       adata->tx_buf[1] = data;
> +
> +       spi_message_init(&msg);
> +       spi_message_add_tail(&xfers, &msg);
> +       err = spi_sync(to_spi_device(adata->dev), &msg);
> +       mutex_unlock(&adata->buf_lock);
> +
> +       return err;
> +}
> +
> +static int __devinit st_sensors_spi_probe(struct spi_device *spi)
> +{
> +       struct iio_dev *indio_dev;
> +       struct st_sensors_data *adata;
> +       int err;
> +
> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->dev = &spi->dev;
> +       spi_set_drvdata(spi, indio_dev);
> +
> +       indio_dev->dev.parent = &spi->dev;
> +       indio_dev->name = spi->modalias;
> +
> +       mutex_init(&adata->buf_lock);
> +       adata->read_byte = st_sensors_spi_read_byte;
> +       adata->write_byte = st_sensors_spi_write_byte;
> +       adata->read_multiple_byte = st_sensors_spi_read_multiple_byte;
> +
> +       err = st_sensors_iio_probe(indio_dev, spi->irq);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int st_sensors_spi_remove(struct spi_device *spi)
> +{
> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +       st_sensors_iio_remove(indio_dev, spi->irq);
> +
> +       return 0;
> +}
> +
> +static const struct spi_device_id st_sensors_id_table[] = {
> +       { LSM303DLH_ACCEL_DEV_NAME },
> +       { LSM303DLHC_ACCEL_DEV_NAME },
> +       { LIS3DH_ACCEL_DEV_NAME },
> +       { LSM330D_ACCEL_DEV_NAME },
> +       { LSM330DL_ACCEL_DEV_NAME },
> +       { LSM330DLC_ACCEL_DEV_NAME },
> +       { LSM303D_ACCEL_DEV_NAME },
> +       { LSM9DS0_ACCEL_DEV_NAME },
> +       { LIS331DLH_ACCEL_DEV_NAME },
> +       { LSM303DL_ACCEL_DEV_NAME },
> +       { LSM303DLM_ACCEL_DEV_NAME },
> +       { LSM330_ACCEL_DEV_NAME },
> +       { L3G4200D_GYRO_DEV_NAME },
> +       { LSM330DL_GYRO_DEV_NAME },
> +       { L3GD20_GYRO_DEV_NAME },
> +       { L3GD20H_GYRO_DEV_NAME },
> +       { LSM330D_GYRO_DEV_NAME },
> +       { LSM330DLC_GYRO_DEV_NAME },
> +       { LSM9DS0_GYRO_DEV_NAME },
> +       { L3G4IS_GYRO_DEV_NAME },
> +       { LSM330_GYRO_DEV_NAME },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(spi, st_sensors_id_table);
> +
> +static struct spi_driver st_sensors_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "st-sensors-spi",
> +       },
> +       .probe = st_sensors_spi_probe,
> +       .remove = st_sensors_spi_remove,
> +       .id_table = st_sensors_id_table,
> +};
> +module_spi_driver(st_sensors_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics sensors spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st-sensors/st_sensors_trigger.c
> b/drivers/iio/common/st-sensors/st_sensors_trigger.c
> new file mode 100644
> index 0000000..37dadc2
> --- /dev/null
> +++ b/drivers/iio/common/st-sensors/st_sensors_trigger.c
> @@ -0,0 +1,83 @@
> +/*
> + * STMicroelectronics sensors driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include "st_sensors.h"
> +
> +static int st_sensors_trig_acc_set_state(struct iio_trigger *trig, bool
> state)
> +{
> +       struct iio_dev *indio_dev = trig->private_data;
> +       return st_sensors_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops st_sensors_trigger_ops = {
> +       .owner = THIS_MODULE,
> +       .set_trigger_state = &st_sensors_trig_acc_set_state,
> +};
> +
> +int st_sensors_probe_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +       if (adata->trig == NULL) {
> +               err = -ENOMEM;
> +               dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +               goto iio_trigger_alloc_error;
> +       }
> +
> +       err = request_threaded_irq(irq,
> +                       iio_trigger_generic_data_rdy_poll,
> +                       NULL,
> +                       IRQF_TRIGGER_RISING,
> +                       adata->trig->name,
> +                       adata->trig);
> +       if (err)
> +               goto request_irq_error;
> +
> +       adata->trig->private_data = indio_dev;
> +       adata->trig->ops = &st_sensors_trigger_ops;
> +       adata->trig->dev.parent = adata->dev;
> +
> +       err = iio_trigger_register(adata->trig);
> +       if (err < 0) {
> +               dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +               goto iio_trigger_register_error;
> +       }
> +       indio_dev->trig = adata->trig;
> +
> +       return 0;
> +
> +iio_trigger_register_error:
> +       free_irq(irq, adata->trig);
> +request_irq_error:
> +       iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_sensors_probe_trigger);
> +
> +void st_sensors_remove_trigger(struct iio_dev *indio_dev, int irq)
> +{
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       iio_trigger_unregister(adata->trig);
> +       free_irq(irq, adata->trig);
> +       iio_trigger_free(adata->trig);
> +}
> +EXPORT_SYMBOL(st_sensors_remove_trigger);
> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
> index 21e27e2..3e8b746 100644
> --- a/drivers/iio/gyro/Kconfig
> +++ b/drivers/iio/gyro/Kconfig
> @@ -13,4 +13,18 @@ config HID_SENSOR_GYRO_3D
>           Say yes here to build support for the HID SENSOR
>           Gyroscope 3D.
> 
> +config ST_GYRO_3D
> +       tristate "STMicroelectronics gyroscopes 3-Axis Driver"
> +       depends on (I2C || SPI_MASTER) && SYSFS
> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       select ST_SENSORS_IIO_COMMON
> +       help
> +         Say yes here to build support for STMicroelectronics gyroscopes:
> +         L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330D, LSM330DL, LSM330DLC,
> +         LSM9DS0, L3G4IS, LSM330.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called st_gyro.
> +
>   endmenu
> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
> index 8a895d9..6a0a0d1 100644
> --- a/drivers/iio/gyro/Makefile
> +++ b/drivers/iio/gyro/Makefile
> @@ -3,3 +3,4 @@
>   #
> 
>   obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
> +obj-$(CONFIG_ST_GYRO_3D) += st_gyro.o
> diff --git a/drivers/iio/gyro/st_gyro.c b/drivers/iio/gyro/st_gyro.c
> new file mode 100644
> index 0000000..a483f19
> --- /dev/null
> +++ b/drivers/iio/gyro/st_gyro.c
> @@ -0,0 +1,252 @@
> +/*
> + * STMicroelectronics gyroscopes driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/irq.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include "../common/st-sensors/st_sensors.h"
> +
> +
> +/* FULLSCALE */
> +#define ST_GYRO_FS_AVL_250DPS                  250
> +#define ST_GYRO_FS_AVL_500DPS                  500
> +#define ST_GYRO_FS_AVL_2000DPS                 2000
> +
> +/* CUSTOM VALUES FOR SENSOR 1 */
> +#define ST_GYRO_1_WAI_EXP                      0xd3
> +#define ST_GYRO_1_ODR_ADDR                     0x20
> +#define ST_GYRO_1_ODR_MASK                     0xc0
> +#define ST_GYRO_1_ODR_N_BIT                    2
> +#define ST_GYRO_1_ODR_AVL_100HZ_VAL            0x00
> +#define ST_GYRO_1_ODR_AVL_200HZ_VAL            0x01
> +#define ST_GYRO_1_ODR_AVL_400HZ_VAL            0x02
> +#define ST_GYRO_1_ODR_AVL_800HZ_VAL            0x03
> +#define ST_GYRO_1_PW_ADDR                      0x20
> +#define ST_GYRO_1_PW_MASK                      0x08
> +#define ST_GYRO_1_PW_N_BIT                     1
> +#define ST_GYRO_1_FS_N_BIT                     2
> +#define ST_GYRO_1_FS_ADDR                      0x23
> +#define ST_GYRO_1_FS_MASK                      0x30
> +#define ST_GYRO_1_FS_AVL_250_VAL               0x00
> +#define ST_GYRO_1_FS_AVL_500_VAL               0x01
> +#define ST_GYRO_1_FS_AVL_2000_VAL              0x02
> +#define ST_GYRO_1_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
> +#define ST_GYRO_1_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
> +#define ST_GYRO_1_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
> +#define ST_GYRO_1_BDU_ADDR                     0x23
> +#define ST_GYRO_1_BDU_MASK                     0x80
> +#define ST_GYRO_1_DRDY_IRQ_ADDR                        0x22
> +#define ST_GYRO_1_DRDY_IRQ_MASK                        0x08
> +#define ST_GYRO_1_MULTIREAD_BIT                        true
> +
> +/* CUSTOM VALUES FOR SENSOR 2 */
> +#define ST_GYRO_2_WAI_EXP                      0xd4
> +#define ST_GYRO_2_ODR_ADDR                     0x20
> +#define ST_GYRO_2_ODR_MASK                     0xc0
> +#define ST_GYRO_2_ODR_N_BIT                    2
> +#define ST_GYRO_2_ODR_AVL_95HZ_VAL             0x00
> +#define ST_GYRO_2_ODR_AVL_190HZ_VAL            0x01
> +#define ST_GYRO_2_ODR_AVL_380HZ_VAL            0x02
> +#define ST_GYRO_2_ODR_AVL_760HZ_VAL            0x03
> +#define ST_GYRO_2_PW_ADDR                      0x20
> +#define ST_GYRO_2_PW_MASK                      0x08
> +#define ST_GYRO_2_PW_N_BIT                     1
> +#define ST_GYRO_2_FS_N_BIT                     2
> +#define ST_GYRO_2_FS_ADDR                      0x23
> +#define ST_GYRO_2_FS_MASK                      0x30
> +#define ST_GYRO_2_FS_AVL_250_VAL               0x00
> +#define ST_GYRO_2_FS_AVL_500_VAL               0x01
> +#define ST_GYRO_2_FS_AVL_2000_VAL              0x02
> +#define ST_GYRO_2_FS_AVL_250_GAIN              IIO_DEGREE_TO_RAD(8750)
> +#define ST_GYRO_2_FS_AVL_500_GAIN              IIO_DEGREE_TO_RAD(17500)
> +#define ST_GYRO_2_FS_AVL_2000_GAIN             IIO_DEGREE_TO_RAD(70000)
> +#define ST_GYRO_2_BDU_ADDR                     0x23
> +#define ST_GYRO_2_BDU_MASK                     0x80
> +#define ST_GYRO_2_DRDY_IRQ_ADDR                        0x22
> +#define ST_GYRO_2_DRDY_IRQ_MASK                        0x08
> +#define ST_GYRO_2_MULTIREAD_BIT                        true
> +
> +const struct iio_chan_spec st_gyro_16bit_channels[] = {
> +       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_X_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Y_L_ADDR),
> +       ST_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
> +               ST_SENSORS_DEF_16_REALBITS, ST_SENSORS_DEF_OUT_Z_L_ADDR),
> +       IIO_CHAN_SOFT_TIMESTAMP(3)
> +};
> +
> +const struct st_sensors st_gyro_sensors[] = {
> +       {
> +               .wai = ST_GYRO_1_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
> +               .odr = {
> +                       .addr = ST_GYRO_1_ODR_ADDR,
> +                       .mask = ST_GYRO_1_ODR_MASK,
> +                       .num_bit = ST_GYRO_1_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
> +                               { 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
> +                               { 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
> +                               { 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_GYRO_1_PW_ADDR,
> +                       .mask = ST_GYRO_1_PW_MASK,
> +                       .num_bit = ST_GYRO_1_PW_N_BIT,
> +                       .value_on = ST_SENSORS_DEF_POWER_ON_VALUE,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_GYRO_1_FS_ADDR,
> +                       .mask = ST_GYRO_1_FS_MASK,
> +                       .num_bit = ST_GYRO_1_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_GYRO_FS_AVL_250DPS,
> +                                       .value = ST_GYRO_1_FS_AVL_250_VAL,
> +                                       .gain = ST_GYRO_1_FS_AVL_250_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_GYRO_FS_AVL_500DPS,
> +                                       .value = ST_GYRO_1_FS_AVL_500_VAL,
> +                                       .gain = ST_GYRO_1_FS_AVL_500_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_GYRO_FS_AVL_2000DPS,
> +                                       .value = ST_GYRO_1_FS_AVL_2000_VAL,
> +                                       .gain = ST_GYRO_1_FS_AVL_2000_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_GYRO_1_BDU_ADDR,
> +                       .mask = ST_GYRO_1_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_GYRO_1_DRDY_IRQ_ADDR,
> +                       .mask = ST_GYRO_1_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
> +       },
> +       {
> +               .wai = ST_GYRO_2_WAI_EXP,
> +               .ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
> +               .odr = {
> +                       .addr = ST_GYRO_2_ODR_ADDR,
> +                       .mask = ST_GYRO_2_ODR_MASK,
> +                       .num_bit = ST_GYRO_2_ODR_N_BIT,
> +                       .odr_avl = {
> +                               { 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
> +                               { 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
> +                               { 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
> +                               { 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
> +                       },
> +               },
> +               .pw = {
> +                       .addr = ST_GYRO_2_PW_ADDR,
> +                       .mask = ST_GYRO_2_PW_MASK,
> +                       .num_bit = ST_GYRO_2_PW_N_BIT,
> +                       .value_on = ST_SENSORS_DEF_POWER_ON_VALUE,
> +                       .value_off = ST_SENSORS_DEF_POWER_OFF_VALUE,
> +               },
> +               .enable_axis = {
> +                       .addr = ST_SENSORS_DEF_AXIS_ADDR,
> +                       .mask = ST_SENSORS_DEF_AXIS_MASK,
> +               },
> +               .fs = {
> +                       .addr = ST_GYRO_2_FS_ADDR,
> +                       .mask = ST_GYRO_2_FS_MASK,
> +                       .num_bit = ST_GYRO_2_FS_N_BIT,
> +                       .fs_avl = {
> +                               [0] = {
> +                                       .num = ST_GYRO_FS_AVL_250DPS,
> +                                       .value = ST_GYRO_2_FS_AVL_250_VAL,
> +                                       .gain = ST_GYRO_2_FS_AVL_250_GAIN,
> +                               },
> +                               [1] = {
> +                                       .num = ST_GYRO_FS_AVL_500DPS,
> +                                       .value = ST_GYRO_2_FS_AVL_500_VAL,
> +                                       .gain = ST_GYRO_2_FS_AVL_500_GAIN,
> +                               },
> +                               [2] = {
> +                                       .num = ST_GYRO_FS_AVL_2000DPS,
> +                                       .value = ST_GYRO_2_FS_AVL_2000_VAL,
> +                                       .gain = ST_GYRO_2_FS_AVL_2000_GAIN,
> +                               },
> +                       },
> +               },
> +               .bdu = {
> +                       .addr = ST_GYRO_2_BDU_ADDR,
> +                       .mask = ST_GYRO_2_BDU_MASK,
> +               },
> +               .drdy_irq = {
> +                       .addr = ST_GYRO_2_DRDY_IRQ_ADDR,
> +                       .mask = ST_GYRO_2_DRDY_IRQ_MASK,
> +               },
> +               .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
> +       },
> +};
> +
> +static int st_gyro_check_device_list(struct st_sensors_data *adata, u8 wai)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(st_gyro_sensors); i++) {
> +               if (st_gyro_sensors[i].wai == wai)
> +                       break;
> +       }
> +       if (i == ARRAY_SIZE(st_gyro_sensors))
> +               goto check_device_error;
> +
> +       adata->index = i;
> +
> +       return i;
> +
> +check_device_error:
> +       return -ENODEV;
> +}
> +
> +int st_gyro_init_sensor(struct iio_dev *indio_dev, u8 wai)
> +{
> +       int err;
> +       struct st_sensors_data *adata = iio_priv(indio_dev);
> +
> +       err = st_gyro_check_device_list(adata, wai);
> +       if (err < 0)
> +               goto init_error;
> +
> +       adata->st_sensors = (const struct st_sensors *)st_gyro_sensors;
> +
> +init_error:
> +       return err;
> +}
> +EXPORT_SYMBOL(st_gyro_init_sensor);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.0.4
> 


^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2012-12-03 19:01 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-08 15:39 STMicroelectronics accelerometers driver Denis CIOCCA
2012-10-08 19:14 ` Lars-Peter Clausen
2012-10-08 19:50   ` Pavel Machek
2012-10-08 20:33     ` Lars-Peter Clausen
2012-10-08 20:37       ` Jonathan Cameron
2012-10-14 15:05         ` Denis Ciocca
2012-10-14 19:08           ` Lars-Peter Clausen
2012-10-16 17:51           ` Lars-Peter Clausen
2012-10-22  9:31             ` Denis CIOCCA
2012-10-22 18:07               ` Jonathan Cameron
2012-10-22 19:37                 ` Denis Ciocca
2012-10-24 12:44                 ` Denis CIOCCA
2012-10-26 12:10                   ` Lars-Peter Clausen
2012-10-29  8:55                     ` Denis CIOCCA
2012-10-29  9:13                       ` Lars-Peter Clausen
2012-10-29 10:24                         ` Denis CIOCCA
2012-10-29 10:30                           ` Lars-Peter Clausen
2012-10-29 10:38                             ` Denis CIOCCA
2012-10-31 14:27                             ` Denis CIOCCA
2012-10-31 16:40                               ` Lars-Peter Clausen
2012-10-31 20:33                                 ` Jonathan Cameron
2012-11-04 10:09                                 ` Denis Ciocca
2012-11-05 21:28                                   ` Jonathan Cameron
2012-11-06 11:11                                     ` Denis CIOCCA
2012-11-12 17:10                                       ` Denis CIOCCA
2012-11-12 18:48                                         ` Jonathan Cameron
2012-11-13 15:38                                           ` Denis CIOCCA
2012-11-18 13:20                                             ` Jonathan Cameron
2012-11-23 16:10                                               ` Denis CIOCCA
2012-11-24 16:23                                                 ` Jonathan Cameron
2012-11-26 16:57                                                   ` Denis CIOCCA
2012-11-27 11:52                                                   ` Denis CIOCCA
2012-11-29  9:46                                                     ` Lars-Peter Clausen
2012-11-27 15:36                                                   ` STMicroelectronics gyroscopes driver Denis CIOCCA
2012-11-29  9:51                                                     ` Lars-Peter Clausen
2012-11-30  9:13                                                       ` Denis CIOCCA
2012-11-30 10:36                                                         ` Lars-Peter Clausen
2012-11-30 13:06                                                           ` Jonathan Cameron
2012-12-03 16:40                                                             ` STMicroelectronics driver Denis CIOCCA
2012-12-03 19:01                                                               ` Lars-Peter Clausen
2012-11-19 13:00                                             ` STMicroelectronics accelerometers driver Lars-Peter Clausen
2012-11-06 11:14                                     ` Denis CIOCCA

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.