/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
*
* File Name : lsm303dlh_acc.c
* Authors : MSH - Motion Mems BU - Application Team
* : Carmine Iascone (carmine.iascone@st.com)
* : Matteo Dameno (matteo.dameno@st.com)
* : Both authors are willing to be considered the contact
* : and update points for the driver.
* Version : V 1.7.0
* Date : 2011/03/02
* Description : LSM303DLH 6D module sensor API
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
******************************************************************************
Revision 1.5.0 2010/09/05:
lsm303dlh_acc_device_power_off now calling CTRL_REG1 to set power off
manages 2 interrupts;
correction to update_g_range;
modified_get_acceleration_data function
modified update_odr function and lsm303dlh_acc_odr_table;
don't support ioclt;
supports sysfs;
Revision 1.6.0 2011/02/28
checks for availability of interrupts pins
Revision 1.7.0 2011/03/02
adds self test enable/disable
******************************************************************************/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c/lsm303dlh.h>
#define DEBUG 1
#define G_MAX 8000
#define SENSITIVITY_2G 1 /** mg/LSB */
#define SENSITIVITY_4G 2 /** mg/LSB */
#define SENSITIVITY_8G 4 /** mg/LSB */
#define AXISDATA_REG 0x28
#define WHOAMI_LSM303DLH_ACC 0x32 /* Expctd content for WAI */
/* CONTROL REGISTERS */
#define WHO_AM_I 0x0F /* WhoAmI register */
#define CTRL_REG1 0x20 /* */
#define CTRL_REG2 0x21 /* */
#define CTRL_REG3 0x22 /* */
#define CTRL_REG4 0x23 /* */
#define CTRL_REG5 0x24 /* */
#define INT_CFG1 0x30 /* interrupt 1 config */
#define INT_SRC1 0x31 /* interrupt 1 source */
#define INT_THS1 0x32 /* interrupt 1 threshold */
#define INT_DUR1 0x33 /* interrupt 1 duration */
#define INT_CFG2 0x34 /* interrupt 2 config */
#define INT_SRC2 0x35 /* interrupt 2 source */
#define INT_THS2 0x36 /* interrupt 2 threshold */
#define INT_DUR2 0x37 /* interrupt 2 duration */
/* end CONTROL REGISTRES */
#define LSM303DLH_ACC_ENABLE_ALL_AXES 0x07
#define LSM303DLH_SELFTEST_EN 0x02
#define LSM303DLH_SELFTEST_DIS 0x00
#define LSM303DLH_SELFTEST_POS 0x00
#define LSM303DLH_SELFTEST_NEG 0x08
/* Accelerometer output data rate */
#define LSM303DLH_ACC_ODRHALF 0x40 /* 0.5Hz output data rate */
#define LSM303DLH_ACC_ODR1 0x60 /* 1Hz output data rate */
#define LSM303DLH_ACC_ODR2 0x80 /* 2Hz output data rate */
#define LSM303DLH_ACC_ODR5 0xA0 /* 5Hz output data rate */
#define LSM303DLH_ACC_ODR10 0xC0 /* 10Hz output data rate */
#define LSM303DLH_ACC_ODR50 0x00 /* 50Hz output data rate */
#define LSM303DLH_ACC_ODR100 0x08 /* 100Hz output data rate */
#define LSM303DLH_ACC_ODR400 0x10 /* 400Hz output data rate */
#define LSM303DLH_ACC_ODR1000 0x18 /* 1000Hz output data rate */
#define FUZZ 0
#define FLAT 0
#define I2C_RETRY_DELAY 5
#define I2C_RETRIES 5
#define I2C_AUTO_INCREMENT 0x80
/* RESUME STATE INDICES */
#define RES_CTRL_REG1 0
#define RES_CTRL_REG2 1
#define RES_CTRL_REG3 2
#define RES_CTRL_REG4 3
#define RES_CTRL_REG5 4
#define RES_REFERENCE 5
#define RES_INT_CFG1 6
#define RES_INT_THS1 7
#define RES_INT_DUR1 8
#define RES_INT_CFG2 9
#define RES_INT_THS2 10
#define RES_INT_DUR2 11
#define RESUME_ENTRIES 12
/* end RESUME STATE INDICES */
static struct {
unsigned int cutoff_ms;
unsigned int mask;
} lsm303dlh_acc_odr_table[] = {
{1, LSM303DLH_ACC_PM_NORMAL | LSM303DLH_ACC_ODR1000},
{3, LSM303DLH_ACC_PM_NORMAL | LSM303DLH_ACC_ODR400},
{10, LSM303DLH_ACC_PM_NORMAL | LSM303DLH_ACC_ODR100},
{20, LSM303DLH_ACC_PM_NORMAL | LSM303DLH_ACC_ODR50},
/* low power settings, max low pass filter cut-off freq */
{100, LSM303DLH_ACC_ODR10 | LSM303DLH_ACC_ODR1000},
{200, LSM303DLH_ACC_ODR5 | LSM303DLH_ACC_ODR1000},
{5000, LSM303DLH_ACC_ODR2 | LSM303DLH_ACC_ODR1000 },
{1000, LSM303DLH_ACC_ODR1 | LSM303DLH_ACC_ODR1000 },
{2000, LSM303DLH_ACC_ODRHALF | LSM303DLH_ACC_ODR1000 },
};
struct lsm303dlh_acc_data {
struct i2c_client *client;
struct lsm303dlh_acc_platform_data *pdata;
struct mutex lock;
struct delayed_work input_work;
struct input_dev *input_dev;
int hw_initialized;
/* hw_working=-1 means not tested yet */
int hw_working;
int selftest_enabled;
atomic_t enabled;
int on_before_suspend;
u8 sensitivity;
u8 resume_state[RESUME_ENTRIES];
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;
#ifdef DEBUG
u8 reg_addr;
#endif
};
static int lsm303dlh_acc_i2c_read(struct lsm303dlh_acc_data *acc,
u8 *buf, int len)
{
int err;
int tries = 0;
struct i2c_msg msgs[] = {
{
.addr = acc->client->addr,
.flags = acc->client->flags & I2C_M_TEN,
.len = 1,
.buf = buf,
},
{
.addr = acc->client->addr,
.flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
.len = len,
.buf = buf,
},
};
do {
err = i2c_transfer(acc->client->adapter, msgs, 2);
if (err != 2)
msleep_interruptible(I2C_RETRY_DELAY);
} while ((err != 2) && (++tries < I2C_RETRIES));
if (err != 2) {
dev_err(&acc->client->dev, "read transfer error\n");
return -EIO;
}
return 0;
}
static int lsm303dlh_acc_i2c_write(struct lsm303dlh_acc_data *acc,
u8 *buf, int len)
{
int err;
int tries = 0;
struct i2c_msg msgs[] = {
{
.addr = acc->client->addr,
.flags = acc->client->flags & I2C_M_TEN,
.len = len + 1,
.buf = buf,
},
};
do {
err = i2c_transfer(acc->client->adapter, msgs, 1);
if (err != 1)
msleep_interruptible(I2C_RETRY_DELAY);
} while ((err != 1) && (++tries < I2C_RETRIES));
if (err != 1) {
dev_err(&acc->client->dev, "write transfer error\n");
return -EIO;
}
return err;
}
static int lsm303dlh_acc_hw_init(struct lsm303dlh_acc_data *acc)
{
int err = -1;
u8 buf[6];
printk(KERN_INFO "%s: hw init start\n", LSM303DLH_ACC_DEV_NAME);
buf[0] = WHO_AM_I;
err = lsm303dlh_acc_i2c_read(acc, buf, 1);
if (err < 0){
dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device "
"available/working?\n");
goto err_firstread;
} else
acc->hw_working = 1;
if (buf[0] != WHOAMI_LSM303DLH_ACC) {
dev_err(&acc->client->dev,
"device unknown. Expected: 0x%x,"
" Replies: 0x%x\n", WHOAMI_LSM303DLH_ACC, buf[0]);
err = -1; /* choose the right coded error */
goto err_unknown_device;
}
buf[0] = CTRL_REG1;
buf[1] = acc->resume_state[RES_CTRL_REG1];
err = lsm303dlh_acc_i2c_write(acc, buf, 1);
if (err < 0)
goto err_resume_state;
buf[0] = (I2C_AUTO_INCREMENT | INT_THS1);
buf[1] = acc->resume_state[RES_INT_THS1];
buf[2] = acc->resume_state[RES_INT_DUR1];
err = lsm303dlh_acc_i2c_write(acc, buf, 2);
if (err < 0)
goto err_resume_state;
buf[0] = INT_CFG1;
buf[1] = acc->resume_state[RES_INT_CFG1];
err = lsm303dlh_acc_i2c_write(acc, buf, 1);
if (err < 0)
goto err_resume_state;
buf[0] = (I2C_AUTO_INCREMENT | INT_THS2);
buf[1] = acc->resume_state[RES_INT_THS2];
buf[2] = acc->resume_state[RES_INT_DUR2];
err = lsm303dlh_acc_i2c_write(acc
评论0