2016-02-12 13:44:44 +02:00
/*
* Copyright ( C ) 2012 Invensense , Inc .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*/
# include <linux/acpi.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/i2c-mux.h>
# include <linux/iio/iio.h>
# include <linux/module.h>
# include "inv_mpu_iio.h"
static const struct regmap_config inv_mpu_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
/*
* The i2c read / write needs to happen in unlocked mode . As the parent
* adapter is common . If we use locked versions , it will fail as
* the mux adapter will lock the parent i2c adapter , while calling
* select / deselect functions .
*/
static int inv_mpu6050_write_reg_unlocked ( struct i2c_client * client ,
u8 reg , u8 d )
{
int ret ;
u8 buf [ 2 ] = { reg , d } ;
struct i2c_msg msg [ 1 ] = {
{
. addr = client - > addr ,
. flags = 0 ,
. len = sizeof ( buf ) ,
. buf = buf ,
}
} ;
ret = __i2c_transfer ( client - > adapter , msg , 1 ) ;
if ( ret ! = 1 )
return ret ;
return 0 ;
}
static int inv_mpu6050_select_bypass ( struct i2c_adapter * adap , void * mux_priv ,
u32 chan_id )
{
struct i2c_client * client = mux_priv ;
struct iio_dev * indio_dev = dev_get_drvdata ( & client - > dev ) ;
struct inv_mpu6050_state * st = iio_priv ( indio_dev ) ;
int ret = 0 ;
/* Use the same mutex which was used everywhere to protect power-op */
mutex_lock ( & indio_dev - > mlock ) ;
if ( ! st - > powerup_count ) {
ret = inv_mpu6050_write_reg_unlocked ( client ,
st - > reg - > pwr_mgmt_1 , 0 ) ;
if ( ret )
goto write_error ;
2016-02-22 13:39:08 -08:00
usleep_range ( INV_MPU6050_REG_UP_TIME_MIN ,
INV_MPU6050_REG_UP_TIME_MAX ) ;
2016-02-12 13:44:44 +02:00
}
if ( ! ret ) {
st - > powerup_count + + ;
ret = inv_mpu6050_write_reg_unlocked ( client ,
st - > reg - > int_pin_cfg ,
INV_MPU6050_INT_PIN_CFG |
INV_MPU6050_BIT_BYPASS_EN ) ;
}
write_error :
mutex_unlock ( & indio_dev - > mlock ) ;
return ret ;
}
static int inv_mpu6050_deselect_bypass ( struct i2c_adapter * adap ,
void * mux_priv , u32 chan_id )
{
struct i2c_client * client = mux_priv ;
struct iio_dev * indio_dev = dev_get_drvdata ( & client - > dev ) ;
struct inv_mpu6050_state * st = iio_priv ( indio_dev ) ;
mutex_lock ( & indio_dev - > mlock ) ;
/* It doesn't really mattter, if any of the calls fails */
inv_mpu6050_write_reg_unlocked ( client , st - > reg - > int_pin_cfg ,
INV_MPU6050_INT_PIN_CFG ) ;
st - > powerup_count - - ;
if ( ! st - > powerup_count )
inv_mpu6050_write_reg_unlocked ( client , st - > reg - > pwr_mgmt_1 ,
INV_MPU6050_BIT_SLEEP ) ;
mutex_unlock ( & indio_dev - > mlock ) ;
return 0 ;
}
/**
* inv_mpu_probe ( ) - probe function .
* @ client : i2c client .
* @ id : i2c device id .
*
* Returns 0 on success , a negative error code otherwise .
*/
static int inv_mpu_probe ( struct i2c_client * client ,
2016-02-18 17:53:12 +02:00
const struct i2c_device_id * id )
2016-02-12 13:44:44 +02:00
{
struct inv_mpu6050_state * st ;
int result ;
const char * name = id ? id - > name : NULL ;
struct regmap * regmap ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_I2C_BLOCK ) )
return - ENOSYS ;
regmap = devm_regmap_init_i2c ( client , & inv_mpu_regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( & client - > dev , " Failed to register i2c regmap %d \n " ,
( int ) PTR_ERR ( regmap ) ) ;
return PTR_ERR ( regmap ) ;
}
2016-02-22 13:39:11 -08:00
result = inv_mpu_core_probe ( regmap , client - > irq , name ,
NULL , id - > driver_data ) ;
2016-02-12 13:44:44 +02:00
if ( result < 0 )
return result ;
st = iio_priv ( dev_get_drvdata ( & client - > dev ) ) ;
st - > mux_adapter = i2c_add_mux_adapter ( client - > adapter ,
& client - > dev ,
client ,
0 , 0 , 0 ,
inv_mpu6050_select_bypass ,
inv_mpu6050_deselect_bypass ) ;
if ( ! st - > mux_adapter ) {
result = - ENODEV ;
goto out_unreg_device ;
}
result = inv_mpu_acpi_create_mux_client ( client ) ;
if ( result )
goto out_del_mux ;
return 0 ;
out_del_mux :
i2c_del_mux_adapter ( st - > mux_adapter ) ;
out_unreg_device :
inv_mpu_core_remove ( & client - > dev ) ;
return result ;
}
static int inv_mpu_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct inv_mpu6050_state * st = iio_priv ( indio_dev ) ;
inv_mpu_acpi_delete_mux_client ( client ) ;
i2c_del_mux_adapter ( st - > mux_adapter ) ;
return inv_mpu_core_remove ( & client - > dev ) ;
}
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id inv_mpu_id [ ] = {
{ " mpu6050 " , INV_MPU6050 } ,
{ " mpu6500 " , INV_MPU6500 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , inv_mpu_id ) ;
static const struct acpi_device_id inv_acpi_match [ ] = {
{ " INVN6500 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , inv_acpi_match ) ;
static struct i2c_driver inv_mpu_driver = {
. probe = inv_mpu_probe ,
. remove = inv_mpu_remove ,
. id_table = inv_mpu_id ,
. driver = {
. acpi_match_table = ACPI_PTR ( inv_acpi_match ) ,
. name = " inv-mpu6050-i2c " ,
. pm = & inv_mpu_pmops ,
} ,
} ;
module_i2c_driver ( inv_mpu_driver ) ;
MODULE_AUTHOR ( " Invensense Corporation " ) ;
MODULE_DESCRIPTION ( " Invensense device MPU6050 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;