diff options
Diffstat (limited to 'meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch')
-rw-r--r-- | meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch | 5433 |
1 files changed, 5433 insertions, 0 deletions
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch new file mode 100644 index 0000000..d461b3d --- /dev/null +++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch @@ -0,0 +1,5433 @@ +From dadd0a8a2cf1fecbbb0df7713f3da81ce7c11b00 Mon Sep 17 00:00:00 2001 +From: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Date: Wed, 16 Sep 2015 14:28:22 +0300 +Subject: [PATCH 49/50] Add lsm9ds0 (acc gyro mag) driver + + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +--- + drivers/input/misc/Kconfig | 10 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/lsm9ds0_acc_mag.c | 3435 ++++++++++++++++++++++++++++++++++ + drivers/input/misc/lsm9ds0_gyr.c | 1726 +++++++++++++++++ + include/linux/input/lsm9ds0.h | 201 ++ + 5 files changed, 5373 insertions(+) + create mode 100644 drivers/input/misc/lsm9ds0_acc_mag.c + create mode 100644 drivers/input/misc/lsm9ds0_gyr.c + create mode 100644 include/linux/input/lsm9ds0.h + +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index bb698e1..f1eb2c8 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -197,6 +197,16 @@ config INPUT_MPU3050 + To compile this driver as a module, choose M here: the + module will be called mpu3050. + ++config INPUT_LSM9DS0 ++ tristate "LSM9DS0 iNEMO sensor" ++ depends on I2C ++ help ++ Say Y here if you want to support 3D accelerometer, ++ 3D gyroscope, 3D magnetometer LSM9DS0 ++ ++ To compile this driver as a module, choose M here: the ++ module will be called lsm9ds0. ++ + config INPUT_APANEL + tristate "Fujitsu Lifebook Application Panel buttons" + depends on X86 && I2C && LEDS_CLASS +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index d7fc17f..b726294 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o + obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o + obj-$(CONFIG_INPUT_MMA8450) += mma8450.o + obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o ++obj-$(CONFIG_INPUT_LSM9DS0) += lsm9ds0_acc_mag.o lsm9ds0_gyr.o + obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o + obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o + obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o +diff --git a/drivers/input/misc/lsm9ds0_acc_mag.c b/drivers/input/misc/lsm9ds0_acc_mag.c +new file mode 100644 +index 0000000..3aad517 +--- /dev/null ++++ b/drivers/input/misc/lsm9ds0_acc_mag.c +@@ -0,0 +1,3435 @@ ++/******************** (C) COPYRIGHT 2013 STMicroelectronics ******************* ++* ++* File Name : lsm9ds0_acc_mag.c ++* Authors : AMS - Motion Sensors Div - Application Team ++* : Matteo Dameno (matteo.dameno@st.com) ++* : Denis Ciocca (denis.ciocca@st.com) ++* : Both authors are willing to be considered the contact ++* : and update points for the driver. ++* Version : V.1.0.5 ++* Date : 2013/Oct/23 ++* Description : LSM9DS0 accelerometer & magnetometer driver ++* ++******************************************************************************* ++* ++* 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 1-0-0 2012/05/04 ++ first revision ++Revision 1-0-1 2012/05/07 ++ New sysfs architecture ++ Support antialiasing filter ++Revision 1-0-2 2012/10/15 ++ I2C address bugfix ++Revision 1-0-3 2013/01/21 ++ Move CTLREG7 resume write from acc_power_on to magn_power_on ++Revision 1-0-4 2013/05/09 ++ Added rotation matrix ++Revision 1-0-5 2013/10/23 ++ Corrects Mag Enable bug, Corrects missing BDU enable ++******************************************************************************/ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/i2c.h> ++#include <linux/workqueue.h> ++#include <linux/input.h> ++#include <linux/kernel.h> ++#include <linux/kobject.h> ++#include <linux/gpio.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/hrtimer.h> ++#include <linux/ktime.h> ++ ++#include <linux/input/lsm9ds0.h> ++/* include "lsm9ds0.h" */ ++ ++#define I2C_AUTO_INCREMENT (0x80) ++#define MS_TO_NS(x) (x*1000000L) ++ ++#define ACC_G_MAX_POS 1495040 /** max positive value acc [ug] */ ++#define ACC_G_MAX_NEG 1495770 /** max negative value acc [ug] */ ++#define MAG_G_MAX_POS 983520 /** max positive value mag [ugauss] */ ++#define MAG_G_MAX_NEG 983040 /** max negative value mag [ugauss] */ ++ ++#define FUZZ 0 ++#define FLAT 0 ++ ++/* Address registers */ ++#define REG_WHOAMI_ADDR (0x0F) /** Who am i address register */ ++#define REG_CNTRL0_ADDR (0x1F) /** CNTRL0 address register */ ++#define REG_CNTRL1_ADDR (0x20) /** CNTRL1 address register */ ++#define REG_CNTRL2_ADDR (0x21) /** CNTRL2 address register */ ++#define REG_CNTRL3_ADDR (0x22) /** CNTRL3 address register */ ++#define REG_CNTRL4_ADDR (0x23) /** CNTRL4 address register */ ++#define REG_CNTRL5_ADDR (0x24) /** CNTRL5 address register */ ++#define REG_CNTRL6_ADDR (0x25) /** CNTRL6 address register */ ++#define REG_CNTRL7_ADDR (0x26) /** CNTRL7 address register */ ++ ++#define REG_ACC_DATA_ADDR (0x28) /** Acc. data low address register */ ++#define REG_MAG_DATA_ADDR (0x08) /** Mag. data low address register */ ++#define REG_TEMP_DATA_ADDR (0x05) /** Temp. data low address register */ ++ ++#define REG_GEN_MAG_ADDR (0x12) /** INT_CTRL_REG_M address register */ ++#define INT_SRC_REG_M_ADDR (0x13) /** INT_SRC_REG_M address register */ ++#define REG_GEN_MAG_THR_ADDR (0x14) /** INT_THS_L_M address register */ ++#define MIG_THRESHOLD_ADDR_H (0x15) /** INT_THS_H_M address register */ ++#define REG_GEN1_AXIS_ADDR (0x30) /** INT_GEN1_REG address register */ ++#define INT_GEN1_SRC_ADDR (0x31) /** INT_GEN1_SRC address register */ ++#define REG_GEN1_THR_ADDR (0x32) /** INT_GEN1_THS address register */ ++#define REG_GEN1_DUR_ADDR (0x33) /** INT_GEN1_DUR address register */ ++#define REG_GEN2_AXIS_ADDR (0x34) /** INT_GEN2_REG address register */ ++#define INT_GEN2_SRC_ADDR (0x35) /** INT_GEN2_SRC address register */ ++#define REG_GEN2_THR_ADDR (0x36) /** INT_GEN2_THS address register */ ++#define REG_GEN2_DUR_ADDR (0x37) /** INT_GEN2_DUR address register */ ++ ++/* Sensitivity */ ++#define SENSITIVITY_ACC_2G 60 /** ug/LSB */ ++#define SENSITIVITY_ACC_4G 120 /** ug/LSB */ ++#define SENSITIVITY_ACC_8G 240 /** ug/LSB */ ++#define SENSITIVITY_ACC_16G 730 /** ug/LSB */ ++ ++#define SENSITIVITY_MAG_2G 80 /** ugauss/LSB */ ++#define SENSITIVITY_MAG_4G 160 /** ugauss/LSB */ ++#define SENSITIVITY_MAG_8G 320 /** ugauss/LSB */ ++#define SENSITIVITY_MAG_12G 480 /** ugauss/LSB */ ++ ++/* ODR */ ++#define ODR_ACC_MASK (0XF0) /* Mask for odr change on acc */ ++#define LSM9DS0_ACC_ODR_OFF (0x00) /* Power down */ ++#define LSM9DS0_ACC_ODR3_125 (0x10) /* 3.25Hz output data rate */ ++#define LSM9DS0_ACC_ODR6_25 (0x20) /* 6.25Hz output data rate */ ++#define LSM9DS0_ACC_ODR12_5 (0x30) /* 12.5Hz output data rate */ ++#define LSM9DS0_ACC_ODR25 (0x40) /* 25Hz output data rate */ ++#define LSM9DS0_ACC_ODR50 (0x50) /* 50Hz output data rate */ ++#define LSM9DS0_ACC_ODR100 (0x60) /* 100Hz output data rate */ ++#define LSM9DS0_ACC_ODR200 (0x70) /* 200Hz output data rate */ ++#define LSM9DS0_ACC_ODR400 (0x80) /* 400Hz output data rate */ ++#define LSM9DS0_ACC_ODR800 (0x90) /* 800Hz output data rate */ ++#define LSM9DS0_ACC_ODR1600 (0xA0) /* 1600Hz output data rate */ ++ ++#define ODR_MAG_MASK (0X1C) /* Mask for odr change on mag */ ++#define LSM9DS0_MAG_ODR3_125 (0x00) /* 3.25Hz output data rate */ ++#define LSM9DS0_MAG_ODR6_25 (0x04) /* 6.25Hz output data rate */ ++#define LSM9DS0_MAG_ODR12_5 (0x08) /* 12.5Hz output data rate */ ++#define LSM9DS0_MAG_ODR25 (0x0C) /* 25Hz output data rate */ ++#define LSM9DS0_MAG_ODR50 (0x10) /* 50Hz output data rate */ ++#define LSM9DS0_MAG_ODR100 (0x14) /* 100Hz output data rate */ ++ ++/* Magnetic sensor mode */ ++#define MSMS_MASK (0x03) /* Mask magnetic sensor mode */ ++#define POWEROFF_MAG (0x02) /* Power Down */ ++#define CONTINUOS_CONVERSION (0x00) /* Continuos Conversion */ ++ ++/* Default values loaded in probe function */ ++#define WHOIAM_VALUE (0x49) /** Who Am I default value */ ++#define REG_DEF_CNTRL0 (0x00) /** CNTRL0 default value */ ++#define REG_DEF_CNTRL1 (0x0F) /** CNTRL1 default value */ ++#define REG_DEF_CNTRL2 (0x00) /** CNTRL2 default value */ ++#define REG_DEF_CNTRL3 (0x00) /** CNTRL3 default value */ ++#define REG_DEF_CNTRL4 (0x00) /** CNTRL4 default value */ ++#define REG_DEF_CNTRL5 (0x18) /** CNTRL5 default value */ ++#define REG_DEF_CNTRL6 (0x20) /** CNTRL6 default value */ ++#define REG_DEF_CNTRL7 (0x02) /** CNTRL7 default value */ ++ ++#define REG_DEF_INT_CNTRL_MAG (0x00) /** INT_CTRL_REG_M default value */ ++#define REG_DEF_INT_GEN1 (0x00) /** INT_GEN1_REG default value */ ++#define REG_DEF_INT_GEN2 (0x00) /** INT_GEN2_REG default value */ ++#define REG_DEF_IIG1_DURATION (0x00) /** INT_GEN1_DUR default value */ ++#define REG_DEF_IIG2_DURATION (0x00) /** INT_GEN2_DUR default value */ ++#define REG_DEF_IIG1_THRESHOLD (0x00) /** INT_GEN1_THS default value */ ++#define REG_DEF_IIG2_THRESHOLD (0x00) /** INT_GEN2_THS default value */ ++#define REG_DEF_MIG_THRESHOLD_L (0x00) /** INT_THS_L_M default value */ ++#define REG_DEF_MIG_THRESHOLD_H (0x00) /** INT_THS_H_M default value */ ++ ++#define REG_DEF_ALL_ZEROS (0x00) ++ ++/* Accelerometer Filter */ ++#define LSM9DS0_ACC_FILTER_MASK (0xC0) /* Mask for filter band change on acc */ ++#define FILTER_773 773 /* Anti-Aliasing 773 Hz */ ++#define FILTER_362 362 /* Anti-Aliasing 362 Hz */ ++#define FILTER_194 194 /* Anti-Aliasing 194 Hz */ ++#define FILTER_50 50 /* Anti-Aliasing 50 Hz */ ++ ++/* Temperature */ ++#define TEMP_MASK (0x80) /* Mask for temperature change */ ++#define TEMP_ON (0x80) /* Enable temperature */ ++#define TEMP_OFF (0x00) /* Disable temperature */ ++#define TEMP_SENSITIVITY 8 /* Sensitivity temperature */ ++#define OFFSET_TEMP 25 /* Offset temperature */ ++#define NDTEMP 1000 /* Not Available temperature */ ++ ++/* Interrupt */ ++#define GEN1_PIN1_MASK (0x20) ++#define GEN1_PIN2_MASK (0x40) ++#define GEN2_PIN1_MASK (0x10) ++#define GEN2_PIN2_MASK (0x20) ++#define GEN_MAG_PIN1_MASK (0x08) ++#define GEN_MAG_PIN2_MASK (0x10) ++#define GEN_MAG_EN_MASK (0x01) ++#define MAX_DUR_TH 127 ++#define MAX_TH_MAG 131071 ++#define GEN_X_HIGH_MASK (0x02) ++#define GEN_X_LOW_MASK (0x01) ++#define GEN_Y_HIGH_MASK (0x08) ++#define GEN_Y_LOW_MASK (0x04) ++#define GEN_Z_HIGH_MASK (0x20) ++#define GEN_Z_LOW_MASK (0x10) ++#define GEN_X_MAG_MASK (0x80) ++#define GEN_Y_MAG_MASK (0x40) ++#define GEN_Z_MAG_MASK (0x20) ++ ++#define GEN1_AND_OR_MASK (0x80) ++#define GEN2_AND_OR_MASK (0x83) ++ ++#define INT_PIN_CONF_MASK (0x10) ++#define INT_POLARITY_MASK (0x80) ++ ++#define to_dev(obj) container_of(obj, struct device, kobj) ++#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) ++ ++static struct kobject *acc_kobj; ++static struct kobject *mag_kobj; ++ ++struct workqueue_struct *lsm9ds0_workqueue = 0; ++ ++struct { ++ unsigned int cutoff_us; ++ u8 value; ++} lsm9ds0_acc_odr_table[] = { ++ { 1, LSM9DS0_ACC_ODR800 }, ++ { 2, LSM9DS0_ACC_ODR400 }, ++ { 5, LSM9DS0_ACC_ODR200 }, ++ { 10, LSM9DS0_ACC_ODR100 }, ++ { 20, LSM9DS0_ACC_ODR50 }, ++ { 40, LSM9DS0_ACC_ODR25 }, ++ { 80, LSM9DS0_ACC_ODR12_5 }, ++ { 160, LSM9DS0_ACC_ODR6_25 }, ++ { 320, LSM9DS0_ACC_ODR3_125}, ++}; ++ ++struct { ++ unsigned int cutoff_us; ++ u8 value; ++} lsm9ds0_mag_odr_table[] = { ++ { 10, LSM9DS0_MAG_ODR100 }, ++ { 20, LSM9DS0_MAG_ODR50 }, ++ { 40, LSM9DS0_MAG_ODR25 }, ++ { 80, LSM9DS0_MAG_ODR12_5 }, ++ { 160, LSM9DS0_MAG_ODR6_25 }, ++ { 320, LSM9DS0_MAG_ODR3_125}, ++}; ++ ++struct interrupt_enable { ++ atomic_t enable; ++ u8 address; ++ u8 mask; ++}; ++ ++struct interrupt_value { ++ int value; ++ u8 address; ++}; ++ ++struct lsm9ds0_interrupt { ++ struct interrupt_enable gen1_pin1; ++ struct interrupt_enable gen1_pin2; ++ struct interrupt_enable gen2_pin1; ++ struct interrupt_enable gen2_pin2; ++ struct interrupt_value gen1_threshold; ++ struct interrupt_value gen2_threshold; ++ struct interrupt_value gen1_duration; ++ struct interrupt_value gen2_duration; ++ struct interrupt_enable gen_mag_pin1; ++ struct interrupt_enable gen_mag_pin2; ++ struct interrupt_enable gen_mag; ++ struct interrupt_value gen_mag_threshold; ++ struct interrupt_enable gen1_axis[6]; ++ struct interrupt_enable gen2_axis[6]; ++ struct interrupt_enable gen_mag_axis[3]; ++ struct interrupt_enable gen1_and_or; ++ struct interrupt_enable gen2_and_or; ++ struct interrupt_enable interrupt_pin_conf; ++ struct interrupt_enable interrupt_polarity; ++}; ++ ++struct lsm9ds0_status { ++ struct i2c_client *client; ++ struct lsm9ds0_acc_platform_data *pdata_acc; ++ struct lsm9ds0_mag_platform_data *pdata_mag; ++ ++ struct mutex lock; ++ struct work_struct input_work_acc; ++ struct work_struct input_work_mag; ++ ++ struct hrtimer hr_timer_acc; ++ ktime_t ktime_acc; ++ struct hrtimer hr_timer_mag; ++ ktime_t ktime_mag; ++ ++ struct input_dev *input_dev_acc; ++ struct input_dev *input_dev_mag; ++ struct input_dev *input_dev_temp; ++ ++ struct lsm9ds0_interrupt *interrupt; ++ ++ int hw_initialized; ++ /* hw_working=-1 means not tested yet */ ++ int hw_working; ++ ++ atomic_t enabled_acc; ++ atomic_t enabled_mag; ++ atomic_t enabled_temp; ++ ++ int temp_value_dec; ++ unsigned int temp_value_flo; ++ ++ int on_before_suspend; ++ int use_smbus; ++ ++ u16 sensitivity_acc; ++ u16 sensitivity_mag; ++ ++ int irq1; ++ struct work_struct irq1_work; ++ struct workqueue_struct *irq1_work_queue; ++ int irq2; ++ struct work_struct irq2_work; ++ struct workqueue_struct *irq2_work_queue; ++}; ++ ++static const struct lsm9ds0_acc_platform_data default_lsm9ds0_acc_pdata = { ++ .fs_range = LSM9DS0_ACC_FS_2G, ++ .rot_matrix = { ++ {1, 0, 0}, ++ {0, 1, 0}, ++ {0, 0, 1}, ++ }, ++ .poll_interval = 100, ++ .min_interval = LSM9DS0_ACC_MIN_POLL_PERIOD_MS, ++ .aa_filter_bandwidth = ANTI_ALIASING_773, ++ .gpio_int1 = DEFAULT_INT1_GPIO, ++ .gpio_int2 = DEFAULT_INT2_GPIO, ++}; ++ ++static const struct lsm9ds0_mag_platform_data default_lsm9ds0_mag_pdata = { ++ .poll_interval = 100, ++ .min_interval = LSM9DS0_MAG_MIN_POLL_PERIOD_MS, ++ .fs_range = LSM9DS0_MAG_FS_2G, ++ .rot_matrix = { ++ {1, 0, 0}, ++ {0, 1, 0}, ++ {0, 0, 1}, ++ }, ++}; ++ ++struct reg_rw { ++ u8 address; ++ u8 default_value; ++ u8 resume_value; ++}; ++ ++struct reg_r { ++ u8 address; ++ u8 value; ++}; ++ ++static struct status_registers { ++ struct reg_r who_am_i; ++ struct reg_rw cntrl0; ++ struct reg_rw cntrl1; ++ struct reg_rw cntrl2; ++ struct reg_rw cntrl3; ++ struct reg_rw cntrl4; ++ struct reg_rw cntrl5; ++ struct reg_rw cntrl6; ++ struct reg_rw cntrl7; ++ struct reg_rw int_ctrl_reg_m; ++ struct reg_rw int_mag_threshold_low; ++ struct reg_rw int_mag_threshold_high; ++ struct reg_rw int_gen1_reg; ++ struct reg_rw int_gen2_reg; ++ struct reg_rw int_gen1_duration; ++ struct reg_rw int_gen2_duration; ++ struct reg_rw int_gen1_threshold; ++ struct reg_rw int_gen2_threshold; ++ struct reg_r int_src_reg_m; ++ struct reg_r int_gen1_src; ++ struct reg_r int_gen2_src; ++ struct reg_r int_gen_mag_src; ++} status_registers = { ++ .who_am_i.address=REG_WHOAMI_ADDR, .who_am_i.value=WHOIAM_VALUE, ++ .cntrl0.address=REG_CNTRL0_ADDR, .cntrl0.default_value=REG_DEF_CNTRL0, ++ .cntrl1.address=REG_CNTRL1_ADDR, .cntrl1.default_value=REG_DEF_CNTRL1, ++ .cntrl2.address=REG_CNTRL2_ADDR, .cntrl2.default_value=REG_DEF_CNTRL2, ++ .cntrl3.address=REG_CNTRL3_ADDR, .cntrl3.default_value=REG_DEF_CNTRL3, ++ .cntrl4.address=REG_CNTRL4_ADDR, .cntrl4.default_value=REG_DEF_CNTRL4, ++ .cntrl5.address=REG_CNTRL5_ADDR, .cntrl5.default_value=REG_DEF_CNTRL5, ++ .cntrl6.address=REG_CNTRL6_ADDR, .cntrl6.default_value=REG_DEF_CNTRL6, ++ .cntrl7.address=REG_CNTRL7_ADDR, .cntrl7.default_value=REG_DEF_CNTRL7, ++ .int_ctrl_reg_m.address=REG_GEN_MAG_ADDR, ++ .int_ctrl_reg_m.default_value=REG_DEF_INT_CNTRL_MAG, ++ .int_mag_threshold_low.address=REG_GEN_MAG_THR_ADDR, ++ .int_mag_threshold_low.default_value=REG_DEF_MIG_THRESHOLD_L, ++ .int_mag_threshold_low.address=MIG_THRESHOLD_ADDR_H, ++ .int_mag_threshold_low.default_value=REG_DEF_MIG_THRESHOLD_H, ++ .int_gen1_reg.address=REG_GEN1_AXIS_ADDR, ++ .int_gen1_reg.default_value=REG_DEF_INT_GEN1, ++ .int_gen2_reg.address=REG_GEN2_AXIS_ADDR, ++ .int_gen2_reg.default_value=REG_DEF_INT_GEN2, ++ .int_gen1_duration.address=REG_GEN1_DUR_ADDR, ++ .int_gen1_duration.default_value=REG_DEF_IIG1_DURATION, ++ .int_gen2_duration.address=REG_GEN2_DUR_ADDR, ++ .int_gen2_duration.default_value=REG_DEF_IIG2_DURATION, ++ .int_gen1_threshold.address=REG_GEN1_THR_ADDR, ++ .int_gen1_threshold.default_value=REG_DEF_IIG1_THRESHOLD, ++ .int_gen2_threshold.address=REG_GEN2_THR_ADDR, ++ .int_gen2_threshold.default_value=REG_DEF_IIG2_THRESHOLD, ++ .int_src_reg_m.address = INT_SRC_REG_M_ADDR, ++ .int_src_reg_m.value = REG_DEF_ALL_ZEROS, ++ .int_gen1_src.address = INT_GEN1_SRC_ADDR, ++ .int_gen1_src.value = REG_DEF_ALL_ZEROS, ++ .int_gen2_src.address = INT_GEN2_SRC_ADDR, ++ .int_gen2_src.value = REG_DEF_ALL_ZEROS, ++ .int_gen_mag_src.address = INT_SRC_REG_M_ADDR, ++ .int_gen_mag_src.value = REG_DEF_ALL_ZEROS, ++}; ++ ++static int lsm9ds0_i2c_read(struct lsm9ds0_status *stat, u8 *buf, int len) ++{ ++ int ret; ++ u8 reg = buf[0]; ++ u8 cmd = reg; ++#ifdef DEBUG ++ unsigned int ii; ++#endif ++ ++ ++ if (len > 1) ++ cmd = (I2C_AUTO_INCREMENT | reg); ++ if (stat->use_smbus) { ++ if (len == 1) { ++ ret = i2c_smbus_read_byte_data(stat->client, cmd); ++ buf[0] = ret & 0xff; ++#ifdef DEBUG ++ dev_warn(&stat->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(stat->client, ++ cmd, len, buf); ++#ifdef DEBUG ++ dev_warn(&stat->client->dev, ++ "i2c_smbus_read_i2c_block_data: ret:%d len:%d, " ++ "command=0x%02x, ", ++ ret, len, cmd); ++ 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(&stat->client->dev, ++ "read transfer error: len:%d, command=0x%02x\n", ++ len, cmd); ++ return 0; ++ } ++ return len; ++ } ++ ++ ret = i2c_master_send(stat->client, &cmd, sizeof(cmd)); ++ if (ret != sizeof(cmd)) ++ return ret; ++ ++ return i2c_master_recv(stat->client, buf, len); ++} ++ ++static int lsm9ds0_i2c_write(struct lsm9ds0_status *stat, u8 *buf, int len) ++{ ++ int ret; ++ u8 reg, value; ++#ifdef DEBUG ++ unsigned int ii; ++#endif ++ ++ if (len > 1) ++ buf[0] = (I2C_AUTO_INCREMENT | buf[0]); ++ ++ reg = buf[0]; ++ value = buf[1]; ++ ++ if (stat->use_smbus) { ++ if (len == 1) { ++ ret = i2c_smbus_write_byte_data(stat->client, ++ reg, value); ++#ifdef DEBUG ++ dev_warn(&stat->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(stat->client, ++ reg, len, buf + 1); ++#ifdef DEBUG ++ dev_warn(&stat->client->dev, ++ "i2c_smbus_write_i2c_block_data: ret=%d, " ++ "len:%d, command=0x%02x, ", ++ ret, len, reg); ++ 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(stat->client, buf, len+1); ++ return (ret == len+1) ? 0 : ret; ++} ++ ++static int lsm9ds0_hw_init(struct lsm9ds0_status *stat) ++{ ++ int err = -1; ++ u8 buf[1]; ++ int i; ++ ++ pr_info("%s: hw init start\n", LSM9DS0_DEV_NAME); ++ ++ buf[0] = status_registers.who_am_i.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ ++ if (err < 0) { ++ dev_warn(&stat->client->dev, "Error reading WHO_AM_I: is device" ++ " available/working?\n"); ++ goto err_firstread; ++ } else ++ stat->hw_working = 1; ++ ++ if (buf[0] != status_registers.who_am_i.value) { ++ dev_err(&stat->client->dev, ++ "device unknown. Expected: 0x%02x," ++ " Replies: 0x%02x\n", status_registers.who_am_i.value, buf[0]); ++ err = -1; ++ goto err_unknown_device; ++ } ++ ++ status_registers.cntrl1.resume_value = ++ status_registers.cntrl1.default_value; ++ status_registers.cntrl2.resume_value = ++ status_registers.cntrl2.default_value; ++ status_registers.cntrl3.resume_value = ++ status_registers.cntrl3.default_value; ++ status_registers.cntrl4.resume_value = ++ status_registers.cntrl4.default_value; ++ status_registers.cntrl5.resume_value = ++ status_registers.cntrl5.default_value; ++ status_registers.cntrl6.resume_value = ++ status_registers.cntrl6.default_value; ++ status_registers.cntrl7.resume_value = ++ status_registers.cntrl7.default_value; ++ ++ status_registers.int_ctrl_reg_m.resume_value = ++ status_registers.int_ctrl_reg_m.default_value; ++ status_registers.int_mag_threshold_low.resume_value = ++ status_registers.int_mag_threshold_low.default_value; ++ status_registers.int_mag_threshold_high.resume_value = ++ status_registers.int_mag_threshold_high.default_value; ++ status_registers.int_gen1_reg.resume_value = ++ status_registers.int_gen1_reg.default_value; ++ status_registers.int_gen2_reg.resume_value = ++ status_registers.int_gen2_reg.default_value; ++ status_registers.int_gen1_duration.resume_value = ++ status_registers.int_gen1_duration.default_value; ++ status_registers.int_gen2_duration.resume_value = ++ status_registers.int_gen2_duration.default_value; ++ status_registers.int_gen1_threshold.resume_value = ++ status_registers.int_gen1_threshold.default_value; ++ status_registers.int_gen2_threshold.resume_value = ++ status_registers.int_gen2_threshold.default_value; ++ ++ ++ stat->temp_value_dec = NDTEMP; ++ ++ if((stat->pdata_acc->gpio_int1 >= 0) || ++ (stat->pdata_acc->gpio_int2 >= 0)) { ++ ++ stat->interrupt = kmalloc(sizeof(*stat->interrupt), ++ GFP_KERNEL); ++ ++ if(stat->interrupt == NULL) ++ goto error_interrupt; ++ ++ stat->interrupt->gen1_pin1.address = REG_CNTRL3_ADDR; ++ stat->interrupt->gen1_pin2.address = REG_CNTRL4_ADDR; ++ stat->interrupt->gen2_pin1.address = REG_CNTRL3_ADDR; ++ stat->interrupt->gen2_pin2.address = REG_CNTRL4_ADDR; ++ stat->interrupt->gen_mag_pin1.address = REG_CNTRL3_ADDR; ++ stat->interrupt->gen_mag_pin2.address = REG_CNTRL4_ADDR; ++ stat->interrupt->gen_mag.address = REG_GEN_MAG_ADDR; ++ stat->interrupt->gen1_duration.address = REG_GEN1_DUR_ADDR; ++ stat->interrupt->gen2_duration.address = REG_GEN2_DUR_ADDR; ++ stat->interrupt->gen1_threshold.address = REG_GEN1_THR_ADDR; ++ stat->interrupt->gen2_threshold.address = REG_GEN2_THR_ADDR; ++ stat->interrupt->gen_mag_threshold.address = ++ REG_GEN_MAG_THR_ADDR; ++ ++ stat->interrupt->gen1_pin1.mask = GEN1_PIN1_MASK; ++ stat->interrupt->gen1_pin2.mask = GEN1_PIN2_MASK; ++ stat->interrupt->gen2_pin1.mask = GEN2_PIN1_MASK; ++ stat->interrupt->gen2_pin2.mask = GEN2_PIN2_MASK; ++ stat->interrupt->gen_mag_pin1.mask = GEN_MAG_PIN1_MASK; ++ stat->interrupt->gen_mag_pin2.mask = GEN_MAG_PIN2_MASK; ++ stat->interrupt->gen_mag.mask = GEN_MAG_EN_MASK; ++ ++ atomic_set(&stat->interrupt->gen1_pin1.enable, 0); ++ atomic_set(&stat->interrupt->gen1_pin2.enable, 0); ++ atomic_set(&stat->interrupt->gen2_pin1.enable, 0); ++ atomic_set(&stat->interrupt->gen2_pin2.enable, 0); ++ atomic_set(&stat->interrupt->gen_mag_pin1.enable, 0); ++ atomic_set(&stat->interrupt->gen_mag_pin2.enable, 0); ++ atomic_set(&stat->interrupt->gen_mag.enable, 0); ++ ++ stat->interrupt->gen1_threshold.value = 0; ++ stat->interrupt->gen2_threshold.value = 0; ++ stat->interrupt->gen1_duration.value = 0; ++ stat->interrupt->gen2_duration.value = 0; ++ stat->interrupt->gen_mag_threshold.value = 0; ++ ++ for(i=0; i<6; i++) { ++ stat->interrupt->gen1_axis[i].address = ++ REG_GEN1_AXIS_ADDR; ++ stat->interrupt->gen2_axis[i].address = ++ REG_GEN2_AXIS_ADDR; ++ ++ atomic_set(&stat->interrupt->gen1_axis[i].enable, 0); ++ atomic_set(&stat->interrupt->gen2_axis[i].enable, 0); ++ } ++ for(i=0; i<3; i++) { ++ stat->interrupt->gen_mag_axis[i].address = ++ REG_GEN_MAG_ADDR; ++ atomic_set(&stat->interrupt->gen_mag_axis[i].enable, 0); ++ } ++ ++ stat->interrupt->gen1_axis[0].mask = GEN_X_LOW_MASK; ++ stat->interrupt->gen1_axis[1].mask = GEN_Y_LOW_MASK; ++ stat->interrupt->gen1_axis[2].mask = GEN_Z_LOW_MASK; ++ stat->interrupt->gen1_axis[3].mask = GEN_X_HIGH_MASK; ++ stat->interrupt->gen1_axis[4].mask = GEN_Y_HIGH_MASK; ++ stat->interrupt->gen1_axis[5].mask = GEN_Z_HIGH_MASK; ++ ++ stat->interrupt->gen2_axis[0].mask = GEN_X_LOW_MASK; ++ stat->interrupt->gen2_axis[1].mask = GEN_Y_LOW_MASK; ++ stat->interrupt->gen2_axis[2].mask = GEN_Z_LOW_MASK; ++ stat->interrupt->gen2_axis[3].mask = GEN_X_HIGH_MASK; ++ stat->interrupt->gen2_axis[4].mask = GEN_Y_HIGH_MASK; ++ stat->interrupt->gen2_axis[5].mask = GEN_Z_HIGH_MASK; ++ ++ stat->interrupt->gen_mag_axis[0].mask = GEN_X_MAG_MASK; ++ stat->interrupt->gen_mag_axis[1].mask = GEN_Y_MAG_MASK; ++ stat->interrupt->gen_mag_axis[2].mask = GEN_Z_MAG_MASK; ++ ++ stat->interrupt->gen1_and_or.address = REG_GEN1_AXIS_ADDR; ++ stat->interrupt->gen1_and_or.mask = GEN1_AND_OR_MASK; ++ atomic_set(&stat->interrupt->gen1_and_or.enable, 0); ++ stat->interrupt->gen2_and_or.address = REG_GEN1_DUR_ADDR; ++ stat->interrupt->gen2_and_or.mask = GEN2_AND_OR_MASK; ++ atomic_set(&stat->interrupt->gen2_and_or.enable, 0); ++ ++ stat->interrupt->interrupt_pin_conf.address = REG_GEN_MAG_ADDR; ++ stat->interrupt->interrupt_pin_conf.mask = INT_PIN_CONF_MASK; ++ atomic_set(&stat->interrupt->interrupt_pin_conf.enable, 0); ++ ++ stat->interrupt->interrupt_polarity.address = REG_GEN_MAG_ADDR; ++ stat->interrupt->interrupt_polarity.mask = INT_POLARITY_MASK; ++ atomic_set(&stat->interrupt->interrupt_polarity.enable, 0); ++ } ++ ++ stat->hw_initialized = 1; ++ pr_info("%s: hw init done\n", LSM9DS0_DEV_NAME); ++ ++ return 0; ++ ++error_interrupt: ++err_unknown_device: ++err_firstread: ++ stat->hw_working = 0; ++ stat->hw_initialized = 0; ++ return err; ++} ++ ++static irqreturn_t lsm9ds0_isr1(int irq, void *dev) ++{ ++ struct lsm9ds0_status *stat = dev; ++ ++ disable_irq_nosync(irq); ++ queue_work(stat->irq1_work_queue, &stat->irq1_work); ++ pr_debug("%s: isr1 queued\n", LSM9DS0_DEV_NAME); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t lsm9ds0_isr2(int irq, void *dev) ++{ ++ struct lsm9ds0_status *stat = dev; ++ ++ disable_irq_nosync(irq); ++ queue_work(stat->irq2_work_queue, &stat->irq2_work); ++ pr_debug("%s: isr2 queued\n", LSM9DS0_DEV_NAME); ++ return IRQ_HANDLED; ++} ++ ++static void lsm9ds0_interrupt_catch(struct lsm9ds0_status *stat, int pin ) ++{ ++ u8 buf[2]; ++ u8 val; ++ ++ if(atomic_read(&stat->interrupt->gen1_pin1.enable) == 1) { ++ buf[0] = status_registers.int_gen1_src.address; ++ val = lsm9ds0_i2c_read(stat, buf, 1); ++ if(val < 0) ++ return; ++ status_registers.int_gen1_src.value = buf[0]; ++ ++ if(((int)status_registers.int_gen1_src.value) > 64) ++ pr_info("interrupt send by accelerometer interrupt " ++ "generator 1\n"); ++ } ++ if(atomic_read(&stat->interrupt->gen_mag_pin1.enable) == 1) { ++ buf[0] = status_registers.int_gen_mag_src.address; ++ val = lsm9ds0_i2c_read(stat, buf, 1); ++ if(val < 0) ++ return; ++ status_registers.int_gen_mag_src.value = buf[0]; ++ ++ if(((int)status_registers.int_gen_mag_src.value) > 1) ++ pr_info("interrupt send by magnetometer interrupt " ++ "generator\n"); ++ } ++ ++} ++ ++static void lsm9ds0_irq1_work_func(struct work_struct *work) ++{ ++ ++ struct lsm9ds0_status *stat = ++ container_of(work, struct lsm9ds0_status, irq1_work); ++ /* TODO add interrupt service procedure. ++ ie:lsm9ds0_get_int1_source(stat); */ ++ ++ lsm9ds0_interrupt_catch(stat,1); ++ pr_info("%s: IRQ1 triggered\n", LSM9DS0_DEV_NAME); ++exit: ++ enable_irq(stat->irq1); ++} ++ ++static void lsm9ds0_irq2_work_func(struct work_struct *work) ++{ ++ ++ struct lsm9ds0_status *stat = ++ container_of(work, struct lsm9ds0_status, irq2_work); ++ /* TODO add interrupt service procedure. ++ ie:lsm9ds0_get_int2_source(stat); */ ++ ++ lsm9ds0_interrupt_catch(stat,2); ++ pr_info("%s: IRQ2 triggered\n", LSM9DS0_DEV_NAME); ++exit: ++ enable_irq(stat->irq2); ++} ++ ++static int lsm9ds0_acc_device_power_off(struct lsm9ds0_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ ++ buf[0] = status_registers.cntrl1.address; ++ buf[1] = ((ODR_ACC_MASK & LSM9DS0_ACC_ODR_OFF) | ++ ((~ODR_ACC_MASK) & status_registers.cntrl1.resume_value)); ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ dev_err(&stat->client->dev, "accelerometer soft power off " ++ "failed: %d\n", err); ++ ++ if (stat->pdata_acc->power_off) { ++ stat->pdata_acc->power_off(); ++ } ++ ++ atomic_set(&stat->enabled_acc, 0); ++ ++ return 0; ++} ++ ++static int lsm9ds0_mag_device_power_off(struct lsm9ds0_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ ++ buf[0] = status_registers.cntrl7.address; ++ buf[1] = ((MSMS_MASK & POWEROFF_MAG) | ++ ((~MSMS_MASK) & status_registers.cntrl7.resume_value)); ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ dev_err(&stat->client->dev, "magnetometer soft power off " ++ "failed: %d\n", err); ++ ++ if (stat->pdata_mag->power_off) { ++ stat->pdata_mag->power_off(); ++ } ++ ++ atomic_set(&stat->enabled_mag, 0); ++ ++ return 0; ++} ++ ++static int lsm9ds0_acc_device_power_on(struct lsm9ds0_status *stat) ++{ ++ int err = -1; ++ u8 buf[5]; ++ ++ if (stat->pdata_acc->power_on) { ++ err = stat->pdata_acc->power_on(); ++ if (err < 0) { ++ dev_err(&stat->client->dev, ++ "accelerometer power_on failed: %d\n", err); ++ return err; ++ } ++ } ++ ++ buf[0] = status_registers.cntrl0.address; ++ buf[1] = status_registers.cntrl0.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.cntrl1.address; ++ buf[1] = status_registers.cntrl1.resume_value; ++ buf[2] = status_registers.cntrl2.resume_value; ++ buf[3] = status_registers.cntrl3.resume_value; ++ buf[4] = status_registers.cntrl4.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 4); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_gen1_reg.address; ++ buf[1] = status_registers.int_gen1_reg.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_gen1_threshold.address; ++ buf[1] = status_registers.int_gen1_threshold.resume_value; ++ buf[2] = status_registers.int_gen1_duration.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 2); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_gen2_reg.address; ++ buf[1] = status_registers.int_gen2_reg.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_gen2_threshold.address; ++ buf[1] = status_registers.int_gen2_threshold.resume_value; ++ buf[2] = status_registers.int_gen2_duration.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 2); ++ if (err < 0) ++ goto err_resume_state; ++ ++ atomic_set(&stat->enabled_acc, 1); ++ ++ return 0; ++ ++err_resume_state: ++ atomic_set(&stat->enabled_acc, 0); ++ dev_err(&stat->client->dev, "accelerometer hw power on error " ++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err); ++ return err; ++} ++ ++static int lsm9ds0_mag_device_power_on(struct lsm9ds0_status *stat) ++{ ++ int err = -1; ++ u8 buf[6]; ++ ++ if (stat->pdata_mag->power_on) { ++ err = stat->pdata_mag->power_on(); ++ if (err < 0) { ++ dev_err(&stat->client->dev, ++ "magnetometer power_on failed: %d\n", err); ++ return err; ++ } ++ } ++ ++ buf[0] = status_registers.cntrl0.address; ++ buf[1] = status_registers.cntrl0.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.cntrl3.address; ++ buf[1] = status_registers.cntrl3.resume_value; ++ buf[2] = status_registers.cntrl4.resume_value; ++ buf[3] = status_registers.cntrl5.resume_value; ++ buf[4] = status_registers.cntrl6.resume_value; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 4); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_ctrl_reg_m.address; ++ buf[1] = status_registers.int_ctrl_reg_m.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.int_mag_threshold_low.address; ++ buf[1] = status_registers.int_mag_threshold_low.resume_value; ++ buf[2] = status_registers.int_mag_threshold_high.resume_value; ++ err = lsm9ds0_i2c_write(stat, buf, 2); ++ if (err < 0) ++ goto err_resume_state; ++ ++ buf[0] = status_registers.cntrl7.address; ++ buf[1] = ((MSMS_MASK & CONTINUOS_CONVERSION) | ++ ((~MSMS_MASK) & status_registers.cntrl7.resume_value)); ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto err_resume_state; ++ ++ atomic_set(&stat->enabled_mag, 1); ++ ++ return 0; ++ ++err_resume_state: ++ atomic_set(&stat->enabled_mag, 0); ++ dev_err(&stat->client->dev, "magnetometer hw power on error " ++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err); ++ return err; ++} ++ ++static int lsm9ds0_acc_update_filter(struct lsm9ds0_status *stat, ++ u8 new_bandwidth) ++{ ++ int err=-1; ++ ++ u8 updated_val; ++ u8 buf[2]; ++ ++ switch (new_bandwidth) { ++ case ANTI_ALIASING_50: ++ break; ++ case ANTI_ALIASING_194: ++ break; ++ case ANTI_ALIASING_362: ++ break; ++ case ANTI_ALIASING_773: ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid accelerometer " ++ "update bandwidth requested: %u\n", new_bandwidth); ++ return -EINVAL; ++ } ++ ++ buf[0] = status_registers.cntrl2.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ ++ status_registers.cntrl2.resume_value = buf[0]; ++ updated_val = ((LSM9DS0_ACC_FILTER_MASK & new_bandwidth) | ++ ((~LSM9DS0_ACC_FILTER_MASK) & buf[0])); ++ buf[1] = updated_val; ++ buf[0] = status_registers.cntrl2.address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl2.resume_value = updated_val; ++ ++ return err; ++ ++error: ++ dev_err(&stat->client->dev, "update accelerometer fs range failed " ++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err); ++ return err; ++} ++ ++static int lsm9ds0_acc_update_fs_range(struct lsm9ds0_status *stat, ++ u8 new_fs_range) ++{ ++ int err=-1; ++ ++ u16 sensitivity; ++ u8 updated_val; ++ u8 buf[2]; ++ ++ switch (new_fs_range) { ++ case LSM9DS0_ACC_FS_2G: ++ sensitivity = SENSITIVITY_ACC_2G; ++ break; ++ case LSM9DS0_ACC_FS_4G: ++ sensitivity = SENSITIVITY_ACC_4G; ++ break; ++ case LSM9DS0_ACC_FS_8G: ++ sensitivity = SENSITIVITY_ACC_8G; ++ break; ++ case LSM9DS0_ACC_FS_16G: ++ sensitivity = SENSITIVITY_ACC_16G; ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid accelerometer " ++ "fs range requested: %u\n", new_fs_range); ++ return -EINVAL; ++ } ++ ++ buf[0] = status_registers.cntrl2.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ ++ status_registers.cntrl2.resume_value = buf[0]; ++ updated_val = ((LSM9DS0_ACC_FS_MASK & new_fs_range) | ++ ((~LSM9DS0_ACC_FS_MASK) & buf[0])); ++ buf[1] = updated_val; ++ buf[0] = status_registers.cntrl2.address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl2.resume_value = updated_val; ++ stat->sensitivity_acc = sensitivity; ++ ++ return err; ++ ++error: ++ dev_err(&stat->client->dev, "update accelerometer fs range failed " ++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err); ++ return err; ++} ++ ++static int lsm9ds0_mag_update_fs_range(struct lsm9ds0_status *stat, ++ u8 new_fs_range) ++{ ++ int err=-1; ++ ++ u16 sensitivity; ++ u8 updated_val; ++ u8 buf[2]; ++ ++ switch (new_fs_range) { ++ case LSM9DS0_MAG_FS_2G: ++ sensitivity = SENSITIVITY_MAG_2G; ++ break; ++ case LSM9DS0_MAG_FS_4G: ++ sensitivity = SENSITIVITY_MAG_4G; ++ break; ++ case LSM9DS0_MAG_FS_8G: ++ sensitivity = SENSITIVITY_MAG_8G; ++ break; ++ case LSM9DS0_MAG_FS_12G: ++ sensitivity = SENSITIVITY_MAG_12G; ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid magnetometer " ++ "fs range requested: %u\n", new_fs_range); ++ return -EINVAL; ++ } ++ ++ buf[0] = status_registers.cntrl6.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ ++ status_registers.cntrl6.resume_value = buf[0]; ++ updated_val = (LSM9DS0_MAG_FS_MASK & new_fs_range); ++ buf[1] = updated_val; ++ buf[0] = status_registers.cntrl6.address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl6.resume_value = updated_val; ++ stat->sensitivity_mag = sensitivity; ++ ++ return err; ++ ++error: ++ dev_err(&stat->client->dev, "update magnetometer fs range failed " ++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err); ++ return err; ++} ++ ++static int lsm9ds0_acc_update_odr(struct lsm9ds0_status *stat, ++ unsigned int poll_interval_ms) ++{ ++ int err = -1; ++ u8 config[2]; ++ int i; ++ ++ for (i = ARRAY_SIZE(lsm9ds0_acc_odr_table) - 1; i >= 0; i--) { ++ if ((lsm9ds0_acc_odr_table[i].cutoff_us <= poll_interval_ms) ++ || (i == 0)) ++ break; ++ } ++ ++ config[1] = ((ODR_ACC_MASK & lsm9ds0_acc_odr_table[i].value) | ++ ((~ODR_ACC_MASK) & status_registers.cntrl1.resume_value)); ++ ++ if (atomic_read(&stat->enabled_acc)) { ++ config[0] = status_registers.cntrl1.address; ++ err = lsm9ds0_i2c_write(stat, config, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl1.resume_value = config[1]; ++ stat->ktime_acc = ktime_set(0, MS_TO_NS(poll_interval_ms)); ++ } ++ ++ return err; ++ ++error: ++ dev_err(&stat->client->dev, "update accelerometer odr failed " ++ "0x%02x,0x%02x: %d\n", config[0], config[1], err); ++ ++ return err; ++} ++ ++static int lsm9ds0_mag_update_odr(struct lsm9ds0_status *stat, ++ unsigned int poll_interval_ms) ++{ ++ int err = -1; ++ u8 config[2]; ++ int i; ++ ++ for (i = ARRAY_SIZE(lsm9ds0_mag_odr_table) - 1; i >= 0; i--) { ++ if ((lsm9ds0_mag_odr_table[i].cutoff_us <= poll_interval_ms) ++ || (i == 0)) ++ break; ++ } ++ ++ config[1] = ((ODR_MAG_MASK & lsm9ds0_mag_odr_table[i].value) | ++ ((~ODR_MAG_MASK) & status_registers.cntrl5.resume_value)); ++ ++ if (atomic_read(&stat->enabled_mag)) { ++ config[0] = status_registers.cntrl5.address; ++ err = lsm9ds0_i2c_write(stat, config, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl5.resume_value = config[1]; ++ stat->ktime_mag = ktime_set(0, MS_TO_NS(poll_interval_ms)); ++ } ++ ++ return err; ++ ++error: ++ dev_err(&stat->client->dev, "update magnetometer odr failed " ++ "0x%02x,0x%02x: %d\n", config[0], config[1], err); ++ ++ return err; ++} ++ ++static void lsm9ds0_validate_polling(unsigned int *min_interval, ++ unsigned int *poll_interval, ++ unsigned int min, ++ struct i2c_client *client) ++{ ++ *min_interval = max(min, *min_interval); ++ *poll_interval = max(*poll_interval, *min_interval); ++} ++ ++static int lsm9ds0_acc_validate_pdata(struct lsm9ds0_status *stat) ++{ ++ int res = -EINVAL; ++ ++ lsm9ds0_validate_polling(&stat->pdata_acc->min_interval, ++ &stat->pdata_acc->poll_interval, ++ (unsigned int)LSM9DS0_ACC_MIN_POLL_PERIOD_MS, ++ stat->client); ++ ++ switch (stat->pdata_acc->aa_filter_bandwidth) { ++ case ANTI_ALIASING_50: ++ res = 1; ++ break; ++ case ANTI_ALIASING_194: ++ res = 1; ++ break; ++ case ANTI_ALIASING_362: ++ res = 1; ++ break; ++ case ANTI_ALIASING_773: ++ res = 1; ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid accelerometer " ++ "bandwidth selected: %u\n", ++ stat->pdata_acc->aa_filter_bandwidth); ++ } ++ ++ return res; ++} ++ ++static int lsm9ds0_mag_validate_pdata(struct lsm9ds0_status *stat) ++{ ++ lsm9ds0_validate_polling(&stat->pdata_mag->min_interval, ++ &stat->pdata_mag->poll_interval, ++ (unsigned int)LSM9DS0_MAG_MIN_POLL_PERIOD_MS, ++ stat->client); ++ ++ return 0; ++} ++ ++static int lsm9ds0_acc_enable(struct lsm9ds0_status *stat) ++{ ++ int err; ++ ++ if (!atomic_cmpxchg(&stat->enabled_acc, 0, 1)) { ++ err = lsm9ds0_acc_device_power_on(stat); ++ if (err < 0) { ++ atomic_set(&stat->enabled_acc, 0); ++ return err; ++ } ++ hrtimer_start(&stat->hr_timer_acc, stat->ktime_acc, HRTIMER_MODE_REL); ++ if(!atomic_read(&stat->enabled_mag)) { ++ if(stat->pdata_acc->gpio_int1 >= 0) ++ enable_irq(stat->irq1); ++ if(stat->pdata_acc->gpio_int2 >= 0) ++ enable_irq(stat->irq2); ++ } ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_acc_disable(struct lsm9ds0_status *stat) ++{ ++ if (atomic_cmpxchg(&stat->enabled_acc, 1, 0)) { ++ cancel_work_sync(&stat->input_work_acc); ++ hrtimer_cancel(&stat->hr_timer_acc); ++ lsm9ds0_acc_device_power_off(stat); ++ ++ if(!atomic_read(&stat->enabled_mag)) { ++ if(stat->pdata_acc->gpio_int1 >= 0) ++ disable_irq_nosync(stat->irq1); ++ if(stat->pdata_acc->gpio_int2 >= 0) ++ disable_irq_nosync(stat->irq2); ++ } ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_mag_enable(struct lsm9ds0_status *stat) ++{ ++ int err; ++ ++ if (!atomic_cmpxchg(&stat->enabled_mag, 0, 1)) { ++ err = lsm9ds0_mag_device_power_on(stat); ++ if (err < 0) { ++ atomic_set(&stat->enabled_mag, 0); ++ return err; ++ } ++ if(!atomic_read(&stat->enabled_temp)) { ++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL); ++ } ++ if(!atomic_read(&stat->enabled_acc)) { ++ if(stat->pdata_acc->gpio_int1 >= 0) ++ enable_irq(stat->irq1); ++ if(stat->pdata_acc->gpio_int2 >= 0) ++ enable_irq(stat->irq2); ++ } ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_mag_disable(struct lsm9ds0_status *stat) ++{ ++ if (atomic_cmpxchg(&stat->enabled_mag, 1, 0)) { ++ if(!atomic_read(&stat->enabled_temp)) { ++ cancel_work_sync(&stat->input_work_mag); ++ hrtimer_cancel(&stat->hr_timer_mag); ++ } ++ lsm9ds0_mag_device_power_off(stat); ++ if(!atomic_read(&stat->enabled_acc)) { ++ if(stat->pdata_acc->gpio_int1 >= 0) ++ disable_irq(stat->irq1); ++ if(stat->pdata_acc->gpio_int2 >= 0) ++ disable_irq(stat->irq2); ++ } ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_temperature_enable(struct lsm9ds0_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ u8 updated_val; ++ ++ buf[0] = status_registers.cntrl5.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ ++ status_registers.cntrl5.resume_value = buf[0]; ++ updated_val = ((TEMP_MASK & TEMP_ON) | ++ ((~TEMP_MASK) & buf[0])); ++ buf[1] = updated_val; ++ buf[0] = status_registers.cntrl5.address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl5.resume_value = updated_val; ++ ++ if(!atomic_read(&stat->enabled_mag)) { ++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL); ++ } ++ atomic_set(&stat->enabled_temp, 1); ++ return 0; ++ ++error: ++ return -1; ++} ++ ++static int lsm9ds0_temperature_disable(struct lsm9ds0_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ u8 updated_val; ++ ++ buf[0] = status_registers.cntrl5.address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ ++ status_registers.cntrl5.resume_value = buf[0]; ++ updated_val = ((TEMP_MASK & TEMP_OFF) | ++ ((~TEMP_MASK) & buf[0])); ++ buf[1] = updated_val; ++ buf[0] = status_registers.cntrl5.address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ goto error; ++ status_registers.cntrl5.resume_value = updated_val; ++ ++ if(!atomic_read(&stat->enabled_mag)) { ++ cancel_work_sync(&stat->input_work_mag); ++ hrtimer_cancel(&stat->hr_timer_mag); ++ } ++ atomic_set(&stat->enabled_temp, 0); ++ stat->temp_value_dec = NDTEMP; ++ return 0; ++ ++error: ++ return -1; ++} ++ ++static void lsm9ds0_acc_input_cleanup(struct lsm9ds0_status *stat) ++{ ++ input_unregister_device(stat->input_dev_acc); ++ input_free_device(stat->input_dev_acc); ++} ++ ++static void lsm9ds0_mag_input_cleanup(struct lsm9ds0_status *stat) ++{ ++ input_unregister_device(stat->input_dev_mag); ++ input_free_device(stat->input_dev_mag); ++} ++ ++static ssize_t attr_get_polling_rate_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ unsigned int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ mutex_lock(&stat->lock); ++ val = stat->pdata_acc->poll_interval; ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%u\n", val); ++} ++ ++static ssize_t attr_get_polling_rate_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ unsigned int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ mutex_lock(&stat->lock); ++ val = stat->pdata_mag->poll_interval; ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%u\n", val); ++} ++ ++static ssize_t attr_set_polling_rate_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long interval_ms; ++ ++ if (strict_strtoul(buf, 10, &interval_ms)) ++ return -EINVAL; ++ if (!interval_ms) ++ return -EINVAL; ++ interval_ms = (unsigned int)max((unsigned int)interval_ms, ++ stat->pdata_acc->min_interval); ++ mutex_lock(&stat->lock); ++ stat->pdata_acc->poll_interval = (unsigned int)interval_ms; ++ lsm9ds0_acc_update_odr(stat, interval_ms); ++ mutex_unlock(&stat->lock); ++ return size; ++} ++ ++static ssize_t attr_set_polling_rate_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long interval_ms; ++ ++ if (strict_strtoul(buf, 10, &interval_ms)) ++ return -EINVAL; ++ if (!interval_ms) ++ return -EINVAL; ++ interval_ms = (unsigned int)max((unsigned int)interval_ms, ++ stat->pdata_mag->min_interval); ++ mutex_lock(&stat->lock); ++ stat->pdata_mag->poll_interval = (unsigned int)interval_ms; ++ lsm9ds0_mag_update_odr(stat, interval_ms); ++ mutex_unlock(&stat->lock); ++ return size; ++} ++ ++static ssize_t attr_get_enable_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ int val = (int)atomic_read(&stat->enabled_acc); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_get_enable_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ int val = (int)atomic_read(&stat->enabled_mag); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_enable_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if (val) ++ lsm9ds0_acc_enable(stat); ++ else ++ lsm9ds0_acc_disable(stat); ++ ++ return size; ++} ++ ++static ssize_t attr_set_enable_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if (val) ++ lsm9ds0_mag_enable(stat); ++ else ++ lsm9ds0_mag_disable(stat); ++ ++ return size; ++} ++ ++static ssize_t attr_get_range_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ u8 val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ int range = 2; ++ mutex_lock(&stat->lock); ++ val = stat->pdata_acc->fs_range ; ++ switch (val) { ++ case LSM9DS0_ACC_FS_2G: ++ range = 2; ++ break; ++ case LSM9DS0_ACC_FS_4G: ++ range = 4; ++ break; ++ case LSM9DS0_ACC_FS_8G: ++ range = 8; ++ break; ++ case LSM9DS0_ACC_FS_16G: ++ range = 16; ++ break; ++ } ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%d\n", range); ++} ++ ++static ssize_t attr_get_range_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ u8 val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ int range = 2; ++ mutex_lock(&stat->lock); ++ val = stat->pdata_mag->fs_range ; ++ switch (val) { ++ case LSM9DS0_MAG_FS_2G: ++ range = 2; ++ break; ++ case LSM9DS0_MAG_FS_4G: ++ range = 4; ++ break; ++ case LSM9DS0_MAG_FS_8G: ++ range = 8; ++ break; ++ case LSM9DS0_MAG_FS_12G: ++ range = 12; ++ break; ++ } ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%d\n", range); ++} ++ ++static ssize_t attr_set_range_acc(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ u8 range; ++ int err; ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ switch (val) { ++ case 2: ++ range = LSM9DS0_ACC_FS_2G; ++ break; ++ case 4: ++ range = LSM9DS0_ACC_FS_4G; ++ break; ++ case 8: ++ range = LSM9DS0_ACC_FS_8G; ++ break; ++ case 16: ++ range = LSM9DS0_ACC_FS_16G; ++ break; ++ default: ++ dev_err(&stat->client->dev, "accelerometer invalid range " ++ "request: %lu, discarded\n", val); ++ return -EINVAL; ++ } ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_acc_update_fs_range(stat, range); ++ if (err < 0) { ++ mutex_unlock(&stat->lock); ++ return err; ++ } ++ stat->pdata_acc->fs_range = range; ++ mutex_unlock(&stat->lock); ++ dev_info(&stat->client->dev, "accelerometer range set to:" ++ " %lu g\n", val); ++ ++ return size; ++} ++ ++static ssize_t attr_set_range_mag(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ u8 range; ++ int err; ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ switch (val) { ++ case 2: ++ range = LSM9DS0_MAG_FS_2G; ++ break; ++ case 4: ++ range = LSM9DS0_MAG_FS_4G; ++ break; ++ case 8: ++ range = LSM9DS0_MAG_FS_8G; ++ break; ++ case 12: ++ range = LSM9DS0_MAG_FS_12G; ++ break; ++ default: ++ dev_err(&stat->client->dev, "magnetometer invalid range " ++ "request: %lu, discarded\n", val); ++ return -EINVAL; ++ } ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_mag_update_fs_range(stat, range); ++ if (err < 0) { ++ mutex_unlock(&stat->lock); ++ return err; ++ } ++ stat->pdata_mag->fs_range = range; ++ mutex_unlock(&stat->lock); ++ dev_info(&stat->client->dev, "magnetometer range set to:" ++ " %lu g\n", val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_aa_filter(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ u8 val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ int frequency=FILTER_773; ++ mutex_lock(&stat->lock); ++ val = stat->pdata_acc->aa_filter_bandwidth; ++ switch (val) { ++ case ANTI_ALIASING_50: ++ frequency = FILTER_50; ++ break; ++ case ANTI_ALIASING_194: ++ frequency = FILTER_194; ++ break; ++ case ANTI_ALIASING_362: ++ frequency = FILTER_362; ++ break; ++ case ANTI_ALIASING_773: ++ frequency = FILTER_773; ++ break; ++ } ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%d\n", frequency); ++} ++ ++static ssize_t attr_set_aa_filter(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ u8 frequency; ++ int err; ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ switch (val) { ++ case FILTER_50: ++ frequency = ANTI_ALIASING_50; ++ break; ++ case FILTER_194: ++ frequency = ANTI_ALIASING_194; ++ break; ++ case FILTER_362: ++ frequency = ANTI_ALIASING_362; ++ break; ++ case FILTER_773: ++ frequency = ANTI_ALIASING_773; ++ break; ++ default: ++ dev_err(&stat->client->dev, "accelerometer invalid filter " ++ "request: %lu, discarded\n", val); ++ return -EINVAL; ++ } ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_acc_update_filter(stat, frequency); ++ if (err < 0) { ++ mutex_unlock(&stat->lock); ++ return err; ++ } ++ stat->pdata_acc->aa_filter_bandwidth = frequency; ++ mutex_unlock(&stat->lock); ++ dev_info(&stat->client->dev, "accelerometer anti-aliasing filter " ++ "set to: %lu Hz\n", val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_temp_enable(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ int val = (int)atomic_read(&stat->enabled_temp); ++ ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_temp_enable(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ unsigned long val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ atomic_set(&stat->enabled_temp, (int)val); ++ ++ if(val>0) { ++ lsm9ds0_temperature_enable(stat); ++ } else { ++ lsm9ds0_temperature_disable(stat); ++ } ++ ++ return size; ++} ++ ++static ssize_t attr_get_temp(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int dec; ++ unsigned int flo; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ dec = stat->temp_value_dec; ++ flo = stat->temp_value_flo; ++ ++ if(dec==NDTEMP) ++ return sprintf(buf, "n.d.\n"); ++ ++ return sprintf(buf, "%d.%u\n", dec, flo); ++} ++ ++static struct kobj_attribute poll_attr_acc = ++__ATTR(pollrate_ms, 0664, attr_get_polling_rate_acc, attr_set_polling_rate_acc); ++static struct kobj_attribute enable_attr_acc = ++__ATTR(enable_device, 0664, attr_get_enable_acc, attr_set_enable_acc); ++static struct kobj_attribute fs_attr_acc = ++__ATTR(full_scale, 0664, attr_get_range_acc, attr_set_range_acc); ++static struct kobj_attribute aa_filter_attr = ++__ATTR(anti_aliasing_frequency, 0664, attr_get_aa_filter, attr_set_aa_filter); ++static struct kobj_attribute poll_attr_mag = ++__ATTR(pollrate_ms, 0664, attr_get_polling_rate_mag, attr_set_polling_rate_mag); ++static struct kobj_attribute enable_attr_mag = ++__ATTR(enable_device, 0664, attr_get_enable_mag, attr_set_enable_mag); ++static struct kobj_attribute fs_attr_mag = ++__ATTR(full_scale, 0664, attr_get_range_mag, attr_set_range_mag); ++ ++static int write_bit_on_register(struct lsm9ds0_status *stat, u8 address, ++ u8 *resume_value, u8 mask, int value) ++{ ++ int err; ++ u8 updated_val; ++ u8 buf[2]; ++ u8 val = 0x00; ++ ++ buf[0] = address; ++ err = lsm9ds0_i2c_read(stat, buf, 1); ++ if (err < 0) ++ return -1; ++ ++ if(resume_value != NULL) ++ *resume_value = buf[0]; ++ ++ if(mask == 0) ++ updated_val = (u8)value; ++ else { ++ if(value>0) ++ val = 0xFF; ++ updated_val = (mask & val) | ((~mask) & buf[0]); ++ } ++ ++ buf[1] = updated_val; ++ buf[0] = address; ++ ++ err = lsm9ds0_i2c_write(stat, buf, 1); ++ if (err < 0) ++ return -1; ++ ++ if(resume_value != NULL) ++ *resume_value = updated_val; ++ ++ return err; ++} ++ ++static int write_gen_int(struct lsm9ds0_status *stat, ++ struct interrupt_enable *ie, int val) ++{ ++ int err; ++ ++ if(val>0) ++ val = 1; ++ else ++ val = 0; ++ ++ err = write_bit_on_register(stat, ie->address, NULL, ie->mask, val); ++ if(err < 0) ++ return -1; ++ ++ atomic_set(&ie->enable, val); ++ return err; ++} ++ ++static int write_duration_threshold_int(struct lsm9ds0_status *stat, ++ struct interrupt_value *ie, int val) ++{ ++ int err; ++ ++ if(val<0) ++ return -1; ++ ++ if(val>MAX_DUR_TH) ++ return -1; ++ ++ err = write_bit_on_register(stat, ie->address, NULL, 0, val); ++ if(err<0) ++ return -1; ++ ++ ie->value = val; ++ ++ return err; ++} ++ ++static int write_threshold_mag_int(struct lsm9ds0_status *stat, ++ struct interrupt_value *ie, int val) ++{ ++ int err; ++ u8 high; ++ u8 low; ++ ++ if(val<0) ++ return -1; ++ ++ if(val>MAX_TH_MAG) ++ return -1; ++ ++ low = (u8)(0xff & val); ++ ++ err = write_bit_on_register(stat, ie->address, NULL, 0, low); ++ if(err<0) ++ return -1; ++ ++ high = (u8)(0xff & (val >> 8)); ++ ++ err = write_bit_on_register(stat, (ie->address)+1, NULL, 0, high); ++ if(err<0) ++ return -1; ++ ++ ie->value = val; ++ ++ return err; ++} ++ ++static ssize_t attr_get_gen1_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val = -1; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen1_pin1.enable); ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen1_pin2.enable); ++ } ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen1_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen1_pin1, (int)val); ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen1_pin2, (int)val); ++ } ++ return size; ++} ++ ++static ssize_t attr_get_gen2_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val = -1; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen2_pin1.enable); ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen2_pin2.enable); ++ } ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen2_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen2_pin1, (int)val); ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen2_pin2, (int)val); ++ } ++ return size; ++} ++ ++static ssize_t attr_get_gen1_duration(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = stat->interrupt->gen1_duration.value; ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen1_duration(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_duration_threshold_int(stat, ++ &stat->interrupt->gen1_duration, (int)val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen2_duration(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = stat->interrupt->gen2_duration.value; ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen2_duration(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_duration_threshold_int(stat, ++ &stat->interrupt->gen2_duration, (int)val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen1_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = stat->interrupt->gen1_threshold.value; ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen1_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_duration_threshold_int(stat, ++ &stat->interrupt->gen1_threshold, (int)val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen2_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = stat->interrupt->gen2_threshold.value; ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen2_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_duration_threshold_int(stat, ++ &stat->interrupt->gen2_threshold, (int)val); ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen_mag_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val = -1; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen_mag_pin1.enable); ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ val = atomic_read(&stat->interrupt->gen_mag_pin2.enable); ++ } ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen_mag_status(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if(strcmp(attr->attr.name, "pin1_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen_mag_pin1, (int)val); ++ if(err >= 0) { ++ if((atomic_read(&stat->interrupt->gen_mag_pin2.enable))==0) ++ write_gen_int(stat, ++ &stat->interrupt->gen_mag, (int)val); ++ } ++ } ++ if(strcmp(attr->attr.name, "pin2_enable") == 0) { ++ err = write_gen_int(stat, ++ &stat->interrupt->gen_mag_pin2, (int)val); ++ if(err >= 0) { ++ if((atomic_read(&stat->interrupt->gen_mag_pin1.enable))==0) ++ write_gen_int(stat, ++ &stat->interrupt->gen_mag, (int)val); ++ } ++ } ++ return size; ++} ++ ++static ssize_t attr_get_gen_mag_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = stat->interrupt->gen_mag_threshold.value; ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen_mag_threshold(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err = -1; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_threshold_mag_int(stat, ++ &stat->interrupt->gen_mag_threshold, (int)val); ++ ++ return size; ++} ++ ++static int get_axis(struct lsm9ds0_status *stat, ++ int generator, const char *name) { ++ ++ int val; ++ int axis; ++ ++ if(strcmp(name, "x_high_enable") == 0) { ++ axis = 3; ++ } ++ if(strcmp(name, "x_low_enable") == 0) { ++ axis = 0; ++ } ++ if(strcmp(name, "y_high_enable") == 0) { ++ axis = 4; ++ } ++ if(strcmp(name, "y_low_enable") == 0) { ++ axis = 1; ++ } ++ if(strcmp(name, "z_high_enable") == 0) { ++ axis = 5; ++ } ++ if(strcmp(name, "z_low_enable") == 0) { ++ axis = 2; ++ } ++ ++ if(generator == 1) ++ val = atomic_read(&stat->interrupt->gen1_axis[axis].enable); ++ else ++ val = atomic_read(&stat->interrupt->gen2_axis[axis].enable); ++ ++ return val; ++} ++ ++static int set_axis(struct lsm9ds0_status *stat, int generator, ++ const char *name, unsigned long value) ++{ ++ int err = -1; ++ int axis; ++ ++ if(strcmp(name, "x_high_enable") == 0) { ++ axis = 3; ++ } ++ if((strcmp(name, "x_low_enable") == 0) || ++ (strcmp(name, "x_enable") == 0)) { ++ axis = 0; ++ } ++ if(strcmp(name, "y_high_enable") == 0) { ++ axis = 4; ++ } ++ if((strcmp(name, "y_low_enable") == 0) || ++ (strcmp(name, "y_enable") == 0)) { ++ axis = 1; ++ } ++ if(strcmp(name, "z_high_enable") == 0) { ++ axis = 5; ++ } ++ if((strcmp(name, "z_low_enable") == 0) || ++ (strcmp(name, "z_enable") == 0)) { ++ axis = 2; ++ } ++ ++ if(generator == 1) ++ err = write_gen_int(stat, ++ &(stat->interrupt->gen1_axis[axis]), (int)value); ++ if(generator == 2) ++ err = write_gen_int(stat, ++ &(stat->interrupt->gen2_axis[axis]), (int)value); ++ if(generator == 3) ++ err = write_gen_int(stat, ++ &(stat->interrupt->gen_mag_axis[axis]), (int)value); ++ ++ if(err < 0) ++ return -1; ++ ++ return err; ++} ++ ++static ssize_t attr_get_gen1_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = get_axis(stat,1,attr->attr.name); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen1_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = set_axis(stat, 1, attr->attr.name, val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen2_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = get_axis(stat,2,attr->attr.name); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen2_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = set_axis(stat, 2, attr->attr.name, val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen_mag_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = get_axis(stat, 3, attr->attr.name); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen_mag_axis(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = set_axis(stat, 3, attr->attr.name, val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen1_and_or(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = atomic_read(&stat->interrupt->gen1_and_or.enable); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen1_and_or(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_gen_int(stat, &(stat->interrupt->gen1_and_or), (int)val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_gen2_and_or(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = atomic_read(&stat->interrupt->gen2_and_or.enable); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_gen2_and_or(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ struct device *dev = to_dev(kobj->parent); ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_gen_int(stat, &(stat->interrupt->gen2_and_or), (int)val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_set_pin_conf(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_gen_int(stat, ++ &(stat->interrupt->interrupt_pin_conf), (int)val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_pin_conf(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = atomic_read(&stat->interrupt->interrupt_pin_conf.enable); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_set_interrupt_polarity(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ unsigned long val; ++ ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ err = write_gen_int(stat, ++ &(stat->interrupt->interrupt_polarity), (int)val); ++ if(err < 0) ++ return -1; ++ ++ return size; ++} ++ ++static ssize_t attr_get_interrupt_polarity(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ val = atomic_read(&stat->interrupt->interrupt_polarity.enable); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static struct kobj_attribute gen1_interrupt_pin1_enable = ++__ATTR(pin1_enable, 0664, attr_get_gen1_status, attr_set_gen1_status); ++static struct kobj_attribute gen1_interrupt_pin2_enable = ++__ATTR(pin2_enable, 0664, attr_get_gen1_status, attr_set_gen1_status); ++ ++static struct kobj_attribute gen2_interrupt_pin1_enable = ++__ATTR(pin1_enable, 0664, attr_get_gen2_status, attr_set_gen2_status); ++static struct kobj_attribute gen2_interrupt_pin2_enable = ++__ATTR(pin2_enable, 0664, attr_get_gen2_status, attr_set_gen2_status); ++ ++static struct kobj_attribute gen1_duration = ++__ATTR(duration, 0664, attr_get_gen1_duration, attr_set_gen1_duration); ++static struct kobj_attribute gen2_duration = ++__ATTR(duration, 0664, attr_get_gen2_duration, attr_set_gen2_duration); ++ ++static struct kobj_attribute gen1_threshold = ++__ATTR(threshold, 0664, attr_get_gen1_threshold, attr_set_gen1_threshold); ++static struct kobj_attribute gen2_threshold = ++__ATTR(threshold, 0664, attr_get_gen2_threshold, attr_set_gen2_threshold); ++ ++static struct kobj_attribute mag_gen_interrupt_pin1 = ++__ATTR(pin1_enable, 0664, attr_get_gen_mag_status, attr_set_gen_mag_status); ++static struct kobj_attribute mag_gen_interrupt_pin2 = ++__ATTR(pin2_enable, 0664, attr_get_gen_mag_status, attr_set_gen_mag_status); ++ ++static struct kobj_attribute mag_gen_threshold = ++__ATTR(threshold, 0664, attr_get_gen_mag_threshold, attr_set_gen_mag_threshold); ++ ++static struct kobj_attribute gen1_x_high = ++__ATTR(x_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++static struct kobj_attribute gen1_x_low = ++__ATTR(x_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++ ++static struct kobj_attribute gen2_x_high = ++__ATTR(x_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++static struct kobj_attribute gen2_x_low = ++__ATTR(x_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++ ++static struct kobj_attribute gen1_y_high = ++__ATTR(y_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++static struct kobj_attribute gen1_y_low = ++__ATTR(y_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++ ++static struct kobj_attribute gen2_y_high = ++__ATTR(y_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++static struct kobj_attribute gen2_y_low = ++__ATTR(y_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++ ++static struct kobj_attribute gen1_z_high = ++__ATTR(z_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++static struct kobj_attribute gen1_z_low = ++__ATTR(z_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis); ++ ++static struct kobj_attribute gen2_z_high = ++__ATTR(z_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++static struct kobj_attribute gen2_z_low = ++__ATTR(z_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis); ++ ++static struct kobj_attribute gen_mag_x = ++__ATTR(x_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis); ++static struct kobj_attribute gen_mag_y = ++__ATTR(y_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis); ++static struct kobj_attribute gen_mag_z = ++__ATTR(z_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis); ++ ++static struct kobj_attribute gen1_and_or = ++__ATTR(and(1)_or(0)_combination, 0664, attr_get_gen1_and_or, ++ attr_set_gen1_and_or); ++static struct kobj_attribute gen2_and_or = ++__ATTR(and(1)_or(0)_combination, 0664, attr_get_gen2_and_or, ++ attr_set_gen2_and_or); ++ ++ ++static struct attribute *attributes_acc_interrupt1[] = { ++ &gen1_interrupt_pin1_enable.attr, ++ &gen1_interrupt_pin2_enable.attr, ++ &gen1_duration.attr, ++ &gen1_threshold.attr, ++ &gen1_x_high.attr, ++ &gen1_x_low.attr, ++ &gen1_y_high.attr, ++ &gen1_y_low.attr, ++ &gen1_z_high.attr, ++ &gen1_z_low.attr, ++ &gen1_and_or.attr, ++ NULL, ++}; ++ ++static struct attribute *attributes_acc_interrupt2[] = { ++ &gen2_interrupt_pin1_enable.attr, ++ &gen2_interrupt_pin2_enable.attr, ++ &gen2_duration.attr, ++ &gen2_threshold.attr, ++ &gen2_x_high.attr, ++ &gen2_x_low.attr, ++ &gen2_y_high.attr, ++ &gen2_y_low.attr, ++ &gen2_z_high.attr, ++ &gen2_z_low.attr, ++ &gen2_and_or.attr, ++ NULL, ++}; ++ ++static struct attribute *attributes_mag_interrupt[] = { ++ &mag_gen_interrupt_pin1.attr, ++ &mag_gen_interrupt_pin2.attr, ++ &mag_gen_threshold.attr, ++ &gen_mag_x.attr, ++ &gen_mag_y.attr, ++ &gen_mag_z.attr, ++ NULL, ++}; ++ ++static struct attribute *attributes_acc[] = { ++ &poll_attr_acc.attr, ++ &enable_attr_acc.attr, ++ &fs_attr_acc.attr, ++ &aa_filter_attr.attr, ++ NULL, ++}; ++ ++static struct attribute *attributes_mag[] = { ++ &poll_attr_mag.attr, ++ &enable_attr_mag.attr, ++ &fs_attr_mag.attr, ++ NULL, ++}; ++ ++static struct attribute_group attr_group_acc = { ++ .attrs = attributes_acc, ++}; ++ ++static struct attribute_group attr_group_mag = { ++ .attrs = attributes_mag, ++}; ++ ++static struct attribute_group attr_group_int1_acc = { ++ .attrs = attributes_acc_interrupt1, ++ .name = "interrupt_generator1", ++}; ++ ++static struct attribute_group attr_group_int2_acc = { ++ .attrs = attributes_acc_interrupt2, ++ .name = "interrupt_generator2", ++}; ++ ++static struct attribute_group attr_group_int_mag = { ++ .attrs = attributes_mag_interrupt, ++ .name = "interrupt_generator", ++}; ++ ++static struct device_attribute attributes_com[] = { ++ __ATTR(enable_temperature, 0664, attr_get_temp_enable, ++ attr_set_temp_enable), ++ __ATTR(read_temperature, 0444, attr_get_temp, NULL), ++}; ++ ++static struct device_attribute attributes_interrupt_com[] = { ++ __ATTR(interrupt_pin_configuration, 0664, attr_get_pin_conf, ++ attr_set_pin_conf), ++ __ATTR(interrupt_polarity, 0664, attr_get_interrupt_polarity, ++ attr_set_interrupt_polarity), ++}; ++ ++static int create_sysfs_interfaces(struct device *dev) ++{ ++ int err; ++ int i,n; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ ++ acc_kobj = kobject_create_and_add("accelerometer", &dev->kobj); ++ if(!acc_kobj) ++ return -ENOMEM; ++ ++ mag_kobj = kobject_create_and_add("magnetometer", &dev->kobj); ++ if(!mag_kobj) ++ return -ENOMEM; ++ ++ err = sysfs_create_group(acc_kobj, &attr_group_acc); ++ if (err) ++ kobject_put(acc_kobj); ++ ++ err = sysfs_create_group(mag_kobj, &attr_group_mag); ++ if (err) ++ kobject_put(mag_kobj); ++ ++ if((stat->pdata_acc->gpio_int1 >= 0)|| ++ (stat->pdata_acc->gpio_int2 >= 0)) { ++ err = sysfs_create_group(acc_kobj, &attr_group_int1_acc); ++ if (err) ++ kobject_put(acc_kobj); ++ ++ err = sysfs_create_group(acc_kobj, &attr_group_int2_acc); ++ if (err) ++ kobject_put(acc_kobj); ++ ++ err = sysfs_create_group(mag_kobj, &attr_group_int_mag); ++ if (err) ++ kobject_put(mag_kobj); ++ ++ for (n = 0; n < ARRAY_SIZE(attributes_interrupt_com); n++) ++ if (device_create_file(dev, attributes_interrupt_com + n)) ++ goto error1; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(attributes_com); i++) ++ if (device_create_file(dev, attributes_com + i)) ++ goto error; ++ ++ ++ ++ return 0; ++ ++error: ++ for ( ; i >= 0; i--) ++ device_remove_file(dev, attributes_com + i); ++ ++error1: ++ for ( ; n >= 0; n--) ++ device_remove_file(dev, attributes_interrupt_com + n); ++ ++ dev_err(dev, "%s:Unable to create interface\n", __func__); ++ return -1; ++} ++ ++static void remove_sysfs_interfaces(struct device *dev) ++{ ++ int i; ++ struct lsm9ds0_status *stat = dev_get_drvdata(dev); ++ kobject_put(acc_kobj); ++ kobject_put(mag_kobj); ++ for (i = 0; i < ARRAY_SIZE(attributes_com); i++) ++ device_remove_file(dev, attributes_com + i); ++ if((stat->pdata_acc->gpio_int1 >= 0)|| ++ (stat->pdata_acc->gpio_int2 >= 0)) { ++ for (i = 0; i < ARRAY_SIZE(attributes_interrupt_com); i++) ++ device_remove_file(dev, attributes_interrupt_com + i); ++ } ++} ++ ++int lsm9ds0_acc_input_open(struct input_dev *input) ++{ ++ struct lsm9ds0_status *stat = input_get_drvdata(input); ++ ++ return lsm9ds0_acc_enable(stat); ++} ++ ++void lsm9ds0_acc_input_close(struct input_dev *dev) ++{ ++ struct lsm9ds0_status *stat = input_get_drvdata(dev); ++ ++ lsm9ds0_acc_disable(stat); ++} ++ ++int lsm9ds0_mag_input_open(struct input_dev *input) ++{ ++ struct lsm9ds0_status *stat = input_get_drvdata(input); ++ ++ return lsm9ds0_mag_enable(stat); ++} ++ ++void lsm9ds0_mag_input_close(struct input_dev *dev) ++{ ++ struct lsm9ds0_status *stat = input_get_drvdata(dev); ++ ++ lsm9ds0_mag_disable(stat); ++} ++ ++static int lsm9ds0_acc_get_data(struct lsm9ds0_status *stat, int *xyz) ++{ ++ int i, err = -1; ++ u8 acc_data[6]; ++ s32 hw_d[3] = { 0 }; ++ ++ acc_data[0] = (REG_ACC_DATA_ADDR); ++ err = lsm9ds0_i2c_read(stat, acc_data, 6); ++ if (err < 0) ++ return err; ++ ++ hw_d[0] = ((s32)( (s16)((acc_data[1] << 8) | (acc_data[0])))); ++ hw_d[1] = ((s32)( (s16)((acc_data[3] << 8) | (acc_data[2])))); ++ hw_d[2] = ((s32)( (s16)((acc_data[5] << 8) | (acc_data[4])))); ++ ++#ifdef DEBUG ++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n", ++ LSM9DS0_ACC_DEV_NAME, acc_data[1], acc_data[0], hw_d[0]); ++ pr_debug("%s read y=%X %X(regH regL), y=%d(dec) [ug]\n", ++ LSM9DS0_ACC_DEV_NAME, acc_data[3], acc_data[2], hw_d[1]); ++ pr_debug("%s read z=%X %X(regH regL), z=%d(dec) [ug]\n", ++ LSM9DS0_ACC_DEV_NAME, acc_data[5], acc_data[4], hw_d[2]); ++#endif ++ ++ hw_d[0] = hw_d[0] * stat->sensitivity_acc; ++ hw_d[1] = hw_d[1] * stat->sensitivity_acc; ++ hw_d[2] = hw_d[2] * stat->sensitivity_acc; ++ ++ for (i = 0; i < 3; i++) { ++ xyz[i] = stat->pdata_acc->rot_matrix[0][i] * hw_d[0] + ++ stat->pdata_acc->rot_matrix[1][i] * hw_d[1] + ++ stat->pdata_acc->rot_matrix[2][i] * hw_d[2]; ++ } ++ ++ return err; ++} ++ ++static int lsm9ds0_mag_get_data(struct lsm9ds0_status *stat, int *xyz) ++{ ++ int i, err = -1; ++ u8 mag_data[6]; ++ s32 hw_d[3] = { 0 }; ++ ++ mag_data[0] = (REG_MAG_DATA_ADDR); ++ err = lsm9ds0_i2c_read(stat, mag_data, 6); ++ if (err < 0) ++ return err; ++ ++ hw_d[0] = ((s32)( (s16)((mag_data[1] << 8) | (mag_data[0])))); ++ hw_d[1] = ((s32)( (s16)((mag_data[3] << 8) | (mag_data[2])))); ++ hw_d[2] = ((s32)( (s16)((mag_data[5] << 8) | (mag_data[4])))); ++ ++#ifdef DEBUG ++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n", ++ LSM9DS0_MAG_DEV_NAME, mag_data[1], mag_data[0], hw_d[0]); ++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n", ++ LSM9DS0_MAG_DEV_NAME, mag_data[3], mag_data[2], hw_d[1]); ++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n", ++ LSM9DS0_MAG_DEV_NAME, mag_data[5], mag_data[4], hw_d[2]); ++#endif ++ ++ hw_d[0] = hw_d[0] * stat->sensitivity_mag; ++ hw_d[1] = hw_d[1] * stat->sensitivity_mag; ++ hw_d[2] = hw_d[2] * stat->sensitivity_mag; ++ ++ for (i = 0; i < 3; i++) { ++ xyz[i] = stat->pdata_acc->rot_matrix[0][i] * hw_d[0] + ++ stat->pdata_acc->rot_matrix[1][i] * hw_d[1] + ++ stat->pdata_acc->rot_matrix[2][i] * hw_d[2]; ++ } ++ ++ return err; ++} ++ ++static int lsm9ds0_temp_get_data(struct lsm9ds0_status *stat, ++ int *dec, int *flo) ++{ ++ int err = -1; ++ u8 temp_data[2]; ++ s16 hw_d = 0; ++ ++ temp_data[0] = (REG_TEMP_DATA_ADDR); ++ err = lsm9ds0_i2c_read(stat, temp_data, 2); ++ if (err < 0) ++ return err; ++ ++ hw_d = (s16)((temp_data[1] << 8) | (temp_data[0])); ++ ++ ++#ifdef DEBUG ++ pr_debug("%s read T=%X %X(regH regL), T=%d(dec) [C]\n", ++ LSM9DS0_DEV_NAME, temp_data[1], temp_data[0], hw_d); ++#endif ++ ++ *dec = (int)(hw_d/TEMP_SENSITIVITY) + OFFSET_TEMP; ++ *flo = (((unsigned int)hw_d)%TEMP_SENSITIVITY); ++ ++ return err; ++} ++ ++static void lsm9ds0_acc_report_values(struct lsm9ds0_status *stat, int *xyz) ++{ ++ input_report_abs(stat->input_dev_acc, ABS_X, xyz[0]); ++ input_report_abs(stat->input_dev_acc, ABS_Y, xyz[1]); ++ input_report_abs(stat->input_dev_acc, ABS_Z, xyz[2]); ++ input_sync(stat->input_dev_acc); ++} ++ ++static void lsm9ds0_mag_report_values(struct lsm9ds0_status *stat, int *xyz) ++{ ++ input_report_abs(stat->input_dev_mag, ABS_X, xyz[0]); ++ input_report_abs(stat->input_dev_mag, ABS_Y, xyz[1]); ++ input_report_abs(stat->input_dev_mag, ABS_Z, xyz[2]); ++ input_sync(stat->input_dev_mag); ++} ++ ++static int lsm9ds0_acc_input_init(struct lsm9ds0_status *stat) ++{ ++ int err; ++ ++ stat->input_dev_acc = input_allocate_device(); ++ if (!stat->input_dev_acc) { ++ err = -ENOMEM; ++ dev_err(&stat->client->dev, "accelerometer " ++ "input device allocation failed\n"); ++ goto err0; ++ } ++ ++ stat->input_dev_acc->open = lsm9ds0_acc_input_open; ++ stat->input_dev_acc->close = lsm9ds0_acc_input_close; ++ stat->input_dev_acc->name = LSM9DS0_ACC_DEV_NAME; ++ stat->input_dev_acc->id.bustype = BUS_I2C; ++ stat->input_dev_acc->dev.parent = &stat->client->dev; ++ ++ input_set_drvdata(stat->input_dev_acc, stat); ++ ++ set_bit(EV_ABS, stat->input_dev_acc->evbit); ++ ++ input_set_abs_params(stat->input_dev_acc, ABS_X, ++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT); ++ input_set_abs_params(stat->input_dev_acc, ABS_Y, ++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT); ++ input_set_abs_params(stat->input_dev_acc, ABS_Z, ++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT); ++ ++ err = input_register_device(stat->input_dev_acc); ++ if (err) { ++ dev_err(&stat->client->dev, ++ "unable to register accelerometer input device %s\n", ++ stat->input_dev_acc->name); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ input_free_device(stat->input_dev_acc); ++err0: ++ return err; ++} ++ ++static int lsm9ds0_mag_input_init(struct lsm9ds0_status *stat) ++{ ++ int err; ++ ++ stat->input_dev_mag = input_allocate_device(); ++ if (!stat->input_dev_mag) { ++ err = -ENOMEM; ++ dev_err(&stat->client->dev, "magnetometer " ++ "input device allocation failed\n"); ++ goto err0; ++ } ++ ++ stat->input_dev_mag->open = lsm9ds0_mag_input_open; ++ stat->input_dev_mag->close = lsm9ds0_mag_input_close; ++ stat->input_dev_mag->name = LSM9DS0_MAG_DEV_NAME; ++ stat->input_dev_mag->id.bustype = BUS_I2C; ++ stat->input_dev_mag->dev.parent = &stat->client->dev; ++ ++ input_set_drvdata(stat->input_dev_mag, stat); ++ ++ set_bit(EV_ABS, stat->input_dev_mag->evbit); ++ ++ input_set_abs_params(stat->input_dev_mag, ABS_X, ++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT); ++ input_set_abs_params(stat->input_dev_mag, ABS_Y, ++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT); ++ input_set_abs_params(stat->input_dev_mag, ABS_Z, ++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT); ++ ++ err = input_register_device(stat->input_dev_mag); ++ if (err) { ++ dev_err(&stat->client->dev, ++ "unable to register magnetometer input device %s\n", ++ stat->input_dev_mag->name); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ input_free_device(stat->input_dev_mag); ++err0: ++ return err; ++} ++ ++static void lsm9ds0_input_cleanup(struct lsm9ds0_status *stat) ++{ ++ input_unregister_device(stat->input_dev_acc); ++ input_free_device(stat->input_dev_acc); ++ ++ input_unregister_device(stat->input_dev_mag); ++ input_free_device(stat->input_dev_mag); ++} ++ ++static void poll_function_work_acc(struct work_struct *input_work_acc) ++{ ++ struct lsm9ds0_status *stat; ++ int xyz[3] = { 0 }; ++ int err; ++ ++ stat = container_of((struct work_struct *)input_work_acc, ++ struct lsm9ds0_status, input_work_acc); ++ ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_acc_get_data(stat, xyz); ++ if (err < 0) ++ dev_err(&stat->client->dev, "get_accelerometer_data failed\n"); ++ else ++ lsm9ds0_acc_report_values(stat, xyz); ++ ++ mutex_unlock(&stat->lock); ++ hrtimer_start(&stat->hr_timer_acc, stat->ktime_acc, HRTIMER_MODE_REL); ++} ++ ++static void poll_function_work_mag(struct work_struct *input_work_mag) ++{ ++ struct lsm9ds0_status *stat; ++ int xyz[3] = { 0 }; ++ int err; ++ int dec; ++ int flo; ++ ++ stat = container_of((struct work_struct *)input_work_mag, ++ struct lsm9ds0_status, input_work_mag); ++ ++ mutex_lock(&stat->lock); ++ ++ if(atomic_read(&stat->enabled_temp)) { ++ err = lsm9ds0_temp_get_data(stat, &dec, &flo); ++ if (err < 0) ++ dev_err(&stat->client->dev, "get_temperature_data" ++ " failed\n"); ++ else { ++ stat->temp_value_dec = dec; ++ stat->temp_value_flo = flo; ++ } ++ } ++ ++ if(atomic_read(&stat->enabled_mag)) { ++ err = lsm9ds0_mag_get_data(stat, xyz); ++ if (err < 0) ++ dev_err(&stat->client->dev, "get_magnetometer_data" ++ " failed\n"); ++ else ++ lsm9ds0_mag_report_values(stat, xyz); ++ } ++ ++ mutex_unlock(&stat->lock); ++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL); ++} ++ ++enum hrtimer_restart poll_function_read_acc(struct hrtimer *timer) ++{ ++ struct lsm9ds0_status *stat; ++ ++ ++ stat = container_of((struct hrtimer *)timer, ++ struct lsm9ds0_status, hr_timer_acc); ++ ++ queue_work(lsm9ds0_workqueue, &stat->input_work_acc); ++ return HRTIMER_NORESTART; ++} ++ ++enum hrtimer_restart poll_function_read_mag(struct hrtimer *timer) ++{ ++ struct lsm9ds0_status *stat; ++ ++ ++ stat = container_of((struct hrtimer *)timer, ++ struct lsm9ds0_status, hr_timer_mag); ++ ++ queue_work(lsm9ds0_workqueue, &stat->input_work_mag); ++ return HRTIMER_NORESTART; ++} ++ ++static int lsm9ds0_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct lsm9ds0_status *stat; ++ ++ u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; ++ ++ int err = -1; ++ dev_info(&client->dev, "probe start.\n"); ++ stat = kzalloc(sizeof(struct lsm9ds0_status), GFP_KERNEL); ++ if (stat == NULL) { ++ err = -ENOMEM; ++ dev_err(&client->dev, ++ "failed to allocate memory for module data: " ++ "%d\n", err); ++ goto exit_check_functionality_failed; ++ } ++ ++ stat->use_smbus = 0; ++ 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)){ ++ stat->use_smbus = 1; ++ dev_warn(&client->dev, "client using SMBUS\n"); ++ } else { ++ err = -ENODEV; ++ dev_err(&client->dev, "client nor SMBUS capable\n"); ++ goto exit_check_functionality_failed; ++ } ++ } ++ ++ if(lsm9ds0_workqueue == 0) ++ lsm9ds0_workqueue = create_workqueue("lsm9ds0_workqueue"); ++ ++ hrtimer_init(&stat->hr_timer_acc, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ stat->hr_timer_acc.function = &poll_function_read_acc; ++ hrtimer_init(&stat->hr_timer_mag, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ stat->hr_timer_mag.function = &poll_function_read_mag; ++ ++ mutex_init(&stat->lock); ++ mutex_lock(&stat->lock); ++ ++ stat->client = client; ++ i2c_set_clientdata(client, stat); ++ ++ stat->pdata_acc = kmalloc(sizeof(*stat->pdata_acc), GFP_KERNEL); ++ stat->pdata_mag = kmalloc(sizeof(*stat->pdata_mag), GFP_KERNEL); ++ if ((stat->pdata_acc == NULL)||(stat->pdata_mag == 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(stat->pdata_acc, &default_lsm9ds0_acc_pdata, ++ sizeof(*stat->pdata_acc)); ++ memcpy(stat->pdata_mag, &default_lsm9ds0_mag_pdata, ++ sizeof(*stat->pdata_mag)); ++ dev_info(&client->dev, "using default plaform_data for " ++ "accelerometer and magnetometer\n"); ++ } else { ++ struct lsm9ds0_main_platform_data *tmp; ++ tmp = kzalloc(sizeof(struct lsm9ds0_main_platform_data), ++ GFP_KERNEL); ++ if(tmp == NULL) ++ goto exit_kfree_pdata; ++ memcpy(tmp, client->dev.platform_data, sizeof(*tmp)); ++ if(tmp->pdata_acc == NULL) { ++ memcpy(stat->pdata_acc, &default_lsm9ds0_acc_pdata, ++ sizeof(*stat->pdata_acc)); ++ dev_info(&client->dev, "using default plaform_data for " ++ "accelerometer\n"); ++ } else { ++ memcpy(stat->pdata_acc, tmp->pdata_acc, ++ sizeof(*stat->pdata_acc)); ++ } ++ if(tmp->pdata_mag == NULL) { ++ memcpy(stat->pdata_mag, &default_lsm9ds0_mag_pdata, ++ sizeof(*stat->pdata_mag)); ++ dev_info(&client->dev, "using default plaform_data for " ++ "magnetometer\n"); ++ } else { ++ memcpy(stat->pdata_mag, tmp->pdata_mag, ++ sizeof(*stat->pdata_mag)); ++ } ++ kfree(tmp); ++ } ++ ++ err = lsm9ds0_acc_validate_pdata(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "failed to validate platform data for " ++ "accelerometer \n"); ++ goto exit_kfree_pdata; ++ } ++ ++ err = lsm9ds0_mag_validate_pdata(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "failed to validate platform data for " ++ "magnetometer\n"); ++ goto exit_kfree_pdata; ++ } ++ ++ if (stat->pdata_acc->init) { ++ err = stat->pdata_acc->init(); ++ if (err < 0) { ++ dev_err(&client->dev, "accelerometer init failed: " ++ "%d\n", err); ++ goto err_pdata_acc_init; ++ } ++ } ++ if (stat->pdata_mag->init) { ++ err = stat->pdata_mag->init(); ++ if (err < 0) { ++ dev_err(&client->dev, "magnetometer init failed: " ++ "%d\n", err); ++ goto err_pdata_mag_init; ++ } ++ } ++ ++ if(stat->pdata_acc->gpio_int1 >= 0) { ++ if (!gpio_is_valid(stat->pdata_acc->gpio_int1)) { ++ dev_err(&client->dev, "The requested GPIO [%d] is not " ++ "available\n", stat->pdata_acc->gpio_int1); ++ err = -EINVAL; ++ goto err_gpio1_valid; ++ } ++ ++ err = gpio_request(stat->pdata_acc->gpio_int1, ++ "INTERRUPT_PIN1_LSM9DS0"); ++ if(err < 0) { ++ dev_err(&client->dev, "Unable to request GPIO [%d].\n", ++ stat->pdata_acc->gpio_int1); ++ err = -EINVAL; ++ goto err_gpio1_valid; ++ } ++ gpio_direction_input(stat->pdata_acc->gpio_int1); ++ stat->irq1 = gpio_to_irq(stat->pdata_acc->gpio_int1); ++ if(stat->irq1 < 0) { ++ dev_err(&client->dev, "GPIO [%d] cannot be used as " ++ "interrupt.\n", stat->pdata_acc->gpio_int1); ++ err = -EINVAL; ++ goto err_gpio1_irq; ++ } ++ pr_info("%s: %s has set irq1 to irq: %d, mapped on gpio:%d\n", ++ LSM9DS0_DEV_NAME, __func__, stat->irq1, ++ stat->pdata_acc->gpio_int1); ++ } ++ ++ if(stat->pdata_acc->gpio_int2 >= 0) { ++ if (!gpio_is_valid(stat->pdata_acc->gpio_int2)) { ++ dev_err(&client->dev, "The requested GPIO [%d] is not " ++ "available\n", stat->pdata_acc->gpio_int2); ++ err = -EINVAL; ++ goto err_gpio2_valid; ++ } ++ ++ err = gpio_request(stat->pdata_acc->gpio_int2, ++ "INTERRUPT_PIN2_LSM9DS0"); ++ if(err < 0) { ++ dev_err(&client->dev, "Unable to request GPIO [%d].\n", ++ stat->pdata_acc->gpio_int2); ++ err = -EINVAL; ++ goto err_gpio2_valid; ++ } ++ gpio_direction_input(stat->pdata_acc->gpio_int2); ++ stat->irq2 = gpio_to_irq(stat->pdata_acc->gpio_int2); ++ if(stat->irq2 < 0) { ++ dev_err(&client->dev, "GPIO [%d] cannot be used as " ++ "interrupt.\n", stat->pdata_acc->gpio_int2); ++ err = -EINVAL; ++ goto err_gpio2_irq; ++ } ++ pr_info("%s: %s has set irq2 to irq: %d, " ++ "mapped on gpio:%d\n", ++ LSM9DS0_DEV_NAME, __func__, stat->irq2, ++ stat->pdata_acc->gpio_int2); ++ } ++ ++ err = lsm9ds0_hw_init(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "hw init failed: %d\n", err); ++ goto err_hw_init; ++ } ++ ++ err = lsm9ds0_acc_device_power_on(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "accelerometer power on failed: " ++ "%d\n", err); ++ goto err_pdata_init; ++ } ++ err = lsm9ds0_mag_device_power_on(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "magnetometer power on failed: " ++ "%d\n", err); ++ goto err_pdata_init; ++ } ++ ++ err = lsm9ds0_acc_update_fs_range(stat, stat->pdata_acc->fs_range); ++ if (err < 0) { ++ dev_err(&client->dev, "update_fs_range on accelerometer " ++ "failed\n"); ++ goto err_power_off_acc; ++ } ++ ++ err = lsm9ds0_mag_update_fs_range(stat, stat->pdata_mag->fs_range); ++ if (err < 0) { ++ dev_err(&client->dev, "update_fs_range on magnetometer " ++ "failed\n"); ++ goto err_power_off_mag; ++ } ++ ++ err = lsm9ds0_acc_update_odr(stat, stat->pdata_acc->poll_interval); ++ if (err < 0) { ++ dev_err(&client->dev, "update_odr on accelerometer failed\n"); ++ goto err_power_off; ++ } ++ ++ err = lsm9ds0_mag_update_odr(stat, stat->pdata_mag->poll_interval); ++ if (err < 0) { ++ dev_err(&client->dev, "update_odr on magnetometer failed\n"); ++ goto err_power_off; ++ } ++ ++ err = lsm9ds0_acc_update_filter(stat, ++ stat->pdata_acc->aa_filter_bandwidth); ++ if (err < 0) { ++ dev_err(&client->dev, "update_filter on accelerometer " ++ "failed\n"); ++ goto err_power_off; ++ } ++ ++ err = lsm9ds0_acc_input_init(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "accelerometer input init failed\n"); ++ goto err_power_off; ++ } ++ ++ err = lsm9ds0_mag_input_init(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "magnetometer input init failed\n"); ++ goto err_power_off; ++ } ++ ++ err = create_sysfs_interfaces(&client->dev); ++ if (err < 0) { ++ dev_err(&client->dev, ++ "device LSM9DS0_DEV_NAME sysfs register failed\n"); ++ goto err_input_cleanup; ++ } ++ ++ lsm9ds0_acc_device_power_off(stat); ++ lsm9ds0_mag_device_power_off(stat); ++ ++ if(stat->pdata_acc->gpio_int1 >= 0){ ++ INIT_WORK(&stat->irq1_work, lsm9ds0_irq1_work_func); ++ stat->irq1_work_queue = ++ create_singlethread_workqueue("lsm9ds0_wq1"); ++ if (!stat->irq1_work_queue) { ++ err = -ENOMEM; ++ dev_err(&client->dev, ++ "cannot create work queue1: %d\n", err); ++ goto err_remove_sysfs_int; ++ } ++ err = request_irq(stat->irq1, lsm9ds0_isr1, ++ IRQF_TRIGGER_RISING, "lsm9ds0_irq1", stat); ++ if (err < 0) { ++ dev_err(&client->dev, "request irq1 failed: %d\n", err); ++ goto err_destoyworkqueue1; ++ } ++ disable_irq_nosync(stat->irq1); ++ } ++ ++ if(stat->pdata_acc->gpio_int2 >= 0){ ++ INIT_WORK(&stat->irq2_work, lsm9ds0_irq2_work_func); ++ stat->irq2_work_queue = ++ create_singlethread_workqueue("lsm9ds0_wq2"); ++ if (!stat->irq2_work_queue) { ++ err = -ENOMEM; ++ dev_err(&client->dev, ++ "cannot create work queue2: %d\n", err); ++ goto err_free_irq1; ++ } ++ err = request_irq(stat->irq2, lsm9ds0_isr2, ++ IRQF_TRIGGER_RISING, "lsm9ds0_irq2", stat); ++ if (err < 0) { ++ dev_err(&client->dev, "request irq2 failed: %d\n", err); ++ goto err_destoyworkqueue2; ++ } ++ disable_irq_nosync(stat->irq2); ++ } ++ ++ INIT_WORK(&stat->input_work_acc, poll_function_work_acc); ++ INIT_WORK(&stat->input_work_mag, poll_function_work_mag); ++ ++ mutex_unlock(&stat->lock); ++ dev_info(&client->dev, "%s: probed\n", LSM9DS0_DEV_NAME); ++ return 0; ++ ++err_destoyworkqueue2: ++ destroy_workqueue(stat->irq2_work_queue); ++err_free_irq1: ++ free_irq(stat->irq1, stat); ++err_destoyworkqueue1: ++ destroy_workqueue(stat->irq1_work_queue); ++err_remove_sysfs_int: ++ remove_sysfs_interfaces(&client->dev); ++err_input_cleanup: ++ lsm9ds0_input_cleanup(stat); ++err_power_off: ++err_power_off_mag: ++ lsm9ds0_mag_device_power_off(stat); ++err_power_off_acc: ++ lsm9ds0_acc_device_power_off(stat); ++ kfree(stat->interrupt); ++err_hw_init: ++err_gpio2_irq: ++ gpio_free(stat->pdata_acc->gpio_int2); ++err_gpio2_valid: ++err_gpio1_irq: ++ gpio_free(stat->pdata_acc->gpio_int1); ++err_gpio1_valid: ++err_pdata_init: ++err_pdata_mag_init: ++ if (stat->pdata_mag->exit) ++ stat->pdata_mag->exit(); ++err_pdata_acc_init: ++ if (stat->pdata_acc->exit) ++ stat->pdata_acc->exit(); ++exit_kfree_pdata: ++ kfree(stat->pdata_acc); ++ kfree(stat->pdata_mag); ++err_mutexunlock: ++ mutex_unlock(&stat->lock); ++ kfree(stat); ++ if(!lsm9ds0_workqueue) { ++ flush_workqueue(lsm9ds0_workqueue); ++ destroy_workqueue(lsm9ds0_workqueue); ++ } ++exit_check_functionality_failed: ++ pr_err("%s: Driver Init failed\n", LSM9DS0_DEV_NAME); ++ return err; ++} ++ ++static int lsm9ds0_remove(struct i2c_client *client) ++{ ++ struct lsm9ds0_status *stat = i2c_get_clientdata(client); ++ ++ lsm9ds0_acc_disable(stat); ++ lsm9ds0_mag_disable(stat); ++ lsm9ds0_temperature_disable(stat); ++ ++ if(stat->pdata_acc->gpio_int1 >= 0) { ++ free_irq(stat->irq1, stat); ++ gpio_free(stat->pdata_acc->gpio_int1); ++ destroy_workqueue(stat->irq1_work_queue); ++ } ++ ++ if(stat->pdata_acc->gpio_int2 >= 0) { ++ free_irq(stat->irq2, stat); ++ gpio_free(stat->pdata_acc->gpio_int2); ++ destroy_workqueue(stat->irq2_work_queue); ++ } ++ ++ lsm9ds0_acc_input_cleanup(stat); ++ lsm9ds0_mag_input_cleanup(stat); ++ ++ remove_sysfs_interfaces(&client->dev); ++ ++ if (stat->pdata_acc->exit) ++ stat->pdata_acc->exit(); ++ ++ if (stat->pdata_mag->exit) ++ stat->pdata_mag->exit(); ++ ++ if((stat->pdata_acc->gpio_int1 >= 0)|| ++ (stat->pdata_acc->gpio_int2 >= 0)) { ++ kfree(stat->interrupt); ++ } ++ ++ if(!lsm9ds0_workqueue) { ++ flush_workqueue(lsm9ds0_workqueue); ++ destroy_workqueue(lsm9ds0_workqueue); ++ } ++ ++ kfree(stat->pdata_acc); ++ kfree(stat->pdata_mag); ++ kfree(stat); ++ return 0; ++} ++ ++static const struct i2c_device_id lsm9ds0_id[] = { ++ { LSM9DS0_DEV_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, lsm9ds0_id); ++ ++static const struct of_device_id lsm9ds0_of_match[] = { ++ { .compatible = "st,"LSM9DS0_DEV_NAME, }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, lsm9ds0_of_match); ++ ++static struct i2c_driver lsm9ds0_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = LSM9DS0_DEV_NAME, ++ .of_match_table = lsm9ds0_of_match, ++ }, ++ .probe = lsm9ds0_probe, ++ .remove = lsm9ds0_remove, ++ .id_table = lsm9ds0_id, ++}; ++ ++module_i2c_driver(lsm9ds0_i2c_driver); ++ ++ ++MODULE_DESCRIPTION("lsm9ds0 accelerometer and magnetometer driver"); ++MODULE_AUTHOR("Matteo Dameno, Denis Ciocca, STMicroelectronics"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/misc/lsm9ds0_gyr.c b/drivers/input/misc/lsm9ds0_gyr.c +new file mode 100644 +index 0000000..360f85c +--- /dev/null ++++ b/drivers/input/misc/lsm9ds0_gyr.c +@@ -0,0 +1,1726 @@ ++/******************** (C) COPYRIGHT 2012 STMicroelectronics ******************** ++* ++* File Name : lsm9ds0_gyr_sysfs.c ++* Authors : MEMS Motion Sensors Products Div- Application Team ++* : Matteo Dameno (matteo.dameno@st.com) ++* : Denis Ciocca (denis.ciocca@st.com) ++* : Both authors are willing to be considered the contact ++* : and update points for the driver. ++* Version : V 1.2 sysfs ++* Date : 2012/Jul/10 ++* Description : LSM9DS0 digital output gyroscope sensor API ++* ++******************************************************************************** ++* ++* 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. ++* ++******************************************************************************** ++* REVISON HISTORY ++* ++* VERSION | DATE | AUTHORS | DESCRIPTION ++* 1.0 | 2010/May/02 | Carmine Iascone | First Release ++* 1.1.3 | 2011/Jun/24 | Matteo Dameno | Corrects ODR Bug ++* 1.1.4 | 2011/Sep/02 | Matteo Dameno | SMB Bus Mng, ++* | | | forces BDU setting ++* 1.1.5 | 2011/Sep/24 | Matteo Dameno | Introduces FIFO Feat. ++* 1.1.5.2 | 2011/Nov/11 | Matteo Dameno | enable gpio_int to be ++* | | | passed as parameter at ++* | | | module loading time; ++* | | | corrects polling ++* | | | bug at end of probing; ++* 1.1.5.3 | 2011/Dec/20 | Matteo Dameno | corrects error in ++* | | | I2C SADROOT; Modifies ++* | | | resume suspend func. ++* 1.1.5.4 | 2012/Jan/09 | Matteo Dameno | moved under input/misc; ++* 1.1.5.5 | 2012/Mar/30 | Matteo Dameno | moved watermark, use_smbus, ++* | | | fifomode @ struct foo_status ++* | | | sysfs range input format ++* | | | changed to decimal ++* 1.2 | 2012/Jul/10 | Denis Ciocca | input_poll_dev removal ++* 1.2.1 | 2012/Jul/10 | Denis Ciocca | added high resolution timers ++*******************************************************************************/ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/mutex.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/gpio.h> ++#include <linux/slab.h> ++#include <linux/moduleparam.h> ++#include <linux/kernel.h> ++#include <linux/stat.h> ++ ++ ++#include <linux/input/lsm9ds0.h> ++/* #include "lsm9ds0.h" */ ++ ++/* Maximum polled-device-reported rot speed value value in dps */ ++#define FS_MAX 32768 ++#define MS_TO_NS(x) (x*1000000L) ++ ++/* lsm9ds0 gyroscope registers */ ++#define WHO_AM_I (0x0F) ++ ++#define SENSITIVITY_250 8750 /* udps/LSB */ ++#define SENSITIVITY_500 17500 /* udps/LSB */ ++#define SENSITIVITY_2000 70000 /* udps/LSB */ ++ ++#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 FIFO_CTRL_REG (0x2E) /* FIFO CONTROL REGISTER */ ++#define FIFO_SRC_REG (0x2F) /* FIFO SOURCE REGISTER */ ++#define OUT_X_L (0x28) /* 1st AXIS OUT REG of 6 */ ++ ++#define AXISDATA_REG OUT_X_L ++ ++/* 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 ODR095 (0x00) /* ODR = 95Hz */ ++#define ODR190 (0x40) /* ODR = 190Hz */ ++#define ODR380 (0x80) /* ODR = 380Hz */ ++#define ODR760 (0xC0) /* ODR = 760Hz */ ++ ++/* 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) ++ ++#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 ++ ++ ++/* #define DEBUG 1 */ ++ ++/** Registers Contents */ ++#define WHOAMI_LSM9DS0_GYR (0xD4) /* Expected content for WAI register*/ ++ ++static int int1_gpio = LSM9DS0_GYR_DEFAULT_INT1_GPIO; ++static int int2_gpio = LSM9DS0_GYR_DEFAULT_INT2_GPIO; ++/* module_param(int1_gpio, int, S_IRUGO); */ ++module_param(int2_gpio, int, S_IRUGO); ++ ++/* ++ * LSM9DS0 gyroscope data ++ * brief structure containing gyroscope values for yaw, pitch and roll in ++ * s32 ++ */ ++ ++struct lsm9ds0_gyr_triple { ++ s32 x, /* x-axis angular rate data. */ ++ y, /* y-axis angluar rate data. */ ++ z; /* z-axis angular rate data. */ ++}; ++ ++struct output_rate { ++ int poll_rate_ms; ++ u8 mask; ++}; ++ ++static const struct output_rate odr_table[] = { ++ ++ { 2, ODR760|BW10}, ++ { 3, ODR380|BW01}, ++ { 6, ODR190|BW00}, ++ { 11, ODR095|BW00}, ++}; ++ ++static struct lsm9ds0_gyr_platform_data default_lsm9ds0_gyr_pdata = { ++ .fs_range = LSM9DS0_GYR_FS_250DPS, ++ .axis_map_x = 0, ++ .axis_map_y = 1, ++ .axis_map_z = 2, ++ .negate_x = 0, ++ .negate_y = 0, ++ .negate_z = 0, ++ ++ .poll_interval = 100, ++ .min_interval = LSM9DS0_GYR_MIN_POLL_PERIOD_MS, /* 2ms */ ++ ++ .gpio_int1 = LSM9DS0_GYR_DEFAULT_INT1_GPIO, ++ .gpio_int2 = LSM9DS0_GYR_DEFAULT_INT2_GPIO, /* int for fifo */ ++ ++}; ++ ++struct workqueue_struct *lsm9ds0_gyr_workqueue = 0; ++ ++struct lsm9ds0_gyr_status { ++ struct i2c_client *client; ++ struct lsm9ds0_gyr_platform_data *pdata; ++ ++ struct mutex lock; ++ ++ struct input_dev *input_dev; ++ ++ int hw_initialized; ++ atomic_t enabled; ++ int use_smbus; ++ ++ u8 reg_addr; ++ u8 resume_state[RESUME_ENTRIES]; ++ ++ u32 sensitivity; ++ ++ /* interrupt related */ ++ int irq2; ++ struct work_struct irq2_work; ++ struct workqueue_struct *irq2_work_queue; ++ ++ bool polling_enabled; ++ /* fifo related */ ++ u8 watermark; ++ u8 fifomode; ++ ++ struct hrtimer hr_timer; ++ ktime_t ktime; ++ struct work_struct polling_task; ++}; ++ ++ ++static int lsm9ds0_gyr_i2c_read(struct lsm9ds0_gyr_status *stat, u8 *buf, ++ int len) ++{ ++ int ret; ++ u8 reg = buf[0]; ++ u8 cmd = reg; ++ ++/* ++ if (len > sizeof(buf)) ++ dev_err(&stat->client->dev, ++ "read error insufficient buffer length: " ++ "len:%d, buf size=%d\n", ++ len, sizeof(buf)); ++*/ ++ if (len > 1) ++ cmd = (I2C_AUTO_INCREMENT | reg); ++ if (stat->use_smbus) { ++ if (len == 1) { ++ ret = i2c_smbus_read_byte_data(stat->client, cmd); ++ buf[0] = ret & 0xff; ++#ifdef DEBUG ++ dev_warn(&stat->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(stat->client, ++ cmd, len, buf); ++#ifdef DEBUG ++ dev_warn(&stat->client->dev, ++ "i2c_smbus_read_i2c_block_data: ret:%d len:%d, " ++ "command=0x%02x, ", ++ ret, len, cmd); ++ unsigned int 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(&stat->client->dev, ++ "read transfer error: len:%d, command=0x%02x\n", ++ len, cmd); ++ return 0; /* failure */ ++ } ++ return len; /* success */ ++ } ++ ++ ret = i2c_master_send(stat->client, &cmd, sizeof(cmd)); ++ if (ret != sizeof(cmd)) ++ return ret; ++ ++ return i2c_master_recv(stat->client, buf, len); ++} ++ ++static int lsm9ds0_gyr_i2c_write(struct lsm9ds0_gyr_status *stat, 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 (stat->use_smbus) { ++ if (len == 1) { ++ ret = i2c_smbus_write_byte_data(stat->client, ++ reg, value); ++#ifdef DEBUG ++ dev_warn(&stat->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(stat->client, ++ reg, len, buf + 1); ++#ifdef DEBUG ++ dev_warn(&stat->client->dev, ++ "i2c_smbus_write_i2c_block_data: ret=%d, " ++ "len:%d, command=0x%02x, ", ++ ret, len, reg); ++ unsigned int 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(stat->client, buf, len+1); ++ return (ret == len+1) ? 0 : ret; ++} ++ ++ ++static int lsm9ds0_gyr_register_write(struct lsm9ds0_gyr_status *stat, ++ 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 = lsm9ds0_gyr_i2c_write(stat, buf, 1); ++ if (err < 0) ++ return err; ++ ++ return err; ++} ++ ++static int lsm9ds0_gyr_register_read(struct lsm9ds0_gyr_status *stat, ++ u8 *buf, u8 reg_address) ++{ ++ ++ int err = -1; ++ buf[0] = (reg_address); ++ err = lsm9ds0_gyr_i2c_read(stat, buf, 1); ++ return err; ++} ++ ++static int lsm9ds0_gyr_register_update(struct lsm9ds0_gyr_status *stat, ++ u8 *buf, u8 reg_address, u8 mask, u8 new_bit_values) ++{ ++ int err = -1; ++ u8 init_val; ++ u8 updated_val; ++ err = lsm9ds0_gyr_register_read(stat, buf, reg_address); ++ if (!(err < 0)) { ++ init_val = buf[0]; ++ updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); ++ err = lsm9ds0_gyr_register_write(stat, buf, reg_address, ++ updated_val); ++ } ++ return err; ++} ++ ++ ++static int lsm9ds0_gyr_update_watermark(struct lsm9ds0_gyr_status *stat, ++ u8 watermark) ++{ ++ int res = 0; ++ u8 buf[2]; ++ u8 new_value; ++ ++ mutex_lock(&stat->lock); ++ new_value = (watermark % 0x20); ++ res = lsm9ds0_gyr_register_update(stat, buf, FIFO_CTRL_REG, ++ FIFO_WATERMARK_MASK, new_value); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "failed to update watermark\n"); ++ return res; ++ } ++ dev_dbg(&stat->client->dev, "%s new_value:0x%02x,watermark:0x%02x\n", ++ __func__, new_value, watermark); ++ ++ stat->resume_state[RES_FIFO_CTRL_REG] = ++ ((FIFO_WATERMARK_MASK & new_value) | ++ (~FIFO_WATERMARK_MASK & ++ stat->resume_state[RES_FIFO_CTRL_REG])); ++ stat->watermark = new_value; ++ mutex_unlock(&stat->lock); ++ return res; ++} ++ ++static int lsm9ds0_gyr_update_fifomode(struct lsm9ds0_gyr_status *stat, ++ u8 fifomode) ++{ ++ int res; ++ u8 buf[2]; ++ u8 new_value; ++ ++ new_value = fifomode; ++ res = lsm9ds0_gyr_register_update(stat, buf, FIFO_CTRL_REG, ++ FIFO_MODE_MASK, new_value); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "failed to update fifoMode\n"); ++ return res; ++ } ++ /* ++ dev_dbg(&stat->client->dev, "new_value:0x%02x,prev fifomode:0x%02x\n", ++ __func__, new_value, stat->fifomode); ++ */ ++ stat->resume_state[RES_FIFO_CTRL_REG] = ++ ((FIFO_MODE_MASK & new_value) | ++ (~FIFO_MODE_MASK & ++ stat->resume_state[RES_FIFO_CTRL_REG])); ++ stat->fifomode = new_value; ++ ++ return res; ++} ++ ++static int lsm9ds0_gyr_fifo_reset(struct lsm9ds0_gyr_status *stat) ++{ ++ u8 oldmode; ++ int res; ++ ++ oldmode = stat->fifomode; ++ res = lsm9ds0_gyr_update_fifomode(stat, FIFO_MODE_BYPASS); ++ if (res < 0) ++ return res; ++ res = lsm9ds0_gyr_update_fifomode(stat, oldmode); ++ if (res >= 0) ++ dev_dbg(&stat->client->dev, "%s fifo reset to: 0x%02x\n", ++ __func__, oldmode); ++ ++ return res; ++} ++ ++static int lsm9ds0_gyr_fifo_hwenable(struct lsm9ds0_gyr_status *stat, ++ u8 enable) ++{ ++ int res; ++ u8 buf[2]; ++ u8 set = 0x00; ++ if (enable) ++ set = FIFO_ENABLE; ++ ++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG5, ++ FIFO_ENABLE, set); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "fifo_hw switch to:0x%02x failed\n", ++ set); ++ return res; ++ } ++ stat->resume_state[RES_CTRL_REG5] = ++ ((FIFO_ENABLE & set) | ++ (~FIFO_ENABLE & stat->resume_state[RES_CTRL_REG5])); ++ dev_dbg(&stat->client->dev, "%s set to:0x%02x\n", __func__, set); ++ return res; ++} ++ ++static int lsm9ds0_gyr_manage_int2settings(struct lsm9ds0_gyr_status *stat, ++ u8 fifomode) ++{ ++ int res; ++ u8 buf[2]; ++ bool enable_fifo_hw; ++ bool recognized_mode = false; ++ u8 int2bits = I2_NONE; ++/* ++ if (stat->polling_enabled) { ++ fifomode = FIFO_MODE_BYPASS; ++ dbg_warn(&stat->client->dev, "in polling mode, fifo mode forced" ++ " to BYPASS mode\n"); ++ } ++*/ ++ ++ ++ switch (fifomode) { ++ case FIFO_MODE_FIFO: ++ recognized_mode = true; ++ ++ if (stat->polling_enabled) { ++ int2bits = I2_NONE; ++ enable_fifo_hw = false; ++ } else { ++ int2bits = (I2_WTM | I2_OVRUN); ++ enable_fifo_hw = true; ++ } ++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3, ++ I2_MASK, int2bits); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "%s : failed to update " ++ "CTRL_REG3:0x%02x\n", ++ __func__, fifomode); ++ goto err_mutex_unlock; ++ } ++ stat->resume_state[RES_CTRL_REG3] = ++ ((I2_MASK & int2bits) | ++ (~(I2_MASK) & stat->resume_state[RES_CTRL_REG3])); ++ /* enable_fifo_hw = true; */ ++ break; ++ ++ case FIFO_MODE_BYPASS: ++ recognized_mode = true; ++ ++ if (stat->polling_enabled) ++ int2bits = I2_NONE; ++ else ++ int2bits = I2_DRDY; ++ ++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3, ++ I2_MASK, int2bits); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "%s : failed to update" ++ " to CTRL_REG3:0x%02x\n", ++ __func__, fifomode); ++ goto err_mutex_unlock; ++ } ++ stat->resume_state[RES_CTRL_REG3] = ++ ((I2_MASK & int2bits) | ++ (~I2_MASK & stat->resume_state[RES_CTRL_REG3])); ++ enable_fifo_hw = false; ++ break; ++ ++ default: ++ recognized_mode = false; ++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3, ++ I2_MASK, I2_NONE); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "%s : failed to update " ++ "CTRL_REG3:0x%02x\n", ++ __func__, fifomode); ++ goto err_mutex_unlock; ++ } ++ enable_fifo_hw = false; ++ stat->resume_state[RES_CTRL_REG3] = ++ ((I2_MASK & 0x00) | ++ (~I2_MASK & stat->resume_state[RES_CTRL_REG3])); ++ break; ++ ++ } ++ if (recognized_mode) { ++ res = lsm9ds0_gyr_update_fifomode(stat, fifomode); ++ if (res < 0) { ++ dev_err(&stat->client->dev, "%s : failed to " ++ "set fifoMode\n", __func__); ++ goto err_mutex_unlock; ++ } ++ } ++ res = lsm9ds0_gyr_fifo_hwenable(stat, enable_fifo_hw); ++ ++err_mutex_unlock: ++ ++ return res; ++} ++ ++ ++static int lsm9ds0_gyr_update_fs_range(struct lsm9ds0_gyr_status *stat, ++ u8 new_fs) ++{ ++ int res ; ++ u8 buf[2]; ++ ++ u32 sensitivity; ++ ++ switch(new_fs) { ++ case LSM9DS0_GYR_FS_250DPS: ++ sensitivity = SENSITIVITY_250; ++ break; ++ case LSM9DS0_GYR_FS_500DPS: ++ sensitivity = SENSITIVITY_500; ++ break; ++ case LSM9DS0_GYR_FS_2000DPS: ++ sensitivity = SENSITIVITY_2000; ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid g range " ++ "requested: %u\n", new_fs); ++ return -EINVAL; ++ } ++ ++ ++ buf[0] = CTRL_REG4; ++ ++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG4, ++ FS_MASK, new_fs); ++ ++ if (res < 0) { ++ dev_err(&stat->client->dev, "%s : failed to update fs:0x%02x\n", ++ __func__, new_fs); ++ return res; ++ } ++ stat->resume_state[RES_CTRL_REG4] = ++ ((FS_MASK & new_fs) | ++ (~FS_MASK & stat->resume_state[RES_CTRL_REG4])); ++ ++ stat->sensitivity = sensitivity; ++ return res; ++} ++ ++ ++static int lsm9ds0_gyr_update_odr(struct lsm9ds0_gyr_status *stat, ++ unsigned int poll_interval_ms) ++{ ++ int err = -1; ++ int i; ++ u8 config[2]; ++ ++ for (i = ARRAY_SIZE(odr_table) - 1; i >= 0; i--) { ++ if ((odr_table[i].poll_rate_ms <= poll_interval_ms) || (i == 0)) ++ break; ++ } ++ ++ config[1] = odr_table[i].mask; ++ config[1] |= (ENABLE_ALL_AXES + PM_NORMAL); ++ ++ /* If device is currently enabled, we need to write new ++ * configuration out to it */ ++ if (atomic_read(&stat->enabled)) { ++ config[0] = CTRL_REG1; ++ err = lsm9ds0_gyr_i2c_write(stat, config, 1); ++ if (err < 0) ++ return err; ++ stat->resume_state[RES_CTRL_REG1] = config[1]; ++ stat->ktime = ktime_set(0, MS_TO_NS(poll_interval_ms)); ++ } ++ ++ return err; ++} ++ ++/* gyroscope data readout */ ++static int lsm9ds0_gyr_get_data(struct lsm9ds0_gyr_status *stat, ++ struct lsm9ds0_gyr_triple *data) ++{ ++ int err; ++ unsigned char gyro_out[6]; ++ /* y,p,r hardware data */ ++ s32 hw_d[3] = { 0 }; ++ ++ gyro_out[0] = (AXISDATA_REG); ++ ++ err = lsm9ds0_gyr_i2c_read(stat, gyro_out, 6); ++ ++ if (err < 0) ++ return err; ++ ++ hw_d[0] = (s32) ((s16)((gyro_out[1]) << 8) | gyro_out[0]); ++ hw_d[1] = (s32) ((s16)((gyro_out[3]) << 8) | gyro_out[2]); ++ hw_d[2] = (s32) ((s16)((gyro_out[5]) << 8) | gyro_out[4]); ++ ++ //hw_d[0] = hw_d[0] * stat->sensitivity; ++ //hw_d[1] = hw_d[1] * stat->sensitivity; ++ //hw_d[2] = hw_d[2] * stat->sensitivity; ++ ++ data->x = ((stat->pdata->negate_x) ? (-hw_d[stat->pdata->axis_map_x]) ++ : (hw_d[stat->pdata->axis_map_x])); ++ data->y = ((stat->pdata->negate_y) ? (-hw_d[stat->pdata->axis_map_y]) ++ : (hw_d[stat->pdata->axis_map_y])); ++ data->z = ((stat->pdata->negate_z) ? (-hw_d[stat->pdata->axis_map_z]) ++ : (hw_d[stat->pdata->axis_map_z])); ++ ++#ifdef DEBUG ++ /* dev_info(&stat->client->dev, "gyro_out: x = %d, y = %d, z = %d\n", ++ data->x, data->y, data->z); */ ++#endif ++ ++ return err; ++} ++ ++static void lsm9ds0_gyr_report_values(struct lsm9ds0_gyr_status *stat, ++ struct lsm9ds0_gyr_triple *data) ++{ ++ input_report_abs(stat->input_dev, ABS_X, data->x); ++ input_report_abs(stat->input_dev, ABS_Y, data->y); ++ input_report_abs(stat->input_dev, ABS_Z, data->z); ++ input_sync(stat->input_dev); ++} ++ ++static int lsm9ds0_gyr_hw_init(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ u8 buf[6]; ++ ++ dev_info(&stat->client->dev, "hw init\n"); ++ ++ buf[0] = (CTRL_REG1); ++ buf[1] = stat->resume_state[RES_CTRL_REG1]; ++ buf[2] = stat->resume_state[RES_CTRL_REG2]; ++ buf[3] = stat->resume_state[RES_CTRL_REG3]; ++ buf[4] = stat->resume_state[RES_CTRL_REG4]; ++ buf[5] = stat->resume_state[RES_CTRL_REG5]; ++ ++ err = lsm9ds0_gyr_i2c_write(stat, buf, 5); ++ if (err < 0) ++ return err; ++ ++ buf[0] = (FIFO_CTRL_REG); ++ buf[1] = stat->resume_state[RES_FIFO_CTRL_REG]; ++ err = lsm9ds0_gyr_i2c_write(stat, buf, 1); ++ if (err < 0) ++ return err; ++ ++ stat->hw_initialized = 1; ++ ++ return err; ++} ++ ++static void lsm9ds0_gyr_device_power_off(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ ++ dev_info(&stat->client->dev, "power off\n"); ++ ++ buf[0] = (CTRL_REG1); ++ buf[1] = (PM_OFF); ++ err = lsm9ds0_gyr_i2c_write(stat, buf, 1); ++ if (err < 0) ++ dev_err(&stat->client->dev, "soft power off failed\n"); ++ ++ if (stat->pdata->power_off) { ++ /* disable_irq_nosync(acc->irq1); */ ++ disable_irq_nosync(stat->irq2); ++ stat->pdata->power_off(); ++ stat->hw_initialized = 0; ++ } ++ ++ if (stat->hw_initialized) { ++ /*if (stat->pdata->gpio_int1 >= 0)*/ ++ /* disable_irq_nosync(stat->irq1);*/ ++ if (stat->pdata->gpio_int2 >= 0) { ++ disable_irq_nosync(stat->irq2); ++ dev_info(&stat->client->dev, ++ "power off: irq2 disabled\n"); ++ } ++ stat->hw_initialized = 0; ++ } ++} ++ ++static int lsm9ds0_gyr_device_power_on(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ ++ if (stat->pdata->power_on) { ++ err = stat->pdata->power_on(); ++ if (err < 0) ++ return err; ++ if (stat->pdata->gpio_int2 >= 0) ++ enable_irq(stat->irq2); ++ } ++ ++ ++ if (!stat->hw_initialized) { ++ err = lsm9ds0_gyr_hw_init(stat); ++ if (err < 0) { ++ lsm9ds0_gyr_device_power_off(stat); ++ return err; ++ } ++ } ++ ++ if (stat->hw_initialized) { ++ /* if (stat->pdata->gpio_int1) { ++ enable_irq(stat->irq1); ++ dev_info(&stat->client->dev, ++ "power on: irq1 enabled\n"); ++ } */ ++ dev_dbg(&stat->client->dev, "stat->pdata->gpio_int2 = %d\n", ++ stat->pdata->gpio_int2); ++ if (stat->pdata->gpio_int2 >= 0) { ++ enable_irq(stat->irq2); ++ dev_info(&stat->client->dev, ++ "power on: irq2 enabled\n"); ++ } ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_gyr_enable(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ ++ if (!atomic_cmpxchg(&stat->enabled, 0, 1)) { ++ ++ err = lsm9ds0_gyr_device_power_on(stat); ++ if (err < 0) { ++ atomic_set(&stat->enabled, 0); ++ return err; ++ } ++ ++ if (stat->polling_enabled) { ++ hrtimer_start(&(stat->hr_timer), stat->ktime, HRTIMER_MODE_REL); ++ } ++ ++ } ++ ++ return 0; ++} ++ ++static int lsm9ds0_gyr_disable(struct lsm9ds0_gyr_status *stat) ++{ ++ dev_dbg(&stat->client->dev, "%s: stat->enabled = %d\n", __func__, ++ atomic_read(&stat->enabled)); ++ ++ if (atomic_cmpxchg(&stat->enabled, 1, 0)) { ++ ++ lsm9ds0_gyr_device_power_off(stat); ++ hrtimer_cancel(&stat->hr_timer); ++ dev_dbg(&stat->client->dev, "%s: cancel_hrtimer ", __func__); ++ } ++ return 0; ++} ++ ++static ssize_t attr_polling_rate_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int val; ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ mutex_lock(&stat->lock); ++ val = stat->pdata->poll_interval; ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_polling_rate_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int err; ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long interval_ms; ++ ++ if (strict_strtoul(buf, 10, &interval_ms)) ++ return -EINVAL; ++ if (!interval_ms) ++ return -EINVAL; ++ ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_gyr_update_odr(stat, interval_ms); ++ if(err >= 0) ++ stat->pdata->poll_interval = interval_ms; ++ mutex_unlock(&stat->lock); ++ return size; ++} ++ ++static ssize_t attr_range_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ int range = 0; ++ u8 val; ++ mutex_lock(&stat->lock); ++ val = stat->pdata->fs_range; ++ ++ switch (val) { ++ case LSM9DS0_GYR_FS_250DPS: ++ range = 250; ++ break; ++ case LSM9DS0_GYR_FS_500DPS: ++ range = 500; ++ break; ++ case LSM9DS0_GYR_FS_2000DPS: ++ range = 2000; ++ break; ++ } ++ mutex_unlock(&stat->lock); ++ /* return sprintf(buf, "0x%02x\n", val); */ ++ return sprintf(buf, "%d\n", range); ++} ++ ++static ssize_t attr_range_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ u8 range; ++ int err; ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ switch (val) { ++ case 250: ++ range = LSM9DS0_GYR_FS_250DPS; ++ break; ++ case 500: ++ range = LSM9DS0_GYR_FS_500DPS; ++ break; ++ case 2000: ++ range = LSM9DS0_GYR_FS_2000DPS; ++ break; ++ default: ++ dev_err(&stat->client->dev, "invalid range request: %lu," ++ " discarded\n", val); ++ return -EINVAL; ++ } ++ mutex_lock(&stat->lock); ++ err = lsm9ds0_gyr_update_fs_range(stat, range); ++ if (err >= 0) ++ stat->pdata->fs_range = range; ++ mutex_unlock(&stat->lock); ++ dev_info(&stat->client->dev, "range set to: %lu dps\n", val); ++ return size; ++} ++ ++static ssize_t attr_enable_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ int val = atomic_read(&stat->enabled); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_enable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ if (val) ++ lsm9ds0_gyr_enable(stat); ++ else ++ lsm9ds0_gyr_disable(stat); ++ ++ return size; ++} ++ ++static ssize_t attr_polling_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int val = 0; ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ ++ mutex_lock(&stat->lock); ++ if (stat->polling_enabled) ++ val = 1; ++ mutex_unlock(&stat->lock); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t attr_polling_mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 10, &val)) ++ return -EINVAL; ++ ++ mutex_lock(&stat->lock); ++ if (val) { ++ stat->polling_enabled = true; ++ lsm9ds0_gyr_manage_int2settings(stat, stat->fifomode); ++ dev_info(dev, "polling mode enabled\n"); ++ if (atomic_read(&stat->enabled)) { ++ hrtimer_start(&(stat->hr_timer), stat->ktime, HRTIMER_MODE_REL); ++ } ++ } else { ++ if (stat->polling_enabled) { ++ hrtimer_cancel(&stat->hr_timer); ++ } ++ stat->polling_enabled = false; ++ lsm9ds0_gyr_manage_int2settings(stat, stat->fifomode); ++ dev_info(dev, "polling mode disabled\n"); ++ } ++ mutex_unlock(&stat->lock); ++ return size; ++} ++ ++static ssize_t attr_watermark_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long watermark; ++ int res; ++ ++ if (strict_strtoul(buf, 16, &watermark)) ++ return -EINVAL; ++ ++ res = lsm9ds0_gyr_update_watermark(stat, watermark); ++ if (res < 0) ++ return res; ++ ++ return size; ++} ++ ++static ssize_t attr_watermark_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ int val = stat->watermark; ++ return sprintf(buf, "0x%02x\n", val); ++} ++ ++static ssize_t attr_fifomode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long fifomode; ++ int res; ++ ++ if (strict_strtoul(buf, 16, &fifomode)) ++ return -EINVAL; ++ /* if (!fifomode) ++ return -EINVAL; */ ++ ++ dev_dbg(dev, "%s, got value:0x%02x\n", __func__, (u8)fifomode); ++ ++ mutex_lock(&stat->lock); ++ res = lsm9ds0_gyr_manage_int2settings(stat, (u8) fifomode); ++ mutex_unlock(&stat->lock); ++ ++ if (res < 0) ++ return res; ++ return size; ++} ++ ++static ssize_t attr_fifomode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ u8 val = stat->fifomode; ++ return sprintf(buf, "0x%02x\n", val); ++} ++ ++#ifdef DEBUG ++static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int rc; ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ u8 x[2]; ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 16, &val)) ++ return -EINVAL; ++ mutex_lock(&stat->lock); ++ x[0] = stat->reg_addr; ++ mutex_unlock(&stat->lock); ++ x[1] = val; ++ rc = lsm9ds0_gyr_i2c_write(stat, x, 1); ++ return size; ++} ++ ++static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ ssize_t ret; ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ int rc; ++ u8 data; ++ ++ mutex_lock(&stat->lock); ++ data = stat->reg_addr; ++ mutex_unlock(&stat->lock); ++ rc = lsm9ds0_gyr_i2c_read(stat, &data, 1); ++ ret = sprintf(buf, "0x%02x\n", data); ++ return ret; ++} ++ ++static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev); ++ unsigned long val; ++ ++ if (strict_strtoul(buf, 16, &val)) ++ return -EINVAL; ++ ++ mutex_lock(&stat->lock); ++ ++ stat->reg_addr = val; ++ ++ mutex_unlock(&stat->lock); ++ ++ return size; ++} ++#endif /* DEBUG */ ++ ++static struct device_attribute attributes[] = { ++ __ATTR(pollrate_ms, 0666, attr_polling_rate_show, ++ attr_polling_rate_store), ++ __ATTR(range, 0666, attr_range_show, attr_range_store), ++ __ATTR(enable_device, 0666, attr_enable_show, attr_enable_store), ++ __ATTR(enable_polling, 0666, attr_polling_mode_show, ++ attr_polling_mode_store), ++ __ATTR(fifo_samples, 0666, attr_watermark_show, attr_watermark_store), ++ __ATTR(fifo_mode, 0666, attr_fifomode_show, attr_fifomode_store), ++#ifdef DEBUG ++ __ATTR(reg_value, 0600, attr_reg_get, attr_reg_set), ++ __ATTR(reg_addr, 0200, NULL, attr_addr_set), ++#endif ++}; ++ ++static int create_sysfs_interfaces(struct device *dev) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(attributes); i++) ++ if (device_create_file(dev, attributes + i)) ++ goto error; ++ return 0; ++ ++error: ++ for (; i >= 0; i--) ++ device_remove_file(dev, attributes + i); ++ dev_err(dev, "%s:Unable to create interface\n", __func__); ++ return -1; ++} ++ ++static int remove_sysfs_interfaces(struct device *dev) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(attributes); i++) ++ device_remove_file(dev, attributes + i); ++ return 0; ++} ++ ++static void lsm9ds0_gyr_report_triple(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ struct lsm9ds0_gyr_triple data_out; ++ ++ err = lsm9ds0_gyr_get_data(stat, &data_out); ++ if (err < 0) ++ dev_err(&stat->client->dev, "get_gyroscope_data failed\n"); ++ else ++ lsm9ds0_gyr_report_values(stat, &data_out); ++} ++ ++ ++static void lsm9ds0_gyr_irq2_fifo(struct lsm9ds0_gyr_status *stat) ++{ ++ int err; ++ u8 buf[2]; ++ u8 int_source; ++ u8 samples; ++ u8 workingmode; ++ u8 stored_samples; ++ ++ mutex_lock(&stat->lock); ++ ++ workingmode = stat->fifomode; ++ ++ ++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__, ++ workingmode); ++ ++ ++ switch (workingmode) { ++ case FIFO_MODE_BYPASS: ++ { ++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__, ++ stat->fifomode); ++ lsm9ds0_gyr_report_triple(stat); ++ break; ++ } ++ case FIFO_MODE_FIFO: ++ samples = (stat->watermark)+1; ++ dev_dbg(&stat->client->dev, ++ "%s : FIFO_SRC_REG init samples:%d\n", ++ __func__, samples); ++ err = lsm9ds0_gyr_register_read(stat, buf, FIFO_SRC_REG); ++ if (err < 0) ++ dev_err(&stat->client->dev, ++ "error reading fifo source reg\n"); ++ ++ int_source = buf[0]; ++ dev_dbg(&stat->client->dev, "%s :FIFO_SRC_REG content:0x%02x\n", ++ __func__, int_source); ++ ++ stored_samples = int_source & FIFO_STORED_DATA_MASK; ++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__, ++ stat->fifomode); ++ ++ dev_dbg(&stat->client->dev, "%s : samples:%d stored:%d\n", ++ __func__, samples, stored_samples); ++ ++ for (; samples > 0; samples--) { ++#ifdef DEBUG ++ input_report_abs(stat->input_dev, ABS_MISC, 1); ++ input_sync(stat->input_dev); ++#endif ++ dev_dbg(&stat->client->dev, "%s : current sample:%d\n", ++ __func__, samples); ++ ++ lsm9ds0_gyr_report_triple(stat); ++ ++#ifdef DEBUG ++ input_report_abs(stat->input_dev, ABS_MISC, 0); ++ input_sync(stat->input_dev); ++#endif ++ } ++ lsm9ds0_gyr_fifo_reset(stat); ++ break; ++ } ++#ifdef DEBUG ++ input_report_abs(stat->input_dev, ABS_MISC, 3); ++ input_sync(stat->input_dev); ++#endif ++ ++ mutex_unlock(&stat->lock); ++} ++ ++static irqreturn_t lsm9ds0_gyr_isr2(int irq, void *dev) ++{ ++ struct lsm9ds0_gyr_status *stat = dev; ++ ++ disable_irq_nosync(irq); ++#ifdef DEBUG ++ input_report_abs(stat->input_dev, ABS_MISC, 2); ++ input_sync(stat->input_dev->input); ++#endif ++ queue_work(stat->irq2_work_queue, &stat->irq2_work); ++ pr_debug("%s %s: isr2 queued\n", LSM9DS0_GYR_DEV_NAME, __func__); ++ ++ return IRQ_HANDLED; ++} ++ ++static void lsm9ds0_gyr_irq2_work_func(struct work_struct *work) ++{ ++ ++ struct lsm9ds0_gyr_status *stat = ++ container_of(work, struct lsm9ds0_gyr_status, irq2_work); ++ /* TODO add interrupt service procedure. ++ ie:lsm9ds0_gyr_irq2_XXX(stat); */ ++ lsm9ds0_gyr_irq2_fifo(stat); ++ /* */ ++ pr_debug("%s %s: IRQ2 served\n", LSM9DS0_GYR_DEV_NAME, __func__); ++/* exit: */ ++ enable_irq(stat->irq2); ++} ++ ++int lsm9ds0_gyr_input_open(struct input_dev *input) ++{ ++ struct lsm9ds0_gyr_status *stat = input_get_drvdata(input); ++ dev_dbg(&stat->client->dev, "%s\n", __func__); ++ return lsm9ds0_gyr_enable(stat); ++} ++ ++void lsm9ds0_gyr_input_close(struct input_dev *dev) ++{ ++ struct lsm9ds0_gyr_status *stat = input_get_drvdata(dev); ++ dev_dbg(&stat->client->dev, "%s\n", __func__); ++ lsm9ds0_gyr_disable(stat); ++} ++ ++static int lsm9ds0_gyr_validate_pdata(struct lsm9ds0_gyr_status *stat) ++{ ++ /* checks for correctness of minimal polling period */ ++ stat->pdata->min_interval = ++ max((unsigned int) LSM9DS0_GYR_MIN_POLL_PERIOD_MS, ++ stat->pdata->min_interval); ++ ++ stat->pdata->poll_interval = max(stat->pdata->poll_interval, ++ stat->pdata->min_interval); ++ ++ if (stat->pdata->axis_map_x > 2 || ++ stat->pdata->axis_map_y > 2 || ++ stat->pdata->axis_map_z > 2) { ++ dev_err(&stat->client->dev, ++ "invalid axis_map value x:%u y:%u z%u\n", ++ stat->pdata->axis_map_x, ++ stat->pdata->axis_map_y, ++ stat->pdata->axis_map_z); ++ return -EINVAL; ++ } ++ ++ /* Only allow 0 and 1 for negation boolean flag */ ++ if (stat->pdata->negate_x > 1 || ++ stat->pdata->negate_y > 1 || ++ stat->pdata->negate_z > 1) { ++ dev_err(&stat->client->dev, ++ "invalid negate value x:%u y:%u z:%u\n", ++ stat->pdata->negate_x, ++ stat->pdata->negate_y, ++ stat->pdata->negate_z); ++ return -EINVAL; ++ } ++ ++ /* Enforce minimum polling interval */ ++ if (stat->pdata->poll_interval < stat->pdata->min_interval) { ++ dev_err(&stat->client->dev, ++ "minimum poll interval violated\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int lsm9ds0_gyr_input_init(struct lsm9ds0_gyr_status *stat) ++{ ++ int err = -1; ++ ++ dev_dbg(&stat->client->dev, "%s\n", __func__); ++ ++ stat->input_dev = input_allocate_device(); ++ if (!stat->input_dev) { ++ err = -ENOMEM; ++ dev_err(&stat->client->dev, ++ "input device allocation failed\n"); ++ goto err0; ++ } ++ ++ stat->input_dev->open = lsm9ds0_gyr_input_open; ++ stat->input_dev->close = lsm9ds0_gyr_input_close; ++ stat->input_dev->name = LSM9DS0_GYR_DEV_NAME; ++ ++ stat->input_dev->id.bustype = BUS_I2C; ++ stat->input_dev->dev.parent = &stat->client->dev; ++ ++ input_set_drvdata(stat->input_dev, stat); ++ ++ set_bit(EV_ABS, stat->input_dev->evbit); ++ ++#ifdef DEBUG ++ set_bit(EV_KEY, stat->input_dev->keybit); ++ set_bit(KEY_LEFT, stat->input_dev->keybit); ++ input_set_abs_params(stat->input_dev, ABS_MISC, 0, 1, 0, 0); ++#endif ++ ++ input_set_abs_params(stat->input_dev, ABS_X, -FS_MAX-1, FS_MAX, 0, 0); ++ input_set_abs_params(stat->input_dev, ABS_Y, -FS_MAX-1, FS_MAX, 0, 0); ++ input_set_abs_params(stat->input_dev, ABS_Z, -FS_MAX-1, FS_MAX, 0, 0); ++ ++ ++ err = input_register_device(stat->input_dev); ++ if (err) { ++ dev_err(&stat->client->dev, ++ "unable to register input polled device %s\n", ++ stat->input_dev->name); ++ goto err1; ++ } ++ ++ return 0; ++ ++err1: ++ input_free_device(stat->input_dev); ++err0: ++ return err; ++} ++ ++static void lsm9ds0_gyr_input_cleanup(struct lsm9ds0_gyr_status *stat) ++{ ++ input_unregister_device(stat->input_dev); ++ input_free_device(stat->input_dev); ++} ++ ++static void poll_function_work(struct work_struct *polling_task) ++{ ++ struct lsm9ds0_gyr_status *stat; ++ struct lsm9ds0_gyr_triple data_out; ++ int err; ++ ++ stat = container_of((struct work_struct *)polling_task, ++ struct lsm9ds0_gyr_status, polling_task); ++ ++ err = lsm9ds0_gyr_get_data(stat, &data_out); ++ if (err < 0) ++ dev_err(&stat->client->dev, "get_rotation_data failed.\n"); ++ else ++ lsm9ds0_gyr_report_values(stat, &data_out); ++ ++ hrtimer_start(&stat->hr_timer, stat->ktime, HRTIMER_MODE_REL); ++} ++ ++enum hrtimer_restart poll_function_read(struct hrtimer *timer) ++{ ++ struct lsm9ds0_gyr_status *stat; ++ ++ stat = container_of((struct hrtimer *)timer, ++ struct lsm9ds0_gyr_status, hr_timer); ++ ++ queue_work(lsm9ds0_gyr_workqueue, &stat->polling_task); ++ return HRTIMER_NORESTART; ++} ++ ++static int lsm9ds0_gyr_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct lsm9ds0_gyr_status *stat; ++ ++ u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK ; ++ ++ int err = -1; ++ ++ dev_info(&client->dev, "probe start.\n"); ++ ++ ++ stat = kzalloc(sizeof(*stat), GFP_KERNEL); ++ if (stat == NULL) { ++ dev_err(&client->dev, ++ "failed to allocate memory for module data\n"); ++ err = -ENOMEM; ++ goto err0; ++ } ++ ++ ++ /* 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)) { ++ stat->use_smbus = 1; ++ dev_warn(&client->dev, "client using SMBUS\n"); ++ } else { ++ err = -ENODEV; ++ dev_err(&client->dev, "client nor SMBUS capable\n"); ++ stat->use_smbus = 0; ++ goto err0; ++ } ++ } ++ ++ if(lsm9ds0_gyr_workqueue == 0) ++ lsm9ds0_gyr_workqueue = create_workqueue("lsm9ds0_gyr_workqueue"); ++ ++ hrtimer_init(&stat->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ stat->hr_timer.function = &poll_function_read; ++ ++ mutex_init(&stat->lock); ++ mutex_lock(&stat->lock); ++ stat->client = client; ++ ++ stat->pdata = kmalloc(sizeof(*stat->pdata), GFP_KERNEL); ++ if (stat->pdata == NULL) { ++ dev_err(&client->dev, ++ "failed to allocate memory for pdata: %d\n", err); ++ goto err1; ++ } ++ ++ if (client->dev.platform_data == NULL) { ++ default_lsm9ds0_gyr_pdata.gpio_int1 = int1_gpio; ++ default_lsm9ds0_gyr_pdata.gpio_int2 = int2_gpio; ++ memcpy(stat->pdata, &default_lsm9ds0_gyr_pdata, ++ sizeof(*stat->pdata)); ++ dev_info(&client->dev, "using default plaform_data\n"); ++ } else { ++ memcpy(stat->pdata, client->dev.platform_data, ++ sizeof(*stat->pdata)); ++ } ++ ++ err = lsm9ds0_gyr_validate_pdata(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "failed to validate platform data\n"); ++ goto err1_1; ++ } ++ ++ i2c_set_clientdata(client, stat); ++ ++ if (stat->pdata->init) { ++ err = stat->pdata->init(); ++ if (err < 0) { ++ dev_err(&client->dev, "init failed: %d\n", err); ++ goto err1_1; ++ } ++ } ++ ++ ++ memset(stat->resume_state, 0, ARRAY_SIZE(stat->resume_state)); ++ ++ stat->resume_state[RES_CTRL_REG1] = ALL_ZEROES | ENABLE_ALL_AXES ++ | PM_NORMAL; ++ stat->resume_state[RES_CTRL_REG2] = ALL_ZEROES; ++ stat->resume_state[RES_CTRL_REG3] = ALL_ZEROES; ++ stat->resume_state[RES_CTRL_REG4] = ALL_ZEROES | BDU_ENABLE; ++ stat->resume_state[RES_CTRL_REG5] = ALL_ZEROES; ++ stat->resume_state[RES_FIFO_CTRL_REG] = ALL_ZEROES; ++ ++ stat->polling_enabled = true; ++ dev_info(&client->dev, "polling mode enabled\n"); ++ ++ err = lsm9ds0_gyr_device_power_on(stat); ++ if (err < 0) { ++ dev_err(&client->dev, "power on failed: %d\n", err); ++ goto err2; ++ } ++ ++ atomic_set(&stat->enabled, 1); ++ ++ err = lsm9ds0_gyr_update_fs_range(stat, stat->pdata->fs_range); ++ if (err < 0) { ++ dev_err(&client->dev, "update_fs_range failed\n"); ++ goto err2; ++ } ++ ++ err = lsm9ds0_gyr_update_odr(stat, stat->pdata->poll_interval); ++ if (err < 0) { ++ dev_err(&client->dev, "update_odr failed\n"); ++ goto err2; ++ } ++ ++ err = lsm9ds0_gyr_input_init(stat); ++ if (err < 0) ++ goto err3; ++ ++ err = create_sysfs_interfaces(&client->dev); ++ if (err < 0) { ++ dev_err(&client->dev, ++ "%s device register failed\n", LSM9DS0_GYR_DEV_NAME); ++ goto err4; ++ } ++ ++ lsm9ds0_gyr_device_power_off(stat); ++ ++ /* As default, do not report information */ ++ atomic_set(&stat->enabled, 0); ++ ++ ++ if (stat->pdata->gpio_int2 >= 0) { ++ stat->irq2 = gpio_to_irq(stat->pdata->gpio_int2); ++ dev_info(&client->dev, "%s: %s has set irq2 to irq:" ++ " %d mapped on gpio:%d\n", ++ LSM9DS0_GYR_DEV_NAME, __func__, stat->irq2, ++ stat->pdata->gpio_int2); ++ ++ INIT_WORK(&stat->irq2_work, lsm9ds0_gyr_irq2_work_func); ++ stat->irq2_work_queue = ++ create_singlethread_workqueue("lsm9ds0_gyr_irq2_wq"); ++ if (!stat->irq2_work_queue) { ++ err = -ENOMEM; ++ dev_err(&client->dev, "cannot create " ++ "work queue2: %d\n", err); ++ goto err5; ++ } ++ ++ err = request_irq(stat->irq2, lsm9ds0_gyr_isr2, ++ IRQF_TRIGGER_HIGH, "lsm9ds0_gyr_irq2", stat); ++ ++ if (err < 0) { ++ dev_err(&client->dev, "request irq2 failed: %d\n", err); ++ goto err6; ++ } ++ disable_irq_nosync(stat->irq2); ++ } ++ ++ mutex_unlock(&stat->lock); ++ ++ INIT_WORK(&stat->polling_task, poll_function_work); ++ dev_info(&client->dev, "%s probed: device created successfully\n", ++ LSM9DS0_GYR_DEV_NAME); ++ ++ ++ return 0; ++ ++/*err7: ++ free_irq(stat->irq2, stat); ++*/ ++err6: ++ destroy_workqueue(stat->irq2_work_queue); ++err5: ++ lsm9ds0_gyr_device_power_off(stat); ++ remove_sysfs_interfaces(&client->dev); ++err4: ++ lsm9ds0_gyr_input_cleanup(stat); ++err3: ++ lsm9ds0_gyr_device_power_off(stat); ++err2: ++ if (stat->pdata->exit) ++ stat->pdata->exit(); ++err1_1: ++ mutex_unlock(&stat->lock); ++ kfree(stat->pdata); ++err1: ++ destroy_workqueue(lsm9ds0_gyr_workqueue); ++ kfree(stat); ++err0: ++ pr_err("%s: Driver Initialization failed\n", ++ LSM9DS0_GYR_DEV_NAME); ++ return err; ++} ++ ++static int lsm9ds0_gyr_remove(struct i2c_client *client) ++{ ++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client); ++ ++ dev_info(&stat->client->dev, "driver removing\n"); ++ ++ cancel_work_sync(&stat->polling_task); ++ if(!lsm9ds0_gyr_workqueue) { ++ flush_workqueue(lsm9ds0_gyr_workqueue); ++ destroy_workqueue(lsm9ds0_gyr_workqueue); ++ } ++ /* ++ if (stat->pdata->gpio_int1 >= 0) ++ { ++ free_irq(stat->irq1, stat); ++ gpio_free(stat->pdata->gpio_int1); ++ destroy_workqueue(stat->irq1_work_queue); ++ } ++ */ ++ if (stat->pdata->gpio_int2 >= 0) { ++ free_irq(stat->irq2, stat); ++ gpio_free(stat->pdata->gpio_int2); ++ destroy_workqueue(stat->irq2_work_queue); ++ } ++ ++ lsm9ds0_gyr_disable(stat); ++ lsm9ds0_gyr_input_cleanup(stat); ++ ++ remove_sysfs_interfaces(&client->dev); ++ ++ kfree(stat->pdata); ++ kfree(stat); ++ return 0; ++} ++ ++static int lsm9ds0_gyr_suspend(struct device *dev) ++{ ++ int err = 0; ++#define SLEEP ++#ifdef CONFIG_PM ++ struct i2c_client *client = to_i2c_client(dev); ++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client); ++ u8 buf[2]; ++ ++ dev_info(&client->dev, "suspend\n"); ++ ++ dev_dbg(&client->dev, "%s\n", __func__); ++ if (atomic_read(&stat->enabled)) { ++ mutex_lock(&stat->lock); ++ if (stat->polling_enabled) { ++ dev_info(&stat->client->dev, "polling mode disabled\n"); ++ hrtimer_cancel(&stat->hr_timer); ++ } ++#ifdef SLEEP ++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1, ++ 0x0F, (ENABLE_NO_AXES | PM_NORMAL)); ++#else ++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1, ++ 0x08, PM_OFF); ++#endif /*SLEEP*/ ++ mutex_unlock(&stat->lock); ++ } ++#endif /*CONFIG_PM*/ ++ return err; ++} ++ ++static int lsm9ds0_gyr_resume(struct device *dev) ++{ ++ int err = 0; ++#ifdef CONFIG_PM ++ struct i2c_client *client = to_i2c_client(dev); ++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client); ++ u8 buf[2]; ++ ++ ++ dev_info(&client->dev, "resume\n"); ++ ++ dev_dbg(&client->dev, "%s\n", __func__); ++ if (atomic_read(&stat->enabled)) { ++ mutex_lock(&stat->lock); ++ if (stat->polling_enabled) { ++ dev_info(&stat->client->dev, "polling mode enabled\n"); ++ hrtimer_init(&stat->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ } ++#ifdef SLEEP ++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1, ++ 0x0F, (ENABLE_ALL_AXES | PM_NORMAL)); ++#else ++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1, ++ 0x08, PM_NORMAL); ++#endif ++ mutex_unlock(&stat->lock); ++ ++ } ++#endif /*CONFIG_PM*/ ++ return err; ++} ++ ++ ++static const struct i2c_device_id lsm9ds0_gyr_id[] = { ++ { LSM9DS0_GYR_DEV_NAME , 0 }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, lsm9ds0_gyr_id); ++ ++static const struct of_device_id lsm9ds0_gyr_of_match[] = { ++ { .compatible = "st,"LSM9DS0_GYR_DEV_NAME, }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, lsm9ds0_gyr_of_match); ++ ++static const struct dev_pm_ops lsm9ds0_gyr_pm = { ++ .suspend = lsm9ds0_gyr_suspend, ++ .resume = lsm9ds0_gyr_resume, ++}; ++ ++static struct i2c_driver lsm9ds0_i2c_gyr_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = LSM9DS0_GYR_DEV_NAME, ++ .pm = &lsm9ds0_gyr_pm, ++ .of_match_table = lsm9ds0_gyr_of_match, ++ }, ++ .probe = lsm9ds0_gyr_probe, ++ .remove = lsm9ds0_gyr_remove, ++ .id_table = lsm9ds0_gyr_id, ++ ++}; ++ ++module_i2c_driver(lsm9ds0_i2c_gyr_driver); ++ ++MODULE_DESCRIPTION("lsm9ds0 gyroscope driver"); ++MODULE_AUTHOR("Matteo Dameno, Denis Ciocca, STMicroelectronics"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/input/lsm9ds0.h b/include/linux/input/lsm9ds0.h +new file mode 100644 +index 0000000..c115eb7 +--- /dev/null ++++ b/include/linux/input/lsm9ds0.h +@@ -0,0 +1,201 @@ ++/******************** (C) COPYRIGHT 2012 STMicroelectronics ******************** ++* ++* File Name : lsm9ds0.h ++* Authors : MSH - C&I BU - Application Team ++* : Matteo Dameno (matteo.dameno@st.com) ++* : Denis Ciocca (denis.ciocca@st.com) ++* Version : V.1.0.2 ++* Date : 2013/Oct/23 ++* ++******************************************************************************** ++* ++* 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. ++* ++******************************************************************************** ++******************************************************************************** ++* REVISON HISTORY ++* 1.0.1 | 2012/Aug/30 | Denis Ciocca | corrects gyr_get_data func ++* 1.0.2 | 2013/Oct/23 | Matteo Dameno | introduced acc_mag 1.0.5 ++*******************************************************************************/ ++ ++#ifndef __LSM9DS0_H__ ++#define __LSM9DS0_H__ ++ ++#define LSM9DS0_DEV_NAME "lsm9ds0_acc_mag" /* i2c system name */ ++#define LSM9DS0_ACC_DEV_NAME "lsm9ds0_acc" /* Input file name */ ++#define LSM9DS0_MAG_DEV_NAME "lsm9ds0_mag" /* Input file name */ ++#define LSM9DS0_GYR_DEV_NAME "lsm9ds0_gyr" /* Input file name */ ++ ++#define LSM9DS0_SAD0L_ACC_MAG (0x02) ++#define LSM9DS0_SAD0H_ACC_MAG (0x01) ++#define LSM9DS0_SAD0L_GYR (0x00) ++#define LSM9DS0_SAD0H_GYR (0x01) ++ ++/************************************************/ ++/* Output data */ ++/************************************************* ++accelerometer: ug ++magnetometer: ugauss ++gyroscope: udps ++*************************************************/ ++ ++/************************************************/ ++/* sysfs data */ ++/************************************************* ++accelerometer: ++ - pollrate->ms ++ - fullscale->g ++magnetometer: ++ - pollrate->ms ++ - fullscale->gauss ++gyroscope: ++ - pollrate->ms ++ - fullscale->dps ++*************************************************/ ++ ++#define LSM9DS0_ACC_MAG_I2C_SADROOT (0x07) ++ ++/* I2C address if gyr SA0 pin to GND */ ++#define LSM9DS0_ACC_MAG_I2C_SAD_L ((LSM9DS0_ACC_MAG_I2C_SADROOT<<2)| \ ++ LSM9DS0_SAD0L_ACC_MAG) ++/* I2C address if gyr SA0 pin to Vdd */ ++#define LSM9DS0_ACC_MAG_I2C_SAD_H ((LSM9DS0_ACC_MAG_I2C_SADROOT<<2)| \ ++ LSM9DS0_SAD0H_ACC_MAG) ++ ++/************************************************/ ++/* Accelerometer section defines */ ++/************************************************/ ++ ++/* Accelerometer Sensor Full Scale */ ++#define LSM9DS0_ACC_FS_MASK (0x18) ++#define LSM9DS0_ACC_FS_2G (0x00) /* Full scale 2g */ ++#define LSM9DS0_ACC_FS_4G (0x08) /* Full scale 4g */ ++#define LSM9DS0_ACC_FS_8G (0x10) /* Full scale 8g */ ++#define LSM9DS0_ACC_FS_16G (0x18) /* Full scale 16g */ ++ ++/* Accelerometer Anti-Aliasing Filter */ ++#define ANTI_ALIASING_773 (0X00) ++#define ANTI_ALIASING_362 (0X40) ++#define ANTI_ALIASING_194 (0X80) ++#define ANTI_ALIASING_50 (0XC0) ++ ++/************************************************/ ++/* Magnetometer section defines */ ++/************************************************/ ++ ++/* Magnetometer Sensor Full Scale */ ++#define LSM9DS0_MAG_FS_MASK (0x60) ++#define LSM9DS0_MAG_FS_2G (0x00) /* Full scale 2 gauss */ ++#define LSM9DS0_MAG_FS_4G (0x20) /* Full scale 4 gauss */ ++#define LSM9DS0_MAG_FS_8G (0x40) /* Full scale 8 gauss */ ++#define LSM9DS0_MAG_FS_12G (0x60) /* Full scale 12 gauss */ ++ ++/************************************************/ ++/* Gyroscope section defines */ ++/************************************************/ ++ ++#define LSM9DS0_GYR_I2C_SADROOT (0x35) ++ ++/* I2C address if gyr SA0 pin to GND */ ++#define LSM9DS0_GYR_I2C_SAD_L ((LSM9DS0_GYR_I2C_SADROOT<<1)| \ ++ LSM9DS0_SAD0L_GYR) ++/* I2C address if gyr SA0 pin to Vdd */ ++#define LSM9DS0_GYR_I2C_SAD_H ((LSM9DS0_GYR_I2C_SADROOT<<1)| \ ++ LSM9DS0_SAD0H_GYR) ++ ++#ifdef __KERNEL__ ++ ++/* to set gpios numb connected to gyro interrupt pins, ++ * the unused ones havew to be set to -EINVAL ++ */ ++#define DEFAULT_INT1_GPIO (-EINVAL) ++#define DEFAULT_INT2_GPIO (-EINVAL) ++ ++#define LSM9DS0_ACC_MIN_POLL_PERIOD_MS 1 ++#define LSM9DS0_MAG_MIN_POLL_PERIOD_MS 5 ++ ++#define LSM9DS0_GYR_DEFAULT_INT1_GPIO (-EINVAL) ++#define LSM9DS0_GYR_DEFAULT_INT2_GPIO (-EINVAL) ++ ++#define LSM9DS0_GYR_MIN_POLL_PERIOD_MS 2 ++ ++#define LSM9DS0_GYR_FS_250DPS (0x00) ++#define LSM9DS0_GYR_FS_500DPS (0x10) ++#define LSM9DS0_GYR_FS_2000DPS (0x30) ++ ++struct lsm9ds0_acc_platform_data { ++ ++ unsigned int poll_interval; ++ unsigned int min_interval; ++ ++ u8 fs_range; ++ ++ short rot_matrix[3][3]; ++ ++ u8 aa_filter_bandwidth; ++ ++ int (*init)(void); ++ void (*exit)(void); ++ int (*power_on)(void); ++ int (*power_off)(void); ++ ++ int gpio_int1; ++ int gpio_int2; ++}; ++ ++struct lsm9ds0_mag_platform_data { ++ ++ unsigned int poll_interval; ++ unsigned int min_interval; ++ ++ u8 fs_range; ++ ++ short rot_matrix[3][3]; ++ ++ int (*init)(void); ++ void (*exit)(void); ++ int (*power_on)(void); ++ int (*power_off)(void); ++}; ++ ++struct lsm9ds0_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; ++ ++ /* gpio ports for interrupt pads */ ++ int gpio_int1; ++ int gpio_int2; /* int for fifo */ ++ ++ u8 axis_map_x; ++ u8 axis_map_y; ++ u8 axis_map_z; ++ ++ u8 negate_x; ++ u8 negate_y; ++ u8 negate_z; ++}; ++ ++struct lsm9ds0_main_platform_data { ++ ++ struct lsm9ds0_acc_platform_data *pdata_acc; ++ struct lsm9ds0_mag_platform_data *pdata_mag; ++}; ++ ++#endif /* __KERNEL__ */ ++#endif /* __LSM9DS0_H__ */ +-- +1.7.10.4 |