2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-05-19 16:24:40 +03:00
/*
* TI BQ25890 charger driver
*
* Copyright ( C ) 2015 Intel Corporation
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/power_supply.h>
2022-02-01 14:06:56 +01:00
# include <linux/power/bq25890_charger.h>
2015-05-19 16:24:40 +03:00
# include <linux/regmap.h>
2022-02-01 14:06:56 +01:00
# include <linux/regulator/driver.h>
2015-05-19 16:24:40 +03:00
# include <linux/types.h>
# include <linux/gpio/consumer.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/usb/phy.h>
# include <linux/acpi.h>
# include <linux/of.h>
# define BQ25890_MANUFACTURER "Texas Instruments"
# define BQ25890_IRQ_PIN "bq25890_irq"
# define BQ25890_ID 3
2019-07-05 05:37:51 -06:00
# define BQ25895_ID 7
2018-07-31 11:49:08 -06:00
# define BQ25896_ID 0
2015-05-19 16:24:40 +03:00
2022-02-01 14:06:58 +01:00
# define PUMP_EXPRESS_START_DELAY (5 * HZ)
# define PUMP_EXPRESS_MAX_TRIES 6
# define PUMP_EXPRESS_VBUS_MARGIN_uV 1000000
2020-01-02 01:46:25 +03:00
enum bq25890_chip_version {
BQ25890 ,
BQ25892 ,
BQ25895 ,
BQ25896 ,
} ;
2020-05-03 17:21:10 +02:00
static const char * const bq25890_chip_name [ ] = {
" BQ25890 " ,
" BQ25892 " ,
" BQ25895 " ,
" BQ25896 " ,
} ;
2015-05-19 16:24:40 +03:00
enum bq25890_fields {
2022-02-01 14:06:48 +01:00
F_EN_HIZ , F_EN_ILIM , F_IINLIM , /* Reg00 */
2015-05-19 16:24:40 +03:00
F_BHOT , F_BCOLD , F_VINDPM_OFS , /* Reg01 */
F_CONV_START , F_CONV_RATE , F_BOOSTF , F_ICO_EN ,
F_HVDCP_EN , F_MAXC_EN , F_FORCE_DPM , F_AUTO_DPDM_EN , /* Reg02 */
2020-01-02 01:46:25 +03:00
F_BAT_LOAD_EN , F_WD_RST , F_OTG_CFG , F_CHG_CFG , F_SYSVMIN ,
F_MIN_VBAT_SEL , /* Reg03 */
2015-05-19 16:24:40 +03:00
F_PUMPX_EN , F_ICHG , /* Reg04 */
F_IPRECHG , F_ITERM , /* Reg05 */
F_VREG , F_BATLOWV , F_VRECHG , /* Reg06 */
F_TERM_EN , F_STAT_DIS , F_WD , F_TMR_EN , F_CHG_TMR ,
F_JEITA_ISET , /* Reg07 */
F_BATCMP , F_VCLAMP , F_TREG , /* Reg08 */
F_FORCE_ICO , F_TMR2X_EN , F_BATFET_DIS , F_JEITA_VSET ,
F_BATFET_DLY , F_BATFET_RST_EN , F_PUMPX_UP , F_PUMPX_DN , /* Reg09 */
2020-01-02 01:46:25 +03:00
F_BOOSTV , F_PFM_OTG_DIS , F_BOOSTI , /* Reg0A */
F_VBUS_STAT , F_CHG_STAT , F_PG_STAT , F_SDP_STAT , F_0B_RSVD ,
F_VSYS_STAT , /* Reg0B */
2015-05-19 16:24:40 +03:00
F_WD_FAULT , F_BOOST_FAULT , F_CHG_FAULT , F_BAT_FAULT ,
F_NTC_FAULT , /* Reg0C */
F_FORCE_VINDPM , F_VINDPM , /* Reg0D */
F_THERM_STAT , F_BATV , /* Reg0E */
F_SYSV , /* Reg0F */
F_TSPCT , /* Reg10 */
F_VBUS_GD , F_VBUSV , /* Reg11 */
F_ICHGR , /* Reg12 */
F_VDPM_STAT , F_IDPM_STAT , F_IDPM_LIM , /* Reg13 */
F_REG_RST , F_ICO_OPTIMIZED , F_PN , F_TS_PROFILE , F_DEV_REV , /* Reg14 */
F_MAX_FIELDS
} ;
/* initial field values, converted to register values */
struct bq25890_init_data {
u8 ichg ; /* charge current */
u8 vreg ; /* regulation voltage */
u8 iterm ; /* termination current */
u8 iprechg ; /* precharge current */
u8 sysvmin ; /* minimum system voltage limit */
u8 boostv ; /* boost regulation voltage */
u8 boosti ; /* boost current limit */
u8 boostf ; /* boost frequency */
u8 ilim_en ; /* enable ILIM pin */
u8 treg ; /* thermal regulation threshold */
2020-09-26 21:05:34 +02:00
u8 rbatcomp ; /* IBAT sense resistor value */
u8 vclamp ; /* IBAT compensation voltage limit */
2015-05-19 16:24:40 +03:00
} ;
struct bq25890_state {
u8 online ;
u8 chrg_status ;
u8 chrg_fault ;
u8 vsys_status ;
u8 boost_fault ;
u8 bat_fault ;
2022-02-01 14:06:49 +01:00
u8 ntc_fault ;
2015-05-19 16:24:40 +03:00
} ;
struct bq25890_device {
struct i2c_client * client ;
struct device * dev ;
struct power_supply * charger ;
struct usb_phy * usb_phy ;
struct notifier_block usb_nb ;
struct work_struct usb_work ;
2022-02-01 14:06:58 +01:00
struct delayed_work pump_express_work ;
2015-05-19 16:24:40 +03:00
unsigned long usb_event ;
struct regmap * rmap ;
struct regmap_field * rmap_fields [ F_MAX_FIELDS ] ;
2022-02-01 14:06:51 +01:00
bool skip_reset ;
2022-02-01 14:06:52 +01:00
bool read_back_init_data ;
2022-02-01 14:06:58 +01:00
u32 pump_express_vbus_max ;
2020-01-02 01:46:25 +03:00
enum bq25890_chip_version chip_version ;
2015-05-19 16:24:40 +03:00
struct bq25890_init_data init_data ;
struct bq25890_state state ;
struct mutex lock ; /* protect state data */
} ;
static const struct regmap_range bq25890_readonly_reg_ranges [ ] = {
regmap_reg_range ( 0x0b , 0x0c ) ,
regmap_reg_range ( 0x0e , 0x13 ) ,
} ;
static const struct regmap_access_table bq25890_writeable_regs = {
. no_ranges = bq25890_readonly_reg_ranges ,
. n_no_ranges = ARRAY_SIZE ( bq25890_readonly_reg_ranges ) ,
} ;
static const struct regmap_range bq25890_volatile_reg_ranges [ ] = {
regmap_reg_range ( 0x00 , 0x00 ) ,
2020-05-04 21:47:45 +02:00
regmap_reg_range ( 0x02 , 0x02 ) ,
2015-05-19 16:24:40 +03:00
regmap_reg_range ( 0x09 , 0x09 ) ,
2020-01-02 01:46:25 +03:00
regmap_reg_range ( 0x0b , 0x14 ) ,
2015-05-19 16:24:40 +03:00
} ;
static const struct regmap_access_table bq25890_volatile_regs = {
. yes_ranges = bq25890_volatile_reg_ranges ,
. n_yes_ranges = ARRAY_SIZE ( bq25890_volatile_reg_ranges ) ,
} ;
static const struct regmap_config bq25890_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0x14 ,
. cache_type = REGCACHE_RBTREE ,
. wr_table = & bq25890_writeable_regs ,
. volatile_table = & bq25890_volatile_regs ,
} ;
static const struct reg_field bq25890_reg_fields [ ] = {
/* REG00 */
[ F_EN_HIZ ] = REG_FIELD ( 0x00 , 7 , 7 ) ,
[ F_EN_ILIM ] = REG_FIELD ( 0x00 , 6 , 6 ) ,
2022-02-01 14:06:48 +01:00
[ F_IINLIM ] = REG_FIELD ( 0x00 , 0 , 5 ) ,
2015-05-19 16:24:40 +03:00
/* REG01 */
[ F_BHOT ] = REG_FIELD ( 0x01 , 6 , 7 ) ,
[ F_BCOLD ] = REG_FIELD ( 0x01 , 5 , 5 ) ,
[ F_VINDPM_OFS ] = REG_FIELD ( 0x01 , 0 , 4 ) ,
/* REG02 */
[ F_CONV_START ] = REG_FIELD ( 0x02 , 7 , 7 ) ,
[ F_CONV_RATE ] = REG_FIELD ( 0x02 , 6 , 6 ) ,
[ F_BOOSTF ] = REG_FIELD ( 0x02 , 5 , 5 ) ,
[ F_ICO_EN ] = REG_FIELD ( 0x02 , 4 , 4 ) ,
2018-07-31 11:49:08 -06:00
[ F_HVDCP_EN ] = REG_FIELD ( 0x02 , 3 , 3 ) , // reserved on BQ25896
[ F_MAXC_EN ] = REG_FIELD ( 0x02 , 2 , 2 ) , // reserved on BQ25896
2015-05-19 16:24:40 +03:00
[ F_FORCE_DPM ] = REG_FIELD ( 0x02 , 1 , 1 ) ,
[ F_AUTO_DPDM_EN ] = REG_FIELD ( 0x02 , 0 , 0 ) ,
/* REG03 */
[ F_BAT_LOAD_EN ] = REG_FIELD ( 0x03 , 7 , 7 ) ,
[ F_WD_RST ] = REG_FIELD ( 0x03 , 6 , 6 ) ,
[ F_OTG_CFG ] = REG_FIELD ( 0x03 , 5 , 5 ) ,
[ F_CHG_CFG ] = REG_FIELD ( 0x03 , 4 , 4 ) ,
[ F_SYSVMIN ] = REG_FIELD ( 0x03 , 1 , 3 ) ,
2020-01-02 01:46:25 +03:00
[ F_MIN_VBAT_SEL ] = REG_FIELD ( 0x03 , 0 , 0 ) , // BQ25896 only
2015-05-19 16:24:40 +03:00
/* REG04 */
[ F_PUMPX_EN ] = REG_FIELD ( 0x04 , 7 , 7 ) ,
[ F_ICHG ] = REG_FIELD ( 0x04 , 0 , 6 ) ,
/* REG05 */
[ F_IPRECHG ] = REG_FIELD ( 0x05 , 4 , 7 ) ,
[ F_ITERM ] = REG_FIELD ( 0x05 , 0 , 3 ) ,
/* REG06 */
[ F_VREG ] = REG_FIELD ( 0x06 , 2 , 7 ) ,
[ F_BATLOWV ] = REG_FIELD ( 0x06 , 1 , 1 ) ,
[ F_VRECHG ] = REG_FIELD ( 0x06 , 0 , 0 ) ,
/* REG07 */
[ F_TERM_EN ] = REG_FIELD ( 0x07 , 7 , 7 ) ,
[ F_STAT_DIS ] = REG_FIELD ( 0x07 , 6 , 6 ) ,
[ F_WD ] = REG_FIELD ( 0x07 , 4 , 5 ) ,
[ F_TMR_EN ] = REG_FIELD ( 0x07 , 3 , 3 ) ,
[ F_CHG_TMR ] = REG_FIELD ( 0x07 , 1 , 2 ) ,
2019-07-05 05:37:51 -06:00
[ F_JEITA_ISET ] = REG_FIELD ( 0x07 , 0 , 0 ) , // reserved on BQ25895
2015-05-19 16:24:40 +03:00
/* REG08 */
2018-12-17 20:28:15 +01:00
[ F_BATCMP ] = REG_FIELD ( 0x08 , 5 , 7 ) ,
2015-05-19 16:24:40 +03:00
[ F_VCLAMP ] = REG_FIELD ( 0x08 , 2 , 4 ) ,
[ F_TREG ] = REG_FIELD ( 0x08 , 0 , 1 ) ,
/* REG09 */
[ F_FORCE_ICO ] = REG_FIELD ( 0x09 , 7 , 7 ) ,
[ F_TMR2X_EN ] = REG_FIELD ( 0x09 , 6 , 6 ) ,
[ F_BATFET_DIS ] = REG_FIELD ( 0x09 , 5 , 5 ) ,
2019-07-05 05:37:51 -06:00
[ F_JEITA_VSET ] = REG_FIELD ( 0x09 , 4 , 4 ) , // reserved on BQ25895
2015-05-19 16:24:40 +03:00
[ F_BATFET_DLY ] = REG_FIELD ( 0x09 , 3 , 3 ) ,
[ F_BATFET_RST_EN ] = REG_FIELD ( 0x09 , 2 , 2 ) ,
[ F_PUMPX_UP ] = REG_FIELD ( 0x09 , 1 , 1 ) ,
[ F_PUMPX_DN ] = REG_FIELD ( 0x09 , 0 , 0 ) ,
/* REG0A */
[ F_BOOSTV ] = REG_FIELD ( 0x0A , 4 , 7 ) ,
2019-07-05 05:37:51 -06:00
[ F_BOOSTI ] = REG_FIELD ( 0x0A , 0 , 2 ) , // reserved on BQ25895
2020-01-02 01:46:25 +03:00
[ F_PFM_OTG_DIS ] = REG_FIELD ( 0x0A , 3 , 3 ) , // BQ25896 only
2015-05-19 16:24:40 +03:00
/* REG0B */
[ F_VBUS_STAT ] = REG_FIELD ( 0x0B , 5 , 7 ) ,
[ F_CHG_STAT ] = REG_FIELD ( 0x0B , 3 , 4 ) ,
[ F_PG_STAT ] = REG_FIELD ( 0x0B , 2 , 2 ) ,
2018-07-31 11:49:08 -06:00
[ F_SDP_STAT ] = REG_FIELD ( 0x0B , 1 , 1 ) , // reserved on BQ25896
2015-05-19 16:24:40 +03:00
[ F_VSYS_STAT ] = REG_FIELD ( 0x0B , 0 , 0 ) ,
/* REG0C */
[ F_WD_FAULT ] = REG_FIELD ( 0x0C , 7 , 7 ) ,
[ F_BOOST_FAULT ] = REG_FIELD ( 0x0C , 6 , 6 ) ,
[ F_CHG_FAULT ] = REG_FIELD ( 0x0C , 4 , 5 ) ,
[ F_BAT_FAULT ] = REG_FIELD ( 0x0C , 3 , 3 ) ,
[ F_NTC_FAULT ] = REG_FIELD ( 0x0C , 0 , 2 ) ,
/* REG0D */
[ F_FORCE_VINDPM ] = REG_FIELD ( 0x0D , 7 , 7 ) ,
[ F_VINDPM ] = REG_FIELD ( 0x0D , 0 , 6 ) ,
/* REG0E */
[ F_THERM_STAT ] = REG_FIELD ( 0x0E , 7 , 7 ) ,
[ F_BATV ] = REG_FIELD ( 0x0E , 0 , 6 ) ,
/* REG0F */
[ F_SYSV ] = REG_FIELD ( 0x0F , 0 , 6 ) ,
/* REG10 */
[ F_TSPCT ] = REG_FIELD ( 0x10 , 0 , 6 ) ,
/* REG11 */
[ F_VBUS_GD ] = REG_FIELD ( 0x11 , 7 , 7 ) ,
[ F_VBUSV ] = REG_FIELD ( 0x11 , 0 , 6 ) ,
/* REG12 */
[ F_ICHGR ] = REG_FIELD ( 0x12 , 0 , 6 ) ,
/* REG13 */
[ F_VDPM_STAT ] = REG_FIELD ( 0x13 , 7 , 7 ) ,
[ F_IDPM_STAT ] = REG_FIELD ( 0x13 , 6 , 6 ) ,
[ F_IDPM_LIM ] = REG_FIELD ( 0x13 , 0 , 5 ) ,
/* REG14 */
[ F_REG_RST ] = REG_FIELD ( 0x14 , 7 , 7 ) ,
[ F_ICO_OPTIMIZED ] = REG_FIELD ( 0x14 , 6 , 6 ) ,
[ F_PN ] = REG_FIELD ( 0x14 , 3 , 5 ) ,
[ F_TS_PROFILE ] = REG_FIELD ( 0x14 , 2 , 2 ) ,
[ F_DEV_REV ] = REG_FIELD ( 0x14 , 0 , 1 )
} ;
/*
* Most of the val - > idx conversions can be computed , given the minimum ,
* maximum and the step between values . For the rest of conversions , we use
* lookup tables .
*/
enum bq25890_table_ids {
/* range tables */
TBL_ICHG ,
TBL_ITERM ,
2022-02-01 14:06:48 +01:00
TBL_IINLIM ,
2015-05-19 16:24:40 +03:00
TBL_VREG ,
TBL_BOOSTV ,
TBL_SYSVMIN ,
2022-02-01 14:06:58 +01:00
TBL_VBUSV ,
2020-09-26 21:05:34 +02:00
TBL_VBATCOMP ,
TBL_RBATCOMP ,
2015-05-19 16:24:40 +03:00
/* lookup tables */
TBL_TREG ,
TBL_BOOSTI ,
2021-11-18 08:18:45 -08:00
TBL_TSPCT ,
2015-05-19 16:24:40 +03:00
} ;
/* Thermal Regulation Threshold lookup table, in degrees Celsius */
static const u32 bq25890_treg_tbl [ ] = { 60 , 80 , 100 , 120 } ;
# define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl)
/* Boost mode current limit lookup table, in uA */
static const u32 bq25890_boosti_tbl [ ] = {
500000 , 700000 , 1100000 , 1300000 , 1600000 , 1800000 , 2100000 , 2400000
} ;
# define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl)
2021-11-18 08:18:45 -08:00
/* NTC 10K temperature lookup table in tenths of a degree */
static const u32 bq25890_tspct_tbl [ ] = {
850 , 840 , 830 , 820 , 810 , 800 , 790 , 780 ,
770 , 760 , 750 , 740 , 730 , 720 , 710 , 700 ,
690 , 685 , 680 , 675 , 670 , 660 , 650 , 645 ,
640 , 630 , 620 , 615 , 610 , 600 , 590 , 585 ,
580 , 570 , 565 , 560 , 550 , 540 , 535 , 530 ,
520 , 515 , 510 , 500 , 495 , 490 , 480 , 475 ,
470 , 460 , 455 , 450 , 440 , 435 , 430 , 425 ,
420 , 410 , 405 , 400 , 390 , 385 , 380 , 370 ,
365 , 360 , 355 , 350 , 340 , 335 , 330 , 320 ,
310 , 305 , 300 , 290 , 285 , 280 , 275 , 270 ,
260 , 250 , 245 , 240 , 230 , 225 , 220 , 210 ,
205 , 200 , 190 , 180 , 175 , 170 , 160 , 150 ,
145 , 140 , 130 , 120 , 115 , 110 , 100 , 90 ,
80 , 70 , 60 , 50 , 40 , 30 , 20 , 10 ,
0 , - 10 , - 20 , - 30 , - 40 , - 60 , - 70 , - 80 ,
- 90 , - 10 , - 120 , - 140 , - 150 , - 170 , - 190 , - 210 ,
} ;
# define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl)
2015-05-19 16:24:40 +03:00
struct bq25890_range {
u32 min ;
u32 max ;
u32 step ;
} ;
struct bq25890_lookup {
const u32 * tbl ;
u32 size ;
} ;
static const union {
struct bq25890_range rt ;
struct bq25890_lookup lt ;
} bq25890_tables [ ] = {
/* range tables */
2020-01-02 01:46:25 +03:00
/* TODO: BQ25896 has max ICHG 3008 mA */
2022-02-01 14:06:58 +01:00
[ TBL_ICHG ] = { . rt = { 0 , 5056000 , 64000 } } , /* uA */
[ TBL_ITERM ] = { . rt = { 64000 , 1024000 , 64000 } } , /* uA */
[ TBL_IINLIM ] = { . rt = { 100000 , 3250000 , 50000 } } , /* uA */
[ TBL_VREG ] = { . rt = { 3840000 , 4608000 , 16000 } } , /* uV */
[ TBL_BOOSTV ] = { . rt = { 4550000 , 5510000 , 64000 } } , /* uV */
[ TBL_SYSVMIN ] = { . rt = { 3000000 , 3700000 , 100000 } } , /* uV */
[ TBL_VBUSV ] = { . rt = { 2600000 , 15300000 , 100000 } } , /* uV */
[ TBL_VBATCOMP ] = { . rt = { 0 , 224000 , 32000 } } , /* uV */
[ TBL_RBATCOMP ] = { . rt = { 0 , 140000 , 20000 } } , /* uOhm */
2015-05-19 16:24:40 +03:00
/* lookup tables */
[ TBL_TREG ] = { . lt = { bq25890_treg_tbl , BQ25890_TREG_TBL_SIZE } } ,
2021-11-18 08:18:45 -08:00
[ TBL_BOOSTI ] = { . lt = { bq25890_boosti_tbl , BQ25890_BOOSTI_TBL_SIZE } } ,
[ TBL_TSPCT ] = { . lt = { bq25890_tspct_tbl , BQ25890_TSPCT_TBL_SIZE } }
2015-05-19 16:24:40 +03:00
} ;
static int bq25890_field_read ( struct bq25890_device * bq ,
enum bq25890_fields field_id )
{
int ret ;
int val ;
ret = regmap_field_read ( bq - > rmap_fields [ field_id ] , & val ) ;
if ( ret < 0 )
return ret ;
return val ;
}
static int bq25890_field_write ( struct bq25890_device * bq ,
enum bq25890_fields field_id , u8 val )
{
return regmap_field_write ( bq - > rmap_fields [ field_id ] , val ) ;
}
static u8 bq25890_find_idx ( u32 value , enum bq25890_table_ids id )
{
u8 idx ;
if ( id > = TBL_TREG ) {
const u32 * tbl = bq25890_tables [ id ] . lt . tbl ;
u32 tbl_size = bq25890_tables [ id ] . lt . size ;
for ( idx = 1 ; idx < tbl_size & & tbl [ idx ] < = value ; idx + + )
;
} else {
const struct bq25890_range * rtbl = & bq25890_tables [ id ] . rt ;
u8 rtbl_size ;
rtbl_size = ( rtbl - > max - rtbl - > min ) / rtbl - > step + 1 ;
for ( idx = 1 ;
idx < rtbl_size & & ( idx * rtbl - > step + rtbl - > min < = value ) ;
idx + + )
;
}
return idx - 1 ;
}
static u32 bq25890_find_val ( u8 idx , enum bq25890_table_ids id )
{
const struct bq25890_range * rtbl ;
/* lookup table? */
if ( id > = TBL_TREG )
return bq25890_tables [ id ] . lt . tbl [ idx ] ;
/* range table */
rtbl = & bq25890_tables [ id ] . rt ;
return ( rtbl - > min + idx * rtbl - > step ) ;
}
enum bq25890_status {
STATUS_NOT_CHARGING ,
STATUS_PRE_CHARGING ,
STATUS_FAST_CHARGING ,
STATUS_TERMINATION_DONE ,
} ;
enum bq25890_chrg_fault {
CHRG_FAULT_NORMAL ,
CHRG_FAULT_INPUT ,
CHRG_FAULT_THERMAL_SHUTDOWN ,
CHRG_FAULT_TIMER_EXPIRED ,
} ;
2022-02-01 14:06:49 +01:00
enum bq25890_ntc_fault {
NTC_FAULT_NORMAL = 0 ,
NTC_FAULT_WARM = 2 ,
NTC_FAULT_COOL = 3 ,
NTC_FAULT_COLD = 5 ,
NTC_FAULT_HOT = 6 ,
} ;
2020-05-04 21:47:45 +02:00
static bool bq25890_is_adc_property ( enum power_supply_property psp )
{
switch ( psp ) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW :
case POWER_SUPPLY_PROP_CURRENT_NOW :
2021-11-18 08:18:45 -08:00
case POWER_SUPPLY_PROP_TEMP :
2020-05-04 21:47:45 +02:00
return true ;
default :
return false ;
}
}
2020-05-04 21:47:45 +02:00
static irqreturn_t __bq25890_handle_irq ( struct bq25890_device * bq ) ;
2022-02-01 14:06:58 +01:00
static int bq25890_get_vbus_voltage ( struct bq25890_device * bq )
{
int ret ;
ret = bq25890_field_read ( bq , F_VBUSV ) ;
if ( ret < 0 )
return ret ;
return bq25890_find_val ( ret , TBL_VBUSV ) ;
}
2015-05-19 16:24:40 +03:00
static int bq25890_power_supply_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct bq25890_device * bq = power_supply_get_drvdata ( psy ) ;
struct bq25890_state state ;
2020-05-04 21:47:45 +02:00
bool do_adc_conv ;
int ret ;
2015-05-19 16:24:40 +03:00
mutex_lock ( & bq - > lock ) ;
2020-05-04 21:47:45 +02:00
/* update state in case we lost an interrupt */
__bq25890_handle_irq ( bq ) ;
2015-05-19 16:24:40 +03:00
state = bq - > state ;
2020-05-04 21:47:45 +02:00
do_adc_conv = ! state . online & & bq25890_is_adc_property ( psp ) ;
if ( do_adc_conv )
bq25890_field_write ( bq , F_CONV_START , 1 ) ;
2015-05-19 16:24:40 +03:00
mutex_unlock ( & bq - > lock ) ;
2020-05-04 21:47:45 +02:00
if ( do_adc_conv )
regmap_field_read_poll_timeout ( bq - > rmap_fields [ F_CONV_START ] ,
ret , ! ret , 25000 , 1000000 ) ;
2015-05-19 16:24:40 +03:00
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
if ( ! state . online )
val - > intval = POWER_SUPPLY_STATUS_DISCHARGING ;
else if ( state . chrg_status = = STATUS_NOT_CHARGING )
val - > intval = POWER_SUPPLY_STATUS_NOT_CHARGING ;
else if ( state . chrg_status = = STATUS_PRE_CHARGING | |
state . chrg_status = = STATUS_FAST_CHARGING )
val - > intval = POWER_SUPPLY_STATUS_CHARGING ;
else if ( state . chrg_status = = STATUS_TERMINATION_DONE )
val - > intval = POWER_SUPPLY_STATUS_FULL ;
else
val - > intval = POWER_SUPPLY_STATUS_UNKNOWN ;
break ;
2020-05-04 21:47:46 +02:00
case POWER_SUPPLY_PROP_CHARGE_TYPE :
if ( ! state . online | | state . chrg_status = = STATUS_NOT_CHARGING | |
state . chrg_status = = STATUS_TERMINATION_DONE )
val - > intval = POWER_SUPPLY_CHARGE_TYPE_NONE ;
else if ( state . chrg_status = = STATUS_PRE_CHARGING )
val - > intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD ;
else if ( state . chrg_status = = STATUS_FAST_CHARGING )
val - > intval = POWER_SUPPLY_CHARGE_TYPE_FAST ;
else /* unreachable */
val - > intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN ;
break ;
2015-05-19 16:24:40 +03:00
case POWER_SUPPLY_PROP_MANUFACTURER :
val - > strval = BQ25890_MANUFACTURER ;
break ;
2018-07-31 11:49:08 -06:00
case POWER_SUPPLY_PROP_MODEL_NAME :
2020-05-03 17:21:10 +02:00
val - > strval = bq25890_chip_name [ bq - > chip_version ] ;
2018-07-31 11:49:08 -06:00
break ;
2015-05-19 16:24:40 +03:00
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = state . online ;
break ;
case POWER_SUPPLY_PROP_HEALTH :
if ( ! state . chrg_fault & & ! state . bat_fault & & ! state . boost_fault )
val - > intval = POWER_SUPPLY_HEALTH_GOOD ;
else if ( state . bat_fault )
val - > intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
else if ( state . chrg_fault = = CHRG_FAULT_TIMER_EXPIRED )
val - > intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE ;
else if ( state . chrg_fault = = CHRG_FAULT_THERMAL_SHUTDOWN )
val - > intval = POWER_SUPPLY_HEALTH_OVERHEAT ;
else
val - > intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE ;
break ;
2020-05-04 21:47:46 +02:00
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT :
val - > intval = bq25890_find_val ( bq - > init_data . iprechg , TBL_ITERM ) ;
break ;
2015-05-19 16:24:40 +03:00
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT :
val - > intval = bq25890_find_val ( bq - > init_data . iterm , TBL_ITERM ) ;
break ;
2020-05-04 21:47:47 +02:00
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
2022-02-01 14:06:48 +01:00
ret = bq25890_field_read ( bq , F_IINLIM ) ;
2020-05-04 21:47:47 +02:00
if ( ret < 0 )
return ret ;
2022-02-01 14:06:48 +01:00
val - > intval = bq25890_find_val ( ret , TBL_IINLIM ) ;
2020-05-04 21:47:47 +02:00
break ;
2022-10-14 19:24:21 +02:00
case POWER_SUPPLY_PROP_CURRENT_NOW : /* I_BAT now */
/*
* This is ADC - sampled immediate charge current supplied
* from charger to battery . The property name is confusing ,
* for clarification refer to :
* Documentation / ABI / testing / sysfs - class - power
* / sys / class / power_supply / < supply_name > / current_now
*/
2020-05-04 21:47:30 +02:00
ret = bq25890_field_read ( bq , F_ICHGR ) ; /* read measured value */
if ( ret < 0 )
return ret ;
/* converted_val = ADC_val * 50mA (table 10.3.19) */
val - > intval = ret * - 50000 ;
break ;
2022-10-14 19:24:22 +02:00
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT : /* I_BAT user limit */
/*
* This is user - configured constant charge current supplied
* from charger to battery in first phase of charging , when
* battery voltage is below constant charge voltage .
*
* This value reflects the current hardware setting .
*
* The POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX is the
* maximum value of this property .
*/
ret = bq25890_field_read ( bq , F_ICHG ) ;
if ( ret < 0 )
return ret ;
val - > intval = bq25890_find_val ( ret , TBL_ICHG ) ;
/* When temperature is too low, charge current is decreased */
if ( bq - > state . ntc_fault = = NTC_FAULT_COOL ) {
ret = bq25890_field_read ( bq , F_JEITA_ISET ) ;
if ( ret < 0 )
return ret ;
if ( ret )
val - > intval / = 5 ;
else
val - > intval / = 2 ;
}
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX : /* I_BAT max */
/*
* This is maximum allowed constant charge current supplied
* from charger to battery in first phase of charging , when
* battery voltage is below constant charge voltage .
*
* This value is constant for each battery and set from DT .
*/
val - > intval = bq25890_find_val ( bq - > init_data . ichg , TBL_ICHG ) ;
break ;
2022-10-14 19:24:23 +02:00
case POWER_SUPPLY_PROP_VOLTAGE_NOW : /* V_BAT now */
/*
* This is ADC - sampled immediate charge voltage supplied
* from charger to battery . The property name is confusing ,
* for clarification refer to :
* Documentation / ABI / testing / sysfs - class - power
* / sys / class / power_supply / < supply_name > / voltage_now
*/
ret = bq25890_field_read ( bq , F_BATV ) ; /* read measured value */
if ( ret < 0 )
return ret ;
/* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
val - > intval = 2304000 + ret * 20000 ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE : /* V_BAT user limit */
/*
* This is user - configured constant charge voltage supplied
* from charger to battery in second phase of charging , when
* battery voltage reached constant charge voltage .
*
* This value reflects the current hardware setting .
*
* The POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX is the
* maximum value of this property .
*/
ret = bq25890_field_read ( bq , F_VREG ) ;
if ( ret < 0 )
return ret ;
val - > intval = bq25890_find_val ( ret , TBL_VREG ) ;
break ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX : /* V_BAT max */
/*
* This is maximum allowed constant charge voltage supplied
* from charger to battery in second phase of charging , when
* battery voltage reached constant charge voltage .
*
* This value is constant for each battery and set from DT .
*/
val - > intval = bq25890_find_val ( bq - > init_data . vreg , TBL_VREG ) ;
break ;
2021-11-18 08:18:45 -08:00
case POWER_SUPPLY_PROP_TEMP :
ret = bq25890_field_read ( bq , F_TSPCT ) ;
if ( ret < 0 )
return ret ;
/* convert TS percentage into rough temperature */
val - > intval = bq25890_find_val ( ret , TBL_TSPCT ) ;
break ;
2015-05-19 16:24:40 +03:00
default :
return - EINVAL ;
}
return 0 ;
}
2022-08-01 04:57:27 +02:00
static int bq25890_power_supply_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
struct bq25890_device * bq = power_supply_get_drvdata ( psy ) ;
u8 lval ;
switch ( psp ) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
2022-09-12 07:15:53 -07:00
lval = bq25890_find_idx ( val - > intval , TBL_IINLIM ) ;
2022-08-01 04:57:27 +02:00
return bq25890_field_write ( bq , F_IINLIM , lval ) ;
default :
return - EINVAL ;
}
}
static int bq25890_power_supply_property_is_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
switch ( psp ) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
return true ;
default :
return false ;
}
}
2022-02-01 14:06:57 +01:00
/* On the BQ25892 try to get charger-type info from our supplier */
static void bq25890_charger_external_power_changed ( struct power_supply * psy )
{
struct bq25890_device * bq = power_supply_get_drvdata ( psy ) ;
union power_supply_propval val ;
int input_current_limit , ret ;
if ( bq - > chip_version ! = BQ25892 )
return ;
ret = power_supply_get_property_from_supplier ( bq - > charger ,
POWER_SUPPLY_PROP_USB_TYPE ,
& val ) ;
if ( ret )
return ;
switch ( val . intval ) {
case POWER_SUPPLY_USB_TYPE_DCP :
input_current_limit = bq25890_find_idx ( 2000000 , TBL_IINLIM ) ;
2022-02-01 14:06:58 +01:00
if ( bq - > pump_express_vbus_max ) {
queue_delayed_work ( system_power_efficient_wq ,
& bq - > pump_express_work ,
PUMP_EXPRESS_START_DELAY ) ;
}
2022-02-01 14:06:57 +01:00
break ;
case POWER_SUPPLY_USB_TYPE_CDP :
case POWER_SUPPLY_USB_TYPE_ACA :
input_current_limit = bq25890_find_idx ( 1500000 , TBL_IINLIM ) ;
break ;
case POWER_SUPPLY_USB_TYPE_SDP :
default :
input_current_limit = bq25890_find_idx ( 500000 , TBL_IINLIM ) ;
}
bq25890_field_write ( bq , F_IINLIM , input_current_limit ) ;
}
2015-05-19 16:24:40 +03:00
static int bq25890_get_chip_state ( struct bq25890_device * bq ,
struct bq25890_state * state )
{
int i , ret ;
struct {
enum bq25890_fields id ;
u8 * data ;
} state_fields [ ] = {
{ F_CHG_STAT , & state - > chrg_status } ,
{ F_PG_STAT , & state - > online } ,
{ F_VSYS_STAT , & state - > vsys_status } ,
{ F_BOOST_FAULT , & state - > boost_fault } ,
{ F_BAT_FAULT , & state - > bat_fault } ,
2022-02-01 14:06:49 +01:00
{ F_CHG_FAULT , & state - > chrg_fault } ,
{ F_NTC_FAULT , & state - > ntc_fault }
2015-05-19 16:24:40 +03:00
} ;
for ( i = 0 ; i < ARRAY_SIZE ( state_fields ) ; i + + ) {
ret = bq25890_field_read ( bq , state_fields [ i ] . id ) ;
if ( ret < 0 )
return ret ;
* state_fields [ i ] . data = ret ;
}
2022-02-01 14:06:49 +01:00
dev_dbg ( bq - > dev , " S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d \n " ,
2015-05-19 16:24:40 +03:00
state - > chrg_status , state - > online , state - > vsys_status ,
2022-02-01 14:06:49 +01:00
state - > chrg_fault , state - > boost_fault , state - > bat_fault ,
state - > ntc_fault ) ;
2015-05-19 16:24:40 +03:00
return 0 ;
}
2020-05-03 17:21:11 +02:00
static irqreturn_t __bq25890_handle_irq ( struct bq25890_device * bq )
2015-05-19 16:24:40 +03:00
{
2020-05-03 17:21:11 +02:00
struct bq25890_state new_state ;
2015-05-19 16:24:40 +03:00
int ret ;
2020-05-03 17:21:11 +02:00
ret = bq25890_get_chip_state ( bq , & new_state ) ;
if ( ret < 0 )
return IRQ_NONE ;
2015-05-19 16:24:40 +03:00
2020-05-03 17:21:11 +02:00
if ( ! memcmp ( & bq - > state , & new_state , sizeof ( new_state ) ) )
return IRQ_NONE ;
if ( ! new_state . online & & bq - > state . online ) { /* power removed */
2015-05-19 16:24:40 +03:00
/* disable ADC */
2021-11-07 23:20:01 +03:00
ret = bq25890_field_write ( bq , F_CONV_RATE , 0 ) ;
2015-05-19 16:24:40 +03:00
if ( ret < 0 )
goto error ;
2020-05-03 17:21:11 +02:00
} else if ( new_state . online & & ! bq - > state . online ) { /* power inserted */
2015-05-19 16:24:40 +03:00
/* enable ADC, to have control of charge current/voltage */
2021-11-07 23:20:01 +03:00
ret = bq25890_field_write ( bq , F_CONV_RATE , 1 ) ;
2015-05-19 16:24:40 +03:00
if ( ret < 0 )
goto error ;
}
2020-05-03 17:21:11 +02:00
bq - > state = new_state ;
power_supply_changed ( bq - > charger ) ;
2015-05-19 16:24:40 +03:00
2020-05-03 17:21:11 +02:00
return IRQ_HANDLED ;
2015-05-19 16:24:40 +03:00
error :
2020-05-03 17:21:11 +02:00
dev_err ( bq - > dev , " Error communicating with the chip: %pe \n " ,
ERR_PTR ( ret ) ) ;
return IRQ_HANDLED ;
2015-05-19 16:24:40 +03:00
}
static irqreturn_t bq25890_irq_handler_thread ( int irq , void * private )
{
struct bq25890_device * bq = private ;
2020-05-03 17:21:11 +02:00
irqreturn_t ret ;
2015-05-19 16:24:40 +03:00
mutex_lock ( & bq - > lock ) ;
2020-05-03 17:21:11 +02:00
ret = __bq25890_handle_irq ( bq ) ;
2015-05-19 16:24:40 +03:00
mutex_unlock ( & bq - > lock ) ;
2020-05-03 17:21:11 +02:00
return ret ;
2015-05-19 16:24:40 +03:00
}
static int bq25890_chip_reset ( struct bq25890_device * bq )
{
int ret ;
int rst_check_counter = 10 ;
ret = bq25890_field_write ( bq , F_REG_RST , 1 ) ;
if ( ret < 0 )
return ret ;
do {
ret = bq25890_field_read ( bq , F_REG_RST ) ;
if ( ret < 0 )
return ret ;
usleep_range ( 5 , 10 ) ;
} while ( ret = = 1 & & - - rst_check_counter ) ;
if ( ! rst_check_counter )
return - ETIMEDOUT ;
return 0 ;
}
2022-02-01 14:06:50 +01:00
static int bq25890_rw_init_data ( struct bq25890_device * bq )
2015-05-19 16:24:40 +03:00
{
2022-02-01 14:06:52 +01:00
bool write = ! bq - > read_back_init_data ;
2015-05-19 16:24:40 +03:00
int ret ;
int i ;
const struct {
enum bq25890_fields id ;
2022-02-01 14:06:50 +01:00
u8 * value ;
2015-05-19 16:24:40 +03:00
} init_data [ ] = {
2022-02-01 14:06:50 +01:00
{ F_ICHG , & bq - > init_data . ichg } ,
{ F_VREG , & bq - > init_data . vreg } ,
{ F_ITERM , & bq - > init_data . iterm } ,
{ F_IPRECHG , & bq - > init_data . iprechg } ,
{ F_SYSVMIN , & bq - > init_data . sysvmin } ,
{ F_BOOSTV , & bq - > init_data . boostv } ,
{ F_BOOSTI , & bq - > init_data . boosti } ,
{ F_BOOSTF , & bq - > init_data . boostf } ,
{ F_EN_ILIM , & bq - > init_data . ilim_en } ,
{ F_TREG , & bq - > init_data . treg } ,
{ F_BATCMP , & bq - > init_data . rbatcomp } ,
{ F_VCLAMP , & bq - > init_data . vclamp } ,
2015-05-19 16:24:40 +03:00
} ;
2022-02-01 14:06:50 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( init_data ) ; i + + ) {
if ( write ) {
ret = bq25890_field_write ( bq , init_data [ i ] . id ,
* init_data [ i ] . value ) ;
} else {
ret = bq25890_field_read ( bq , init_data [ i ] . id ) ;
if ( ret > = 0 )
* init_data [ i ] . value = ret ;
}
if ( ret < 0 ) {
dev_dbg ( bq - > dev , " Accessing init data failed %d \n " , ret ) ;
return ret ;
}
}
return 0 ;
}
static int bq25890_hw_init ( struct bq25890_device * bq )
{
int ret ;
2022-02-01 14:06:51 +01:00
if ( ! bq - > skip_reset ) {
ret = bq25890_chip_reset ( bq ) ;
if ( ret < 0 ) {
dev_dbg ( bq - > dev , " Reset failed %d \n " , ret ) ;
return ret ;
}
2022-02-01 14:06:53 +01:00
} else {
/*
* Ensure charging is enabled , on some boards where the fw
* takes care of initalizition F_CHG_CFG is set to 0 before
* handing control over to the OS .
*/
ret = bq25890_field_write ( bq , F_CHG_CFG , 1 ) ;
if ( ret < 0 ) {
dev_dbg ( bq - > dev , " Enabling charging failed %d \n " , ret ) ;
return ret ;
}
2018-09-17 10:23:20 +08:00
}
2015-05-19 16:24:40 +03:00
/* disable watchdog */
ret = bq25890_field_write ( bq , F_WD , 0 ) ;
2018-07-31 11:49:06 -06:00
if ( ret < 0 ) {
dev_dbg ( bq - > dev , " Disabling watchdog failed %d \n " , ret ) ;
2015-05-19 16:24:40 +03:00
return ret ;
2018-09-17 10:23:20 +08:00
}
2015-05-19 16:24:40 +03:00
/* initialize currents/voltages and other parameters */
2022-02-01 14:06:50 +01:00
ret = bq25890_rw_init_data ( bq ) ;
if ( ret )
return ret ;
2015-05-19 16:24:40 +03:00
2021-10-30 20:28:04 +02:00
ret = bq25890_get_chip_state ( bq , & bq - > state ) ;
2018-07-31 11:49:06 -06:00
if ( ret < 0 ) {
2021-10-30 20:28:04 +02:00
dev_dbg ( bq - > dev , " Get state failed %d \n " , ret ) ;
2015-05-19 16:24:40 +03:00
return ret ;
2018-09-17 10:23:20 +08:00
}
2015-05-19 16:24:40 +03:00
2021-10-30 20:28:04 +02:00
/* Configure ADC for continuous conversions when charging */
ret = bq25890_field_write ( bq , F_CONV_RATE , ! ! bq - > state . online ) ;
2018-07-31 11:49:06 -06:00
if ( ret < 0 ) {
2021-10-30 20:28:04 +02:00
dev_dbg ( bq - > dev , " Config ADC failed %d \n " , ret ) ;
2015-05-19 16:24:40 +03:00
return ret ;
2018-09-17 10:23:20 +08:00
}
2015-05-19 16:24:40 +03:00
return 0 ;
}
2020-05-03 17:21:10 +02:00
static const enum power_supply_property bq25890_power_supply_props [ ] = {
2015-05-19 16:24:40 +03:00
POWER_SUPPLY_PROP_MANUFACTURER ,
2018-07-31 11:49:08 -06:00
POWER_SUPPLY_PROP_MODEL_NAME ,
2015-05-19 16:24:40 +03:00
POWER_SUPPLY_PROP_STATUS ,
2020-05-04 21:47:46 +02:00
POWER_SUPPLY_PROP_CHARGE_TYPE ,
2015-05-19 16:24:40 +03:00
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_HEALTH ,
2022-10-14 19:24:22 +02:00
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ,
2015-05-19 16:24:40 +03:00
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX ,
2020-05-04 21:47:46 +02:00
POWER_SUPPLY_PROP_PRECHARGE_CURRENT ,
2015-05-19 16:24:40 +03:00
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT ,
2020-05-04 21:47:47 +02:00
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT ,
2018-07-31 11:49:09 -06:00
POWER_SUPPLY_PROP_VOLTAGE_NOW ,
2020-05-04 21:47:30 +02:00
POWER_SUPPLY_PROP_CURRENT_NOW ,
2021-11-18 08:18:45 -08:00
POWER_SUPPLY_PROP_TEMP ,
2015-05-19 16:24:40 +03:00
} ;
static char * bq25890_charger_supplied_to [ ] = {
" main-battery " ,
} ;
static const struct power_supply_desc bq25890_power_supply_desc = {
. name = " bq25890-charger " ,
. type = POWER_SUPPLY_TYPE_USB ,
. properties = bq25890_power_supply_props ,
. num_properties = ARRAY_SIZE ( bq25890_power_supply_props ) ,
. get_property = bq25890_power_supply_get_property ,
2022-08-01 04:57:27 +02:00
. set_property = bq25890_power_supply_set_property ,
. property_is_writeable = bq25890_power_supply_property_is_writeable ,
2022-02-01 14:06:57 +01:00
. external_power_changed = bq25890_charger_external_power_changed ,
2015-05-19 16:24:40 +03:00
} ;
static int bq25890_power_supply_init ( struct bq25890_device * bq )
{
struct power_supply_config psy_cfg = { . drv_data = bq , } ;
psy_cfg . supplied_to = bq25890_charger_supplied_to ;
psy_cfg . num_supplicants = ARRAY_SIZE ( bq25890_charger_supplied_to ) ;
2021-10-30 20:28:03 +02:00
bq - > charger = devm_power_supply_register ( bq - > dev ,
& bq25890_power_supply_desc ,
& psy_cfg ) ;
2015-05-19 16:24:40 +03:00
return PTR_ERR_OR_ZERO ( bq - > charger ) ;
}
2022-02-01 14:06:55 +01:00
static int bq25890_set_otg_cfg ( struct bq25890_device * bq , u8 val )
{
int ret ;
ret = bq25890_field_write ( bq , F_OTG_CFG , val ) ;
if ( ret < 0 )
dev_err ( bq - > dev , " Error switching to boost/charger mode: %d \n " , ret ) ;
return ret ;
}
2022-02-01 14:06:58 +01:00
static void bq25890_pump_express_work ( struct work_struct * data )
{
struct bq25890_device * bq =
container_of ( data , struct bq25890_device , pump_express_work . work ) ;
int voltage , i , ret ;
dev_dbg ( bq - > dev , " Start to request input voltage increasing \n " ) ;
/* Enable current pulse voltage control protocol */
ret = bq25890_field_write ( bq , F_PUMPX_EN , 1 ) ;
if ( ret < 0 )
goto error_print ;
for ( i = 0 ; i < PUMP_EXPRESS_MAX_TRIES ; i + + ) {
voltage = bq25890_get_vbus_voltage ( bq ) ;
if ( voltage < 0 )
goto error_print ;
dev_dbg ( bq - > dev , " input voltage = %d uV \n " , voltage ) ;
if ( ( voltage + PUMP_EXPRESS_VBUS_MARGIN_uV ) >
bq - > pump_express_vbus_max )
break ;
ret = bq25890_field_write ( bq , F_PUMPX_UP , 1 ) ;
if ( ret < 0 )
goto error_print ;
/* Note a single PUMPX up pulse-sequence takes 2.1s */
ret = regmap_field_read_poll_timeout ( bq - > rmap_fields [ F_PUMPX_UP ] ,
ret , ! ret , 100000 , 3000000 ) ;
if ( ret < 0 )
goto error_print ;
/* Make sure ADC has sampled Vbus before checking again */
msleep ( 1000 ) ;
}
bq25890_field_write ( bq , F_PUMPX_EN , 0 ) ;
dev_info ( bq - > dev , " Hi-voltage charging requested, input voltage is %d mV \n " ,
voltage ) ;
return ;
error_print :
2022-07-31 12:02:28 +02:00
bq25890_field_write ( bq , F_PUMPX_EN , 0 ) ;
2022-02-01 14:06:58 +01:00
dev_err ( bq - > dev , " Failed to request hi-voltage charging \n " ) ;
}
2015-05-19 16:24:40 +03:00
static void bq25890_usb_work ( struct work_struct * data )
{
int ret ;
struct bq25890_device * bq =
container_of ( data , struct bq25890_device , usb_work ) ;
switch ( bq - > usb_event ) {
case USB_EVENT_ID :
/* Enable boost mode */
2022-02-01 14:06:55 +01:00
bq25890_set_otg_cfg ( bq , 1 ) ;
2015-05-19 16:24:40 +03:00
break ;
case USB_EVENT_NONE :
/* Disable boost mode */
2022-02-01 14:06:55 +01:00
ret = bq25890_set_otg_cfg ( bq , 0 ) ;
if ( ret = = 0 )
power_supply_changed ( bq - > charger ) ;
2015-05-19 16:24:40 +03:00
break ;
}
}
static int bq25890_usb_notifier ( struct notifier_block * nb , unsigned long val ,
void * priv )
{
struct bq25890_device * bq =
container_of ( nb , struct bq25890_device , usb_nb ) ;
bq - > usb_event = val ;
queue_work ( system_power_efficient_wq , & bq - > usb_work ) ;
return NOTIFY_OK ;
}
2022-02-01 14:06:56 +01:00
# ifdef CONFIG_REGULATOR
static int bq25890_vbus_enable ( struct regulator_dev * rdev )
{
struct bq25890_device * bq = rdev_get_drvdata ( rdev ) ;
return bq25890_set_otg_cfg ( bq , 1 ) ;
}
static int bq25890_vbus_disable ( struct regulator_dev * rdev )
{
struct bq25890_device * bq = rdev_get_drvdata ( rdev ) ;
return bq25890_set_otg_cfg ( bq , 0 ) ;
}
static int bq25890_vbus_is_enabled ( struct regulator_dev * rdev )
{
struct bq25890_device * bq = rdev_get_drvdata ( rdev ) ;
return bq25890_field_read ( bq , F_OTG_CFG ) ;
}
static const struct regulator_ops bq25890_vbus_ops = {
. enable = bq25890_vbus_enable ,
. disable = bq25890_vbus_disable ,
. is_enabled = bq25890_vbus_is_enabled ,
} ;
static const struct regulator_desc bq25890_vbus_desc = {
. name = " usb_otg_vbus " ,
. of_match = " usb-otg-vbus " ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. ops = & bq25890_vbus_ops ,
. fixed_uV = 5000000 ,
. n_voltages = 1 ,
} ;
# endif
2020-01-02 01:46:25 +03:00
static int bq25890_get_chip_version ( struct bq25890_device * bq )
{
int id , rev ;
id = bq25890_field_read ( bq , F_PN ) ;
if ( id < 0 ) {
2021-10-14 11:45:33 +02:00
dev_err ( bq - > dev , " Cannot read chip ID: %d \n " , id ) ;
2020-01-02 01:46:25 +03:00
return id ;
}
rev = bq25890_field_read ( bq , F_DEV_REV ) ;
if ( rev < 0 ) {
2021-10-14 11:45:33 +02:00
dev_err ( bq - > dev , " Cannot read chip revision: %d \n " , rev ) ;
2020-01-16 17:09:00 +00:00
return rev ;
2020-01-02 01:46:25 +03:00
}
switch ( id ) {
case BQ25890_ID :
bq - > chip_version = BQ25890 ;
break ;
/* BQ25892 and BQ25896 share same ID 0 */
case BQ25896_ID :
switch ( rev ) {
case 2 :
bq - > chip_version = BQ25896 ;
break ;
case 1 :
bq - > chip_version = BQ25892 ;
break ;
default :
dev_err ( bq - > dev ,
" Unknown device revision %d, assume BQ25892 \n " ,
rev ) ;
bq - > chip_version = BQ25892 ;
}
break ;
case BQ25895_ID :
bq - > chip_version = BQ25895 ;
break ;
default :
dev_err ( bq - > dev , " Unknown chip ID %d \n " , id ) ;
return - ENODEV ;
}
return 0 ;
}
2015-05-19 16:24:40 +03:00
static int bq25890_irq_probe ( struct bq25890_device * bq )
{
struct gpio_desc * irq ;
2017-02-20 19:12:23 +02:00
irq = devm_gpiod_get ( bq - > dev , BQ25890_IRQ_PIN , GPIOD_IN ) ;
2021-10-14 11:45:33 +02:00
if ( IS_ERR ( irq ) )
return dev_err_probe ( bq - > dev , PTR_ERR ( irq ) ,
" Could not probe irq pin. \n " ) ;
2015-05-19 16:24:40 +03:00
return gpiod_to_irq ( irq ) ;
}
static int bq25890_fw_read_u32_props ( struct bq25890_device * bq )
{
int ret ;
u32 property ;
int i ;
struct bq25890_init_data * init = & bq - > init_data ;
struct {
char * name ;
bool optional ;
enum bq25890_table_ids tbl_id ;
u8 * conv_data ; /* holds converted value from given property */
} props [ ] = {
/* required properties */
{ " ti,charge-current " , false , TBL_ICHG , & init - > ichg } ,
{ " ti,battery-regulation-voltage " , false , TBL_VREG , & init - > vreg } ,
{ " ti,termination-current " , false , TBL_ITERM , & init - > iterm } ,
{ " ti,precharge-current " , false , TBL_ITERM , & init - > iprechg } ,
{ " ti,minimum-sys-voltage " , false , TBL_SYSVMIN , & init - > sysvmin } ,
{ " ti,boost-voltage " , false , TBL_BOOSTV , & init - > boostv } ,
{ " ti,boost-max-current " , false , TBL_BOOSTI , & init - > boosti } ,
/* optional properties */
2020-09-26 21:05:34 +02:00
{ " ti,thermal-regulation-threshold " , true , TBL_TREG , & init - > treg } ,
{ " ti,ibatcomp-micro-ohms " , true , TBL_RBATCOMP , & init - > rbatcomp } ,
{ " ti,ibatcomp-clamp-microvolt " , true , TBL_VBATCOMP , & init - > vclamp } ,
2015-05-19 16:24:40 +03:00
} ;
/* initialize data for optional properties */
init - > treg = 3 ; /* 120 degrees Celsius */
2020-09-26 21:05:34 +02:00
init - > rbatcomp = init - > vclamp = 0 ; /* IBAT compensation disabled */
2015-05-19 16:24:40 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( props ) ; i + + ) {
ret = device_property_read_u32 ( bq - > dev , props [ i ] . name ,
& property ) ;
if ( ret < 0 ) {
if ( props [ i ] . optional )
continue ;
2018-07-31 11:49:06 -06:00
dev_err ( bq - > dev , " Unable to read property %d %s \n " , ret ,
props [ i ] . name ) ;
2015-05-19 16:24:40 +03:00
return ret ;
}
* props [ i ] . conv_data = bq25890_find_idx ( property ,
props [ i ] . tbl_id ) ;
}
return 0 ;
}
static int bq25890_fw_probe ( struct bq25890_device * bq )
{
int ret ;
struct bq25890_init_data * init = & bq - > init_data ;
2022-02-01 14:06:58 +01:00
/* Optional, left at 0 if property is not present */
device_property_read_u32 ( bq - > dev , " linux,pump-express-vbus-max " ,
& bq - > pump_express_vbus_max ) ;
2022-02-01 14:06:51 +01:00
bq - > skip_reset = device_property_read_bool ( bq - > dev , " linux,skip-reset " ) ;
2022-02-01 14:06:52 +01:00
bq - > read_back_init_data = device_property_read_bool ( bq - > dev ,
" linux,read-back-settings " ) ;
if ( bq - > read_back_init_data )
return 0 ;
2022-02-01 14:06:51 +01:00
2015-05-19 16:24:40 +03:00
ret = bq25890_fw_read_u32_props ( bq ) ;
if ( ret < 0 )
return ret ;
init - > ilim_en = device_property_read_bool ( bq - > dev , " ti,use-ilim-pin " ) ;
init - > boostf = device_property_read_bool ( bq - > dev , " ti,boost-low-freq " ) ;
return 0 ;
}
static int bq25890_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct bq25890_device * bq ;
int ret ;
bq = devm_kzalloc ( dev , sizeof ( * bq ) , GFP_KERNEL ) ;
if ( ! bq )
return - ENOMEM ;
bq - > client = client ;
bq - > dev = dev ;
mutex_init ( & bq - > lock ) ;
2022-02-01 14:06:58 +01:00
INIT_DELAYED_WORK ( & bq - > pump_express_work , bq25890_pump_express_work ) ;
2015-05-19 16:24:40 +03:00
bq - > rmap = devm_regmap_init_i2c ( client , & bq25890_regmap_config ) ;
2021-10-14 11:45:33 +02:00
if ( IS_ERR ( bq - > rmap ) )
return dev_err_probe ( dev , PTR_ERR ( bq - > rmap ) ,
" failed to allocate register map \n " ) ;
2015-05-19 16:24:40 +03:00
2022-02-01 14:06:59 +01:00
ret = devm_regmap_field_bulk_alloc ( dev , bq - > rmap , bq - > rmap_fields ,
bq25890_reg_fields , F_MAX_FIELDS ) ;
if ( ret )
return ret ;
2015-05-19 16:24:40 +03:00
i2c_set_clientdata ( client , bq ) ;
2020-01-02 01:46:25 +03:00
ret = bq25890_get_chip_version ( bq ) ;
if ( ret ) {
2021-10-14 11:45:33 +02:00
dev_err ( dev , " Cannot read chip ID or unknown chip: %d \n " , ret ) ;
2020-01-02 01:46:25 +03:00
return ret ;
2015-05-19 16:24:40 +03:00
}
2022-02-01 14:06:54 +01:00
ret = bq25890_fw_probe ( bq ) ;
if ( ret < 0 )
return dev_err_probe ( dev , ret , " reading device properties \n " ) ;
2015-05-19 16:24:40 +03:00
ret = bq25890_hw_init ( bq ) ;
if ( ret < 0 ) {
2021-10-14 11:45:33 +02:00
dev_err ( dev , " Cannot initialize the chip: %d \n " , ret ) ;
2015-05-19 16:24:40 +03:00
return ret ;
}
if ( client - > irq < = 0 )
client - > irq = bq25890_irq_probe ( bq ) ;
if ( client - > irq < 0 ) {
dev_err ( dev , " No irq resource found. \n " ) ;
return client - > irq ;
}
/* OTG reporting */
bq - > usb_phy = devm_usb_get_phy ( dev , USB_PHY_TYPE_USB2 ) ;
if ( ! IS_ERR_OR_NULL ( bq - > usb_phy ) ) {
INIT_WORK ( & bq - > usb_work , bq25890_usb_work ) ;
bq - > usb_nb . notifier_call = bq25890_usb_notifier ;
usb_register_notifier ( bq - > usb_phy , & bq - > usb_nb ) ;
}
2022-02-01 14:06:56 +01:00
# ifdef CONFIG_REGULATOR
else {
struct bq25890_platform_data * pdata = dev_get_platdata ( dev ) ;
struct regulator_config cfg = { } ;
struct regulator_dev * reg ;
cfg . dev = dev ;
cfg . driver_data = bq ;
if ( pdata )
cfg . init_data = pdata - > regulator_init_data ;
reg = devm_regulator_register ( dev , & bq25890_vbus_desc , & cfg ) ;
if ( IS_ERR ( reg ) )
return dev_err_probe ( dev , PTR_ERR ( reg ) , " registering regulator " ) ;
}
# endif
2015-05-19 16:24:40 +03:00
2021-10-30 20:28:03 +02:00
ret = bq25890_power_supply_init ( bq ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to register power supply \n " ) ;
goto err_unregister_usb_notifier ;
}
2015-05-19 16:24:40 +03:00
ret = devm_request_threaded_irq ( dev , client - > irq , NULL ,
bq25890_irq_handler_thread ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
BQ25890_IRQ_PIN , bq ) ;
if ( ret )
2021-10-30 20:28:03 +02:00
goto err_unregister_usb_notifier ;
2015-05-19 16:24:40 +03:00
return 0 ;
2021-10-30 20:28:03 +02:00
err_unregister_usb_notifier :
2015-05-19 16:24:40 +03:00
if ( ! IS_ERR_OR_NULL ( bq - > usb_phy ) )
usb_unregister_notifier ( bq - > usb_phy , & bq - > usb_nb ) ;
return ret ;
}
2022-08-15 10:02:30 +02:00
static void bq25890_remove ( struct i2c_client * client )
2015-05-19 16:24:40 +03:00
{
struct bq25890_device * bq = i2c_get_clientdata ( client ) ;
if ( ! IS_ERR_OR_NULL ( bq - > usb_phy ) )
usb_unregister_notifier ( bq - > usb_phy , & bq - > usb_nb ) ;
2022-02-01 14:06:51 +01:00
if ( ! bq - > skip_reset ) {
/* reset all registers to default values */
bq25890_chip_reset ( bq ) ;
}
2015-05-19 16:24:40 +03:00
}
2022-02-01 14:06:56 +01:00
static void bq25890_shutdown ( struct i2c_client * client )
{
struct bq25890_device * bq = i2c_get_clientdata ( client ) ;
/*
* TODO this if + return should probably be removed , but that would
* introduce a function change for boards using the usb - phy framework .
* This needs to be tested on such a board before making this change .
*/
if ( ! IS_ERR_OR_NULL ( bq - > usb_phy ) )
return ;
/*
* Turn off the 5 v Boost regulator which outputs Vbus to the device ' s
* Micro - USB or Type - C USB port . Leaving this on drains power and
* this avoids the PMIC on some device - models seeing this as Vbus
* getting inserted after shutdown , causing the device to immediately
* power - up again .
*/
bq25890_set_otg_cfg ( bq , 0 ) ;
}
2015-05-19 16:24:40 +03:00
# ifdef CONFIG_PM_SLEEP
static int bq25890_suspend ( struct device * dev )
{
struct bq25890_device * bq = dev_get_drvdata ( dev ) ;
/*
* If charger is removed , while in suspend , make sure ADC is diabled
* since it consumes slightly more power .
*/
2020-05-04 21:47:45 +02:00
return bq25890_field_write ( bq , F_CONV_RATE , 0 ) ;
2015-05-19 16:24:40 +03:00
}
static int bq25890_resume ( struct device * dev )
{
int ret ;
struct bq25890_device * bq = dev_get_drvdata ( dev ) ;
2020-05-03 17:21:11 +02:00
mutex_lock ( & bq - > lock ) ;
ret = bq25890_get_chip_state ( bq , & bq - > state ) ;
2015-05-19 16:24:40 +03:00
if ( ret < 0 )
2020-05-06 13:11:16 +03:00
goto unlock ;
2015-05-19 16:24:40 +03:00
/* Re-enable ADC only if charger is plugged in. */
2020-05-03 17:21:11 +02:00
if ( bq - > state . online ) {
2020-05-04 21:47:45 +02:00
ret = bq25890_field_write ( bq , F_CONV_RATE , 1 ) ;
2015-05-19 16:24:40 +03:00
if ( ret < 0 )
2020-05-06 13:11:16 +03:00
goto unlock ;
2015-05-19 16:24:40 +03:00
}
/* signal userspace, maybe state changed while suspended */
power_supply_changed ( bq - > charger ) ;
2020-05-06 13:11:16 +03:00
unlock :
2020-05-03 17:21:11 +02:00
mutex_unlock ( & bq - > lock ) ;
2020-05-06 13:11:16 +03:00
return ret ;
2015-05-19 16:24:40 +03:00
}
# endif
static const struct dev_pm_ops bq25890_pm = {
SET_SYSTEM_SLEEP_PM_OPS ( bq25890_suspend , bq25890_resume )
} ;
static const struct i2c_device_id bq25890_i2c_ids [ ] = {
{ " bq25890 " , 0 } ,
2020-01-02 01:46:26 +03:00
{ " bq25892 " , 0 } ,
{ " bq25895 " , 0 } ,
{ " bq25896 " , 0 } ,
2015-05-19 16:24:40 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , bq25890_i2c_ids ) ;
static const struct of_device_id bq25890_of_match [ ] = {
{ . compatible = " ti,bq25890 " , } ,
2020-01-02 01:46:26 +03:00
{ . compatible = " ti,bq25892 " , } ,
{ . compatible = " ti,bq25895 " , } ,
{ . compatible = " ti,bq25896 " , } ,
2015-05-19 16:24:40 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , bq25890_of_match ) ;
2020-09-11 18:27:25 +02:00
# ifdef CONFIG_ACPI
2015-05-19 16:24:40 +03:00
static const struct acpi_device_id bq25890_acpi_match [ ] = {
{ " BQ258900 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , bq25890_acpi_match ) ;
2020-09-11 18:27:25 +02:00
# endif
2015-05-19 16:24:40 +03:00
static struct i2c_driver bq25890_driver = {
. driver = {
. name = " bq25890-charger " ,
. of_match_table = of_match_ptr ( bq25890_of_match ) ,
. acpi_match_table = ACPI_PTR ( bq25890_acpi_match ) ,
. pm = & bq25890_pm ,
} ,
. probe = bq25890_probe ,
. remove = bq25890_remove ,
2022-02-01 14:06:56 +01:00
. shutdown = bq25890_shutdown ,
2015-05-19 16:24:40 +03:00
. id_table = bq25890_i2c_ids ,
} ;
module_i2c_driver ( bq25890_driver ) ;
MODULE_AUTHOR ( " Laurentiu Palcu <laurentiu.palcu@intel.com> " ) ;
MODULE_DESCRIPTION ( " bq25890 charger driver " ) ;
MODULE_LICENSE ( " GPL " ) ;