2009-12-18 02:27:07 +03:00
/*
* drivers / hwmon / lis3lv02d_i2c . c
*
* Implements I2C interface for lis3lv02d ( STMicroelectronics ) accelerometer .
* Driver is based on corresponding SPI driver written by Daniel Mack
* ( lis3lv02d_spi . c ( C ) 2009 Daniel Mack < daniel @ caiaq . de > ) .
*
* Copyright ( C ) 2009 Nokia Corporation and / or its subsidiary ( - ies ) .
*
* Contact : Samu Onkalo < samu . p . onkalo @ nokia . com >
*
* 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 .
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/i2c.h>
2010-10-22 15:57:23 +04:00
# include <linux/pm_runtime.h>
2010-10-22 15:57:24 +04:00
# include <linux/delay.h>
2009-12-18 02:27:07 +03:00
# include "lis3lv02d.h"
# define DRV_NAME "lis3lv02d_i2c"
2010-10-22 15:57:24 +04:00
static const char reg_vdd [ ] = " Vdd " ;
static const char reg_vdd_io [ ] = " Vdd_IO " ;
static int lis3_reg_ctrl ( struct lis3lv02d * lis3 , bool state )
{
int ret ;
if ( state = = LIS3_REG_OFF ) {
ret = regulator_bulk_disable ( ARRAY_SIZE ( lis3 - > regulators ) ,
lis3 - > regulators ) ;
} else {
ret = regulator_bulk_enable ( ARRAY_SIZE ( lis3 - > regulators ) ,
lis3 - > regulators ) ;
/* Chip needs time to wakeup. Not mentioned in datasheet */
usleep_range ( 10000 , 20000 ) ;
}
return ret ;
}
2009-12-18 02:27:07 +03:00
static inline s32 lis3_i2c_write ( struct lis3lv02d * lis3 , int reg , u8 value )
{
struct i2c_client * c = lis3 - > bus_priv ;
return i2c_smbus_write_byte_data ( c , reg , value ) ;
}
static inline s32 lis3_i2c_read ( struct lis3lv02d * lis3 , int reg , u8 * v )
{
struct i2c_client * c = lis3 - > bus_priv ;
* v = i2c_smbus_read_byte_data ( c , reg ) ;
return 0 ;
}
2010-10-22 15:57:31 +04:00
static inline s32 lis3_i2c_blockread ( struct lis3lv02d * lis3 , int reg , int len ,
u8 * v )
{
struct i2c_client * c = lis3 - > bus_priv ;
reg | = ( 1 < < 7 ) ; /* 7th bit enables address auto incrementation */
return i2c_smbus_read_i2c_block_data ( c , reg , len , v ) ;
}
2009-12-18 02:27:07 +03:00
static int lis3_i2c_init ( struct lis3lv02d * lis3 )
{
u8 reg ;
int ret ;
2010-10-22 15:57:24 +04:00
if ( lis3 - > reg_ctrl )
lis3_reg_ctrl ( lis3 , LIS3_REG_ON ) ;
lis3 - > read ( lis3 , WHO_AM_I , & reg ) ;
if ( reg ! = lis3 - > whoami )
printk ( KERN_ERR " lis3: power on failure \n " ) ;
2009-12-18 02:27:07 +03:00
/* power up the device */
ret = lis3 - > read ( lis3 , CTRL_REG1 , & reg ) ;
if ( ret < 0 )
return ret ;
2010-10-22 15:57:28 +04:00
reg | = CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen ;
2009-12-18 02:27:07 +03:00
return lis3 - > write ( lis3 , CTRL_REG1 , reg ) ;
}
/* Default axis mapping but it can be overwritten by platform data */
2010-10-02 01:14:25 +04:00
static union axis_conversion lis3lv02d_axis_map =
{ . as_array = { LIS3_DEV_X , LIS3_DEV_Y , LIS3_DEV_Z } } ;
2009-12-18 02:27:07 +03:00
static int __devinit lis3lv02d_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int ret = 0 ;
struct lis3lv02d_platform_data * pdata = client - > dev . platform_data ;
if ( pdata ) {
2010-10-22 15:57:24 +04:00
/* Regulator control is optional */
if ( pdata - > driver_features & LIS3_USE_REGULATOR_CTRL )
lis3_dev . reg_ctrl = lis3_reg_ctrl ;
2010-10-22 15:57:31 +04:00
if ( ( pdata - > driver_features & LIS3_USE_BLOCK_READ ) & &
( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_I2C_BLOCK ) ) )
lis3_dev . blkread = lis3_i2c_blockread ;
2009-12-18 02:27:07 +03:00
if ( pdata - > axis_x )
lis3lv02d_axis_map . x = pdata - > axis_x ;
if ( pdata - > axis_y )
lis3lv02d_axis_map . y = pdata - > axis_y ;
if ( pdata - > axis_z )
lis3lv02d_axis_map . z = pdata - > axis_z ;
if ( pdata - > setup_resources )
ret = pdata - > setup_resources ( ) ;
if ( ret )
goto fail ;
}
2010-10-22 15:57:24 +04:00
if ( lis3_dev . reg_ctrl ) {
lis3_dev . regulators [ 0 ] . supply = reg_vdd ;
lis3_dev . regulators [ 1 ] . supply = reg_vdd_io ;
ret = regulator_bulk_get ( & client - > dev ,
ARRAY_SIZE ( lis3_dev . regulators ) ,
lis3_dev . regulators ) ;
if ( ret < 0 )
goto fail ;
}
2009-12-18 02:27:07 +03:00
lis3_dev . pdata = pdata ;
lis3_dev . bus_priv = client ;
lis3_dev . init = lis3_i2c_init ;
lis3_dev . read = lis3_i2c_read ;
lis3_dev . write = lis3_i2c_write ;
lis3_dev . irq = client - > irq ;
lis3_dev . ac = lis3lv02d_axis_map ;
2010-10-22 15:57:23 +04:00
lis3_dev . pm_dev = & client - > dev ;
2009-12-18 02:27:07 +03:00
i2c_set_clientdata ( client , & lis3_dev ) ;
2010-10-22 15:57:24 +04:00
/* Provide power over the init call */
if ( lis3_dev . reg_ctrl )
lis3_reg_ctrl ( & lis3_dev , LIS3_REG_ON ) ;
2009-12-18 02:27:07 +03:00
ret = lis3lv02d_init_device ( & lis3_dev ) ;
2010-10-22 15:57:24 +04:00
if ( lis3_dev . reg_ctrl )
lis3_reg_ctrl ( & lis3_dev , LIS3_REG_OFF ) ;
2010-10-22 15:57:34 +04:00
if ( ret = = 0 )
return 0 ;
2009-12-18 02:27:07 +03:00
fail :
2010-10-22 15:57:34 +04:00
if ( pdata & & pdata - > release_resources )
pdata - > release_resources ( ) ;
2009-12-18 02:27:07 +03:00
return ret ;
}
static int __devexit lis3lv02d_i2c_remove ( struct i2c_client * client )
{
2010-10-22 15:57:24 +04:00
struct lis3lv02d * lis3 = i2c_get_clientdata ( client ) ;
2009-12-18 02:27:07 +03:00
struct lis3lv02d_platform_data * pdata = client - > dev . platform_data ;
if ( pdata & & pdata - > release_resources )
pdata - > release_resources ( ) ;
lis3lv02d_joystick_disable ( ) ;
2010-10-22 15:57:24 +04:00
lis3lv02d_remove_fs ( & lis3_dev ) ;
2009-12-18 02:27:07 +03:00
2010-10-22 15:57:24 +04:00
if ( lis3_dev . reg_ctrl )
regulator_bulk_free ( ARRAY_SIZE ( lis3 - > regulators ) ,
lis3_dev . regulators ) ;
return 0 ;
2009-12-18 02:27:07 +03:00
}
# ifdef CONFIG_PM
2010-10-22 15:57:23 +04:00
static int lis3lv02d_i2c_suspend ( struct device * dev )
2009-12-18 02:27:07 +03:00
{
2010-10-22 15:57:23 +04:00
struct i2c_client * client = container_of ( dev , struct i2c_client , dev ) ;
2009-12-18 02:27:07 +03:00
struct lis3lv02d * lis3 = i2c_get_clientdata ( client ) ;
2010-09-17 19:24:10 +04:00
if ( ! lis3 - > pdata | | ! lis3 - > pdata - > wakeup_flags )
2009-12-18 02:27:07 +03:00
lis3lv02d_poweroff ( lis3 ) ;
return 0 ;
}
2010-10-22 15:57:23 +04:00
static int lis3lv02d_i2c_resume ( struct device * dev )
2009-12-18 02:27:07 +03:00
{
2010-10-22 15:57:23 +04:00
struct i2c_client * client = container_of ( dev , struct i2c_client , dev ) ;
2009-12-18 02:27:07 +03:00
struct lis3lv02d * lis3 = i2c_get_clientdata ( client ) ;
2010-10-22 15:57:23 +04:00
/*
* pm_runtime documentation says that devices should always
* be powered on at resume . Pm_runtime turns them off after system
* wide resume is complete .
*/
if ( ! lis3 - > pdata | | ! lis3 - > pdata - > wakeup_flags | |
pm_runtime_suspended ( dev ) )
2009-12-18 02:27:07 +03:00
lis3lv02d_poweron ( lis3 ) ;
2010-10-22 15:57:23 +04:00
return 0 ;
2009-12-18 02:27:07 +03:00
}
# else
# define lis3lv02d_i2c_suspend NULL
# define lis3lv02d_i2c_resume NULL
# define lis3lv02d_i2c_shutdown NULL
# endif
2010-10-22 15:57:23 +04:00
static int lis3_i2c_runtime_suspend ( struct device * dev )
{
struct i2c_client * client = container_of ( dev , struct i2c_client , dev ) ;
struct lis3lv02d * lis3 = i2c_get_clientdata ( client ) ;
lis3lv02d_poweroff ( lis3 ) ;
return 0 ;
}
static int lis3_i2c_runtime_resume ( struct device * dev )
{
struct i2c_client * client = container_of ( dev , struct i2c_client , dev ) ;
struct lis3lv02d * lis3 = i2c_get_clientdata ( client ) ;
lis3lv02d_poweron ( lis3 ) ;
return 0 ;
}
2009-12-18 02:27:07 +03:00
static const struct i2c_device_id lis3lv02d_id [ ] = {
{ " lis3lv02d " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lis3lv02d_id ) ;
2010-10-22 15:57:23 +04:00
static const struct dev_pm_ops lis3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( lis3lv02d_i2c_suspend ,
lis3lv02d_i2c_resume )
SET_RUNTIME_PM_OPS ( lis3_i2c_runtime_suspend ,
lis3_i2c_runtime_resume ,
NULL )
} ;
2009-12-18 02:27:07 +03:00
static struct i2c_driver lis3lv02d_i2c_driver = {
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
2010-10-22 15:57:23 +04:00
. pm = & lis3_pm_ops ,
2009-12-18 02:27:07 +03:00
} ,
. probe = lis3lv02d_i2c_probe ,
. remove = __devexit_p ( lis3lv02d_i2c_remove ) ,
. id_table = lis3lv02d_id ,
} ;
static int __init lis3lv02d_init ( void )
{
return i2c_add_driver ( & lis3lv02d_i2c_driver ) ;
}
static void __exit lis3lv02d_exit ( void )
{
i2c_del_driver ( & lis3lv02d_i2c_driver ) ;
}
MODULE_AUTHOR ( " Nokia Corporation " ) ;
MODULE_DESCRIPTION ( " lis3lv02d I2C interface " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( lis3lv02d_init ) ;
module_exit ( lis3lv02d_exit ) ;