2008-11-13 00:27:02 +03:00
/*
* lis3lv02d . c - ST LIS3LV02DL accelerometer driver
*
* Copyright ( C ) 2007 - 2008 Yan Burman
* Copyright ( C ) 2008 Eric Piel
2009-02-19 01:48:23 +03:00
* Copyright ( C ) 2008 - 2009 Pavel Machek
2008-11-13 00:27:02 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2010-10-20 10:51:40 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-11-13 00:27:02 +03:00
# include <linux/kernel.h>
# include <linux/dmi.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
2009-06-17 02:34:15 +04:00
# include <linux/input-polldev.h>
2008-11-13 00:27:02 +03:00
# include <linux/delay.h>
# include <linux/wait.h>
# include <linux/poll.h>
2010-10-22 15:57:24 +04:00
# include <linux/slab.h>
2008-11-13 00:27:02 +03:00
# include <linux/freezer.h>
# include <linux/uaccess.h>
2009-02-19 01:48:23 +03:00
# include <linux/miscdevice.h>
2010-10-22 15:57:23 +04:00
# include <linux/pm_runtime.h>
2011-03-21 19:59:36 +03:00
# include <linux/atomic.h>
2012-09-27 00:58:16 +04:00
# include <linux/of_device.h>
2008-11-13 00:27:02 +03:00
# include "lis3lv02d.h"
# define DRIVER_NAME "lis3lv02d"
/* joystick device poll interval in milliseconds */
# define MDPS_POLL_INTERVAL 50
2010-05-25 01:33:37 +04:00
# define MDPS_POLL_MIN 0
# define MDPS_POLL_MAX 2000
2010-10-22 15:57:23 +04:00
# define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */
2010-10-22 15:57:32 +04:00
# define SELFTEST_OK 0
# define SELFTEST_FAIL -1
# define SELFTEST_IRQ -2
# define IRQ_LINE0 0
# define IRQ_LINE1 1
2008-11-13 00:27:02 +03:00
/*
* The sensor can also generate interrupts ( DRDY ) but it ' s pretty pointless
2009-12-15 05:01:39 +03:00
* because they are generated even if the data do not change . So it ' s better
2008-11-13 00:27:02 +03:00
* to keep the interrupt for the free - fall event . The values are updated at
* 40 Hz ( at the lowest frequency ) , but as it can be pretty time consuming on
* some low processor , we poll the sensor only at 20 Hz . . . enough for the
* joystick .
*/
2009-12-15 05:01:41 +03:00
# define LIS3_PWRON_DELAY_WAI_12B (5000)
# define LIS3_PWRON_DELAY_WAI_8B (3000)
2009-12-15 05:01:46 +03:00
/*
* LIS3LV02D spec says 1024 LSBs corresponds 1 G - > 1L SB is 1000 / 1024 mG
* LIS302D spec says : 18 mG / digit
* LIS3_ACCURACY is used to increase accuracy of the intermediate
* calculation results .
*/
# define LIS3_ACCURACY 1024
/* Sensitivity values for -2G +2G scale */
# define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
# define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
2012-08-22 10:30:39 +04:00
/*
2012-09-24 13:23:49 +04:00
* LIS331DLH spec says 1L SBs corresponds 4 G / 4096 - > 1L SB is 1000 / 1024 mG .
* Below macros defines sensitivity values for + / - 2 G . Dataout bits for
* + / - 2 G range is 12 bits so 4 bits adjustment must be done to get 12 bit
* data from 16 bit value . Currently this driver supports only 2 G range .
2012-08-22 10:30:39 +04:00
*/
# define LIS3DLH_SENSITIVITY_2G ((LIS3_ACCURACY * 1000) / 1024)
# define SHIFT_ADJ_2G 4
2010-10-22 15:57:30 +04:00
# define LIS3_DEFAULT_FUZZ_12B 3
# define LIS3_DEFAULT_FLAT_12B 3
# define LIS3_DEFAULT_FUZZ_8B 1
# define LIS3_DEFAULT_FLAT_8B 1
2009-12-15 05:01:46 +03:00
2009-04-01 02:24:32 +04:00
struct lis3lv02d lis3_dev = {
2009-04-01 02:24:26 +04:00
. misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER ( lis3_dev . misc_wait ) ,
2009-02-19 01:48:23 +03:00
} ;
2009-04-01 02:24:26 +04:00
EXPORT_SYMBOL_GPL ( lis3_dev ) ;
2008-11-13 00:27:02 +03:00
2010-10-02 01:14:25 +04:00
/* just like param_set_int() but does sanity-check so that it won't point
* over the axis array size
*/
static int param_set_axis ( const char * val , const struct kernel_param * kp )
{
int ret = param_set_int ( val , kp ) ;
if ( ! ret ) {
int val = * ( int * ) kp - > arg ;
if ( val < 0 )
val = - val ;
if ( ! val | | val > 3 )
return - EINVAL ;
}
return ret ;
}
static struct kernel_param_ops param_ops_axis = {
. set = param_set_axis ,
. get = param_get_int ,
} ;
2012-01-13 03:02:16 +04:00
# define param_check_axis(name, p) param_check_int(name, p)
2010-10-02 01:14:25 +04:00
module_param_array_named ( axes , lis3_dev . ac . as_array , axis , NULL , 0644 ) ;
MODULE_PARM_DESC ( axes , " Axis-mapping for x,y,z directions " ) ;
2009-04-01 02:24:32 +04:00
static s16 lis3lv02d_read_8 ( struct lis3lv02d * lis3 , int reg )
{
s8 lo ;
if ( lis3 - > read ( lis3 , reg , & lo ) < 0 )
return 0 ;
return lo ;
}
2009-12-15 05:01:39 +03:00
static s16 lis3lv02d_read_12 ( struct lis3lv02d * lis3 , int reg )
2009-04-01 02:24:26 +04:00
{
u8 lo , hi ;
2009-04-01 02:24:32 +04:00
lis3 - > read ( lis3 , reg - 1 , & lo ) ;
lis3 - > read ( lis3 , reg , & hi ) ;
2009-04-01 02:24:26 +04:00
/* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */
return ( s16 ) ( ( hi < < 8 ) | lo ) ;
}
2012-08-22 10:30:39 +04:00
/* 12bits for 2G range, 13 bits for 4G range and 14 bits for 8G range */
2012-09-24 13:23:49 +04:00
static s16 lis331dlh_read_data ( struct lis3lv02d * lis3 , int reg )
2012-08-22 10:30:39 +04:00
{
u8 lo , hi ;
int v ;
lis3 - > read ( lis3 , reg - 1 , & lo ) ;
lis3 - > read ( lis3 , reg , & hi ) ;
v = ( int ) ( ( hi < < 8 ) | lo ) ;
return ( s16 ) v > > lis3 - > shift_adj ;
}
2008-11-13 00:27:02 +03:00
/**
* lis3lv02d_get_axis - For the given axis , give the value converted
* @ axis : 1 , 2 , 3 - can also be negative
* @ hw_values : raw values returned by the hardware
*
* Returns the converted value .
*/
static inline int lis3lv02d_get_axis ( s8 axis , int hw_values [ 3 ] )
{
if ( axis > 0 )
return hw_values [ axis - 1 ] ;
else
return - hw_values [ - axis - 1 ] ;
}
/**
* lis3lv02d_get_xyz - Get X , Y and Z axis values from the accelerometer
2009-04-01 02:24:32 +04:00
* @ lis3 : pointer to the device struct
* @ x : where to store the X axis value
* @ y : where to store the Y axis value
* @ z : where to store the Z axis value
2008-11-13 00:27:02 +03:00
*
* Note that 40 Hz input device can eat up about 10 % CPU at 800 MHZ
*/
2009-04-01 02:24:32 +04:00
static void lis3lv02d_get_xyz ( struct lis3lv02d * lis3 , int * x , int * y , int * z )
2008-11-13 00:27:02 +03:00
{
int position [ 3 ] ;
2009-12-15 05:01:46 +03:00
int i ;
2008-11-13 00:27:02 +03:00
2010-10-22 15:57:31 +04:00
if ( lis3 - > blkread ) {
2011-11-01 04:11:05 +04:00
if ( lis3 - > whoami = = WAI_12B ) {
2010-10-22 15:57:31 +04:00
u16 data [ 3 ] ;
lis3 - > blkread ( lis3 , OUTX_L , 6 , ( u8 * ) data ) ;
for ( i = 0 ; i < 3 ; i + + )
position [ i ] = ( s16 ) le16_to_cpu ( data [ i ] ) ;
} else {
u8 data [ 5 ] ;
/* Data: x, dummy, y, dummy, z */
lis3 - > blkread ( lis3 , OUTX , 5 , data ) ;
for ( i = 0 ; i < 3 ; i + + )
position [ i ] = ( s8 ) data [ i * 2 ] ;
}
} else {
position [ 0 ] = lis3 - > read_data ( lis3 , OUTX ) ;
position [ 1 ] = lis3 - > read_data ( lis3 , OUTY ) ;
position [ 2 ] = lis3 - > read_data ( lis3 , OUTZ ) ;
}
2008-11-13 00:27:02 +03:00
2009-12-15 05:01:46 +03:00
for ( i = 0 ; i < 3 ; i + + )
position [ i ] = ( position [ i ] * lis3 - > scale ) / LIS3_ACCURACY ;
2009-06-17 02:34:14 +04:00
* x = lis3lv02d_get_axis ( lis3 - > ac . x , position ) ;
* y = lis3lv02d_get_axis ( lis3 - > ac . y , position ) ;
* z = lis3lv02d_get_axis ( lis3 - > ac . z , position ) ;
2008-11-13 00:27:02 +03:00
}
2009-12-15 05:01:41 +03:00
/* conversion btw sampling rate and the register values */
static int lis3_12_rates [ 4 ] = { 40 , 160 , 640 , 2560 } ;
static int lis3_8_rates [ 2 ] = { 100 , 400 } ;
2010-09-23 21:01:39 +04:00
static int lis3_3dc_rates [ 16 ] = { 0 , 1 , 10 , 25 , 50 , 100 , 200 , 400 , 1600 , 5000 } ;
2012-08-22 10:30:39 +04:00
static int lis3_3dlh_rates [ 4 ] = { 50 , 100 , 400 , 1000 } ;
2009-12-15 05:01:41 +03:00
2009-12-15 05:01:44 +03:00
/* ODR is Output Data Rate */
2011-11-01 04:11:05 +04:00
static int lis3lv02d_get_odr ( struct lis3lv02d * lis3 )
2009-12-15 05:01:41 +03:00
{
u8 ctrl ;
2009-12-15 05:01:44 +03:00
int shift ;
2009-12-15 05:01:41 +03:00
2011-11-01 04:11:05 +04:00
lis3 - > read ( lis3 , CTRL_REG1 , & ctrl ) ;
ctrl & = lis3 - > odr_mask ;
shift = ffs ( lis3 - > odr_mask ) - 1 ;
return lis3 - > odrs [ ( ctrl > > shift ) ] ;
2009-12-15 05:01:44 +03:00
}
2009-12-15 05:01:41 +03:00
2011-11-01 04:10:31 +04:00
static int lis3lv02d_get_pwron_wait ( struct lis3lv02d * lis3 )
{
2011-11-01 04:11:05 +04:00
int div = lis3lv02d_get_odr ( lis3 ) ;
2011-11-01 04:10:31 +04:00
if ( WARN_ONCE ( div = = 0 , " device returned spurious data " ) )
return - ENXIO ;
/* LIS3 power on delay is quite long */
msleep ( lis3 - > pwron_delay / div ) ;
return 0 ;
}
2011-11-01 04:11:05 +04:00
static int lis3lv02d_set_odr ( struct lis3lv02d * lis3 , int rate )
2009-12-15 05:01:44 +03:00
{
u8 ctrl ;
int i , len , shift ;
2010-09-23 21:01:39 +04:00
if ( ! rate )
return - EINVAL ;
2011-11-01 04:11:05 +04:00
lis3 - > read ( lis3 , CTRL_REG1 , & ctrl ) ;
ctrl & = ~ lis3 - > odr_mask ;
len = 1 < < hweight_long ( lis3 - > odr_mask ) ; /* # of possible values */
shift = ffs ( lis3 - > odr_mask ) - 1 ;
2009-12-15 05:01:44 +03:00
for ( i = 0 ; i < len ; i + + )
2011-11-01 04:11:05 +04:00
if ( lis3 - > odrs [ i ] = = rate ) {
lis3 - > write ( lis3 , CTRL_REG1 ,
2009-12-15 05:01:44 +03:00
ctrl | ( i < < shift ) ) ;
return 0 ;
}
return - EINVAL ;
2009-12-15 05:01:41 +03:00
}
2009-12-15 05:01:43 +03:00
static int lis3lv02d_selftest ( struct lis3lv02d * lis3 , s16 results [ 3 ] )
{
2010-09-23 21:01:39 +04:00
u8 ctlreg , reg ;
2009-12-15 05:01:43 +03:00
s16 x , y , z ;
u8 selftest ;
int ret ;
2010-10-22 15:57:32 +04:00
u8 ctrl_reg_data ;
unsigned char irq_cfg ;
2009-12-15 05:01:43 +03:00
mutex_lock ( & lis3 - > mutex ) ;
2010-10-22 15:57:32 +04:00
irq_cfg = lis3 - > irq_cfg ;
2011-11-01 04:11:05 +04:00
if ( lis3 - > whoami = = WAI_8B ) {
2010-10-22 15:57:32 +04:00
lis3 - > data_ready_count [ IRQ_LINE0 ] = 0 ;
lis3 - > data_ready_count [ IRQ_LINE1 ] = 0 ;
/* Change interrupt cfg to data ready for selftest */
2011-11-01 04:11:05 +04:00
atomic_inc ( & lis3 - > wake_thread ) ;
2010-10-22 15:57:32 +04:00
lis3 - > irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY ;
lis3 - > read ( lis3 , CTRL_REG3 , & ctrl_reg_data ) ;
lis3 - > write ( lis3 , CTRL_REG3 , ( ctrl_reg_data &
~ ( LIS3_IRQ1_MASK | LIS3_IRQ2_MASK ) ) |
( LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY ) ) ;
}
2012-08-22 10:30:39 +04:00
if ( ( lis3 - > whoami = = WAI_3DC ) | | ( lis3 - > whoami = = WAI_3DLH ) ) {
2010-09-23 21:01:39 +04:00
ctlreg = CTRL_REG4 ;
selftest = CTRL4_ST0 ;
} else {
ctlreg = CTRL_REG1 ;
2011-11-01 04:11:05 +04:00
if ( lis3 - > whoami = = WAI_12B )
2010-09-23 21:01:39 +04:00
selftest = CTRL1_ST ;
else
selftest = CTRL1_STP ;
}
2009-12-15 05:01:43 +03:00
2010-09-23 21:01:39 +04:00
lis3 - > read ( lis3 , ctlreg , & reg ) ;
lis3 - > write ( lis3 , ctlreg , ( reg | selftest ) ) ;
2011-11-01 04:10:31 +04:00
ret = lis3lv02d_get_pwron_wait ( lis3 ) ;
if ( ret )
goto fail ;
2009-12-15 05:01:43 +03:00
/* Read directly to avoid axis remap */
x = lis3 - > read_data ( lis3 , OUTX ) ;
y = lis3 - > read_data ( lis3 , OUTY ) ;
z = lis3 - > read_data ( lis3 , OUTZ ) ;
/* back to normal settings */
2010-09-23 21:01:39 +04:00
lis3 - > write ( lis3 , ctlreg , reg ) ;
2011-11-01 04:10:31 +04:00
ret = lis3lv02d_get_pwron_wait ( lis3 ) ;
if ( ret )
goto fail ;
2009-12-15 05:01:43 +03:00
results [ 0 ] = x - lis3 - > read_data ( lis3 , OUTX ) ;
results [ 1 ] = y - lis3 - > read_data ( lis3 , OUTY ) ;
results [ 2 ] = z - lis3 - > read_data ( lis3 , OUTZ ) ;
ret = 0 ;
2010-10-22 15:57:32 +04:00
2011-11-01 04:11:05 +04:00
if ( lis3 - > whoami = = WAI_8B ) {
2010-10-22 15:57:32 +04:00
/* Restore original interrupt configuration */
2011-11-01 04:11:05 +04:00
atomic_dec ( & lis3 - > wake_thread ) ;
2010-10-22 15:57:32 +04:00
lis3 - > write ( lis3 , CTRL_REG3 , ctrl_reg_data ) ;
lis3 - > irq_cfg = irq_cfg ;
if ( ( irq_cfg & LIS3_IRQ1_MASK ) & &
lis3 - > data_ready_count [ IRQ_LINE0 ] < 2 ) {
ret = SELFTEST_IRQ ;
goto fail ;
}
if ( ( irq_cfg & LIS3_IRQ2_MASK ) & &
lis3 - > data_ready_count [ IRQ_LINE1 ] < 2 ) {
ret = SELFTEST_IRQ ;
goto fail ;
}
}
2009-12-15 05:01:43 +03:00
if ( lis3 - > pdata ) {
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
/* Check against selftest acceptance limits */
if ( ( results [ i ] < lis3 - > pdata - > st_min_limits [ i ] ) | |
( results [ i ] > lis3 - > pdata - > st_max_limits [ i ] ) ) {
2010-10-22 15:57:32 +04:00
ret = SELFTEST_FAIL ;
2009-12-15 05:01:43 +03:00
goto fail ;
}
}
}
/* test passed */
fail :
mutex_unlock ( & lis3 - > mutex ) ;
return ret ;
}
2010-10-22 15:57:24 +04:00
/*
* Order of registers in the list affects to order of the restore process .
* Perhaps it is a good idea to set interrupt enable register as a last one
* after all other configurations
*/
static u8 lis3_wai8_regs [ ] = { FF_WU_CFG_1 , FF_WU_THS_1 , FF_WU_DURATION_1 ,
FF_WU_CFG_2 , FF_WU_THS_2 , FF_WU_DURATION_2 ,
CLICK_CFG , CLICK_SRC , CLICK_THSY_X , CLICK_THSZ ,
CLICK_TIMELIMIT , CLICK_LATENCY , CLICK_WINDOW ,
CTRL_REG1 , CTRL_REG2 , CTRL_REG3 } ;
static u8 lis3_wai12_regs [ ] = { FF_WU_CFG , FF_WU_THS_L , FF_WU_THS_H ,
FF_WU_DURATION , DD_CFG , DD_THSI_L , DD_THSI_H ,
DD_THSE_L , DD_THSE_H ,
CTRL_REG1 , CTRL_REG3 , CTRL_REG2 } ;
static inline void lis3_context_save ( struct lis3lv02d * lis3 )
{
int i ;
for ( i = 0 ; i < lis3 - > regs_size ; i + + )
lis3 - > read ( lis3 , lis3 - > regs [ i ] , & lis3 - > reg_cache [ i ] ) ;
lis3 - > regs_stored = true ;
}
static inline void lis3_context_restore ( struct lis3lv02d * lis3 )
{
int i ;
if ( lis3 - > regs_stored )
for ( i = 0 ; i < lis3 - > regs_size ; i + + )
lis3 - > write ( lis3 , lis3 - > regs [ i ] , lis3 - > reg_cache [ i ] ) ;
}
2009-04-01 02:24:32 +04:00
void lis3lv02d_poweroff ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2010-10-22 15:57:24 +04:00
if ( lis3 - > reg_ctrl )
lis3_context_save ( lis3 ) ;
2009-06-17 02:34:14 +04:00
/* disable X,Y,Z axis and power down */
lis3 - > write ( lis3 , CTRL_REG1 , 0x00 ) ;
2010-10-22 15:57:24 +04:00
if ( lis3 - > reg_ctrl )
lis3 - > reg_ctrl ( lis3 , LIS3_REG_OFF ) ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_poweroff ) ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:10:31 +04:00
int lis3lv02d_poweron ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2011-11-01 04:10:31 +04:00
int err ;
2009-06-17 02:34:14 +04:00
u8 reg ;
2008-11-13 00:27:02 +03:00
2009-06-17 02:34:14 +04:00
lis3 - > init ( lis3 ) ;
2008-11-13 00:27:02 +03:00
2009-06-17 02:34:14 +04:00
/*
* Common configuration
2009-12-15 05:01:40 +03:00
* BDU : ( 12 bits sensors only ) LSB and MSB values are not updated until
* both have been read . So the value read will always be correct .
2010-10-22 15:57:27 +04:00
* Set BOOT bit to refresh factory tuning values .
2009-06-17 02:34:14 +04:00
*/
2011-10-04 05:09:14 +04:00
if ( lis3 - > pdata ) {
lis3 - > read ( lis3 , CTRL_REG2 , & reg ) ;
if ( lis3 - > whoami = = WAI_12B )
reg | = CTRL2_BDU | CTRL2_BOOT ;
2012-08-22 10:30:39 +04:00
else if ( lis3 - > whoami = = WAI_3DLH )
reg | = CTRL2_BOOT_3DLH ;
2011-10-04 05:09:14 +04:00
else
reg | = CTRL2_BOOT_8B ;
lis3 - > write ( lis3 , CTRL_REG2 , reg ) ;
2012-08-22 10:30:39 +04:00
if ( lis3 - > whoami = = WAI_3DLH ) {
lis3 - > read ( lis3 , CTRL_REG4 , & reg ) ;
reg | = CTRL4_BDU ;
lis3 - > write ( lis3 , CTRL_REG4 , reg ) ;
}
2011-10-04 05:09:14 +04:00
}
2010-10-22 15:57:27 +04:00
2011-11-01 04:10:31 +04:00
err = lis3lv02d_get_pwron_wait ( lis3 ) ;
if ( err )
return err ;
2010-10-22 15:57:27 +04:00
2010-10-22 15:57:24 +04:00
if ( lis3 - > reg_ctrl )
lis3_context_restore ( lis3 ) ;
2011-11-01 04:10:31 +04:00
return 0 ;
2008-11-13 00:27:02 +03:00
}
2009-06-17 02:34:14 +04:00
EXPORT_SYMBOL_GPL ( lis3lv02d_poweron ) ;
2008-11-13 00:27:02 +03:00
2010-05-25 01:33:37 +04:00
static void lis3lv02d_joystick_poll ( struct input_polled_dev * pidev )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = pidev - > private ;
2010-05-25 01:33:37 +04:00
int x , y , z ;
2011-11-01 04:11:05 +04:00
mutex_lock ( & lis3 - > mutex ) ;
lis3lv02d_get_xyz ( lis3 , & x , & y , & z ) ;
2010-05-25 01:33:37 +04:00
input_report_abs ( pidev - > input , ABS_X , x ) ;
input_report_abs ( pidev - > input , ABS_Y , y ) ;
input_report_abs ( pidev - > input , ABS_Z , z ) ;
input_sync ( pidev - > input ) ;
2011-11-01 04:11:05 +04:00
mutex_unlock ( & lis3 - > mutex ) ;
2010-05-25 01:33:37 +04:00
}
2010-10-22 15:57:23 +04:00
static void lis3lv02d_joystick_open ( struct input_polled_dev * pidev )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = pidev - > private ;
2010-10-22 15:57:25 +04:00
2011-11-01 04:11:05 +04:00
if ( lis3 - > pm_dev )
pm_runtime_get_sync ( lis3 - > pm_dev ) ;
if ( lis3 - > pdata & & lis3 - > whoami = = WAI_8B & & lis3 - > idev )
atomic_set ( & lis3 - > wake_thread , 1 ) ;
2010-10-22 15:57:26 +04:00
/*
* Update coordinates for the case where poll interval is 0 and
* the chip in running purely under interrupt control
*/
lis3lv02d_joystick_poll ( pidev ) ;
2010-10-22 15:57:23 +04:00
}
static void lis3lv02d_joystick_close ( struct input_polled_dev * pidev )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = pidev - > private ;
atomic_set ( & lis3 - > wake_thread , 0 ) ;
if ( lis3 - > pm_dev )
pm_runtime_put ( lis3 - > pm_dev ) ;
2010-10-22 15:57:23 +04:00
}
2011-11-01 04:11:05 +04:00
static irqreturn_t lis302dl_interrupt ( int irq , void * data )
2009-02-19 01:48:23 +03:00
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = data ;
if ( ! test_bit ( 0 , & lis3 - > misc_opened ) )
2010-05-25 01:33:36 +04:00
goto out ;
2009-02-19 01:48:23 +03:00
/*
* Be careful : on some HP laptops the bios force DD when on battery and
* the lid is closed . This leads to interrupts as soon as a little move
* is done .
*/
2011-11-01 04:11:05 +04:00
atomic_inc ( & lis3 - > count ) ;
2009-02-19 01:48:23 +03:00
2011-11-01 04:11:05 +04:00
wake_up_interruptible ( & lis3 - > misc_wait ) ;
kill_fasync ( & lis3 - > async_queue , SIGIO , POLL_IN ) ;
2010-05-25 01:33:36 +04:00
out :
2011-11-01 04:11:05 +04:00
if ( atomic_read ( & lis3 - > wake_thread ) )
2010-05-25 01:33:36 +04:00
return IRQ_WAKE_THREAD ;
2009-02-19 01:48:23 +03:00
return IRQ_HANDLED ;
}
2010-05-25 01:33:37 +04:00
static void lis302dl_interrupt_handle_click ( struct lis3lv02d * lis3 )
{
struct input_dev * dev = lis3 - > idev - > input ;
u8 click_src ;
mutex_lock ( & lis3 - > mutex ) ;
lis3 - > read ( lis3 , CLICK_SRC , & click_src ) ;
if ( click_src & CLICK_SINGLE_X ) {
input_report_key ( dev , lis3 - > mapped_btns [ 0 ] , 1 ) ;
input_report_key ( dev , lis3 - > mapped_btns [ 0 ] , 0 ) ;
}
if ( click_src & CLICK_SINGLE_Y ) {
input_report_key ( dev , lis3 - > mapped_btns [ 1 ] , 1 ) ;
input_report_key ( dev , lis3 - > mapped_btns [ 1 ] , 0 ) ;
}
if ( click_src & CLICK_SINGLE_Z ) {
input_report_key ( dev , lis3 - > mapped_btns [ 2 ] , 1 ) ;
input_report_key ( dev , lis3 - > mapped_btns [ 2 ] , 0 ) ;
}
input_sync ( dev ) ;
mutex_unlock ( & lis3 - > mutex ) ;
}
2010-10-22 15:57:32 +04:00
static inline void lis302dl_data_ready ( struct lis3lv02d * lis3 , int index )
2009-02-19 01:48:23 +03:00
{
2010-10-22 15:57:32 +04:00
int dummy ;
/* Dummy read to ack interrupt */
lis3lv02d_get_xyz ( lis3 , & dummy , & dummy , & dummy ) ;
lis3 - > data_ready_count [ index ] + + ;
}
2010-05-25 01:33:37 +04:00
2010-10-22 15:57:32 +04:00
static irqreturn_t lis302dl_interrupt_thread1_8b ( int irq , void * data )
{
2010-05-25 01:33:37 +04:00
struct lis3lv02d * lis3 = data ;
2010-10-22 15:57:32 +04:00
u8 irq_cfg = lis3 - > irq_cfg & LIS3_IRQ1_MASK ;
2010-05-25 01:33:37 +04:00
2010-10-22 15:57:32 +04:00
if ( irq_cfg = = LIS3_IRQ1_CLICK )
2010-05-25 01:33:37 +04:00
lis302dl_interrupt_handle_click ( lis3 ) ;
2010-10-22 15:57:32 +04:00
else if ( unlikely ( irq_cfg = = LIS3_IRQ1_DATA_READY ) )
lis302dl_data_ready ( lis3 , IRQ_LINE0 ) ;
2010-05-25 01:33:37 +04:00
else
2010-10-22 15:57:25 +04:00
lis3lv02d_joystick_poll ( lis3 - > idev ) ;
2010-05-25 01:33:37 +04:00
2010-05-25 01:33:36 +04:00
return IRQ_HANDLED ;
}
2009-02-19 01:48:23 +03:00
2010-05-25 01:33:36 +04:00
static irqreturn_t lis302dl_interrupt_thread2_8b ( int irq , void * data )
{
2010-05-25 01:33:37 +04:00
struct lis3lv02d * lis3 = data ;
2010-10-22 15:57:32 +04:00
u8 irq_cfg = lis3 - > irq_cfg & LIS3_IRQ2_MASK ;
2010-05-25 01:33:37 +04:00
2010-10-22 15:57:32 +04:00
if ( irq_cfg = = LIS3_IRQ2_CLICK )
2010-05-25 01:33:37 +04:00
lis302dl_interrupt_handle_click ( lis3 ) ;
2010-10-22 15:57:32 +04:00
else if ( unlikely ( irq_cfg = = LIS3_IRQ2_DATA_READY ) )
lis302dl_data_ready ( lis3 , IRQ_LINE1 ) ;
2010-05-25 01:33:37 +04:00
else
2010-10-22 15:57:25 +04:00
lis3lv02d_joystick_poll ( lis3 - > idev ) ;
2010-05-25 01:33:37 +04:00
2010-05-25 01:33:36 +04:00
return IRQ_HANDLED ;
}
static int lis3lv02d_misc_open ( struct inode * inode , struct file * file )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = container_of ( file - > private_data ,
struct lis3lv02d , miscdev ) ;
if ( test_and_set_bit ( 0 , & lis3 - > misc_opened ) )
2009-02-19 01:48:23 +03:00
return - EBUSY ; /* already open */
2011-11-01 04:11:05 +04:00
if ( lis3 - > pm_dev )
pm_runtime_get_sync ( lis3 - > pm_dev ) ;
2010-10-22 15:57:23 +04:00
2011-11-01 04:11:05 +04:00
atomic_set ( & lis3 - > count , 0 ) ;
2009-02-19 01:48:23 +03:00
return 0 ;
}
static int lis3lv02d_misc_release ( struct inode * inode , struct file * file )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = container_of ( file - > private_data ,
struct lis3lv02d , miscdev ) ;
clear_bit ( 0 , & lis3 - > misc_opened ) ; /* release the device */
if ( lis3 - > pm_dev )
pm_runtime_put ( lis3 - > pm_dev ) ;
2009-02-19 01:48:23 +03:00
return 0 ;
}
static ssize_t lis3lv02d_misc_read ( struct file * file , char __user * buf ,
size_t count , loff_t * pos )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = container_of ( file - > private_data ,
struct lis3lv02d , miscdev ) ;
2009-02-19 01:48:23 +03:00
DECLARE_WAITQUEUE ( wait , current ) ;
u32 data ;
unsigned char byte_data ;
ssize_t retval = 1 ;
if ( count < 1 )
return - EINVAL ;
2011-11-01 04:11:05 +04:00
add_wait_queue ( & lis3 - > misc_wait , & wait ) ;
2009-02-19 01:48:23 +03:00
while ( true ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2011-11-01 04:11:05 +04:00
data = atomic_xchg ( & lis3 - > count , 0 ) ;
2009-02-19 01:48:23 +03:00
if ( data )
break ;
if ( file - > f_flags & O_NONBLOCK ) {
retval = - EAGAIN ;
goto out ;
}
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
goto out ;
}
schedule ( ) ;
}
if ( data < 255 )
byte_data = data ;
else
byte_data = 255 ;
/* make sure we are not going into copy_to_user() with
* TASK_INTERRUPTIBLE state */
set_current_state ( TASK_RUNNING ) ;
if ( copy_to_user ( buf , & byte_data , sizeof ( byte_data ) ) )
retval = - EFAULT ;
out :
__set_current_state ( TASK_RUNNING ) ;
2011-11-01 04:11:05 +04:00
remove_wait_queue ( & lis3 - > misc_wait , & wait ) ;
2009-02-19 01:48:23 +03:00
return retval ;
}
static unsigned int lis3lv02d_misc_poll ( struct file * file , poll_table * wait )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = container_of ( file - > private_data ,
struct lis3lv02d , miscdev ) ;
poll_wait ( file , & lis3 - > misc_wait , wait ) ;
if ( atomic_read ( & lis3 - > count ) )
2009-02-19 01:48:23 +03:00
return POLLIN | POLLRDNORM ;
return 0 ;
}
static int lis3lv02d_misc_fasync ( int fd , struct file * file , int on )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = container_of ( file - > private_data ,
struct lis3lv02d , miscdev ) ;
return fasync_helper ( fd , file , on , & lis3 - > async_queue ) ;
2009-02-19 01:48:23 +03:00
}
static const struct file_operations lis3lv02d_misc_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = lis3lv02d_misc_read ,
. open = lis3lv02d_misc_open ,
. release = lis3lv02d_misc_release ,
. poll = lis3lv02d_misc_poll ,
. fasync = lis3lv02d_misc_fasync ,
} ;
2011-11-01 04:11:02 +04:00
int lis3lv02d_joystick_enable ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-06-17 02:34:15 +04:00
struct input_dev * input_dev ;
2008-11-13 00:27:02 +03:00
int err ;
2009-12-15 05:01:46 +03:00
int max_val , fuzz , flat ;
2010-05-25 01:33:37 +04:00
int btns [ ] = { BTN_X , BTN_Y , BTN_Z } ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:11:05 +04:00
if ( lis3 - > idev )
2008-11-13 00:27:02 +03:00
return - EINVAL ;
2011-11-01 04:11:05 +04:00
lis3 - > idev = input_allocate_polled_device ( ) ;
if ( ! lis3 - > idev )
2008-11-13 00:27:02 +03:00
return - ENOMEM ;
2011-11-01 04:11:05 +04:00
lis3 - > idev - > poll = lis3lv02d_joystick_poll ;
lis3 - > idev - > open = lis3lv02d_joystick_open ;
lis3 - > idev - > close = lis3lv02d_joystick_close ;
lis3 - > idev - > poll_interval = MDPS_POLL_INTERVAL ;
lis3 - > idev - > poll_interval_min = MDPS_POLL_MIN ;
lis3 - > idev - > poll_interval_max = MDPS_POLL_MAX ;
lis3 - > idev - > private = lis3 ;
input_dev = lis3 - > idev - > input ;
2009-06-17 02:34:15 +04:00
input_dev - > name = " ST LIS3LV02DL Accelerometer " ;
input_dev - > phys = DRIVER_NAME " /input0 " ;
input_dev - > id . bustype = BUS_HOST ;
input_dev - > id . vendor = 0 ;
2011-11-01 04:11:05 +04:00
input_dev - > dev . parent = & lis3 - > pdev - > dev ;
2008-11-13 00:27:02 +03:00
2009-06-17 02:34:15 +04:00
set_bit ( EV_ABS , input_dev - > evbit ) ;
2011-11-01 04:11:05 +04:00
max_val = ( lis3 - > mdps_max_val * lis3 - > scale ) / LIS3_ACCURACY ;
if ( lis3 - > whoami = = WAI_12B ) {
2010-10-22 15:57:30 +04:00
fuzz = LIS3_DEFAULT_FUZZ_12B ;
flat = LIS3_DEFAULT_FLAT_12B ;
} else {
fuzz = LIS3_DEFAULT_FUZZ_8B ;
flat = LIS3_DEFAULT_FLAT_8B ;
}
2011-11-01 04:11:05 +04:00
fuzz = ( fuzz * lis3 - > scale ) / LIS3_ACCURACY ;
flat = ( flat * lis3 - > scale ) / LIS3_ACCURACY ;
2010-10-22 15:57:30 +04:00
2009-12-15 05:01:46 +03:00
input_set_abs_params ( input_dev , ABS_X , - max_val , max_val , fuzz , flat ) ;
input_set_abs_params ( input_dev , ABS_Y , - max_val , max_val , fuzz , flat ) ;
input_set_abs_params ( input_dev , ABS_Z , - max_val , max_val , fuzz , flat ) ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:11:05 +04:00
lis3 - > mapped_btns [ 0 ] = lis3lv02d_get_axis ( abs ( lis3 - > ac . x ) , btns ) ;
lis3 - > mapped_btns [ 1 ] = lis3lv02d_get_axis ( abs ( lis3 - > ac . y ) , btns ) ;
lis3 - > mapped_btns [ 2 ] = lis3lv02d_get_axis ( abs ( lis3 - > ac . z ) , btns ) ;
2010-05-25 01:33:37 +04:00
2011-11-01 04:11:05 +04:00
err = input_register_polled_device ( lis3 - > idev ) ;
2008-11-13 00:27:02 +03:00
if ( err ) {
2011-11-01 04:11:05 +04:00
input_free_polled_device ( lis3 - > idev ) ;
lis3 - > idev = NULL ;
2008-11-13 00:27:02 +03:00
}
return err ;
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_joystick_enable ) ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:11:02 +04:00
void lis3lv02d_joystick_disable ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2011-11-01 04:11:05 +04:00
if ( lis3 - > irq )
free_irq ( lis3 - > irq , lis3 ) ;
if ( lis3 - > pdata & & lis3 - > pdata - > irq2 )
free_irq ( lis3 - > pdata - > irq2 , lis3 ) ;
2010-05-25 01:33:36 +04:00
2011-11-01 04:11:05 +04:00
if ( ! lis3 - > idev )
2008-11-13 00:27:02 +03:00
return ;
2011-11-01 04:11:05 +04:00
if ( lis3 - > irq )
misc_deregister ( & lis3 - > miscdev ) ;
input_unregister_polled_device ( lis3 - > idev ) ;
input_free_polled_device ( lis3 - > idev ) ;
lis3 - > idev = NULL ;
2008-11-13 00:27:02 +03:00
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_joystick_disable ) ;
2008-11-13 00:27:02 +03:00
/* Sysfs stuff */
2010-10-22 15:57:23 +04:00
static void lis3lv02d_sysfs_poweron ( struct lis3lv02d * lis3 )
{
/*
* SYSFS functions are fast visitors so put - call
* immediately after the get - call . However , keep
* chip running for a while and schedule delayed
* suspend . This way periodic sysfs calls doesn ' t
* suffer from relatively long power up time .
*/
if ( lis3 - > pm_dev ) {
pm_runtime_get_sync ( lis3 - > pm_dev ) ;
pm_runtime_put_noidle ( lis3 - > pm_dev ) ;
pm_schedule_suspend ( lis3 - > pm_dev , LIS3_SYSFS_POWERDOWN_DELAY ) ;
}
}
2009-12-15 05:01:43 +03:00
static ssize_t lis3lv02d_selftest_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = dev_get_drvdata ( dev ) ;
2009-12-15 05:01:43 +03:00
s16 values [ 3 ] ;
2010-10-22 15:57:32 +04:00
static const char ok [ ] = " OK " ;
static const char fail [ ] = " FAIL " ;
static const char irq [ ] = " FAIL_IRQ " ;
const char * res ;
2011-11-01 04:11:05 +04:00
lis3lv02d_sysfs_poweron ( lis3 ) ;
switch ( lis3lv02d_selftest ( lis3 , values ) ) {
2010-10-22 15:57:32 +04:00
case SELFTEST_FAIL :
res = fail ;
break ;
case SELFTEST_IRQ :
res = irq ;
break ;
case SELFTEST_OK :
default :
res = ok ;
break ;
}
return sprintf ( buf , " %s %d %d %d \n " , res ,
2009-12-15 05:01:43 +03:00
values [ 0 ] , values [ 1 ] , values [ 2 ] ) ;
}
2008-11-13 00:27:02 +03:00
static ssize_t lis3lv02d_position_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = dev_get_drvdata ( dev ) ;
2008-11-13 00:27:02 +03:00
int x , y , z ;
2011-11-01 04:11:05 +04:00
lis3lv02d_sysfs_poweron ( lis3 ) ;
mutex_lock ( & lis3 - > mutex ) ;
lis3lv02d_get_xyz ( lis3 , & x , & y , & z ) ;
mutex_unlock ( & lis3 - > mutex ) ;
2008-11-13 00:27:02 +03:00
return sprintf ( buf , " (%d,%d,%d) \ n " , x, y, z) ;
}
static ssize_t lis3lv02d_rate_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = dev_get_drvdata ( dev ) ;
lis3lv02d_sysfs_poweron ( lis3 ) ;
return sprintf ( buf , " %d \n " , lis3lv02d_get_odr ( lis3 ) ) ;
2008-11-13 00:27:02 +03:00
}
2009-12-15 05:01:44 +03:00
static ssize_t lis3lv02d_rate_set ( struct device * dev ,
struct device_attribute * attr , const char * buf ,
size_t count )
{
2011-11-01 04:11:05 +04:00
struct lis3lv02d * lis3 = dev_get_drvdata ( dev ) ;
2009-12-15 05:01:44 +03:00
unsigned long rate ;
2013-06-04 08:15:16 +04:00
int ret ;
2009-12-15 05:01:44 +03:00
2013-06-04 08:15:16 +04:00
ret = kstrtoul ( buf , 0 , & rate ) ;
if ( ret )
return ret ;
2009-12-15 05:01:44 +03:00
2011-11-01 04:11:05 +04:00
lis3lv02d_sysfs_poweron ( lis3 ) ;
if ( lis3lv02d_set_odr ( lis3 , rate ) )
2009-12-15 05:01:44 +03:00
return - EINVAL ;
return count ;
}
2009-12-15 05:01:43 +03:00
static DEVICE_ATTR ( selftest , S_IRUSR , lis3lv02d_selftest_show , NULL ) ;
2008-11-13 00:27:02 +03:00
static DEVICE_ATTR ( position , S_IRUGO , lis3lv02d_position_show , NULL ) ;
2009-12-15 05:01:44 +03:00
static DEVICE_ATTR ( rate , S_IRUGO | S_IWUSR , lis3lv02d_rate_show ,
lis3lv02d_rate_set ) ;
2008-11-13 00:27:02 +03:00
static struct attribute * lis3lv02d_attributes [ ] = {
2009-12-15 05:01:43 +03:00
& dev_attr_selftest . attr ,
2008-11-13 00:27:02 +03:00
& dev_attr_position . attr ,
& dev_attr_rate . attr ,
NULL
} ;
static struct attribute_group lis3lv02d_attribute_group = {
. attrs = lis3lv02d_attributes
} ;
2009-01-10 03:41:01 +03:00
2009-04-01 02:24:32 +04:00
static int lis3lv02d_add_fs ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-06-17 02:34:14 +04:00
lis3 - > pdev = platform_device_register_simple ( DRIVER_NAME , - 1 , NULL , 0 ) ;
if ( IS_ERR ( lis3 - > pdev ) )
return PTR_ERR ( lis3 - > pdev ) ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:11:05 +04:00
platform_set_drvdata ( lis3 - > pdev , lis3 ) ;
2009-06-17 02:34:14 +04:00
return sysfs_create_group ( & lis3 - > pdev - > dev . kobj , & lis3lv02d_attribute_group ) ;
2008-11-13 00:27:02 +03:00
}
2009-06-17 02:34:14 +04:00
int lis3lv02d_remove_fs ( struct lis3lv02d * lis3 )
2008-11-13 00:27:02 +03:00
{
2009-06-17 02:34:14 +04:00
sysfs_remove_group ( & lis3 - > pdev - > dev . kobj , & lis3lv02d_attribute_group ) ;
platform_device_unregister ( lis3 - > pdev ) ;
2010-10-22 15:57:23 +04:00
if ( lis3 - > pm_dev ) {
/* Barrier after the sysfs remove */
pm_runtime_barrier ( lis3 - > pm_dev ) ;
/* SYSFS may have left chip running. Turn off if necessary */
if ( ! pm_runtime_suspended ( lis3 - > pm_dev ) )
2011-11-01 04:11:05 +04:00
lis3lv02d_poweroff ( lis3 ) ;
2010-10-22 15:57:23 +04:00
pm_runtime_disable ( lis3 - > pm_dev ) ;
pm_runtime_set_suspended ( lis3 - > pm_dev ) ;
}
2010-10-22 15:57:24 +04:00
kfree ( lis3 - > reg_cache ) ;
2008-11-13 00:27:02 +03:00
return 0 ;
}
2009-01-10 03:41:01 +03:00
EXPORT_SYMBOL_GPL ( lis3lv02d_remove_fs ) ;
2008-11-13 00:27:02 +03:00
2011-11-01 04:10:58 +04:00
static void lis3lv02d_8b_configure ( struct lis3lv02d * lis3 ,
2010-05-25 01:33:34 +04:00
struct lis3lv02d_platform_data * p )
{
2010-05-25 01:33:36 +04:00
int err ;
2010-05-25 01:33:35 +04:00
int ctrl2 = p - > hipass_ctrl ;
2010-05-25 01:33:34 +04:00
if ( p - > click_flags ) {
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , CLICK_CFG , p - > click_flags ) ;
lis3 - > write ( lis3 , CLICK_TIMELIMIT , p - > click_time_limit ) ;
lis3 - > write ( lis3 , CLICK_LATENCY , p - > click_latency ) ;
lis3 - > write ( lis3 , CLICK_WINDOW , p - > click_window ) ;
lis3 - > write ( lis3 , CLICK_THSZ , p - > click_thresh_z & 0xf ) ;
lis3 - > write ( lis3 , CLICK_THSY_X ,
2010-05-25 01:33:34 +04:00
( p - > click_thresh_x & 0xf ) |
( p - > click_thresh_y < < 4 ) ) ;
2010-05-25 01:33:37 +04:00
2011-11-01 04:10:58 +04:00
if ( lis3 - > idev ) {
2011-11-01 04:11:05 +04:00
struct input_dev * input_dev = lis3 - > idev - > input ;
2010-05-25 01:33:37 +04:00
input_set_capability ( input_dev , EV_KEY , BTN_X ) ;
input_set_capability ( input_dev , EV_KEY , BTN_Y ) ;
input_set_capability ( input_dev , EV_KEY , BTN_Z ) ;
}
2010-05-25 01:33:34 +04:00
}
if ( p - > wakeup_flags ) {
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , FF_WU_CFG_1 , p - > wakeup_flags ) ;
lis3 - > write ( lis3 , FF_WU_THS_1 , p - > wakeup_thresh & 0x7f ) ;
2010-10-22 15:57:29 +04:00
/* pdata value + 1 to keep this backward compatible*/
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , FF_WU_DURATION_1 , p - > duration1 + 1 ) ;
2010-05-25 01:33:35 +04:00
ctrl2 ^ = HP_FF_WU1 ; /* Xor to keep compatible with old pdata*/
}
if ( p - > wakeup_flags2 ) {
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , FF_WU_CFG_2 , p - > wakeup_flags2 ) ;
lis3 - > write ( lis3 , FF_WU_THS_2 , p - > wakeup_thresh2 & 0x7f ) ;
2010-10-22 15:57:29 +04:00
/* pdata value + 1 to keep this backward compatible*/
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , FF_WU_DURATION_2 , p - > duration2 + 1 ) ;
2010-05-25 01:33:35 +04:00
ctrl2 ^ = HP_FF_WU2 ; /* Xor to keep compatible with old pdata*/
2010-05-25 01:33:34 +04:00
}
2010-05-25 01:33:35 +04:00
/* Configure hipass filters */
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , CTRL_REG2 , ctrl2 ) ;
2010-05-25 01:33:36 +04:00
if ( p - > irq2 ) {
err = request_threaded_irq ( p - > irq2 ,
NULL ,
lis302dl_interrupt_thread2_8b ,
2010-10-22 15:57:29 +04:00
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
( p - > irq_flags2 & IRQF_TRIGGER_MASK ) ,
2011-11-01 04:11:05 +04:00
DRIVER_NAME , lis3 ) ;
2010-05-25 01:33:36 +04:00
if ( err < 0 )
2010-10-20 10:51:40 +04:00
pr_err ( " No second IRQ. Limited functionality \n " ) ;
2010-05-25 01:33:36 +04:00
}
2010-05-25 01:33:34 +04:00
}
2012-09-27 00:58:16 +04:00
# ifdef CONFIG_OF
2012-09-27 00:58:21 +04:00
int lis3lv02d_init_dt ( struct lis3lv02d * lis3 )
2012-09-27 00:58:16 +04:00
{
struct lis3lv02d_platform_data * pdata ;
struct device_node * np = lis3 - > of_node ;
u32 val ;
2015-03-27 17:39:43 +03:00
s32 sval ;
2012-09-27 00:58:16 +04:00
if ( ! lis3 - > of_node )
return 0 ;
pdata = kzalloc ( sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
if ( of_get_property ( np , " st,click-single-x " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_SINGLE_X ;
if ( of_get_property ( np , " st,click-double-x " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_DOUBLE_X ;
if ( of_get_property ( np , " st,click-single-y " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_SINGLE_Y ;
if ( of_get_property ( np , " st,click-double-y " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_DOUBLE_Y ;
if ( of_get_property ( np , " st,click-single-z " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_SINGLE_Z ;
if ( of_get_property ( np , " st,click-double-z " , NULL ) )
pdata - > click_flags | = LIS3_CLICK_DOUBLE_Z ;
if ( ! of_property_read_u32 ( np , " st,click-threshold-x " , & val ) )
pdata - > click_thresh_x = val ;
if ( ! of_property_read_u32 ( np , " st,click-threshold-y " , & val ) )
pdata - > click_thresh_y = val ;
if ( ! of_property_read_u32 ( np , " st,click-threshold-z " , & val ) )
pdata - > click_thresh_z = val ;
if ( ! of_property_read_u32 ( np , " st,click-time-limit " , & val ) )
pdata - > click_time_limit = val ;
if ( ! of_property_read_u32 ( np , " st,click-latency " , & val ) )
pdata - > click_latency = val ;
if ( ! of_property_read_u32 ( np , " st,click-window " , & val ) )
pdata - > click_window = val ;
if ( of_get_property ( np , " st,irq1-disable " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ1_DISABLE ;
if ( of_get_property ( np , " st,irq1-ff-wu-1 " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ1_FF_WU_1 ;
if ( of_get_property ( np , " st,irq1-ff-wu-2 " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ1_FF_WU_2 ;
if ( of_get_property ( np , " st,irq1-data-ready " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ1_DATA_READY ;
if ( of_get_property ( np , " st,irq1-click " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ1_CLICK ;
if ( of_get_property ( np , " st,irq2-disable " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ2_DISABLE ;
if ( of_get_property ( np , " st,irq2-ff-wu-1 " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ2_FF_WU_1 ;
if ( of_get_property ( np , " st,irq2-ff-wu-2 " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ2_FF_WU_2 ;
if ( of_get_property ( np , " st,irq2-data-ready " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ2_DATA_READY ;
if ( of_get_property ( np , " st,irq2-click " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ2_CLICK ;
if ( of_get_property ( np , " st,irq-open-drain " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ_OPEN_DRAIN ;
if ( of_get_property ( np , " st,irq-active-low " , NULL ) )
pdata - > irq_cfg | = LIS3_IRQ_ACTIVE_LOW ;
if ( ! of_property_read_u32 ( np , " st,wu-duration-1 " , & val ) )
pdata - > duration1 = val ;
if ( ! of_property_read_u32 ( np , " st,wu-duration-2 " , & val ) )
pdata - > duration2 = val ;
if ( of_get_property ( np , " st,wakeup-x-lo " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_X_LO ;
if ( of_get_property ( np , " st,wakeup-x-hi " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_X_HI ;
if ( of_get_property ( np , " st,wakeup-y-lo " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_Y_LO ;
if ( of_get_property ( np , " st,wakeup-y-hi " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_Y_HI ;
if ( of_get_property ( np , " st,wakeup-z-lo " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_Z_LO ;
if ( of_get_property ( np , " st,wakeup-z-hi " , NULL ) )
pdata - > wakeup_flags | = LIS3_WAKEUP_Z_HI ;
2015-03-27 17:39:44 +03:00
if ( of_get_property ( np , " st,wakeup-threshold " , & val ) )
pdata - > wakeup_thresh = val ;
if ( of_get_property ( np , " st,wakeup2-x-lo " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_X_LO ;
if ( of_get_property ( np , " st,wakeup2-x-hi " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_X_HI ;
if ( of_get_property ( np , " st,wakeup2-y-lo " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_Y_LO ;
if ( of_get_property ( np , " st,wakeup2-y-hi " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_Y_HI ;
if ( of_get_property ( np , " st,wakeup2-z-lo " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_Z_LO ;
if ( of_get_property ( np , " st,wakeup2-z-hi " , NULL ) )
pdata - > wakeup_flags2 | = LIS3_WAKEUP_Z_HI ;
if ( of_get_property ( np , " st,wakeup2-threshold " , & val ) )
pdata - > wakeup_thresh2 = val ;
2012-09-27 00:58:16 +04:00
if ( ! of_property_read_u32 ( np , " st,highpass-cutoff-hz " , & val ) ) {
switch ( val ) {
case 1 :
pdata - > hipass_ctrl = LIS3_HIPASS_CUTFF_1HZ ;
break ;
case 2 :
pdata - > hipass_ctrl = LIS3_HIPASS_CUTFF_2HZ ;
break ;
case 4 :
pdata - > hipass_ctrl = LIS3_HIPASS_CUTFF_4HZ ;
break ;
case 8 :
pdata - > hipass_ctrl = LIS3_HIPASS_CUTFF_8HZ ;
break ;
}
}
if ( of_get_property ( np , " st,hipass1-disable " , NULL ) )
pdata - > hipass_ctrl | = LIS3_HIPASS1_DISABLE ;
if ( of_get_property ( np , " st,hipass2-disable " , NULL ) )
pdata - > hipass_ctrl | = LIS3_HIPASS2_DISABLE ;
2015-03-27 17:39:43 +03:00
if ( of_property_read_s32 ( np , " st,axis-x " , & sval ) = = 0 )
pdata - > axis_x = sval ;
if ( of_property_read_s32 ( np , " st,axis-y " , & sval ) = = 0 )
pdata - > axis_y = sval ;
if ( of_property_read_s32 ( np , " st,axis-z " , & sval ) = = 0 )
pdata - > axis_z = sval ;
2012-09-27 00:58:16 +04:00
if ( of_get_property ( np , " st,default-rate " , NULL ) )
pdata - > default_rate = val ;
2015-03-27 17:39:43 +03:00
if ( of_property_read_s32 ( np , " st,min-limit-x " , & sval ) = = 0 )
pdata - > st_min_limits [ 0 ] = sval ;
if ( of_property_read_s32 ( np , " st,min-limit-y " , & sval ) = = 0 )
pdata - > st_min_limits [ 1 ] = sval ;
if ( of_property_read_s32 ( np , " st,min-limit-z " , & sval ) = = 0 )
pdata - > st_min_limits [ 2 ] = sval ;
if ( of_property_read_s32 ( np , " st,max-limit-x " , & sval ) = = 0 )
pdata - > st_max_limits [ 0 ] = sval ;
if ( of_property_read_s32 ( np , " st,max-limit-y " , & sval ) = = 0 )
pdata - > st_max_limits [ 1 ] = sval ;
if ( of_property_read_s32 ( np , " st,max-limit-z " , & sval ) = = 0 )
pdata - > st_max_limits [ 2 ] = sval ;
2012-09-27 00:58:16 +04:00
lis3 - > pdata = pdata ;
return 0 ;
}
# else
2012-09-27 00:58:21 +04:00
int lis3lv02d_init_dt ( struct lis3lv02d * lis3 )
2012-09-27 00:58:16 +04:00
{
return 0 ;
}
# endif
EXPORT_SYMBOL_GPL ( lis3lv02d_init_dt ) ;
2009-04-01 02:24:31 +04:00
/*
* Initialise the accelerometer and the various subsystems .
2009-12-15 05:01:39 +03:00
* Should be rather independent of the bus system .
2009-04-01 02:24:31 +04:00
*/
2011-11-01 04:10:58 +04:00
int lis3lv02d_init_device ( struct lis3lv02d * lis3 )
2009-04-01 02:24:31 +04:00
{
2010-05-25 01:33:36 +04:00
int err ;
irq_handler_t thread_fn ;
2010-10-22 15:57:29 +04:00
int irq_flags = 0 ;
2010-05-25 01:33:36 +04:00
2011-11-01 04:10:58 +04:00
lis3 - > whoami = lis3lv02d_read_8 ( lis3 , WHO_AM_I ) ;
2009-04-01 02:24:32 +04:00
2011-11-01 04:10:58 +04:00
switch ( lis3 - > whoami ) {
2009-12-15 05:01:39 +03:00
case WAI_12B :
2010-10-20 10:51:40 +04:00
pr_info ( " 12 bits sensor found \n " ) ;
2011-11-01 04:10:58 +04:00
lis3 - > read_data = lis3lv02d_read_12 ;
lis3 - > mdps_max_val = 2048 ;
lis3 - > pwron_delay = LIS3_PWRON_DELAY_WAI_12B ;
lis3 - > odrs = lis3_12_rates ;
lis3 - > odr_mask = CTRL1_DF0 | CTRL1_DF1 ;
lis3 - > scale = LIS3_SENSITIVITY_12B ;
lis3 - > regs = lis3_wai12_regs ;
lis3 - > regs_size = ARRAY_SIZE ( lis3_wai12_regs ) ;
2009-04-01 02:24:32 +04:00
break ;
2009-12-15 05:01:39 +03:00
case WAI_8B :
2010-10-20 10:51:40 +04:00
pr_info ( " 8 bits sensor found \n " ) ;
2011-11-01 04:10:58 +04:00
lis3 - > read_data = lis3lv02d_read_8 ;
lis3 - > mdps_max_val = 128 ;
lis3 - > pwron_delay = LIS3_PWRON_DELAY_WAI_8B ;
lis3 - > odrs = lis3_8_rates ;
lis3 - > odr_mask = CTRL1_DR ;
lis3 - > scale = LIS3_SENSITIVITY_8B ;
lis3 - > regs = lis3_wai8_regs ;
lis3 - > regs_size = ARRAY_SIZE ( lis3_wai8_regs ) ;
2009-04-01 02:24:32 +04:00
break ;
2010-09-23 21:01:39 +04:00
case WAI_3DC :
2010-10-20 10:51:40 +04:00
pr_info ( " 8 bits 3DC sensor found \n " ) ;
2011-11-01 04:10:58 +04:00
lis3 - > read_data = lis3lv02d_read_8 ;
lis3 - > mdps_max_val = 128 ;
lis3 - > pwron_delay = LIS3_PWRON_DELAY_WAI_8B ;
lis3 - > odrs = lis3_3dc_rates ;
lis3 - > odr_mask = CTRL1_ODR0 | CTRL1_ODR1 | CTRL1_ODR2 | CTRL1_ODR3 ;
lis3 - > scale = LIS3_SENSITIVITY_8B ;
2010-09-23 21:01:39 +04:00
break ;
2012-08-22 10:30:39 +04:00
case WAI_3DLH :
2012-09-24 13:23:49 +04:00
pr_info ( " 16 bits lis331dlh sensor found \n " ) ;
lis3 - > read_data = lis331dlh_read_data ;
2012-08-22 10:30:39 +04:00
lis3 - > mdps_max_val = 2048 ; /* 12 bits for 2G */
lis3 - > shift_adj = SHIFT_ADJ_2G ;
lis3 - > pwron_delay = LIS3_PWRON_DELAY_WAI_8B ;
lis3 - > odrs = lis3_3dlh_rates ;
lis3 - > odr_mask = CTRL1_DR0 | CTRL1_DR1 ;
lis3 - > scale = LIS3DLH_SENSITIVITY_2G ;
break ;
2009-04-01 02:24:32 +04:00
default :
2011-11-01 04:10:58 +04:00
pr_err ( " unknown sensor type 0x%X \n " , lis3 - > whoami ) ;
2009-04-01 02:24:32 +04:00
return - EINVAL ;
}
2011-11-01 04:10:58 +04:00
lis3 - > reg_cache = kzalloc ( max ( sizeof ( lis3_wai8_regs ) ,
2010-10-22 15:57:24 +04:00
sizeof ( lis3_wai12_regs ) ) , GFP_KERNEL ) ;
2011-11-01 04:10:58 +04:00
if ( lis3 - > reg_cache = = NULL ) {
2010-10-22 15:57:24 +04:00
printk ( KERN_ERR DRIVER_NAME " out of memory \n " ) ;
return - ENOMEM ;
}
2011-11-01 04:10:58 +04:00
mutex_init ( & lis3 - > mutex ) ;
atomic_set ( & lis3 - > wake_thread , 0 ) ;
2009-12-15 05:01:43 +03:00
2011-11-01 04:10:58 +04:00
lis3lv02d_add_fs ( lis3 ) ;
err = lis3lv02d_poweron ( lis3 ) ;
2011-11-01 04:10:31 +04:00
if ( err ) {
2011-11-01 04:10:58 +04:00
lis3lv02d_remove_fs ( lis3 ) ;
2011-11-01 04:10:31 +04:00
return err ;
}
2009-04-01 02:24:31 +04:00
2011-11-01 04:10:58 +04:00
if ( lis3 - > pm_dev ) {
pm_runtime_set_active ( lis3 - > pm_dev ) ;
pm_runtime_enable ( lis3 - > pm_dev ) ;
2010-10-22 15:57:23 +04:00
}
2011-11-01 04:11:02 +04:00
if ( lis3lv02d_joystick_enable ( lis3 ) )
2010-10-20 10:51:40 +04:00
pr_err ( " joystick initialization failed \n " ) ;
2009-04-01 02:24:31 +04:00
2009-06-17 02:34:17 +04:00
/* passing in platform specific data is purely optional and only
* used by the SPI transport layer at the moment */
2011-11-01 04:10:58 +04:00
if ( lis3 - > pdata ) {
struct lis3lv02d_platform_data * p = lis3 - > pdata ;
2009-06-17 02:34:17 +04:00
2011-11-01 04:10:58 +04:00
if ( lis3 - > whoami = = WAI_8B )
lis3lv02d_8b_configure ( lis3 , p ) ;
2009-09-22 04:04:43 +04:00
2010-10-22 15:57:29 +04:00
irq_flags = p - > irq_flags1 & IRQF_TRIGGER_MASK ;
2011-11-01 04:10:58 +04:00
lis3 - > irq_cfg = p - > irq_cfg ;
2009-06-17 02:34:17 +04:00
if ( p - > irq_cfg )
2011-11-01 04:10:58 +04:00
lis3 - > write ( lis3 , CTRL_REG3 , p - > irq_cfg ) ;
2010-10-22 15:57:29 +04:00
if ( p - > default_rate )
2011-11-01 04:11:05 +04:00
lis3lv02d_set_odr ( lis3 , p - > default_rate ) ;
2009-06-17 02:34:17 +04:00
}
2009-04-01 02:24:32 +04:00
/* bail if we did not get an IRQ from the bus layer */
2011-11-01 04:10:58 +04:00
if ( ! lis3 - > irq ) {
2011-01-25 17:24:37 +03:00
pr_debug ( " No IRQ. Disabling /dev/freefall \n " ) ;
2009-04-01 02:24:31 +04:00
goto out ;
}
2010-05-25 01:33:36 +04:00
/*
* The sensor can generate interrupts for free - fall and direction
* detection ( distinguishable with FF_WU_SRC and DD_SRC ) but to keep
* the things simple and _fast_ we activate it only for free - fall , so
* no need to read register ( very slow with ACPI ) . For the same reason ,
* we forbid shared interrupts .
*
* IRQF_TRIGGER_RISING seems pointless on HP laptops because the
* io - apic is not configurable ( and generates a warning ) but I keep it
* in case of support for other hardware .
*/
2011-11-01 04:10:58 +04:00
if ( lis3 - > pdata & & lis3 - > whoami = = WAI_8B )
2010-05-25 01:33:36 +04:00
thread_fn = lis302dl_interrupt_thread1_8b ;
else
thread_fn = NULL ;
2011-11-01 04:10:58 +04:00
err = request_threaded_irq ( lis3 - > irq , lis302dl_interrupt ,
2010-05-25 01:33:36 +04:00
thread_fn ,
2010-10-22 15:57:29 +04:00
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
irq_flags ,
2011-11-01 04:11:05 +04:00
DRIVER_NAME , lis3 ) ;
2010-05-25 01:33:36 +04:00
if ( err < 0 ) {
2010-10-20 10:51:40 +04:00
pr_err ( " Cannot get IRQ \n " ) ;
2010-05-25 01:33:36 +04:00
goto out ;
}
2011-11-01 04:11:05 +04:00
lis3 - > miscdev . minor = MISC_DYNAMIC_MINOR ;
lis3 - > miscdev . name = " freefall " ;
lis3 - > miscdev . fops = & lis3lv02d_misc_fops ;
if ( misc_register ( & lis3 - > miscdev ) )
2010-10-20 10:51:40 +04:00
pr_err ( " misc_register failed \n " ) ;
2009-04-01 02:24:31 +04:00
out :
return 0 ;
}
EXPORT_SYMBOL_GPL ( lis3lv02d_init_device ) ;
2008-11-13 00:27:02 +03:00
MODULE_DESCRIPTION ( " ST LIS3LV02Dx three-axis digital accelerometer driver " ) ;
2009-02-19 01:48:23 +03:00
MODULE_AUTHOR ( " Yan Burman, Eric Piel, Pavel Machek " ) ;
2008-11-13 00:27:02 +03:00
MODULE_LICENSE ( " GPL " ) ;