All of lore.kernel.org
 help / color / mirror / Atom feed
* STMicroelectronics drivers
@ 2013-01-18 16:58 Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18 16:58 UTC (permalink / raw)
  To: jic23, lars, linux-iio

Hi Jonathan, Lars,

I have modified some part of code and added it to common library, the remainder I think can not be moved.
I have also modified the kconfig and makefile.

Thanks!

Denis

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
@ 2013-01-18 16:58 ` Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 2/4] iio:accel: Add STMicroelectronics accelerometers driver Denis CIOCCA
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18 16:58 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch add a generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |   1 +
 drivers/iio/common/Makefile                        |   1 +
 drivers/iio/common/st_sensors/Kconfig              |   6 +
 drivers/iio/common/st_sensors/Makefile             |  21 ++
 drivers/iio/common/st_sensors/st_sensors_buffer.c  | 115 ++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    | 411 +++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |  81 ++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     | 128 +++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |  77 ++++
 include/linux/iio/common/st_sensors.h              | 280 ++++++++++++++
 10 files changed, 1121 insertions(+)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..0749f5c
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,6 @@
+#
+# Hid Sensor common modules
+#
+
+config IIO_ST_SENSORS_CORE
+	bool
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..8c9a57a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+ifneq ($(CONFIG_I2C),)
+st_accel_i2c			= st_sensors_i2c.o
+endif
+
+ifneq ($(CONFIG_SPI_MASTER),)
+st_accel_spi			= st_sensors_spi.o
+endif
+
+ifneq ($(CONFIG_IIO_BUFFER),)
+st_sensors_triggered_buffer	= st_sensors_trigger.o st_sensors_buffer.o
+endif
+
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += 	st_sensors_library.o
+st_sensors_library-y := st_sensors_core.o \
+			$(st_accel_i2c) \
+			$(st_accel_spi) \
+			$(st_sensors_triggered_buffer)
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..f4a5adc
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..661cc2b
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,411 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/unaligned/le_byteshift.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+							"%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+					"0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (sensor->odr.odr_avl[i].hz == 0)
+			goto st_sensors_match_odr_error;
+
+		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;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (sensor->fs.fs_avl[i].num == 0)
+			goto st_sensors_match_odr_error;
+
+		if (sensor->fs.fs_avl[i].num == fs) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+		sensor->fs.addr, sensor->fs.mask, sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+							&sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+							sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+				sensor->enable_axis.addr,
+					sensor->enable_axis.mask, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+							sensor->bdu.mask, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.ig1.en_addr,
+			sensor->drdy_irq.ig1.en_mask, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
+							u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = (s16)get_unaligned_le16(outdata);
+
+read_error:
+	return err;
+}
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+		err = -EBUSY;
+		goto read_error;
+	} else {
+		err = st_sensors_set_enable(indio_dev, sensor, true);
+		if (err < 0)
+			goto read_error;
+
+		msleep((sensor->bootime * 1000) / sdata->odr);
+		err = st_sensors_read_axis_data(indio_dev, ch->address, val);
+		if (err < 0)
+			goto read_error;
+
+		*val = *val >> ch->scan_type.shift;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return err;
+
+read_error:
+	mutex_unlock(&indio_dev->mlock);
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_info_raw);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors)
+{
+	int i, n, err;
+	u8 wai;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
+					ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
+		goto read_wai_error;
+	}
+
+	for (i = 0; i < num_sensors_list; i++) {
+		if (sensors[i].wai == wai)
+			break;
+	}
+	if (i == num_sensors_list)
+		goto device_not_supported;
+
+	for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
+		if (strcmp(indio_dev->name,
+				&sensors[i].sensors_supported[n][0]) == 0)
+			break;
+	}
+	if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
+		dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
+		goto sensor_name_mismatch;
+	}
+
+	sdata->index = i;
+
+	return i;
+
+device_not_supported:
+	dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
+sensor_name_mismatch:
+	err = -ENODEV;
+read_wai_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_check_device_support);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..b2d2fdf
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = &st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..5030766
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = &st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..9c62c6a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,77 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..a8a0c40
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,280 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	const struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @sensors_supported: List of supported sensors by struct itself.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+#ifdef CONFIG_SPI_MASTER
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_I2C
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_IIO_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+					const struct st_sensors *sensor);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors);
+
+#endif /* ST_SENSORS_H */
-- 
1.8.0.3


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

* [PATCH 2/4] iio:accel: Add STMicroelectronics accelerometers driver
  2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
@ 2013-01-18 16:58 ` Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 3/4] iio:gyro: Add STMicroelectronics gyroscopes driver Denis CIOCCA
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18 16:58 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

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

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/accel/Kconfig           |  35 +++
 drivers/iio/accel/Makefile          |   5 +
 drivers/iio/accel/st_accel_buffer.c | 119 ++++++++
 drivers/iio/accel/st_accel_core.c   | 582 ++++++++++++++++++++++++++++++++++++
 drivers/iio/accel/st_accel_i2c.c    |  87 ++++++
 drivers/iio/accel/st_accel_spi.c    |  86 ++++++
 include/linux/iio/accel/st_accel.h  |  52 ++++
 7 files changed, 966 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 include/linux/iio/accel/st_accel.h

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 05e996f..ba0a7a4 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -14,4 +14,39 @@ config HID_SENSOR_ACCEL_3D
 	  Say yes here to build support for the HID SENSOR
 	  accelerometers 3D.
 
+config IIO_ST_ACCEL_3AXIS
+	tristate "STMicroelectronics accelerometers 3-Axis Driver"
+	depends on (I2C || SPI_MASTER) && SYSFS
+	select IIO_ST_SENSORS_CORE
+	select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
+	select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
+	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+	select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER)
+	help
+	  Say yes here to build support for STMicroelectronics accelerometers:
+	  LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
+	  LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+	  This driver can also be built as a module. If so, will be created
+	  these modules:
+	  - st_accel_core (core functions for the driver [it is mandatory]);
+	  - st_accel_i2c (necessary for the I2C devices [optional*]);
+	  - st_accel_spi (necessary for the SPI devices [optional*]);
+	  - st_accel_buffer (necessary for triggered buffer [optional]);
+	  
+	  (*) one of these is necessary to do something.
+
+config IIO_ST_ACCEL_I2C_3AXIS
+	tristate
+	depends on IIO_ST_ACCEL_3AXIS
+
+config IIO_ST_ACCEL_SPI_3AXIS
+	tristate
+	depends on IIO_ST_ACCEL_3AXIS
+
+config IIO_ST_ACCEL_BUFFER
+	tristate
+	depends on IIO_ST_ACCEL_3AXIS
+	depends on (IIO_ST_ACCEL_I2C_3AXIS || IIO_ST_ACCEL_SPI_3AXIS)
+
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855..cb139f1 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,8 @@
 #
 
 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
+
+obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel_core.o
+obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
+obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
+obj-$(CONFIG_IIO_ST_ACCEL_BUFFER) += st_accel_buffer.o
diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..ac90903
--- /dev/null
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -0,0 +1,119 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/delay.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>
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = trig->private_data;
+	return st_accel_set_dataready_irq(indio_dev, state);
+}
+EXPORT_SYMBOL(st_accel_trig_set_state);
+
+static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
+{
+	int err;
+
+	err = st_accel_set_enable(indio_dev, true);
+	if (err < 0)
+		goto st_accel_set_enable_error;
+
+	err = iio_sw_buffer_preenable(indio_dev);
+
+st_accel_set_enable_error:
+	return err;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (adata->buffer_data == NULL) {
+		err = -ENOMEM;
+		goto allocate_memory_error;
+	}
+
+	err = st_accel_set_axis_enable(indio_dev,
+					(u8)indio_dev->active_scan_mask[0]);
+	if (err < 0)
+		goto st_accel_buffer_postenable_error;
+
+	err = iio_triggered_buffer_postenable(indio_dev);
+	if (err < 0)
+		goto st_accel_buffer_postenable_error;
+
+	return err;
+
+st_accel_buffer_postenable_error:
+	kfree(adata->buffer_data);
+allocate_memory_error:
+	return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+	if (err < 0)
+		goto st_accel_buffer_predisable_error;
+
+	err = st_accel_set_axis_enable(indio_dev,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+	if (err < 0)
+		goto st_accel_buffer_predisable_error;
+
+	err = st_accel_set_enable(indio_dev, false);
+
+st_accel_buffer_predisable_error:
+	kfree(adata->buffer_data);
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
+	.preenable = &st_accel_buffer_preenable,
+	.postenable = &st_accel_buffer_postenable,
+	.predisable = &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		&st_sensors_trigger_handler, &st_accel_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..4c32a4d
--- /dev/null
+++ b/drivers/iio/accel/st_accel_core.c
@@ -0,0 +1,582 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR		0x28
+#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR		0x2a
+#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR		0x2c
+
+/* FULLSCALE */
+#define ST_ACCEL_FS_AVL_2G			2
+#define ST_ACCEL_FS_AVL_4G			4
+#define ST_ACCEL_FS_AVL_6G			6
+#define ST_ACCEL_FS_AVL_8G			8
+#define ST_ACCEL_FS_AVL_16G			16
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_ACCEL_1_WAI_EXP			0x33
+#define ST_ACCEL_1_ODR_ADDR			0x20
+#define ST_ACCEL_1_ODR_MASK			0xf0
+#define ST_ACCEL_1_ODR_AVL_1HZ_VAL		0x01
+#define ST_ACCEL_1_ODR_AVL_10HZ_VAL		0x02
+#define ST_ACCEL_1_ODR_AVL_25HZ_VAL		0x03
+#define ST_ACCEL_1_ODR_AVL_50HZ_VAL		0x04
+#define ST_ACCEL_1_ODR_AVL_100HZ_VAL		0x05
+#define ST_ACCEL_1_ODR_AVL_200HZ_VAL		0x06
+#define ST_ACCEL_1_ODR_AVL_400HZ_VAL		0x07
+#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL		0x08
+#define ST_ACCEL_1_FS_ADDR			0x23
+#define ST_ACCEL_1_FS_MASK			0x30
+#define ST_ACCEL_1_FS_AVL_2_VAL			0x00
+#define ST_ACCEL_1_FS_AVL_4_VAL			0x01
+#define ST_ACCEL_1_FS_AVL_8_VAL			0x02
+#define ST_ACCEL_1_FS_AVL_16_VAL		0x03
+#define ST_ACCEL_1_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_1_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_1_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(4000)
+#define ST_ACCEL_1_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(12000)
+#define ST_ACCEL_1_BDU_ADDR			0x23
+#define ST_ACCEL_1_BDU_MASK			0x80
+#define ST_ACCEL_1_DRDY_IRQ_ADDR		0x22
+#define ST_ACCEL_1_DRDY_IRQ_MASK		0x10
+#define ST_ACCEL_1_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_ACCEL_2_WAI_EXP			0x32
+#define ST_ACCEL_2_ODR_ADDR			0x20
+#define ST_ACCEL_2_ODR_MASK			0x18
+#define ST_ACCEL_2_ODR_AVL_50HZ_VAL		0x00
+#define ST_ACCEL_2_ODR_AVL_100HZ_VAL		0x01
+#define ST_ACCEL_2_ODR_AVL_400HZ_VAL		0x02
+#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL		0x03
+#define ST_ACCEL_2_PW_ADDR			0x20
+#define ST_ACCEL_2_PW_MASK			0xe0
+#define ST_ACCEL_2_FS_ADDR			0x23
+#define ST_ACCEL_2_FS_MASK			0x30
+#define ST_ACCEL_2_FS_AVL_2_VAL			0X00
+#define ST_ACCEL_2_FS_AVL_4_VAL			0X01
+#define ST_ACCEL_2_FS_AVL_8_VAL			0x03
+#define ST_ACCEL_2_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_2_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(2000)
+#define ST_ACCEL_2_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(3900)
+#define ST_ACCEL_2_BDU_ADDR			0x23
+#define ST_ACCEL_2_BDU_MASK			0x80
+#define ST_ACCEL_2_DRDY_IRQ_ADDR		0x22
+#define ST_ACCEL_2_DRDY_IRQ_MASK		0x02
+#define ST_ACCEL_2_MULTIREAD_BIT		true
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_ACCEL_3_WAI_EXP			0x40
+#define ST_ACCEL_3_ODR_ADDR			0x20
+#define ST_ACCEL_3_ODR_MASK			0xf0
+#define ST_ACCEL_3_ODR_AVL_3HZ_VAL		0x01
+#define ST_ACCEL_3_ODR_AVL_6HZ_VAL		0x02
+#define ST_ACCEL_3_ODR_AVL_12HZ_VAL		0x03
+#define ST_ACCEL_3_ODR_AVL_25HZ_VAL		0x04
+#define ST_ACCEL_3_ODR_AVL_50HZ_VAL		0x05
+#define ST_ACCEL_3_ODR_AVL_100HZ_VAL		0x06
+#define ST_ACCEL_3_ODR_AVL_200HZ_VAL		0x07
+#define ST_ACCEL_3_ODR_AVL_400HZ_VAL		0x08
+#define ST_ACCEL_3_ODR_AVL_800HZ_VAL		0x09
+#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL		0x0a
+#define ST_ACCEL_3_FS_ADDR			0x24
+#define ST_ACCEL_3_FS_MASK			0x38
+#define ST_ACCEL_3_FS_AVL_2_VAL			0X00
+#define ST_ACCEL_3_FS_AVL_4_VAL			0X01
+#define ST_ACCEL_3_FS_AVL_6_VAL			0x02
+#define ST_ACCEL_3_FS_AVL_8_VAL			0x03
+#define ST_ACCEL_3_FS_AVL_16_VAL		0x04
+#define ST_ACCEL_3_FS_AVL_2_GAIN		IIO_G_TO_M_S_2(61)
+#define ST_ACCEL_3_FS_AVL_4_GAIN		IIO_G_TO_M_S_2(122)
+#define ST_ACCEL_3_FS_AVL_6_GAIN		IIO_G_TO_M_S_2(183)
+#define ST_ACCEL_3_FS_AVL_8_GAIN		IIO_G_TO_M_S_2(244)
+#define ST_ACCEL_3_FS_AVL_16_GAIN		IIO_G_TO_M_S_2(732)
+#define ST_ACCEL_3_BDU_ADDR			0x20
+#define ST_ACCEL_3_BDU_MASK			0x08
+#define ST_ACCEL_3_DRDY_IRQ_ADDR		0x23
+#define ST_ACCEL_3_DRDY_IRQ_MASK		0x80
+#define ST_ACCEL_3_IG1_EN_ADDR			0x23
+#define ST_ACCEL_3_IG1_EN_MASK			0x08
+#define ST_ACCEL_3_MULTIREAD_BIT		false
+
+static const struct iio_chan_spec st_accel_12bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_sensors st_accel_sensors[] = {
+	{
+		.wai = ST_ACCEL_1_WAI_EXP,
+		.sensors_supported = {
+			[0] = LIS3DH_ACCEL_DEV_NAME,
+			[1] = LSM303DLH_ACCEL_DEV_NAME,
+			[2] = LSM303DLHC_ACCEL_DEV_NAME,
+			[3] = LSM330D_ACCEL_DEV_NAME,
+			[4] = LSM330DL_ACCEL_DEV_NAME,
+			[5] = LSM330DLC_ACCEL_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_1_ODR_ADDR,
+			.mask = ST_ACCEL_1_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+				{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+				{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+				{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+				{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+				{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+				{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+				{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_1_ODR_ADDR,
+			.mask = ST_ACCEL_1_ODR_MASK,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_1_FS_ADDR,
+			.mask = ST_ACCEL_1_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_1_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_1_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_1_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = ST_ACCEL_1_FS_AVL_16_VAL,
+					.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_1_BDU_ADDR,
+			.mask = ST_ACCEL_1_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_1_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_ACCEL_2_WAI_EXP,
+		.sensors_supported = {
+			[0] = LIS331DLH_ACCEL_DEV_NAME,
+			[1] = LSM303DL_ACCEL_DEV_NAME,
+			[2] = LSM303DLM_ACCEL_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_2_ODR_ADDR,
+			.mask = ST_ACCEL_2_ODR_MASK,
+			.odr_avl = {
+				{ 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
+				{ 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
+				{ 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
+				{ 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_2_PW_ADDR,
+			.mask = ST_ACCEL_2_PW_MASK,
+			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_2_FS_ADDR,
+			.mask = ST_ACCEL_2_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_2_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_2_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_2_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_2_BDU_ADDR,
+			.mask = ST_ACCEL_2_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_2_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_ACCEL_3_WAI_EXP,
+		.sensors_supported = {
+			[0] = LSM330_ACCEL_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
+		.odr = {
+			.addr = ST_ACCEL_3_ODR_ADDR,
+			.mask = ST_ACCEL_3_ODR_MASK,
+			.odr_avl = {
+				{ 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL },
+				{ 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, },
+				{ 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, },
+				{ 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, },
+				{ 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
+				{ 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
+				{ 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, },
+				{ 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
+				{ 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, },
+				{ 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_ACCEL_3_ODR_ADDR,
+			.mask = ST_ACCEL_3_ODR_MASK,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_ACCEL_3_FS_ADDR,
+			.mask = ST_ACCEL_3_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_ACCEL_FS_AVL_2G,
+					.value = ST_ACCEL_3_FS_AVL_2_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
+				},
+				[1] = {
+					.num = ST_ACCEL_FS_AVL_4G,
+					.value = ST_ACCEL_3_FS_AVL_4_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
+				},
+				[2] = {
+					.num = ST_ACCEL_FS_AVL_6G,
+					.value = ST_ACCEL_3_FS_AVL_6_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_6_GAIN,
+				},
+				[3] = {
+					.num = ST_ACCEL_FS_AVL_8G,
+					.value = ST_ACCEL_3_FS_AVL_8_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
+				},
+				[4] = {
+					.num = ST_ACCEL_FS_AVL_16G,
+					.value = ST_ACCEL_3_FS_AVL_16_VAL,
+					.gain = ST_ACCEL_3_FS_AVL_16_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_ACCEL_3_BDU_ADDR,
+			.mask = ST_ACCEL_3_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
+			.mask = ST_ACCEL_3_DRDY_IRQ_MASK,
+			.ig1 = {
+				.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
+				.en_mask = ST_ACCEL_3_IG1_EN_MASK,
+			},
+		},
+		.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+};
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		err = st_sensors_read_info_raw(indio_dev, ch, val,
+					&st_accel_sensors[adata->index]);
+		if (err < 0)
+			goto read_error;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = adata->current_fullscale->gain;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	int err;
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_sensors_set_fullscale_by_gain(indio_dev,
+					&st_accel_sensors[adata->index], val2);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool state)
+{
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return st_sensors_set_dataready_irq(indio_dev,
+					&st_accel_sensors[adata->index], state);
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit)
+{
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return st_sensors_set_axis_enable(indio_dev,
+				&st_accel_sensors[adata->index], active_bit);
+}
+EXPORT_SYMBOL(st_accel_set_axis_enable);
+
+int st_accel_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return st_sensors_set_enable(indio_dev,
+				&st_accel_sensors[adata->index], enable);
+}
+EXPORT_SYMBOL(st_accel_set_enable);
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned int odr;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	err = kstrtoint(buf, 10, &odr);
+	if (err < 0)
+		goto conversion_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_sensors_set_odr(indio_dev,
+					&st_accel_sensors[adata->index], odr);
+	mutex_unlock(&indio_dev->mlock);
+
+conversion_error:
+	return err < 0 ? err : size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_scale_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return st_sensors_get_scale_avl(indio_dev,
+			st_accel_sensors[adata->index].fs.fs_avl, buf);
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	return st_sensors_get_sampling_frequency_avl(indio_dev,
+			st_accel_sensors[adata->index].odr.odr_avl, buf);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_accel_sysfs_sampling_frequency_avail);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+					st_accel_sysfs_scale_avail, NULL , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			st_accel_sysfs_get_sampling_frequency,
+					st_accel_sysfs_set_sampling_frequency);
+
+static struct attribute *st_accel_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group = {
+	.attrs = st_accel_attributes,
+};
+
+static const struct iio_info accel_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &st_accel_attribute_group,
+	.read_raw = &st_accel_read_raw,
+	.write_raw = &st_accel_write_raw,
+};
+
+static struct iio_trigger_ops st_accel_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &st_accel_trig_set_state,
+};
+
+int st_accel_common_probe(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &accel_info;
+
+	err = st_sensors_check_device_support(indio_dev,
+				ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
+	if (err < 0)
+		goto st_accel_common_probe_error;
+
+	adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+	indio_dev->channels = st_accel_sensors[adata->index].ch;
+	indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
+
+	adata->current_fullscale = (struct st_sensor_fullscale_avl *)
+				&st_accel_sensors[adata->index].fs.fs_avl[0];
+	adata->odr = st_accel_sensors[adata->index].odr.odr_avl[0].hz;
+
+	err = st_sensors_init_sensor(indio_dev,
+					&st_accel_sensors[adata->index]);
+	if (err < 0)
+		goto st_accel_common_probe_error;
+
+	if (adata->get_irq_data_ready(indio_dev) > 0) {
+		err = st_accel_allocate_ring(indio_dev);
+		if (err < 0)
+			goto st_accel_common_probe_error;
+
+		err = st_sensors_allocate_trigger(indio_dev,
+							&st_accel_trigger_ops);
+		if (err < 0)
+			goto st_accel_probe_trigger_error;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto st_accel_device_register_error;
+
+	return err;
+
+st_accel_device_register_error:
+	if (adata->get_irq_data_ready(indio_dev) > 0)
+		st_sensors_deallocate_trigger(indio_dev);
+st_accel_probe_trigger_error:
+	if (adata->get_irq_data_ready(indio_dev) > 0)
+		st_accel_deallocate_ring(indio_dev);
+st_accel_common_probe_error:
+	return err;
+}
+EXPORT_SYMBOL(st_accel_common_probe);
+
+void st_accel_common_remove(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *adata = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	if (adata->get_irq_data_ready(indio_dev) > 0) {
+		st_sensors_deallocate_trigger(indio_dev);
+		st_accel_deallocate_ring(indio_dev);
+	}
+	iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_common_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..e782717
--- /dev/null
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -0,0 +1,87 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_accel_i2c_probe(struct i2c_client *client,
+						const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_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;
+
+	st_sensors_i2c_configure(indio_dev, client, adata);
+
+	err = st_accel_common_probe(indio_dev);
+	if (err < 0)
+		goto st_accel_common_probe_error;
+
+	return 0;
+
+st_accel_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_accel_i2c_remove(struct i2c_client *client)
+{
+	st_accel_common_remove(i2c_get_clientdata(client));
+
+	return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] = {
+	{ LSM303DLH_ACCEL_DEV_NAME },
+	{ LSM303DLHC_ACCEL_DEV_NAME },
+	{ LIS3DH_ACCEL_DEV_NAME },
+	{ LSM330D_ACCEL_DEV_NAME },
+	{ LSM330DL_ACCEL_DEV_NAME },
+	{ LSM330DLC_ACCEL_DEV_NAME },
+	{ LIS331DLH_ACCEL_DEV_NAME },
+	{ LSM303DL_ACCEL_DEV_NAME },
+	{ LSM303DLM_ACCEL_DEV_NAME },
+	{ LSM330_ACCEL_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-accel-i2c",
+	},
+	.probe = st_accel_i2c_probe,
+	.remove = st_accel_i2c_remove,
+	.id_table = st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..92e78ba
--- /dev/null
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -0,0 +1,86 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/accel/st_accel.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_accel_spi_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *adata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*adata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	adata = iio_priv(indio_dev);
+	adata->dev = &spi->dev;
+
+	st_sensors_spi_configure(indio_dev, spi, adata);
+
+	err = st_accel_common_probe(indio_dev);
+	if (err < 0)
+		goto st_accel_common_probe_error;
+
+	return 0;
+
+st_accel_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_accel_spi_remove(struct spi_device *spi)
+{
+	st_accel_common_remove(spi_get_drvdata(spi));
+
+	return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] = {
+	{ LSM303DLH_ACCEL_DEV_NAME },
+	{ LSM303DLHC_ACCEL_DEV_NAME },
+	{ LIS3DH_ACCEL_DEV_NAME },
+	{ LSM330D_ACCEL_DEV_NAME },
+	{ LSM330DL_ACCEL_DEV_NAME },
+	{ LSM330DLC_ACCEL_DEV_NAME },
+	{ LIS331DLH_ACCEL_DEV_NAME },
+	{ LSM303DL_ACCEL_DEV_NAME },
+	{ LSM303DLM_ACCEL_DEV_NAME },
+	{ LSM330_ACCEL_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-accel-spi",
+	},
+	.probe = st_accel_spi_probe,
+	.remove = st_accel_spi_remove,
+	.id_table = st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/accel/st_accel.h b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..e30d1e3
--- /dev/null
+++ b/include/linux/iio/accel/st_accel.h
@@ -0,0 +1,52 @@
+/*
+ * STMicroelectronics accelerometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_ACCEL_H
+#define ST_ACCEL_H
+
+#include <linux/types.h>
+#include "../common/st_sensors.h"
+
+#define LSM303DLH_ACCEL_DEV_NAME	"lsm303dlh_accel"
+#define LSM303DLHC_ACCEL_DEV_NAME	"lsm303dlhc_accel"
+#define LIS3DH_ACCEL_DEV_NAME		"lis3dh"
+#define LSM330D_ACCEL_DEV_NAME		"lsm330d_accel"
+#define LSM330DL_ACCEL_DEV_NAME		"lsm330dl_accel"
+#define LSM330DLC_ACCEL_DEV_NAME	"lsm330dlc_accel"
+#define LIS331DLH_ACCEL_DEV_NAME	"lis331dlh"
+#define LSM303DL_ACCEL_DEV_NAME		"lsm303dl_accel"
+#define LSM303DLM_ACCEL_DEV_NAME	"lsm303dlm_accel"
+#define LSM330_ACCEL_DEV_NAME		"lsm330_accel"
+
+int st_accel_common_probe(struct iio_dev *indio_dev);
+void st_accel_common_remove(struct iio_dev *indio_dev);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool state);
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit);
+int st_accel_set_enable(struct iio_dev *indio_dev, bool enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev);
+int st_accel_trig_set_state(struct iio_trigger *trig, bool state);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	return 0;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
-- 
1.8.0.3

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

* [PATCH 3/4] iio:gyro: Add STMicroelectronics gyroscopes driver
  2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 2/4] iio:accel: Add STMicroelectronics accelerometers driver Denis CIOCCA
@ 2013-01-18 16:58 ` Denis CIOCCA
  2013-01-18 16:58 ` [PATCH 4/4] iio:magnetometer: Add STMicroelectronics magnetometers driver Denis CIOCCA
  2013-01-20 12:54 ` STMicroelectronics drivers Jonathan Cameron
  4 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18 16:58 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch adds a generic gyroscope driver for STMicroelectronics
gyroscopes, currently it supports:
L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/gyro/Kconfig          |  34 +++
 drivers/iio/gyro/Makefile         |   5 +
 drivers/iio/gyro/st_gyro_buffer.c | 119 ++++++++++
 drivers/iio/gyro/st_gyro_core.c   | 448 ++++++++++++++++++++++++++++++++++++++
 drivers/iio/gyro/st_gyro_i2c.c    |  84 +++++++
 drivers/iio/gyro/st_gyro_spi.c    |  83 +++++++
 include/linux/iio/gyro/st_gyro.h  |  49 +++++
 7 files changed, 822 insertions(+)
 create mode 100644 drivers/iio/gyro/st_gyro_buffer.c
 create mode 100644 drivers/iio/gyro/st_gyro_core.c
 create mode 100644 drivers/iio/gyro/st_gyro_i2c.c
 create mode 100644 drivers/iio/gyro/st_gyro_spi.c
 create mode 100644 include/linux/iio/gyro/st_gyro.h

diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 96b68f6..6fb4225 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -23,4 +23,38 @@ config HID_SENSOR_GYRO_3D
 	  Say yes here to build support for the HID SENSOR
 	  Gyroscope 3D.
 
+config IIO_ST_GYRO_3AXIS
+	tristate "STMicroelectronics gyroscopes 3-Axis Driver"
+	depends on (I2C || SPI_MASTER) && SYSFS
+	select IIO_ST_SENSORS_CORE
+	select IIO_ST_GYRO_I2C_3AXIS if (I2C)
+	select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER)
+	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+	select IIO_ST_GYRO_BUFFER if (IIO_TRIGGERED_BUFFER)
+	help
+	  Say yes here to build support for STMicroelectronics gyroscopes:
+	  L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330.
+
+	  This driver can also be built as a module. If so, will be created
+	  these modules:
+	  - st_gyro_core (core functions for the driver [it is mandatory]);
+	  - st_gyro_i2c (necessary for the I2C devices [optional*]);
+	  - st_gyro_spi (necessary for the SPI devices [optional*]);
+	  - st_gyro_buffer (necessary for triggered buffer [optional]);
+	  
+	  (*) one of these is necessary to do something.
+
+config IIO_ST_GYRO_I2C_3AXIS
+	tristate
+	depends on IIO_ST_GYRO_3AXIS
+
+config IIO_ST_GYRO_SPI_3AXIS
+	tristate
+	depends on IIO_ST_GYRO_3AXIS
+
+config IIO_ST_GYRO_BUFFER
+	tristate
+	depends on IIO_ST_GYRO_3AXIS
+	depends on (IIO_ST_GYRO_I2C_3AXIS || IIO_ST_GYRO_SPI_3AXIS)
+
 endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 702a058..e800cd6 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -4,3 +4,8 @@
 
 obj-$(CONFIG_ADIS16136) += adis16136.o
 obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
+
+obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro_core.o
+obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o
+obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o
+obj-$(CONFIG_IIO_ST_GYRO_BUFFER) += st_gyro_buffer.o
diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c
new file mode 100644
index 0000000..c1fc390
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_buffer.c
@@ -0,0 +1,119 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = trig->private_data;
+	return st_gyro_set_dataready_irq(indio_dev, state);
+}
+EXPORT_SYMBOL(st_gyro_trig_set_state);
+
+static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
+{
+	int err;
+
+	err = st_gyro_set_enable(indio_dev, true);
+	if (err < 0)
+		goto st_gyro_set_enable_error;
+
+	err = iio_sw_buffer_preenable(indio_dev);
+
+st_gyro_set_enable_error:
+	return err;
+}
+
+static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (gdata->buffer_data == NULL) {
+		err = -ENOMEM;
+		goto allocate_memory_error;
+	}
+
+	err = st_gyro_set_axis_enable(indio_dev,
+					(u8)indio_dev->active_scan_mask[0]);
+	if (err < 0)
+		goto st_gyro_buffer_postenable_error;
+
+	err = iio_triggered_buffer_postenable(indio_dev);
+	if (err < 0)
+		goto st_gyro_buffer_postenable_error;
+
+	return err;
+
+st_gyro_buffer_postenable_error:
+	kfree(gdata->buffer_data);
+allocate_memory_error:
+	return err;
+}
+
+static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+	if (err < 0)
+		goto st_gyro_buffer_predisable_error;
+
+	err = st_gyro_set_axis_enable(indio_dev,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+	if (err < 0)
+		goto st_gyro_buffer_predisable_error;
+
+	err = st_gyro_set_enable(indio_dev, false);
+
+st_gyro_buffer_predisable_error:
+	kfree(gdata->buffer_data);
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
+	.preenable = &st_gyro_buffer_preenable,
+	.postenable = &st_gyro_buffer_postenable,
+	.predisable = &st_gyro_buffer_predisable,
+};
+
+int st_gyro_allocate_ring(struct iio_dev *indio_dev)
+{
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		&st_sensors_trigger_handler, &st_gyro_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_gyro_allocate_ring);
+
+void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_gyro_deallocate_ring);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
new file mode 100644
index 0000000..28363d4
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -0,0 +1,448 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_GYRO_DEFAULT_OUT_X_L_ADDR		0x28
+#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR		0x2a
+#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR		0x2c
+
+/* FULLSCALE */
+#define ST_GYRO_FS_AVL_250DPS			250
+#define ST_GYRO_FS_AVL_500DPS			500
+#define ST_GYRO_FS_AVL_2000DPS			2000
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_GYRO_1_WAI_EXP			0xd3
+#define ST_GYRO_1_ODR_ADDR			0x20
+#define ST_GYRO_1_ODR_MASK			0xc0
+#define ST_GYRO_1_ODR_AVL_100HZ_VAL		0x00
+#define ST_GYRO_1_ODR_AVL_200HZ_VAL		0x01
+#define ST_GYRO_1_ODR_AVL_400HZ_VAL		0x02
+#define ST_GYRO_1_ODR_AVL_800HZ_VAL		0x03
+#define ST_GYRO_1_PW_ADDR			0x20
+#define ST_GYRO_1_PW_MASK			0x08
+#define ST_GYRO_1_FS_ADDR			0x23
+#define ST_GYRO_1_FS_MASK			0x30
+#define ST_GYRO_1_FS_AVL_250_VAL		0x00
+#define ST_GYRO_1_FS_AVL_500_VAL		0x01
+#define ST_GYRO_1_FS_AVL_2000_VAL		0x02
+#define ST_GYRO_1_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_1_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_1_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_1_BDU_ADDR			0x23
+#define ST_GYRO_1_BDU_MASK			0x80
+#define ST_GYRO_1_DRDY_IRQ_ADDR			0x22
+#define ST_GYRO_1_DRDY_IRQ_MASK			0x08
+#define ST_GYRO_1_MULTIREAD_BIT			true
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_GYRO_2_WAI_EXP			0xd4
+#define ST_GYRO_2_ODR_ADDR			0x20
+#define ST_GYRO_2_ODR_MASK			0xc0
+#define ST_GYRO_2_ODR_AVL_95HZ_VAL		0x00
+#define ST_GYRO_2_ODR_AVL_190HZ_VAL		0x01
+#define ST_GYRO_2_ODR_AVL_380HZ_VAL		0x02
+#define ST_GYRO_2_ODR_AVL_760HZ_VAL		0x03
+#define ST_GYRO_2_PW_ADDR			0x20
+#define ST_GYRO_2_PW_MASK			0x08
+#define ST_GYRO_2_FS_ADDR			0x23
+#define ST_GYRO_2_FS_MASK			0x30
+#define ST_GYRO_2_FS_AVL_250_VAL		0x00
+#define ST_GYRO_2_FS_AVL_500_VAL		0x01
+#define ST_GYRO_2_FS_AVL_2000_VAL		0x02
+#define ST_GYRO_2_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_2_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_2_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_2_BDU_ADDR			0x23
+#define ST_GYRO_2_BDU_MASK			0x80
+#define ST_GYRO_2_DRDY_IRQ_ADDR			0x22
+#define ST_GYRO_2_DRDY_IRQ_MASK			0x08
+#define ST_GYRO_2_MULTIREAD_BIT			true
+
+static const struct iio_chan_spec st_gyro_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X,
+		IIO_MOD_X, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
+						ST_GYRO_DEFAULT_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y,
+		IIO_MOD_Y, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
+						ST_GYRO_DEFAULT_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z,
+		IIO_MOD_Z, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
+						ST_GYRO_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_sensors st_gyro_sensors[] = {
+	{
+		.wai = ST_GYRO_1_WAI_EXP,
+		.sensors_supported = {
+			[0] = L3G4200D_GYRO_DEV_NAME,
+			[1] = LSM330DL_GYRO_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
+		.odr = {
+			.addr = ST_GYRO_1_ODR_ADDR,
+			.mask = ST_GYRO_1_ODR_MASK,
+			.odr_avl = {
+				{ 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
+				{ 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
+				{ 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
+				{ 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_GYRO_1_PW_ADDR,
+			.mask = ST_GYRO_1_PW_MASK,
+			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_GYRO_1_FS_ADDR,
+			.mask = ST_GYRO_1_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_GYRO_FS_AVL_250DPS,
+					.value = ST_GYRO_1_FS_AVL_250_VAL,
+					.gain = ST_GYRO_1_FS_AVL_250_GAIN,
+				},
+				[1] = {
+					.num = ST_GYRO_FS_AVL_500DPS,
+					.value = ST_GYRO_1_FS_AVL_500_VAL,
+					.gain = ST_GYRO_1_FS_AVL_500_GAIN,
+				},
+				[2] = {
+					.num = ST_GYRO_FS_AVL_2000DPS,
+					.value = ST_GYRO_1_FS_AVL_2000_VAL,
+					.gain = ST_GYRO_1_FS_AVL_2000_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_GYRO_1_BDU_ADDR,
+			.mask = ST_GYRO_1_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
+			.mask = ST_GYRO_1_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_GYRO_2_WAI_EXP,
+		.sensors_supported = {
+			[0] = L3GD20_GYRO_DEV_NAME,
+			[1] = L3GD20H_GYRO_DEV_NAME,
+			[2] = LSM330DLC_GYRO_DEV_NAME,
+			[3] = L3G4IS_GYRO_DEV_NAME,
+			[4] = LSM330_GYRO_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
+		.odr = {
+			.addr = ST_GYRO_2_ODR_ADDR,
+			.mask = ST_GYRO_2_ODR_MASK,
+			.odr_avl = {
+				{ 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
+				{ 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
+				{ 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
+				{ 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_GYRO_2_PW_ADDR,
+			.mask = ST_GYRO_2_PW_MASK,
+			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+		},
+		.enable_axis = {
+			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+			.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+		},
+		.fs = {
+			.addr = ST_GYRO_2_FS_ADDR,
+			.mask = ST_GYRO_2_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_GYRO_FS_AVL_250DPS,
+					.value = ST_GYRO_2_FS_AVL_250_VAL,
+					.gain = ST_GYRO_2_FS_AVL_250_GAIN,
+				},
+				[1] = {
+					.num = ST_GYRO_FS_AVL_500DPS,
+					.value = ST_GYRO_2_FS_AVL_500_VAL,
+					.gain = ST_GYRO_2_FS_AVL_500_GAIN,
+				},
+				[2] = {
+					.num = ST_GYRO_FS_AVL_2000DPS,
+					.value = ST_GYRO_2_FS_AVL_2000_VAL,
+					.gain = ST_GYRO_2_FS_AVL_2000_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_GYRO_2_BDU_ADDR,
+			.mask = ST_GYRO_2_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
+			.mask = ST_GYRO_2_DRDY_IRQ_MASK,
+		},
+		.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+};
+
+static int st_gyro_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		err = st_sensors_read_info_raw(indio_dev, ch, val,
+						&st_gyro_sensors[gdata->index]);
+		if (err < 0)
+			goto read_error;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = gdata->current_fullscale->gain;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	return err;
+}
+
+static int st_gyro_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	int err;
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_sensors_set_fullscale_by_gain(indio_dev,
+					&st_gyro_sensors[gdata->index], val2);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+int st_gyro_set_dataready_irq(struct iio_dev *indio_dev, bool state)
+{
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return st_sensors_set_dataready_irq(indio_dev,
+					&st_gyro_sensors[gdata->index], state);
+}
+EXPORT_SYMBOL(st_gyro_set_dataready_irq);
+
+int st_gyro_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit)
+{
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return st_sensors_set_axis_enable(indio_dev,
+				&st_gyro_sensors[gdata->index], active_bit);
+}
+EXPORT_SYMBOL(st_gyro_set_axis_enable);
+
+int st_gyro_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return st_sensors_set_enable(indio_dev,
+				&st_gyro_sensors[gdata->index], enable);
+}
+EXPORT_SYMBOL(st_gyro_set_enable);
+
+static ssize_t st_gyro_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned int odr;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	err = kstrtoint(buf, 10, &odr);
+	if (err < 0)
+		goto conversion_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_sensors_set_odr(indio_dev,
+					&st_gyro_sensors[gdata->index], odr);
+	mutex_unlock(&indio_dev->mlock);
+
+conversion_error:
+	return err < 0 ? err : size;
+}
+
+static ssize_t st_gyro_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", gdata->odr);
+}
+
+static ssize_t st_gyro_sysfs_scale_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return st_sensors_get_scale_avl(indio_dev,
+				st_gyro_sensors[gdata->index].fs.fs_avl, buf);
+}
+
+static ssize_t st_gyro_sysfs_sampling_frequency_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	return st_sensors_get_sampling_frequency_avl(indio_dev,
+			st_gyro_sensors[gdata->index].odr.odr_avl, buf);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_gyro_sysfs_sampling_frequency_avail);
+
+static IIO_DEVICE_ATTR(in_anglvel_scale_available, S_IRUGO,
+					st_gyro_sysfs_scale_avail, NULL , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			st_gyro_sysfs_get_sampling_frequency,
+					st_gyro_sysfs_set_sampling_frequency);
+
+static struct attribute *st_gyro_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_gyro_attribute_group = {
+	.attrs = st_gyro_attributes,
+};
+
+static const struct iio_info gyro_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &st_gyro_attribute_group,
+	.read_raw = &st_gyro_read_raw,
+	.write_raw = &st_gyro_write_raw,
+};
+
+static struct iio_trigger_ops st_gyro_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &st_gyro_trig_set_state,
+};
+
+int st_gyro_common_probe(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &gyro_info;
+
+	err = st_sensors_check_device_support(indio_dev,
+				ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors);
+	if (err < 0)
+		goto st_gyro_common_probe_error;
+
+	gdata->multiread_bit = st_gyro_sensors[gdata->index].multi_read_bit;
+	indio_dev->channels = st_gyro_sensors[gdata->index].ch;
+	indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
+
+	gdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+				&st_gyro_sensors[gdata->index].fs.fs_avl[0];
+	gdata->odr = st_gyro_sensors[gdata->index].odr.odr_avl[0].hz;
+
+	err = st_sensors_init_sensor(indio_dev, &st_gyro_sensors[gdata->index]);
+	if (err < 0)
+		goto st_gyro_common_probe_error;
+
+	if (gdata->get_irq_data_ready(indio_dev) > 0) {
+		err = st_gyro_allocate_ring(indio_dev);
+		if (err < 0)
+			goto st_gyro_common_probe_error;
+
+		err = st_sensors_allocate_trigger(indio_dev,
+							&st_gyro_trigger_ops);
+		if (err < 0)
+			goto st_gyro_probe_trigger_error;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto st_gyro_device_register_error;
+
+	return err;
+
+st_gyro_device_register_error:
+	if (gdata->get_irq_data_ready(indio_dev) > 0)
+		st_sensors_deallocate_trigger(indio_dev);
+st_gyro_probe_trigger_error:
+	if (gdata->get_irq_data_ready(indio_dev) > 0)
+		st_gyro_deallocate_ring(indio_dev);
+st_gyro_common_probe_error:
+	return err;
+}
+EXPORT_SYMBOL(st_gyro_common_probe);
+
+void st_gyro_common_remove(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *gdata = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	if (gdata->get_irq_data_ready(indio_dev) > 0) {
+		st_sensors_deallocate_trigger(indio_dev);
+		st_gyro_deallocate_ring(indio_dev);
+	}
+	iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_gyro_common_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c
new file mode 100644
index 0000000..d71a41d
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -0,0 +1,84 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_gyro_i2c_probe(struct i2c_client *client,
+						const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *gdata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*gdata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	gdata = iio_priv(indio_dev);
+	gdata->dev = &client->dev;
+
+	st_sensors_i2c_configure(indio_dev, client, gdata);
+
+	err = st_gyro_common_probe(indio_dev);
+	if (err < 0)
+		goto st_gyro_common_probe_error;
+
+	return 0;
+
+st_gyro_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_gyro_i2c_remove(struct i2c_client *client)
+{
+	st_gyro_common_remove(i2c_get_clientdata(client));
+
+	return 0;
+}
+
+static const struct i2c_device_id st_gyro_id_table[] = {
+	{ L3G4200D_GYRO_DEV_NAME },
+	{ LSM330DL_GYRO_DEV_NAME },
+	{ L3GD20_GYRO_DEV_NAME },
+	{ L3GD20H_GYRO_DEV_NAME },
+	{ LSM330DLC_GYRO_DEV_NAME },
+	{ L3G4IS_GYRO_DEV_NAME },
+	{ LSM330_GYRO_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
+
+static struct i2c_driver st_gyro_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-gyro-i2c",
+	},
+	.probe = st_gyro_i2c_probe,
+	.remove = st_gyro_i2c_remove,
+	.id_table = st_gyro_id_table,
+};
+module_i2c_driver(st_gyro_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
new file mode 100644
index 0000000..cac4eef
--- /dev/null
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/gyro/st_gyro.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_gyro_spi_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *gdata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*gdata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	gdata = iio_priv(indio_dev);
+	gdata->dev = &spi->dev;
+
+	st_sensors_spi_configure(indio_dev, spi, gdata);
+
+	err = st_gyro_common_probe(indio_dev);
+	if (err < 0)
+		goto st_gyro_common_probe_error;
+
+	return 0;
+
+st_gyro_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_gyro_spi_remove(struct spi_device *spi)
+{
+	st_gyro_common_remove(spi_get_drvdata(spi));
+
+	return 0;
+}
+
+static const struct spi_device_id st_gyro_id_table[] = {
+	{ L3G4200D_GYRO_DEV_NAME },
+	{ LSM330DL_GYRO_DEV_NAME },
+	{ L3GD20_GYRO_DEV_NAME },
+	{ L3GD20H_GYRO_DEV_NAME },
+	{ LSM330DLC_GYRO_DEV_NAME },
+	{ L3G4IS_GYRO_DEV_NAME },
+	{ LSM330_GYRO_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
+
+static struct spi_driver st_gyro_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-gyro-spi",
+	},
+	.probe = st_gyro_spi_probe,
+	.remove = st_gyro_spi_remove,
+	.id_table = st_gyro_id_table,
+};
+module_spi_driver(st_gyro_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/gyro/st_gyro.h b/include/linux/iio/gyro/st_gyro.h
new file mode 100644
index 0000000..9bd7209
--- /dev/null
+++ b/include/linux/iio/gyro/st_gyro.h
@@ -0,0 +1,49 @@
+/*
+ * STMicroelectronics gyroscopes driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_GYRO_H
+#define ST_GYRO_H
+
+#include <linux/types.h>
+#include "../common/st_sensors.h"
+
+#define L3G4200D_GYRO_DEV_NAME		"l3g4200d"
+#define LSM330DL_GYRO_DEV_NAME		"lsm330dl_gyro"
+#define L3GD20_GYRO_DEV_NAME		"l3gd20"
+#define L3GD20H_GYRO_DEV_NAME		"l3gd20h"
+#define LSM330DLC_GYRO_DEV_NAME		"lsm330dlc_gyro"
+#define L3G4IS_GYRO_DEV_NAME		"l3g4is_ui"
+#define LSM330_GYRO_DEV_NAME		"lsm330_gyro"
+
+int st_gyro_common_probe(struct iio_dev *indio_dev);
+void st_gyro_common_remove(struct iio_dev *indio_dev);
+int st_gyro_set_dataready_irq(struct iio_dev *indio_dev, bool state);
+int st_gyro_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit);
+int st_gyro_set_enable(struct iio_dev *indio_dev, bool enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_gyro_allocate_ring(struct iio_dev *indio_dev);
+void st_gyro_deallocate_ring(struct iio_dev *indio_dev);
+int st_gyro_trig_set_state(struct iio_trigger *trig, bool state);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_gyro_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	return 0;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_GYRO_H */
-- 
1.8.0.3


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

* [PATCH 4/4] iio:magnetometer: Add STMicroelectronics magnetometers driver
  2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
                   ` (2 preceding siblings ...)
  2013-01-18 16:58 ` [PATCH 3/4] iio:gyro: Add STMicroelectronics gyroscopes driver Denis CIOCCA
@ 2013-01-18 16:58 ` Denis CIOCCA
  2013-01-20 12:54 ` STMicroelectronics drivers Jonathan Cameron
  4 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18 16:58 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch adds a generic magnetometer driver for STMicroelectronics
magnetometers, currently it supports:
LSM303DLHC, LSM303DLM, LIS3MDL.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/magnetometer/Kconfig          |  34 +++
 drivers/iio/magnetometer/Makefile         |   5 +
 drivers/iio/magnetometer/st_magn_buffer.c | 102 +++++++
 drivers/iio/magnetometer/st_magn_core.c   | 468 ++++++++++++++++++++++++++++++
 drivers/iio/magnetometer/st_magn_i2c.c    |  81 ++++++
 drivers/iio/magnetometer/st_magn_spi.c    |  80 +++++
 include/linux/iio/magnetometer/st_magn.h  |  47 +++
 7 files changed, 817 insertions(+)
 create mode 100644 drivers/iio/magnetometer/st_magn_buffer.c
 create mode 100644 drivers/iio/magnetometer/st_magn_core.c
 create mode 100644 drivers/iio/magnetometer/st_magn_i2c.c
 create mode 100644 drivers/iio/magnetometer/st_magn_spi.c
 create mode 100644 include/linux/iio/magnetometer/st_magn.h

diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ff11d68..89e2727 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -14,4 +14,38 @@ config HID_SENSOR_MAGNETOMETER_3D
 	  Say yes here to build support for the HID SENSOR
 	  Magnetometer 3D.
 
+config IIO_ST_MAGN_3AXIS
+	tristate "STMicroelectronics magnetometers 3-Axis Driver"
+	depends on (I2C || SPI_MASTER) && SYSFS
+	select IIO_ST_SENSORS_CORE
+	select IIO_ST_MAGN_I2C_3AXIS if (I2C)
+	select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER)
+	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+	select IIO_ST_MAGN_BUFFER if (IIO_TRIGGERED_BUFFER)
+	help
+	  Say yes here to build support for STMicroelectronics magnetometers:
+	  LSM303DLHC, LSM303DLM, LIS3MDL.
+
+	  This driver can also be built as a module. If so, will be created
+	  these modules:
+	  - st_magn_core (core functions for the driver [it is mandatory]);
+	  - st_magn_i2c (necessary for the I2C devices [optional*]);
+	  - st_magn_spi (necessary for the SPI devices [optional*]);
+	  - st_magn_buffer (necessary for triggered buffer [optional]);
+	  
+	  (*) one of these is necessary to do something.
+
+config IIO_ST_MAGN_I2C_3AXIS
+	tristate
+	depends on IIO_ST_MAGN_3AXIS
+
+config IIO_ST_MAGN_SPI_3AXIS
+	tristate
+	depends on IIO_ST_MAGN_3AXIS
+
+config IIO_ST_MAGN_BUFFER
+	tristate
+	depends on IIO_ST_MAGN_3AXIS
+	depends on (IIO_ST_MAGN_I2C_3AXIS || IIO_ST_MAGN_SPI_3AXIS)
+
 endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 60dc4f2..215b99e 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -3,3 +3,8 @@
 #
 
 obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
+
+obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn_core.o
+obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
+obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
+obj-$(CONFIG_IIO_ST_MAGN_BUFFER) += st_magn_buffer.o
diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c
new file mode 100644
index 0000000..c396988
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_buffer.c
@@ -0,0 +1,102 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/byteorder/generic.h>
+#include <linux/i2c.h>
+#include <linux/delay.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/magnetometer/st_magn.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
+{
+	int err;
+
+	err = st_magn_set_enable(indio_dev, true);
+	if (err < 0)
+		goto st_magn_set_enable_error;
+
+	err = iio_sw_buffer_preenable(indio_dev);
+
+st_magn_set_enable_error:
+	return err;
+}
+
+static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (mdata->buffer_data == NULL) {
+		err = -ENOMEM;
+		goto allocate_memory_error;
+	}
+
+	err = iio_triggered_buffer_postenable(indio_dev);
+	if (err < 0)
+		goto st_magn_buffer_postenable_error;
+
+	return err;
+
+st_magn_buffer_postenable_error:
+	kfree(mdata->buffer_data);
+allocate_memory_error:
+	return err;
+}
+
+static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+	if (err < 0)
+		goto st_magn_buffer_predisable_error;
+
+	err = st_magn_set_enable(indio_dev, false);
+
+st_magn_buffer_predisable_error:
+	kfree(mdata->buffer_data);
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
+	.preenable = &st_magn_buffer_preenable,
+	.postenable = &st_magn_buffer_postenable,
+	.predisable = &st_magn_buffer_predisable,
+};
+
+int st_magn_allocate_ring(struct iio_dev *indio_dev)
+{
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		&st_sensors_trigger_handler, &st_magn_buffer_setup_ops);
+}
+EXPORT_SYMBOL(st_magn_allocate_ring);
+
+void st_magn_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_magn_deallocate_ring);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
new file mode 100644
index 0000000..eef9a32
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -0,0 +1,468 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/delay.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/magnetometer/st_magn.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_MAGN_DEFAULT_OUT_X_L_ADDR		0X04
+#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR		0X08
+#define ST_MAGN_DEFAULT_OUT_Z_L_ADDR		0X06
+
+/* FULLSCALE */
+#define ST_MAGN_FS_AVL_1300MG			1300
+#define ST_MAGN_FS_AVL_1900MG			1900
+#define ST_MAGN_FS_AVL_2500MG			2500
+#define ST_MAGN_FS_AVL_4000MG			4000
+#define ST_MAGN_FS_AVL_4700MG			4700
+#define ST_MAGN_FS_AVL_5600MG			5600
+#define ST_MAGN_FS_AVL_8000MG			8000
+#define ST_MAGN_FS_AVL_8100MG			8100
+#define ST_MAGN_FS_AVL_10000MG			10000
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_MAGN_1_WAI_EXP			0x3c
+#define ST_MAGN_1_ODR_ADDR			0x00
+#define ST_MAGN_1_ODR_MASK			0x1c
+#define ST_MAGN_1_ODR_AVL_1HZ_VAL		0x00
+#define ST_MAGN_1_ODR_AVL_2HZ_VAL		0x01
+#define ST_MAGN_1_ODR_AVL_3HZ_VAL		0x02
+#define ST_MAGN_1_ODR_AVL_8HZ_VAL		0x03
+#define ST_MAGN_1_ODR_AVL_15HZ_VAL		0x04
+#define ST_MAGN_1_ODR_AVL_30HZ_VAL		0x05
+#define ST_MAGN_1_ODR_AVL_75HZ_VAL		0x06
+#define ST_MAGN_1_ODR_AVL_220HZ_VAL		0x07
+#define ST_MAGN_1_PW_ADDR			0x02
+#define ST_MAGN_1_PW_MASK			0x03
+#define ST_MAGN_1_PW_ON				0x00
+#define ST_MAGN_1_PW_OFF			0x03
+#define ST_MAGN_1_FS_ADDR			0x01
+#define ST_MAGN_1_FS_MASK			0xe0
+#define ST_MAGN_1_FS_AVL_1300_VAL		0x01
+#define ST_MAGN_1_FS_AVL_1900_VAL		0x02
+#define ST_MAGN_1_FS_AVL_2500_VAL		0x03
+#define ST_MAGN_1_FS_AVL_4000_VAL		0x04
+#define ST_MAGN_1_FS_AVL_4700_VAL		0x05
+#define ST_MAGN_1_FS_AVL_5600_VAL		0x06
+#define ST_MAGN_1_FS_AVL_8100_VAL		0x07
+#define ST_MAGN_1_FS_AVL_1300_GAIN_XY		1100
+#define ST_MAGN_1_FS_AVL_1900_GAIN_XY		855
+#define ST_MAGN_1_FS_AVL_2500_GAIN_XY		670
+#define ST_MAGN_1_FS_AVL_4000_GAIN_XY		450
+#define ST_MAGN_1_FS_AVL_4700_GAIN_XY		400
+#define ST_MAGN_1_FS_AVL_5600_GAIN_XY		330
+#define ST_MAGN_1_FS_AVL_8100_GAIN_XY		230
+#define ST_MAGN_1_FS_AVL_1300_GAIN_Z		980
+#define ST_MAGN_1_FS_AVL_1900_GAIN_Z		760
+#define ST_MAGN_1_FS_AVL_2500_GAIN_Z		600
+#define ST_MAGN_1_FS_AVL_4000_GAIN_Z		400
+#define ST_MAGN_1_FS_AVL_4700_GAIN_Z		355
+#define ST_MAGN_1_FS_AVL_5600_GAIN_Z		295
+#define ST_MAGN_1_FS_AVL_8100_GAIN_Z		205
+#define ST_MAGN_1_MULTIREAD_BIT			false
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_MAGN_2_WAI_EXP			0x3d
+#define ST_MAGN_2_ODR_ADDR			0x20
+#define ST_MAGN_2_ODR_MASK			0x1c
+#define ST_MAGN_2_ODR_AVL_1HZ_VAL		0x00
+#define ST_MAGN_2_ODR_AVL_2HZ_VAL		0x01
+#define ST_MAGN_2_ODR_AVL_3HZ_VAL		0x02
+#define ST_MAGN_2_ODR_AVL_5HZ_VAL		0x03
+#define ST_MAGN_2_ODR_AVL_10HZ_VAL		0x04
+#define ST_MAGN_2_ODR_AVL_20HZ_VAL		0x05
+#define ST_MAGN_2_ODR_AVL_40HZ_VAL		0x06
+#define ST_MAGN_2_ODR_AVL_80HZ_VAL		0x07
+#define ST_MAGN_2_PW_ADDR			0x22
+#define ST_MAGN_2_PW_MASK			0x03
+#define ST_MAGN_2_PW_ON				0x00
+#define ST_MAGN_2_PW_OFF			0x03
+#define ST_MAGN_2_FS_ADDR			0x21
+#define ST_MAGN_2_FS_MASK			0x60
+#define ST_MAGN_2_FS_AVL_4000_VAL		0x00
+#define ST_MAGN_2_FS_AVL_8000_VAL		0x01
+#define ST_MAGN_2_FS_AVL_10000_VAL		0x02
+#define ST_MAGN_2_FS_AVL_4000_GAIN		430
+#define ST_MAGN_2_FS_AVL_8000_GAIN		230
+#define ST_MAGN_2_FS_AVL_10000_GAIN		230
+#define ST_MAGN_2_MULTIREAD_BIT			false
+#define ST_MAGN_2_OUT_X_L_ADDR			0x28
+#define ST_MAGN_2_OUT_Y_L_ADDR			0x2a
+#define ST_MAGN_2_OUT_Z_L_ADDR			0x2c
+
+static const struct iio_chan_spec st_magn_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
+		ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_sensors st_magn_sensors[] = {
+	{
+		.wai = ST_MAGN_1_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_1_ODR_ADDR,
+			.mask = ST_MAGN_1_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, },
+				{ 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, },
+				{ 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, },
+				{ 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, },
+				{ 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, },
+				{ 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, },
+				{ 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, },
+				{ 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_1_PW_ADDR,
+			.mask = ST_MAGN_1_PW_MASK,
+			.value_on = ST_MAGN_1_PW_ON,
+			.value_off = ST_MAGN_1_PW_OFF,
+		},
+		.fs = {
+			.addr = ST_MAGN_1_FS_ADDR,
+			.mask = ST_MAGN_1_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_1300MG,
+					.value = ST_MAGN_1_FS_AVL_1300_VAL,
+					.gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_1900MG,
+					.value = ST_MAGN_1_FS_AVL_1900_VAL,
+					.gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_2500MG,
+					.value = ST_MAGN_1_FS_AVL_2500_VAL,
+					.gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = ST_MAGN_1_FS_AVL_4000_VAL,
+					.gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z,
+				},
+				[4] = {
+					.num = ST_MAGN_FS_AVL_4700MG,
+					.value = ST_MAGN_1_FS_AVL_4700_VAL,
+					.gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z,
+				},
+				[5] = {
+					.num = ST_MAGN_FS_AVL_5600MG,
+					.value = ST_MAGN_1_FS_AVL_5600_VAL,
+					.gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z,
+				},
+				[6] = {
+					.num = ST_MAGN_FS_AVL_8100MG,
+					.value = ST_MAGN_1_FS_AVL_8100_VAL,
+					.gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z,
+				},
+			},
+		},
+		.multi_read_bit = ST_MAGN_1_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_MAGN_2_WAI_EXP,
+		.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_2_ODR_ADDR,
+			.mask = ST_MAGN_2_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, },
+				{ 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, },
+				{ 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, },
+				{ 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, },
+				{ 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, },
+				{ 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, },
+				{ 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, },
+				{ 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_2_PW_ADDR,
+			.mask = ST_MAGN_2_PW_MASK,
+			.value_on = ST_MAGN_2_PW_ON,
+			.value_off = ST_MAGN_2_PW_OFF,
+		},
+		.fs = {
+			.addr = ST_MAGN_2_FS_ADDR,
+			.mask = ST_MAGN_2_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = ST_MAGN_2_FS_AVL_4000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_4000_GAIN,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_8000MG,
+					.value = ST_MAGN_2_FS_AVL_8000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_8000_GAIN,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_10000MG,
+					.value = ST_MAGN_2_FS_AVL_10000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_10000_GAIN,
+				},
+			},
+		},
+		.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+};
+
+static int st_magn_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		err = st_sensors_read_info_raw(indio_dev, ch, val,
+						&st_magn_sensors[mdata->index]);
+		if (err < 0)
+			goto read_error;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		if (ch->scan_index != ST_SENSORS_SCAN_Z)
+			*val2 = mdata->current_fullscale->gain;
+		else
+			*val2 = mdata->current_fullscale->gain2;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	return err;
+}
+
+static int st_magn_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_sensors_set_fullscale_by_gain(indio_dev,
+					&st_magn_sensors[mdata->index], val2);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+int st_magn_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit)
+{
+	return 0;
+}
+EXPORT_SYMBOL(st_magn_set_axis_enable);
+
+int st_magn_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	return st_sensors_set_enable(indio_dev,
+				&st_magn_sensors[mdata->index], enable);
+}
+EXPORT_SYMBOL(st_magn_set_enable);
+
+static ssize_t st_magn_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned int odr;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	err = kstrtoint(buf, 10, &odr);
+	if (err < 0)
+		goto conversion_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_sensors_set_odr(indio_dev,
+					&st_magn_sensors[mdata->index], odr);
+	mutex_unlock(&indio_dev->mlock);
+
+conversion_error:
+	return err < 0 ? err : size;
+}
+
+static ssize_t st_magn_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d\n", mdata->odr);
+}
+
+static ssize_t st_magn_sysfs_scale_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	return st_sensors_get_scale_avl(indio_dev,
+				st_magn_sensors[mdata->index].fs.fs_avl, buf);
+}
+
+static ssize_t st_magn_sysfs_sampling_frequency_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	return st_sensors_get_sampling_frequency_avl(indio_dev,
+				st_magn_sensors[mdata->index].odr.odr_avl, buf);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_magn_sysfs_sampling_frequency_available);
+
+static IIO_DEVICE_ATTR(in_magn_scale_available, S_IRUGO,
+				st_magn_sysfs_scale_available, NULL , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			st_magn_sysfs_get_sampling_frequency,
+					st_magn_sysfs_set_sampling_frequency);
+
+static struct attribute *st_magn_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_magn_scale_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_magn_attribute_group = {
+	.attrs = st_magn_attributes,
+};
+
+static const struct iio_info magn_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &st_magn_attribute_group,
+	.read_raw = &st_magn_read_raw,
+	.write_raw = &st_magn_write_raw,
+};
+
+int st_magn_common_probe(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &magn_info;
+
+	err = st_sensors_check_device_support(indio_dev,
+				ARRAY_SIZE(st_magn_sensors), st_magn_sensors);
+	if (err < 0)
+		goto st_magn_common_probe_error;
+
+	mdata->multiread_bit = st_magn_sensors[mdata->index].multi_read_bit;
+	indio_dev->channels = st_magn_sensors[mdata->index].ch;
+	indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
+
+	mdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+				&st_magn_sensors[mdata->index].fs.fs_avl[0];
+	mdata->odr = st_magn_sensors[mdata->index].odr.odr_avl[0].hz;
+
+	err = st_sensors_init_sensor(indio_dev,
+					&st_magn_sensors[mdata->index]);
+	if (err < 0)
+		goto st_magn_common_probe_error;
+
+	if (mdata->get_irq_data_ready(indio_dev) > 0) {
+		err = st_magn_allocate_ring(indio_dev);
+		if (err < 0)
+			goto st_magn_common_probe_error;
+		err = st_sensors_allocate_trigger(indio_dev, NULL);
+		if (err < 0)
+			goto st_magn_probe_trigger_error;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto st_magn_device_register_error;
+
+	return err;
+
+st_magn_device_register_error:
+	if (mdata->get_irq_data_ready(indio_dev) > 0)
+		st_sensors_deallocate_trigger(indio_dev);
+st_magn_probe_trigger_error:
+	if (mdata->get_irq_data_ready(indio_dev) > 0)
+		st_magn_deallocate_ring(indio_dev);
+st_magn_common_probe_error:
+	return err;
+}
+EXPORT_SYMBOL(st_magn_common_probe);
+
+void st_magn_common_remove(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	if (mdata->get_irq_data_ready(indio_dev) > 0) {
+		st_sensors_deallocate_trigger(indio_dev);
+		st_magn_deallocate_ring(indio_dev);
+	}
+	iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_magn_common_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
new file mode 100644
index 0000000..01e33d7
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/magnetometer/st_magn.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_magn_i2c_probe(struct i2c_client *client,
+						const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *mdata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*mdata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	mdata = iio_priv(indio_dev);
+	mdata->dev = &client->dev;
+
+	st_sensors_i2c_configure(indio_dev, client, mdata);
+
+	err = st_magn_common_probe(indio_dev);
+	if (err < 0)
+		goto st_magn_common_probe_error;
+
+	return 0;
+
+st_magn_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_magn_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	st_magn_common_remove(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id st_magn_id_table[] = {
+	{ LSM303DLHC_MAGN_DEV_NAME },
+	{ LSM303DLM_MAGN_DEV_NAME },
+	{ LIS3MDL_MAGN_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
+
+static struct i2c_driver st_magn_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-magn-i2c",
+	},
+	.probe = st_magn_i2c_probe,
+	.remove = st_magn_i2c_remove,
+	.id_table = st_magn_id_table,
+};
+module_i2c_driver(st_magn_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
new file mode 100644
index 0000000..e9c0601
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -0,0 +1,80 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include <linux/iio/magnetometer/st_magn.h>
+#include <linux/iio/common/st_sensors.h>
+
+
+static int st_magn_spi_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *mdata;
+	int err;
+
+	indio_dev = iio_device_alloc(sizeof(*mdata));
+	if (indio_dev == NULL) {
+		err = -ENOMEM;
+		goto iio_device_alloc_error;
+	}
+
+	mdata = iio_priv(indio_dev);
+	mdata->dev = &spi->dev;
+
+	st_sensors_spi_configure(indio_dev, spi, mdata);
+
+	err = st_magn_common_probe(indio_dev);
+	if (err < 0)
+		goto st_magn_common_probe_error;
+
+	return 0;
+
+st_magn_common_probe_error:
+	iio_device_free(indio_dev);
+iio_device_alloc_error:
+	return err;
+}
+
+static int st_magn_spi_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	st_magn_common_remove(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id st_magn_id_table[] = {
+	{ LSM303DLHC_MAGN_DEV_NAME },
+	{ LSM303DLM_MAGN_DEV_NAME },
+	{ LIS3MDL_MAGN_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_magn_id_table);
+
+static struct spi_driver st_magn_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "st-magn-spi",
+	},
+	.probe = st_magn_spi_probe,
+	.remove = st_magn_spi_remove,
+	.id_table = st_magn_id_table,
+};
+module_spi_driver(st_magn_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/magnetometer/st_magn.h b/include/linux/iio/magnetometer/st_magn.h
new file mode 100644
index 0000000..cde476e
--- /dev/null
+++ b/include/linux/iio/magnetometer/st_magn.h
@@ -0,0 +1,47 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_MAGN_H
+#define ST_MAGN_H
+
+#include <linux/types.h>
+#include "../common/st_sensors.h"
+
+#define LSM303DLHC_MAGN_DEV_NAME	"lsm303dlhc_magn"
+#define LSM303DLM_MAGN_DEV_NAME		"lsm303dlm_magn"
+#define LIS3MDL_MAGN_DEV_NAME		"lis3mdl"
+
+int st_magn_common_probe(struct iio_dev *indio_dev);
+void st_magn_common_remove(struct iio_dev *indio_dev);
+int st_magn_set_axis_enable(struct iio_dev *indio_dev, u8 active_bit);
+int st_magn_set_enable(struct iio_dev *indio_dev, bool enable);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_magn_allocate_ring(struct iio_dev *indio_dev);
+void st_magn_deallocate_ring(struct iio_dev *indio_dev);
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return 0;
+}
+static inline void st_magn_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return;
+}
+static inline int st_magn_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_MAGN_H */
-- 
1.8.0.3


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

* Re: STMicroelectronics drivers
  2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
                   ` (3 preceding siblings ...)
  2013-01-18 16:58 ` [PATCH 4/4] iio:magnetometer: Add STMicroelectronics magnetometers driver Denis CIOCCA
@ 2013-01-20 12:54 ` Jonathan Cameron
  2013-01-20 12:56   ` [PATCH 1/2] Make the st_sensors core library stuff build in all combinations Jonathan Cameron
  4 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2013-01-20 12:54 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: lars, linux-iio

On 01/18/2013 04:58 PM, Denis CIOCCA wrote:
> Hi Jonathan, Lars,
> 
> I have modified some part of code and added it to common library, the remainder I think can not be moved.
> I have also modified the kconfig and makefile.
> 
> Thanks!
> 
> Denis
> 
Denis this still doesn't work.

I'll send you a patch to make it actually do so.

With what you sent out in this series, simply building the lot
as modules fails.

Anyhow, patches to follow.  They are not clean but the do
work, please merge them into yours or argue why the approach
used does not work...

Jonathan

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

* [PATCH 1/2] Make the st_sensors core library stuff build in all combinations.
  2013-01-20 12:54 ` STMicroelectronics drivers Jonathan Cameron
@ 2013-01-20 12:56   ` Jonathan Cameron
  2013-01-20 12:56     ` [PATCH 2/2] Rework the Makefile / Kconfig for the st accel driver so that all options work Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2013-01-20 12:56 UTC (permalink / raw)
  To: linux-iio; +Cc: lars, denis.ciocca, Jonathan Cameron

---
 drivers/iio/common/st_sensors/Kconfig          | 11 ++++++++++-
 drivers/iio/common/st_sensors/Makefile         | 24 ++++++------------------
 drivers/iio/common/st_sensors/st_sensors_i2c.c |  3 ++-
 drivers/iio/common/st_sensors/st_sensors_spi.c |  4 ++--
 include/linux/iio/common/st_i2c.h              |  7 +++++++
 include/linux/iio/common/st_sensors.h          | 14 --------------
 include/linux/iio/common/st_spi.h              |  9 +++++++++
 7 files changed, 36 insertions(+), 36 deletions(-)
 create mode 100644 include/linux/iio/common/st_i2c.h
 create mode 100644 include/linux/iio/common/st_spi.h

diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
index 0749f5c..58e06eb 100644
--- a/drivers/iio/common/st_sensors/Kconfig
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -2,5 +2,14 @@
 # Hid Sensor common modules
 #
 
+config IIO_ST_SENSORS_I2C
+       tristate
+
+config IIO_ST_SENSORS_SPI
+       tristate
+
 config IIO_ST_SENSORS_CORE
-	bool
+       tristate
+       select IIO_ST_SENSORS_I2C if I2C
+       select IIO_ST_SENSORS_SPI if SPI_MASTER
+
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
index 8c9a57a..2075ad4 100644
--- a/drivers/iio/common/st_sensors/Makefile
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -1,21 +1,9 @@
 #
 # Makefile for the STMicroelectronics sensor common modules.
 #
-
-ifneq ($(CONFIG_I2C),)
-st_accel_i2c			= st_sensors_i2c.o
-endif
-
-ifneq ($(CONFIG_SPI_MASTER),)
-st_accel_spi			= st_sensors_spi.o
-endif
-
-ifneq ($(CONFIG_IIO_BUFFER),)
-st_sensors_triggered_buffer	= st_sensors_trigger.o st_sensors_buffer.o
-endif
-
-obj-$(CONFIG_IIO_ST_SENSORS_CORE) += 	st_sensors_library.o
-st_sensors_library-y := st_sensors_core.o \
-			$(st_accel_i2c) \
-			$(st_accel_spi) \
-			$(st_sensors_triggered_buffer)
+obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
+st_sensors-y := st_sensors_core.o
+st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
+st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
index b2d2fdf..43169ce 100644
--- a/drivers/iio/common/st_sensors/st_sensors_i2c.c
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -13,12 +13,13 @@
 #include <linux/slab.h>
 #include <linux/iio/iio.h>
 
+#include <linux/iio/common/st_i2c.h>
 #include <linux/iio/common/st_sensors.h>
 
 
 #define ST_SENSORS_I2C_MULTIREAD	0x80
 
-unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
 {
 	struct st_sensor_data *sdata = iio_priv(indio_dev);
 
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
index 5030766..f89726f 100644
--- a/drivers/iio/common/st_sensors/st_sensors_spi.c
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -12,14 +12,14 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/iio/iio.h>
-
+#include <linux/iio/common/st_spi.h>
 #include <linux/iio/common/st_sensors.h>
 
 
 #define ST_SENSORS_SPI_MULTIREAD	0xc0
 #define ST_SENSORS_SPI_READ		0x80
 
-unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
 {
 	struct st_sensor_data *sdata = iio_priv(indio_dev);
 
diff --git a/include/linux/iio/common/st_i2c.h b/include/linux/iio/common/st_i2c.h
new file mode 100644
index 0000000..c4ff039
--- /dev/null
+++ b/include/linux/iio/common/st_i2c.h
@@ -0,0 +1,7 @@
+
+struct iio_dev;
+struct i2c_client;
+struct st_sensor_data;
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
index a8a0c40..f27b1e0 100644
--- a/include/linux/iio/common/st_sensors.h
+++ b/include/linux/iio/common/st_sensors.h
@@ -215,20 +215,6 @@ struct st_sensors {
 	unsigned int bootime;
 };
 
-#ifdef CONFIG_SPI_MASTER
-void st_sensors_spi_configure(struct iio_dev *indio_dev,
-			struct spi_device *spi, struct st_sensor_data *sdata);
-
-unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev);
-#endif
-
-#ifdef CONFIG_I2C
-void st_sensors_i2c_configure(struct iio_dev *indio_dev,
-		struct i2c_client *client, struct st_sensor_data *sdata);
-
-unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev);
-#endif
-
 #ifdef CONFIG_IIO_BUFFER
 int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
 					struct iio_trigger_ops *trigger_ops);
diff --git a/include/linux/iio/common/st_spi.h b/include/linux/iio/common/st_spi.h
new file mode 100644
index 0000000..d1dddc6
--- /dev/null
+++ b/include/linux/iio/common/st_spi.h
@@ -0,0 +1,9 @@
+
+
+struct iio_dev;
+struct st_sensor_data;
+struct spi_device;
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
-- 
1.8.1


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

* [PATCH 2/2] Rework the Makefile / Kconfig for the st accel driver so that all options work.
  2013-01-20 12:56   ` [PATCH 1/2] Make the st_sensors core library stuff build in all combinations Jonathan Cameron
@ 2013-01-20 12:56     ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2013-01-20 12:56 UTC (permalink / raw)
  To: linux-iio; +Cc: lars, denis.ciocca, Jonathan Cameron

---
 drivers/iio/accel/Kconfig        | 42 ++--------------------------------------
 drivers/iio/accel/Makefile       |  7 +++++--
 drivers/iio/accel/st_accel_i2c.c |  2 +-
 drivers/iio/accel/st_accel_spi.c |  2 +-
 4 files changed, 9 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index dbc1af0..c396ae1 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -40,55 +40,17 @@ config IIO_ST_ACCEL_3AXIS
 	  - st_accel_i2c (necessary for the I2C devices [optional*]);
 	  - st_accel_spi (necessary for the SPI devices [optional*]);
 	  - st_accel_buffer (necessary for triggered buffer [optional]);
-	  
-	  (*) one of these is necessary to do something.
-
-config IIO_ST_ACCEL_I2C_3AXIS
-	tristate
-	depends on IIO_ST_ACCEL_3AXIS
-
-config IIO_ST_ACCEL_SPI_3AXIS
-	tristate
-	depends on IIO_ST_ACCEL_3AXIS
-
-config IIO_ST_ACCEL_BUFFER
-	tristate
-	depends on IIO_ST_ACCEL_3AXIS
-	depends on (IIO_ST_ACCEL_I2C_3AXIS || IIO_ST_ACCEL_SPI_3AXIS)
-
-config IIO_ST_ACCEL_3AXIS
-	tristate "STMicroelectronics accelerometers 3-Axis Driver"
-	depends on (I2C || SPI_MASTER) && SYSFS
-	select IIO_ST_SENSORS_CORE
-	select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
-	select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
-	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
-	select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER)
-	help
-	  Say yes here to build support for STMicroelectronics accelerometers:
-	  LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
-	  LIS331DLH, LSM303DL, LSM303DLM, LSM330.
 
-	  This driver can also be built as a module. If so, will be created
-	  these modules:
-	  - st_accel_core (core functions for the driver [it is mandatory]);
-	  - st_accel_i2c (necessary for the I2C devices [optional*]);
-	  - st_accel_spi (necessary for the SPI devices [optional*]);
-	  - st_accel_buffer (necessary for triggered buffer [optional]);
-	  
 	  (*) one of these is necessary to do something.
 
 config IIO_ST_ACCEL_I2C_3AXIS
 	tristate
 	depends on IIO_ST_ACCEL_3AXIS
+	depends on IIO_ST_SENSORS_I2C
 
 config IIO_ST_ACCEL_SPI_3AXIS
 	tristate
 	depends on IIO_ST_ACCEL_3AXIS
-
-config IIO_ST_ACCEL_BUFFER
-	tristate
-	depends on IIO_ST_ACCEL_3AXIS
-	depends on (IIO_ST_ACCEL_I2C_3AXIS || IIO_ST_ACCEL_SPI_3AXIS)
+	depends on IIO_ST_SENSORS_SPI
 
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 820c17a..8d7a1c6 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -5,7 +5,10 @@
 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
 obj-$(CONFIG_KXSD9)	+= kxsd9.o
 
-obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel_core.o
+obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
+st_accel-y := st_accel_core.o
+st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o
+
 obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
 obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
-obj-$(CONFIG_IIO_ST_ACCEL_BUFFER) += st_accel_buffer.o
+
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index e782717..066a9fb 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -17,7 +17,7 @@
 
 #include <linux/iio/accel/st_accel.h>
 #include <linux/iio/common/st_sensors.h>
-
+#include <linux/iio/common/st_i2c.h>
 
 static int st_accel_i2c_probe(struct i2c_client *client,
 						const struct i2c_device_id *id)
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index 92e78ba..67532eb 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -17,7 +17,7 @@
 
 #include <linux/iio/accel/st_accel.h>
 #include <linux/iio/common/st_sensors.h>
-
+#include <linux/iio/common/st_spi.h>
 
 static int st_accel_spi_probe(struct spi_device *spi)
 {
-- 
1.8.1


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

* Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-25 23:44 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis Ciocca
@ 2013-01-29 21:45   ` Lars-Peter Clausen
  0 siblings, 0 replies; 19+ messages in thread
From: Lars-Peter Clausen @ 2013-01-29 21:45 UTC (permalink / raw)
  To: Denis Ciocca; +Cc: jic23, linux-iio, Denis Ciocca

Hi,

here is one of the rough edges I was talking about earlier.

On 01/26/2013 12:44 AM, Denis Ciocca wrote:

> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev, char *buf)
> +{
> +	int i, len = 0;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if (sdata->sensor->odr.odr_avl[i].hz == 0)
> +			break;
> +
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> +					sdata->sensor->odr.odr_avl[i].hz);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);

This function is no longer used outside the st_sensors core module, so there is
no need to export it anymore. In fact it is only used in
st_sensors_sysfs_sampling_frequency_avail, which itself is not more than the
call to st_sensors_get_sampling_frequency_avl, so I'd just move the contents of
st_sensors_get_sampling_frequency_avl to st_sensors_sysfs_sampling_frequency_avail.

> +
> +int st_sensors_get_scale_avl(struct iio_dev *indio_dev, char *buf)
> +{
> +	int i, len = 0;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if (sdata->sensor->fs.fs_avl[i].num == 0)
> +			break;
> +
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
> +					sdata->sensor->fs.fs_avl[i].gain);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_scale_avl);

Similar here.


> +
[...]


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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-25 23:44 STMicroelectronics IIO driver Denis Ciocca
@ 2013-01-25 23:44 ` Denis Ciocca
  2013-01-29 21:45   ` Lars-Peter Clausen
  0 siblings, 1 reply; 19+ messages in thread
From: Denis Ciocca @ 2013-01-25 23:44 UTC (permalink / raw)
  To: denis.ciocca, jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch add a generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/st_sensors/Kconfig              |   14 +
 drivers/iio/common/st_sensors/Makefile             |   10 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  |  116 +++++
 drivers/iio/common/st_sensors/st_sensors_core.c    |  460 ++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |   81 ++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     |  128 ++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |   77 ++++
 include/linux/iio/common/st_sensors.h              |  274 ++++++++++++
 include/linux/iio/common/st_sensors_i2c.h          |   20 +
 include/linux/iio/common/st_sensors_spi.h          |   20 +
 12 files changed, 1202 insertions(+)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h
 create mode 100644 include/linux/iio/common/st_sensors_i2c.h
 create mode 100644 include/linux/iio/common/st_sensors_spi.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..84b2dca
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,14 @@
+#
+# Hid Sensor common modules
+#
+
+config IIO_ST_SENSORS_I2C
+	tristate
+
+config IIO_ST_SENSORS_SPI
+	tristate
+
+config IIO_ST_SENSORS_CORE
+	tristate
+	select IIO_ST_SENSORS_I2C if I2C
+	select IIO_ST_SENSORS_SPI if SPI_MASTER
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..9f3e24f
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
+st_sensors-y := st_sensors_core.o
+st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
+st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..09b236d
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,116 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..fba6d68
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,460 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+
+static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev, char *buf)
+{
+	int i, len = 0;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (sdata->sensor->odr.odr_avl[i].hz == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+					sdata->sensor->odr.odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev, char *buf)
+{
+	int i, len = 0;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (sdata->sensor->fs.fs_avl[i].num == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+					sdata->sensor->fs.fs_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+static int st_sensors_match_odr(struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (sensor->odr.odr_avl[i].hz == 0)
+			goto st_sensors_match_odr_error;
+
+		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;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
+{
+	int err;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_odr(sdata->sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
+			(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sdata->sensor->odr.addr,
+				sdata->sensor->odr.mask,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sdata->sensor->odr.addr, sdata->sensor->odr.mask,
+			odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (sensor->fs.fs_avl[i].num == 0)
+			goto st_sensors_match_odr_error;
+
+		if (sensor->fs.fs_avl[i].num == fs) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sdata->sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+				sdata->sensor->fs.addr,
+				sdata->sensor->fs.mask,
+				sdata->sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+						&sdata->sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+
+int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	bool found;
+	u8 tmp_value;
+	int err = -EINVAL;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sdata->sensor->pw.value_on;
+		if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
+			(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
+			err = st_sensors_match_odr(sdata->sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sdata->sensor->pw.addr,
+				sdata->sensor->pw.mask, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+
+		sdata->enabled = true;
+
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sdata->sensor->pw.addr,
+				sdata->sensor->pw.mask,
+				sdata->sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return st_sensors_write_data_with_mask(indio_dev,
+				sdata->sensor->enable_axis.addr,
+				sdata->sensor->enable_axis.mask, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sdata->sensor->drdy_irq.ig1.en_addr,
+			sdata->sensor->drdy_irq.ig1.en_mask, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sdata->sensor->drdy_irq.addr,
+			sdata->sensor->drdy_irq.mask, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
+{
+	int err = -EINVAL, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sdata->sensor->fs.fs_avl[i].gain == scale) &&
+				(sdata->sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sdata->sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
+							u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+				ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+				outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = (s16)get_unaligned_le16(outdata);
+
+read_error:
+	return err;
+}
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *ch, int *val)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+		err = -EBUSY;
+		goto read_error;
+	} else {
+		err = st_sensors_set_enable(indio_dev, true);
+		if (err < 0)
+			goto read_error;
+
+		msleep((sdata->sensor->bootime * 1000) / sdata->odr);
+		err = st_sensors_read_axis_data(indio_dev, ch->address, val);
+		if (err < 0)
+			goto read_error;
+
+		*val = *val >> ch->scan_type.shift;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return err;
+
+read_error:
+	mutex_unlock(&indio_dev->mlock);
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_info_raw);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors)
+{
+	u8 wai;
+	int i, n, err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
+					ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
+		goto read_wai_error;
+	}
+
+	for (i = 0; i < num_sensors_list; i++) {
+		if (sensors[i].wai == wai)
+			break;
+	}
+	if (i == num_sensors_list)
+		goto device_not_supported;
+
+	for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
+		if (strcmp(indio_dev->name,
+				&sensors[i].sensors_supported[n][0]) == 0)
+			break;
+	}
+	if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
+		dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
+		goto sensor_name_mismatch;
+	}
+
+	sdata->sensor = (struct st_sensors *)&sensors[i];
+
+	return i;
+
+device_not_supported:
+	dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
+sensor_name_mismatch:
+	err = -ENODEV;
+read_wai_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_check_device_support);
+
+ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev));
+
+	return sprintf(buf, "%d\n", adata->odr);
+}
+EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency);
+
+ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int err;
+	unsigned int odr;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	err = kstrtoint(buf, 10, &odr);
+	if (err < 0)
+		goto conversion_error;
+
+	mutex_lock(&indio_dev->mlock);
+	err = st_sensors_set_odr(indio_dev, odr);
+	mutex_unlock(&indio_dev->mlock);
+
+conversion_error:
+	return err < 0 ? err : size;
+}
+EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency);
+
+ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	return st_sensors_get_sampling_frequency_avl(indio_dev, buf);
+}
+EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail);
+
+ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	return st_sensors_get_scale_avl(indio_dev, buf);
+}
+EXPORT_SYMBOL(st_sensors_sysfs_scale_avail);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..38af944
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors_i2c.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = &st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..f0aa2f1
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors_spi.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = &st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..139ed03
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,77 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+				const struct iio_trigger_ops *trigger_ops)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..3cc8571
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,274 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+#define ST_SENSORS_TX_MAX_LENGTH		2
+#define ST_SENSORS_RX_MAX_LENGTH		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_AXIS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+#define ST_SENSOR_DEV_ATTR_SAMP_FREQ() \
+		IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \
+			st_sensors_sysfs_get_sampling_frequency, \
+			st_sensors_sysfs_set_sampling_frequency)
+
+#define ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL() \
+		IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \
+			st_sensors_sysfs_sampling_frequency_avail)
+
+#define ST_SENSORS_DEV_ATTR_SCALE_AVAIL(name) \
+		IIO_DEVICE_ATTR(name, S_IRUGO, \
+			st_sensors_sysfs_scale_avail, NULL , 0);
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGTH];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGTH] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @sensors_supported: List of supported sensors by struct itself.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @sensor: Pointer to the current sensor struct in use.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensors *sensor;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	const struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+#ifdef CONFIG_IIO_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+				const struct iio_trigger_ops *trigger_ops);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev, char *buf);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale);
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *ch, int *val);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors);
+
+ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size);
+
+ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+#endif /* ST_SENSORS_H */
diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h
new file mode 100644
index 0000000..67d8453
--- /dev/null
+++ b/include/linux/iio/common/st_sensors_i2c.h
@@ -0,0 +1,20 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_I2C_H
+#define ST_SENSORS_I2C_H
+
+#include <linux/i2c.h>
+#include <linux/iio/common/st_sensors.h>
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+#endif /* ST_SENSORS_I2C_H */
diff --git a/include/linux/iio/common/st_sensors_spi.h b/include/linux/iio/common/st_sensors_spi.h
new file mode 100644
index 0000000..d964a35
--- /dev/null
+++ b/include/linux/iio/common/st_sensors_spi.h
@@ -0,0 +1,20 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_SPI_H
+#define ST_SENSORS_SPI_H
+
+#include <linux/spi/spi.h>
+#include <linux/iio/common/st_sensors.h>
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+#endif /* ST_SENSORS_SPI_H */
-- 
1.7.9.5


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

* Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-21  8:36 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
  2013-01-22 16:15   ` Jonathan Cameron
@ 2013-01-23  9:33   ` Lars-Peter Clausen
  1 sibling, 0 replies; 19+ messages in thread
From: Lars-Peter Clausen @ 2013-01-23  9:33 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: jic23, linux-iio

Hi,

I guess we are almost there now. A couple of small things, which need to be
fixed though.

On 01/21/2013 09:36 AM, Denis CIOCCA wrote:
> This patch add a generic library for STMicroelectronics 3-axis sensors.
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---
>  drivers/iio/common/Kconfig                         |   1 +
>  drivers/iio/common/Makefile                        |   1 +
>  drivers/iio/common/st_sensors/Kconfig              |  14 +
>  drivers/iio/common/st_sensors/Makefile             |  10 +
>  drivers/iio/common/st_sensors/st_sensors_buffer.c  | 115 ++++++
>  drivers/iio/common/st_sensors/st_sensors_core.c    | 411 +++++++++++++++++++++
>  drivers/iio/common/st_sensors/st_sensors_i2c.c     |  81 ++++
>  drivers/iio/common/st_sensors/st_sensors_spi.c     | 128 +++++++
>  drivers/iio/common/st_sensors/st_sensors_trigger.c |  77 ++++
>  include/linux/iio/common/st_sensors.h              | 266 +++++++++++++
>  include/linux/iio/common/st_sensors_i2c.h          |  20 +
>  include/linux/iio/common/st_sensors_spi.h          |  20 +
>  12 files changed, 1144 insertions(+)
>  create mode 100644 drivers/iio/common/st_sensors/Kconfig
>  create mode 100644 drivers/iio/common/st_sensors/Makefile
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
>  create mode 100644 include/linux/iio/common/st_sensors.h
>  create mode 100644 include/linux/iio/common/st_sensors_i2c.h
>  create mode 100644 include/linux/iio/common/st_sensors_spi.h
> 
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index ed45ee5..0b6e97d 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,3 +3,4 @@
>  #
>  
>  source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 8158400..c2352be 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,3 +7,4 @@
>  #
>  
>  obj-y += hid-sensors/
> +obj-y += st_sensors/
> diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
> new file mode 100644
> index 0000000..84b2dca
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Kconfig
> @@ -0,0 +1,14 @@
> +#
> +# Hid Sensor common modules
> +#
> +
> +config IIO_ST_SENSORS_I2C
> +	tristate
> +
> +config IIO_ST_SENSORS_SPI
> +	tristate
> +
> +config IIO_ST_SENSORS_CORE
> +	tristate
> +	select IIO_ST_SENSORS_I2C if I2C
> +	select IIO_ST_SENSORS_SPI if SPI_MASTER
> diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
> new file mode 100644
> index 0000000..9f3e24f
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Makefile
> @@ -0,0 +1,10 @@
> +#
> +# Makefile for the STMicroelectronics sensor common modules.
> +#
> +
> +obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
> +obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
> +obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
> +st_sensors-y := st_sensors_core.o
> +st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
> +st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
> diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> new file mode 100644
> index 0000000..f4a5adc
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> @@ -0,0 +1,115 @@
> +/*
> + * STMicroelectronics sensors buffer library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/irqreturn.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int i, n = 0, len;
> +	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
> +		if (test_bit(i, indio_dev->active_scan_mask)) {
> +			addr[n] = indio_dev->channels[i].address;
> +			n++;
> +		}
> +	}
> +	switch (n) {
> +	case 1:
> +		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
> +			sdata->multiread_bit);
> +		break;
> +	case 2:
> +		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
> +			len = sdata->tf->read_multiple_byte(&sdata->tb,
> +					sdata->dev, addr[0],
> +					ST_SENSORS_BYTE_FOR_CHANNEL*n,
> +					buf, sdata->multiread_bit);
> +		} else {
> +			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
> +				    ST_SENSORS_NUMBER_DATA_CHANNELS];
> +			len = sdata->tf->read_multiple_byte(&sdata->tb,
> +				sdata->dev, addr[0],
> +				ST_SENSORS_BYTE_FOR_CHANNEL*
> +				ST_SENSORS_NUMBER_DATA_CHANNELS,
> +				rx_array, sdata->multiread_bit);
> +			if (len < 0)
> +				goto read_data_channels_error;
> +
> +			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
> +									i++) {
> +				if (i < n)
> +					buf[i] = rx_array[i];
> +				else
> +					buf[i] = rx_array[n + i];
> +			}
> +			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
> +		}
> +		break;
> +	case 3:
> +		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
> +			ST_SENSORS_NUMBER_DATA_CHANNELS,
> +			buf, sdata->multiread_bit);
> +		break;
> +	default:
> +		len = -EINVAL;
> +		goto read_data_channels_error;
> +	}
> +	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
> +		len = -EIO;
> +		goto read_data_channels_error;
> +	}
> +
> +read_data_channels_error:
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_buffer_element);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p)
> +{
> +	int len;
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
> +	if (len < 0)
> +		goto st_sensors_get_buffer_element_error;
> +
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)sdata->buffer_data +
> +				ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +	iio_push_to_buffers(indio_dev, sdata->buffer_data);
> +
> +st_sensors_get_buffer_element_error:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL(st_sensors_trigger_handler);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
> new file mode 100644
> index 0000000..661cc2b
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_core.c
> @@ -0,0 +1,411 @@
> +/*
> + * STMicroelectronics sensors core library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/unaligned/le_byteshift.h>

Never include that file directly. Use asm/unaligned.h, it will provide the
best suited implementation of the unaligned functions for the current
architecture.

> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
[...]

> diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> new file mode 100644
> index 0000000..9c62c6a
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> @@ -0,0 +1,77 @@
> +/*
> + * STMicroelectronics sensors trigger library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					struct iio_trigger_ops *trigger_ops)

const

> +{
[...]

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

* Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-21  8:36 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
@ 2013-01-22 16:15   ` Jonathan Cameron
  2013-01-23  9:33   ` Lars-Peter Clausen
  1 sibling, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2013-01-22 16:15 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: lars, linux-iio

On 01/21/2013 08:36 AM, Denis CIOCCA wrote:
> This patch add a generic library for STMicroelectronics 3-axis sensors.
> 
> Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
> ---

I very nearly took this series today but in a final look found just enough
slightly ugly corners to merit one more round..  To help you along I've
put together a patch set to fix up the various issues..



Couple of nitpicks

There are some functions exported by the core library that are no longer used
in the various drivers.  These exports should go away.
st_sensors_write_data_with_mask
st_sensors_set_fullscale

A couple of the comments are out of date in Kconfig for the drivers.
(basically there are no buffer specific modules any more..)

The gyro code at least includes a few unwanted exports as well, mostly
as a result of the fact we now have 3 modules in there instead of 2.

Having said that we could go round and round with these little bits for ever so
I hope you don't mind but I've just fixed up the ones I noticed and applied to
togreg branch of iio.git

I'm not convinced there is anything in linux/iio/accel/st_accel.h that wants to
be globally available so move it to driver/iio/accel.

Anyhow, patch set doing these to follow.


>  drivers/iio/common/Kconfig                         |   1 +
>  drivers/iio/common/Makefile                        |   1 +
>  drivers/iio/common/st_sensors/Kconfig              |  14 +
>  drivers/iio/common/st_sensors/Makefile             |  10 +
>  drivers/iio/common/st_sensors/st_sensors_buffer.c  | 115 ++++++
>  drivers/iio/common/st_sensors/st_sensors_core.c    | 411 +++++++++++++++++++++
>  drivers/iio/common/st_sensors/st_sensors_i2c.c     |  81 ++++
>  drivers/iio/common/st_sensors/st_sensors_spi.c     | 128 +++++++
>  drivers/iio/common/st_sensors/st_sensors_trigger.c |  77 ++++
>  include/linux/iio/common/st_sensors.h              | 266 +++++++++++++
>  include/linux/iio/common/st_sensors_i2c.h          |  20 +
>  include/linux/iio/common/st_sensors_spi.h          |  20 +
>  12 files changed, 1144 insertions(+)
>  create mode 100644 drivers/iio/common/st_sensors/Kconfig
>  create mode 100644 drivers/iio/common/st_sensors/Makefile
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
>  create mode 100644 include/linux/iio/common/st_sensors.h
>  create mode 100644 include/linux/iio/common/st_sensors_i2c.h
>  create mode 100644 include/linux/iio/common/st_sensors_spi.h
> 
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index ed45ee5..0b6e97d 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,3 +3,4 @@
>  #
>  
>  source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 8158400..c2352be 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,3 +7,4 @@
>  #
>  
>  obj-y += hid-sensors/
> +obj-y += st_sensors/
> diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
> new file mode 100644
> index 0000000..84b2dca
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Kconfig
> @@ -0,0 +1,14 @@
> +#
> +# Hid Sensor common modules
> +#
> +
> +config IIO_ST_SENSORS_I2C
> +	tristate
> +
> +config IIO_ST_SENSORS_SPI
> +	tristate
> +
> +config IIO_ST_SENSORS_CORE
> +	tristate
> +	select IIO_ST_SENSORS_I2C if I2C
> +	select IIO_ST_SENSORS_SPI if SPI_MASTER
> diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
> new file mode 100644
> index 0000000..9f3e24f
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Makefile
> @@ -0,0 +1,10 @@
> +#
> +# Makefile for the STMicroelectronics sensor common modules.
> +#
> +
> +obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
> +obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
> +obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
> +st_sensors-y := st_sensors_core.o
> +st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
> +st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
> diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> new file mode 100644
> index 0000000..f4a5adc
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> @@ -0,0 +1,115 @@
> +/*
> + * STMicroelectronics sensors buffer library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/irqreturn.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int i, n = 0, len;
> +	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
> +		if (test_bit(i, indio_dev->active_scan_mask)) {
> +			addr[n] = indio_dev->channels[i].address;
> +			n++;
> +		}
> +	}
> +	switch (n) {
> +	case 1:
> +		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
> +			sdata->multiread_bit);
> +		break;
> +	case 2:
> +		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
> +			len = sdata->tf->read_multiple_byte(&sdata->tb,
> +					sdata->dev, addr[0],
> +					ST_SENSORS_BYTE_FOR_CHANNEL*n,
> +					buf, sdata->multiread_bit);
> +		} else {
> +			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
> +				    ST_SENSORS_NUMBER_DATA_CHANNELS];
> +			len = sdata->tf->read_multiple_byte(&sdata->tb,
> +				sdata->dev, addr[0],
> +				ST_SENSORS_BYTE_FOR_CHANNEL*
> +				ST_SENSORS_NUMBER_DATA_CHANNELS,
> +				rx_array, sdata->multiread_bit);
> +			if (len < 0)
> +				goto read_data_channels_error;
> +
> +			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
> +									i++) {
> +				if (i < n)
> +					buf[i] = rx_array[i];
> +				else
> +					buf[i] = rx_array[n + i];
> +			}
> +			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
> +		}
> +		break;
> +	case 3:
> +		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
> +			ST_SENSORS_NUMBER_DATA_CHANNELS,
> +			buf, sdata->multiread_bit);
> +		break;
> +	default:
> +		len = -EINVAL;
> +		goto read_data_channels_error;
> +	}
> +	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
> +		len = -EIO;
> +		goto read_data_channels_error;
> +	}
> +
> +read_data_channels_error:
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_buffer_element);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p)
> +{
> +	int len;
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
> +	if (len < 0)
> +		goto st_sensors_get_buffer_element_error;
> +
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)sdata->buffer_data +
> +				ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +	iio_push_to_buffers(indio_dev, sdata->buffer_data);
> +
> +st_sensors_get_buffer_element_error:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL(st_sensors_trigger_handler);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
> new file mode 100644
> index 0000000..661cc2b
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_core.c
> @@ -0,0 +1,411 @@
> +/*
> + * STMicroelectronics sensors core library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/unaligned/le_byteshift.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_WAI_ADDRESS		0x0f
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
> +						u8 reg_addr, u8 mask, u8 data)
> +{
> +	int err;
> +	u8 new_data;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
> +	if (err < 0)
> +		goto st_sensors_write_data_with_mask_error;
> +
> +	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
> +
> +st_sensors_write_data_with_mask_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_write_data_with_mask);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if (odr_avl[i].hz == 0)
> +			break;
> +
> +		len += scnprintf(buf + len, PAGE_SIZE - len,
> +							"%d ", odr_avl[i].hz);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
> +
> +int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
> +		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if (fullscale_avl[i].num == 0)
> +			break;
> +
> +		len += scnprintf(buf + len, PAGE_SIZE - len,
> +					"0.%06u ", fullscale_avl[i].gain);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_scale_avl);
> +
> +static int st_sensors_match_odr(const struct st_sensors *sensor,
> +			unsigned int odr, struct st_sensor_odr_avl *odr_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if (sensor->odr.odr_avl[i].hz == 0)
> +			goto st_sensors_match_odr_error;
> +
> +		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;
> +		}
> +	}
> +
> +st_sensors_match_odr_error:
> +	return ret;
> +}
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct st_sensor_odr_avl odr_out;
> +
> +	err = st_sensors_match_odr(sensor, odr, &odr_out);
> +	if (err < 0)
> +		goto st_sensors_match_odr_error;
> +
> +	if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +		if (sdata->enabled == true) {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->odr.addr, sensor->odr.mask,
> +				odr_out.value);
> +		} else {
> +			err = 0;
> +		}
> +	} else {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->odr.addr, sensor->odr.mask, odr_out.value);
> +	}
> +	if (err >= 0)
> +		sdata->odr = odr_out.hz;
> +
> +st_sensors_match_odr_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_odr);
> +
> +static int st_sensors_match_fs(const struct st_sensors *sensor,
> +					unsigned int fs, int *index_fs_avl)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if (sensor->fs.fs_avl[i].num == 0)
> +			goto st_sensors_match_odr_error;
> +
> +		if (sensor->fs.fs_avl[i].num == fs) {
> +			*index_fs_avl = i;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +st_sensors_match_odr_error:
> +	return ret;
> +}
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs)
> +{
> +	int err, i;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = st_sensors_match_fs(sensor, fs, &i);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	err = st_sensors_write_data_with_mask(indio_dev,
> +		sensor->fs.addr, sensor->fs.mask, sensor->fs.fs_avl[i].value);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
> +							&sensor->fs.fs_avl[i];
> +	return err;
> +
> +st_accel_set_fullscale_error:
> +	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err = -EINVAL;
> +	bool found;
> +	u8 tmp_value;
> +	struct st_sensor_odr_avl odr_out;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	if (enable) {
> +		found = false;
> +		tmp_value = sensor->pw.value_on;
> +		if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +			err = st_sensors_match_odr(sensor,
> +							sdata->odr, &odr_out);
> +			if (err < 0)
> +				goto set_enable_error;
> +			tmp_value = odr_out.value;
> +			found = true;
> +		}
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask, tmp_value);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = true;
> +		if (found)
> +			sdata->odr = odr_out.hz;
> +	} else {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask,
> +							sensor->pw.value_off);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = false;
> +	}
> +
> +set_enable_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable)
> +{
> +	return st_sensors_write_data_with_mask(indio_dev,
> +				sensor->enable_axis.addr,
> +					sensor->enable_axis.mask, axis_enable);
> +}
> +EXPORT_SYMBOL(st_sensors_set_axis_enable);
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +						const struct st_sensors *sensor)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	mutex_init(&sdata->tb.buf_lock);
> +
> +	err = st_sensors_set_enable(indio_dev, sensor, false);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev, sensor,
> +						sdata->current_fullscale->num);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
> +	if (err < 0)
> +		goto init_error;
> +
> +	/* set BDU */
> +	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
> +							sensor->bdu.mask, true);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_axis_enable(indio_dev, sensor,
> +						ST_SENSORS_ENABLE_ALL_CHANNELS);
> +
> +init_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_init_sensor);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err;
> +
> +	/* Enable/Disable the interrupt generator 1. */
> +	if (sensor->drdy_irq.ig1.en_addr > 0) {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->drdy_irq.ig1.en_addr,
> +			sensor->drdy_irq.ig1.en_mask, (int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	/* Enable/Disable the interrupt generator for data ready. */
> +	err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->drdy_irq.addr,
> +			sensor->drdy_irq.mask, (int)enable);
> +
> +st_accel_set_dataready_irq_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_dataready_irq);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale)
> +{
> +	int err = -EINVAL, i;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if ((sensor->fs.fs_avl[i].gain == scale) &&
> +					(sensor->fs.fs_avl[i].gain != 0)) {
> +			err = 0;
> +			break;
> +		}
> +	}
> +	if (err < 0)
> +		goto st_sensors_match_scale_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev,
> +					sensor, sensor->fs.fs_avl[i].num);
> +
> +st_sensors_match_scale_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
> +
> +static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
> +							u8 ch_addr, int *data)
> +{
> +	int err;
> +	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
> +						outdata, sdata->multiread_bit);
> +	if (err < 0)
> +		goto read_error;
> +
> +	*data = (s16)get_unaligned_le16(outdata);
> +
> +read_error:
> +	return err;
> +}
> +
> +int st_sensors_read_info_raw(struct iio_dev *indio_dev,
> +			struct iio_chan_spec const *ch, int *val,
> +						const struct st_sensors *sensor)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	mutex_lock(&indio_dev->mlock);
> +	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +		err = -EBUSY;
> +		goto read_error;
> +	} else {
> +		err = st_sensors_set_enable(indio_dev, sensor, true);
> +		if (err < 0)
> +			goto read_error;
> +
> +		msleep((sensor->bootime * 1000) / sdata->odr);
> +		err = st_sensors_read_axis_data(indio_dev, ch->address, val);
> +		if (err < 0)
> +			goto read_error;
> +
> +		*val = *val >> ch->scan_type.shift;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return err;
> +
> +read_error:
> +	mutex_unlock(&indio_dev->mlock);
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_read_info_raw);
> +
> +int st_sensors_check_device_support(struct iio_dev *indio_dev,
> +			int num_sensors_list, const struct st_sensors *sensors)
> +{
> +	int i, n, err;
> +	u8 wai;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
> +					ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
> +		goto read_wai_error;
> +	}
> +
> +	for (i = 0; i < num_sensors_list; i++) {
> +		if (sensors[i].wai == wai)
> +			break;
> +	}
> +	if (i == num_sensors_list)
> +		goto device_not_supported;
> +
> +	for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
> +		if (strcmp(indio_dev->name,
> +				&sensors[i].sensors_supported[n][0]) == 0)
> +			break;
> +	}
> +	if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
> +		dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
> +		goto sensor_name_mismatch;
> +	}
> +
> +	sdata->index = i;
> +
> +	return i;
> +
> +device_not_supported:
> +	dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
> +sensor_name_mismatch:
> +	err = -ENODEV;
> +read_wai_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_check_device_support);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> new file mode 100644
> index 0000000..5683436
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> @@ -0,0 +1,81 @@
> +/*
> + * STMicroelectronics sensors i2c library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors_i2c.h>
> +
> +
> +#define ST_SENSORS_I2C_MULTIREAD	0x80
> +
> +static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	return to_i2c_client(sdata->dev)->irq;
> +}
> +
> +static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	int err;
> +
> +	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
> +	if (err < 0)
> +		goto st_accel_i2c_read_byte_error;
> +
> +	*res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +	return err < 0 ? err : 0;
> +}
> +
> +static int st_sensors_i2c_read_multiple_byte(
> +		struct st_sensor_transfer_buffer *tb, struct device *dev,
> +			u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	if (multiread_bit == true)
> +		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
> +
> +	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
> +							reg_addr, len, data);
> +}
> +
> +static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
> +}
> +
> +static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
> +	.read_byte = st_sensors_i2c_read_byte,
> +	.write_byte = st_sensors_i2c_write_byte,
> +	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
> +};
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata)
> +{
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->name = client->name;
> +
> +	sdata->tf = &st_sensors_tf_i2c;
> +	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
> +}
> +EXPORT_SYMBOL(st_sensors_i2c_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
> new file mode 100644
> index 0000000..8a795f5
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
> @@ -0,0 +1,128 @@
> +/*
> + * STMicroelectronics sensors spi library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors_spi.h>
> +
> +
> +#define ST_SENSORS_SPI_MULTIREAD	0xc0
> +#define ST_SENSORS_SPI_READ		0x80
> +
> +static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	return to_spi_device(sdata->dev)->irq;
> +}
> +
> +static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = tb->tx_buf,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
> +		{
> +			.rx_buf = tb->rx_buf,
> +			.bits_per_word = 8,
> +			.len = len,
> +		}
> +	};
> +
> +	mutex_lock(&tb->buf_lock);
> +	if ((multiread_bit == true) && (len > 1))
> +		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
> +	else
> +		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	if (err)
> +		goto acc_spi_read_error;
> +
> +	memcpy(data, tb->rx_buf, len*sizeof(u8));
> +	mutex_unlock(&tb->buf_lock);
> +	return len;
> +
> +acc_spi_read_error:
> +	mutex_unlock(&tb->buf_lock);
> +	return err;
> +}
> +
> +static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
> +}
> +
> +static int st_sensors_spi_read_multiple_byte(
> +	struct st_sensor_transfer_buffer *tb, struct device *dev,
> +			u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
> +}
> +
> +static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers = {
> +		.tx_buf = tb->tx_buf,
> +		.bits_per_word = 8,
> +		.len = 2,
> +	};
> +
> +	mutex_lock(&tb->buf_lock);
> +	tb->tx_buf[0] = reg_addr;
> +	tb->tx_buf[1] = data;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers, &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	mutex_unlock(&tb->buf_lock);
> +
> +	return err;
> +}
> +
> +static const struct st_sensor_transfer_function st_sensors_tf_spi = {
> +	.read_byte = st_sensors_spi_read_byte,
> +	.write_byte = st_sensors_spi_write_byte,
> +	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
> +};
> +
> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata)
> +{
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi->modalias;
> +
> +	sdata->tf = &st_sensors_tf_spi;
> +	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
> +}
> +EXPORT_SYMBOL(st_sensors_spi_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> new file mode 100644
> index 0000000..9c62c6a
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> @@ -0,0 +1,77 @@
> +/*
> + * STMicroelectronics sensors trigger library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					struct iio_trigger_ops *trigger_ops)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +	if (sdata->trig == NULL) {
> +		err = -ENOMEM;
> +		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +		goto iio_trigger_alloc_error;
> +	}
> +
> +	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
> +			iio_trigger_generic_data_rdy_poll,
> +			NULL,
> +			IRQF_TRIGGER_RISING,
> +			sdata->trig->name,
> +			sdata->trig);
> +	if (err)
> +		goto request_irq_error;
> +
> +	sdata->trig->private_data = indio_dev;
> +	sdata->trig->ops = trigger_ops;
> +	sdata->trig->dev.parent = sdata->dev;
> +
> +	err = iio_trigger_register(sdata->trig);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +		goto iio_trigger_register_error;
> +	}
> +	indio_dev->trig = sdata->trig;
> +
> +	return 0;
> +
> +iio_trigger_register_error:
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +request_irq_error:
> +	iio_trigger_free(sdata->trig);
> +iio_trigger_alloc_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_allocate_trigger);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	iio_trigger_unregister(sdata->trig);
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +	iio_trigger_free(sdata->trig);
> +}
> +EXPORT_SYMBOL(st_sensors_deallocate_trigger);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
> new file mode 100644
> index 0000000..f27b1e0
> --- /dev/null
> +++ b/include/linux/iio/common/st_sensors.h
> @@ -0,0 +1,266 @@
> +/*
> + * STMicroelectronics sensors library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_H
> +#define ST_SENSORS_H
> +
> +#include <linux/i2c.h>
> +#include <linux/spi/spi.h>
> +#include <linux/irqreturn.h>
> +#include <linux/iio/trigger.h>
> +
> +
> +#define ST_SENSORS_TX_MAX_LENGHT		2
> +#define ST_SENSORS_RX_MAX_LENGHT		6
> +
> +#define ST_SENSORS_ODR_LIST_MAX			10
> +#define ST_SENSORS_FULLSCALE_AVL_MAX		10
> +
> +#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
> +#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
> +#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
> +#define ST_SENSORS_BYTE_FOR_CHANNEL		2
> +#define ST_SENSORS_SCAN_X			0
> +#define ST_SENSORS_SCAN_Y			1
> +#define ST_SENSORS_SCAN_Z			2
> +#define ST_SENSORS_DEFAULT_12_REALBITS		12
> +#define ST_SENSORS_DEFAULT_16_REALBITS		16
> +#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
> +#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
> +#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
> +#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
> +#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
> +#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
> +
> +#define ST_SENSORS_MAX_NAME			17
> +#define ST_SENSORS_MAX_4WAI			7
> +
> +#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
> +{ \
> +	.type = device_type, \
> +	.modified = 1, \
> +	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +	.scan_index = index, \
> +	.channel2 = mod, \
> +	.address = addr, \
> +	.scan_type = { \
> +		.sign = 's', \
> +		.realbits = bits, \
> +		.shift = 16 - bits, \
> +		.storagebits = 16, \
> +		.endianness = endian, \
> +	}, \
> +}
> +
> +struct st_sensor_odr_avl {
> +	unsigned int hz;
> +	u8 value;
> +};
> +
> +struct st_sensor_odr {
> +	u8 addr;
> +	u8 mask;
> +	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
> +};
> +
> +struct st_sensor_power {
> +	u8 addr;
> +	u8 mask;
> +	u8 value_off;
> +	u8 value_on;
> +};
> +
> +struct st_sensor_axis {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_sensor_fullscale_avl {
> +	unsigned int num;
> +	u8 value;
> +	unsigned int gain;
> +	unsigned int gain2;
> +};
> +
> +struct st_sensor_fullscale {
> +	u8 addr;
> +	u8 mask;
> +	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
> +};
> +
> +/**
> + * struct st_sensor_bdu - ST sensor device block data update
> + * @addr: address of the register.
> + * @mask: mask to write the block data update flag.
> + */
> +struct st_sensor_bdu {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +/**
> + * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
> + * @addr: address of the register.
> + * @mask: mask to write the on/off value.
> + * struct ig1 - represents the Interrupt Generator 1 of sensors.
> + * @en_addr: address of the enable ig1 register.
> + * @en_mask: mask to write the on/off value for enable.
> + */
> +struct st_sensor_data_ready_irq {
> +	u8 addr;
> +	u8 mask;
> +	struct {
> +		u8 en_addr;
> +		u8 en_mask;
> +	} ig1;
> +};
> +
> +/**
> + * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
> + * @buf_lock: Mutex to protect rx and tx buffers.
> + * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + */
> +struct st_sensor_transfer_buffer {
> +	struct mutex buf_lock;
> +	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
> +	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
> +};
> +
> +/**
> + * struct st_sensor_transfer_function - ST sensor device I/O function
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + */
> +struct st_sensor_transfer_function {
> +	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 *res_byte);
> +	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
> +				struct device *dev, u8 reg_addr, u8 data);
> +	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
> +		struct device *dev, u8 reg_addr, int len, u8 *data,
> +							bool multiread_bit);
> +};
> +
> +/**
> + * struct st_sensor_data - ST sensor device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @current_fullscale: Maximum range of measure by the sensor.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the st_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @odr: Output data rate of the sensor [Hz].
> + * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
> + * @tf: Transfer function structure used by I/O operations.
> + * @tb: Transfer buffers and mutex used by I/O operations.
> + */
> +struct st_sensor_data {
> +	struct device *dev;
> +	struct iio_trigger *trig;
> +	struct st_sensor_fullscale_avl *current_fullscale;
> +
> +	bool enabled;
> +	bool multiread_bit;
> +
> +	short index;
> +
> +	char *buffer_data;
> +
> +	unsigned int odr;
> +
> +	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
> +
> +	const struct st_sensor_transfer_function *tf;
> +	struct st_sensor_transfer_buffer tb;
> +};
> +
> +/**
> + * struct st_sensors - ST sensors list
> + * @wai: Contents of WhoAmI register.
> + * @sensors_supported: List of supported sensors by struct itself.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and ODR list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and full scale 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] multi-read.
> + * @bootime: samples to discard when sensor passing from power-down to power-up.
> + */
> +struct st_sensors {
> +	u8 wai;
> +	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
> +	struct iio_chan_spec *ch;
> +	struct st_sensor_odr odr;
> +	struct st_sensor_power pw;
> +	struct st_sensor_axis enable_axis;
> +	struct st_sensor_fullscale fs;
> +	struct st_sensor_bdu bdu;
> +	struct st_sensor_data_ready_irq drdy_irq;
> +	bool multi_read_bit;
> +	unsigned int bootime;
> +};
> +
> +#ifdef CONFIG_IIO_BUFFER
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					struct iio_trigger_ops *trigger_ops);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p);
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
> +#endif
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +					const struct st_sensors *sensor);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable);
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
> +						u8 reg_addr, u8 mask, u8 data);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf);
> +
> +int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
> +		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs);
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale);
> +
> +int st_sensors_read_info_raw(struct iio_dev *indio_dev,
> +			struct iio_chan_spec const *ch, int *val,
> +					const struct st_sensors *sensor);
> +
> +int st_sensors_check_device_support(struct iio_dev *indio_dev,
> +			int num_sensors_list, const struct st_sensors *sensors);
> +
> +#endif /* ST_SENSORS_H */
> diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h
> new file mode 100644
> index 0000000..67d8453
> --- /dev/null
> +++ b/include/linux/iio/common/st_sensors_i2c.h
> @@ -0,0 +1,20 @@
> +/*
> + * STMicroelectronics sensors i2c library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_I2C_H
> +#define ST_SENSORS_I2C_H
> +
> +#include <linux/i2c.h>
> +#include <linux/iio/common/st_sensors.h>
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata);
> +
> +#endif /* ST_SENSORS_I2C_H */
> diff --git a/include/linux/iio/common/st_sensors_spi.h b/include/linux/iio/common/st_sensors_spi.h
> new file mode 100644
> index 0000000..d964a35
> --- /dev/null
> +++ b/include/linux/iio/common/st_sensors_spi.h
> @@ -0,0 +1,20 @@
> +/*
> + * STMicroelectronics sensors spi library driver
> + *
> + * Copyright 2012-2013 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_SPI_H
> +#define ST_SENSORS_SPI_H
> +
> +#include <linux/spi/spi.h>
> +#include <linux/iio/common/st_sensors.h>
> +
> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata);
> +
> +#endif /* ST_SENSORS_SPI_H */
> 

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-21  8:36 STMicroelectronics driver kconfig-makefile bugfix Denis CIOCCA
@ 2013-01-21  8:36 ` Denis CIOCCA
  2013-01-22 16:15   ` Jonathan Cameron
  2013-01-23  9:33   ` Lars-Peter Clausen
  0 siblings, 2 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-21  8:36 UTC (permalink / raw)
  To: jic23; +Cc: lars, linux-iio, Denis Ciocca

This patch add a generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |   1 +
 drivers/iio/common/Makefile                        |   1 +
 drivers/iio/common/st_sensors/Kconfig              |  14 +
 drivers/iio/common/st_sensors/Makefile             |  10 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  | 115 ++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    | 411 +++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |  81 ++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     | 128 +++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |  77 ++++
 include/linux/iio/common/st_sensors.h              | 266 +++++++++++++
 include/linux/iio/common/st_sensors_i2c.h          |  20 +
 include/linux/iio/common/st_sensors_spi.h          |  20 +
 12 files changed, 1144 insertions(+)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h
 create mode 100644 include/linux/iio/common/st_sensors_i2c.h
 create mode 100644 include/linux/iio/common/st_sensors_spi.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..84b2dca
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,14 @@
+#
+# Hid Sensor common modules
+#
+
+config IIO_ST_SENSORS_I2C
+	tristate
+
+config IIO_ST_SENSORS_SPI
+	tristate
+
+config IIO_ST_SENSORS_CORE
+	tristate
+	select IIO_ST_SENSORS_I2C if I2C
+	select IIO_ST_SENSORS_SPI if SPI_MASTER
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..9f3e24f
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
+st_sensors-y := st_sensors_core.o
+st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
+st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..f4a5adc
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..661cc2b
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,411 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/unaligned/le_byteshift.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+							"%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+					"0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (sensor->odr.odr_avl[i].hz == 0)
+			goto st_sensors_match_odr_error;
+
+		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;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (sensor->fs.fs_avl[i].num == 0)
+			goto st_sensors_match_odr_error;
+
+		if (sensor->fs.fs_avl[i].num == fs) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+		sensor->fs.addr, sensor->fs.mask, sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+							&sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+							sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+				sensor->enable_axis.addr,
+					sensor->enable_axis.mask, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+							sensor->bdu.mask, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.ig1.en_addr,
+			sensor->drdy_irq.ig1.en_mask, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
+							u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = (s16)get_unaligned_le16(outdata);
+
+read_error:
+	return err;
+}
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+		err = -EBUSY;
+		goto read_error;
+	} else {
+		err = st_sensors_set_enable(indio_dev, sensor, true);
+		if (err < 0)
+			goto read_error;
+
+		msleep((sensor->bootime * 1000) / sdata->odr);
+		err = st_sensors_read_axis_data(indio_dev, ch->address, val);
+		if (err < 0)
+			goto read_error;
+
+		*val = *val >> ch->scan_type.shift;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return err;
+
+read_error:
+	mutex_unlock(&indio_dev->mlock);
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_info_raw);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors)
+{
+	int i, n, err;
+	u8 wai;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
+					ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
+		goto read_wai_error;
+	}
+
+	for (i = 0; i < num_sensors_list; i++) {
+		if (sensors[i].wai == wai)
+			break;
+	}
+	if (i == num_sensors_list)
+		goto device_not_supported;
+
+	for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
+		if (strcmp(indio_dev->name,
+				&sensors[i].sensors_supported[n][0]) == 0)
+			break;
+	}
+	if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
+		dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
+		goto sensor_name_mismatch;
+	}
+
+	sdata->index = i;
+
+	return i;
+
+device_not_supported:
+	dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
+sensor_name_mismatch:
+	err = -ENODEV;
+read_wai_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_check_device_support);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..5683436
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors_i2c.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = &st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..8a795f5
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors_spi.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = &st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..9c62c6a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,77 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..f27b1e0
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,266 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	const struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @sensors_supported: List of supported sensors by struct itself.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+#ifdef CONFIG_IIO_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_info_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+					const struct st_sensors *sensor);
+
+int st_sensors_check_device_support(struct iio_dev *indio_dev,
+			int num_sensors_list, const struct st_sensors *sensors);
+
+#endif /* ST_SENSORS_H */
diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h
new file mode 100644
index 0000000..67d8453
--- /dev/null
+++ b/include/linux/iio/common/st_sensors_i2c.h
@@ -0,0 +1,20 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_I2C_H
+#define ST_SENSORS_I2C_H
+
+#include <linux/i2c.h>
+#include <linux/iio/common/st_sensors.h>
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+#endif /* ST_SENSORS_I2C_H */
diff --git a/include/linux/iio/common/st_sensors_spi.h b/include/linux/iio/common/st_sensors_spi.h
new file mode 100644
index 0000000..d964a35
--- /dev/null
+++ b/include/linux/iio/common/st_sensors_spi.h
@@ -0,0 +1,20 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_SPI_H
+#define ST_SENSORS_SPI_H
+
+#include <linux/spi/spi.h>
+#include <linux/iio/common/st_sensors.h>
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+#endif /* ST_SENSORS_SPI_H */
-- 
1.8.0.3

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-18  9:31 STMicroelectronics drivers bugfix Denis CIOCCA
@ 2013-01-18  9:31 ` Denis CIOCCA
  0 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-18  9:31 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch add a generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |   1 +
 drivers/iio/common/Makefile                        |   1 +
 drivers/iio/common/st_sensors/Kconfig              |   6 +
 drivers/iio/common/st_sensors/Makefile             |   9 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  | 115 +++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    | 334 +++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |  81 +++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     | 128 ++++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |  77 +++++
 include/linux/iio/common/st_sensors.h              | 275 +++++++++++++++++
 10 files changed, 1027 insertions(+)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..0749f5c
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,6 @@
+#
+# Hid Sensor common modules
+#
+
+config IIO_ST_SENSORS_CORE
+	bool
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..7a8363a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors_core.o
+obj-$(CONFIG_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_SPI) += st_sensors_spi.o
+obj-$(CONFIG_IIO_BUFFER) += st_sensors_triggered_buffer.o
+st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..f4a5adc
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..36b5992
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,334 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/unaligned/le_byteshift.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+							"%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+					"0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (sensor->odr.odr_avl[i].hz == 0)
+			goto st_sensors_match_odr_error;
+
+		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;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (sensor->fs.fs_avl[i].num == 0)
+			goto st_sensors_match_odr_error;
+
+		if (sensor->fs.fs_avl[i].num == fs) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+st_sensors_match_odr_error:
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+		sensor->fs.addr, sensor->fs.mask, sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+							&sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+							sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+				sensor->enable_axis.addr,
+					sensor->enable_axis.mask, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+							sensor->bdu.mask, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.ig1.en_addr,
+			sensor->drdy_irq.ig1.en_mask, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	//*data = ((s16)le16_to_cpup((__le16 *)outdata));
+	*data = (s16)get_unaligned_le16(outdata);
+
+read_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_axis_data);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..b2d2fdf
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = &st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..5030766
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = &st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..9c62c6a
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,77 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..158542e
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,275 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	const struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @sensors_supported: List of supported sensors by struct itself.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+#ifdef CONFIG_SPI_MASTER
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_I2C
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_IIO_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger_ops *trigger_ops);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
+						u8 reg_addr, u8 mask, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
+
+#endif /* ST_SENSORS_H */
-- 
1.8.0.3

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

* Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-09 12:06 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
@ 2013-01-12 21:59   ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2013-01-12 21:59 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: lars, linux-iio

...
> +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
> +{
> +	int err;
> +	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
> +			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
> +						outdata, sdata->multiread_bit);
> +	if (err < 0)
> +		goto read_error;
> +

If you want to keep the cast this way around, then make
it __le16 instead of u16.  Personally I'd make outdata a __le16
array and then do the cast the other way, but it is far from important.

> +	*data = ((s16)le16_to_cpup((u16 *)outdata));
> +
> +read_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_read_axis_data);
...

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-09 12:06 iio: STMicroelectronics iio drivers bugfix Denis CIOCCA
@ 2013-01-09 12:06 ` Denis CIOCCA
  2013-01-12 21:59   ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-09 12:06 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch add generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/st_sensors/Kconfig              |   30 ++
 drivers/iio/common/st_sensors/Makefile             |    9 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  |  115 +++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    |  329 ++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |   81 +++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     |  128 ++++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |   83 +++++
 include/linux/iio/common/st_sensors.h              |  278 +++++++++++++++++
 10 files changed, 1055 insertions(+), 0 deletions(-)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..1cf41fd
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,30 @@
+#
+# Hid Sensor common modules
+#
+menu "STMicroelectronics sensors library"
+
+config IIO_ST_SENSORS_CORE
+	tristate "Common modules for all ST sensors IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common core functions.
+
+config IIO_ST_SENSORS_I2C
+	tristate "I2C common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common i2c functions.
+
+config IIO_ST_SENSORS_SPI
+	tristate "SPI common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common spi functions.
+
+config IIO_ST_SENSORS_TRIGGERED_BUFFER
+	tristate "trigger and buffer common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common trigger and buffer functions.
+
+endmenu
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..8418b6b
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors_core.o
+obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_IIO_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o
+st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..2170b1d
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..e2c0d99
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,329 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+#define ST_SENSORS_G_TO_MG(x)		(x*1000000ULL)
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+						u8 mask, short num_bit, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += sprintf(buf + len, "%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if ((sensor->odr.odr_avl[i].hz == odr) &&
+					(sensor->odr.odr_avl[i].hz != 0)) {
+			odr_out->hz = sensor->odr.odr_avl[i].hz;
+			odr_out->value = sensor->odr.odr_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				sensor->odr.num_bit,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask,
+			sensor->odr.num_bit, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].num == fs) &&
+					(sensor->fs.fs_avl[i].num != 0)) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+		sensor->fs.addr, sensor->fs.mask, sensor->fs.num_bit,
+						sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+							&sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+						sensor->pw.num_bit, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+				sensor->pw.num_bit, sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+		sensor->enable_axis.addr, sensor->enable_axis.mask,
+		3, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+						sensor->bdu.mask, 1, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.ig1.en_addr,
+			sensor->drdy_irq.ig1.en_mask, 1, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, 1, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = ((s16)le16_to_cpup((u16 *)outdata));
+
+read_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_axis_data);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..1498831
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..2079f4b
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..1c69c63
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+static struct iio_trigger_ops st_sensors_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+		int (*set_trigger_state)(struct iio_trigger *trig, bool state),
+		int (*try_reenable)(struct iio_trigger *trig))
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	st_sensors_trigger_ops.set_trigger_state = set_trigger_state;
+	st_sensors_trigger_ops.try_reenable = try_reenable;
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = &st_sensors_trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..f396bc5
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,278 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+#ifdef CONFIG_IIO_ST_SENSORS_SPI
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_IIO_ST_SENSORS_I2C
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_IIO_ST_SENSORS_TRIGGERED_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+		int (*set_trigger_state)(struct iio_trigger *trig, bool state),
+		int (*try_reenable)(struct iio_trigger *trig));
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+					u8 mask, short num_bit, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
+
+#endif /* ST_SENSORS_H */
-- 
1.7.0.4

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2013-01-08 16:30 iio: STMicroelectronics iio drivers Denis CIOCCA
@ 2013-01-08 16:30 ` Denis CIOCCA
  0 siblings, 0 replies; 19+ messages in thread
From: Denis CIOCCA @ 2013-01-08 16:30 UTC (permalink / raw)
  To: jic23, lars, linux-iio; +Cc: Denis Ciocca

This patch add generic library for STMicroelectronics 3-axis sensors.

Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
---
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/st_sensors/Kconfig              |   30 ++
 drivers/iio/common/st_sensors/Makefile             |    9 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  |  115 +++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    |  338 ++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |   81 +++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     |  128 ++++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |   83 +++++
 include/linux/iio/common/st_sensors.h              |  281 ++++++++++++++++
 10 files changed, 1067 insertions(+), 0 deletions(-)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..ddcfcc6
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,30 @@
+#
+# Hid Sensor common modules
+#
+menu "STMicroelectronics sensors library"
+
+config ST_SENSORS_CORE
+	tristate "Common modules for all ST sensors IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common core functions.
+
+config ST_SENSORS_I2C
+	tristate "I2C common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common i2c functions.
+
+config ST_SENSORS_SPI
+	tristate "SPI common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common spi functions.
+
+config ST_SENSORS_TRIGGERED_BUFFER
+	tristate "trigger and buffer common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common trigger and buffer functions.
+
+endmenu
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..a191e65
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_ST_SENSORS_CORE) += st_sensors_core.o
+obj-$(CONFIG_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o
+st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..2170b1d
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf->read_multiple_byte(&sdata->tb,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..bb6db19
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,338 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+#define ST_SENSORS_G_TO_MG(x)		(x*1000000ULL)
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+						u8 mask, short num_bit, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += sprintf(buf + len, "%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_scale_avl);
+
+int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int len = 0;
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_fullscale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if ((sensor->odr.odr_avl[i].hz == odr) &&
+					(sensor->odr.odr_avl[i].hz != 0)) {
+			odr_out->hz = sensor->odr.odr_avl[i].hz;
+			odr_out->value = sensor->odr.odr_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				sensor->odr.num_bit,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask,
+			sensor->odr.num_bit, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+					unsigned int fs, int *index_fs_avl)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].num == fs) &&
+					(sensor->fs.fs_avl[i].num != 0)) {
+			*index_fs_avl = i;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err, i;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_match_fs(sensor, fs, &i);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+		sensor->fs.addr, sensor->fs.mask, sensor->fs.num_bit,
+						sensor->fs.fs_avl[i].value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+							&sensor->fs.fs_avl[i];
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+						sensor->pw.num_bit, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+				sensor->pw.num_bit, sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+		sensor->enable_axis.addr, sensor->enable_axis.mask,
+		3, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	mutex_init(&sdata->tb.buf_lock);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor,
+						sdata->current_fullscale->num);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+						sensor->bdu.mask, 1, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	/* Enable/Disable the interrupt generator 1. */
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.ig1.en_addr,
+			sensor->drdy_irq.ig1.en_mask, 1, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	/* Enable/Disable the interrupt generator for data ready. */
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, 1, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = ((s16)le16_to_cpup((u16 *)outdata));
+
+read_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_axis_data);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..1498831
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,81 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_i2c_client(sdata->dev)->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(
+		struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
+	.read_byte = st_sensors_i2c_read_byte,
+	.write_byte = st_sensors_i2c_write_byte,
+	.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+};
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_i2c;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..2079f4b
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,128 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	return to_spi_device(sdata->dev)->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = tb->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = tb->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&tb->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, tb->rx_buf, len*sizeof(u8));
+	mutex_unlock(&tb->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	mutex_unlock(&tb->buf_lock);
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(
+	struct st_sensor_transfer_buffer *tb, struct device *dev,
+			u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = tb->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&tb->buf_lock);
+	tb->tx_buf[0] = reg_addr;
+	tb->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&tb->buf_lock);
+
+	return err;
+}
+
+static const struct st_sensor_transfer_function st_sensors_tf_spi = {
+	.read_byte = st_sensors_spi_read_byte,
+	.write_byte = st_sensors_spi_write_byte,
+	.read_multiple_byte = st_sensors_spi_read_multiple_byte,
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_spi;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..1c69c63
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+static struct iio_trigger_ops st_sensors_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+		int (*set_trigger_state)(struct iio_trigger *trig, bool state),
+		int (*try_reenable)(struct iio_trigger *trig))
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	st_sensors_trigger_ops.set_trigger_state = set_trigger_state;
+	st_sensors_trigger_ops.try_reenable = try_reenable;
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = &st_sensors_trigger_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..b92f543
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,281 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_MAX_NAME			17
+#define ST_SENSORS_MAX_4WAI			7
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+/**
+ * struct st_sensor_bdu - ST sensor device block data update
+ * @addr: address of the register.
+ * @mask: mask to write the block data update flag.
+ */
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
+ * @addr: address of the register.
+ * @mask: mask to write the on/off value.
+ * struct ig1 - represents the Interrupt Generator 1 of sensors.
+ * @en_addr: address of the enable ig1 register.
+ * @en_mask: mask to write the on/off value for enable.
+ */
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct {
+		u8 en_addr;
+		u8 en_mask;
+	} ig1;
+};
+
+/**
+ * struct st_sensor_transfer_buffer - ST sensor device I/O buffer
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ */
+struct st_sensor_transfer_buffer {
+	struct mutex buf_lock;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT];
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+};
+
+/**
+ * struct st_sensor_transfer_function - ST sensor device I/O function
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer_function {
+	int (*read_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer_buffer *tb,
+				struct device *dev, u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @current_fullscale: Maximum range of measure by the sensor.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @odr: Output data rate of the sensor [Hz].
+ * @get_irq_data_ready: Function to get the IRQ used for data ready signal.
+ * @tf: Transfer function structure used by I/O operations.
+ * @tb: Transfer buffers and mutex used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+	struct st_sensor_fullscale_avl *current_fullscale;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int odr;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+
+	struct st_sensor_transfer_function *tf;
+	struct st_sensor_transfer_buffer tb;
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and ODR list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and full scale 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] multi-read.
+ * @bootime: samples to discard when sensor passing from power-down to power-up.
+ */
+struct st_sensors {
+	u8 wai;
+	char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+	unsigned int bootime;
+};
+
+#ifdef CONFIG_ST_SENSORS_SPI
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_ST_SENSORS_I2C
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev);
+#endif
+
+#ifdef CONFIG_ST_SENSORS_TRIGGERED_BUFFER
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+		int (*set_trigger_state)(struct iio_trigger *trig, bool state),
+		int (*try_reenable)(struct iio_trigger *trig));
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+#endif
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+					u8 mask, short num_bit, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_scale_avl(struct iio_dev *indio_dev,
+	const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
+	const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
+
+#endif /* ST_SENSORS_H */
-- 
1.7.0.4

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

* Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2012-12-13 13:11 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
@ 2012-12-16 12:15   ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2012-12-16 12:15 UTC (permalink / raw)
  To: Denis CIOCCA; +Cc: lars, linux-iio

On 12/13/2012 01:11 PM, Denis CIOCCA wrote:
> This patch add generic library for STMicroelectronics 3-axis sensors.

Nice piece of work.  Couple of little bits and bobs, but basically there
as far as I am concerned...
(just the spi buffer alignment stuff and it really is just a case of
making sure they are the last element in the chunk of memory created
as mallocs are always a whole number of cachelines.)

> ---
>  drivers/iio/common/Kconfig                         |    1 +
>  drivers/iio/common/Makefile                        |    1 +
>  drivers/iio/common/st_sensors/Kconfig              |   30 ++
>  drivers/iio/common/st_sensors/Makefile             |    9 +
>  drivers/iio/common/st_sensors/st_sensors_buffer.c  |  115 +++++++
>  drivers/iio/common/st_sensors/st_sensors_core.c    |  335 ++++++++++++++++++++
>  drivers/iio/common/st_sensors/st_sensors_i2c.c     |   78 +++++
>  drivers/iio/common/st_sensors/st_sensors_spi.c     |  124 +++++++
>  drivers/iio/common/st_sensors/st_sensors_trigger.c |   76 +++++
>  include/linux/iio/common/st_sensors.h              |  246 ++++++++++++++
>  10 files changed, 1015 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/iio/common/st_sensors/Kconfig
>  create mode 100644 drivers/iio/common/st_sensors/Makefile
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
>  create mode 100644 include/linux/iio/common/st_sensors.h
>
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index ed45ee5..0b6e97d 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,3 +3,4 @@
>  #
>
>  source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 8158400..c2352be 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,3 +7,4 @@
>  #
>
>  obj-y += hid-sensors/
> +obj-y += st_sensors/
> diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
> new file mode 100644
> index 0000000..ddcfcc6
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Kconfig
> @@ -0,0 +1,30 @@
> +#
> +# Hid Sensor common modules
> +#
> +menu "STMicroelectronics sensors library"
> +
> +config ST_SENSORS_CORE
> +	tristate "Common modules for all ST sensors IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common core functions.
> +
> +config ST_SENSORS_I2C
> +	tristate "I2C common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common i2c functions.
> +
> +config ST_SENSORS_SPI
> +	tristate "SPI common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common spi functions.
> +
> +config ST_SENSORS_TRIGGERED_BUFFER
> +	tristate "trigger and buffer common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common trigger and buffer functions.
> +
> +endmenu
> diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
> new file mode 100644
> index 0000000..a191e65
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for the STMicroelectronics sensor common modules.
> +#
> +
> +obj-$(CONFIG_ST_SENSORS_CORE) += st_sensors_core.o
> +obj-$(CONFIG_ST_SENSORS_I2C) += st_sensors_i2c.o
> +obj-$(CONFIG_ST_SENSORS_SPI) += st_sensors_spi.o
> +obj-$(CONFIG_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o
> +st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
> diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> new file mode 100644
> index 0000000..b636751
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> @@ -0,0 +1,115 @@
> +/*
> + * STMicroelectronics sensors buffer library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/irqreturn.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int i, n = 0, len;
> +	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
> +		if (test_bit(i, indio_dev->active_scan_mask)) {
> +			addr[n] = indio_dev->channels[i].address;
> +			n++;
> +		}
> +	}
> +	switch (n) {
> +	case 1:
> +		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
> +			sdata->multiread_bit);
> +		break;
> +	case 2:
> +		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
> +			len = sdata->tf.read_multiple_byte(&sdata->tf,
> +					sdata->dev, addr[0],
> +					ST_SENSORS_BYTE_FOR_CHANNEL*n,
> +					buf, sdata->multiread_bit);
> +		} else {
> +			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
> +				    ST_SENSORS_NUMBER_DATA_CHANNELS];
> +			len = sdata->tf.read_multiple_byte(&sdata->tf,
> +				sdata->dev, addr[0],
> +				ST_SENSORS_BYTE_FOR_CHANNEL*
> +				ST_SENSORS_NUMBER_DATA_CHANNELS,
> +				rx_array, sdata->multiread_bit);
> +			if (len < 0)
> +				goto read_data_channels_error;
> +
> +			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
> +									i++) {
> +				if (i < n)
> +					buf[i] = rx_array[i];
> +				else
> +					buf[i] = rx_array[n + i];
> +			}
> +			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
> +		}
> +		break;
> +	case 3:
> +		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
> +			ST_SENSORS_NUMBER_DATA_CHANNELS,
> +			buf, sdata->multiread_bit);
> +		break;
> +	default:
> +		len = -EINVAL;
> +		goto read_data_channels_error;
> +	}
> +	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
> +		len = -EIO;
> +		goto read_data_channels_error;
> +	}
> +
> +read_data_channels_error:
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_buffer_element);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p)
> +{
> +	int len;
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
> +	if (len < 0)
> +		goto st_sensors_get_buffer_element_error;
> +
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)sdata->buffer_data +
> +				ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +	iio_push_to_buffers(indio_dev, sdata->buffer_data);
> +
> +st_sensors_get_buffer_element_error:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL(st_sensors_trigger_handler);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
> new file mode 100644
> index 0000000..2b00ed7
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_core.c
> @@ -0,0 +1,335 @@
> +/*
> + * STMicroelectronics sensors core library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_WAI_ADDRESS		0x0f
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +						u8 mask, short num_bit, u8 data)
> +{
> +	int err;
> +	u8 new_data;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf.read_byte(&sdata->tf, sdata->dev, reg_addr, &new_data);
> +	if (err < 0)
> +		goto st_sensors_write_data_with_mask_error;
> +
> +	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +	err = sdata->tf.write_byte(&sdata->tf, sdata->dev, reg_addr, new_data);
> +
> +st_sensors_write_data_with_mask_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_write_data_with_mask);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if (odr_avl[i].hz == 0)
> +			break;
> +
> +		len += sprintf(buf + len, "%d ", odr_avl[i].hz);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
> +
> +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
> +		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if (fullscale_avl[i].num == 0)
> +			break;
> +
> +		len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_fullscale_avl);
> +
> +static int st_sensors_match_odr(const struct st_sensors *sensor,
> +			unsigned int odr, struct st_sensor_odr_avl *odr_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if ((sensor->odr.odr_avl[i].hz == odr) &&
> +					(sensor->odr.odr_avl[i].hz != 0)) {
> +			odr_out->hz = sensor->odr.odr_avl[i].hz;
> +			odr_out->value = sensor->odr.odr_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct st_sensor_odr_avl odr_out;
> +
> +	err = st_sensors_match_odr(sensor, odr, &odr_out);
> +	if (err < 0)
> +		goto st_sensors_match_odr_error;
> +
> +	if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +		if (sdata->enabled == true) {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->odr.addr, sensor->odr.mask,
> +				sensor->odr.num_bit,
> +				odr_out.value);
> +		} else {
> +			err = 0;
> +		}
> +	} else {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->odr.addr, sensor->odr.mask,
> +			sensor->odr.num_bit, odr_out.value);
> +	}
> +	if (err >= 0)
> +		sdata->odr = odr_out.hz;
> +
> +st_sensors_match_odr_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_odr);
> +
> +static int st_sensors_match_fs(const struct st_sensors *sensor,
> +			unsigned int fs, struct st_sensor_fullscale_avl *fs_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if ((sensor->fs.fs_avl[i].num == fs) &&
> +					(sensor->fs.fs_avl[i].num != 0)) {
> +			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;
  			why not
			fs_out = &sensor->fs.fs_avl[i];
			(obviously with the relevant changes to the call sites.)

> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct st_sensor_fullscale_avl fs_out;
> +
> +	err = st_sensors_match_fs(sensor, fs, &fs_out);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	err = st_sensors_write_data_with_mask(indio_dev, sensor->fs.addr,
> +		sensor->fs.mask, sensor->fs.num_bit, sensor->fs.fs_avl->value);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	sdata->fullscale = sensor->fs.fs_avl->num;
> +	sdata->gain = sensor->fs.fs_avl->gain;
> +	sdata->gain2 = sensor->fs.fs_avl->gain2;
Is there any reason not to just have fs_avl in sdata
so have a struct st_sensor_fullscale_avl *fs_avl in sdata and
then just set the pointer here?

> +	return err;
> +
> +st_accel_set_fullscale_error:
> +	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err = -EINVAL;
> +	bool found;
> +	u8 tmp_value;
> +	struct st_sensor_odr_avl odr_out;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	if (enable) {
> +		found = false;
> +		tmp_value = sensor->pw.value_on;
> +		if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +			err = st_sensors_match_odr(sensor,
> +							sdata->odr, &odr_out);
> +			if (err < 0)
> +				goto set_enable_error;
> +			tmp_value = odr_out.value;
> +			found = true;
> +		}
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask,
> +						sensor->pw.num_bit, tmp_value);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = true;
> +		if (found)
> +			sdata->odr = odr_out.hz;
> +	} else {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask,
> +				sensor->pw.num_bit, sensor->pw.value_off);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = false;
> +	}
> +
> +set_enable_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable)
> +{
> +	return st_sensors_write_data_with_mask(indio_dev,
> +		sensor->enable_axis.addr, sensor->enable_axis.mask,
> +		3, axis_enable);
> +}
> +EXPORT_SYMBOL(st_sensors_set_axis_enable);
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +						const struct st_sensors *sensor)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = st_sensors_set_enable(indio_dev, sensor, false);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev, sensor, sdata->fullscale);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
> +	if (err < 0)
> +		goto init_error;
> +
> +	/* set BDU */
> +	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
> +						sensor->bdu.mask, 1, true);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_axis_enable(indio_dev, sensor,
> +						ST_SENSORS_ENABLE_ALL_CHANNELS);
> +
> +init_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_init_sensor);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err;
> +
I'd like to see some comments in here as the flow / naming is not entirely
obvious.
> +	if (sensor->drdy_irq.ig1.en_addr > 0) {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->drdy_irq.ig1.en_addr,
> +				sensor->drdy_irq.ig1.en_mask, 1, (int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	if (sensor->drdy_irq.ig1.latch_mask_addr > 0) {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->drdy_irq.ig1.latch_mask_addr,
> +				sensor->drdy_irq.ig1.latching_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->drdy_irq.addr,
> +			sensor->drdy_irq.mask, 1, (int)enable);
> +
> +st_accel_set_dataready_irq_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_dataready_irq);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale)
> +{
> +	int err = -EINVAL, i;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if ((sensor->fs.fs_avl[i].gain == scale) &&
> +					(sensor->fs.fs_avl[i].gain != 0)) {
> +			err = 0;
> +			break;
> +		}
> +	}
> +	if (err < 0)
> +		goto st_sensors_match_scale_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev,
> +					sensor, sensor->fs.fs_avl[i].num);
> +
> +st_sensors_match_scale_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
> +
> +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
> +{
> +	int err;
> +	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
> +						outdata, sdata->multiread_bit);
> +	if (err < 0)
> +		goto read_error;
> +
> +	*data = ((s16)le16_to_cpup((u16 *)outdata));
> +
> +read_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_read_axis_data);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> new file mode 100644
> index 0000000..fa4bc7d
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> @@ -0,0 +1,78 @@
> +/*
> + * STMicroelectronics sensors i2c library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_I2C_MULTIREAD	0x80
> +
> +unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct i2c_client *client = to_i2c_client(sdata->dev);
> +
> +	return client->irq;
> +}
> +
> +static int st_sensors_i2c_read_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	int err;
> +
> +	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
> +	if (err < 0)
> +		goto st_accel_i2c_read_byte_error;
> +
> +	*res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +	return err < 0 ? err : 0;
> +}
> +
> +static int st_sensors_i2c_read_multiple_byte(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	if (multiread_bit == true)
> +		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
> +
> +	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
> +							reg_addr, len, data);
> +}
> +
> +static int st_sensors_i2c_write_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
> +}
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata)
> +{
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->name = client->name;
> +
> +	mutex_init(&sdata->tf.buf_lock);
> +	sdata->tf.read_byte = st_sensors_i2c_read_byte;
> +	sdata->tf.write_byte = st_sensors_i2c_write_byte;
> +	sdata->tf.read_multiple_byte = st_sensors_i2c_read_multiple_byte;
> +	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
I review backwards through code so the same comments as written for the
spi equivalent apply here...

> +}
> +EXPORT_SYMBOL(st_sensors_i2c_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
> new file mode 100644
> index 0000000..82fa633
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
> @@ -0,0 +1,124 @@
> +/*
> + * STMicroelectronics sensors spi library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_SPI_MULTIREAD	0xc0
> +#define ST_SENSORS_SPI_READ		0x80
> +
> +unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct spi_device *spi = to_spi_device(sdata->dev);
> +
> +	return spi->irq;
I'd roll this up slightly and drop the intermediate variable.

return to_spi_device(sdata->dev)->irq;

> +}
> +
> +static int st_sensors_spi_read(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx_buf,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
> +		{
> +			.rx_buf = st->rx_buf,
> +			.bits_per_word = 8,
> +			.len = len,
> +		}
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	if ((multiread_bit == true) && (len > 1))
> +		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
> +	else
> +		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	if (err)
> +		goto acc_spi_read_error;
> +
> +	memcpy(data, st->rx_buf, len*sizeof(u8));
> +	mutex_unlock(&st->buf_lock);
> +	return len;
> +
> +acc_spi_read_error:
> +	return err;
> +}
> +
> +static int st_sensors_spi_read_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	return st_sensors_spi_read(st, dev, reg_addr, 1, res_byte, false);
> +}
> +
> +static int st_sensors_spi_read_multiple_byte(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	return st_sensors_spi_read(st, dev, reg_addr, len, data, multiread_bit);
> +}
> +
> +static int st_sensors_spi_write_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers = {
> +		.tx_buf = st->tx_buf,
> +		.bits_per_word = 8,
> +		.len = 2,
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx_buf[0] = reg_addr;
> +	st->tx_buf[1] = data;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers, &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return err;
> +}
> +
> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata)
> +{
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi->modalias;
> +
> +	mutex_init(&sdata->tf.buf_lock);

Silly though it may seem I thik I'd prefer the mutex init in
st_sensors_init_sensor as it isn't spi or i2c specific whereas
everything else in here is...

> +	sdata->tf.read_byte = st_sensors_spi_read_byte;
> +	sdata->tf.write_byte = st_sensors_spi_write_byte;
> +	sdata->tf.read_multiple_byte = st_sensors_spi_read_multiple_byte;

Might be worth wrapping these callbacks up in a const static structure rather
than along side the dynamic data in tf. (like we do with struct iio_info within
struct iio_dev)

> +	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
> +}
> +EXPORT_SYMBOL(st_sensors_spi_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> new file mode 100644
> index 0000000..6d43b13
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> @@ -0,0 +1,76 @@
> +/*
> + * STMicroelectronics sensors trigger library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					const struct iio_trigger_ops *trig_ops)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +	if (sdata->trig == NULL) {
> +		err = -ENOMEM;
> +		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +		goto iio_trigger_alloc_error;
> +	}
> +
> +	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
> +			iio_trigger_generic_data_rdy_poll,
> +			NULL,
> +			IRQF_TRIGGER_RISING,
> +			sdata->trig->name,
> +			sdata->trig);
> +	if (err)
> +		goto request_irq_error;
> +
> +	sdata->trig->private_data = indio_dev;
> +	sdata->trig->ops = trig_ops;
> +	sdata->trig->dev.parent = sdata->dev;
> +
> +	err = iio_trigger_register(sdata->trig);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +		goto iio_trigger_register_error;
> +	}
> +	indio_dev->trig = sdata->trig;
> +
> +	return 0;
> +
> +iio_trigger_register_error:
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +request_irq_error:
> +	iio_trigger_free(sdata->trig);
> +iio_trigger_alloc_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_allocate_trigger);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	iio_trigger_unregister(sdata->trig);
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +	iio_trigger_free(sdata->trig);
> +}
> +EXPORT_SYMBOL(st_sensors_deallocate_trigger);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
> new file mode 100644
> index 0000000..c0f2acc
> --- /dev/null
> +++ b/include/linux/iio/common/st_sensors.h
> @@ -0,0 +1,246 @@
> +/*
> + * STMicroelectronics sensors library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@st.com>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_H
> +#define ST_SENSORS_H
> +
> +#include <linux/i2c.h>
> +#include <linux/spi/spi.h>
> +#include <linux/irqreturn.h>
> +#include <linux/iio/trigger.h>
> +
> +
> +#define ST_SENSORS_TX_MAX_LENGHT		2
> +#define ST_SENSORS_RX_MAX_LENGHT		6
> +
> +#define ST_SENSORS_ODR_LIST_MAX			10
> +#define ST_SENSORS_FULLSCALE_AVL_MAX		10
> +
> +#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
> +#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
> +#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
> +#define ST_SENSORS_BYTE_FOR_CHANNEL		2
> +#define ST_SENSORS_SCAN_X			0
> +#define ST_SENSORS_SCAN_Y			1
> +#define ST_SENSORS_SCAN_Z			2
> +#define ST_SENSORS_DEFAULT_12_REALBITS		12
> +#define ST_SENSORS_DEFAULT_16_REALBITS		16
> +#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
> +#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
> +#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
> +#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
> +#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
> +#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
> +
> +#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
> +{ \
> +	.type = device_type, \
> +	.modified = 1, \
> +	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +	.scan_index = index, \
> +	.channel2 = mod, \
> +	.address = addr, \
> +	.scan_type = { \
> +		.sign = 's', \
> +		.realbits = bits, \
> +		.shift = 16 - bits, \
> +		.storagebits = 16, \
> +		.endianness = endian, \
> +	}, \
> +}
> +
> +struct st_sensor_odr_avl {
> +	unsigned int hz;
> +	u8 value;
> +};
> +
> +struct st_sensor_odr {
> +	u8 addr;
> +	u8 mask;
> +	short num_bit;
> +	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
> +};
> +
> +struct st_sensor_power {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	u8 value_off;
> +	u8 value_on;
> +};
> +
> +struct st_sensor_axis {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_sensor_fullscale_avl {
> +	unsigned int num;
> +	u8 value;
> +	unsigned int gain;
> +	unsigned int gain2;
> +};
> +
> +struct st_sensor_fullscale {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_sensor_bdu {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_sensor_interrupt_generator {
> +	u8 en_addr;
> +	u8 latch_mask_addr;
> +	u8 en_mask;
> +	u8 latching_mask;
> +};
> +
> +struct st_sensor_data_ready_irq {
> +	u8 addr;
> +	u8 mask;
> +	struct st_sensor_interrupt_generator ig1;
I'd just embed the definition in here
i.e. (assuming I remember the syntax correctly...)

struct st_sensor_data_read_irq {
       u8 addr;
       u8 mask;
       struct {
       	      u8 en_addr;
	      u8 latch_mask_addr;
	      u8 en_mask;
	      u8 latching_mask;
       } ig1;
};
> +};
> +
> +/**
> + * struct st_sensor_transfer - ST sensor device I/O struct
> + * @buf_lock: Mutex to protect rx and tx buffers.
> + * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + */
> +struct st_sensor_transfer {
> +	struct mutex buf_lock;
Move these to the end of the structure and all is fine
(as long as this is the last structure in any containing one as well -
see below).

Also only need to force alignment of the tx_buf
(as the rx_buf will pack after it and any hardware
that corrupts that is plain mad)

> +	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
> +	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned;
> +	int (*read_byte) (struct st_sensor_transfer *st, struct device *dev,
> +						u8 reg_addr, u8 *res_byte);
> +	int (*write_byte) (struct st_sensor_transfer *st, struct device *dev,
> +						u8 reg_addr, u8 data);
> +	int (*read_multiple_byte) (struct st_sensor_transfer *st,
> +		struct device *dev, u8 reg_addr, int len, u8 *data,
> +							bool multiread_bit);
> +};
> +
> +/**
> + * struct st_sensor_data - ST sensor device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the st_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [m/s^2/LSB].
> + * @odr: Output data rate of the sensor [Hz].
> + * @tf: Transfer function structure used by I/O operations.
> + */
> +struct st_sensor_data {
> +	struct device *dev;
> +	struct iio_trigger *trig;
> +
> +	bool enabled;
> +	bool multiread_bit;
> +
> +	short index;
> +
> +	char *buffer_data;
> +
> +	unsigned int fullscale;
> +	unsigned int gain;
> +	unsigned int gain2;
> +	unsigned int odr;
> +
This will need to also be at the end of this structure to ensure
that the below callback pointer doesn't end up on the same cacheline as
the tx_buf and rx_buf.  A comment saying that this is a requirement here
will also make future bugs much less likely.
> +	struct st_sensor_transfer tf;
> +
> +	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
> +};
> +
> +/**
> + * struct st_sensors - ST sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + */
> +struct st_sensors {
> +	u8 wai;
> +	struct iio_chan_spec *ch;
> +	struct st_sensor_odr odr;
> +	struct st_sensor_power pw;
> +	struct st_sensor_axis enable_axis;
> +	struct st_sensor_fullscale fs;
> +	struct st_sensor_bdu bdu;
> +	struct st_sensor_data_ready_irq drdy_irq;
> +	bool multi_read_bit;
> +};
> +
Either break these out to separate headers or deal with the fact that
spi or i2c might not be present in a given build with ifdef fun and
games.

> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata);
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata);
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +					u8 mask, short num_bit, u8 data);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf);
> +
> +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
> +	const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr);
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable);
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +					const struct st_sensors *sensor);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale);
> +
> +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					const struct iio_trigger_ops *trig_ops);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p);
> +
> +#endif /* ST_SENSORS_H */
>

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

* [PATCH 1/4] iio:common: Add STMicroelectronics common library
  2012-12-13 13:11 STMicroelectronics sensors driver Denis CIOCCA
@ 2012-12-13 13:11 ` Denis CIOCCA
  2012-12-16 12:15   ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Denis CIOCCA @ 2012-12-13 13:11 UTC (permalink / raw)
  To: jic23, lars; +Cc: linux-iio, Denis Ciocca

This patch add generic library for STMicroelectronics 3-axis sensors.
---
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/st_sensors/Kconfig              |   30 ++
 drivers/iio/common/st_sensors/Makefile             |    9 +
 drivers/iio/common/st_sensors/st_sensors_buffer.c  |  115 +++++++
 drivers/iio/common/st_sensors/st_sensors_core.c    |  335 ++++++++++++++++++++
 drivers/iio/common/st_sensors/st_sensors_i2c.c     |   78 +++++
 drivers/iio/common/st_sensors/st_sensors_spi.c     |  124 +++++++
 drivers/iio/common/st_sensors/st_sensors_trigger.c |   76 +++++
 include/linux/iio/common/st_sensors.h              |  246 ++++++++++++++
 10 files changed, 1015 insertions(+), 0 deletions(-)
 create mode 100644 drivers/iio/common/st_sensors/Kconfig
 create mode 100644 drivers/iio/common/st_sensors/Makefile
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
 create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
 create mode 100644 include/linux/iio/common/st_sensors.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index ed45ee5..0b6e97d 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,3 +3,4 @@
 #
 
 source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 8158400..c2352be 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-y += hid-sensors/
+obj-y += st_sensors/
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
new file mode 100644
index 0000000..ddcfcc6
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -0,0 +1,30 @@
+#
+# Hid Sensor common modules
+#
+menu "STMicroelectronics sensors library"
+
+config ST_SENSORS_CORE
+	tristate "Common modules for all ST sensors IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common core functions.
+
+config ST_SENSORS_I2C
+	tristate "I2C common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common i2c functions.
+
+config ST_SENSORS_SPI
+	tristate "SPI common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common spi functions.
+
+config ST_SENSORS_TRIGGERED_BUFFER
+	tristate "trigger and buffer common library for ST Sensor IIO drivers"
+	help
+	  Say yes here to build support for ST sensors to use
+	  common trigger and buffer functions.
+
+endmenu
diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
new file mode 100644
index 0000000..a191e65
--- /dev/null
+++ b/drivers/iio/common/st_sensors/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the STMicroelectronics sensor common modules.
+#
+
+obj-$(CONFIG_ST_SENSORS_CORE) += st_sensors_core.o
+obj-$(CONFIG_ST_SENSORS_I2C) += st_sensors_i2c.o
+obj-$(CONFIG_ST_SENSORS_SPI) += st_sensors_spi.o
+obj-$(CONFIG_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o
+st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
new file mode 100644
index 0000000..b636751
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -0,0 +1,115 @@
+/*
+ * STMicroelectronics sensors buffer library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
+{
+	int i, n = 0, len;
+	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			addr[n] = indio_dev->channels[i].address;
+			n++;
+		}
+	}
+	switch (n) {
+	case 1:
+		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
+			sdata->multiread_bit);
+		break;
+	case 2:
+		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
+			len = sdata->tf.read_multiple_byte(&sdata->tf,
+					sdata->dev, addr[0],
+					ST_SENSORS_BYTE_FOR_CHANNEL*n,
+					buf, sdata->multiread_bit);
+		} else {
+			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
+				    ST_SENSORS_NUMBER_DATA_CHANNELS];
+			len = sdata->tf.read_multiple_byte(&sdata->tf,
+				sdata->dev, addr[0],
+				ST_SENSORS_BYTE_FOR_CHANNEL*
+				ST_SENSORS_NUMBER_DATA_CHANNELS,
+				rx_array, sdata->multiread_bit);
+			if (len < 0)
+				goto read_data_channels_error;
+
+			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
+									i++) {
+				if (i < n)
+					buf[i] = rx_array[i];
+				else
+					buf[i] = rx_array[n + i];
+			}
+			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
+		}
+		break;
+	case 3:
+		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
+			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
+			ST_SENSORS_NUMBER_DATA_CHANNELS,
+			buf, sdata->multiread_bit);
+		break;
+	default:
+		len = -EINVAL;
+		goto read_data_channels_error;
+	}
+	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
+		len = -EIO;
+		goto read_data_channels_error;
+	}
+
+read_data_channels_error:
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_buffer_element);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p)
+{
+	int len;
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
+	if (len < 0)
+		goto st_sensors_get_buffer_element_error;
+
+	if (indio_dev->scan_timestamp)
+		*(s64 *)((u8 *)sdata->buffer_data +
+				ALIGN(len, sizeof(s64))) = pf->timestamp;
+
+	iio_push_to_buffers(indio_dev, sdata->buffer_data);
+
+st_sensors_get_buffer_element_error:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(st_sensors_trigger_handler);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
new file mode 100644
index 0000000..2b00ed7
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -0,0 +1,335 @@
+/*
+ * STMicroelectronics sensors core library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_WAI_ADDRESS		0x0f
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+						u8 mask, short num_bit, u8 data)
+{
+	int err;
+	u8 new_data;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf.read_byte(&sdata->tf, sdata->dev, reg_addr, &new_data);
+	if (err < 0)
+		goto st_sensors_write_data_with_mask_error;
+
+	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
+	err = sdata->tf.write_byte(&sdata->tf, sdata->dev, reg_addr, new_data);
+
+st_sensors_write_data_with_mask_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_write_data_with_mask);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if (odr_avl[i].hz == 0)
+			break;
+
+		len += sprintf(buf + len, "%d ", odr_avl[i].hz);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
+
+int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
+		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
+{
+	int i, len = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if (fullscale_avl[i].num == 0)
+			break;
+
+		len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain);
+	}
+	mutex_unlock(&indio_dev->mlock);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+EXPORT_SYMBOL(st_sensors_get_fullscale_avl);
+
+static int st_sensors_match_odr(const struct st_sensors *sensor,
+			unsigned int odr, struct st_sensor_odr_avl *odr_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
+		if ((sensor->odr.odr_avl[i].hz == odr) &&
+					(sensor->odr.odr_avl[i].hz != 0)) {
+			odr_out->hz = sensor->odr.odr_avl[i].hz;
+			odr_out->value = sensor->odr.odr_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_odr_avl odr_out;
+
+	err = st_sensors_match_odr(sensor, odr, &odr_out);
+	if (err < 0)
+		goto st_sensors_match_odr_error;
+
+	if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+		if (sdata->enabled == true) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->odr.addr, sensor->odr.mask,
+				sensor->odr.num_bit,
+				odr_out.value);
+		} else {
+			err = 0;
+		}
+	} else {
+		err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->odr.addr, sensor->odr.mask,
+			sensor->odr.num_bit, odr_out.value);
+	}
+	if (err >= 0)
+		sdata->odr = odr_out.hz;
+
+st_sensors_match_odr_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_odr);
+
+static int st_sensors_match_fs(const struct st_sensors *sensor,
+			unsigned int fs, struct st_sensor_fullscale_avl *fs_out)
+{
+	int i, ret = -EINVAL;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].num == fs) &&
+					(sensor->fs.fs_avl[i].num != 0)) {
+			fs_out->num = sensor->fs.fs_avl[i].num;
+			fs_out->gain = sensor->fs.fs_avl[i].gain;
+			fs_out->value = sensor->fs.fs_avl[i].value;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct st_sensor_fullscale_avl fs_out;
+
+	err = st_sensors_match_fs(sensor, fs, &fs_out);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->fs.addr,
+		sensor->fs.mask, sensor->fs.num_bit, sensor->fs.fs_avl->value);
+	if (err < 0)
+		goto st_accel_set_fullscale_error;
+
+	sdata->fullscale = sensor->fs.fs_avl->num;
+	sdata->gain = sensor->fs.fs_avl->gain;
+	sdata->gain2 = sensor->fs.fs_avl->gain2;
+	return err;
+
+st_accel_set_fullscale_error:
+	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err = -EINVAL;
+	bool found;
+	u8 tmp_value;
+	struct st_sensor_odr_avl odr_out;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	if (enable) {
+		found = false;
+		tmp_value = sensor->pw.value_on;
+		if ((sensor->odr.addr == sensor->pw.addr) &&
+				(sensor->odr.mask == sensor->pw.mask)) {
+			err = st_sensors_match_odr(sensor,
+							sdata->odr, &odr_out);
+			if (err < 0)
+				goto set_enable_error;
+			tmp_value = odr_out.value;
+			found = true;
+		}
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+						sensor->pw.num_bit, tmp_value);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = true;
+		if (found)
+			sdata->odr = odr_out.hz;
+	} else {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->pw.addr, sensor->pw.mask,
+				sensor->pw.num_bit, sensor->pw.value_off);
+		if (err < 0)
+			goto set_enable_error;
+		sdata->enabled = false;
+	}
+
+set_enable_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable)
+{
+	return st_sensors_write_data_with_mask(indio_dev,
+		sensor->enable_axis.addr, sensor->enable_axis.mask,
+		3, axis_enable);
+}
+EXPORT_SYMBOL(st_sensors_set_axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+						const struct st_sensors *sensor)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = st_sensors_set_enable(indio_dev, sensor, false);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_fullscale(indio_dev, sensor, sdata->fullscale);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
+	if (err < 0)
+		goto init_error;
+
+	/* set BDU */
+	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
+						sensor->bdu.mask, 1, true);
+	if (err < 0)
+		goto init_error;
+
+	err = st_sensors_set_axis_enable(indio_dev, sensor,
+						ST_SENSORS_ENABLE_ALL_CHANNELS);
+
+init_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_init_sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable)
+{
+	int err;
+
+	if (sensor->drdy_irq.ig1.en_addr > 0) {
+		err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->drdy_irq.ig1.en_addr,
+				sensor->drdy_irq.ig1.en_mask, 1, (int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	if (sensor->drdy_irq.ig1.latch_mask_addr > 0) {
+			err = st_sensors_write_data_with_mask(indio_dev,
+				sensor->drdy_irq.ig1.latch_mask_addr,
+				sensor->drdy_irq.ig1.latching_mask, 1,
+			(int)enable);
+		if (err < 0)
+			goto st_accel_set_dataready_irq_error;
+	}
+
+	err = st_sensors_write_data_with_mask(indio_dev,
+			sensor->drdy_irq.addr,
+			sensor->drdy_irq.mask, 1, (int)enable);
+
+st_accel_set_dataready_irq_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_dataready_irq);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale)
+{
+	int err = -EINVAL, i;
+
+	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
+		if ((sensor->fs.fs_avl[i].gain == scale) &&
+					(sensor->fs.fs_avl[i].gain != 0)) {
+			err = 0;
+			break;
+		}
+	}
+	if (err < 0)
+		goto st_sensors_match_scale_error;
+
+	err = st_sensors_set_fullscale(indio_dev,
+					sensor, sensor->fs.fs_avl[i].num);
+
+st_sensors_match_scale_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
+{
+	int err;
+	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	err = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
+			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
+						outdata, sdata->multiread_bit);
+	if (err < 0)
+		goto read_error;
+
+	*data = ((s16)le16_to_cpup((u16 *)outdata));
+
+read_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_read_axis_data);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
new file mode 100644
index 0000000..fa4bc7d
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -0,0 +1,78 @@
+/*
+ * STMicroelectronics sensors i2c library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_I2C_MULTIREAD	0x80
+
+unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct i2c_client *client = to_i2c_client(sdata->dev);
+
+	return client->irq;
+}
+
+static int st_sensors_i2c_read_byte(struct st_sensor_transfer *st,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	int err;
+
+	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
+	if (err < 0)
+		goto st_accel_i2c_read_byte_error;
+
+	*res_byte = err & 0xff;
+
+st_accel_i2c_read_byte_error:
+	return err < 0 ? err : 0;
+}
+
+static int st_sensors_i2c_read_multiple_byte(struct st_sensor_transfer *st,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	if (multiread_bit == true)
+		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
+
+	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
+							reg_addr, len, data);
+}
+
+static int st_sensors_i2c_write_byte(struct st_sensor_transfer *st,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
+}
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata)
+{
+	i2c_set_clientdata(client, indio_dev);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = client->name;
+
+	mutex_init(&sdata->tf.buf_lock);
+	sdata->tf.read_byte = st_sensors_i2c_read_byte;
+	sdata->tf.write_byte = st_sensors_i2c_write_byte;
+	sdata->tf.read_multiple_byte = st_sensors_i2c_read_multiple_byte;
+	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_i2c_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
new file mode 100644
index 0000000..82fa633
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -0,0 +1,124 @@
+/*
+ * STMicroelectronics sensors spi library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+
+#define ST_SENSORS_SPI_MULTIREAD	0xc0
+#define ST_SENSORS_SPI_READ		0x80
+
+unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+	struct spi_device *spi = to_spi_device(sdata->dev);
+
+	return spi->irq;
+}
+
+static int st_sensors_spi_read(struct st_sensor_transfer *st,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx_buf,
+			.bits_per_word = 8,
+			.len = 1,
+		},
+		{
+			.rx_buf = st->rx_buf,
+			.bits_per_word = 8,
+			.len = len,
+		}
+	};
+
+	mutex_lock(&st->buf_lock);
+	if ((multiread_bit == true) && (len > 1))
+		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
+	else
+		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	if (err)
+		goto acc_spi_read_error;
+
+	memcpy(data, st->rx_buf, len*sizeof(u8));
+	mutex_unlock(&st->buf_lock);
+	return len;
+
+acc_spi_read_error:
+	return err;
+}
+
+static int st_sensors_spi_read_byte(struct st_sensor_transfer *st,
+				struct device *dev, u8 reg_addr, u8 *res_byte)
+{
+	return st_sensors_spi_read(st, dev, reg_addr, 1, res_byte, false);
+}
+
+static int st_sensors_spi_read_multiple_byte(struct st_sensor_transfer *st,
+	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
+{
+	return st_sensors_spi_read(st, dev, reg_addr, len, data, multiread_bit);
+}
+
+static int st_sensors_spi_write_byte(struct st_sensor_transfer *st,
+				struct device *dev, u8 reg_addr, u8 data)
+{
+	struct spi_message msg;
+	int err;
+
+	struct spi_transfer xfers = {
+		.tx_buf = st->tx_buf,
+		.bits_per_word = 8,
+		.len = 2,
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx_buf[0] = reg_addr;
+	st->tx_buf[1] = data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers, &msg);
+	err = spi_sync(to_spi_device(dev), &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return err;
+}
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata)
+{
+	spi_set_drvdata(spi, indio_dev);
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi->modalias;
+
+	mutex_init(&sdata->tf.buf_lock);
+	sdata->tf.read_byte = st_sensors_spi_read_byte;
+	sdata->tf.write_byte = st_sensors_spi_write_byte;
+	sdata->tf.read_multiple_byte = st_sensors_spi_read_multiple_byte;
+	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+}
+EXPORT_SYMBOL(st_sensors_spi_configure);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
new file mode 100644
index 0000000..6d43b13
--- /dev/null
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -0,0 +1,76 @@
+/*
+ * STMicroelectronics sensors trigger library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/common/st_sensors.h>
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					const struct iio_trigger_ops *trig_ops)
+{
+	int err;
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+	if (sdata->trig == NULL) {
+		err = -ENOMEM;
+		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
+		goto iio_trigger_alloc_error;
+	}
+
+	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
+			iio_trigger_generic_data_rdy_poll,
+			NULL,
+			IRQF_TRIGGER_RISING,
+			sdata->trig->name,
+			sdata->trig);
+	if (err)
+		goto request_irq_error;
+
+	sdata->trig->private_data = indio_dev;
+	sdata->trig->ops = trig_ops;
+	sdata->trig->dev.parent = sdata->dev;
+
+	err = iio_trigger_register(sdata->trig);
+	if (err < 0) {
+		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+		goto iio_trigger_register_error;
+	}
+	indio_dev->trig = sdata->trig;
+
+	return 0;
+
+iio_trigger_register_error:
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+request_irq_error:
+	iio_trigger_free(sdata->trig);
+iio_trigger_alloc_error:
+	return err;
+}
+EXPORT_SYMBOL(st_sensors_allocate_trigger);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *sdata = iio_priv(indio_dev);
+
+	iio_trigger_unregister(sdata->trig);
+	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+	iio_trigger_free(sdata->trig);
+}
+EXPORT_SYMBOL(st_sensors_deallocate_trigger);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
new file mode 100644
index 0000000..c0f2acc
--- /dev/null
+++ b/include/linux/iio/common/st_sensors.h
@@ -0,0 +1,246 @@
+/*
+ * STMicroelectronics sensors library driver
+ *
+ * Copyright 2012 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_SENSORS_H
+#define ST_SENSORS_H
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/irqreturn.h>
+#include <linux/iio/trigger.h>
+
+
+#define ST_SENSORS_TX_MAX_LENGHT		2
+#define ST_SENSORS_RX_MAX_LENGHT		6
+
+#define ST_SENSORS_ODR_LIST_MAX			10
+#define ST_SENSORS_FULLSCALE_AVL_MAX		10
+
+#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
+#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
+#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
+#define ST_SENSORS_BYTE_FOR_CHANNEL		2
+#define ST_SENSORS_SCAN_X			0
+#define ST_SENSORS_SCAN_Y			1
+#define ST_SENSORS_SCAN_Z			2
+#define ST_SENSORS_DEFAULT_12_REALBITS		12
+#define ST_SENSORS_DEFAULT_16_REALBITS		16
+#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
+#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
+#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
+#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
+#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
+#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
+
+#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
+{ \
+	.type = device_type, \
+	.modified = 1, \
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+	.scan_index = index, \
+	.channel2 = mod, \
+	.address = addr, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = bits, \
+		.shift = 16 - bits, \
+		.storagebits = 16, \
+		.endianness = endian, \
+	}, \
+}
+
+struct st_sensor_odr_avl {
+	unsigned int hz;
+	u8 value;
+};
+
+struct st_sensor_odr {
+	u8 addr;
+	u8 mask;
+	short num_bit;
+	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
+};
+
+struct st_sensor_power {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	u8 value_off;
+	u8 value_on;
+};
+
+struct st_sensor_axis {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_fullscale_avl {
+	unsigned int num;
+	u8 value;
+	unsigned int gain;
+	unsigned int gain2;
+};
+
+struct st_sensor_fullscale {
+	u8 addr;
+	u8 mask;
+	unsigned short num_bit;
+	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
+};
+
+struct st_sensor_bdu {
+	u8 addr;
+	u8 mask;
+};
+
+struct st_sensor_interrupt_generator {
+	u8 en_addr;
+	u8 latch_mask_addr;
+	u8 en_mask;
+	u8 latching_mask;
+};
+
+struct st_sensor_data_ready_irq {
+	u8 addr;
+	u8 mask;
+	struct st_sensor_interrupt_generator ig1;
+};
+
+/**
+ * struct st_sensor_transfer - ST sensor device I/O struct
+ * @buf_lock: Mutex to protect rx and tx buffers.
+ * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
+ *	This buffer is used to avoid DMA not-aligned issue.
+ * @read_byte: Function used to read one byte.
+ * @write_byte: Function used to write one byte.
+ * @read_multiple_byte: Function used to read multiple byte.
+ */
+struct st_sensor_transfer {
+	struct mutex buf_lock;
+	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
+	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned;
+	int (*read_byte) (struct st_sensor_transfer *st, struct device *dev,
+						u8 reg_addr, u8 *res_byte);
+	int (*write_byte) (struct st_sensor_transfer *st, struct device *dev,
+						u8 reg_addr, u8 data);
+	int (*read_multiple_byte) (struct st_sensor_transfer *st,
+		struct device *dev, u8 reg_addr, int len, u8 *data,
+							bool multiread_bit);
+};
+
+/**
+ * struct st_sensor_data - ST sensor device status
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @trig: The trigger in use by the core driver.
+ * @enabled: Status of the sensor (false->off, true->on).
+ * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
+ * @index: Number used to point the sensor being used in the st_sensors struct.
+ * @buffer_data: Data used by buffer part.
+ * @fullscale: Maximum range of measure by the sensor.
+ * @gain: Sensitivity of the sensor [m/s^2/LSB].
+ * @odr: Output data rate of the sensor [Hz].
+ * @tf: Transfer function structure used by I/O operations.
+ */
+struct st_sensor_data {
+	struct device *dev;
+	struct iio_trigger *trig;
+
+	bool enabled;
+	bool multiread_bit;
+
+	short index;
+
+	char *buffer_data;
+
+	unsigned int fullscale;
+	unsigned int gain;
+	unsigned int gain2;
+	unsigned int odr;
+
+	struct st_sensor_transfer tf;
+
+	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
+};
+
+/**
+ * struct st_sensors - ST sensors list
+ * @wai: Contents of WhoAmI register.
+ * @ch: IIO channels for the sensor.
+ * @odr: Output data rate register and odr list available.
+ * @pw: Power register of the sensor.
+ * @enable_axis: Enable one or more axis of the sensor.
+ * @fs: Full scale register and fs list available.
+ * @bdu: Block data update register.
+ * @drdy_irq: Data ready register of the sensor.
+ * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
+ */
+struct st_sensors {
+	u8 wai;
+	struct iio_chan_spec *ch;
+	struct st_sensor_odr odr;
+	struct st_sensor_power pw;
+	struct st_sensor_axis enable_axis;
+	struct st_sensor_fullscale fs;
+	struct st_sensor_bdu bdu;
+	struct st_sensor_data_ready_irq drdy_irq;
+	bool multi_read_bit;
+};
+
+void st_sensors_spi_configure(struct iio_dev *indio_dev,
+			struct spi_device *spi, struct st_sensor_data *sdata);
+
+void st_sensors_i2c_configure(struct iio_dev *indio_dev,
+		struct i2c_client *client, struct st_sensor_data *sdata);
+
+int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
+					u8 mask, short num_bit, u8 data);
+
+int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
+			const struct st_sensor_odr_avl *odr_avl, char *buf);
+
+int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
+	const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
+
+int st_sensors_set_odr(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int odr);
+
+int st_sensors_set_fullscale(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, unsigned int fs);
+
+int st_sensors_set_enable(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
+			const struct st_sensors *sensor, u8 axis_enable);
+
+int st_sensors_init_sensor(struct iio_dev *indio_dev,
+					const struct st_sensors *sensor);
+
+int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, bool enable);
+
+int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
+				const struct st_sensors *sensor, int scale);
+
+int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
+
+int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
+					const struct iio_trigger_ops *trig_ops);
+
+void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
+
+int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
+
+irqreturn_t st_sensors_trigger_handler(int irq, void *p);
+
+#endif /* ST_SENSORS_H */
-- 
1.7.0.4


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

end of thread, other threads:[~2013-01-29 21:43 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-18 16:58 STMicroelectronics drivers Denis CIOCCA
2013-01-18 16:58 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2013-01-18 16:58 ` [PATCH 2/4] iio:accel: Add STMicroelectronics accelerometers driver Denis CIOCCA
2013-01-18 16:58 ` [PATCH 3/4] iio:gyro: Add STMicroelectronics gyroscopes driver Denis CIOCCA
2013-01-18 16:58 ` [PATCH 4/4] iio:magnetometer: Add STMicroelectronics magnetometers driver Denis CIOCCA
2013-01-20 12:54 ` STMicroelectronics drivers Jonathan Cameron
2013-01-20 12:56   ` [PATCH 1/2] Make the st_sensors core library stuff build in all combinations Jonathan Cameron
2013-01-20 12:56     ` [PATCH 2/2] Rework the Makefile / Kconfig for the st accel driver so that all options work Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2013-01-25 23:44 STMicroelectronics IIO driver Denis Ciocca
2013-01-25 23:44 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis Ciocca
2013-01-29 21:45   ` Lars-Peter Clausen
2013-01-21  8:36 STMicroelectronics driver kconfig-makefile bugfix Denis CIOCCA
2013-01-21  8:36 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2013-01-22 16:15   ` Jonathan Cameron
2013-01-23  9:33   ` Lars-Peter Clausen
2013-01-18  9:31 STMicroelectronics drivers bugfix Denis CIOCCA
2013-01-18  9:31 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2013-01-09 12:06 iio: STMicroelectronics iio drivers bugfix Denis CIOCCA
2013-01-09 12:06 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2013-01-12 21:59   ` Jonathan Cameron
2013-01-08 16:30 iio: STMicroelectronics iio drivers Denis CIOCCA
2013-01-08 16:30 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2012-12-13 13:11 STMicroelectronics sensors driver Denis CIOCCA
2012-12-13 13:11 ` [PATCH 1/4] iio:common: Add STMicroelectronics common library Denis CIOCCA
2012-12-16 12:15   ` Jonathan Cameron

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