2008-08-21 03:52:58 +04:00
/*
* BQ27x00 battery driver
*
* Copyright ( C ) 2008 Rodolfo Giometti < giometti @ linux . it >
* Copyright ( C ) 2008 Eurotech S . p . A . < info @ eurotech . it >
*
* Based on a previous work by Copyright ( C ) 2008 Texas Instruments , Inc .
*
* This package 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 PACKAGE IS PROVIDED ` ` AS IS ' ' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , WITHOUT LIMITATION , THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE .
*
*/
# include <linux/module.h>
# include <linux/param.h>
# include <linux/jiffies.h>
# include <linux/workqueue.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
# include <linux/idr.h>
# include <linux/i2c.h>
2008-09-23 01:53:50 +04:00
# include <asm/unaligned.h>
2008-08-21 03:52:58 +04:00
# define DRIVER_VERSION "1.0.0"
# define BQ27x00_REG_TEMP 0x06
# define BQ27x00_REG_VOLT 0x08
# define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */
# define BQ27x00_REG_AI 0x14
# define BQ27x00_REG_FLAGS 0x0A
/* If the system has several batteries we need a different name for each
* of them . . .
*/
static DEFINE_IDR ( battery_id ) ;
static DEFINE_MUTEX ( battery_mutex ) ;
struct bq27x00_device_info ;
struct bq27x00_access_methods {
int ( * read ) ( u8 reg , int * rt_value , int b_single ,
struct bq27x00_device_info * di ) ;
} ;
struct bq27x00_device_info {
struct device * dev ;
int id ;
struct bq27x00_access_methods * bus ;
struct power_supply bat ;
struct i2c_client * client ;
} ;
static enum power_supply_property bq27x00_battery_props [ ] = {
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_VOLTAGE_NOW ,
POWER_SUPPLY_PROP_CURRENT_NOW ,
POWER_SUPPLY_PROP_CAPACITY ,
POWER_SUPPLY_PROP_TEMP ,
} ;
/*
* Common code for BQ27x00 devices
*/
static int bq27x00_read ( u8 reg , int * rt_value , int b_single ,
struct bq27x00_device_info * di )
{
2010-02-13 00:56:46 +03:00
return di - > bus - > read ( reg , rt_value , b_single , di ) ;
2008-08-21 03:52:58 +04:00
}
/*
2009-03-13 19:15:26 +03:00
* Return the battery temperature in Celsius degrees
2008-08-21 03:52:58 +04:00
* Or < 0 if something fails .
*/
static int bq27x00_battery_temperature ( struct bq27x00_device_info * di )
{
int ret ;
int temp = 0 ;
ret = bq27x00_read ( BQ27x00_REG_TEMP , & temp , 0 , di ) ;
if ( ret ) {
dev_err ( di - > dev , " error reading temperature \n " ) ;
return ret ;
}
return ( temp > > 2 ) - 273 ;
}
/*
* Return the battery Voltage in milivolts
* Or < 0 if something fails .
*/
static int bq27x00_battery_voltage ( struct bq27x00_device_info * di )
{
int ret ;
int volt = 0 ;
ret = bq27x00_read ( BQ27x00_REG_VOLT , & volt , 0 , di ) ;
if ( ret ) {
dev_err ( di - > dev , " error reading voltage \n " ) ;
return ret ;
}
return volt ;
}
/*
* Return the battery average current
* Note that current can be negative signed as well
* Or 0 if something fails .
*/
static int bq27x00_battery_current ( struct bq27x00_device_info * di )
{
int ret ;
int curr = 0 ;
int flags = 0 ;
ret = bq27x00_read ( BQ27x00_REG_AI , & curr , 0 , di ) ;
if ( ret ) {
dev_err ( di - > dev , " error reading current \n " ) ;
return 0 ;
}
ret = bq27x00_read ( BQ27x00_REG_FLAGS , & flags , 0 , di ) ;
if ( ret < 0 ) {
dev_err ( di - > dev , " error reading flags \n " ) ;
return 0 ;
}
if ( ( flags & ( 1 < < 7 ) ) ! = 0 ) {
dev_dbg ( di - > dev , " negative current! \n " ) ;
return - curr ;
}
return curr ;
}
/*
* Return the battery Relative State - of - Charge
* Or < 0 if something fails .
*/
static int bq27x00_battery_rsoc ( struct bq27x00_device_info * di )
{
int ret ;
int rsoc = 0 ;
ret = bq27x00_read ( BQ27x00_REG_RSOC , & rsoc , 1 , di ) ;
if ( ret ) {
dev_err ( di - > dev , " error reading relative State-of-Charge \n " ) ;
return ret ;
}
2010-02-13 00:56:46 +03:00
return rsoc ;
2008-08-21 03:52:58 +04:00
}
# define to_bq27x00_device_info(x) container_of((x), \
struct bq27x00_device_info , bat ) ;
static int bq27x00_battery_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct bq27x00_device_info * di = to_bq27x00_device_info ( psy ) ;
switch ( psp ) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW :
case POWER_SUPPLY_PROP_PRESENT :
val - > intval = bq27x00_battery_voltage ( di ) ;
if ( psp = = POWER_SUPPLY_PROP_PRESENT )
val - > intval = val - > intval < = 0 ? 0 : 1 ;
break ;
case POWER_SUPPLY_PROP_CURRENT_NOW :
val - > intval = bq27x00_battery_current ( di ) ;
break ;
case POWER_SUPPLY_PROP_CAPACITY :
val - > intval = bq27x00_battery_rsoc ( di ) ;
break ;
case POWER_SUPPLY_PROP_TEMP :
val - > intval = bq27x00_battery_temperature ( di ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static void bq27x00_powersupply_init ( struct bq27x00_device_info * di )
{
di - > bat . type = POWER_SUPPLY_TYPE_BATTERY ;
di - > bat . properties = bq27x00_battery_props ;
di - > bat . num_properties = ARRAY_SIZE ( bq27x00_battery_props ) ;
di - > bat . get_property = bq27x00_battery_get_property ;
di - > bat . external_power_changed = NULL ;
}
/*
* BQ27200 specific code
*/
static int bq27200_read ( u8 reg , int * rt_value , int b_single ,
struct bq27x00_device_info * di )
{
struct i2c_client * client = di - > client ;
struct i2c_msg msg [ 1 ] ;
unsigned char data [ 2 ] ;
int err ;
if ( ! client - > adapter )
return - ENODEV ;
msg - > addr = client - > addr ;
msg - > flags = 0 ;
msg - > len = 1 ;
msg - > buf = data ;
data [ 0 ] = reg ;
err = i2c_transfer ( client - > adapter , msg , 1 ) ;
if ( err > = 0 ) {
if ( ! b_single )
msg - > len = 2 ;
else
msg - > len = 1 ;
msg - > flags = I2C_M_RD ;
err = i2c_transfer ( client - > adapter , msg , 1 ) ;
if ( err > = 0 ) {
if ( ! b_single )
2010-02-13 00:56:46 +03:00
* rt_value = get_unaligned_le16 ( data ) ;
2008-08-21 03:52:58 +04:00
else
* rt_value = data [ 0 ] ;
return 0 ;
}
}
return err ;
}
static int bq27200_battery_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
char * name ;
struct bq27x00_device_info * di ;
struct bq27x00_access_methods * bus ;
int num ;
int retval = 0 ;
/* Get new ID for the new battery device */
retval = idr_pre_get ( & battery_id , GFP_KERNEL ) ;
if ( retval = = 0 )
return - ENOMEM ;
mutex_lock ( & battery_mutex ) ;
retval = idr_get_new ( & battery_id , client , & num ) ;
mutex_unlock ( & battery_mutex ) ;
if ( retval < 0 )
return retval ;
name = kasprintf ( GFP_KERNEL , " bq27200-%d " , num ) ;
if ( ! name ) {
dev_err ( & client - > dev , " failed to allocate device name \n " ) ;
retval = - ENOMEM ;
goto batt_failed_1 ;
}
di = kzalloc ( sizeof ( * di ) , GFP_KERNEL ) ;
if ( ! di ) {
dev_err ( & client - > dev , " failed to allocate device info data \n " ) ;
retval = - ENOMEM ;
goto batt_failed_2 ;
}
di - > id = num ;
bus = kzalloc ( sizeof ( * bus ) , GFP_KERNEL ) ;
if ( ! bus ) {
dev_err ( & client - > dev , " failed to allocate access method "
" data \n " ) ;
retval = - ENOMEM ;
goto batt_failed_3 ;
}
i2c_set_clientdata ( client , di ) ;
di - > dev = & client - > dev ;
di - > bat . name = name ;
bus - > read = & bq27200_read ;
di - > bus = bus ;
di - > client = client ;
bq27x00_powersupply_init ( di ) ;
retval = power_supply_register ( & client - > dev , & di - > bat ) ;
if ( retval ) {
dev_err ( & client - > dev , " failed to register battery \n " ) ;
goto batt_failed_4 ;
}
dev_info ( & client - > dev , " support ver. %s enabled \n " , DRIVER_VERSION ) ;
return 0 ;
batt_failed_4 :
kfree ( bus ) ;
batt_failed_3 :
kfree ( di ) ;
batt_failed_2 :
kfree ( name ) ;
batt_failed_1 :
mutex_lock ( & battery_mutex ) ;
idr_remove ( & battery_id , num ) ;
mutex_unlock ( & battery_mutex ) ;
return retval ;
}
static int bq27200_battery_remove ( struct i2c_client * client )
{
struct bq27x00_device_info * di = i2c_get_clientdata ( client ) ;
power_supply_unregister ( & di - > bat ) ;
kfree ( di - > bat . name ) ;
mutex_lock ( & battery_mutex ) ;
idr_remove ( & battery_id , di - > id ) ;
mutex_unlock ( & battery_mutex ) ;
kfree ( di ) ;
return 0 ;
}
/*
* Module stuff
*/
static const struct i2c_device_id bq27200_id [ ] = {
{ " bq27200 " , 0 } ,
{ } ,
} ;
static struct i2c_driver bq27200_battery_driver = {
. driver = {
. name = " bq27200-battery " ,
} ,
. probe = bq27200_battery_probe ,
. remove = bq27200_battery_remove ,
. id_table = bq27200_id ,
} ;
static int __init bq27x00_battery_init ( void )
{
int ret ;
ret = i2c_add_driver ( & bq27200_battery_driver ) ;
if ( ret )
printk ( KERN_ERR " Unable to register BQ27200 driver \n " ) ;
return ret ;
}
module_init ( bq27x00_battery_init ) ;
static void __exit bq27x00_battery_exit ( void )
{
i2c_del_driver ( & bq27200_battery_driver ) ;
}
module_exit ( bq27x00_battery_exit ) ;
MODULE_AUTHOR ( " Rodolfo Giometti <giometti@linux.it> " ) ;
MODULE_DESCRIPTION ( " BQ27x00 battery monitor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;