Re: STMicroelectronics accelerometers driver.

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

 



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@xxxxxx>
+ *
+ * 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@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/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@xxxxxx>");
+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@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/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@xxxxxx>");
+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@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/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@xxxxxx>");
+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@xxxxxx>
+ *
+ * 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@xxxxxx>
+ * 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@xxxxxx>
+ *
+ * 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
--
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