2015-11-23 19:53:51 +03:00
/*
* SCI Reset driver for Keystone based devices
*
* Copyright ( C ) 2015 Texas Instruments Incorporated - http : //www.ti.com/
* Andrew F . Davis < afd @ ti . 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 " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <asm/unaligned.h>
# include <linux/power/bq27xxx_battery.h>
2016-02-02 15:47:37 +03:00
static DEFINE_IDR ( battery_id ) ;
static DEFINE_MUTEX ( battery_mutex ) ;
2015-11-23 19:53:51 +03:00
static irqreturn_t bq27xxx_battery_irq_handler_thread ( int irq , void * data )
{
struct bq27xxx_device_info * di = data ;
bq27xxx_battery_update ( di ) ;
return IRQ_HANDLED ;
}
static int bq27xxx_battery_i2c_read ( struct bq27xxx_device_info * di , u8 reg ,
bool single )
{
struct i2c_client * client = to_i2c_client ( di - > dev ) ;
struct i2c_msg msg [ 2 ] ;
unsigned char data [ 2 ] ;
int ret ;
if ( ! client - > adapter )
return - ENODEV ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . buf = & reg ;
msg [ 0 ] . len = sizeof ( reg ) ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = I2C_M_RD ;
msg [ 1 ] . buf = data ;
if ( single )
msg [ 1 ] . len = 1 ;
else
msg [ 1 ] . len = 2 ;
ret = i2c_transfer ( client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( ret < 0 )
return ret ;
if ( ! single )
ret = get_unaligned_le16 ( data ) ;
else
ret = data [ 0 ] ;
return ret ;
}
static int bq27xxx_battery_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct bq27xxx_device_info * di ;
int ret ;
2016-02-02 15:47:37 +03:00
char * name ;
int num ;
/* Get new ID for the new battery device */
mutex_lock ( & battery_mutex ) ;
num = idr_alloc ( & battery_id , client , 0 , 0 , GFP_KERNEL ) ;
mutex_unlock ( & battery_mutex ) ;
if ( num < 0 )
return num ;
name = devm_kasprintf ( & client - > dev , GFP_KERNEL , " %s-%d " , id - > name , num ) ;
if ( ! name )
goto err_mem ;
2015-11-23 19:53:51 +03:00
di = devm_kzalloc ( & client - > dev , sizeof ( * di ) , GFP_KERNEL ) ;
if ( ! di )
2016-02-02 15:47:37 +03:00
goto err_mem ;
2015-11-23 19:53:51 +03:00
2016-02-02 15:47:37 +03:00
di - > id = num ;
2015-11-23 19:53:51 +03:00
di - > dev = & client - > dev ;
di - > chip = id - > driver_data ;
2016-02-02 15:47:37 +03:00
di - > name = name ;
2015-11-23 19:53:51 +03:00
di - > bus . read = bq27xxx_battery_i2c_read ;
ret = bq27xxx_battery_setup ( di ) ;
if ( ret )
2016-02-02 15:47:37 +03:00
goto err_failed ;
2015-11-23 19:53:51 +03:00
/* Schedule a polling after about 1 min */
schedule_delayed_work ( & di - > work , 60 * HZ ) ;
i2c_set_clientdata ( client , di ) ;
if ( client - > irq ) {
ret = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , bq27xxx_battery_irq_handler_thread ,
IRQF_ONESHOT ,
di - > name , di ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" Unable to register IRQ %d error %d \n " ,
client - > irq , ret ) ;
return ret ;
}
}
return 0 ;
2016-02-02 15:47:37 +03:00
err_mem :
ret = - ENOMEM ;
err_failed :
mutex_lock ( & battery_mutex ) ;
idr_remove ( & battery_id , num ) ;
mutex_unlock ( & battery_mutex ) ;
return ret ;
2015-11-23 19:53:51 +03:00
}
static int bq27xxx_battery_i2c_remove ( struct i2c_client * client )
{
struct bq27xxx_device_info * di = i2c_get_clientdata ( client ) ;
bq27xxx_battery_teardown ( di ) ;
2016-02-02 15:47:37 +03:00
mutex_lock ( & battery_mutex ) ;
idr_remove ( & battery_id , di - > id ) ;
mutex_unlock ( & battery_mutex ) ;
2015-11-23 19:53:51 +03:00
return 0 ;
}
static const struct i2c_device_id bq27xxx_i2c_id_table [ ] = {
{ " bq27200 " , BQ27000 } ,
{ " bq27210 " , BQ27010 } ,
{ " bq27500 " , BQ27500 } ,
{ " bq27510 " , BQ27500 } ,
{ " bq27520 " , BQ27500 } ,
{ " bq27530 " , BQ27530 } ,
{ " bq27531 " , BQ27530 } ,
{ " bq27541 " , BQ27541 } ,
{ " bq27542 " , BQ27541 } ,
{ " bq27546 " , BQ27541 } ,
{ " bq27742 " , BQ27541 } ,
{ " bq27545 " , BQ27545 } ,
{ " bq27421 " , BQ27421 } ,
{ " bq27425 " , BQ27421 } ,
{ " bq27441 " , BQ27421 } ,
{ " bq27621 " , BQ27421 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , bq27xxx_i2c_id_table ) ;
2016-02-21 14:28:22 +03:00
# ifdef CONFIG_OF
static const struct of_device_id bq27xxx_battery_i2c_of_match_table [ ] = {
{ . compatible = " ti,bq27200 " } ,
{ . compatible = " ti,bq27210 " } ,
{ . compatible = " ti,bq27500 " } ,
{ . compatible = " ti,bq27510 " } ,
{ . compatible = " ti,bq27520 " } ,
{ . compatible = " ti,bq27530 " } ,
{ . compatible = " ti,bq27531 " } ,
{ . compatible = " ti,bq27541 " } ,
{ . compatible = " ti,bq27542 " } ,
{ . compatible = " ti,bq27546 " } ,
{ . compatible = " ti,bq27742 " } ,
{ . compatible = " ti,bq27545 " } ,
{ . compatible = " ti,bq27421 " } ,
{ . compatible = " ti,bq27425 " } ,
{ . compatible = " ti,bq27441 " } ,
{ . compatible = " ti,bq27621 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , bq27xxx_battery_i2c_of_match_table ) ;
# endif
2015-11-23 19:53:51 +03:00
static struct i2c_driver bq27xxx_battery_i2c_driver = {
. driver = {
. name = " bq27xxx-battery " ,
2016-02-21 14:28:22 +03:00
. of_match_table = of_match_ptr ( bq27xxx_battery_i2c_of_match_table ) ,
2015-11-23 19:53:51 +03:00
} ,
. probe = bq27xxx_battery_i2c_probe ,
. remove = bq27xxx_battery_i2c_remove ,
. id_table = bq27xxx_i2c_id_table ,
} ;
module_i2c_driver ( bq27xxx_battery_i2c_driver ) ;
MODULE_AUTHOR ( " Andrew F. Davis <afd@ti.com> " ) ;
MODULE_DESCRIPTION ( " BQ27xxx battery monitor i2c driver " ) ;
MODULE_LICENSE ( " GPL " ) ;