STMicroelectronics accelerometers driver.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxx>
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@xxxxxx>
+ *
+ * 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;
+       }
+       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)
+{
+       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);
+       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@xxxxxx>
+ *
+ * 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 = 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(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[] = {
+       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[] = {
+       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[] = {
+       {
+       .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] = {
+                       .hz = S2_ODR_AVL_3HZ,
+                       .value = S2_ODR_AVL_3HZ_VALUE,
+               },
+               .odr_avl[1] = {
+                       .hz = S2_ODR_AVL_6HZ,
+                       .value = S2_ODR_AVL_6HZ_VALUE,
+               },
+               .odr_avl[2] = {
+                       .hz = S2_ODR_AVL_12HZ,
+                       .value = S2_ODR_AVL_12HZ_VALUE,
+               },
+               .odr_avl[3] = {
+                       .hz = DEFAULT_ODR_AVL_25HZ,
+                       .value = DEFAULT_ODR_AVL_25HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[4] = {
+                       .hz = DEFAULT_ODR_AVL_50HZ,
+                       .value = DEFAULT_ODR_AVL_50HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[5] = {
+                       .hz = DEFAULT_ODR_AVL_100HZ,
+                       .value = DEFAULT_ODR_AVL_100HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[6] = {
+                       .hz = DEFAULT_ODR_AVL_200HZ,
+                       .value = DEFAULT_ODR_AVL_200HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[7] = {
+                       .hz = DEFAULT_ODR_AVL_400HZ,
+                       .value = DEFAULT_ODR_AVL_400HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[8] = {
+                       .hz = S2_ODR_AVL_800HZ,
+                       .value = S2_ODR_AVL_800HZ_VALUE,
+               },
+               .odr_avl[9] = {
+                       .hz = DEFAULT_ODR_AVL_1600HZ,
+                       .value = DEFAULT_ODR_AVL_1600HZ_VALUE + 2*OFFSET_1,
+               },
+       },
+       .fs = {
+               .addr = S3_FS_ADDR,
+               .mask = S3_FS_MASK,
+               .num_bit = S3_FS_N_BIT,
+               .fs_avl[0] = {
+                       .num = DEFAULT_FS_AVL_2_NUM,
+                       .value = DEFAULT_FS_AVL_2_VALUE,
+                       .gain = S2_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] = {
+                       .num = DEFAULT_FS_AVL_4_NUM,
+                       .value = DEFAULT_FS_AVL_4_VALUE,
+                       .gain = S2_FS_AVL_4_GAIN,
+               },
+               .fs_avl[2] = {
+                       .num = S2_FS_AVL_6_NUM,
+                       .value = S2_FS_AVL_6_VALUE,
+                       .gain = S2_FS_AVL_6_GAIN,
+               },
+               .fs_avl[3] = {
+                       .num = DEFAULT_FS_AVL_8_NUM,
+                       .value = DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+                       .gain = S2_FS_AVL_8_GAIN,
+               },
+               .fs_avl[4] = {
+                       .num = DEFAULT_FS_AVL_16_NUM,
+                       .value = DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+                       .gain = S2_FS_AVL_16_GAIN,
+               },
+       },
+       .drdy_irq = {
+               .mask = S2_DRDY_IRQ_MASK,
+       },
+       .data = {
+               .bdu = {
+                       .addr = S2_BDU_ADDR,
+                       .mask = S2_BDU_MASK,
+               },
+       },
+       },
+       {
+       .wai = S3_WAI_EXP,
+       .ch = (struct iio_chan_spec *)default_channels,
+       .odr = {
+               .mask = S3_ODR_MASK,
+               .num_bit = S3_ODR_N_BIT,
+               .odr_avl[0] = {
+                       .hz = S3_ODR_AVL_40HZ,
+                       .value = S3_ODR_AVL_40HZ_VALUE,
+               },
+               .odr_avl[1] = {
+                       .hz = S3_ODR_AVL_160HZ,
+                       .value = S3_ODR_AVL_160HZ_VALUE,
+               },
+               .odr_avl[2] = {
+                       .hz = S3_ODR_AVL_640HZ,
+                       .value = S3_ODR_AVL_640HZ_VALUE,
+               },
+               .odr_avl[3] = {
+                       .hz = S3_ODR_AVL_2560HZ,
+                       .value = S3_ODR_AVL_2560HZ_VALUE,
+               },
+       },
+       .pw = {
+               .addr = S3_POWER_ADDR,
+               .mask = S3_POWER_MASK,
+               .num_bit = S3_POWER_N_BIT,
+               .value_on = S3_POWER_ON_VALUE,
+               .value_off = S3_POWER_OFF_VALUE,
+       },
+       .fs = {
+               .addr = S2_FS_ADDR,
+               .mask = S2_FS_MASK,
+               .num_bit = S2_FS_N_BIT,
+               .fs_avl[0] = {
+                       .num = S3_FS_AVL_2_NUM,
+                       .value = S3_FS_AVL_2_VALUE,
+                       .gain = S3_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] = {
+                       .num = S3_FS_AVL_6_NUM,
+                       .value = S3_FS_AVL_6_VALUE,
+                       .gain = S3_FS_AVL_6_GAIN,
+               },
+       },
+       .drdy_irq = {
+               .addr = S3_DRDY_IRQ_ADDR,
+               .mask = S3_DRDY_IRQ_MASK,
+       },
+       .data = {
+               .bdu = {
+                       .addr = S3_BDU_ADDR,
+                       .mask = S3_BDU_MASK,
+               },
+       },
+       },
+       {
+       .wai = S4_WAI_EXP,
+       .ch = (struct iio_chan_spec *)default_channels,
+       .odr = {
+               .mask = S4_ODR_MASK,
+               .num_bit = S4_ODR_N_BIT,
+               .odr_avl[0] = {
+                       .hz = DEFAULT_ODR_AVL_50HZ,
+                       .value = S4_ODR_AVL_50HZ_VALUE,
+               },
+               .odr_avl[1] = {
+                       .hz = DEFAULT_ODR_AVL_100HZ,
+                       .value = S4_ODR_AVL_100HZ_VALUE,
+               },
+               .odr_avl[2] = {
+                       .hz = DEFAULT_ODR_AVL_400HZ,
+                       .value = S4_ODR_AVL_400HZ_VALUE,
+               },
+               .odr_avl[3] = {
+                       .hz = S4_ODR_AVL_1000HZ,
+                       .value = S4_ODR_AVL_1000HZ_VALUE,
+               },
+       },
+       .pw = {
+               .mask = S4_POWER_MASK,
+               .num_bit = S4_POWER_N_BIT,
+       },
+       .fs = {
+               .fs_avl[0] = {
+                       .num = S4_FS_AVL_2_NUM,
+                       .value = S4_FS_AVL_2_VALUE,
+                       .gain = S4_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] = {
+                       .num = S4_FS_AVL_4_NUM,
+                       .value = S4_FS_AVL_4_VALUE,
+                       .gain = S4_FS_AVL_4_GAIN,
+               },
+               .fs_avl[1] = {
+                       .num = S4_FS_AVL_8_NUM,
+                       .value = S4_FS_AVL_8_VALUE,
+                       .gain = S4_FS_AVL_8_GAIN,
+               },
+       },
+       },
+       {
+       .wai = S5_WAI_EXP,
+       .ch = (struct iio_chan_spec *)s5_channels,
+       .odr = {
+               .odr_avl[0] = {
+                       .hz = S5_ODR_AVL_3HZ,
+                       .value = S5_ODR_AVL_3HZ_VALUE,
+               },
+               .odr_avl[1] = {
+                       .hz = S5_ODR_AVL_6HZ,
+                       .value = S5_ODR_AVL_6HZ_VALUE,
+               },
+               .odr_avl[2] = {
+                       .hz = S5_ODR_AVL_12HZ,
+                       .value = S5_ODR_AVL_12HZ_VALUE,
+               },
+               .odr_avl[3] = {
+                       .hz = DEFAULT_ODR_AVL_25HZ,
+                       .value = DEFAULT_ODR_AVL_25HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[4] = {
+                       .hz = DEFAULT_ODR_AVL_50HZ,
+                       .value = DEFAULT_ODR_AVL_50HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[5] = {
+                       .hz = DEFAULT_ODR_AVL_100HZ,
+                       .value = DEFAULT_ODR_AVL_100HZ_VALUE + OFFSET_1,
+               },
+               .odr_avl[6] = {
+                       .hz = DEFAULT_ODR_AVL_400HZ,
+                       .value = DEFAULT_ODR_AVL_400HZ_VALUE,
+               },
+               .odr_avl[7] = {
+                       .hz = S5_ODR_AVL_800HZ,
+                       .value = S5_ODR_AVL_800HZ_VALUE - OFFSET_1,
+               },
+               .odr_avl[8] = {
+                       .hz = DEFAULT_ODR_AVL_1600HZ,
+                       .value = DEFAULT_ODR_AVL_1600HZ_VALUE + OFFSET_1,
+               },
+       },
+       .fs = {
+               .addr = S5_FS_ADDR,
+               .mask = S5_FS_MASK,
+               .num_bit = S5_FS_N_BIT,
+               .fs_avl[0] = {
+                       .num = DEFAULT_FS_AVL_2_NUM,
+                       .value = DEFAULT_FS_AVL_2_VALUE,
+                       .gain = S5_FS_AVL_2_GAIN,
+               },
+               .fs_avl[1] = {
+                       .num = DEFAULT_FS_AVL_4_NUM,
+                       .value = DEFAULT_FS_AVL_4_VALUE,
+                       .gain = S5_FS_AVL_4_GAIN,
+               },
+               .fs_avl[2] = {
+                       .num = S5_FS_AVL_6_NUM,
+                       .value = S5_FS_AVL_6_VALUE,
+                       .gain = S5_FS_AVL_6_GAIN,
+               },
+               .fs_avl[3] = {
+                       .num = DEFAULT_FS_AVL_8_NUM,
+                       .value = DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+                       .gain = S5_FS_AVL_8_GAIN,
+               },
+               .fs_avl[4] = {
+                       .num = DEFAULT_FS_AVL_16_NUM,
+                       .value = DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+                       .gain = S5_FS_AVL_16_GAIN,
+               },
+       },
+       .drdy_irq = {
+               .addr = S5_DRDY_IRQ_ADDR,
+               .mask = S5_DRDY_IRQ_MASK,
+               .ig1 = {
+                       .en_addr = S5_IG1_EN_ADDR,
+                       .en_mask = S5_IG1_EN_MASK,
+                       .latching_mask_addr = S5_IG1_LATCHING_MASK_ADDR,
+                       .latching_mask = S5_IG1_LATCHING_MASK,
+               },
+       },
+       .data = {
+               .bdu = {
+                       .addr = S5_BDU_ADDR,
+                       .mask = S5_BDU_MASK,
+               },
+       },
+       }
+};
+
+static struct acc_platform_data acc_default_pdata = {
+       .fullscale = DEFAULT_FS_AVL_2_NUM,
+       .sampling_frequency = 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 = iio_priv(indio_dev);
+
+       pos = 7;
+       for (j = 128; j >= 0; j = j/2) {
+               if (mask/j > 0)
+                       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));
+       err = 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_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(struct sensor *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 acc_data *adata;
+
+       adata = iio_priv(indio_dev);
+       if (sensor[adata->index].drdy_irq.ig1.en_addr > 0) {
+               err = 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 = acc_write_data_with_mask(indio_dev,
+                       sensor[adata->index].drdy_irq.ig1.latching_mask_addr,
+                       sensor[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,
+                       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 = 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 = iio_priv(indio_dev);
+
+       if ((sensor[adata->index].odr.addr == sensor[adata->index].pw.addr) &&
+                       (sensor[adata->index].odr.mask ==
+                               sensor[adata->index].pw.mask)) {
+               if (atomic_read(&adata->enabled) == POWER_ON) {
+                       err = 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 = odr_available->hz;
+                       err = 0;
+               }
+       } else {
+               err = 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 = 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 POWER_ON:
+               found = 0;
+               tmp_value = sensor[adata->index].pw.value_on;
+               if ((sensor[adata->index].odr.addr ==
+                               sensor[adata->index].pw.addr) &&
+                       (sensor[adata->index].odr.mask ==
+                                       sensor[adata->index].pw.mask)) {
+                       err = match_odr(&sensor[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,
+                               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 == 1)
+                       adata->odr = odr_out->hz;
+               break;
+       case POWER_OFF:
+               err = 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 = -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 = iio_priv(indio_dev);
+
+       err = 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 = 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[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;
+                       } 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)
+               err = -EINVAL;
+       else
+               err = 0;
+
+       return err;
+}
+
+static void register_channels(struct iio_dev *indio_dev)
+{
+       struct acc_data *adata = iio_priv(indio_dev);
+
+       indio_dev->channels = sensor[adata->index].ch;
+       indio_dev->num_channels = 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 = iio_priv(indio_dev);
+
+       odr_out = kzalloc(sizeof(*odr_out), GFP_KERNEL);
+       if (odr_out == NULL) {
+               err = -ENOMEM;
+               goto odr_out_allocate_memory_error;
+       }
+       fs_out = kzalloc(sizeof(*fs_out), GFP_KERNEL);
+       if (fs_out == NULL) {
+               err = -ENOMEM;
+               goto fs_out_allocate_memory_error;
+       }
+       err = match_fs(&sensor[adata->index], adata->pdata->fullscale, fs_out);
+       if (err < 0) {
+               pr_err("%s: validate fullscale pdata failed!\n", adata->name);
+               goto validate_fullscale_error;
+       }
+       err = 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 = 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;
+}
+
+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;
+}
+
+static int check_device_list(struct iio_dev *indio_dev, u8 wai)
+{
+       int i, sensor_length, found;
+       struct acc_data *adata;
+
+       adata = iio_priv(indio_dev);
+       found = 0;
+       sensor_length = ARRAY_SIZE(sensor);
+       for (i = 0; i < sensor_length; i++) {
+               if (sensor[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 acc_data *adata;
+
+       adata = iio_priv(indio_dev);
+       buf = reg_addr;
+       ret = adata->read_byte(adata, reg_addr);
+       if (ret < 0)
+               goto read_byte_wai_error;
+       *value = ret;
+       return (int)ret;
+
+read_byte_wai_error:
+       pr_err("%s: failed to read wai register (0x%x).\n",
+                                                       adata->name, reg_addr);
+       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 = dev_get_drvdata(dev);
+       struct 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(&sensor[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 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 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 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 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 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(&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 = 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 acc_data *adata = iio_priv(indio_dev);
+
+       mutex_lock(&indio_dev->mlock);
+       n = ARRAY_SIZE(sensor[adata->index].fs.fs_avl);
+       for (i = 0; i < n; i++) {
+               if (sensor[adata->index].fs.fs_avl[i].num != 0) {
+                       len = 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] = "";
+       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);
+}
+
+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 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;
+
+       adata->fullscale = adata->pdata->fullscale;
+       adata->odr = adata->pdata->sampling_frequency;
+       set_enable(indio_dev, POWER_OFF);
+       match_fs(&sensor[adata->index], adata->fullscale, fs_out);
+       err = set_fullscale(indio_dev, fs_out);
+       if (err < 0)
+               goto init_error;
+       match_odr(&sensor[adata->index], adata->odr, odr_out);
+       err = set_odr(indio_dev, odr_out);
+       if (err < 0)
+               goto init_error;
+       err = 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 = 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@xxxxxx>");
+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@xxxxxx>
+ *
+ * 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);
+       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->irq_data_ready = &client->irq;
+
+       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_i2c_remove(struct i2c_client *client)
+{
+       int err;
+       struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+       err = acc_iio_remove(indio_dev);
+       iio_device_free(indio_dev);
+       return err;
+}
+
+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 },
+       { 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 = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "STMicroelectronics i2c accelerometers",
+       },
+       .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@xxxxxx>");
+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@xxxxxx>
+ *
+ * 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[] = {
+               {
+                       .tx_buf = &tx,
+                       .bits_per_word = 8,
+                       .len = 1,
+                       .cs_change = 0,
+                       .delay_usecs = 10,
+               },
+               {
+                       .rx_buf = &res,
+                       .bits_per_word = 8,
+                       .len = 1,
+                       .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(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 *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 != 0)
+               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(adata->spi, &msg);
+       mutex_unlock(&adata->slock);
+
+       return err;
+}
+
+static inline s32 acc_spi_write_byte(struct 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(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 = 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->irq_data_ready = &client->irq;
+
+       /* dummy read */
+       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);
+       iio_device_free(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 },
+       { 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 = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "STMicroelectronics spi accelerometers",
+       },
+       .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@xxxxxx>");
+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@xxxxxx>
+ *
+ * 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 = 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);
+       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;
+}
+
+int acc_remove_trigger(struct iio_dev *indio_dev)
+{
+       struct 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);
+
+       return 0;
+}
--
1.7.0.4


 From 1fe8b75e0ec1197781c559935de23e116773c441 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <denis.ciocca@xxxxxx>
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@xxxxxx>
+ * 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
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux