2013-08-24 06:21:03 +04:00
/*
* Driver for the TI bq24190 battery charger .
*
* Author : Mark A . Greer < mgreer @ animalcreek . 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 .
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/of_irq.h>
# include <linux/of_device.h>
# include <linux/pm_runtime.h>
# include <linux/power_supply.h>
2017-08-30 12:48:11 +03:00
# include <linux/power/bq24190_charger.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2017-03-23 11:32:35 +03:00
# include <linux/workqueue.h>
2013-08-24 06:21:03 +04:00
# include <linux/gpio.h>
# include <linux/i2c.h>
# define BQ24190_MANUFACTURER "Texas Instruments"
# define BQ24190_REG_ISC 0x00 /* Input Source Control */
# define BQ24190_REG_ISC_EN_HIZ_MASK BIT(7)
# define BQ24190_REG_ISC_EN_HIZ_SHIFT 7
# define BQ24190_REG_ISC_VINDPM_MASK (BIT(6) | BIT(5) | BIT(4) | \
BIT ( 3 ) )
# define BQ24190_REG_ISC_VINDPM_SHIFT 3
# define BQ24190_REG_ISC_IINLIM_MASK (BIT(2) | BIT(1) | BIT(0))
# define BQ24190_REG_ISC_IINLIM_SHIFT 0
# define BQ24190_REG_POC 0x01 /* Power-On Configuration */
# define BQ24190_REG_POC_RESET_MASK BIT(7)
# define BQ24190_REG_POC_RESET_SHIFT 7
# define BQ24190_REG_POC_WDT_RESET_MASK BIT(6)
# define BQ24190_REG_POC_WDT_RESET_SHIFT 6
# define BQ24190_REG_POC_CHG_CONFIG_MASK (BIT(5) | BIT(4))
# define BQ24190_REG_POC_CHG_CONFIG_SHIFT 4
2017-04-11 14:59:55 +03:00
# define BQ24190_REG_POC_CHG_CONFIG_DISABLE 0x0
# define BQ24190_REG_POC_CHG_CONFIG_CHARGE 0x1
# define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2
2013-08-24 06:21:03 +04:00
# define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1))
# define BQ24190_REG_POC_SYS_MIN_SHIFT 1
2017-08-29 00:23:48 +03:00
# define BQ24190_REG_POC_SYS_MIN_MIN 3000
# define BQ24190_REG_POC_SYS_MIN_MAX 3700
2013-08-24 06:21:03 +04:00
# define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0)
# define BQ24190_REG_POC_BOOST_LIM_SHIFT 0
# define BQ24190_REG_CCC 0x02 /* Charge Current Control */
# define BQ24190_REG_CCC_ICHG_MASK (BIT(7) | BIT(6) | BIT(5) | \
BIT ( 4 ) | BIT ( 3 ) | BIT ( 2 ) )
# define BQ24190_REG_CCC_ICHG_SHIFT 2
# define BQ24190_REG_CCC_FORCE_20PCT_MASK BIT(0)
# define BQ24190_REG_CCC_FORCE_20PCT_SHIFT 0
# define BQ24190_REG_PCTCC 0x03 /* Pre-charge/Termination Current Cntl */
# define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \
BIT ( 4 ) )
# define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4
2017-08-29 00:23:49 +03:00
# define BQ24190_REG_PCTCC_IPRECHG_MIN 128
# define BQ24190_REG_PCTCC_IPRECHG_MAX 2048
2013-08-24 06:21:03 +04:00
# define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \
BIT ( 0 ) )
# define BQ24190_REG_PCTCC_ITERM_SHIFT 0
2017-08-29 00:23:49 +03:00
# define BQ24190_REG_PCTCC_ITERM_MIN 128
# define BQ24190_REG_PCTCC_ITERM_MAX 2048
2013-08-24 06:21:03 +04:00
# define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */
# define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \
BIT ( 4 ) | BIT ( 3 ) | BIT ( 2 ) )
# define BQ24190_REG_CVC_VREG_SHIFT 2
# define BQ24190_REG_CVC_BATLOWV_MASK BIT(1)
# define BQ24190_REG_CVC_BATLOWV_SHIFT 1
# define BQ24190_REG_CVC_VRECHG_MASK BIT(0)
# define BQ24190_REG_CVC_VRECHG_SHIFT 0
# define BQ24190_REG_CTTC 0x05 /* Charge Term/Timer Control */
# define BQ24190_REG_CTTC_EN_TERM_MASK BIT(7)
# define BQ24190_REG_CTTC_EN_TERM_SHIFT 7
# define BQ24190_REG_CTTC_TERM_STAT_MASK BIT(6)
# define BQ24190_REG_CTTC_TERM_STAT_SHIFT 6
# define BQ24190_REG_CTTC_WATCHDOG_MASK (BIT(5) | BIT(4))
# define BQ24190_REG_CTTC_WATCHDOG_SHIFT 4
# define BQ24190_REG_CTTC_EN_TIMER_MASK BIT(3)
# define BQ24190_REG_CTTC_EN_TIMER_SHIFT 3
# define BQ24190_REG_CTTC_CHG_TIMER_MASK (BIT(2) | BIT(1))
# define BQ24190_REG_CTTC_CHG_TIMER_SHIFT 1
# define BQ24190_REG_CTTC_JEITA_ISET_MASK BIT(0)
# define BQ24190_REG_CTTC_JEITA_ISET_SHIFT 0
# define BQ24190_REG_ICTRC 0x06 /* IR Comp/Thermal Regulation Control */
# define BQ24190_REG_ICTRC_BAT_COMP_MASK (BIT(7) | BIT(6) | BIT(5))
# define BQ24190_REG_ICTRC_BAT_COMP_SHIFT 5
# define BQ24190_REG_ICTRC_VCLAMP_MASK (BIT(4) | BIT(3) | BIT(2))
# define BQ24190_REG_ICTRC_VCLAMP_SHIFT 2
# define BQ24190_REG_ICTRC_TREG_MASK (BIT(1) | BIT(0))
# define BQ24190_REG_ICTRC_TREG_SHIFT 0
# define BQ24190_REG_MOC 0x07 /* Misc. Operation Control */
# define BQ24190_REG_MOC_DPDM_EN_MASK BIT(7)
# define BQ24190_REG_MOC_DPDM_EN_SHIFT 7
# define BQ24190_REG_MOC_TMR2X_EN_MASK BIT(6)
# define BQ24190_REG_MOC_TMR2X_EN_SHIFT 6
# define BQ24190_REG_MOC_BATFET_DISABLE_MASK BIT(5)
# define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT 5
# define BQ24190_REG_MOC_JEITA_VSET_MASK BIT(4)
# define BQ24190_REG_MOC_JEITA_VSET_SHIFT 4
# define BQ24190_REG_MOC_INT_MASK_MASK (BIT(1) | BIT(0))
# define BQ24190_REG_MOC_INT_MASK_SHIFT 0
# define BQ24190_REG_SS 0x08 /* System Status */
# define BQ24190_REG_SS_VBUS_STAT_MASK (BIT(7) | BIT(6))
# define BQ24190_REG_SS_VBUS_STAT_SHIFT 6
# define BQ24190_REG_SS_CHRG_STAT_MASK (BIT(5) | BIT(4))
# define BQ24190_REG_SS_CHRG_STAT_SHIFT 4
# define BQ24190_REG_SS_DPM_STAT_MASK BIT(3)
# define BQ24190_REG_SS_DPM_STAT_SHIFT 3
# define BQ24190_REG_SS_PG_STAT_MASK BIT(2)
# define BQ24190_REG_SS_PG_STAT_SHIFT 2
# define BQ24190_REG_SS_THERM_STAT_MASK BIT(1)
# define BQ24190_REG_SS_THERM_STAT_SHIFT 1
# define BQ24190_REG_SS_VSYS_STAT_MASK BIT(0)
# define BQ24190_REG_SS_VSYS_STAT_SHIFT 0
# define BQ24190_REG_F 0x09 /* Fault */
# define BQ24190_REG_F_WATCHDOG_FAULT_MASK BIT(7)
# define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT 7
# define BQ24190_REG_F_BOOST_FAULT_MASK BIT(6)
# define BQ24190_REG_F_BOOST_FAULT_SHIFT 6
# define BQ24190_REG_F_CHRG_FAULT_MASK (BIT(5) | BIT(4))
# define BQ24190_REG_F_CHRG_FAULT_SHIFT 4
# define BQ24190_REG_F_BAT_FAULT_MASK BIT(3)
# define BQ24190_REG_F_BAT_FAULT_SHIFT 3
# define BQ24190_REG_F_NTC_FAULT_MASK (BIT(2) | BIT(1) | BIT(0))
# define BQ24190_REG_F_NTC_FAULT_SHIFT 0
# define BQ24190_REG_VPRS 0x0A /* Vendor/Part/Revision Status */
# define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3))
# define BQ24190_REG_VPRS_PN_SHIFT 3
# define BQ24190_REG_VPRS_PN_24190 0x4
# define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */
# define BQ24190_REG_VPRS_PN_24192I 0x3
# define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2)
# define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2
# define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0))
# define BQ24190_REG_VPRS_DEV_REG_SHIFT 0
/*
* The FAULT register is latched by the bq24190 ( except for NTC_FAULT )
* so the first read after a fault returns the latched value and subsequent
* reads return the current value . In order to return the fault status
* to the user , have the interrupt handler save the reg ' s value and retrieve
2017-01-18 20:26:53 +03:00
* it in the appropriate health / status routine .
2013-08-24 06:21:03 +04:00
*/
struct bq24190_dev_info {
struct i2c_client * client ;
struct device * dev ;
2015-03-12 10:44:11 +03:00
struct power_supply * charger ;
struct power_supply * battery ;
2017-08-30 12:48:12 +03:00
struct delayed_work input_current_limit_work ;
2013-08-24 06:21:03 +04:00
char model_name [ I2C_NAME_SIZE ] ;
2017-02-09 02:13:00 +03:00
bool initialized ;
bool irq_event ;
2017-08-29 00:23:48 +03:00
u16 sys_min ;
2017-08-29 00:23:49 +03:00
u16 iprechg ;
u16 iterm ;
2013-08-24 06:21:03 +04:00
struct mutex f_reg_lock ;
u8 f_reg ;
u8 ss_reg ;
u8 watchdog ;
} ;
/*
* The tables below provide a 2 - way mapping for the value that goes in
* the register field and the real - world value that it represents .
* The index of the array is the value that goes in the register ; the
* number at that index in the array is the real - world value that it
* represents .
*/
2017-03-23 11:32:35 +03:00
/* REG00[2:0] (IINLIM) in uAh */
2017-04-11 14:59:55 +03:00
static const int bq24190_isc_iinlim_values [ ] = {
100000 , 150000 , 500000 , 900000 , 1200000 , 1500000 , 2000000 , 3000000
} ;
2017-03-23 11:32:35 +03:00
2013-08-24 06:21:03 +04:00
/* REG02[7:2] (ICHG) in uAh */
static const int bq24190_ccc_ichg_values [ ] = {
512000 , 576000 , 640000 , 704000 , 768000 , 832000 , 896000 , 960000 ,
1024000 , 1088000 , 1152000 , 1216000 , 1280000 , 1344000 , 1408000 , 1472000 ,
1536000 , 1600000 , 1664000 , 1728000 , 1792000 , 1856000 , 1920000 , 1984000 ,
2048000 , 2112000 , 2176000 , 2240000 , 2304000 , 2368000 , 2432000 , 2496000 ,
2560000 , 2624000 , 2688000 , 2752000 , 2816000 , 2880000 , 2944000 , 3008000 ,
3072000 , 3136000 , 3200000 , 3264000 , 3328000 , 3392000 , 3456000 , 3520000 ,
3584000 , 3648000 , 3712000 , 3776000 , 3840000 , 3904000 , 3968000 , 4032000 ,
4096000 , 4160000 , 4224000 , 4288000 , 4352000 , 4416000 , 4480000 , 4544000
} ;
/* REG04[7:2] (VREG) in uV */
static const int bq24190_cvc_vreg_values [ ] = {
3504000 , 3520000 , 3536000 , 3552000 , 3568000 , 3584000 , 3600000 , 3616000 ,
3632000 , 3648000 , 3664000 , 3680000 , 3696000 , 3712000 , 3728000 , 3744000 ,
3760000 , 3776000 , 3792000 , 3808000 , 3824000 , 3840000 , 3856000 , 3872000 ,
3888000 , 3904000 , 3920000 , 3936000 , 3952000 , 3968000 , 3984000 , 4000000 ,
4016000 , 4032000 , 4048000 , 4064000 , 4080000 , 4096000 , 4112000 , 4128000 ,
4144000 , 4160000 , 4176000 , 4192000 , 4208000 , 4224000 , 4240000 , 4256000 ,
4272000 , 4288000 , 4304000 , 4320000 , 4336000 , 4352000 , 4368000 , 4384000 ,
4400000
} ;
2016-12-29 00:59:26 +03:00
/* REG06[1:0] (TREG) in tenths of degrees Celsius */
2013-08-24 06:21:03 +04:00
static const int bq24190_ictrc_treg_values [ ] = {
600 , 800 , 1000 , 1200
} ;
/*
* Return the index in ' tbl ' of greatest value that is less than or equal to
* ' val ' . The index range returned is 0 to ' tbl_size ' - 1. Assumes that
* the values in ' tbl ' are sorted from smallest to largest and ' tbl_size '
* is less than 2 ^ 8.
*/
static u8 bq24190_find_idx ( const int tbl [ ] , int tbl_size , int v )
{
int i ;
for ( i = 1 ; i < tbl_size ; i + + )
if ( v < tbl [ i ] )
break ;
return i - 1 ;
}
/* Basic driver I/O routines */
static int bq24190_read ( struct bq24190_dev_info * bdi , u8 reg , u8 * data )
{
int ret ;
ret = i2c_smbus_read_byte_data ( bdi - > client , reg ) ;
if ( ret < 0 )
return ret ;
* data = ret ;
return 0 ;
}
static int bq24190_write ( struct bq24190_dev_info * bdi , u8 reg , u8 data )
{
return i2c_smbus_write_byte_data ( bdi - > client , reg , data ) ;
}
static int bq24190_read_mask ( struct bq24190_dev_info * bdi , u8 reg ,
u8 mask , u8 shift , u8 * data )
{
u8 v ;
int ret ;
ret = bq24190_read ( bdi , reg , & v ) ;
if ( ret < 0 )
return ret ;
v & = mask ;
v > > = shift ;
* data = v ;
return 0 ;
}
static int bq24190_write_mask ( struct bq24190_dev_info * bdi , u8 reg ,
u8 mask , u8 shift , u8 data )
{
u8 v ;
int ret ;
ret = bq24190_read ( bdi , reg , & v ) ;
if ( ret < 0 )
return ret ;
v & = ~ mask ;
v | = ( ( data < < shift ) & mask ) ;
return bq24190_write ( bdi , reg , v ) ;
}
static int bq24190_get_field_val ( struct bq24190_dev_info * bdi ,
u8 reg , u8 mask , u8 shift ,
const int tbl [ ] , int tbl_size ,
int * val )
{
u8 v ;
int ret ;
ret = bq24190_read_mask ( bdi , reg , mask , shift , & v ) ;
if ( ret < 0 )
return ret ;
v = ( v > = tbl_size ) ? ( tbl_size - 1 ) : v ;
* val = tbl [ v ] ;
return 0 ;
}
static int bq24190_set_field_val ( struct bq24190_dev_info * bdi ,
u8 reg , u8 mask , u8 shift ,
const int tbl [ ] , int tbl_size ,
int val )
{
u8 idx ;
idx = bq24190_find_idx ( tbl , tbl_size , val ) ;
return bq24190_write_mask ( bdi , reg , mask , shift , idx ) ;
}
# ifdef CONFIG_SYSFS
/*
* There are a numerous options that are configurable on the bq24190
* that go well beyond what the power_supply properties provide access to .
* Provide sysfs access to them so they can be examined and possibly modified
* on the fly . They will be provided for the charger power_supply object only
* and will be prefixed by ' f_ ' to make them easier to recognize .
*/
# define BQ24190_SYSFS_FIELD(_name, r, f, m, store) \
{ \
. attr = __ATTR ( f_ # # _name , m , bq24190_sysfs_show , store ) , \
. reg = BQ24190_REG_ # # r , \
. mask = BQ24190_REG_ # # r # # _ # # f # # _MASK , \
. shift = BQ24190_REG_ # # r # # _ # # f # # _SHIFT , \
}
# define BQ24190_SYSFS_FIELD_RW(_name, r, f) \
BQ24190_SYSFS_FIELD ( _name , r , f , S_IWUSR | S_IRUGO , \
bq24190_sysfs_store )
# define BQ24190_SYSFS_FIELD_RO(_name, r, f) \
BQ24190_SYSFS_FIELD ( _name , r , f , S_IRUGO , NULL )
static ssize_t bq24190_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
static ssize_t bq24190_sysfs_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count ) ;
struct bq24190_sysfs_field_info {
struct device_attribute attr ;
u8 reg ;
u8 mask ;
u8 shift ;
} ;
2013-08-29 04:45:07 +04:00
/* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */
# undef SS
2013-08-24 06:21:03 +04:00
static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl [ ] = {
/* sysfs name reg field in reg */
BQ24190_SYSFS_FIELD_RW ( en_hiz , ISC , EN_HIZ ) ,
BQ24190_SYSFS_FIELD_RW ( vindpm , ISC , VINDPM ) ,
BQ24190_SYSFS_FIELD_RW ( iinlim , ISC , IINLIM ) ,
BQ24190_SYSFS_FIELD_RW ( chg_config , POC , CHG_CONFIG ) ,
BQ24190_SYSFS_FIELD_RW ( sys_min , POC , SYS_MIN ) ,
BQ24190_SYSFS_FIELD_RW ( boost_lim , POC , BOOST_LIM ) ,
BQ24190_SYSFS_FIELD_RW ( ichg , CCC , ICHG ) ,
BQ24190_SYSFS_FIELD_RW ( force_20_pct , CCC , FORCE_20PCT ) ,
BQ24190_SYSFS_FIELD_RW ( iprechg , PCTCC , IPRECHG ) ,
BQ24190_SYSFS_FIELD_RW ( iterm , PCTCC , ITERM ) ,
BQ24190_SYSFS_FIELD_RW ( vreg , CVC , VREG ) ,
BQ24190_SYSFS_FIELD_RW ( batlowv , CVC , BATLOWV ) ,
BQ24190_SYSFS_FIELD_RW ( vrechg , CVC , VRECHG ) ,
BQ24190_SYSFS_FIELD_RW ( en_term , CTTC , EN_TERM ) ,
BQ24190_SYSFS_FIELD_RW ( term_stat , CTTC , TERM_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( watchdog , CTTC , WATCHDOG ) ,
BQ24190_SYSFS_FIELD_RW ( en_timer , CTTC , EN_TIMER ) ,
BQ24190_SYSFS_FIELD_RW ( chg_timer , CTTC , CHG_TIMER ) ,
BQ24190_SYSFS_FIELD_RW ( jeta_iset , CTTC , JEITA_ISET ) ,
BQ24190_SYSFS_FIELD_RW ( bat_comp , ICTRC , BAT_COMP ) ,
BQ24190_SYSFS_FIELD_RW ( vclamp , ICTRC , VCLAMP ) ,
BQ24190_SYSFS_FIELD_RW ( treg , ICTRC , TREG ) ,
BQ24190_SYSFS_FIELD_RW ( dpdm_en , MOC , DPDM_EN ) ,
BQ24190_SYSFS_FIELD_RW ( tmr2x_en , MOC , TMR2X_EN ) ,
BQ24190_SYSFS_FIELD_RW ( batfet_disable , MOC , BATFET_DISABLE ) ,
BQ24190_SYSFS_FIELD_RW ( jeita_vset , MOC , JEITA_VSET ) ,
BQ24190_SYSFS_FIELD_RO ( int_mask , MOC , INT_MASK ) ,
BQ24190_SYSFS_FIELD_RO ( vbus_stat , SS , VBUS_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( chrg_stat , SS , CHRG_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( dpm_stat , SS , DPM_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( pg_stat , SS , PG_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( therm_stat , SS , THERM_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( vsys_stat , SS , VSYS_STAT ) ,
BQ24190_SYSFS_FIELD_RO ( watchdog_fault , F , WATCHDOG_FAULT ) ,
BQ24190_SYSFS_FIELD_RO ( boost_fault , F , BOOST_FAULT ) ,
BQ24190_SYSFS_FIELD_RO ( chrg_fault , F , CHRG_FAULT ) ,
BQ24190_SYSFS_FIELD_RO ( bat_fault , F , BAT_FAULT ) ,
BQ24190_SYSFS_FIELD_RO ( ntc_fault , F , NTC_FAULT ) ,
BQ24190_SYSFS_FIELD_RO ( pn , VPRS , PN ) ,
BQ24190_SYSFS_FIELD_RO ( ts_profile , VPRS , TS_PROFILE ) ,
BQ24190_SYSFS_FIELD_RO ( dev_reg , VPRS , DEV_REG ) ,
} ;
static struct attribute *
bq24190_sysfs_attrs [ ARRAY_SIZE ( bq24190_sysfs_field_tbl ) + 1 ] ;
static const struct attribute_group bq24190_sysfs_attr_group = {
. attrs = bq24190_sysfs_attrs ,
} ;
static void bq24190_sysfs_init_attrs ( void )
{
int i , limit = ARRAY_SIZE ( bq24190_sysfs_field_tbl ) ;
for ( i = 0 ; i < limit ; i + + )
bq24190_sysfs_attrs [ i ] = & bq24190_sysfs_field_tbl [ i ] . attr . attr ;
bq24190_sysfs_attrs [ limit ] = NULL ; /* Has additional entry for this */
}
static struct bq24190_sysfs_field_info * bq24190_sysfs_field_lookup (
const char * name )
{
int i , limit = ARRAY_SIZE ( bq24190_sysfs_field_tbl ) ;
for ( i = 0 ; i < limit ; i + + )
if ( ! strcmp ( name , bq24190_sysfs_field_tbl [ i ] . attr . attr . name ) )
break ;
if ( i > = limit )
return NULL ;
return & bq24190_sysfs_field_tbl [ i ] ;
}
static ssize_t bq24190_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct power_supply * psy = dev_get_drvdata ( dev ) ;
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
struct bq24190_sysfs_field_info * info ;
2017-02-09 02:13:01 +03:00
ssize_t count ;
2013-08-24 06:21:03 +04:00
int ret ;
u8 v ;
info = bq24190_sysfs_field_lookup ( attr - > attr . name ) ;
if ( ! info )
return - EINVAL ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
ret = bq24190_read_mask ( bdi , info - > reg , info - > mask , info - > shift , & v ) ;
if ( ret )
2017-02-09 02:13:01 +03:00
count = ret ;
else
count = scnprintf ( buf , PAGE_SIZE , " %hhx \n " , v ) ;
2013-08-24 06:21:03 +04:00
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
return count ;
2013-08-24 06:21:03 +04:00
}
static ssize_t bq24190_sysfs_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct power_supply * psy = dev_get_drvdata ( dev ) ;
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
struct bq24190_sysfs_field_info * info ;
int ret ;
u8 v ;
info = bq24190_sysfs_field_lookup ( attr - > attr . name ) ;
if ( ! info )
return - EINVAL ;
ret = kstrtou8 ( buf , 0 , & v ) ;
if ( ret < 0 )
return ret ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
ret = bq24190_write_mask ( bdi , info - > reg , info - > mask , info - > shift , v ) ;
if ( ret )
2017-02-09 02:13:01 +03:00
count = ret ;
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
return count ;
}
static int bq24190_sysfs_create_group ( struct bq24190_dev_info * bdi )
{
bq24190_sysfs_init_attrs ( ) ;
2015-03-12 10:44:11 +03:00
return sysfs_create_group ( & bdi - > charger - > dev . kobj ,
2013-08-24 06:21:03 +04:00
& bq24190_sysfs_attr_group ) ;
}
static void bq24190_sysfs_remove_group ( struct bq24190_dev_info * bdi )
{
2015-03-12 10:44:11 +03:00
sysfs_remove_group ( & bdi - > charger - > dev . kobj , & bq24190_sysfs_attr_group ) ;
2013-08-24 06:21:03 +04:00
}
# else
static int bq24190_sysfs_create_group ( struct bq24190_dev_info * bdi )
{
return 0 ;
}
static inline void bq24190_sysfs_remove_group ( struct bq24190_dev_info * bdi ) { }
# endif
2017-08-30 12:48:11 +03:00
# ifdef CONFIG_REGULATOR
static int bq24190_set_charge_mode ( struct regulator_dev * dev , u8 val )
{
struct bq24190_dev_info * bdi = rdev_get_drvdata ( dev ) ;
int ret ;
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , ret ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
return ret ;
}
ret = bq24190_write_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_CHG_CONFIG_MASK ,
BQ24190_REG_POC_CHG_CONFIG_SHIFT , val ) ;
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
return ret ;
}
static int bq24190_vbus_enable ( struct regulator_dev * dev )
{
return bq24190_set_charge_mode ( dev , BQ24190_REG_POC_CHG_CONFIG_OTG ) ;
}
static int bq24190_vbus_disable ( struct regulator_dev * dev )
{
return bq24190_set_charge_mode ( dev , BQ24190_REG_POC_CHG_CONFIG_CHARGE ) ;
}
static int bq24190_vbus_is_enabled ( struct regulator_dev * dev )
{
struct bq24190_dev_info * bdi = rdev_get_drvdata ( dev ) ;
int ret ;
u8 val ;
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , ret ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
return ret ;
}
ret = bq24190_read_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_CHG_CONFIG_MASK ,
BQ24190_REG_POC_CHG_CONFIG_SHIFT , & val ) ;
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
return ret ? ret : val = = BQ24190_REG_POC_CHG_CONFIG_OTG ;
}
static const struct regulator_ops bq24190_vbus_ops = {
. enable = bq24190_vbus_enable ,
. disable = bq24190_vbus_disable ,
. is_enabled = bq24190_vbus_is_enabled ,
} ;
static const struct regulator_desc bq24190_vbus_desc = {
. name = " usb_otg_vbus " ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. ops = & bq24190_vbus_ops ,
. fixed_uV = 5000000 ,
. n_voltages = 1 ,
} ;
static const struct regulator_init_data bq24190_vbus_init_data = {
. constraints = {
. valid_ops_mask = REGULATOR_CHANGE_STATUS ,
} ,
} ;
static int bq24190_register_vbus_regulator ( struct bq24190_dev_info * bdi )
{
struct bq24190_platform_data * pdata = bdi - > dev - > platform_data ;
struct regulator_config cfg = { } ;
struct regulator_dev * reg ;
int ret = 0 ;
cfg . dev = bdi - > dev ;
if ( pdata & & pdata - > regulator_init_data )
cfg . init_data = pdata - > regulator_init_data ;
else
cfg . init_data = & bq24190_vbus_init_data ;
cfg . driver_data = bdi ;
reg = devm_regulator_register ( bdi - > dev , & bq24190_vbus_desc , & cfg ) ;
if ( IS_ERR ( reg ) ) {
ret = PTR_ERR ( reg ) ;
dev_err ( bdi - > dev , " Can't register regulator: %d \n " , ret ) ;
}
return ret ;
}
# else
static int bq24190_register_vbus_regulator ( struct bq24190_dev_info * bdi )
{
return 0 ;
}
# endif
2017-08-29 00:23:47 +03:00
static int bq24190_set_config ( struct bq24190_dev_info * bdi )
2013-08-24 06:21:03 +04:00
{
int ret ;
u8 v ;
ret = bq24190_read ( bdi , BQ24190_REG_CTTC , & v ) ;
if ( ret < 0 )
return ret ;
bdi - > watchdog = ( ( v & BQ24190_REG_CTTC_WATCHDOG_MASK ) > >
BQ24190_REG_CTTC_WATCHDOG_SHIFT ) ;
2017-08-29 00:23:47 +03:00
/*
* According to the " Host Mode and default Mode " section of the
* manual , a write to any register causes the bq24190 to switch
* from default mode to host mode . It will switch back to default
* mode after a WDT timeout unless the WDT is turned off as well .
* So , by simply turning off the WDT , we accomplish both with the
* same write .
*/
2013-08-24 06:21:03 +04:00
v & = ~ BQ24190_REG_CTTC_WATCHDOG_MASK ;
2017-08-29 00:23:47 +03:00
ret = bq24190_write ( bdi , BQ24190_REG_CTTC , v ) ;
if ( ret < 0 )
return ret ;
2017-08-29 00:23:48 +03:00
if ( bdi - > sys_min ) {
v = bdi - > sys_min / 100 - 30 ; // manual section 9.5.1.2, table 9
ret = bq24190_write_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_SYS_MIN_MASK ,
BQ24190_REG_POC_SYS_MIN_SHIFT ,
v ) ;
if ( ret < 0 )
return ret ;
}
2017-08-29 00:23:49 +03:00
if ( bdi - > iprechg ) {
v = bdi - > iprechg / 128 - 1 ; // manual section 9.5.1.4, table 11
ret = bq24190_write_mask ( bdi , BQ24190_REG_PCTCC ,
BQ24190_REG_PCTCC_IPRECHG_MASK ,
BQ24190_REG_PCTCC_IPRECHG_SHIFT ,
v ) ;
if ( ret < 0 )
return ret ;
}
if ( bdi - > iterm ) {
v = bdi - > iterm / 128 - 1 ; // manual section 9.5.1.4, table 11
ret = bq24190_write_mask ( bdi , BQ24190_REG_PCTCC ,
BQ24190_REG_PCTCC_ITERM_MASK ,
BQ24190_REG_PCTCC_ITERM_SHIFT ,
v ) ;
if ( ret < 0 )
return ret ;
}
2017-08-29 00:23:47 +03:00
return 0 ;
2013-08-24 06:21:03 +04:00
}
static int bq24190_register_reset ( struct bq24190_dev_info * bdi )
{
int ret , limit = 100 ;
u8 v ;
2017-11-28 12:08:30 +03:00
/*
* This prop . can be passed on device instantiation from platform code :
* struct property_entry pe [ ] =
* { PROPERTY_ENTRY_BOOL ( " disable-reset " ) , . . . } ;
* struct i2c_board_info bi =
* { . type = " bq24190 " , . addr = 0x6b , . properties = pe , . irq = irq } ;
* struct i2c_adapter ad = { . . . } ;
* i2c_add_adapter ( & ad ) ;
* i2c_new_device ( & ad , & bi ) ;
*/
2017-04-14 19:52:33 +03:00
if ( device_property_read_bool ( bdi - > dev , " disable-reset " ) )
return 0 ;
2013-08-24 06:21:03 +04:00
/* Reset the registers */
ret = bq24190_write_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_RESET_MASK ,
BQ24190_REG_POC_RESET_SHIFT ,
0x1 ) ;
if ( ret < 0 )
return ret ;
/* Reset bit will be cleared by hardware so poll until it is */
do {
ret = bq24190_read_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_RESET_MASK ,
BQ24190_REG_POC_RESET_SHIFT ,
& v ) ;
if ( ret < 0 )
return ret ;
2017-04-11 14:59:57 +03:00
if ( v = = 0 )
return 0 ;
2013-08-24 06:21:03 +04:00
2017-04-11 14:59:57 +03:00
usleep_range ( 100 , 200 ) ;
2013-08-24 06:21:03 +04:00
} while ( - - limit ) ;
2017-04-11 14:59:57 +03:00
return - EIO ;
2013-08-24 06:21:03 +04:00
}
/* Charger power supply property routines */
static int bq24190_charger_get_charge_type ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
int type , ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_CHG_CONFIG_MASK ,
BQ24190_REG_POC_CHG_CONFIG_SHIFT ,
& v ) ;
if ( ret < 0 )
return ret ;
/* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */
if ( ! v ) {
type = POWER_SUPPLY_CHARGE_TYPE_NONE ;
} else {
ret = bq24190_read_mask ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_FORCE_20PCT_MASK ,
BQ24190_REG_CCC_FORCE_20PCT_SHIFT ,
& v ) ;
if ( ret < 0 )
return ret ;
type = ( v ) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE :
POWER_SUPPLY_CHARGE_TYPE_FAST ;
}
val - > intval = type ;
return 0 ;
}
static int bq24190_charger_set_charge_type ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
u8 chg_config , force_20pct , en_term ;
int ret ;
/*
* According to the " Termination when REG02[0] = 1 " section of
* the bq24190 manual , the trickle charge could be less than the
* termination current so it recommends turning off the termination
* function .
*
* Note : AFAICT from the datasheet , the user will have to manually
* turn off the charging when in 20 % mode . If its not turned off ,
* there could be battery damage . So , use this mode at your own risk .
*/
switch ( val - > intval ) {
case POWER_SUPPLY_CHARGE_TYPE_NONE :
chg_config = 0x0 ;
break ;
case POWER_SUPPLY_CHARGE_TYPE_TRICKLE :
chg_config = 0x1 ;
force_20pct = 0x1 ;
en_term = 0x0 ;
break ;
case POWER_SUPPLY_CHARGE_TYPE_FAST :
chg_config = 0x1 ;
force_20pct = 0x0 ;
en_term = 0x1 ;
break ;
default :
return - EINVAL ;
}
if ( chg_config ) { /* Enabling the charger */
ret = bq24190_write_mask ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_FORCE_20PCT_MASK ,
BQ24190_REG_CCC_FORCE_20PCT_SHIFT ,
force_20pct ) ;
if ( ret < 0 )
return ret ;
ret = bq24190_write_mask ( bdi , BQ24190_REG_CTTC ,
BQ24190_REG_CTTC_EN_TERM_MASK ,
BQ24190_REG_CTTC_EN_TERM_SHIFT ,
en_term ) ;
if ( ret < 0 )
return ret ;
}
return bq24190_write_mask ( bdi , BQ24190_REG_POC ,
BQ24190_REG_POC_CHG_CONFIG_MASK ,
BQ24190_REG_POC_CHG_CONFIG_SHIFT , chg_config ) ;
}
static int bq24190_charger_get_health ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
2017-01-18 20:26:53 +03:00
int health ;
2013-08-24 06:21:03 +04:00
mutex_lock ( & bdi - > f_reg_lock ) ;
2017-01-18 20:26:53 +03:00
v = bdi - > f_reg ;
mutex_unlock ( & bdi - > f_reg_lock ) ;
2013-08-24 06:21:03 +04:00
2017-04-17 02:06:26 +03:00
if ( v & BQ24190_REG_F_NTC_FAULT_MASK ) {
switch ( v > > BQ24190_REG_F_NTC_FAULT_SHIFT & 0x7 ) {
case 0x1 : /* TS1 Cold */
case 0x3 : /* TS2 Cold */
case 0x5 : /* Both Cold */
health = POWER_SUPPLY_HEALTH_COLD ;
2013-08-24 06:21:03 +04:00
break ;
2017-04-17 02:06:26 +03:00
case 0x2 : /* TS1 Hot */
case 0x4 : /* TS2 Hot */
case 0x6 : /* Both Hot */
health = POWER_SUPPLY_HEALTH_OVERHEAT ;
break ;
default :
health = POWER_SUPPLY_HEALTH_UNKNOWN ;
}
} else if ( v & BQ24190_REG_F_BAT_FAULT_MASK ) {
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
} else if ( v & BQ24190_REG_F_CHRG_FAULT_MASK ) {
switch ( v > > BQ24190_REG_F_CHRG_FAULT_SHIFT & 0x3 ) {
2013-08-24 06:21:03 +04:00
case 0x1 : /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */
/*
* This could be over - voltage or under - voltage
* and there ' s no way to tell which . Instead
* of looking foolish and returning ' OVERVOLTAGE '
* when its really under - voltage , just return
* ' UNSPEC_FAILURE ' .
*/
health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE ;
break ;
case 0x2 : /* Thermal Shutdown */
health = POWER_SUPPLY_HEALTH_OVERHEAT ;
break ;
case 0x3 : /* Charge Safety Timer Expiration */
health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE ;
break ;
2017-04-17 02:06:26 +03:00
default : /* prevent compiler warning */
health = - 1 ;
2013-08-24 06:21:03 +04:00
}
2017-04-17 02:06:26 +03:00
} else if ( v & BQ24190_REG_F_BOOST_FAULT_MASK ) {
/*
* This could be over - current or over - voltage but there ' s
* no way to tell which . Return ' OVERVOLTAGE ' since there
* isn ' t an ' OVERCURRENT ' value defined that we can return
* even if it was over - current .
*/
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
} else {
health = POWER_SUPPLY_HEALTH_GOOD ;
2013-08-24 06:21:03 +04:00
}
val - > intval = health ;
return 0 ;
}
static int bq24190_charger_get_online ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
2017-04-17 02:06:26 +03:00
u8 pg_stat , batfet_disable ;
2013-08-24 06:21:03 +04:00
int ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_SS ,
BQ24190_REG_SS_PG_STAT_MASK ,
2017-04-17 02:06:26 +03:00
BQ24190_REG_SS_PG_STAT_SHIFT , & pg_stat ) ;
2013-08-24 06:21:03 +04:00
if ( ret < 0 )
return ret ;
2017-04-17 02:06:26 +03:00
ret = bq24190_read_mask ( bdi , BQ24190_REG_MOC ,
BQ24190_REG_MOC_BATFET_DISABLE_MASK ,
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT , & batfet_disable ) ;
if ( ret < 0 )
return ret ;
val - > intval = pg_stat & & ! batfet_disable ;
2013-08-24 06:21:03 +04:00
return 0 ;
}
2017-04-17 02:06:26 +03:00
static int bq24190_battery_set_online ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val ) ;
static int bq24190_battery_get_status ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val ) ;
static int bq24190_battery_get_temp_alert_max ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val ) ;
static int bq24190_battery_set_temp_alert_max ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val ) ;
static int bq24190_charger_set_online ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_battery_set_online ( bdi , val ) ;
}
static int bq24190_charger_get_status ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
return bq24190_battery_get_status ( bdi , val ) ;
}
static int bq24190_charger_get_temp_alert_max ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
return bq24190_battery_get_temp_alert_max ( bdi , val ) ;
}
static int bq24190_charger_set_temp_alert_max ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_battery_set_temp_alert_max ( bdi , val ) ;
}
2017-08-29 00:23:49 +03:00
static int bq24190_charger_get_precharge ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
int ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_PCTCC ,
BQ24190_REG_PCTCC_IPRECHG_MASK ,
BQ24190_REG_PCTCC_IPRECHG_SHIFT , & v ) ;
if ( ret < 0 )
return ret ;
val - > intval = + + v * 128 * 1000 ;
return 0 ;
}
static int bq24190_charger_get_charge_term ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
int ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_PCTCC ,
BQ24190_REG_PCTCC_ITERM_MASK ,
BQ24190_REG_PCTCC_ITERM_SHIFT , & v ) ;
if ( ret < 0 )
return ret ;
val - > intval = + + v * 128 * 1000 ;
return 0 ;
}
2013-08-24 06:21:03 +04:00
static int bq24190_charger_get_current ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
int curr , ret ;
ret = bq24190_get_field_val ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_ICHG_MASK , BQ24190_REG_CCC_ICHG_SHIFT ,
bq24190_ccc_ichg_values ,
ARRAY_SIZE ( bq24190_ccc_ichg_values ) , & curr ) ;
if ( ret < 0 )
return ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_FORCE_20PCT_MASK ,
BQ24190_REG_CCC_FORCE_20PCT_SHIFT , & v ) ;
if ( ret < 0 )
return ret ;
/* If FORCE_20PCT is enabled, then current is 20% of ICHG value */
if ( v )
curr / = 5 ;
val - > intval = curr ;
return 0 ;
}
static int bq24190_charger_get_current_max ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
int idx = ARRAY_SIZE ( bq24190_ccc_ichg_values ) - 1 ;
val - > intval = bq24190_ccc_ichg_values [ idx ] ;
return 0 ;
}
static int bq24190_charger_set_current ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
u8 v ;
int ret , curr = val - > intval ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_FORCE_20PCT_MASK ,
BQ24190_REG_CCC_FORCE_20PCT_SHIFT , & v ) ;
if ( ret < 0 )
return ret ;
/* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */
if ( v )
curr * = 5 ;
return bq24190_set_field_val ( bdi , BQ24190_REG_CCC ,
BQ24190_REG_CCC_ICHG_MASK , BQ24190_REG_CCC_ICHG_SHIFT ,
bq24190_ccc_ichg_values ,
ARRAY_SIZE ( bq24190_ccc_ichg_values ) , curr ) ;
}
static int bq24190_charger_get_voltage ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
int voltage , ret ;
ret = bq24190_get_field_val ( bdi , BQ24190_REG_CVC ,
BQ24190_REG_CVC_VREG_MASK , BQ24190_REG_CVC_VREG_SHIFT ,
bq24190_cvc_vreg_values ,
ARRAY_SIZE ( bq24190_cvc_vreg_values ) , & voltage ) ;
if ( ret < 0 )
return ret ;
val - > intval = voltage ;
return 0 ;
}
static int bq24190_charger_get_voltage_max ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
int idx = ARRAY_SIZE ( bq24190_cvc_vreg_values ) - 1 ;
val - > intval = bq24190_cvc_vreg_values [ idx ] ;
return 0 ;
}
static int bq24190_charger_set_voltage ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_set_field_val ( bdi , BQ24190_REG_CVC ,
BQ24190_REG_CVC_VREG_MASK , BQ24190_REG_CVC_VREG_SHIFT ,
bq24190_cvc_vreg_values ,
ARRAY_SIZE ( bq24190_cvc_vreg_values ) , val - > intval ) ;
}
2017-08-15 23:04:58 +03:00
static int bq24190_charger_get_iinlimit ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
int iinlimit , ret ;
ret = bq24190_get_field_val ( bdi , BQ24190_REG_ISC ,
BQ24190_REG_ISC_IINLIM_MASK ,
BQ24190_REG_ISC_IINLIM_SHIFT ,
bq24190_isc_iinlim_values ,
ARRAY_SIZE ( bq24190_isc_iinlim_values ) , & iinlimit ) ;
if ( ret < 0 )
return ret ;
val - > intval = iinlimit ;
return 0 ;
}
static int bq24190_charger_set_iinlimit ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_set_field_val ( bdi , BQ24190_REG_ISC ,
BQ24190_REG_ISC_IINLIM_MASK ,
BQ24190_REG_ISC_IINLIM_SHIFT ,
bq24190_isc_iinlim_values ,
ARRAY_SIZE ( bq24190_isc_iinlim_values ) , val - > intval ) ;
}
2013-08-24 06:21:03 +04:00
static int bq24190_charger_get_property ( struct power_supply * psy ,
enum power_supply_property psp , union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
int ret ;
dev_dbg ( bdi - > dev , " prop: %d \n " , psp ) ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
switch ( psp ) {
case POWER_SUPPLY_PROP_CHARGE_TYPE :
ret = bq24190_charger_get_charge_type ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_HEALTH :
ret = bq24190_charger_get_health ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
ret = bq24190_charger_get_online ( bdi , val ) ;
break ;
2017-04-17 02:06:26 +03:00
case POWER_SUPPLY_PROP_STATUS :
ret = bq24190_charger_get_status ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
ret = bq24190_charger_get_temp_alert_max ( bdi , val ) ;
break ;
2017-08-29 00:23:49 +03:00
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT :
ret = bq24190_charger_get_precharge ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT :
ret = bq24190_charger_get_charge_term ( bdi , val ) ;
break ;
2013-08-24 06:21:03 +04:00
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
ret = bq24190_charger_get_current ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX :
ret = bq24190_charger_get_current_max ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
ret = bq24190_charger_get_voltage ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX :
ret = bq24190_charger_get_voltage_max ( bdi , val ) ;
break ;
2017-08-15 23:04:58 +03:00
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
ret = bq24190_charger_get_iinlimit ( bdi , val ) ;
break ;
2013-08-24 06:21:03 +04:00
case POWER_SUPPLY_PROP_SCOPE :
val - > intval = POWER_SUPPLY_SCOPE_SYSTEM ;
ret = 0 ;
break ;
case POWER_SUPPLY_PROP_MODEL_NAME :
val - > strval = bdi - > model_name ;
ret = 0 ;
break ;
case POWER_SUPPLY_PROP_MANUFACTURER :
val - > strval = BQ24190_MANUFACTURER ;
ret = 0 ;
break ;
default :
ret = - ENODATA ;
}
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
return ret ;
}
static int bq24190_charger_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
int ret ;
dev_dbg ( bdi - > dev , " prop: %d \n " , psp ) ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
switch ( psp ) {
2017-04-17 02:06:26 +03:00
case POWER_SUPPLY_PROP_ONLINE :
ret = bq24190_charger_set_online ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
ret = bq24190_charger_set_temp_alert_max ( bdi , val ) ;
break ;
2013-08-24 06:21:03 +04:00
case POWER_SUPPLY_PROP_CHARGE_TYPE :
ret = bq24190_charger_set_charge_type ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
ret = bq24190_charger_set_current ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
ret = bq24190_charger_set_voltage ( bdi , val ) ;
break ;
2017-08-15 23:04:58 +03:00
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
ret = bq24190_charger_set_iinlimit ( bdi , val ) ;
break ;
2013-08-24 06:21:03 +04:00
default :
ret = - EINVAL ;
}
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
return ret ;
}
static int bq24190_charger_property_is_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
switch ( psp ) {
2017-04-17 02:06:26 +03:00
case POWER_SUPPLY_PROP_ONLINE :
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
2013-08-24 06:21:03 +04:00
case POWER_SUPPLY_PROP_CHARGE_TYPE :
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
2017-08-15 23:04:58 +03:00
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
2017-12-27 12:20:42 +03:00
return 1 ;
2013-08-24 06:21:03 +04:00
default :
2017-12-27 12:20:42 +03:00
return 0 ;
2013-08-24 06:21:03 +04:00
}
}
2017-08-30 12:48:12 +03:00
static void bq24190_input_current_limit_work ( struct work_struct * work )
{
struct bq24190_dev_info * bdi =
container_of ( work , struct bq24190_dev_info ,
input_current_limit_work . work ) ;
power_supply_set_input_current_limit_from_supplier ( bdi - > charger ) ;
}
/* Sync the input-current-limit with our parent supply (if we have one) */
static void bq24190_charger_external_power_changed ( struct power_supply * psy )
{
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
/*
* The Power - Good detection may take up to 220 ms , sometimes
* the external charger detection is quicker , and the bq24190 will
* reset to iinlim based on its own charger detection ( which is not
* hooked up when using external charger detection ) resulting in a
* too low default 500 mA iinlim . Delay setting the input - current - limit
* for 300 ms to avoid this .
*/
queue_delayed_work ( system_wq , & bdi - > input_current_limit_work ,
msecs_to_jiffies ( 300 ) ) ;
}
2013-08-24 06:21:03 +04:00
static enum power_supply_property bq24190_charger_properties [ ] = {
2015-08-04 19:36:17 +03:00
POWER_SUPPLY_PROP_CHARGE_TYPE ,
2013-08-24 06:21:03 +04:00
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_ONLINE ,
2017-04-17 02:06:26 +03:00
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX ,
2017-08-29 00:23:49 +03:00
POWER_SUPPLY_PROP_PRECHARGE_CURRENT ,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT ,
2013-08-24 06:21:03 +04:00
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX ,
2017-08-15 23:04:58 +03:00
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT ,
2013-08-24 06:21:03 +04:00
POWER_SUPPLY_PROP_SCOPE ,
POWER_SUPPLY_PROP_MODEL_NAME ,
POWER_SUPPLY_PROP_MANUFACTURER ,
} ;
static char * bq24190_charger_supplied_to [ ] = {
" main-battery " ,
} ;
2015-03-12 10:44:11 +03:00
static const struct power_supply_desc bq24190_charger_desc = {
. name = " bq24190-charger " ,
. type = POWER_SUPPLY_TYPE_USB ,
. properties = bq24190_charger_properties ,
. num_properties = ARRAY_SIZE ( bq24190_charger_properties ) ,
. get_property = bq24190_charger_get_property ,
. set_property = bq24190_charger_set_property ,
. property_is_writeable = bq24190_charger_property_is_writeable ,
2017-08-30 12:48:12 +03:00
. external_power_changed = bq24190_charger_external_power_changed ,
2015-03-12 10:44:11 +03:00
} ;
2013-08-24 06:21:03 +04:00
/* Battery power supply property routines */
static int bq24190_battery_get_status ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 ss_reg , chrg_fault ;
int status , ret ;
mutex_lock ( & bdi - > f_reg_lock ) ;
2017-01-18 20:26:53 +03:00
chrg_fault = bdi - > f_reg ;
mutex_unlock ( & bdi - > f_reg_lock ) ;
2013-08-24 06:21:03 +04:00
chrg_fault & = BQ24190_REG_F_CHRG_FAULT_MASK ;
chrg_fault > > = BQ24190_REG_F_CHRG_FAULT_SHIFT ;
ret = bq24190_read ( bdi , BQ24190_REG_SS , & ss_reg ) ;
if ( ret < 0 )
return ret ;
/*
* The battery must be discharging when any of these are true :
* - there is no good power source ;
* - there is a charge fault .
* Could also be discharging when in " supplement mode " but
* there is no way to tell when its in that mode .
*/
if ( ! ( ss_reg & BQ24190_REG_SS_PG_STAT_MASK ) | | chrg_fault ) {
status = POWER_SUPPLY_STATUS_DISCHARGING ;
} else {
ss_reg & = BQ24190_REG_SS_CHRG_STAT_MASK ;
ss_reg > > = BQ24190_REG_SS_CHRG_STAT_SHIFT ;
switch ( ss_reg ) {
case 0x0 : /* Not Charging */
status = POWER_SUPPLY_STATUS_NOT_CHARGING ;
break ;
case 0x1 : /* Pre-charge */
case 0x2 : /* Fast Charging */
status = POWER_SUPPLY_STATUS_CHARGING ;
break ;
case 0x3 : /* Charge Termination Done */
status = POWER_SUPPLY_STATUS_FULL ;
break ;
default :
ret = - EIO ;
}
}
if ( ! ret )
val - > intval = status ;
return ret ;
}
static int bq24190_battery_get_health ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 v ;
2017-01-18 20:26:53 +03:00
int health ;
2013-08-24 06:21:03 +04:00
mutex_lock ( & bdi - > f_reg_lock ) ;
2017-01-18 20:26:53 +03:00
v = bdi - > f_reg ;
mutex_unlock ( & bdi - > f_reg_lock ) ;
2013-08-24 06:21:03 +04:00
if ( v & BQ24190_REG_F_BAT_FAULT_MASK ) {
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
} else {
v & = BQ24190_REG_F_NTC_FAULT_MASK ;
v > > = BQ24190_REG_F_NTC_FAULT_SHIFT ;
switch ( v ) {
case 0x0 : /* Normal */
health = POWER_SUPPLY_HEALTH_GOOD ;
break ;
case 0x1 : /* TS1 Cold */
case 0x3 : /* TS2 Cold */
case 0x5 : /* Both Cold */
health = POWER_SUPPLY_HEALTH_COLD ;
break ;
case 0x2 : /* TS1 Hot */
case 0x4 : /* TS2 Hot */
case 0x6 : /* Both Hot */
health = POWER_SUPPLY_HEALTH_OVERHEAT ;
break ;
default :
health = POWER_SUPPLY_HEALTH_UNKNOWN ;
}
}
val - > intval = health ;
return 0 ;
}
static int bq24190_battery_get_online ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
u8 batfet_disable ;
int ret ;
ret = bq24190_read_mask ( bdi , BQ24190_REG_MOC ,
BQ24190_REG_MOC_BATFET_DISABLE_MASK ,
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT , & batfet_disable ) ;
if ( ret < 0 )
return ret ;
val - > intval = ! batfet_disable ;
return 0 ;
}
static int bq24190_battery_set_online ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_write_mask ( bdi , BQ24190_REG_MOC ,
BQ24190_REG_MOC_BATFET_DISABLE_MASK ,
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT , ! val - > intval ) ;
}
static int bq24190_battery_get_temp_alert_max ( struct bq24190_dev_info * bdi ,
union power_supply_propval * val )
{
int temp , ret ;
ret = bq24190_get_field_val ( bdi , BQ24190_REG_ICTRC ,
BQ24190_REG_ICTRC_TREG_MASK ,
BQ24190_REG_ICTRC_TREG_SHIFT ,
bq24190_ictrc_treg_values ,
ARRAY_SIZE ( bq24190_ictrc_treg_values ) , & temp ) ;
if ( ret < 0 )
return ret ;
val - > intval = temp ;
return 0 ;
}
static int bq24190_battery_set_temp_alert_max ( struct bq24190_dev_info * bdi ,
const union power_supply_propval * val )
{
return bq24190_set_field_val ( bdi , BQ24190_REG_ICTRC ,
BQ24190_REG_ICTRC_TREG_MASK ,
BQ24190_REG_ICTRC_TREG_SHIFT ,
bq24190_ictrc_treg_values ,
ARRAY_SIZE ( bq24190_ictrc_treg_values ) , val - > intval ) ;
}
static int bq24190_battery_get_property ( struct power_supply * psy ,
enum power_supply_property psp , union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
int ret ;
2017-04-17 02:06:26 +03:00
dev_warn ( bdi - > dev , " warning: /sys/class/power_supply/bq24190-battery is deprecated \n " ) ;
2013-08-24 06:21:03 +04:00
dev_dbg ( bdi - > dev , " prop: %d \n " , psp ) ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
ret = bq24190_battery_get_status ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_HEALTH :
ret = bq24190_battery_get_health ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
ret = bq24190_battery_get_online ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_TECHNOLOGY :
/* Could be Li-on or Li-polymer but no way to tell which */
val - > intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN ;
ret = 0 ;
break ;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
ret = bq24190_battery_get_temp_alert_max ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_SCOPE :
val - > intval = POWER_SUPPLY_SCOPE_SYSTEM ;
ret = 0 ;
break ;
default :
ret = - ENODATA ;
}
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
return ret ;
}
static int bq24190_battery_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct bq24190_dev_info * bdi = power_supply_get_drvdata ( psy ) ;
2013-08-24 06:21:03 +04:00
int ret ;
2017-04-17 02:06:26 +03:00
dev_warn ( bdi - > dev , " warning: /sys/class/power_supply/bq24190-battery is deprecated \n " ) ;
2013-08-24 06:21:03 +04:00
dev_dbg ( bdi - > dev , " prop: %d \n " , psp ) ;
2017-02-09 02:13:01 +03:00
ret = pm_runtime_get_sync ( bdi - > dev ) ;
if ( ret < 0 )
return ret ;
2013-08-24 06:21:03 +04:00
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
ret = bq24190_battery_set_online ( bdi , val ) ;
break ;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
ret = bq24190_battery_set_temp_alert_max ( bdi , val ) ;
break ;
default :
ret = - EINVAL ;
}
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
return ret ;
}
static int bq24190_battery_property_is_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
int ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX :
ret = 1 ;
break ;
default :
ret = 0 ;
}
return ret ;
}
static enum power_supply_property bq24190_battery_properties [ ] = {
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_TECHNOLOGY ,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX ,
POWER_SUPPLY_PROP_SCOPE ,
} ;
2015-03-12 10:44:11 +03:00
static const struct power_supply_desc bq24190_battery_desc = {
. name = " bq24190-battery " ,
. type = POWER_SUPPLY_TYPE_BATTERY ,
. properties = bq24190_battery_properties ,
. num_properties = ARRAY_SIZE ( bq24190_battery_properties ) ,
. get_property = bq24190_battery_get_property ,
. set_property = bq24190_battery_set_property ,
. property_is_writeable = bq24190_battery_property_is_writeable ,
} ;
2013-08-24 06:21:03 +04:00
2017-02-09 02:13:00 +03:00
static void bq24190_check_status ( struct bq24190_dev_info * bdi )
2013-08-24 06:21:03 +04:00
{
2017-01-18 20:26:52 +03:00
const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK ;
const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK
| BQ24190_REG_F_NTC_FAULT_MASK ;
bool alert_charger = false , alert_battery = false ;
power: bq24190_charger: suppress build warning
This patch fixes following build warning:
In file included from include/linux/printk.h:261:0,
from include/linux/kernel.h:13,
from include/linux/list.h:8,
from include/linux/module.h:9,
from drivers/power/bq24190_charger.c:11:
drivers/power/bq24190_charger.c: In function ‘bq24190_irq_handler_thread’:
include/linux/dynamic_debug.h:86:20: warning: ‘ss_reg’ may be used uninitialized in this function [-Wmaybe-uninitialized]
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
drivers/power/bq24190_charger.c:1211:5: note: ‘ss_reg’ was declared here
u8 ss_reg, f_reg;
^
In file included from include/linux/printk.h:261:0,
from include/linux/kernel.h:13,
from include/linux/list.h:8,
from include/linux/module.h:9,
from drivers/power/bq24190_charger.c:11:
include/linux/dynamic_debug.h:86:20: warning: ‘f_reg’ may be used uninitialized in this function [-Wmaybe-uninitialized]
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
drivers/power/bq24190_charger.c:1211:13: note: ‘f_reg’ was declared here
u8 ss_reg, f_reg;
Signed-off-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
2014-12-04 17:56:04 +03:00
u8 ss_reg = 0 , f_reg = 0 ;
2017-01-18 20:26:53 +03:00
int i , ret ;
2013-08-24 06:21:03 +04:00
ret = bq24190_read ( bdi , BQ24190_REG_SS , & ss_reg ) ;
if ( ret < 0 ) {
dev_err ( bdi - > dev , " Can't read SS reg: %d \n " , ret ) ;
2017-02-09 02:13:00 +03:00
return ;
2013-08-24 06:21:03 +04:00
}
2017-01-18 20:26:53 +03:00
i = 0 ;
do {
ret = bq24190_read ( bdi , BQ24190_REG_F , & f_reg ) ;
if ( ret < 0 ) {
dev_err ( bdi - > dev , " Can't read F reg: %d \n " , ret ) ;
2017-02-09 02:13:00 +03:00
return ;
2017-01-18 20:26:53 +03:00
}
} while ( f_reg & & + + i < 2 ) ;
2013-08-24 06:21:03 +04:00
2017-04-11 14:59:54 +03:00
/* ignore over/under voltage fault after disconnect */
if ( f_reg = = ( 1 < < BQ24190_REG_F_CHRG_FAULT_SHIFT ) & &
! ( ss_reg & BQ24190_REG_SS_PG_STAT_MASK ) )
f_reg = 0 ;
2013-08-24 06:21:03 +04:00
if ( f_reg ! = bdi - > f_reg ) {
2017-04-11 14:59:54 +03:00
dev_warn ( bdi - > dev ,
2017-01-18 20:26:53 +03:00
" Fault: boost %d, charge %d, battery %d, ntc %d \n " ,
! ! ( f_reg & BQ24190_REG_F_BOOST_FAULT_MASK ) ,
! ! ( f_reg & BQ24190_REG_F_CHRG_FAULT_MASK ) ,
! ! ( f_reg & BQ24190_REG_F_BAT_FAULT_MASK ) ,
! ! ( f_reg & BQ24190_REG_F_NTC_FAULT_MASK ) ) ;
mutex_lock ( & bdi - > f_reg_lock ) ;
2017-01-18 20:26:52 +03:00
if ( ( bdi - > f_reg & battery_mask_f ) ! = ( f_reg & battery_mask_f ) )
alert_battery = true ;
if ( ( bdi - > f_reg & ~ battery_mask_f ) ! = ( f_reg & ~ battery_mask_f ) )
alert_charger = true ;
2013-08-24 06:21:03 +04:00
bdi - > f_reg = f_reg ;
2017-01-18 20:26:53 +03:00
mutex_unlock ( & bdi - > f_reg_lock ) ;
2013-08-24 06:21:03 +04:00
}
2017-01-18 20:26:54 +03:00
if ( ss_reg ! = bdi - > ss_reg ) {
/*
* The device is in host mode so when PG_STAT goes from 1 - > 0
* ( i . e . , power removed ) HIZ needs to be disabled .
*/
if ( ( bdi - > ss_reg & BQ24190_REG_SS_PG_STAT_MASK ) & &
! ( ss_reg & BQ24190_REG_SS_PG_STAT_MASK ) ) {
ret = bq24190_write_mask ( bdi , BQ24190_REG_ISC ,
BQ24190_REG_ISC_EN_HIZ_MASK ,
BQ24190_REG_ISC_EN_HIZ_SHIFT ,
0 ) ;
if ( ret < 0 )
dev_err ( bdi - > dev , " Can't access ISC reg: %d \n " ,
ret ) ;
}
if ( ( bdi - > ss_reg & battery_mask_ss ) ! = ( ss_reg & battery_mask_ss ) )
alert_battery = true ;
if ( ( bdi - > ss_reg & ~ battery_mask_ss ) ! = ( ss_reg & ~ battery_mask_ss ) )
alert_charger = true ;
bdi - > ss_reg = ss_reg ;
}
2017-04-17 02:06:26 +03:00
if ( alert_charger | | alert_battery )
2017-01-18 20:26:52 +03:00
power_supply_changed ( bdi - > charger ) ;
2017-04-17 02:06:26 +03:00
if ( alert_battery & & bdi - > battery )
2017-01-18 20:26:52 +03:00
power_supply_changed ( bdi - > battery ) ;
2013-08-24 06:21:03 +04:00
dev_dbg ( bdi - > dev , " ss_reg: 0x%02x, f_reg: 0x%02x \n " , ss_reg , f_reg ) ;
2017-02-09 02:13:00 +03:00
}
static irqreturn_t bq24190_irq_handler_thread ( int irq , void * data )
{
struct bq24190_dev_info * bdi = data ;
2017-04-11 14:59:56 +03:00
int error ;
2017-02-09 02:13:00 +03:00
bdi - > irq_event = true ;
2017-04-11 14:59:56 +03:00
error = pm_runtime_get_sync ( bdi - > dev ) ;
if ( error < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , error ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
2017-02-09 02:13:01 +03:00
return IRQ_NONE ;
}
2017-02-09 02:13:00 +03:00
bq24190_check_status ( bdi ) ;
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
2017-02-09 02:13:00 +03:00
bdi - > irq_event = false ;
2013-08-24 06:21:03 +04:00
return IRQ_HANDLED ;
}
static int bq24190_hw_init ( struct bq24190_dev_info * bdi )
{
u8 v ;
int ret ;
/* First check that the device really is what its supposed to be */
ret = bq24190_read_mask ( bdi , BQ24190_REG_VPRS ,
BQ24190_REG_VPRS_PN_MASK ,
BQ24190_REG_VPRS_PN_SHIFT ,
& v ) ;
if ( ret < 0 )
2017-02-09 02:13:01 +03:00
return ret ;
2013-08-24 06:21:03 +04:00
2018-11-01 03:11:44 +03:00
switch ( v ) {
case BQ24190_REG_VPRS_PN_24190 :
case BQ24190_REG_VPRS_PN_24192 :
case BQ24190_REG_VPRS_PN_24192I :
break ;
default :
2017-03-22 17:55:31 +03:00
dev_err ( bdi - > dev , " Error unknown model: 0x%02x \n " , v ) ;
2017-02-09 02:13:01 +03:00
return - ENODEV ;
2017-03-22 17:55:31 +03:00
}
2013-08-24 06:21:03 +04:00
ret = bq24190_register_reset ( bdi ) ;
if ( ret < 0 )
2017-02-09 02:13:01 +03:00
return ret ;
2013-08-24 06:21:03 +04:00
2017-08-29 00:23:47 +03:00
ret = bq24190_set_config ( bdi ) ;
2017-01-18 20:26:52 +03:00
if ( ret < 0 )
2017-02-09 02:13:01 +03:00
return ret ;
2017-01-18 20:26:52 +03:00
2017-02-09 02:13:01 +03:00
return bq24190_read ( bdi , BQ24190_REG_SS , & bdi - > ss_reg ) ;
2013-08-24 06:21:03 +04:00
}
2017-08-29 00:23:47 +03:00
static int bq24190_get_config ( struct bq24190_dev_info * bdi )
{
2017-08-29 00:23:48 +03:00
const char * const s = " ti,system-minimum-microvolt " ;
2017-08-29 00:23:49 +03:00
struct power_supply_battery_info info = { } ;
2017-08-29 00:23:48 +03:00
int v ;
if ( device_property_read_u32 ( bdi - > dev , s , & v ) = = 0 ) {
v / = 1000 ;
if ( v > = BQ24190_REG_POC_SYS_MIN_MIN
& & v < = BQ24190_REG_POC_SYS_MIN_MAX )
bdi - > sys_min = v ;
else
dev_warn ( bdi - > dev , " invalid value for %s: %u \n " , s , v ) ;
}
2017-08-29 00:23:49 +03:00
if ( bdi - > dev - > of_node & &
! power_supply_get_battery_info ( bdi - > charger , & info ) ) {
v = info . precharge_current_ua / 1000 ;
if ( v > = BQ24190_REG_PCTCC_IPRECHG_MIN
& & v < = BQ24190_REG_PCTCC_IPRECHG_MAX )
bdi - > iprechg = v ;
else
dev_warn ( bdi - > dev , " invalid value for battery:precharge-current-microamp: %d \n " ,
v ) ;
v = info . charge_term_current_ua / 1000 ;
if ( v > = BQ24190_REG_PCTCC_ITERM_MIN
& & v < = BQ24190_REG_PCTCC_ITERM_MAX )
bdi - > iterm = v ;
else
dev_warn ( bdi - > dev , " invalid value for battery:charge-term-current-microamp: %d \n " ,
v ) ;
}
2017-08-29 00:23:47 +03:00
return 0 ;
}
2013-08-24 06:21:03 +04:00
static int bq24190_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct i2c_adapter * adapter = to_i2c_adapter ( client - > dev . parent ) ;
struct device * dev = & client - > dev ;
2015-03-12 10:44:11 +03:00
struct power_supply_config charger_cfg = { } , battery_cfg = { } ;
2013-08-24 06:21:03 +04:00
struct bq24190_dev_info * bdi ;
int ret ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) ) {
dev_err ( dev , " No support for SMBUS_BYTE_DATA \n " ) ;
return - ENODEV ;
}
bdi = devm_kzalloc ( dev , sizeof ( * bdi ) , GFP_KERNEL ) ;
if ( ! bdi ) {
dev_err ( dev , " Can't alloc bdi struct \n " ) ;
return - ENOMEM ;
}
bdi - > client = client ;
bdi - > dev = dev ;
strncpy ( bdi - > model_name , id - > name , I2C_NAME_SIZE ) ;
mutex_init ( & bdi - > f_reg_lock ) ;
2017-01-18 20:26:52 +03:00
bdi - > f_reg = 0 ;
bdi - > ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK ; /* impossible state */
2017-08-30 12:48:12 +03:00
INIT_DELAYED_WORK ( & bdi - > input_current_limit_work ,
bq24190_input_current_limit_work ) ;
2013-08-24 06:21:03 +04:00
i2c_set_clientdata ( client , bdi ) ;
2017-08-29 00:23:47 +03:00
if ( client - > irq < = 0 ) {
2013-08-24 06:21:03 +04:00
dev_err ( dev , " Can't get irq info \n " ) ;
return - EINVAL ;
}
pm_runtime_enable ( dev ) ;
2017-02-09 02:13:01 +03:00
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_set_autosuspend_delay ( dev , 600 ) ;
ret = pm_runtime_get_sync ( dev ) ;
2017-04-11 14:59:56 +03:00
if ( ret < 0 ) {
dev_err ( dev , " pm_runtime_get failed: %i \n " , ret ) ;
goto out_pmrt ;
}
2013-08-24 06:21:03 +04:00
2015-03-12 10:44:11 +03:00
charger_cfg . drv_data = bdi ;
2017-08-29 00:23:49 +03:00
charger_cfg . of_node = dev - > of_node ;
2015-03-12 10:44:11 +03:00
charger_cfg . supplied_to = bq24190_charger_supplied_to ;
charger_cfg . num_supplicants = ARRAY_SIZE ( bq24190_charger_supplied_to ) ,
bdi - > charger = power_supply_register ( dev , & bq24190_charger_desc ,
& charger_cfg ) ;
if ( IS_ERR ( bdi - > charger ) ) {
2013-08-24 06:21:03 +04:00
dev_err ( dev , " Can't register charger \n " ) ;
2015-03-12 10:44:11 +03:00
ret = PTR_ERR ( bdi - > charger ) ;
2017-04-11 14:59:56 +03:00
goto out_pmrt ;
2013-08-24 06:21:03 +04:00
}
2017-04-17 02:06:26 +03:00
/* the battery class is deprecated and will be removed. */
/* in the interim, this property hides it. */
if ( ! device_property_read_bool ( dev , " omit-battery-class " ) ) {
battery_cfg . drv_data = bdi ;
bdi - > battery = power_supply_register ( dev , & bq24190_battery_desc ,
& battery_cfg ) ;
if ( IS_ERR ( bdi - > battery ) ) {
dev_err ( dev , " Can't register battery \n " ) ;
ret = PTR_ERR ( bdi - > battery ) ;
goto out_charger ;
}
2013-08-24 06:21:03 +04:00
}
2017-08-29 00:23:47 +03:00
ret = bq24190_get_config ( bdi ) ;
if ( ret < 0 ) {
dev_err ( dev , " Can't get devicetree config \n " ) ;
goto out_charger ;
}
ret = bq24190_hw_init ( bdi ) ;
if ( ret < 0 ) {
dev_err ( dev , " Hardware init failed \n " ) ;
goto out_charger ;
}
2013-08-24 06:21:03 +04:00
ret = bq24190_sysfs_create_group ( bdi ) ;
2017-08-29 00:23:47 +03:00
if ( ret < 0 ) {
2013-08-24 06:21:03 +04:00
dev_err ( dev , " Can't create sysfs entries \n " ) ;
2017-04-17 02:06:26 +03:00
goto out_charger ;
2017-01-18 20:26:50 +03:00
}
2017-02-09 02:13:00 +03:00
bdi - > initialized = true ;
2017-03-22 17:55:30 +03:00
ret = devm_request_threaded_irq ( dev , client - > irq , NULL ,
2017-01-18 20:26:50 +03:00
bq24190_irq_handler_thread ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" bq24190-charger " , bdi ) ;
if ( ret < 0 ) {
dev_err ( dev , " Can't set up irq handler \n " ) ;
2017-04-11 14:59:56 +03:00
goto out_sysfs ;
2013-08-24 06:21:03 +04:00
}
2017-08-30 12:48:11 +03:00
ret = bq24190_register_vbus_regulator ( bdi ) ;
if ( ret < 0 )
goto out_sysfs ;
2017-03-22 17:55:30 +03:00
enable_irq_wake ( client - > irq ) ;
2017-02-09 02:13:01 +03:00
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_autosuspend ( dev ) ;
2013-08-24 06:21:03 +04:00
return 0 ;
2017-04-11 14:59:56 +03:00
out_sysfs :
2017-01-18 20:26:50 +03:00
bq24190_sysfs_remove_group ( bdi ) ;
2017-01-18 20:26:51 +03:00
2017-04-11 14:59:56 +03:00
out_charger :
2017-04-17 02:06:26 +03:00
if ( ! IS_ERR_OR_NULL ( bdi - > battery ) )
power_supply_unregister ( bdi - > battery ) ;
2017-01-18 20:26:50 +03:00
power_supply_unregister ( bdi - > charger ) ;
2017-01-18 20:26:51 +03:00
2017-04-11 14:59:56 +03:00
out_pmrt :
2017-02-09 02:13:01 +03:00
pm_runtime_put_sync ( dev ) ;
pm_runtime_dont_use_autosuspend ( dev ) ;
2017-01-18 20:26:50 +03:00
pm_runtime_disable ( dev ) ;
2013-08-24 06:21:03 +04:00
return ret ;
}
static int bq24190_remove ( struct i2c_client * client )
{
struct bq24190_dev_info * bdi = i2c_get_clientdata ( client ) ;
2017-02-09 02:13:01 +03:00
int error ;
2013-08-24 06:21:03 +04:00
2017-02-09 02:13:01 +03:00
error = pm_runtime_get_sync ( bdi - > dev ) ;
if ( error < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , error ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
}
2013-08-24 06:21:03 +04:00
2017-02-09 02:13:01 +03:00
bq24190_register_reset ( bdi ) ;
2013-08-24 06:21:03 +04:00
bq24190_sysfs_remove_group ( bdi ) ;
2017-04-17 02:06:26 +03:00
if ( bdi - > battery )
power_supply_unregister ( bdi - > battery ) ;
2015-03-12 10:44:11 +03:00
power_supply_unregister ( bdi - > charger ) ;
2017-02-09 02:13:01 +03:00
if ( error > = 0 )
pm_runtime_put_sync ( bdi - > dev ) ;
pm_runtime_dont_use_autosuspend ( bdi - > dev ) ;
2013-08-24 06:21:03 +04:00
pm_runtime_disable ( bdi - > dev ) ;
return 0 ;
}
2017-03-20 16:14:15 +03:00
static __maybe_unused int bq24190_runtime_suspend ( struct device * dev )
2017-02-09 02:13:00 +03:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct bq24190_dev_info * bdi = i2c_get_clientdata ( client ) ;
if ( ! bdi - > initialized )
return 0 ;
dev_dbg ( bdi - > dev , " %s \n " , __func__ ) ;
return 0 ;
}
2017-03-20 16:14:15 +03:00
static __maybe_unused int bq24190_runtime_resume ( struct device * dev )
2017-02-09 02:13:00 +03:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct bq24190_dev_info * bdi = i2c_get_clientdata ( client ) ;
if ( ! bdi - > initialized )
return 0 ;
if ( ! bdi - > irq_event ) {
dev_dbg ( bdi - > dev , " checking events on possible wakeirq \n " ) ;
bq24190_check_status ( bdi ) ;
}
return 0 ;
}
2017-03-20 16:14:15 +03:00
static __maybe_unused int bq24190_pm_suspend ( struct device * dev )
2013-08-24 06:21:03 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct bq24190_dev_info * bdi = i2c_get_clientdata ( client ) ;
2017-02-09 02:13:01 +03:00
int error ;
error = pm_runtime_get_sync ( bdi - > dev ) ;
if ( error < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , error ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
}
2013-08-24 06:21:03 +04:00
bq24190_register_reset ( bdi ) ;
2017-02-09 02:13:01 +03:00
if ( error > = 0 ) {
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
}
2013-08-24 06:21:03 +04:00
return 0 ;
}
2017-03-20 16:14:15 +03:00
static __maybe_unused int bq24190_pm_resume ( struct device * dev )
2013-08-24 06:21:03 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct bq24190_dev_info * bdi = i2c_get_clientdata ( client ) ;
2017-02-09 02:13:01 +03:00
int error ;
2013-08-24 06:21:03 +04:00
2017-01-18 20:26:52 +03:00
bdi - > f_reg = 0 ;
bdi - > ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK ; /* impossible state */
2013-08-24 06:21:03 +04:00
2017-02-09 02:13:01 +03:00
error = pm_runtime_get_sync ( bdi - > dev ) ;
if ( error < 0 ) {
dev_warn ( bdi - > dev , " pm_runtime_get failed: %i \n " , error ) ;
pm_runtime_put_noidle ( bdi - > dev ) ;
}
2013-08-24 06:21:03 +04:00
bq24190_register_reset ( bdi ) ;
2017-08-29 00:23:47 +03:00
bq24190_set_config ( bdi ) ;
2017-01-18 20:26:52 +03:00
bq24190_read ( bdi , BQ24190_REG_SS , & bdi - > ss_reg ) ;
2017-02-09 02:13:01 +03:00
if ( error > = 0 ) {
pm_runtime_mark_last_busy ( bdi - > dev ) ;
pm_runtime_put_autosuspend ( bdi - > dev ) ;
}
2013-08-24 06:21:03 +04:00
/* Things may have changed while suspended so alert upper layer */
2015-03-12 10:44:11 +03:00
power_supply_changed ( bdi - > charger ) ;
2017-04-17 02:06:26 +03:00
if ( bdi - > battery )
power_supply_changed ( bdi - > battery ) ;
2013-08-24 06:21:03 +04:00
return 0 ;
}
2017-02-09 02:13:00 +03:00
static const struct dev_pm_ops bq24190_pm_ops = {
SET_RUNTIME_PM_OPS ( bq24190_runtime_suspend , bq24190_runtime_resume ,
NULL )
SET_SYSTEM_SLEEP_PM_OPS ( bq24190_pm_suspend , bq24190_pm_resume )
} ;
2013-08-24 06:21:03 +04:00
static const struct i2c_device_id bq24190_i2c_ids [ ] = {
2017-03-22 17:55:31 +03:00
{ " bq24190 " } ,
2018-11-01 03:11:44 +03:00
{ " bq24192 " } ,
2017-03-22 17:55:31 +03:00
{ " bq24192i " } ,
2013-08-24 06:21:03 +04:00
{ } ,
} ;
2015-07-30 19:18:33 +03:00
MODULE_DEVICE_TABLE ( i2c , bq24190_i2c_ids ) ;
2013-08-24 06:21:03 +04:00
# ifdef CONFIG_OF
static const struct of_device_id bq24190_of_match [ ] = {
{ . compatible = " ti,bq24190 " , } ,
2018-11-01 03:11:44 +03:00
{ . compatible = " ti,bq24192 " , } ,
2017-08-29 00:23:45 +03:00
{ . compatible = " ti,bq24192i " , } ,
2013-08-24 06:21:03 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , bq24190_of_match ) ;
# else
static const struct of_device_id bq24190_of_match [ ] = {
{ } ,
} ;
# endif
static struct i2c_driver bq24190_driver = {
. probe = bq24190_probe ,
. remove = bq24190_remove ,
. id_table = bq24190_i2c_ids ,
. driver = {
. name = " bq24190-charger " ,
. pm = & bq24190_pm_ops ,
. of_match_table = of_match_ptr ( bq24190_of_match ) ,
} ,
} ;
module_i2c_driver ( bq24190_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mark A. Greer <mgreer@animalcreek.com> " ) ;
MODULE_DESCRIPTION ( " TI BQ24190 Charger Driver " ) ;