* [PATCH] Staging: iio: gyro: Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13
@ 2012-08-16 2:02 Swapnil TIWARI
2012-08-16 18:56 ` Jonathan Cameron
0 siblings, 1 reply; 2+ messages in thread
From: Swapnil TIWARI @ 2012-08-16 2:02 UTC (permalink / raw)
To: jic23@cam.ac.uk, linux-iio@vger.kernel.org,
linux-kernel@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 39839 bytes --]
Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13
Signed-off-by: Swapnil Tiwari <swapnil.tiwari@st.com>
---
drivers/staging/iio/gyro/Kconfig | 8 +
drivers/staging/iio/gyro/Makefile | 2 +
drivers/staging/iio/gyro/l3gd20.h | 155 ++++
drivers/staging/iio/gyro/l3gd20_gyr_core.c | 1056 ++++++++++++++++++++++++++++
4 files changed, 1221 insertions(+), 0 deletions(-)
diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
index 7e0b7f1..af2e423 100644
--- a/drivers/staging/iio/gyro/Kconfig
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -12,3 +12,11 @@ config ADIS16260
Say yes here to build support for Analog Devices adis16260/5
programmable digital gyroscope sensor.
+config L3GD20_GYR_IIO
+ tristate "STM L3GD20 3 Axis Digital Gyroscope Sensor I2C driver (iio)"
+ depends on I2C
+ help
+ Say yes here to build support for the STMicrolectronics L3GD20 3 Axis
+ Digital .
+ To compile this driver as a module, choose M here: the module
+ will be called l3gd20_gyr_iio.
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
index a3a28ed..28f530b 100644
--- a/drivers/staging/iio/gyro/Makefile
+++ b/drivers/staging/iio/gyro/Makefile
@@ -6,3 +6,5 @@ adis16260-y := adis16260_core.o
adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
obj-$(CONFIG_ADIS16260) += adis16260.o
+l3gd20_gyr-y := l3gd20_gyr_core.o
+obj-$(CONFIG_L3GD20_GYR_IIO) += l3gd20_gyr.o
diff --git a/drivers/staging/iio/gyro/l3gd20.h b/drivers/staging/iio/gyro/l3gd20.h
index e69de29..d2a734a 100644
--- a/drivers/staging/iio/gyro/l3gd20.h
+++ b/drivers/staging/iio/gyro/l3gd20.h
@@ -0,0 +1,155 @@
+/******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
+*
+* File Name : l3gd20_iio.h
+* Authors : MH - C&I BU - Application Team
+* : Matteo Dameno (matteo.dameno@st.com
+* : author is willing to be considered the contact
+* : and update points for the driver.
+* Version : V.2.0.0
+* Date : 2011/Aug/16
+* Description : L3GD20 3 Axis Digital Gyroscope Sensor device driver iio
+* :
+********************************************************************************
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*
+*******************************************************************************/
+/*******************************************************************************
+Version History.
+ Revision 2.0.0: 2012/Nov/16
+ first ioo implementation;
+*******************************************************************************/
+
+#ifndef __L3GD20_H__
+#define __L3GD20_H__
+
+
+#define L3GD20_GYR_DEV_NAME "l3gd20_gyr_iio"
+
+#define L3GD20_GYR_FS_250DPS 0x00
+#define L3GD20_GYR_FS_500DPS 0x10
+#define L3GD20_GYR_FS_2000DPS 0x30
+
+#define L3GD20_GYR_ENABLED 1
+#define L3GD20_GYR_DISABLED 0
+
+
+#ifdef __KERNEL__
+
+/* to set gpios numb connected to gyro interrupt pins,
+ * the unused ones have to be set to -EINVAL
+ */
+#define DEFAULT_INT1_GPIO (-EINVAL)
+#define DEFAULT_INT2_GPIO (-EINVAL)
+
+#define SAD0L 0x00
+#define SAD0H 0x01
+#define L3GD20_GYR_I2C_SADROOT 0x35
+#define L3GD20_GYR_I2C_SAD_L ((L3GD20_GYR_I2C_SADROOT<<1)|SAD0L)
+#define L3GD20_GYR_I2C_SAD_H ((L3GD20_GYR_I2C_SADROOT<<1)|SAD0H)
+
+#define L3GD20_MIN_POLL_PERIOD_MS 2
+
+struct l3gd20_gyr_platform_data {
+ int (*init)(void);
+ void (*exit)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+ unsigned int poll_interval;
+ unsigned int min_interval;
+
+ u8 fs_range;
+ u8 sampling_frequency;
+
+ /* set gpio_int[1,2] either to the choosen gpio pin number or to -EINVAL
+ * if leaved unconnected
+ */
+ int gpio_int1;
+ int gpio_int2; /* int for fifo */
+
+ /* axis mapping */
+ u8 axis_map_x;
+ u8 axis_map_y;
+ u8 axis_map_z;
+
+ u8 negate_x;
+ u8 negate_y;
+ u8 negate_z;
+};
+
+#ifdef CONFIG_IIO_RING_BUFFER
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum l3gd20_gyr_scan {
+ L3GD20_GYR_SCAN_GYR_X,
+ L3GD20_GYR_SCAN_GYR_Y,
+ L3GD20_GYR_SCAN_GYR_Z,
+};
+
+void l3gd20_gyr_remove_trigger(struct iio_dev *indio_dev);
+int l3gd20_gyr_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t l3gd20_gyr_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+
+int l3gd20_gyr_configure_ring(struct iio_dev *indio_dev);
+void l3gd20_gyr_unconfigure_ring(struct iio_dev *indio_dev);
+
+int l3gd20_gyr_initialize_ring(struct iio_ring_buffer *ring);
+void l3gd20_gyr_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void l3gd20_gyr_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int l3gd20_gyr_probe_trigger(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline ssize_t
+l3gd20_gyr_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static int l3gd20_gyr_configure_ring(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void l3gd20_gyr_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int l3gd20_gyr_initialize_ring(struct iio_ring_buffer *ring)
+{
+ return 0;
+}
+
+static inline void l3gd20_gyr_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif /* __KERNEL__ */
+
+#endif /* __L3GD20_H__ */
diff --git a/drivers/staging/iio/gyro/l3gd20_gyr_core.c b/drivers/staging/iio/gyro/l3gd20_gyr_core.c
index e69de29..64871b6 100644
--- a/drivers/staging/iio/gyro/l3gd20_gyr_core.c
+++ b/drivers/staging/iio/gyro/l3gd20_gyr_core.c
@@ -0,0 +1,1056 @@
+/******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
+*
+* File Name : l3gd20_gyr_core_iio.c
+* Authors : MSH - Motion Mems BU - Application Team
+* : Matteo Dameno (matteo.dameno@st.com)
+* : author is willing to be considered the contact
+* : and update points for the driver.*
+* Version : V.2.0.0
+* Date : 2011/Nov/16
+* Description : L3GD20 3 Axis Digital Gyroscope Sensor device driver iio
+* :
+********************************************************************************
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*
+********************************************************************************
+ Revision 2.0.0: 2012/Nov/16
+ first ioo implementation;
+
+*******************************************************************************/
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include <linux/sysfs.h>
+
+#include <linux/moduleparam.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "gyro.h"
+#include "l3gd20.h"
+
+
+#define SENSITIVITY_250DPS "8.75" /** mdps/counts */
+#define SENSITIVITY_500DPS "17.50" /** mdps/counts */
+#define SENSITIVITY_2000DPS "70.00" /** mdps/counts */
+
+#define SCALE_250DPS "0.000153" /** (rad/sec)/count */
+#define SCALE_500DPS "0.000297" /** (rad/sec)/count */
+#define SCALE_2000DPS "0.001222" /** (rad/sec)/count */
+
+#define GAIN_250DPS "6548.089087" /** count/(rad/sec) */
+#define GAIN_500DPS "3370.339971" /** count/(rad/sec) */
+#define GAIN_2000DPS "818.511136" /** count/(rad/sec) */
+
+/* l3gd20 gyroscope registers */
+#define WHO_AM_I (0x0F)
+
+#define CTRL_REG1 (0x20) /* CTRL REG1 */
+#define CTRL_REG2 (0x21) /* CTRL REG2 */
+#define CTRL_REG3 (0x22) /* CTRL_REG3 */
+#define CTRL_REG4 (0x23) /* CTRL_REG4 */
+#define CTRL_REG5 (0x24) /* CTRL_REG5 */
+#define REFERENCE (0x25) /* REFERENCE REG */
+
+#define OUT_X_LSB (0x28) /* 1st AXIS OUT REG of 6 */
+#define OUT_X_MSB (0x29)
+#define OUT_Y_LSB (0x2A)
+#define OUT_Y_MSB (0x2B)
+#define OUT_Z_LSB (0x2C)
+#define OUT_Z_MSB (0x2D)
+#define AXISDATA_REG OUT_X_LSB
+
+#define FIFO_CTRL_REG (0x2E) /* FIFO CONTROL REGISTER */
+#define FIFO_SRC_REG (0x2F) /* FIFO SOURCE REGISTER */
+/* */
+
+/* CTRL_REG1 */
+#define ALL_ZEROES (0x00)
+#define PM_OFF (0x00)
+#define PM_NORMAL (0x08)
+#define ENABLE_ALL_AXES (0x07)
+#define ENABLE_NO_AXES (0x00)
+#define BW00 (0x00)
+#define BW01 (0x10)
+#define BW10 (0x20)
+#define BW11 (0x30)
+#define CTRL_REG1_BW_MASK (0x30)
+#define ODR095 (0x00) /* ODR = 95Hz */
+#define ODR190 (0x40) /* ODR = 190Hz */
+#define ODR380 (0x80) /* ODR = 380Hz */
+#define ODR760 (0xC0) /* ODR = 760Hz */
+#define CTRL_REG1_ODR_MASK (0xC0)
+
+
+/* CTRL_REG3 bits */
+#define I2_DRDY (0x08)
+#define I2_WTM (0x04)
+#define I2_OVRUN (0x02)
+#define I2_EMPTY (0x01)
+#define I2_NONE (0x00)
+#define I2_MASK (0x0F)
+
+/* CTRL_REG4 bits */
+#define FS_MASK (0x30)
+#define BDU_ENABLE (0x80)
+
+/* CTRL_REG5 bits */
+#define FIFO_ENABLE (0x40)
+#define HPF_ENALBE (0x11)
+
+/* FIFO_CTRL_REG bits */
+#define FIFO_MODE_MASK (0xE0)
+#define FIFO_MODE_BYPASS (0x00)
+#define FIFO_MODE_FIFO (0x20)
+#define FIFO_MODE_STREAM (0x40)
+#define FIFO_MODE_STR2FIFO (0x60)
+#define FIFO_MODE_BYPASS2STR (0x80)
+#define FIFO_WATERMARK_MASK (0x1F)
+
+#define FIFO_STORED_DATA_MASK (0x1F)
+
+/* I2C multi read/write option*/
+#define I2C_AUTO_INCREMENT (0x80)
+
+/* RESUME STATE INDICES */
+#define RES_CTRL_REG1 0
+#define RES_CTRL_REG2 1
+#define RES_CTRL_REG3 2
+#define RES_CTRL_REG4 3
+#define RES_CTRL_REG5 4
+#define RES_FIFO_CTRL_REG 5
+#define RESUME_ENTRIES 6
+
+
+/** Registers Contents */
+#define WHOAMI_L3GD20 (0x00D4) /* Expected content for WAI register*/
+
+
+struct output_rate {
+ unsigned int poll_rate_ms;
+ u8 sampling_rate_setting;
+ const char *hz_label;
+};
+
+static const struct output_rate l3gd20_gyr_odr_table[] = {
+
+ { 2, ODR760|BW10, "760"},
+ { 3, ODR380|BW01, "380"},
+ { 6, ODR190|BW00, "190"},
+ { 11, ODR095|BW00, "95"},
+};
+
+static struct l3gd20_gyr_platform_data default_l3gd20_gyr_pdata = {
+ .fs_range = L3GD20_GYR_FS_250DPS,
+ .axis_map_x = 0,
+ .axis_map_y = 1,
+ .axis_map_z = 2,
+ .negate_x = 0,
+ .negate_y = 0,
+ .negate_z = 0,
+
+ .gpio_int1 = DEFAULT_INT1_GPIO,
+ .gpio_int2 = DEFAULT_INT2_GPIO, /* int for fifo */
+
+};
+
+struct l3gd20_gyr_data {
+
+ struct work_struct work_trigger_to_ring;
+ struct iio_work_cont work_cont_thresh;
+ s64 last_timestamp;
+ struct iio_dev *indio_dev;
+ struct iio_trigger *trig;
+
+ struct i2c_client *client;
+ struct l3gd20_gyr_platform_data *pdata;
+ int use_smbus;
+
+ struct mutex lock;
+ int hw_initialized;
+
+ atomic_t enabled;
+
+ char const *scale;
+ char const *gain;
+
+ u8 reg_addr;
+ u8 resume_state[RESUME_ENTRIES];
+
+
+ int irq1;
+ int irq2;
+
+};
+
+
+static int l3gd20_gyr_i2c_read(struct l3gd20_gyr_data *data,
+ u8 *buf, int len)
+{
+ int ret;
+ u8 reg = buf[0];
+ u8 cmd = reg;
+
+ if (len > 1)
+ cmd = (I2C_AUTO_INCREMENT | reg);
+ if (data->use_smbus) {
+ if (len == 1) {
+ ret = i2c_smbus_read_byte_data(data->client, cmd);
+ buf[0] = ret & 0xff;
+#ifdef DEBUG
+ dev_dbg(&data->client->dev,
+ "i2c_smbus_read_byte_data: ret=0x%02x, len:%d ,"
+ "command=0x%02x, buf[0]=0x%02x\n",
+ ret, len, cmd , buf[0]);
+#endif
+ } else if (len > 1) {
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ cmd, len, buf);
+#ifdef DEBUG
+ dev_dbg(&data->client->dev,
+ "i2c_smbus_read_i2c_block_data: ret:%d len:%d, "
+ "command=0x%02x, ",
+ ret, len, cmd);
+ char ii;
+ for (ii = 0; ii < len; ii++)
+ printk(KERN_DEBUG "buf[%d]=0x%02x,",
+ ii, buf[ii]);
+ printk("\n");
+#endif
+ } else
+ ret = -1;
+
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "read transfer error: len:%d, command=0x%02x\n",
+ len, cmd);
+ return 0; /* failure */
+ }
+ return len; /* success */
+ }
+
+
+ ret = i2c_master_send(data->client, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd))
+ return ret;
+
+ return i2c_master_recv(data->client, buf, len);
+}
+
+static int l3gd20_gyr_i2c_write(struct l3gd20_gyr_data *data, u8 *buf,
+ int len)
+{
+ int ret;
+ u8 reg, value;
+
+ if (len > 1)
+ buf[0] = (I2C_AUTO_INCREMENT | buf[0]);
+
+ reg = buf[0];
+ value = buf[1];
+
+ if (data->use_smbus) {
+ if (len == 1) {
+ ret = i2c_smbus_write_byte_data(data->client, reg,
+ value);
+#ifdef DEBUG
+ dev_dbg(&data->client->dev,
+ "i2c_smbus_write_byte_data: ret=%d, len:%d, "
+ "command=0x%02x, value=0x%02x\n",
+ ret, len, reg , value);
+#endif
+ return ret;
+ } else if (len > 1) {
+ ret = i2c_smbus_write_i2c_block_data(data->client,
+ reg, len, buf + 1);
+#ifdef DEBUG
+ dev_dbg(&data->client->dev,
+ "i2c_smbus_write_i2c_block_data: ret=%d, "
+ "len:%d, command=0x%02x, ",
+ ret, len, reg);
+ char ii;
+ for (ii = 0; ii < len + 1; ii++)
+ printk(KERN_DEBUG "value[%d]=0x%02x,",
+ ii, buf[ii]);
+
+ printk("\n");
+#endif
+ return ret;
+ }
+ }
+
+ ret = i2c_master_send(data->client, buf, len+1);
+ return (ret == len+1) ? 0 : ret;
+}
+
+
+static int l3gd20_gyr_register_write(struct l3gd20_gyr_data *data,
+ u8 *buf, u8 reg_address, u8 new_value)
+{
+ int err;
+
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = l3gd20_gyr_i2c_write(data, buf, 1);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+static int l3gd20_gyr_register_read(struct l3gd20_gyr_data *data,
+ u8 *buf, u8 reg_address)
+{
+
+ int err = -1;
+ buf[0] = (reg_address);
+ err = l3gd20_gyr_i2c_read(data, buf, 1);
+ return err;
+}
+
+static int l3gd20_gyr_register_update(struct l3gd20_gyr_data *data,
+ u8 *buf, u8 reg_address, u8 mask, u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = l3gd20_gyr_register_read(data, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[0];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = l3gd20_gyr_register_write(data, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+static int l3gd20_gyr_hw_init(struct l3gd20_gyr_data *dev_data)
+{
+ int err;
+ u8 buf[6];
+
+ pr_info("%s hw init\n", L3GD20_GYR_DEV_NAME);
+
+ buf[0] = (CTRL_REG1);
+ buf[1] = dev_data->resume_state[RES_CTRL_REG1];
+ buf[2] = dev_data->resume_state[RES_CTRL_REG2];
+ buf[3] = dev_data->resume_state[RES_CTRL_REG3];
+ buf[4] = dev_data->resume_state[RES_CTRL_REG4];
+ buf[5] = dev_data->resume_state[RES_CTRL_REG5];
+
+ err = l3gd20_gyr_i2c_write(dev_data, buf, 5);
+ if (err < 0)
+ return err;
+
+ buf[0] = FIFO_CTRL_REG;
+ buf[1] = dev_data->resume_state[RES_FIFO_CTRL_REG];
+ err = l3gd20_gyr_i2c_write(dev_data, buf, 1);
+ if (err < 0)
+ return err;
+
+ dev_data->hw_initialized = 1;
+
+ return err;
+}
+
+static void l3gd20_gyr_device_power_off(struct l3gd20_gyr_data *dev_data)
+{
+ int err;
+ u8 buf[2];
+
+ pr_info("%s power off\n", L3GD20_GYR_DEV_NAME);
+
+ buf[0] = CTRL_REG1;
+ buf[1] = PM_OFF;
+ err = l3gd20_gyr_i2c_write(dev_data, buf, 1);
+ if (err < 0)
+ dev_err(&dev_data->client->dev, "soft power off failed\n");
+
+ if (dev_data->pdata->power_off) {
+ dev_data->pdata->power_off();
+ dev_data->hw_initialized = 0;
+ }
+
+ if (dev_data->hw_initialized)
+ dev_data->hw_initialized = 0;
+}
+
+static int l3gd20_gyr_device_power_on(struct l3gd20_gyr_data *dev_data)
+{
+ int err;
+
+ if (dev_data->pdata->power_on) {
+ err = dev_data->pdata->power_on();
+ if (err < 0)
+ return err;
+ }
+
+
+ if (!dev_data->hw_initialized) {
+ err = l3gd20_gyr_hw_init(dev_data);
+ if (err < 0) {
+ l3gd20_gyr_device_power_off(dev_data);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+static int l3gd20_gyr_enable(struct l3gd20_gyr_data *dev_data)
+{
+ int err;
+
+ if (!atomic_cmpxchg(&dev_data->enabled, 0, 1)) {
+
+ err = l3gd20_gyr_device_power_on(dev_data);
+ if (err < 0) {
+ atomic_set(&dev_data->enabled, 0);
+ return err;
+ }
+
+ }
+
+ return 0;
+}
+
+static int l3gd20_gyr_disable(struct l3gd20_gyr_data *dev_data)
+{
+
+ printk(KERN_DEBUG "%s: dev_data->enabled = %d\n", __func__,
+ atomic_read(&dev_data->enabled));
+
+ if (atomic_cmpxchg(&dev_data->enabled, 1, 0))
+ l3gd20_gyr_device_power_off(dev_data);
+
+ return 0;
+}
+
+static int l3gd20_gyr_update_fs_range(struct l3gd20_gyr_data *dev_data,
+ u8 new_fs_range)
+{
+ int res ;
+ u8 buf[2];
+ char const *scale;
+ char const *gain;
+ buf[0] = CTRL_REG4;
+
+ switch (new_fs_range) {
+ case L3GD20_GYR_FS_250DPS:
+ gain = GAIN_250DPS;
+ scale = SCALE_250DPS;
+ break;
+
+ case L3GD20_GYR_FS_500DPS:
+ gain = GAIN_500DPS;
+ scale = SCALE_500DPS;
+ break;
+
+ case L3GD20_GYR_FS_2000DPS:
+ gain = GAIN_2000DPS;
+ scale = SCALE_2000DPS;
+ break;
+
+ default:
+ dev_err(&dev_data->client->dev,
+ "invalid fs range requested: %u\n",
+ new_fs_range);
+ return -EINVAL;
+ }
+
+ res = l3gd20_gyr_register_update(dev_data, buf, CTRL_REG4,
+ FS_MASK, new_fs_range);
+
+ if (res < 0) {
+ pr_err("%s : failed to update fs:0x%02x\n",
+ __func__, new_fs_range);
+ return res;
+ }
+ dev_data->resume_state[RES_CTRL_REG4] =
+ ((FS_MASK & new_fs_range) |
+ (~FS_MASK & dev_data->resume_state[RES_CTRL_REG4]));
+ dev_data->gain = gain;
+ dev_data->scale = scale;
+ return res;
+}
+
+static int l3gd20_gyr_update_sampling_freq(struct l3gd20_gyr_data *data,
+ u8 freq)
+{
+ int res = 0;
+ u8 buf[2];
+ u8 new_value;
+ u8 mask = (CTRL_REG1_ODR_MASK|CTRL_REG1_BW_MASK);
+
+
+ new_value = freq;
+ res = l3gd20_gyr_register_update(data, buf, CTRL_REG1,
+ mask, new_value);
+
+ if (res < 0) {
+ dev_err(&data->client->dev,
+ "%s : failed to update sampling frequency setting\n",
+ __func__);
+ return res;
+ }
+ dev_dbg(&data->client->dev, "%s : new_value:0x%02x, freq:0x%02x\n",
+ __func__, new_value, freq);
+
+ data->resume_state[RES_CTRL_REG1] =
+ ((mask & new_value) |
+ (~mask & data->resume_state[RES_CTRL_REG1]));
+
+ data->pdata->sampling_frequency = new_value;
+
+ return res;
+}
+
+static ssize_t l3gd20_gyr_show_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ u8 i;
+ u8 val;
+
+
+ mutex_lock(&data->lock);
+ val = data->pdata->sampling_frequency;
+ mutex_unlock(&data->lock);
+ dev_dbg(dev, "getting sampling frequency: 0x%02x\n", val);
+
+ for (i = 0; i < ARRAY_SIZE(l3gd20_gyr_odr_table); i++)
+ if (l3gd20_gyr_odr_table[i].sampling_rate_setting == val)
+ break;
+
+ return sprintf(buf, "%s\n", l3gd20_gyr_odr_table[i].hz_label);
+}
+
+static ssize_t l3gd20_gyr_store_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ u8 found = 0;
+ u8 i;
+ u8 ret;
+
+ for (i = 0; i < ARRAY_SIZE(l3gd20_gyr_odr_table); i++) {
+ if (strcmp(buf, l3gd20_gyr_odr_table[i].hz_label) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_err(dev, "trying to set invalid sampling frequency: %s\n"
+ "new sampling frequency not set!\n", buf);
+ return -EINVAL;
+ } else {
+ mutex_lock(&data->lock);
+ ret = l3gd20_gyr_update_sampling_freq(data,
+ l3gd20_gyr_odr_table[i].sampling_rate_setting);
+ if (ret < 0)
+ goto error;
+ data->pdata->sampling_frequency =
+ l3gd20_gyr_odr_table[i].sampling_rate_setting;
+ dev_info(dev, "set new sampling frequency: %s Hz\n",
+ l3gd20_gyr_odr_table[i].hz_label);
+ mutex_unlock(&data->lock);
+ }
+ return size;
+error:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ l3gd20_gyr_show_sampling_frequency,
+ l3gd20_gyr_store_sampling_frequency);
+
+static ssize_t l3gd20_gyr_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ int val = atomic_read(&data->enabled);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t l3gd20_gyr_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val)
+ l3gd20_gyr_enable(data);
+ else
+ l3gd20_gyr_disable(data);
+
+ return size;
+}
+
+static IIO_DEVICE_ATTR(enable_device,
+ S_IWUSR | S_IRUGO,
+ l3gd20_gyr_show_enable,
+ l3gd20_gyr_store_enable,
+ 0);
+
+static int l3gd20_gyr_show_channel_rawdata(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int err;
+
+ /* Data bytes from hardware (GxL, GxH) or (GyL, GyH) or (GzL, GzH) */
+ u8 out_data[2];
+ /* x,y,z hardware data */
+ s16 raw_d;
+
+ out_data[0] = this_attr->address;
+
+ mutex_lock(&data->lock);
+ err = l3gd20_gyr_i2c_read(data, out_data, 2);
+ mutex_unlock(&data->lock);
+ if (err < 0) {
+ dev_err(dev, "error reading output registers\n");
+ return err;
+ }
+
+ raw_d = ((s16) ((out_data[1]) << 8) | out_data[0]);
+ return sprintf(buf, "%d\n", raw_d);
+
+}
+
+static IIO_DEV_ATTR_GYRO_X(l3gd20_gyr_show_channel_rawdata, OUT_X_LSB);
+static IIO_DEV_ATTR_GYRO_Y(l3gd20_gyr_show_channel_rawdata, OUT_Y_LSB);
+static IIO_DEV_ATTR_GYRO_Z(l3gd20_gyr_show_channel_rawdata, OUT_Z_LSB);
+
+static ssize_t l3gd20_gyr_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ int label = 0;
+ u8 val;
+ mutex_lock(&data->lock);
+ val = data->pdata->fs_range;
+ mutex_unlock(&data->lock);
+
+ switch (val) {
+ case L3GD20_GYR_FS_250DPS:
+ label = 250;
+ break;
+ case L3GD20_GYR_FS_500DPS:
+ label = 500;
+ break;
+ case L3GD20_GYR_FS_2000DPS:
+ label = 2000;
+ break;
+ }
+
+ return sprintf(buf, "%d dps\n", label);
+}
+
+static ssize_t l3gd20_gyr_store_range(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ unsigned long val;
+ u8 range;
+ int err = -1;
+ if (strict_strtoul(buf, 10, &val)) {
+ dev_err(dev, "invalid range %lu\n. Range not changed", val);
+ return -EINVAL;
+ }
+ switch (val) {
+ case 250:
+ range = L3GD20_GYR_FS_250DPS;
+ break;
+ case 500:
+ range = L3GD20_GYR_FS_500DPS;
+ break;
+ case 2000:
+ range = L3GD20_GYR_FS_2000DPS;
+ break;
+ default:
+ dev_err(dev, "invalid range %lu. Range not changed\n", val);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->lock);
+ data->pdata->fs_range = range;
+ err = l3gd20_gyr_update_fs_range(data, range);
+ mutex_unlock(&data->lock);
+ if (err < 0)
+ goto error;
+ dev_info(dev, "set range to %lu dps\n", val);
+ return size;
+error:
+ dev_err(dev, "error changing range\n");
+ return err;
+}
+
+
+
+static IIO_DEVICE_ATTR(gyro_range,
+ S_IWUSR | S_IRUGO,
+ l3gd20_gyr_show_range,
+ l3gd20_gyr_store_range,
+ 0);
+
+
+static IIO_CONST_ATTR(gyro_range_available, "250 500 2000");
+
+
+static ssize_t l3gd20_gyr_show_gain(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ return sprintf(buf, "%s\n", data->gain);
+}
+
+static IIO_DEVICE_ATTR(gyro_gain, S_IRUGO,
+ l3gd20_gyr_show_gain,
+ NULL , 0);
+
+
+static ssize_t l3gd20_gyr_show_scale(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct l3gd20_gyr_data *data = indio_dev->dev_data;
+ return sprintf(buf, "%s\n", data->scale);
+}
+
+static IIO_DEVICE_ATTR(gyro_scale, S_IRUGO,
+ l3gd20_gyr_show_scale,
+ NULL , 0);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("95 190 380 760");
+
+static IIO_CONST_ATTR(name, L3GD20_GYR_DEV_NAME);
+
+
+
+static struct attribute *l3gd20_gyr_attributes[] = {
+ &iio_const_attr_name.dev_attr.attr,
+ &iio_dev_attr_enable_device.dev_attr.attr,
+ &iio_const_attr_gyro_range_available.dev_attr.attr,
+ &iio_dev_attr_gyro_range.dev_attr.attr,
+ &iio_dev_attr_gyro_gain.dev_attr.attr,
+ &iio_dev_attr_gyro_scale.dev_attr.attr,
+ &iio_dev_attr_gyro_x_raw.dev_attr.attr,
+ &iio_dev_attr_gyro_y_raw.dev_attr.attr,
+ &iio_dev_attr_gyro_z_raw.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group l3gd20_gyr_attribute_group = {
+ .attrs = l3gd20_gyr_attributes,
+};
+
+static int l3gd20_gyr_validate_pdata(struct l3gd20_gyr_data *dev_data)
+{
+
+ if (dev_data->pdata->axis_map_x > 2 ||
+ dev_data->pdata->axis_map_y > 2 ||
+ dev_data->pdata->axis_map_z > 2) {
+ dev_err(&dev_data->client->dev,
+ "invalid axis_map value x:%u y:%u z%u\n",
+ dev_data->pdata->axis_map_x,
+ dev_data->pdata->axis_map_y,
+ dev_data->pdata->axis_map_z);
+ return -EINVAL;
+ }
+
+ /* Only allow 0 and 1 for negation boolean flag */
+ if (dev_data->pdata->negate_x > 1 ||
+ dev_data->pdata->negate_y > 1 ||
+ dev_data->pdata->negate_z > 1) {
+ dev_err(&dev_data->client->dev,
+ "invalid negate value x:%u y:%u z:%u\n",
+ dev_data->pdata->negate_x,
+ dev_data->pdata->negate_y,
+ dev_data->pdata->negate_z);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int l3gd20_gyr_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct l3gd20_gyr_data *data;
+ int err = -1;
+ data = kzalloc(sizeof(struct l3gd20_gyr_data), GFP_KERNEL);
+ if (data == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_check_functionality_failed;
+ }
+
+ u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK ;
+
+ int regdone = 0;
+
+ pr_info("%s: probe start.\n", L3GD20_GYR_DEV_NAME);
+
+
+ /* Support for both I2C and SMBUS adapter interfaces. */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_warn(&client->dev, "client not i2c capable\n");
+ if (i2c_check_functionality(client->adapter, smbus_func)) {
+ data->use_smbus = 1;
+ dev_warn(&client->dev, "client using SMBUS\n");
+ } else {
+ err = -ENODEV;
+ dev_err(&client->dev, "client nor SMBUS capable\n");
+ data->use_smbus = 0;
+ goto exit_check_functionality_failed;
+ }
+ }
+
+ mutex_init(&data->lock);
+ mutex_lock(&data->lock);
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ data->pdata = kmalloc(sizeof(*data->pdata), GFP_KERNEL);
+ if (data->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n",
+ err);
+ goto err_mutexunlock;
+ }
+
+ if (client->dev.platform_data == NULL) {
+ memcpy(data->pdata, &default_l3gd20_gyr_pdata,
+ sizeof(*data->pdata));
+ dev_info(&client->dev, "using default platform data\n");
+ } else {
+ memcpy(data->pdata, client->dev.platform_data,
+ sizeof(*data->pdata));
+ }
+
+ err = l3gd20_gyr_validate_pdata(data);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+
+ if (data->pdata->init) {
+ err = data->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err_pdata_init;
+ }
+ }
+
+ memset(data->resume_state, 0, ARRAY_SIZE(data->resume_state));
+
+ data->resume_state[RES_CTRL_REG1] = (ALL_ZEROES | PM_NORMAL
+ | ENABLE_ALL_AXES);
+ data->resume_state[RES_CTRL_REG2] = ALL_ZEROES;
+ data->resume_state[RES_CTRL_REG3] = ALL_ZEROES;
+ data->resume_state[RES_CTRL_REG4] = (ALL_ZEROES | BDU_ENABLE);
+ data->resume_state[RES_CTRL_REG5] = ALL_ZEROES;
+ data->resume_state[RES_FIFO_CTRL_REG] = ALL_ZEROES;
+
+ err = l3gd20_gyr_device_power_on(data);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto exit_kfree_pdata;
+ }
+
+ atomic_set(&data->enabled, 1);
+
+ err = l3gd20_gyr_update_fs_range(data, data->pdata->fs_range);
+ if (err < 0) {
+ dev_err(&client->dev, "update_fs_range failed\n");
+ goto err_power_off;
+ }
+
+ err = l3gd20_gyr_update_sampling_freq(data,
+ data->pdata->sampling_frequency);
+ if (err < 0) {
+ dev_err(&client->dev, "update sampling freq failed\n");
+ goto err_power_off;
+ }
+
+ data->indio_dev = iio_allocate_device();
+ if (!data->indio_dev) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "error allocating iio device: %d\n", err);
+ goto err_power_off;
+ }
+ data->indio_dev->attrs = &l3gd20_gyr_attribute_group;
+ data->indio_dev->dev.parent = &client->dev;
+ data->indio_dev->dev_data = (void *)(data);
+ data->indio_dev->driver_module = THIS_MODULE;
+ data->indio_dev->modes = INDIO_DIRECT_MODE;
+
+
+
+ l3gd20_gyr_device_power_off(data);
+ /* As default, do not report information */
+ atomic_set(&data->enabled, 0);
+
+ err = l3gd20_gyr_configure_ring(data->indio_dev);
+ if (err)
+ goto error_free_dev;
+
+ err = iio_device_register(data->indio_dev);
+ if (err) {
+ dev_err(&client->dev, "error registering iio dev: %d\n", err);
+ goto error_unreg_ring_funcs;
+ }
+ regdone = 1;
+
+ err = l3gd20_gyr_initialize_ring(data->indio_dev->ring);
+ if (err) {
+ dev_err(&client->dev, "failed to initialize the ring\n");
+ goto error_unreg_ring_funcs;
+ }
+
+
+ mutex_unlock(&data->lock);
+ dev_info(&client->dev, "%s: probed\n", L3GD20_GYR_DEV_NAME);
+ return 0;
+
+
+
+error_unreg_ring_funcs:
+ l3gd20_gyr_unconfigure_ring(data->indio_dev);
+error_free_dev:
+ if (regdone)
+ iio_device_unregister(data->indio_dev);
+ else
+ iio_free_device(data->indio_dev);
+err_power_off:
+ l3gd20_gyr_device_power_off(data);
+err_pdata_init:
+ if (data->pdata->exit)
+ data->pdata->exit();
+exit_kfree_pdata:
+ kfree(data->pdata);
+err_mutexunlock:
+ mutex_unlock(&data->lock);
+ kfree(data);
+exit_check_functionality_failed:
+ printk(KERN_ERR "%s: Driver Init failed\n", L3GD20_GYR_DEV_NAME);
+ return err;
+}
+
+
+static int __devexit l3gd20_gyr_remove(struct i2c_client *client)
+{
+
+ struct l3gd20_gyr_data *dev_data = i2c_get_clientdata(client);
+
+ l3gd20_gyr_device_power_off(dev_data);
+
+ l3gd20_gyr_uninitialize_ring(dev_data->indio_dev->ring);
+ iio_device_unregister(dev_data->indio_dev);
+ l3gd20_gyr_unconfigure_ring(dev_data->indio_dev);
+
+ if (dev_data->pdata->exit)
+ dev_data->pdata->exit();
+ kfree(dev_data->pdata);
+ kfree(dev_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id l3gd20_gyr_id[] = {
+ { L3GD20_GYR_DEV_NAME , 0 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, l3gd20_gyr_id);
+
+static struct i2c_driver l3gd20_gyr_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = L3GD20_GYR_DEV_NAME,
+ },
+ .probe = l3gd20_gyr_probe,
+ .remove = __devexit_p(l3gd20_gyr_remove),
+ .id_table = l3gd20_gyr_id,
+};
+
+static int __init l3gd20_gyr_init(void)
+{
+ pr_info("%s: gyroscope iio driver init\n", L3GD20_GYR_DEV_NAME);
+ return i2c_add_driver(&l3gd20_gyr_driver);
+}
+
+static void __exit l3gd20_gyr_exit(void)
+{
+ pr_info("%s exit\n", L3GD20_GYR_DEV_NAME);
+ i2c_del_driver(&l3gd20_gyr_driver);
+ return;
+}
+
+module_init(l3gd20_gyr_init);
+module_exit(l3gd20_gyr_exit);
+
+MODULE_DESCRIPTION("STMicroelectronics l3gd20 digital gyroscope iio driver");
+MODULE_AUTHOR("Matteo Dameno, STMicroelectronics");
+MODULE_LICENSE("GPL v2");
--
1.7.5.4
P.S.: We are working on the driver to make it compatible for the latest kernel.
[-- Attachment #2: Type: text/html, Size: 209288 bytes --]
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] Staging: iio: gyro: Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13
2012-08-16 2:02 [PATCH] Staging: iio: gyro: Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13 Swapnil TIWARI
@ 2012-08-16 18:56 ` Jonathan Cameron
0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Cameron @ 2012-08-16 18:56 UTC (permalink / raw)
To: Swapnil TIWARI
Cc: jic23@cam.ac.uk, linux-iio@vger.kernel.org,
linux-kernel@vger.kernel.org
On 08/16/2012 03:02 AM, Swapnil TIWARI wrote:
> Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13
>
ah, I didn't see the comment at the bottom about moving to the latest kernel
until I'd written a mini rant saying that we would only look at it properly
once it works with the latest kernel.
Also note that it will want to go directly into drivers/iio/gyro/ now.
Interfaces etc have changed a lot in the last 2 years so I'll hold off taking
a look until you have it up to date. Good to have this email to know that
it is a work in progress though!
Thanks
Jonathan
>
>
> Signed-off-by: Swapnil Tiwari <swapnil.tiwari@st.com>
>
>
>
> ---
>
> drivers/staging/iio/gyro/Kconfig | 8 +
>
> drivers/staging/iio/gyro/Makefile | 2 +
>
> drivers/staging/iio/gyro/l3gd20.h | 155 ++++
>
> drivers/staging/iio/gyro/l3gd20_gyr_core.c | 1056 ++++++++++++++++++++++++++++
>
> 4 files changed, 1221 insertions(+), 0 deletions(-)
>
>
>
> diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
>
> index 7e0b7f1..af2e423 100644
>
> --- a/drivers/staging/iio/gyro/Kconfig
>
> +++ b/drivers/staging/iio/gyro/Kconfig
>
> @@ -12,3 +12,11 @@ config ADIS16260
>
> Say yes here to build support for Analog Devices adis16260/5
>
> programmable digital gyroscope sensor.
>
> +config L3GD20_GYR_IIO
>
> + tristate "STM L3GD20 3 Axis Digital Gyroscope Sensor I2C driver (iio)"
>
> + depends on I2C
>
> + help
>
> + Say yes here to build support for the STMicrolectronics L3GD20 3 Axis
>
> + Digital .
>
> + To compile this driver as a module, choose M here: the module
>
> + will be called l3gd20_gyr_iio.
>
> diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
>
> index a3a28ed..28f530b 100644
>
> --- a/drivers/staging/iio/gyro/Makefile
>
> +++ b/drivers/staging/iio/gyro/Makefile
>
> @@ -6,3 +6,5 @@ adis16260-y := adis16260_core.o
>
> adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o
>
> obj-$(CONFIG_ADIS16260) += adis16260.o
>
> +l3gd20_gyr-y := l3gd20_gyr_core.o
>
> +obj-$(CONFIG_L3GD20_GYR_IIO) += l3gd20_gyr.o
>
> diff --git a/drivers/staging/iio/gyro/l3gd20.h b/drivers/staging/iio/gyro/l3gd20.h
>
> index e69de29..d2a734a 100644
>
> --- a/drivers/staging/iio/gyro/l3gd20.h
>
> +++ b/drivers/staging/iio/gyro/l3gd20.h
>
> @@ -0,0 +1,155 @@
>
> +/******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
>
> +*
>
> +* File Name : l3gd20_iio.h
>
> +* Authors : MH - C&I BU - Application Team
>
> +* : Matteo Dameno (matteo.dameno@st.com
>
> +* : author is willing to be considered the contact
>
> +* : and update points for the driver.
>
> +* Version : V.2.0.0
>
> +* Date : 2011/Aug/16
>
> +* Description : L3GD20 3 Axis Digital Gyroscope Sensor device driver iio
>
> +* :
>
> +********************************************************************************
>
> +*
>
> +* This program is free software; you can redistribute it and/or modify
>
> +* it under the terms of the GNU General Public License version 2 as
>
> +* published by the Free Software Foundation.
>
> +*
>
> +* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
>
> +* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
>
> +* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
>
> +* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
>
> +* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
>
> +* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
>
> +* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
>
> +*
>
> +*******************************************************************************/
>
> +/*******************************************************************************
>
> +Version History.
>
> + Revision 2.0.0: 2012/Nov/16
>
> + first ioo implementation;
>
> +*******************************************************************************/
>
> +
>
> +#ifndef __L3GD20_H__
>
> +#define __L3GD20_H__
>
> +
>
> +
>
> +#define L3GD20_GYR_DEV_NAME "l3gd20_gyr_iio"
>
> +
>
> +#define L3GD20_GYR_FS_250DPS 0x00
>
> +#define L3GD20_GYR_FS_500DPS 0x10
>
> +#define L3GD20_GYR_FS_2000DPS 0x30
>
> +
>
> +#define L3GD20_GYR_ENABLED 1
>
> +#define L3GD20_GYR_DISABLED 0
>
> +
>
> +
>
> +#ifdef __KERNEL__
>
> +
>
> +/* to set gpios numb connected to gyro interrupt pins,
>
> + * the unused ones have to be set to -EINVAL
>
> + */
>
> +#define DEFAULT_INT1_GPIO (-EINVAL)
>
> +#define DEFAULT_INT2_GPIO (-EINVAL)
>
> +
>
> +#define SAD0L 0x00
>
> +#define SAD0H 0x01
>
> +#define L3GD20_GYR_I2C_SADROOT 0x35
>
> +#define L3GD20_GYR_I2C_SAD_L ((L3GD20_GYR_I2C_SADROOT<<1)|SAD0L)
>
> +#define L3GD20_GYR_I2C_SAD_H ((L3GD20_GYR_I2C_SADROOT<<1)|SAD0H)
>
> +
>
> +#define L3GD20_MIN_POLL_PERIOD_MS 2
>
> +
>
> +struct l3gd20_gyr_platform_data {
>
> + int (*init)(void);
>
> + void (*exit)(void);
>
> + int (*power_on)(void);
>
> + int (*power_off)(void);
>
> + unsigned int poll_interval;
>
> + unsigned int min_interval;
>
> +
>
> + u8 fs_range;
>
> + u8 sampling_frequency;
>
> +
>
> + /* set gpio_int[1,2] either to the choosen gpio pin number or to -EINVAL
>
> + * if leaved unconnected
>
> + */
>
> + int gpio_int1;
>
> + int gpio_int2; /* int for fifo */
>
> +
>
> + /* axis mapping */
>
> + u8 axis_map_x;
>
> + u8 axis_map_y;
>
> + u8 axis_map_z;
>
> +
>
> + u8 negate_x;
>
> + u8 negate_y;
>
> + u8 negate_z;
>
> +};
>
> +
>
> +#ifdef CONFIG_IIO_RING_BUFFER
>
> +/* At the moment triggers are only used for ring buffer
>
> + * filling. This may change!
>
> + */
>
> +
>
> +enum l3gd20_gyr_scan {
>
> + L3GD20_GYR_SCAN_GYR_X,
>
> + L3GD20_GYR_SCAN_GYR_Y,
>
> + L3GD20_GYR_SCAN_GYR_Z,
>
> +};
>
> +
>
> +void l3gd20_gyr_remove_trigger(struct iio_dev *indio_dev);
>
> +int l3gd20_gyr_probe_trigger(struct iio_dev *indio_dev);
>
> +
>
> +ssize_t l3gd20_gyr_read_data_from_ring(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf);
>
> +
>
> +
>
> +int l3gd20_gyr_configure_ring(struct iio_dev *indio_dev);
>
> +void l3gd20_gyr_unconfigure_ring(struct iio_dev *indio_dev);
>
> +
>
> +int l3gd20_gyr_initialize_ring(struct iio_ring_buffer *ring);
>
> +void l3gd20_gyr_uninitialize_ring(struct iio_ring_buffer *ring);
>
> +#else /* CONFIG_IIO_RING_BUFFER */
>
> +
>
> +static inline void l3gd20_gyr_remove_trigger(struct iio_dev *indio_dev)
>
> +{
>
> +}
>
> +
>
> +static inline int l3gd20_gyr_probe_trigger(struct iio_dev *indio_dev)
>
> +{
>
> + return 0;
>
> +}
>
> +
>
> +static inline ssize_t
>
> +l3gd20_gyr_read_data_from_ring(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + return 0;
>
> +}
>
> +
>
> +static int l3gd20_gyr_configure_ring(struct iio_dev *indio_dev)
>
> +{
>
> + return 0;
>
> +}
>
> +
>
> +static inline void l3gd20_gyr_unconfigure_ring(struct iio_dev *indio_dev)
>
> +{
>
> +}
>
> +
>
> +static inline int l3gd20_gyr_initialize_ring(struct iio_ring_buffer *ring)
>
> +{
>
> + return 0;
>
> +}
>
> +
>
> +static inline void l3gd20_gyr_uninitialize_ring(struct iio_ring_buffer *ring)
>
> +{
>
> +}
>
> +
>
> +#endif /* CONFIG_IIO_RING_BUFFER */
>
> +
>
> +#endif /* __KERNEL__ */
>
> +
>
> +#endif /* __L3GD20_H__ */
>
> diff --git a/drivers/staging/iio/gyro/l3gd20_gyr_core.c b/drivers/staging/iio/gyro/l3gd20_gyr_core.c
>
> index e69de29..64871b6 100644
>
> --- a/drivers/staging/iio/gyro/l3gd20_gyr_core.c
>
> +++ b/drivers/staging/iio/gyro/l3gd20_gyr_core.c
>
> @@ -0,0 +1,1056 @@
>
> +/******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
>
> +*
>
> +* File Name : l3gd20_gyr_core_iio.c
>
> +* Authors : MSH - Motion Mems BU - Application Team
>
> +* : Matteo Dameno (matteo.dameno@st.com)
>
> +* : author is willing to be considered the contact
>
> +* : and update points for the driver.*
>
> +* Version : V.2.0.0
>
> +* Date : 2011/Nov/16
>
> +* Description : L3GD20 3 Axis Digital Gyroscope Sensor device driver iio
>
> +* :
>
> +********************************************************************************
>
> +*
>
> +* This program is free software; you can redistribute it and/or modify
>
> +* it under the terms of the GNU General Public License version 2 as
>
> +* published by the Free Software Foundation.
>
> +*
>
> +* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
>
> +* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
>
> +* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
>
> +* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
>
> +* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
>
> +* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
>
> +* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
>
> +*
>
> +********************************************************************************
>
> + Revision 2.0.0: 2012/Nov/16
>
> + first ioo implementation;
>
> +
>
> +*******************************************************************************/
>
> +
>
> +#include <linux/err.h>
>
> +#include <linux/errno.h>
>
> +#include <linux/delay.h>
>
> +#include <linux/fs.h>
>
> +#include <linux/i2c.h>
>
> +#include <linux/mutex.h>
>
> +#include <linux/input.h>
>
> +#include <linux/uaccess.h>
>
> +#include <linux/workqueue.h>
>
> +#include <linux/irq.h>
>
> +#include <linux/gpio.h>
>
> +#include <linux/interrupt.h>
>
> +#include <linux/gpio.h>
>
> +#include <linux/slab.h>
>
> +#include <linux/device.h>
>
> +#include <linux/kernel.h>
>
> +
>
> +#include <linux/sysfs.h>
>
> +
>
> +#include <linux/moduleparam.h>
>
> +
>
> +#include "../iio.h"
>
> +#include "../sysfs.h"
>
> +#include "gyro.h"
>
> +#include "l3gd20.h"
>
> +
>
> +
>
> +#define SENSITIVITY_250DPS "8.75" /** mdps/counts */
>
> +#define SENSITIVITY_500DPS "17.50" /** mdps/counts */
>
> +#define SENSITIVITY_2000DPS "70.00" /** mdps/counts */
>
> +
>
> +#define SCALE_250DPS "0.000153" /** (rad/sec)/count */
>
> +#define SCALE_500DPS "0.000297" /** (rad/sec)/count */
>
> +#define SCALE_2000DPS "0.001222" /** (rad/sec)/count */
>
> +
>
> +#define GAIN_250DPS "6548.089087" /** count/(rad/sec) */
>
> +#define GAIN_500DPS "3370.339971" /** count/(rad/sec) */
>
> +#define GAIN_2000DPS "818.511136" /** count/(rad/sec) */
>
> +
>
> +/* l3gd20 gyroscope registers */
>
> +#define WHO_AM_I (0x0F)
>
> +
>
> +#define CTRL_REG1 (0x20) /* CTRL REG1 */
>
> +#define CTRL_REG2 (0x21) /* CTRL REG2 */
>
> +#define CTRL_REG3 (0x22) /* CTRL_REG3 */
>
> +#define CTRL_REG4 (0x23) /* CTRL_REG4 */
>
> +#define CTRL_REG5 (0x24) /* CTRL_REG5 */
>
> +#define REFERENCE (0x25) /* REFERENCE REG */
>
> +
>
> +#define OUT_X_LSB (0x28) /* 1st AXIS OUT REG of 6 */
>
> +#define OUT_X_MSB (0x29)
>
> +#define OUT_Y_LSB (0x2A)
>
> +#define OUT_Y_MSB (0x2B)
>
> +#define OUT_Z_LSB (0x2C)
>
> +#define OUT_Z_MSB (0x2D)
>
> +#define AXISDATA_REG OUT_X_LSB
>
> +
>
> +#define FIFO_CTRL_REG (0x2E) /* FIFO CONTROL REGISTER */
>
> +#define FIFO_SRC_REG (0x2F) /* FIFO SOURCE REGISTER */
>
> +/* */
>
> +
>
> +/* CTRL_REG1 */
>
> +#define ALL_ZEROES (0x00)
>
> +#define PM_OFF (0x00)
>
> +#define PM_NORMAL (0x08)
>
> +#define ENABLE_ALL_AXES (0x07)
>
> +#define ENABLE_NO_AXES (0x00)
>
> +#define BW00 (0x00)
>
> +#define BW01 (0x10)
>
> +#define BW10 (0x20)
>
> +#define BW11 (0x30)
>
> +#define CTRL_REG1_BW_MASK (0x30)
>
> +#define ODR095 (0x00) /* ODR = 95Hz */
>
> +#define ODR190 (0x40) /* ODR = 190Hz */
>
> +#define ODR380 (0x80) /* ODR = 380Hz */
>
> +#define ODR760 (0xC0) /* ODR = 760Hz */
>
> +#define CTRL_REG1_ODR_MASK (0xC0)
>
> +
>
> +
>
> +/* CTRL_REG3 bits */
>
> +#define I2_DRDY (0x08)
>
> +#define I2_WTM (0x04)
>
> +#define I2_OVRUN (0x02)
>
> +#define I2_EMPTY (0x01)
>
> +#define I2_NONE (0x00)
>
> +#define I2_MASK (0x0F)
>
> +
>
> +/* CTRL_REG4 bits */
>
> +#define FS_MASK (0x30)
>
> +#define BDU_ENABLE (0x80)
>
> +
>
> +/* CTRL_REG5 bits */
>
> +#define FIFO_ENABLE (0x40)
>
> +#define HPF_ENALBE (0x11)
>
> +
>
> +/* FIFO_CTRL_REG bits */
>
> +#define FIFO_MODE_MASK (0xE0)
>
> +#define FIFO_MODE_BYPASS (0x00)
>
> +#define FIFO_MODE_FIFO (0x20)
>
> +#define FIFO_MODE_STREAM (0x40)
>
> +#define FIFO_MODE_STR2FIFO (0x60)
>
> +#define FIFO_MODE_BYPASS2STR (0x80)
>
> +#define FIFO_WATERMARK_MASK (0x1F)
>
> +
>
> +#define FIFO_STORED_DATA_MASK (0x1F)
>
> +
>
> +/* I2C multi read/write option*/
>
> +#define I2C_AUTO_INCREMENT (0x80)
>
> +
>
> +/* RESUME STATE INDICES */
>
> +#define RES_CTRL_REG1 0
>
> +#define RES_CTRL_REG2 1
>
> +#define RES_CTRL_REG3 2
>
> +#define RES_CTRL_REG4 3
>
> +#define RES_CTRL_REG5 4
>
> +#define RES_FIFO_CTRL_REG 5
>
> +#define RESUME_ENTRIES 6
>
> +
>
> +
>
> +/** Registers Contents */
>
> +#define WHOAMI_L3GD20 (0x00D4) /* Expected content for WAI register*/
>
> +
>
> +
>
> +struct output_rate {
>
> + unsigned int poll_rate_ms;
>
> + u8 sampling_rate_setting;
>
> + const char *hz_label;
>
> +};
>
> +
>
> +static const struct output_rate l3gd20_gyr_odr_table[] = {
>
> +
>
> + { 2, ODR760|BW10, "760"},
>
> + { 3, ODR380|BW01, "380"},
>
> + { 6, ODR190|BW00, "190"},
>
> + { 11, ODR095|BW00, "95"},
>
> +};
>
> +
>
> +static struct l3gd20_gyr_platform_data default_l3gd20_gyr_pdata = {
>
> + .fs_range = L3GD20_GYR_FS_250DPS,
>
> + .axis_map_x = 0,
>
> + .axis_map_y = 1,
>
> + .axis_map_z = 2,
>
> + .negate_x = 0,
>
> + .negate_y = 0,
>
> + .negate_z = 0,
>
> +
>
> + .gpio_int1 = DEFAULT_INT1_GPIO,
>
> + .gpio_int2 = DEFAULT_INT2_GPIO, /* int for fifo */
>
> +
>
> +};
>
> +
>
> +struct l3gd20_gyr_data {
>
> +
>
> + struct work_struct work_trigger_to_ring;
>
> + struct iio_work_cont work_cont_thresh;
>
> + s64 last_timestamp;
>
> + struct iio_dev *indio_dev;
>
> + struct iio_trigger *trig;
>
> +
>
> + struct i2c_client *client;
>
> + struct l3gd20_gyr_platform_data *pdata;
>
> + int use_smbus;
>
> +
>
> + struct mutex lock;
>
> + int hw_initialized;
>
> +
>
> + atomic_t enabled;
>
> +
>
> + char const *scale;
>
> + char const *gain;
>
> +
>
> + u8 reg_addr;
>
> + u8 resume_state[RESUME_ENTRIES];
>
> +
>
> +
>
> + int irq1;
>
> + int irq2;
>
> +
>
> +};
>
> +
>
> +
>
> +static int l3gd20_gyr_i2c_read(struct l3gd20_gyr_data *data,
>
> + u8 *buf, int len)
>
> +{
>
> + int ret;
>
> + u8 reg = buf[0];
>
> + u8 cmd = reg;
>
> +
>
> + if (len > 1)
>
> + cmd = (I2C_AUTO_INCREMENT | reg);
>
> + if (data->use_smbus) {
>
> + if (len == 1) {
>
> + ret = i2c_smbus_read_byte_data(data->client, cmd);
>
> + buf[0] = ret & 0xff;
>
> +#ifdef DEBUG
>
> + dev_dbg(&data->client->dev,
>
> + "i2c_smbus_read_byte_data: ret=0x%02x, len:%d ,"
>
> + "command=0x%02x, buf[0]=0x%02x\n",
>
> + ret, len, cmd , buf[0]);
>
> +#endif
>
> + } else if (len > 1) {
>
> + ret = i2c_smbus_read_i2c_block_data(data->client,
>
> + cmd, len, buf);
>
> +#ifdef DEBUG
>
> + dev_dbg(&data->client->dev,
>
> + "i2c_smbus_read_i2c_block_data: ret:%d len:%d, "
>
> + "command=0x%02x, ",
>
> + ret, len, cmd);
>
> + char ii;
>
> + for (ii = 0; ii < len; ii++)
>
> + printk(KERN_DEBUG "buf[%d]=0x%02x,",
>
> + ii, buf[ii]);
>
> + printk("\n");
>
> +#endif
>
> + } else
>
> + ret = -1;
>
> +
>
> + if (ret < 0) {
>
> + dev_err(&data->client->dev,
>
> + "read transfer error: len:%d, command=0x%02x\n",
>
> + len, cmd);
>
> + return 0; /* failure */
>
> + }
>
> + return len; /* success */
>
> + }
>
> +
>
> +
>
> + ret = i2c_master_send(data->client, &cmd, sizeof(cmd));
>
> + if (ret != sizeof(cmd))
>
> + return ret;
>
> +
>
> + return i2c_master_recv(data->client, buf, len);
>
> +}
>
> +
>
> +static int l3gd20_gyr_i2c_write(struct l3gd20_gyr_data *data, u8 *buf,
>
> + int len)
>
> +{
>
> + int ret;
>
> + u8 reg, value;
>
> +
>
> + if (len > 1)
>
> + buf[0] = (I2C_AUTO_INCREMENT | buf[0]);
>
> +
>
> + reg = buf[0];
>
> + value = buf[1];
>
> +
>
> + if (data->use_smbus) {
>
> + if (len == 1) {
>
> + ret = i2c_smbus_write_byte_data(data->client, reg,
>
> + value);
>
> +#ifdef DEBUG
>
> + dev_dbg(&data->client->dev,
>
> + "i2c_smbus_write_byte_data: ret=%d, len:%d, "
>
> + "command=0x%02x, value=0x%02x\n",
>
> + ret, len, reg , value);
>
> +#endif
>
> + return ret;
>
> + } else if (len > 1) {
>
> + ret = i2c_smbus_write_i2c_block_data(data->client,
>
> + reg, len, buf + 1);
>
> +#ifdef DEBUG
>
> + dev_dbg(&data->client->dev,
>
> + "i2c_smbus_write_i2c_block_data: ret=%d, "
>
> + "len:%d, command=0x%02x, ",
>
> + ret, len, reg);
>
> + char ii;
>
> + for (ii = 0; ii < len + 1; ii++)
>
> + printk(KERN_DEBUG "value[%d]=0x%02x,",
>
> + ii, buf[ii]);
>
> +
>
> + printk("\n");
>
> +#endif
>
> + return ret;
>
> + }
>
> + }
>
> +
>
> + ret = i2c_master_send(data->client, buf, len+1);
>
> + return (ret == len+1) ? 0 : ret;
>
> +}
>
> +
>
> +
>
> +static int l3gd20_gyr_register_write(struct l3gd20_gyr_data *data,
>
> + u8 *buf, u8 reg_address, u8 new_value)
>
> +{
>
> + int err;
>
> +
>
> + /* Sets configuration register at reg_address
>
> + * NOTE: this is a straight overwrite */
>
> + buf[0] = reg_address;
>
> + buf[1] = new_value;
>
> + err = l3gd20_gyr_i2c_write(data, buf, 1);
>
> + if (err < 0)
>
> + return err;
>
> +
>
> + return err;
>
> +}
>
> +
>
> +static int l3gd20_gyr_register_read(struct l3gd20_gyr_data *data,
>
> + u8 *buf, u8 reg_address)
>
> +{
>
> +
>
> + int err = -1;
>
> + buf[0] = (reg_address);
>
> + err = l3gd20_gyr_i2c_read(data, buf, 1);
>
> + return err;
>
> +}
>
> +
>
> +static int l3gd20_gyr_register_update(struct l3gd20_gyr_data *data,
>
> + u8 *buf, u8 reg_address, u8 mask, u8 new_bit_values)
>
> +{
>
> + int err = -1;
>
> + u8 init_val;
>
> + u8 updated_val;
>
> + err = l3gd20_gyr_register_read(data, buf, reg_address);
>
> + if (!(err < 0)) {
>
> + init_val = buf[0];
>
> + updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
>
> + err = l3gd20_gyr_register_write(data, buf, reg_address,
>
> + updated_val);
>
> + }
>
> + return err;
>
> +}
>
> +
>
> +static int l3gd20_gyr_hw_init(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> + int err;
>
> + u8 buf[6];
>
> +
>
> + pr_info("%s hw init\n", L3GD20_GYR_DEV_NAME);
>
> +
>
> + buf[0] = (CTRL_REG1);
>
> + buf[1] = dev_data->resume_state[RES_CTRL_REG1];
>
> + buf[2] = dev_data->resume_state[RES_CTRL_REG2];
>
> + buf[3] = dev_data->resume_state[RES_CTRL_REG3];
>
> + buf[4] = dev_data->resume_state[RES_CTRL_REG4];
>
> + buf[5] = dev_data->resume_state[RES_CTRL_REG5];
>
> +
>
> + err = l3gd20_gyr_i2c_write(dev_data, buf, 5);
>
> + if (err < 0)
>
> + return err;
>
> +
>
> + buf[0] = FIFO_CTRL_REG;
>
> + buf[1] = dev_data->resume_state[RES_FIFO_CTRL_REG];
>
> + err = l3gd20_gyr_i2c_write(dev_data, buf, 1);
>
> + if (err < 0)
>
> + return err;
>
> +
>
> + dev_data->hw_initialized = 1;
>
> +
>
> + return err;
>
> +}
>
> +
>
> +static void l3gd20_gyr_device_power_off(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> + int err;
>
> + u8 buf[2];
>
> +
>
> + pr_info("%s power off\n", L3GD20_GYR_DEV_NAME);
>
> +
>
> + buf[0] = CTRL_REG1;
>
> + buf[1] = PM_OFF;
>
> + err = l3gd20_gyr_i2c_write(dev_data, buf, 1);
>
> + if (err < 0)
>
> + dev_err(&dev_data->client->dev, "soft power off failed\n");
>
> +
>
> + if (dev_data->pdata->power_off) {
>
> + dev_data->pdata->power_off();
>
> + dev_data->hw_initialized = 0;
>
> + }
>
> +
>
> + if (dev_data->hw_initialized)
>
> + dev_data->hw_initialized = 0;
>
> +}
>
> +
>
> +static int l3gd20_gyr_device_power_on(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> + int err;
>
> +
>
> + if (dev_data->pdata->power_on) {
>
> + err = dev_data->pdata->power_on();
>
> + if (err < 0)
>
> + return err;
>
> + }
>
> +
>
> +
>
> + if (!dev_data->hw_initialized) {
>
> + err = l3gd20_gyr_hw_init(dev_data);
>
> + if (err < 0) {
>
> + l3gd20_gyr_device_power_off(dev_data);
>
> + return err;
>
> + }
>
> + }
>
> +
>
> + return 0;
>
> +}
>
> +
>
> +
>
> +static int l3gd20_gyr_enable(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> + int err;
>
> +
>
> + if (!atomic_cmpxchg(&dev_data->enabled, 0, 1)) {
>
> +
>
> + err = l3gd20_gyr_device_power_on(dev_data);
>
> + if (err < 0) {
>
> + atomic_set(&dev_data->enabled, 0);
>
> + return err;
>
> + }
>
> +
>
> + }
>
> +
>
> + return 0;
>
> +}
>
> +
>
> +static int l3gd20_gyr_disable(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> +
>
> + printk(KERN_DEBUG "%s: dev_data->enabled = %d\n", __func__,
>
> + atomic_read(&dev_data->enabled));
>
> +
>
> + if (atomic_cmpxchg(&dev_data->enabled, 1, 0))
>
> + l3gd20_gyr_device_power_off(dev_data);
>
> +
>
> + return 0;
>
> +}
>
> +
>
> +static int l3gd20_gyr_update_fs_range(struct l3gd20_gyr_data *dev_data,
>
> + u8 new_fs_range)
>
> +{
>
> + int res ;
>
> + u8 buf[2];
>
> + char const *scale;
>
> + char const *gain;
>
> + buf[0] = CTRL_REG4;
>
> +
>
> + switch (new_fs_range) {
>
> + case L3GD20_GYR_FS_250DPS:
>
> + gain = GAIN_250DPS;
>
> + scale = SCALE_250DPS;
>
> + break;
>
> +
>
> + case L3GD20_GYR_FS_500DPS:
>
> + gain = GAIN_500DPS;
>
> + scale = SCALE_500DPS;
>
> + break;
>
> +
>
> + case L3GD20_GYR_FS_2000DPS:
>
> + gain = GAIN_2000DPS;
>
> + scale = SCALE_2000DPS;
>
> + break;
>
> +
>
> + default:
>
> + dev_err(&dev_data->client->dev,
>
> + "invalid fs range requested: %u\n",
>
> + new_fs_range);
>
> + return -EINVAL;
>
> + }
>
> +
>
> + res = l3gd20_gyr_register_update(dev_data, buf, CTRL_REG4,
>
> + FS_MASK, new_fs_range);
>
> +
>
> + if (res < 0) {
>
> + pr_err("%s : failed to update fs:0x%02x\n",
>
> + __func__, new_fs_range);
>
> + return res;
>
> + }
>
> + dev_data->resume_state[RES_CTRL_REG4] =
>
> + ((FS_MASK & new_fs_range) |
>
> + (~FS_MASK & dev_data->resume_state[RES_CTRL_REG4]));
>
> + dev_data->gain = gain;
>
> + dev_data->scale = scale;
>
> + return res;
>
> +}
>
> +
>
> +static int l3gd20_gyr_update_sampling_freq(struct l3gd20_gyr_data *data,
>
> + u8 freq)
>
> +{
>
> + int res = 0;
>
> + u8 buf[2];
>
> + u8 new_value;
>
> + u8 mask = (CTRL_REG1_ODR_MASK|CTRL_REG1_BW_MASK);
>
> +
>
> +
>
> + new_value = freq;
>
> + res = l3gd20_gyr_register_update(data, buf, CTRL_REG1,
>
> + mask, new_value);
>
> +
>
> + if (res < 0) {
>
> + dev_err(&data->client->dev,
>
> + "%s : failed to update sampling frequency setting\n",
>
> + __func__);
>
> + return res;
>
> + }
>
> + dev_dbg(&data->client->dev, "%s : new_value:0x%02x, freq:0x%02x\n",
>
> + __func__, new_value, freq);
>
> +
>
> + data->resume_state[RES_CTRL_REG1] =
>
> + ((mask & new_value) |
>
> + (~mask & data->resume_state[RES_CTRL_REG1]));
>
> +
>
> + data->pdata->sampling_frequency = new_value;
>
> +
>
> + return res;
>
> +}
>
> +
>
> +static ssize_t l3gd20_gyr_show_sampling_frequency(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + u8 i;
>
> + u8 val;
>
> +
>
> +
>
> + mutex_lock(&data->lock);
>
> + val = data->pdata->sampling_frequency;
>
> + mutex_unlock(&data->lock);
>
> + dev_dbg(dev, "getting sampling frequency: 0x%02x\n", val);
>
> +
>
> + for (i = 0; i < ARRAY_SIZE(l3gd20_gyr_odr_table); i++)
>
> + if (l3gd20_gyr_odr_table[i].sampling_rate_setting == val)
>
> + break;
>
> +
>
> + return sprintf(buf, "%s\n", l3gd20_gyr_odr_table[i].hz_label);
>
> +}
>
> +
>
> +static ssize_t l3gd20_gyr_store_sampling_frequency(struct device *dev,
>
> + struct device_attribute *attr,
>
> + const char *buf, size_t size)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + u8 found = 0;
>
> + u8 i;
>
> + u8 ret;
>
> +
>
> + for (i = 0; i < ARRAY_SIZE(l3gd20_gyr_odr_table); i++) {
>
> + if (strcmp(buf, l3gd20_gyr_odr_table[i].hz_label) == 0) {
>
> + found = 1;
>
> + break;
>
> + }
>
> + }
>
> +
>
> + if (!found) {
>
> + dev_err(dev, "trying to set invalid sampling frequency: %s\n"
>
> + "new sampling frequency not set!\n", buf);
>
> + return -EINVAL;
>
> + } else {
>
> + mutex_lock(&data->lock);
>
> + ret = l3gd20_gyr_update_sampling_freq(data,
>
> + l3gd20_gyr_odr_table[i].sampling_rate_setting);
>
> + if (ret < 0)
>
> + goto error;
>
> + data->pdata->sampling_frequency =
>
> + l3gd20_gyr_odr_table[i].sampling_rate_setting;
>
> + dev_info(dev, "set new sampling frequency: %s Hz\n",
>
> + l3gd20_gyr_odr_table[i].hz_label);
>
> + mutex_unlock(&data->lock);
>
> + }
>
> + return size;
>
> +error:
>
> + mutex_unlock(&data->lock);
>
> + return ret;
>
> +}
>
> +
>
> +
>
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>
> + l3gd20_gyr_show_sampling_frequency,
>
> + l3gd20_gyr_store_sampling_frequency);
>
> +
>
> +static ssize_t l3gd20_gyr_show_enable(struct device *dev,
>
> + struct device_attribute *attr, char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + int val = atomic_read(&data->enabled);
>
> + return sprintf(buf, "%d\n", val);
>
> +}
>
> +
>
> +static ssize_t l3gd20_gyr_store_enable(struct device *dev,
>
> + struct device_attribute *attr,
>
> + const char *buf, size_t size)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + unsigned long val;
>
> +
>
> + if (strict_strtoul(buf, 10, &val))
>
> + return -EINVAL;
>
> +
>
> + if (val)
>
> + l3gd20_gyr_enable(data);
>
> + else
>
> + l3gd20_gyr_disable(data);
>
> +
>
> + return size;
>
> +}
>
> +
>
> +static IIO_DEVICE_ATTR(enable_device,
>
> + S_IWUSR | S_IRUGO,
>
> + l3gd20_gyr_show_enable,
>
> + l3gd20_gyr_store_enable,
>
> + 0);
>
> +
>
> +static int l3gd20_gyr_show_channel_rawdata(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>
> + int err;
>
> +
>
> + /* Data bytes from hardware (GxL, GxH) or (GyL, GyH) or (GzL, GzH) */
>
> + u8 out_data[2];
>
> + /* x,y,z hardware data */
>
> + s16 raw_d;
>
> +
>
> + out_data[0] = this_attr->address;
>
> +
>
> + mutex_lock(&data->lock);
>
> + err = l3gd20_gyr_i2c_read(data, out_data, 2);
>
> + mutex_unlock(&data->lock);
>
> + if (err < 0) {
>
> + dev_err(dev, "error reading output registers\n");
>
> + return err;
>
> + }
>
> +
>
> + raw_d = ((s16) ((out_data[1]) << 8) | out_data[0]);
>
> + return sprintf(buf, "%d\n", raw_d);
>
> +
>
> +}
>
> +
>
> +static IIO_DEV_ATTR_GYRO_X(l3gd20_gyr_show_channel_rawdata, OUT_X_LSB);
>
> +static IIO_DEV_ATTR_GYRO_Y(l3gd20_gyr_show_channel_rawdata, OUT_Y_LSB);
>
> +static IIO_DEV_ATTR_GYRO_Z(l3gd20_gyr_show_channel_rawdata, OUT_Z_LSB);
>
> +
>
> +static ssize_t l3gd20_gyr_show_range(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + int label = 0;
>
> + u8 val;
>
> + mutex_lock(&data->lock);
>
> + val = data->pdata->fs_range;
>
> + mutex_unlock(&data->lock);
>
> +
>
> + switch (val) {
>
> + case L3GD20_GYR_FS_250DPS:
>
> + label = 250;
>
> + break;
>
> + case L3GD20_GYR_FS_500DPS:
>
> + label = 500;
>
> + break;
>
> + case L3GD20_GYR_FS_2000DPS:
>
> + label = 2000;
>
> + break;
>
> + }
>
> +
>
> + return sprintf(buf, "%d dps\n", label);
>
> +}
>
> +
>
> +static ssize_t l3gd20_gyr_store_range(struct device *dev,
>
> + struct device_attribute *attr,
>
> + const char *buf,
>
> + size_t size)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + unsigned long val;
>
> + u8 range;
>
> + int err = -1;
>
> + if (strict_strtoul(buf, 10, &val)) {
>
> + dev_err(dev, "invalid range %lu\n. Range not changed", val);
>
> + return -EINVAL;
>
> + }
>
> + switch (val) {
>
> + case 250:
>
> + range = L3GD20_GYR_FS_250DPS;
>
> + break;
>
> + case 500:
>
> + range = L3GD20_GYR_FS_500DPS;
>
> + break;
>
> + case 2000:
>
> + range = L3GD20_GYR_FS_2000DPS;
>
> + break;
>
> + default:
>
> + dev_err(dev, "invalid range %lu. Range not changed\n", val);
>
> + return -EINVAL;
>
> + }
>
> +
>
> + mutex_lock(&data->lock);
>
> + data->pdata->fs_range = range;
>
> + err = l3gd20_gyr_update_fs_range(data, range);
>
> + mutex_unlock(&data->lock);
>
> + if (err < 0)
>
> + goto error;
>
> + dev_info(dev, "set range to %lu dps\n", val);
>
> + return size;
>
> +error:
>
> + dev_err(dev, "error changing range\n");
>
> + return err;
>
> +}
>
> +
>
> +
>
> +
>
> +static IIO_DEVICE_ATTR(gyro_range,
>
> + S_IWUSR | S_IRUGO,
>
> + l3gd20_gyr_show_range,
>
> + l3gd20_gyr_store_range,
>
> + 0);
>
> +
>
> +
>
> +static IIO_CONST_ATTR(gyro_range_available, "250 500 2000");
>
> +
>
> +
>
> +static ssize_t l3gd20_gyr_show_gain(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + return sprintf(buf, "%s\n", data->gain);
>
> +}
>
> +
>
> +static IIO_DEVICE_ATTR(gyro_gain, S_IRUGO,
>
> + l3gd20_gyr_show_gain,
>
> + NULL , 0);
>
> +
>
> +
>
> +static ssize_t l3gd20_gyr_show_scale(struct device *dev,
>
> + struct device_attribute *attr,
>
> + char *buf)
>
> +{
>
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>
> + struct l3gd20_gyr_data *data = indio_dev->dev_data;
>
> + return sprintf(buf, "%s\n", data->scale);
>
> +}
>
> +
>
> +static IIO_DEVICE_ATTR(gyro_scale, S_IRUGO,
>
> + l3gd20_gyr_show_scale,
>
> + NULL , 0);
>
> +
>
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("95 190 380 760");
>
> +
>
> +static IIO_CONST_ATTR(name, L3GD20_GYR_DEV_NAME);
>
> +
>
> +
>
> +
>
> +static struct attribute *l3gd20_gyr_attributes[] = {
>
> + &iio_const_attr_name.dev_attr.attr,
>
> + &iio_dev_attr_enable_device.dev_attr.attr,
>
> + &iio_const_attr_gyro_range_available.dev_attr.attr,
>
> + &iio_dev_attr_gyro_range.dev_attr.attr,
>
> + &iio_dev_attr_gyro_gain.dev_attr.attr,
>
> + &iio_dev_attr_gyro_scale.dev_attr.attr,
>
> + &iio_dev_attr_gyro_x_raw.dev_attr.attr,
>
> + &iio_dev_attr_gyro_y_raw.dev_attr.attr,
>
> + &iio_dev_attr_gyro_z_raw.dev_attr.attr,
>
> + &iio_dev_attr_sampling_frequency.dev_attr.attr,
>
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
>
> + NULL
>
> +};
>
> +
>
> +static const struct attribute_group l3gd20_gyr_attribute_group = {
>
> + .attrs = l3gd20_gyr_attributes,
>
> +};
>
> +
>
> +static int l3gd20_gyr_validate_pdata(struct l3gd20_gyr_data *dev_data)
>
> +{
>
> +
>
> + if (dev_data->pdata->axis_map_x > 2 ||
>
> + dev_data->pdata->axis_map_y > 2 ||
>
> + dev_data->pdata->axis_map_z > 2) {
>
> + dev_err(&dev_data->client->dev,
>
> + "invalid axis_map value x:%u y:%u z%u\n",
>
> + dev_data->pdata->axis_map_x,
>
> + dev_data->pdata->axis_map_y,
>
> + dev_data->pdata->axis_map_z);
>
> + return -EINVAL;
>
> + }
>
> +
>
> + /* Only allow 0 and 1 for negation boolean flag */
>
> + if (dev_data->pdata->negate_x > 1 ||
>
> + dev_data->pdata->negate_y > 1 ||
>
> + dev_data->pdata->negate_z > 1) {
>
> + dev_err(&dev_data->client->dev,
>
> + "invalid negate value x:%u y:%u z:%u\n",
>
> + dev_data->pdata->negate_x,
>
> + dev_data->pdata->negate_y,
>
> + dev_data->pdata->negate_z);
>
> + return -EINVAL;
>
> + }
>
> +
>
> + return 0;
>
> +}
>
> +
>
> +static int l3gd20_gyr_probe(struct i2c_client *client,
>
> + const struct i2c_device_id *id)
>
> +{
>
> + struct l3gd20_gyr_data *data;
>
> + int err = -1;
>
> + data = kzalloc(sizeof(struct l3gd20_gyr_data), GFP_KERNEL);
>
> + if (data == NULL) {
>
> + err = -ENOMEM;
>
> + dev_err(&client->dev,
>
> + "failed to allocate memory for module data: "
>
> + "%d\n", err);
>
> + goto exit_check_functionality_failed;
>
> + }
>
> +
>
> + u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |
>
> + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK ;
>
> +
>
> + int regdone = 0;
>
> +
>
> + pr_info("%s: probe start.\n", L3GD20_GYR_DEV_NAME);
>
> +
>
> +
>
> + /* Support for both I2C and SMBUS adapter interfaces. */
>
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
>
> + dev_warn(&client->dev, "client not i2c capable\n");
>
> + if (i2c_check_functionality(client->adapter, smbus_func)) {
>
> + data->use_smbus = 1;
>
> + dev_warn(&client->dev, "client using SMBUS\n");
>
> + } else {
>
> + err = -ENODEV;
>
> + dev_err(&client->dev, "client nor SMBUS capable\n");
>
> + data->use_smbus = 0;
>
> + goto exit_check_functionality_failed;
>
> + }
>
> + }
>
> +
>
> + mutex_init(&data->lock);
>
> + mutex_lock(&data->lock);
>
> + data->client = client;
>
> + i2c_set_clientdata(client, data);
>
> +
>
> + data->pdata = kmalloc(sizeof(*data->pdata), GFP_KERNEL);
>
> + if (data->pdata == NULL) {
>
> + err = -ENOMEM;
>
> + dev_err(&client->dev,
>
> + "failed to allocate memory for pdata: %d\n",
>
> + err);
>
> + goto err_mutexunlock;
>
> + }
>
> +
>
> + if (client->dev.platform_data == NULL) {
>
> + memcpy(data->pdata, &default_l3gd20_gyr_pdata,
>
> + sizeof(*data->pdata));
>
> + dev_info(&client->dev, "using default platform data\n");
>
> + } else {
>
> + memcpy(data->pdata, client->dev.platform_data,
>
> + sizeof(*data->pdata));
>
> + }
>
> +
>
> + err = l3gd20_gyr_validate_pdata(data);
>
> + if (err < 0) {
>
> + dev_err(&client->dev, "failed to validate platform data\n");
>
> + goto exit_kfree_pdata;
>
> + }
>
> +
>
> +
>
> + if (data->pdata->init) {
>
> + err = data->pdata->init();
>
> + if (err < 0) {
>
> + dev_err(&client->dev, "init failed: %d\n", err);
>
> + goto err_pdata_init;
>
> + }
>
> + }
>
> +
>
> + memset(data->resume_state, 0, ARRAY_SIZE(data->resume_state));
>
> +
>
> + data->resume_state[RES_CTRL_REG1] = (ALL_ZEROES | PM_NORMAL
>
> + | ENABLE_ALL_AXES);
>
> + data->resume_state[RES_CTRL_REG2] = ALL_ZEROES;
>
> + data->resume_state[RES_CTRL_REG3] = ALL_ZEROES;
>
> + data->resume_state[RES_CTRL_REG4] = (ALL_ZEROES | BDU_ENABLE);
>
> + data->resume_state[RES_CTRL_REG5] = ALL_ZEROES;
>
> + data->resume_state[RES_FIFO_CTRL_REG] = ALL_ZEROES;
>
> +
>
> + err = l3gd20_gyr_device_power_on(data);
>
> + if (err < 0) {
>
> + dev_err(&client->dev, "power on failed: %d\n", err);
>
> + goto exit_kfree_pdata;
>
> + }
>
> +
>
> + atomic_set(&data->enabled, 1);
>
> +
>
> + err = l3gd20_gyr_update_fs_range(data, data->pdata->fs_range);
>
> + if (err < 0) {
>
> + dev_err(&client->dev, "update_fs_range failed\n");
>
> + goto err_power_off;
>
> + }
>
> +
>
> + err = l3gd20_gyr_update_sampling_freq(data,
>
> + data->pdata->sampling_frequency);
>
> + if (err < 0) {
>
> + dev_err(&client->dev, "update sampling freq failed\n");
>
> + goto err_power_off;
>
> + }
>
> +
>
> + data->indio_dev = iio_allocate_device();
>
> + if (!data->indio_dev) {
>
> + err = -ENOMEM;
>
> + dev_err(&client->dev, "error allocating iio device: %d\n", err);
>
> + goto err_power_off;
>
> + }
>
> + data->indio_dev->attrs = &l3gd20_gyr_attribute_group;
>
> + data->indio_dev->dev.parent = &client->dev;
>
> + data->indio_dev->dev_data = (void *)(data);
>
> + data->indio_dev->driver_module = THIS_MODULE;
>
> + data->indio_dev->modes = INDIO_DIRECT_MODE;
>
> +
>
> +
>
> +
>
> + l3gd20_gyr_device_power_off(data);
>
> + /* As default, do not report information */
>
> + atomic_set(&data->enabled, 0);
>
> +
>
> + err = l3gd20_gyr_configure_ring(data->indio_dev);
>
> + if (err)
>
> + goto error_free_dev;
>
> +
>
> + err = iio_device_register(data->indio_dev);
>
> + if (err) {
>
> + dev_err(&client->dev, "error registering iio dev: %d\n", err);
>
> + goto error_unreg_ring_funcs;
>
> + }
>
> + regdone = 1;
>
> +
>
> + err = l3gd20_gyr_initialize_ring(data->indio_dev->ring);
>
> + if (err) {
>
> + dev_err(&client->dev, "failed to initialize the ring\n");
>
> + goto error_unreg_ring_funcs;
>
> + }
>
> +
>
> +
>
> + mutex_unlock(&data->lock);
>
> + dev_info(&client->dev, "%s: probed\n", L3GD20_GYR_DEV_NAME);
>
> + return 0;
>
> +
>
> +
>
> +
>
> +error_unreg_ring_funcs:
>
> + l3gd20_gyr_unconfigure_ring(data->indio_dev);
>
> +error_free_dev:
>
> + if (regdone)
>
> + iio_device_unregister(data->indio_dev);
>
> + else
>
> + iio_free_device(data->indio_dev);
>
> +err_power_off:
>
> + l3gd20_gyr_device_power_off(data);
>
> +err_pdata_init:
>
> + if (data->pdata->exit)
>
> + data->pdata->exit();
>
> +exit_kfree_pdata:
>
> + kfree(data->pdata);
>
> +err_mutexunlock:
>
> + mutex_unlock(&data->lock);
>
> + kfree(data);
>
> +exit_check_functionality_failed:
>
> + printk(KERN_ERR "%s: Driver Init failed\n", L3GD20_GYR_DEV_NAME);
>
> + return err;
>
> +}
>
> +
>
> +
>
> +static int __devexit l3gd20_gyr_remove(struct i2c_client *client)
>
> +{
>
> +
>
> + struct l3gd20_gyr_data *dev_data = i2c_get_clientdata(client);
>
> +
>
> + l3gd20_gyr_device_power_off(dev_data);
>
> +
>
> + l3gd20_gyr_uninitialize_ring(dev_data->indio_dev->ring);
>
> + iio_device_unregister(dev_data->indio_dev);
>
> + l3gd20_gyr_unconfigure_ring(dev_data->indio_dev);
>
> +
>
> + if (dev_data->pdata->exit)
>
> + dev_data->pdata->exit();
>
> + kfree(dev_data->pdata);
>
> + kfree(dev_data);
>
> +
>
> + return 0;
>
> +}
>
> +
>
> +static const struct i2c_device_id l3gd20_gyr_id[] = {
>
> + { L3GD20_GYR_DEV_NAME , 0 },
>
> + {},
>
> +};
>
> +
>
> +MODULE_DEVICE_TABLE(i2c, l3gd20_gyr_id);
>
> +
>
> +static struct i2c_driver l3gd20_gyr_driver = {
>
> + .driver = {
>
> + .owner = THIS_MODULE,
>
> + .name = L3GD20_GYR_DEV_NAME,
>
> + },
>
> + .probe = l3gd20_gyr_probe,
>
> + .remove = __devexit_p(l3gd20_gyr_remove),
>
> + .id_table = l3gd20_gyr_id,
>
> +};
>
> +
>
> +static int __init l3gd20_gyr_init(void)
>
> +{
>
> + pr_info("%s: gyroscope iio driver init\n", L3GD20_GYR_DEV_NAME);
>
> + return i2c_add_driver(&l3gd20_gyr_driver);
>
> +}
>
> +
>
> +static void __exit l3gd20_gyr_exit(void)
>
> +{
>
> + pr_info("%s exit\n", L3GD20_GYR_DEV_NAME);
>
> + i2c_del_driver(&l3gd20_gyr_driver);
>
> + return;
>
> +}
>
> +
>
> +module_init(l3gd20_gyr_init);
>
> +module_exit(l3gd20_gyr_exit);
>
> +
>
> +MODULE_DESCRIPTION("STMicroelectronics l3gd20 digital gyroscope iio driver");
>
> +MODULE_AUTHOR("Matteo Dameno, STMicroelectronics");
>
> +MODULE_LICENSE("GPL v2");
>
> --
>
> 1.7.5.4
>
>
>
>
>
> P.S.: We are working on the driver to make it compatible for the latest kernel.
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-08-16 18:56 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-08-16 2:02 [PATCH] Staging: iio: gyro: Add STMicroelectronics L3GD20 gyroscope device driver for Kernel version 2.6.35.13 Swapnil TIWARI
2012-08-16 18:56 ` 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.