3701 lines
98 KiB
C
3701 lines
98 KiB
C
/******************** (C) COPYRIGHT 2013 STMicroelectronics *******************
|
|
*
|
|
* File Name : lsm303d.h
|
|
* Authors : AMS - MSH 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 : LSM303D 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
|
|
******************************************************************************/
|
|
//#define DEBUG //zfming
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h> //zfming
|
|
#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/earlysuspend.h>
|
|
#include <linux/tablet_config.h>
|
|
#include "lsm303d.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 62 /** 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 LSM303D_ACC_ODR_OFF (0x00) /* Power down */
|
|
#define LSM303D_ACC_ODR3_125 (0x10) /* 3.25Hz output data rate */
|
|
#define LSM303D_ACC_ODR6_25 (0x20) /* 6.25Hz output data rate */
|
|
#define LSM303D_ACC_ODR12_5 (0x30) /* 12.5Hz output data rate */
|
|
#define LSM303D_ACC_ODR25 (0x40) /* 25Hz output data rate */
|
|
#define LSM303D_ACC_ODR50 (0x50) /* 50Hz output data rate */
|
|
#define LSM303D_ACC_ODR100 (0x60) /* 100Hz output data rate */
|
|
#define LSM303D_ACC_ODR200 (0x70) /* 200Hz output data rate */
|
|
#define LSM303D_ACC_ODR400 (0x80) /* 400Hz output data rate */
|
|
#define LSM303D_ACC_ODR800 (0x90) /* 800Hz output data rate */
|
|
#define LSM303D_ACC_ODR1600 (0xA0) /* 1600Hz output data rate */
|
|
|
|
#define ODR_MAG_MASK (0X1C) /* Mask for odr change on mag */
|
|
#define LSM303D_MAG_ODR3_125 (0x00) /* 3.25Hz output data rate */
|
|
#define LSM303D_MAG_ODR6_25 (0x04) /* 6.25Hz output data rate */
|
|
#define LSM303D_MAG_ODR12_5 (0x08) /* 12.5Hz output data rate */
|
|
#define LSM303D_MAG_ODR25 (0x0C) /* 25Hz output data rate */
|
|
#define LSM303D_MAG_ODR50 (0x10) /* 50Hz output data rate */
|
|
#define LSM303D_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 LSM303D_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;
|
|
|
|
/*lenovo-sw molg1 add for lsm303d suspend/resume contrl 20140513*/
|
|
#define LSM303D_CONFIG_SUSPEND 1
|
|
#define LSM303D_GS_ENABLE 1
|
|
|
|
struct workqueue_struct *lsm303d_workqueue = 0;
|
|
|
|
struct {
|
|
unsigned int cutoff_us;
|
|
u8 value;
|
|
} lsm303d_acc_odr_table[] = {
|
|
{ 1, LSM303D_ACC_ODR800 },
|
|
{ 2, LSM303D_ACC_ODR400 },
|
|
{ 5, LSM303D_ACC_ODR200 },
|
|
{ 10, LSM303D_ACC_ODR100 },
|
|
{ 20, LSM303D_ACC_ODR50 },
|
|
{ 40, LSM303D_ACC_ODR25 },
|
|
{ 80, LSM303D_ACC_ODR12_5 },
|
|
{ 160, LSM303D_ACC_ODR6_25 },
|
|
{ 320, LSM303D_ACC_ODR3_125},
|
|
};
|
|
|
|
struct {
|
|
unsigned int cutoff_us;
|
|
u8 value;
|
|
} lsm303d_mag_odr_table[] = {
|
|
{ 10, LSM303D_MAG_ODR100 },
|
|
{ 20, LSM303D_MAG_ODR50 },
|
|
{ 40, LSM303D_MAG_ODR25 },
|
|
{ 80, LSM303D_MAG_ODR12_5 },
|
|
{ 160, LSM303D_MAG_ODR6_25 },
|
|
{ 320, LSM303D_MAG_ODR3_125},
|
|
};
|
|
|
|
struct interrupt_enable {
|
|
atomic_t enable;
|
|
u8 address;
|
|
u8 mask;
|
|
};
|
|
|
|
struct interrupt_value {
|
|
int value;
|
|
u8 address;
|
|
};
|
|
|
|
struct lsm303d_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 lsm303d_status {
|
|
struct i2c_client *client;
|
|
struct lsm303d_acc_platform_data *pdata_acc;
|
|
struct lsm303d_mag_platform_data *pdata_mag;
|
|
|
|
struct mutex lock;
|
|
struct work_struct input_work_acc;
|
|
struct work_struct input_work_mag;
|
|
|
|
struct early_suspend early_suspend;
|
|
|
|
int acc_need_resume;
|
|
int mag_need_resume;
|
|
|
|
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 lsm303d_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 lsm303d_acc_platform_data default_lsm303d_acc_pdata = {
|
|
.fs_range = LSM303D_ACC_FS_2G,
|
|
.rot_matrix = {
|
|
{1, 0, 0},
|
|
{0, 1, 0},
|
|
{0, 0, 1},
|
|
},
|
|
.cali_sw = {0,0,0},
|
|
.poll_interval = 100,
|
|
.min_interval = LSM303D_ACC_MIN_POLL_PERIOD_MS,
|
|
.aa_filter_bandwidth = ANTI_ALIASING_773,
|
|
.gpio_int1 = DEFAULT_INT1_GPIO,
|
|
.gpio_int2 = DEFAULT_INT2_GPIO,
|
|
};
|
|
|
|
static const struct lsm303d_mag_platform_data default_lsm303d_mag_pdata = {
|
|
.poll_interval = 100,
|
|
.min_interval = LSM303D_MAG_MIN_POLL_PERIOD_MS,
|
|
.fs_range = LSM303D_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,
|
|
};
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static void lsm303d_earlysuspend(struct early_suspend *handler);
|
|
static void lsm303d_lateresume(struct early_suspend *handler);
|
|
#endif
|
|
|
|
static int lsm303d_i2c_read(struct lsm303d_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 lsm303d_i2c_write(struct lsm303d_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 lsm303d_hw_init(struct lsm303d_status *stat)
|
|
{
|
|
int err = -1;
|
|
u8 buf[1];
|
|
int i;
|
|
|
|
pr_info("%s: hw init start\n", LSM303D_DEV_NAME);
|
|
|
|
buf[0] = status_registers.who_am_i.address;
|
|
err = lsm303d_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", LSM303D_DEV_NAME);
|
|
|
|
return 0;
|
|
|
|
error_interrupt:
|
|
err_unknown_device:
|
|
err_firstread:
|
|
stat->hw_working = 0;
|
|
stat->hw_initialized = 0;
|
|
return err;
|
|
}
|
|
|
|
static irqreturn_t lsm303d_isr1(int irq, void *dev)
|
|
{
|
|
struct lsm303d_status *stat = dev;
|
|
|
|
disable_irq_nosync(irq);
|
|
queue_work(stat->irq1_work_queue, &stat->irq1_work);
|
|
pr_debug("%s: isr1 queued\n", LSM303D_DEV_NAME);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t lsm303d_isr2(int irq, void *dev)
|
|
{
|
|
struct lsm303d_status *stat = dev;
|
|
|
|
disable_irq_nosync(irq);
|
|
queue_work(stat->irq2_work_queue, &stat->irq2_work);
|
|
pr_debug("%s: isr2 queued\n", LSM303D_DEV_NAME);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void lsm303d_interrupt_catch(struct lsm303d_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 = lsm303d_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 = lsm303d_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 lsm303d_irq1_work_func(struct work_struct *work)
|
|
{
|
|
|
|
struct lsm303d_status *stat =
|
|
container_of(work, struct lsm303d_status, irq1_work);
|
|
/* TODO add interrupt service procedure.
|
|
ie:lsm303d_get_int1_source(stat); */
|
|
|
|
lsm303d_interrupt_catch(stat,1);
|
|
pr_info("%s: IRQ1 triggered\n", LSM303D_DEV_NAME);
|
|
//exit:
|
|
enable_irq(stat->irq1);
|
|
}
|
|
|
|
static void lsm303d_irq2_work_func(struct work_struct *work)
|
|
{
|
|
|
|
struct lsm303d_status *stat =
|
|
container_of(work, struct lsm303d_status, irq2_work);
|
|
/* TODO add interrupt service procedure.
|
|
ie:lsm303d_get_int2_source(stat); */
|
|
|
|
lsm303d_interrupt_catch(stat,2);
|
|
pr_info("%s: IRQ2 triggered\n", LSM303D_DEV_NAME);
|
|
//exit:
|
|
enable_irq(stat->irq2);
|
|
}
|
|
|
|
static int lsm303d_acc_device_power_off(struct lsm303d_status *stat)
|
|
{
|
|
int err;
|
|
u8 buf[2];
|
|
|
|
buf[0] = status_registers.cntrl1.address;
|
|
buf[1] = ((ODR_ACC_MASK & LSM303D_ACC_ODR_OFF) |
|
|
((~ODR_ACC_MASK) & status_registers.cntrl1.resume_value));
|
|
|
|
err = lsm303d_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 lsm303d_mag_device_power_off(struct lsm303d_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 = lsm303d_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 lsm303d_acc_device_power_on(struct lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 lsm303d_mag_device_power_on(struct lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 lsm303d_acc_update_filter(struct lsm303d_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 = lsm303d_i2c_read(stat, buf, 1);
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
status_registers.cntrl2.resume_value = buf[0];
|
|
updated_val = ((LSM303D_ACC_FILTER_MASK & new_bandwidth) |
|
|
((~LSM303D_ACC_FILTER_MASK) & buf[0]));
|
|
buf[1] = updated_val;
|
|
buf[0] = status_registers.cntrl2.address;
|
|
|
|
err = lsm303d_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 lsm303d_acc_update_fs_range(struct lsm303d_status *stat,
|
|
u8 new_fs_range)
|
|
{
|
|
int err=-1;
|
|
|
|
u16 sensitivity;
|
|
u8 updated_val;
|
|
u8 buf[2];
|
|
|
|
switch (new_fs_range) {
|
|
case LSM303D_ACC_FS_2G:
|
|
sensitivity = SENSITIVITY_ACC_2G;
|
|
break;
|
|
case LSM303D_ACC_FS_4G:
|
|
sensitivity = SENSITIVITY_ACC_4G;
|
|
break;
|
|
case LSM303D_ACC_FS_8G:
|
|
sensitivity = SENSITIVITY_ACC_8G;
|
|
break;
|
|
case LSM303D_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 = lsm303d_i2c_read(stat, buf, 1);
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
status_registers.cntrl2.resume_value = buf[0];
|
|
updated_val = ((LSM303D_ACC_FS_MASK & new_fs_range) |
|
|
((~LSM303D_ACC_FS_MASK) & buf[0]));
|
|
buf[1] = updated_val;
|
|
buf[0] = status_registers.cntrl2.address;
|
|
|
|
err = lsm303d_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 lsm303d_mag_update_fs_range(struct lsm303d_status *stat,
|
|
u8 new_fs_range)
|
|
{
|
|
int err=-1;
|
|
|
|
u16 sensitivity;
|
|
u8 updated_val;
|
|
u8 buf[2];
|
|
|
|
switch (new_fs_range) {
|
|
case LSM303D_MAG_FS_2G:
|
|
sensitivity = SENSITIVITY_MAG_2G;
|
|
break;
|
|
case LSM303D_MAG_FS_4G:
|
|
sensitivity = SENSITIVITY_MAG_4G;
|
|
break;
|
|
case LSM303D_MAG_FS_8G:
|
|
sensitivity = SENSITIVITY_MAG_8G;
|
|
break;
|
|
case LSM303D_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 = lsm303d_i2c_read(stat, buf, 1);
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
status_registers.cntrl6.resume_value = buf[0];
|
|
updated_val = (LSM303D_MAG_FS_MASK & new_fs_range);
|
|
buf[1] = updated_val;
|
|
buf[0] = status_registers.cntrl6.address;
|
|
|
|
err = lsm303d_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 lsm303d_acc_update_odr(struct lsm303d_status *stat,
|
|
unsigned int poll_interval_ms)
|
|
{
|
|
int err = -1;
|
|
u8 config[2];
|
|
int i;
|
|
|
|
for (i = ARRAY_SIZE(lsm303d_acc_odr_table) - 1; i >= 0; i--) {
|
|
if ((lsm303d_acc_odr_table[i].cutoff_us <= poll_interval_ms)
|
|
|| (i == 0))
|
|
break;
|
|
}
|
|
|
|
config[1] = ((ODR_ACC_MASK & lsm303d_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 = lsm303d_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 lsm303d_mag_update_odr(struct lsm303d_status *stat,
|
|
unsigned int poll_interval_ms)
|
|
{
|
|
int err = -1;
|
|
u8 config[2];
|
|
int i;
|
|
|
|
for (i = ARRAY_SIZE(lsm303d_mag_odr_table) - 1; i >= 0; i--) {
|
|
if ((lsm303d_mag_odr_table[i].cutoff_us <= poll_interval_ms)
|
|
|| (i == 0))
|
|
break;
|
|
}
|
|
|
|
config[1] = ((ODR_MAG_MASK & lsm303d_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 = lsm303d_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 lsm303d_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 lsm303d_acc_validate_pdata(struct lsm303d_status *stat)
|
|
{
|
|
int res = -EINVAL;
|
|
|
|
lsm303d_validate_polling(&stat->pdata_acc->min_interval,
|
|
&stat->pdata_acc->poll_interval,
|
|
(unsigned int)LSM303D_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 lsm303d_mag_validate_pdata(struct lsm303d_status *stat)
|
|
{
|
|
lsm303d_validate_polling(&stat->pdata_mag->min_interval,
|
|
&stat->pdata_mag->poll_interval,
|
|
(unsigned int)LSM303D_MAG_MIN_POLL_PERIOD_MS,
|
|
stat->client);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lsm303d_acc_enable(struct lsm303d_status *stat)
|
|
{
|
|
int err;
|
|
|
|
if (!atomic_cmpxchg(&stat->enabled_acc, 0, 1)) {
|
|
err = lsm303d_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 lsm303d_acc_disable(struct lsm303d_status *stat)
|
|
{
|
|
if (atomic_cmpxchg(&stat->enabled_acc, 1, 0)) {
|
|
cancel_work_sync(&stat->input_work_acc);
|
|
hrtimer_cancel(&stat->hr_timer_acc);
|
|
lsm303d_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 lsm303d_mag_enable(struct lsm303d_status *stat)
|
|
{
|
|
int err;
|
|
|
|
if (!atomic_cmpxchg(&stat->enabled_mag, 0, 1)) {
|
|
err = lsm303d_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 lsm303d_mag_disable(struct lsm303d_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);
|
|
}
|
|
lsm303d_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 lsm303d_temperature_enable(struct lsm303d_status *stat)
|
|
{
|
|
int err;
|
|
u8 buf[2];
|
|
u8 updated_val;
|
|
|
|
buf[0] = status_registers.cntrl5.address;
|
|
err = lsm303d_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 = lsm303d_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 lsm303d_temperature_disable(struct lsm303d_status *stat)
|
|
{
|
|
int err;
|
|
u8 buf[2];
|
|
u8 updated_val;
|
|
|
|
buf[0] = status_registers.cntrl5.address;
|
|
err = lsm303d_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 = lsm303d_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 lsm303d_acc_input_cleanup(struct lsm303d_status *stat)
|
|
{
|
|
input_unregister_device(stat->input_dev_acc);
|
|
input_free_device(stat->input_dev_acc);
|
|
}
|
|
|
|
static void lsm303d_mag_input_cleanup(struct lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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;
|
|
lsm303d_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 lsm303d_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);
|
|
interval_ms = (unsigned int)min((unsigned int)interval_ms,
|
|
stat->pdata_mag->max_interval);
|
|
mutex_lock(&stat->lock);
|
|
stat->pdata_mag->poll_interval = (unsigned int)interval_ms;
|
|
lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_status *stat = dev_get_drvdata(dev);
|
|
unsigned long val;
|
|
|
|
if (strict_strtoul(buf, 10, &val))
|
|
return -EINVAL;
|
|
|
|
printk("################## acc enable val = %ld \n", val);
|
|
|
|
if (val)
|
|
lsm303d_acc_enable(stat);
|
|
else
|
|
lsm303d_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 lsm303d_status *stat = dev_get_drvdata(dev);
|
|
unsigned long val;
|
|
|
|
if (strict_strtoul(buf, 10, &val))
|
|
return -EINVAL;
|
|
printk("################## mag enable val = %ld \n", val);
|
|
|
|
if (val)
|
|
lsm303d_mag_enable(stat);
|
|
else
|
|
lsm303d_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 lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int range = 2;
|
|
mutex_lock(&stat->lock);
|
|
val = stat->pdata_acc->fs_range ;
|
|
switch (val) {
|
|
case LSM303D_ACC_FS_2G:
|
|
range = 2;
|
|
break;
|
|
case LSM303D_ACC_FS_4G:
|
|
range = 4;
|
|
break;
|
|
case LSM303D_ACC_FS_8G:
|
|
range = 8;
|
|
break;
|
|
case LSM303D_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 lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int range = 2;
|
|
mutex_lock(&stat->lock);
|
|
val = stat->pdata_mag->fs_range ;
|
|
switch (val) {
|
|
case LSM303D_MAG_FS_2G:
|
|
range = 2;
|
|
break;
|
|
case LSM303D_MAG_FS_4G:
|
|
range = 4;
|
|
break;
|
|
case LSM303D_MAG_FS_8G:
|
|
range = 8;
|
|
break;
|
|
case LSM303D_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 lsm303d_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 = LSM303D_ACC_FS_2G;
|
|
break;
|
|
case 4:
|
|
range = LSM303D_ACC_FS_4G;
|
|
break;
|
|
case 8:
|
|
range = LSM303D_ACC_FS_8G;
|
|
break;
|
|
case 16:
|
|
range = LSM303D_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 = lsm303d_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 lsm303d_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 = LSM303D_MAG_FS_2G;
|
|
break;
|
|
case 4:
|
|
range = LSM303D_MAG_FS_4G;
|
|
break;
|
|
case 8:
|
|
range = LSM303D_MAG_FS_8G;
|
|
break;
|
|
case 12:
|
|
range = LSM303D_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 = lsm303d_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 lsm303d_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 lsm303d_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 = lsm303d_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;
|
|
}
|
|
#ifdef LSM303D_GS_ENABLE
|
|
static ssize_t attr_get_cali_data_x(struct kobject *kobj,struct kobj_attribute *attr,char *buf)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int cali_x,cali_y,cali_z;
|
|
mutex_lock(&stat->lock);
|
|
cali_x =stat->pdata_acc->cali_sw[0];
|
|
cali_y =stat->pdata_acc->cali_sw[1];
|
|
cali_z =stat->pdata_acc->cali_sw[2];
|
|
mutex_unlock(&stat->lock);
|
|
return sprintf(buf, "x:%d y:%d z:%d\n", cali_x, cali_y, cali_z);
|
|
}
|
|
|
|
static ssize_t attr_set_cali_data_x(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t size)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
long val;
|
|
|
|
if (strict_strtol(buf, 10, &val))
|
|
return -EINVAL;
|
|
|
|
printk("%s: ######val = %ld\n", __func__, val);
|
|
mutex_lock(&stat->lock);
|
|
#if defined(BLADE2_8)
|
|
stat->pdata_acc->cali_sw[1] = -val;
|
|
#else
|
|
stat->pdata_acc->cali_sw[0] = -val;
|
|
#endif
|
|
|
|
mutex_unlock(&stat->lock);
|
|
return size;
|
|
}
|
|
static ssize_t attr_get_cali_data_y(struct kobject *kobj,struct kobj_attribute *attr,char *buf)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int cali_x,cali_y,cali_z;
|
|
mutex_lock(&stat->lock);
|
|
cali_x =stat->pdata_acc->cali_sw[0];
|
|
cali_y =stat->pdata_acc->cali_sw[1];
|
|
cali_z =stat->pdata_acc->cali_sw[2];
|
|
mutex_unlock(&stat->lock);
|
|
return sprintf(buf, "x:%d y:%d z:%d\n", cali_x, cali_y, cali_z);
|
|
}
|
|
|
|
static ssize_t attr_set_cali_data_y(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t size)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
long val;
|
|
|
|
if (strict_strtol(buf, 10, &val))
|
|
return -EINVAL;
|
|
|
|
printk("%s: ######val = %ld\n", __func__, val);
|
|
mutex_lock(&stat->lock);
|
|
#if defined(BLADE2_8)
|
|
stat->pdata_acc->cali_sw[0] = val;
|
|
#else
|
|
stat->pdata_acc->cali_sw[1] = -val;
|
|
#endif
|
|
|
|
mutex_unlock(&stat->lock);
|
|
return size;
|
|
}
|
|
static ssize_t attr_get_cali_data_z(struct kobject *kobj,struct kobj_attribute *attr,char *buf)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int cali_x,cali_y,cali_z;
|
|
mutex_lock(&stat->lock);
|
|
cali_x =stat->pdata_acc->cali_sw[0];
|
|
cali_y =stat->pdata_acc->cali_sw[1];
|
|
cali_z =stat->pdata_acc->cali_sw[2];
|
|
mutex_unlock(&stat->lock);
|
|
return sprintf(buf, "x:%d y:%d z:%d\n", cali_x, cali_y, cali_z);
|
|
}
|
|
static int lsm303d_acc_get_data(struct lsm303d_status *stat, int *xyz);
|
|
static ssize_t attr_get_gs_data(struct kobject *kobj,struct kobj_attribute *attr,char *buf)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
int xyz[3] = { 0 };
|
|
int err;
|
|
|
|
mutex_lock(&stat->lock);
|
|
err = lsm303d_acc_get_data(stat, xyz);
|
|
mutex_unlock(&stat->lock);
|
|
return sprintf(buf, "%d %d %d\n", xyz[0]/1000, xyz[1]/1000, xyz[2]/1000);
|
|
}
|
|
|
|
static ssize_t attr_set_cali_data_z(struct kobject *kobj,struct kobj_attribute *attr,const char *buf, size_t size)
|
|
{
|
|
struct device *dev = to_dev(kobj->parent);
|
|
struct lsm303d_status *stat = dev_get_drvdata(dev);
|
|
long val;
|
|
|
|
if (strict_strtol(buf, 10, &val))
|
|
return -EINVAL;
|
|
|
|
printk("%s: ######val = %ld\n", __func__, val);
|
|
mutex_lock(&stat->lock);
|
|
stat->pdata_acc->cali_sw[2] = val;
|
|
mutex_unlock(&stat->lock);
|
|
return size;
|
|
}
|
|
#endif
|
|
static ssize_t attr_get_temp_enable(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct lsm303d_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 lsm303d_status *stat = dev_get_drvdata(dev);
|
|
|
|
if (strict_strtoul(buf, 10, &val))
|
|
return -EINVAL;
|
|
|
|
atomic_set(&stat->enabled_temp, (int)val);
|
|
|
|
if(val>0) {
|
|
lsm303d_temperature_enable(stat);
|
|
} else {
|
|
lsm303d_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 lsm303d_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, 0644, attr_get_polling_rate_acc, attr_set_polling_rate_acc);
|
|
static struct kobj_attribute enable_attr_acc =
|
|
__ATTR(enable_device, 0644, attr_get_enable_acc, attr_set_enable_acc);
|
|
static struct kobj_attribute fs_attr_acc =
|
|
__ATTR(full_scale, 0644, attr_get_range_acc, attr_set_range_acc);
|
|
static struct kobj_attribute aa_filter_attr =
|
|
__ATTR(anti_aliasing_frequency, 0644, attr_get_aa_filter, attr_set_aa_filter);
|
|
#ifdef LSM303D_GS_ENABLE
|
|
static struct kobj_attribute gs_cali_x =
|
|
__ATTR(gsensor_cali_x, 0644, attr_get_cali_data_x, attr_set_cali_data_x);
|
|
static struct kobj_attribute gs_cali_y =
|
|
__ATTR(gsensor_cali_y, 0644, attr_get_cali_data_y, attr_set_cali_data_y);
|
|
static struct kobj_attribute gs_cali_z =
|
|
__ATTR(gsensor_cali_z, 0644, attr_get_cali_data_z, attr_set_cali_data_z);
|
|
#endif
|
|
static struct kobj_attribute gs_data =
|
|
__ATTR(gsensor_data, 0444, attr_get_gs_data, NULL);
|
|
static struct kobj_attribute poll_attr_mag =
|
|
__ATTR(pollrate_ms, 0644, attr_get_polling_rate_mag, attr_set_polling_rate_mag);
|
|
static struct kobj_attribute enable_attr_mag =
|
|
__ATTR(enable_device, 0644, attr_get_enable_mag, attr_set_enable_mag);
|
|
static struct kobj_attribute fs_attr_mag =
|
|
__ATTR(full_scale, 0644, attr_get_range_mag, attr_set_range_mag);
|
|
|
|
static int write_bit_on_register(struct lsm303d_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 = lsm303d_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 = lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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 lsm303d_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, 0644, attr_get_gen1_status, attr_set_gen1_status);
|
|
static struct kobj_attribute gen1_interrupt_pin2_enable =
|
|
__ATTR(pin2_enable, 0644, attr_get_gen1_status, attr_set_gen1_status);
|
|
|
|
static struct kobj_attribute gen2_interrupt_pin1_enable =
|
|
__ATTR(pin1_enable, 0644, attr_get_gen2_status, attr_set_gen2_status);
|
|
static struct kobj_attribute gen2_interrupt_pin2_enable =
|
|
__ATTR(pin2_enable, 0644, attr_get_gen2_status, attr_set_gen2_status);
|
|
|
|
static struct kobj_attribute gen1_duration =
|
|
__ATTR(duration, 0644, attr_get_gen1_duration, attr_set_gen1_duration);
|
|
static struct kobj_attribute gen2_duration =
|
|
__ATTR(duration, 0644, attr_get_gen2_duration, attr_set_gen2_duration);
|
|
|
|
static struct kobj_attribute gen1_threshold =
|
|
__ATTR(threshold, 0644, attr_get_gen1_threshold, attr_set_gen1_threshold);
|
|
static struct kobj_attribute gen2_threshold =
|
|
__ATTR(threshold, 0644, attr_get_gen2_threshold, attr_set_gen2_threshold);
|
|
|
|
static struct kobj_attribute mag_gen_interrupt_pin1 =
|
|
__ATTR(pin1_enable, 0644, attr_get_gen_mag_status, attr_set_gen_mag_status);
|
|
static struct kobj_attribute mag_gen_interrupt_pin2 =
|
|
__ATTR(pin2_enable, 0644, attr_get_gen_mag_status, attr_set_gen_mag_status);
|
|
|
|
static struct kobj_attribute mag_gen_threshold =
|
|
__ATTR(threshold, 0644, attr_get_gen_mag_threshold, attr_set_gen_mag_threshold);
|
|
|
|
static struct kobj_attribute gen1_x_high =
|
|
__ATTR(x_high_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
static struct kobj_attribute gen1_x_low =
|
|
__ATTR(x_low_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
|
|
static struct kobj_attribute gen2_x_high =
|
|
__ATTR(x_high_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
static struct kobj_attribute gen2_x_low =
|
|
__ATTR(x_low_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
|
|
static struct kobj_attribute gen1_y_high =
|
|
__ATTR(y_high_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
static struct kobj_attribute gen1_y_low =
|
|
__ATTR(y_low_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
|
|
static struct kobj_attribute gen2_y_high =
|
|
__ATTR(y_high_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
static struct kobj_attribute gen2_y_low =
|
|
__ATTR(y_low_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
|
|
static struct kobj_attribute gen1_z_high =
|
|
__ATTR(z_high_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
static struct kobj_attribute gen1_z_low =
|
|
__ATTR(z_low_enable, 0644, attr_get_gen1_axis, attr_set_gen1_axis);
|
|
|
|
static struct kobj_attribute gen2_z_high =
|
|
__ATTR(z_high_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
static struct kobj_attribute gen2_z_low =
|
|
__ATTR(z_low_enable, 0644, attr_get_gen2_axis, attr_set_gen2_axis);
|
|
|
|
static struct kobj_attribute gen_mag_x =
|
|
__ATTR(x_enable, 0644, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
|
|
static struct kobj_attribute gen_mag_y =
|
|
__ATTR(y_enable, 0644, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
|
|
static struct kobj_attribute gen_mag_z =
|
|
__ATTR(z_enable, 0644, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
|
|
|
|
static struct kobj_attribute gen1_and_or =
|
|
__ATTR(and(1)_or(0)_combination, 0644, attr_get_gen1_and_or,
|
|
attr_set_gen1_and_or);
|
|
static struct kobj_attribute gen2_and_or =
|
|
__ATTR(and(1)_or(0)_combination, 0644, 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,
|
|
#ifdef LSM303D_GS_ENABLE
|
|
&gs_cali_x.attr,
|
|
&gs_cali_y.attr,
|
|
&gs_cali_z.attr,
|
|
#endif
|
|
&gs_data.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, 0644, 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, 0644, attr_get_pin_conf,
|
|
attr_set_pin_conf),
|
|
__ATTR(interrupt_polarity, 0644, attr_get_interrupt_polarity,
|
|
attr_set_interrupt_polarity),
|
|
};
|
|
|
|
static int create_sysfs_interfaces(struct device *dev)
|
|
{
|
|
int err;
|
|
int i,n;
|
|
struct lsm303d_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 lsm303d_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 lsm303d_acc_input_open(struct input_dev *input)
|
|
{
|
|
//struct lsm303d_status *stat = input_get_drvdata(input);
|
|
|
|
//return lsm303d_acc_enable(stat);
|
|
return 0;
|
|
}
|
|
|
|
void lsm303d_acc_input_close(struct input_dev *dev)
|
|
{
|
|
//struct lsm303d_status *stat = input_get_drvdata(dev);
|
|
|
|
//lsm303d_acc_disable(stat);
|
|
}
|
|
|
|
int lsm303d_mag_input_open(struct input_dev *input)
|
|
{
|
|
//struct lsm303d_status *stat = input_get_drvdata(input);
|
|
|
|
//return lsm303d_mag_enable(stat);
|
|
return 0;
|
|
}
|
|
|
|
void lsm303d_mag_input_close(struct input_dev *dev)
|
|
{
|
|
//struct lsm303d_status *stat = input_get_drvdata(dev);
|
|
|
|
//lsm303d_mag_disable(stat);
|
|
}
|
|
|
|
static int lsm303d_acc_get_data(struct lsm303d_status *stat, int *xyz)
|
|
{
|
|
int i, err = -1;
|
|
u8 acc_data[6] = { 0 };
|
|
s32 hw_d[3] = { 0 };
|
|
|
|
acc_data[0] = (REG_ACC_DATA_ADDR);
|
|
err = lsm303d_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",
|
|
LSM303D_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",
|
|
LSM303D_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",
|
|
LSM303D_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] +
|
|
stat->pdata_acc->cali_sw[i];
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int lsm303d_mag_get_data(struct lsm303d_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 = lsm303d_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",
|
|
LSM303D_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",
|
|
LSM303D_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",
|
|
LSM303D_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++) {
|
|
/* Fix magnetometer bug. oujf1 */
|
|
/*
|
|
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];
|
|
*/
|
|
xyz[i] = stat->pdata_mag->rot_matrix[0][i] * hw_d[0] +
|
|
stat->pdata_mag->rot_matrix[1][i] * hw_d[1] +
|
|
stat->pdata_mag->rot_matrix[2][i] * hw_d[2];
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int lsm303d_temp_get_data(struct lsm303d_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 = lsm303d_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",
|
|
LSM303D_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 lsm303d_acc_report_values(struct lsm303d_status *stat, int *xyz)
|
|
{
|
|
input_report_abs(stat->input_dev_acc, ABS_X, xyz[0]/1000);
|
|
input_report_abs(stat->input_dev_acc, ABS_Y, xyz[1]/1000);
|
|
input_report_abs(stat->input_dev_acc, ABS_Z, xyz[2]/1000);
|
|
input_sync(stat->input_dev_acc);
|
|
}
|
|
|
|
static void lsm303d_mag_report_values(struct lsm303d_status *stat, int *xyz)
|
|
{
|
|
input_report_abs(stat->input_dev_mag, ABS_X, xyz[0]/1000);
|
|
input_report_abs(stat->input_dev_mag, ABS_Y, xyz[1]/1000);
|
|
input_report_abs(stat->input_dev_mag, ABS_Z, xyz[2]/1000);
|
|
input_sync(stat->input_dev_mag);
|
|
}
|
|
|
|
static int lsm303d_acc_input_init(struct lsm303d_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 = lsm303d_acc_input_open;
|
|
stat->input_dev_acc->close = lsm303d_acc_input_close;
|
|
stat->input_dev_acc->name = LSM303D_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 lsm303d_mag_input_init(struct lsm303d_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 = lsm303d_mag_input_open;
|
|
stat->input_dev_mag->close = lsm303d_mag_input_close;
|
|
stat->input_dev_mag->name = LSM303D_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 lsm303d_input_cleanup(struct lsm303d_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 lsm303d_status *stat;
|
|
int xyz[3] = { 0 };
|
|
int err;
|
|
|
|
stat = container_of((struct work_struct *)input_work_acc,
|
|
struct lsm303d_status, input_work_acc);
|
|
|
|
mutex_lock(&stat->lock);
|
|
err = lsm303d_acc_get_data(stat, xyz);
|
|
if (err < 0)
|
|
dev_err(&stat->client->dev, "get_accelerometer_data failed\n");
|
|
else
|
|
lsm303d_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 lsm303d_status *stat;
|
|
int xyz[3] = { 0 };
|
|
int err;
|
|
int dec;
|
|
int flo;
|
|
|
|
stat = container_of((struct work_struct *)input_work_mag,
|
|
struct lsm303d_status, input_work_mag);
|
|
|
|
mutex_lock(&stat->lock);
|
|
|
|
if(atomic_read(&stat->enabled_temp)) {
|
|
err = lsm303d_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 = lsm303d_mag_get_data(stat, xyz);
|
|
if (err < 0)
|
|
dev_err(&stat->client->dev, "get_magnetometer_data"
|
|
" failed\n");
|
|
else
|
|
lsm303d_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 lsm303d_status *stat;
|
|
|
|
|
|
stat = container_of((struct hrtimer *)timer,
|
|
struct lsm303d_status, hr_timer_acc);
|
|
|
|
queue_work(lsm303d_workqueue, &stat->input_work_acc);
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
enum hrtimer_restart poll_function_read_mag(struct hrtimer *timer)
|
|
{
|
|
struct lsm303d_status *stat;
|
|
|
|
|
|
stat = container_of((struct hrtimer *)timer,
|
|
struct lsm303d_status, hr_timer_mag);
|
|
|
|
queue_work(lsm303d_workqueue, &stat->input_work_mag);
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
extern int get_gpio_by_name(const char *name); //zfming
|
|
|
|
static int lsm303d_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct lsm303d_status *stat;
|
|
u32 smbus_func;
|
|
int err = -1;
|
|
|
|
printk("%s\n",__func__);
|
|
|
|
smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |
|
|
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
|
|
|
|
dev_info(&client->dev, "probe start.\n");
|
|
stat = kzalloc(sizeof(struct lsm303d_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;
|
|
}
|
|
}
|
|
|
|
err = i2c_smbus_read_byte_data(client,REG_WHOAMI_ADDR);
|
|
pr_info("%s:======msensor id is 0x%x=======\n", __func__, err);
|
|
|
|
if(lsm303d_workqueue == 0)
|
|
lsm303d_workqueue = create_workqueue("lsm303d_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_lsm303d_acc_pdata,
|
|
sizeof(*stat->pdata_acc));
|
|
memcpy(stat->pdata_mag, &default_lsm303d_mag_pdata,
|
|
sizeof(*stat->pdata_mag));
|
|
dev_info(&client->dev, "using default plaform_data for "
|
|
"accelerometer and magnetometer\n");
|
|
} else {
|
|
struct lsm303d_main_platform_data *tmp;
|
|
tmp = kzalloc(sizeof(struct lsm303d_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_lsm303d_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_lsm303d_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 = lsm303d_acc_validate_pdata(stat);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "failed to validate platform data for "
|
|
"accelerometer \n");
|
|
goto exit_kfree_pdata;
|
|
}
|
|
|
|
err = lsm303d_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;
|
|
}
|
|
}
|
|
|
|
//stat->pdata_acc->gpio_int1 = get_gpio_by_name("accel_int1");
|
|
//stat->pdata_acc->gpio_int1 = 2;
|
|
|
|
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_LSM303D");
|
|
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",
|
|
LSM303D_DEV_NAME, __func__, stat->irq1,
|
|
stat->pdata_acc->gpio_int1);
|
|
}
|
|
|
|
//stat->pdata_acc->gpio_int2 = get_gpio_by_name("accel_int2");
|
|
//stat->pdata_acc->gpio_int2 = 3;
|
|
|
|
|
|
dev_info(&client->dev, "pdata_acc--gpio_int1:%d, gpio_int2:%d\n", stat->pdata_acc->gpio_int1, stat->pdata_acc->gpio_int2); //zfming
|
|
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_LSM303D");
|
|
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",
|
|
LSM303D_DEV_NAME, __func__, stat->irq2,
|
|
stat->pdata_acc->gpio_int2);
|
|
}
|
|
|
|
err = lsm303d_hw_init(stat);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "hw init failed: %d\n", err);
|
|
goto err_hw_init;
|
|
}
|
|
|
|
err = lsm303d_acc_device_power_on(stat);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "accelerometer power on failed: "
|
|
"%d\n", err);
|
|
goto err_pdata_init;
|
|
}
|
|
err = lsm303d_mag_device_power_on(stat);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "magnetometer power on failed: "
|
|
"%d\n", err);
|
|
goto err_pdata_init;
|
|
}
|
|
|
|
err = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_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 = lsm303d_acc_input_init(stat);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "accelerometer input init failed\n");
|
|
goto err_power_off;
|
|
}
|
|
|
|
err = lsm303d_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 LSM303D_DEV_NAME sysfs register failed\n");
|
|
goto err_input_cleanup;
|
|
}
|
|
|
|
lsm303d_acc_device_power_off(stat);
|
|
lsm303d_mag_device_power_off(stat);
|
|
|
|
if(stat->pdata_acc->gpio_int1 >= 0){
|
|
INIT_WORK(&stat->irq1_work, lsm303d_irq1_work_func);
|
|
stat->irq1_work_queue =
|
|
create_singlethread_workqueue("lsm303d_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, lsm303d_isr1,
|
|
IRQF_TRIGGER_RISING, "lsm303d_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, lsm303d_irq2_work_func);
|
|
stat->irq2_work_queue =
|
|
create_singlethread_workqueue("lsm303d_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, lsm303d_isr2,
|
|
IRQF_TRIGGER_RISING, "lsm303d_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);
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
printk("==register_early_suspend =\n");
|
|
stat->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 50;
|
|
stat->early_suspend.suspend = lsm303d_earlysuspend;
|
|
stat->early_suspend.resume = lsm303d_lateresume;
|
|
register_early_suspend(&stat->early_suspend);
|
|
#endif
|
|
|
|
mutex_unlock(&stat->lock);
|
|
dev_info(&client->dev, "%s: probed\n", LSM303D_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:
|
|
lsm303d_input_cleanup(stat);
|
|
err_power_off:
|
|
err_power_off_mag:
|
|
lsm303d_mag_device_power_off(stat);
|
|
err_power_off_acc:
|
|
lsm303d_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(!lsm303d_workqueue) {
|
|
flush_workqueue(lsm303d_workqueue);
|
|
destroy_workqueue(lsm303d_workqueue);
|
|
}
|
|
exit_check_functionality_failed:
|
|
pr_err("%s: Driver Init failed\n", LSM303D_DEV_NAME);
|
|
return err;
|
|
}
|
|
|
|
static int __exit lsm303d_remove(struct i2c_client *client)
|
|
{
|
|
struct lsm303d_status *stat = i2c_get_clientdata(client);
|
|
|
|
lsm303d_acc_disable(stat);
|
|
lsm303d_mag_disable(stat);
|
|
lsm303d_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);
|
|
}
|
|
|
|
lsm303d_acc_input_cleanup(stat);
|
|
lsm303d_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(!lsm303d_workqueue) {
|
|
flush_workqueue(lsm303d_workqueue);
|
|
destroy_workqueue(lsm303d_workqueue);
|
|
}
|
|
|
|
kfree(stat->pdata_acc);
|
|
kfree(stat->pdata_mag);
|
|
kfree(stat);
|
|
return 0;
|
|
}
|
|
static void lsm303d_earlysuspend(struct early_suspend *handler)
|
|
{
|
|
struct lsm303d_status *stat = container_of(handler, struct lsm303d_status, early_suspend);
|
|
|
|
#ifdef CONFIG_PM
|
|
pr_info("##################%s\n", __func__);
|
|
|
|
if (atomic_read(&stat->enabled_acc))
|
|
{
|
|
stat->acc_need_resume = 1;
|
|
lsm303d_acc_disable(stat);
|
|
}
|
|
else
|
|
{
|
|
stat->acc_need_resume = 0;
|
|
}
|
|
|
|
if (atomic_read(&stat->enabled_mag))
|
|
{
|
|
stat->mag_need_resume = 1;
|
|
lsm303d_mag_disable(stat);
|
|
}
|
|
else
|
|
{
|
|
stat->mag_need_resume = 0;
|
|
}
|
|
pr_info("##################%s acc_need_resume %d, mag_need_resume %d\n", __func__, stat->acc_need_resume, stat->mag_need_resume);
|
|
#endif /*CONFIG_PM*/
|
|
}
|
|
|
|
static void lsm303d_lateresume(struct early_suspend *handler)
|
|
{
|
|
struct lsm303d_status *stat = container_of(handler, struct lsm303d_status, early_suspend);
|
|
|
|
#ifdef CONFIG_PM
|
|
pr_info("##################%s acc_need_resume %d, mag_need_resume %d\n", __func__, stat->acc_need_resume, stat->mag_need_resume);
|
|
|
|
if (stat->acc_need_resume)
|
|
{
|
|
stat->acc_need_resume = 0;
|
|
lsm303d_acc_enable(stat);
|
|
}
|
|
|
|
if (stat->mag_need_resume)
|
|
{
|
|
stat->mag_need_resume = 0;
|
|
lsm303d_mag_enable(stat);
|
|
}
|
|
|
|
#endif /*CONFIG_PM*/
|
|
}
|
|
|
|
static const struct i2c_device_id lsm303d_id[] = {
|
|
{ LSM303D_DEV_NAME, 0 },
|
|
{ },
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, lsm303d_id);
|
|
|
|
#ifdef LSM303D_CONFIG_SUSPEND
|
|
static const struct dev_pm_ops lsm303d_pm_ops = {
|
|
//.suspend = lsm303d_suspend,
|
|
//.resume = lsm303d_resume,
|
|
};
|
|
#endif
|
|
|
|
static struct i2c_driver lsm303d_driver = {
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "lsm303d",
|
|
#ifdef LSM303D_CONFIG_SUSPEND
|
|
//.pm = &lsm303d_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = lsm303d_probe,
|
|
.remove = lsm303d_remove,
|
|
.id_table = lsm303d_id,
|
|
};
|
|
|
|
|
|
static int __init lsm303d_init(void)
|
|
{
|
|
int i2c_busnum = 5;
|
|
struct i2c_board_info i2c_info;
|
|
// void *pdata = NULL;
|
|
|
|
i2c_add_driver(&lsm303d_driver);
|
|
|
|
memset(&i2c_info, 0, sizeof(i2c_info));
|
|
strlcpy(i2c_info.type, "lsm303d", sizeof("lsm303d"));
|
|
|
|
i2c_info.addr = 0x1D;
|
|
|
|
pr_info("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n",
|
|
i2c_busnum,
|
|
i2c_info.type,
|
|
i2c_info.irq,
|
|
i2c_info.addr);
|
|
|
|
return i2c_register_board_info(i2c_busnum, &i2c_info, 1);
|
|
}
|
|
|
|
|
|
static void __exit lsm303d_exit(void)
|
|
{
|
|
pr_info("%s driver exit\n", LSM303D_DEV_NAME);
|
|
i2c_del_driver(&lsm303d_driver);
|
|
}
|
|
|
|
fs_initcall(lsm303d_init);
|
|
module_exit(lsm303d_exit);
|
|
|
|
MODULE_DESCRIPTION("lsm303d accelerometer and magnetometer driver");
|
|
MODULE_AUTHOR("Matteo Dameno, Denis Ciocca, STMicroelectronics");
|
|
MODULE_LICENSE("GPL");
|