2019-06-03 10:28:20 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Copyright (C) 2018 ROHM Semiconductors
//
// power-supply driver for ROHM BD70528 PMIC
/*
* BD70528 charger HW state machine .
*
* The thermal shutdown state is not drawn . From any other state but
* battery error and suspend it is possible to go to TSD / TMP states
* if temperature is out of bounds .
*
* CHG_RST = H
* or CHG_EN = L
* or ( DCIN2_UVLO = L & & DCIN1_UVLO = L )
* or ( DCIN2_OVLO = H & DCIN1_UVKLO = L )
*
* + - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - +
* | | | |
* | Any state + - - - - - - - > | Suspend |
* | | | |
* + - - - - - - - - - - - - - - + + - - - - - - + - - - - - - - +
* |
* CHG_EN = H & & BAT_DET = H & & |
* No errors ( temp , bat_ov , UVLO , |
* OVLO . . . ) |
* |
* BAT_OV or + - - - - - - - - - v - - - - - - - - - - +
* ( DBAT & & TTRI ) | |
* + - - - - - - - - - - - - - - - - - + Trickle Charge | < - - - - - - - - - - - - - - - +
* | | | |
* | + - - - - - - - + - - - - - - - - - - - - + |
* | | |
* | | ^ |
* | V_BAT > VTRI_TH | | VBAT < VTRI_TH - 50 mV |
* | | | |
* | v | |
* | | |
* | BAT_OV or + - - - - - - - - - - + - - - - + |
* | ( DBAT & & TFST ) | | |
* | + - - - - - - - - - - - - - - - - + Fast Charge | |
* | | | | |
* v v + - - - - + - - - - - - - - - - + |
* | |
* + - - - - - - - - - - - - - - - - + ILIM_DET = L | ^ ILIM_DET |
* | | & CV_DET = H | | or CV_DET = L |
* | Battery Error | & VBAT > | | or VBAT < VRECHG_TH |
* | | VRECHG_TH | | or IBAT > IFST / x |
* + - - - - - - - - - - - - - - - - + & IBAT < | | |
* IFST / x v | |
* ^ | |
* | + - - - - - - - - - + - + |
* | | | |
* + - - - - - - - - - - - - - - - - - - - + Top OFF | |
* BAT_OV = H or | | |
* ( DBAT & & TFST ) + - - - - - + - - - - - + |
* | |
* Stay top - off for 15 s | |
* v |
* |
* + - - - - - - - - + |
* | | |
* | Done + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | |
* + - - - - - - - - + VBAT < VRECHG_TH
*/
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/mfd/rohm-bd70528.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
2020-05-08 18:44:45 +03:00
# include <linux/linear_range.h>
2019-06-03 10:28:20 +03:00
# define CHG_STAT_SUSPEND 0x0
# define CHG_STAT_TRICKLE 0x1
# define CHG_STAT_FAST 0x3
# define CHG_STAT_TOPOFF 0xe
# define CHG_STAT_DONE 0xf
# define CHG_STAT_OTP_TRICKLE 0x10
# define CHG_STAT_OTP_FAST 0x11
# define CHG_STAT_OTP_DONE 0x12
# define CHG_STAT_TSD_TRICKLE 0x20
# define CHG_STAT_TSD_FAST 0x21
# define CHG_STAT_TSD_TOPOFF 0x22
# define CHG_STAT_BAT_ERR 0x7f
static const char * bd70528_charger_model = " BD70528 " ;
static const char * bd70528_charger_manufacturer = " ROHM Semiconductors " ;
# define BD_ERR_IRQ_HND(_name_, _wrn_) \
static irqreturn_t bd0528_ # # _name_ # # _interrupt ( int irq , void * arg ) \
{ \
struct power_supply * psy = ( struct power_supply * ) arg ; \
\
power_supply_changed ( psy ) ; \
dev_err ( & psy - > dev , ( _wrn_ ) ) ; \
\
return IRQ_HANDLED ; \
}
# define BD_INFO_IRQ_HND(_name_, _wrn_) \
static irqreturn_t bd0528_ # # _name_ # # _interrupt ( int irq , void * arg ) \
{ \
struct power_supply * psy = ( struct power_supply * ) arg ; \
\
power_supply_changed ( psy ) ; \
dev_dbg ( & psy - > dev , ( _wrn_ ) ) ; \
\
return IRQ_HANDLED ; \
}
# define BD_IRQ_HND(_name_) bd0528_##_name_##_interrupt
struct bd70528_psy {
struct regmap * regmap ;
struct device * dev ;
struct power_supply * psy ;
} ;
BD_ERR_IRQ_HND ( BAT_OV_DET , " Battery overvoltage detected \n " ) ;
BD_ERR_IRQ_HND ( DBAT_DET , " Dead battery detected \n " ) ;
BD_ERR_IRQ_HND ( COLD_DET , " Battery cold \n " ) ;
BD_ERR_IRQ_HND ( HOT_DET , " Battery hot \n " ) ;
BD_ERR_IRQ_HND ( CHG_TSD , " Charger thermal shutdown \n " ) ;
BD_ERR_IRQ_HND ( DCIN2_OV_DET , " DCIN2 overvoltage detected \n " ) ;
BD_INFO_IRQ_HND ( BAT_OV_RES , " Battery voltage back to normal \n " ) ;
BD_INFO_IRQ_HND ( COLD_RES , " Battery temperature back to normal \n " ) ;
BD_INFO_IRQ_HND ( HOT_RES , " Battery temperature back to normal \n " ) ;
BD_INFO_IRQ_HND ( BAT_RMV , " Battery removed \n " ) ;
BD_INFO_IRQ_HND ( BAT_DET , " Battery detected \n " ) ;
BD_INFO_IRQ_HND ( DCIN2_OV_RES , " DCIN2 voltage back to normal \n " ) ;
BD_INFO_IRQ_HND ( DCIN2_RMV , " DCIN2 removed \n " ) ;
BD_INFO_IRQ_HND ( DCIN2_DET , " DCIN2 detected \n " ) ;
BD_INFO_IRQ_HND ( DCIN1_RMV , " DCIN1 removed \n " ) ;
BD_INFO_IRQ_HND ( DCIN1_DET , " DCIN1 detected \n " ) ;
struct irq_name_pair {
const char * n ;
irqreturn_t ( * h ) ( int irq , void * arg ) ;
} ;
static int bd70528_get_irqs ( struct platform_device * pdev ,
struct bd70528_psy * bdpsy )
{
int irq , i , ret ;
unsigned int mask ;
static const struct irq_name_pair bd70528_chg_irqs [ ] = {
{ . n = " bd70528-bat-ov-res " , . h = BD_IRQ_HND ( BAT_OV_RES ) } ,
{ . n = " bd70528-bat-ov-det " , . h = BD_IRQ_HND ( BAT_OV_DET ) } ,
{ . n = " bd70528-bat-dead " , . h = BD_IRQ_HND ( DBAT_DET ) } ,
{ . n = " bd70528-bat-warmed " , . h = BD_IRQ_HND ( COLD_RES ) } ,
{ . n = " bd70528-bat-cold " , . h = BD_IRQ_HND ( COLD_DET ) } ,
{ . n = " bd70528-bat-cooled " , . h = BD_IRQ_HND ( HOT_RES ) } ,
{ . n = " bd70528-bat-hot " , . h = BD_IRQ_HND ( HOT_DET ) } ,
{ . n = " bd70528-chg-tshd " , . h = BD_IRQ_HND ( CHG_TSD ) } ,
{ . n = " bd70528-bat-removed " , . h = BD_IRQ_HND ( BAT_RMV ) } ,
{ . n = " bd70528-bat-detected " , . h = BD_IRQ_HND ( BAT_DET ) } ,
{ . n = " bd70528-dcin2-ov-res " , . h = BD_IRQ_HND ( DCIN2_OV_RES ) } ,
{ . n = " bd70528-dcin2-ov-det " , . h = BD_IRQ_HND ( DCIN2_OV_DET ) } ,
{ . n = " bd70528-dcin2-removed " , . h = BD_IRQ_HND ( DCIN2_RMV ) } ,
{ . n = " bd70528-dcin2-detected " , . h = BD_IRQ_HND ( DCIN2_DET ) } ,
{ . n = " bd70528-dcin1-removed " , . h = BD_IRQ_HND ( DCIN1_RMV ) } ,
{ . n = " bd70528-dcin1-detected " , . h = BD_IRQ_HND ( DCIN1_DET ) } ,
} ;
for ( i = 0 ; i < ARRAY_SIZE ( bd70528_chg_irqs ) ; i + + ) {
irq = platform_get_irq_byname ( pdev , bd70528_chg_irqs [ i ] . n ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " Bad IRQ information for %s (%d) \n " ,
bd70528_chg_irqs [ i ] . n , irq ) ;
return irq ;
}
ret = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
bd70528_chg_irqs [ i ] . h ,
IRQF_ONESHOT ,
bd70528_chg_irqs [ i ] . n ,
bdpsy - > psy ) ;
if ( ret )
return ret ;
}
/*
* BD70528 irq controller is not touching the main mask register .
* So enable the charger block interrupts at main level . We can just
* leave them enabled as irq - controller should disable irqs
* from sub - registers when IRQ is disabled or freed .
*/
mask = BD70528_REG_INT_BAT1_MASK | BD70528_REG_INT_BAT2_MASK ;
ret = regmap_update_bits ( bdpsy - > regmap ,
BD70528_REG_INT_MAIN_MASK , mask , 0 ) ;
if ( ret )
dev_err ( & pdev - > dev , " Failed to enable charger IRQs \n " ) ;
return ret ;
}
static int bd70528_get_charger_status ( struct bd70528_psy * bdpsy , int * val )
{
int ret ;
unsigned int v ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_CURR_STAT , & v ) ;
if ( ret ) {
dev_err ( bdpsy - > dev , " Charger state read failure %d \n " ,
ret ) ;
return ret ;
}
switch ( v & BD70528_MASK_CHG_STAT ) {
case CHG_STAT_SUSPEND :
/* Maybe we should check the CHG_TTRI_EN? */
case CHG_STAT_OTP_TRICKLE :
case CHG_STAT_OTP_FAST :
case CHG_STAT_OTP_DONE :
case CHG_STAT_TSD_TRICKLE :
case CHG_STAT_TSD_FAST :
case CHG_STAT_TSD_TOPOFF :
case CHG_STAT_BAT_ERR :
* val = POWER_SUPPLY_STATUS_NOT_CHARGING ;
break ;
case CHG_STAT_DONE :
* val = POWER_SUPPLY_STATUS_FULL ;
break ;
case CHG_STAT_TRICKLE :
case CHG_STAT_FAST :
case CHG_STAT_TOPOFF :
* val = POWER_SUPPLY_STATUS_CHARGING ;
break ;
default :
* val = POWER_SUPPLY_STATUS_UNKNOWN ;
break ;
}
return 0 ;
}
static int bd70528_get_charge_type ( struct bd70528_psy * bdpsy , int * val )
{
int ret ;
unsigned int v ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_CURR_STAT , & v ) ;
if ( ret ) {
dev_err ( bdpsy - > dev , " Charger state read failure %d \n " ,
ret ) ;
return ret ;
}
switch ( v & BD70528_MASK_CHG_STAT ) {
case CHG_STAT_TRICKLE :
* val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE ;
break ;
case CHG_STAT_FAST :
case CHG_STAT_TOPOFF :
* val = POWER_SUPPLY_CHARGE_TYPE_FAST ;
break ;
case CHG_STAT_DONE :
case CHG_STAT_SUSPEND :
/* Maybe we should check the CHG_TTRI_EN? */
case CHG_STAT_OTP_TRICKLE :
case CHG_STAT_OTP_FAST :
case CHG_STAT_OTP_DONE :
case CHG_STAT_TSD_TRICKLE :
case CHG_STAT_TSD_FAST :
case CHG_STAT_TSD_TOPOFF :
case CHG_STAT_BAT_ERR :
* val = POWER_SUPPLY_CHARGE_TYPE_NONE ;
break ;
default :
* val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN ;
break ;
}
return 0 ;
}
static int bd70528_get_battery_health ( struct bd70528_psy * bdpsy , int * val )
{
int ret ;
unsigned int v ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_BAT_STAT , & v ) ;
if ( ret ) {
dev_err ( bdpsy - > dev , " Battery state read failure %d \n " ,
ret ) ;
return ret ;
}
/* No battery? */
if ( ! ( v & BD70528_MASK_CHG_BAT_DETECT ) )
* val = POWER_SUPPLY_HEALTH_DEAD ;
else if ( v & BD70528_MASK_CHG_BAT_OVERVOLT )
* val = POWER_SUPPLY_HEALTH_OVERVOLTAGE ;
else if ( v & BD70528_MASK_CHG_BAT_TIMER )
* val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE ;
else
* val = POWER_SUPPLY_HEALTH_GOOD ;
return 0 ;
}
static int bd70528_get_online ( struct bd70528_psy * bdpsy , int * val )
{
int ret ;
unsigned int v ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_IN_STAT , & v ) ;
if ( ret ) {
dev_err ( bdpsy - > dev , " DC1 IN state read failure %d \n " ,
ret ) ;
return ret ;
}
* val = ( v & BD70528_MASK_CHG_DCIN1_UVLO ) ? 1 : 0 ;
return 0 ;
}
static int bd70528_get_present ( struct bd70528_psy * bdpsy , int * val )
{
int ret ;
unsigned int v ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_BAT_STAT , & v ) ;
if ( ret ) {
dev_err ( bdpsy - > dev , " Battery state read failure %d \n " ,
ret ) ;
return ret ;
}
* val = ( v & BD70528_MASK_CHG_BAT_DETECT ) ? 1 : 0 ;
return 0 ;
}
2020-05-08 18:44:45 +03:00
static const struct linear_range current_limit_ranges [ ] = {
2019-06-03 10:28:20 +03:00
{
. min = 5 ,
. step = 1 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0 ,
. max_sel = 0x22 ,
2019-06-03 10:28:20 +03:00
} ,
{
. min = 40 ,
. step = 5 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0x23 ,
. max_sel = 0x26 ,
2019-06-03 10:28:20 +03:00
} ,
{
. min = 60 ,
. step = 20 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0x27 ,
. max_sel = 0x2d ,
2019-06-03 10:28:20 +03:00
} ,
{
. min = 200 ,
. step = 50 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0x2e ,
. max_sel = 0x34 ,
} ,
{
. min = 500 ,
. step = 0 ,
. min_sel = 0x35 ,
. max_sel = 0x3f ,
} ,
2019-06-03 10:28:20 +03:00
} ;
/*
* BD70528 would support setting and getting own charge current /
* voltage for low temperatures . The driver currently only reads
* the charge current at room temperature . We do set both though .
*/
2020-05-08 18:44:45 +03:00
static const struct linear_range warm_charge_curr [ ] = {
2019-06-03 10:28:20 +03:00
{
. min = 10 ,
. step = 10 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0 ,
. max_sel = 0x12 ,
2019-06-03 10:28:20 +03:00
} ,
{
. min = 200 ,
. step = 25 ,
2020-05-08 18:44:45 +03:00
. min_sel = 0x13 ,
. max_sel = 0x1f ,
2019-06-03 10:28:20 +03:00
} ,
} ;
/*
* Cold charge current selectors are identical to warm charge current
* selectors . The difference is that only smaller currents are available
* at cold charge range .
*/
# define MAX_COLD_CHG_CURR_SEL 0x15
# define MAX_WARM_CHG_CURR_SEL 0x1f
# define MIN_CHG_CURR_SEL 0x0
static int get_charge_current ( struct bd70528_psy * bdpsy , int * ma )
{
unsigned int sel ;
int ret ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_CHG_CURR_WARM ,
& sel ) ;
if ( ret ) {
dev_err ( bdpsy - > dev ,
" Charge current reading failed (%d) \n " , ret ) ;
return ret ;
}
sel & = BD70528_MASK_CHG_CHG_CURR ;
2020-05-08 18:44:45 +03:00
ret = linear_range_get_value_array ( & warm_charge_curr [ 0 ] ,
ARRAY_SIZE ( warm_charge_curr ) ,
sel , ma ) ;
2019-06-03 10:28:20 +03:00
if ( ret ) {
dev_err ( bdpsy - > dev ,
" Unknown charge current value 0x%x \n " ,
sel ) ;
}
return ret ;
}
static int get_current_limit ( struct bd70528_psy * bdpsy , int * ma )
{
unsigned int sel ;
int ret ;
ret = regmap_read ( bdpsy - > regmap , BD70528_REG_CHG_DCIN_ILIM ,
& sel ) ;
if ( ret ) {
dev_err ( bdpsy - > dev ,
" Input current limit reading failed (%d) \n " , ret ) ;
return ret ;
}
sel & = BD70528_MASK_CHG_DCIN_ILIM ;
2020-05-08 18:44:45 +03:00
ret = linear_range_get_value_array ( & current_limit_ranges [ 0 ] ,
ARRAY_SIZE ( current_limit_ranges ) ,
sel , ma ) ;
2019-06-03 10:28:20 +03:00
if ( ret ) {
/* Unspecified values mean 500 mA */
* ma = 500 ;
}
return 0 ;
}
static enum power_supply_property bd70528_charger_props [ ] = {
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_CHARGE_TYPE ,
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ,
POWER_SUPPLY_PROP_MODEL_NAME ,
POWER_SUPPLY_PROP_MANUFACTURER ,
} ;
static int bd70528_charger_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct bd70528_psy * bdpsy = power_supply_get_drvdata ( psy ) ;
int ret = 0 ;
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
return bd70528_get_charger_status ( bdpsy , & val - > intval ) ;
case POWER_SUPPLY_PROP_CHARGE_TYPE :
return bd70528_get_charge_type ( bdpsy , & val - > intval ) ;
case POWER_SUPPLY_PROP_HEALTH :
return bd70528_get_battery_health ( bdpsy , & val - > intval ) ;
case POWER_SUPPLY_PROP_PRESENT :
return bd70528_get_present ( bdpsy , & val - > intval ) ;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
ret = get_current_limit ( bdpsy , & val - > intval ) ;
val - > intval * = 1000 ;
return ret ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
ret = get_charge_current ( bdpsy , & val - > intval ) ;
val - > intval * = 1000 ;
return ret ;
case POWER_SUPPLY_PROP_ONLINE :
return bd70528_get_online ( bdpsy , & val - > intval ) ;
case POWER_SUPPLY_PROP_MODEL_NAME :
val - > strval = bd70528_charger_model ;
return 0 ;
case POWER_SUPPLY_PROP_MANUFACTURER :
val - > strval = bd70528_charger_manufacturer ;
return 0 ;
default :
break ;
}
return - EINVAL ;
}
static int bd70528_prop_is_writable ( struct power_supply * psy ,
enum power_supply_property psp )
{
switch ( psp ) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
return 1 ;
default :
break ;
}
return 0 ;
}
static int set_charge_current ( struct bd70528_psy * bdpsy , int ma )
{
unsigned int reg ;
int ret = 0 , tmpret ;
bool found ;
if ( ma > 500 ) {
dev_warn ( bdpsy - > dev ,
" Requested charge current %u exceed maximum (500mA) \n " ,
ma ) ;
reg = MAX_WARM_CHG_CURR_SEL ;
goto set ;
}
if ( ma < 10 ) {
dev_err ( bdpsy - > dev ,
" Requested charge current %u smaller than min (10mA) \n " ,
ma ) ;
reg = MIN_CHG_CURR_SEL ;
ret = - EINVAL ;
goto set ;
}
2020-05-08 18:44:45 +03:00
/*
* For BD70528 voltage / current limits we happily accept any value which
* belongs the range . We could check if value matching the selector is
* desired by computing the range min + ( sel - sel_low ) * range step - but
* I guess it is enough if we use voltage / current which is closest ( below )
* the requested ?
*/
ret = linear_range_get_selector_low_array ( warm_charge_curr ,
ARRAY_SIZE ( warm_charge_curr ) ,
ma , & reg , & found ) ;
2019-06-03 10:28:20 +03:00
if ( ret ) {
2020-05-08 18:44:45 +03:00
dev_err ( bdpsy - > dev ,
" Unsupported charge current %u mA \n " , ma ) ;
2019-06-03 10:28:20 +03:00
reg = MIN_CHG_CURR_SEL ;
goto set ;
}
if ( ! found ) {
2020-05-08 18:44:45 +03:00
/*
* There was a gap in supported values and we hit it .
* Yet a smaller value was found so we use it .
*/
2019-06-03 10:28:20 +03:00
dev_warn ( bdpsy - > dev ,
" Unsupported charge current %u mA \n " , ma ) ;
}
set :
tmpret = regmap_update_bits ( bdpsy - > regmap ,
BD70528_REG_CHG_CHG_CURR_WARM ,
BD70528_MASK_CHG_CHG_CURR , reg ) ;
if ( tmpret )
dev_err ( bdpsy - > dev ,
" Charge current write failure (%d) \n " , tmpret ) ;
if ( reg > MAX_COLD_CHG_CURR_SEL )
reg = MAX_COLD_CHG_CURR_SEL ;
if ( ! tmpret )
tmpret = regmap_update_bits ( bdpsy - > regmap ,
BD70528_REG_CHG_CHG_CURR_COLD ,
BD70528_MASK_CHG_CHG_CURR , reg ) ;
if ( ! ret )
ret = tmpret ;
return ret ;
}
# define MAX_CURR_LIMIT_SEL 0x34
# define MIN_CURR_LIMIT_SEL 0x0
static int set_current_limit ( struct bd70528_psy * bdpsy , int ma )
{
unsigned int reg ;
int ret = 0 , tmpret ;
bool found ;
if ( ma > 500 ) {
dev_warn ( bdpsy - > dev ,
" Requested current limit %u exceed maximum (500mA) \n " ,
ma ) ;
reg = MAX_CURR_LIMIT_SEL ;
goto set ;
}
if ( ma < 5 ) {
dev_err ( bdpsy - > dev ,
" Requested current limit %u smaller than min (5mA) \n " ,
ma ) ;
reg = MIN_CURR_LIMIT_SEL ;
ret = - EINVAL ;
goto set ;
}
2020-05-08 18:44:45 +03:00
ret = linear_range_get_selector_low_array ( current_limit_ranges ,
ARRAY_SIZE ( current_limit_ranges ) ,
ma , & reg , & found ) ;
2019-06-03 10:28:20 +03:00
if ( ret ) {
2020-05-08 18:44:45 +03:00
dev_err ( bdpsy - > dev , " Unsupported current limit %umA \n " , ma ) ;
2019-06-03 10:28:20 +03:00
reg = MIN_CURR_LIMIT_SEL ;
goto set ;
}
if ( ! found ) {
2020-05-08 18:44:45 +03:00
/*
* There was a gap in supported values and we hit it .
* We found a smaller value from ranges and use it .
* Warn user though .
*/
dev_warn ( bdpsy - > dev , " Unsupported current limit %umA \n " , ma ) ;
2019-06-03 10:28:20 +03:00
}
set :
tmpret = regmap_update_bits ( bdpsy - > regmap ,
BD70528_REG_CHG_DCIN_ILIM ,
BD70528_MASK_CHG_DCIN_ILIM , reg ) ;
if ( ! ret )
ret = tmpret ;
return ret ;
}
static int bd70528_charger_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
struct bd70528_psy * bdpsy = power_supply_get_drvdata ( psy ) ;
switch ( psp ) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT :
return set_current_limit ( bdpsy , val - > intval / 1000 ) ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
return set_charge_current ( bdpsy , val - > intval / 1000 ) ;
default :
break ;
}
return - EINVAL ;
}
static const struct power_supply_desc bd70528_charger_desc = {
. name = " bd70528-charger " ,
. type = POWER_SUPPLY_TYPE_MAINS ,
. properties = bd70528_charger_props ,
. num_properties = ARRAY_SIZE ( bd70528_charger_props ) ,
. get_property = bd70528_charger_get_property ,
. set_property = bd70528_charger_set_property ,
. property_is_writeable = bd70528_prop_is_writable ,
} ;
static int bd70528_power_probe ( struct platform_device * pdev )
{
struct bd70528_psy * bdpsy ;
struct power_supply_config cfg = { } ;
bdpsy = devm_kzalloc ( & pdev - > dev , sizeof ( * bdpsy ) , GFP_KERNEL ) ;
if ( ! bdpsy )
return - ENOMEM ;
bdpsy - > regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! bdpsy - > regmap ) {
dev_err ( & pdev - > dev , " No regmap found for chip \n " ) ;
return - EINVAL ;
}
bdpsy - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , bdpsy ) ;
cfg . drv_data = bdpsy ;
cfg . of_node = pdev - > dev . parent - > of_node ;
bdpsy - > psy = devm_power_supply_register ( & pdev - > dev ,
& bd70528_charger_desc , & cfg ) ;
if ( IS_ERR ( bdpsy - > psy ) ) {
dev_err ( & pdev - > dev , " failed: power supply register \n " ) ;
return PTR_ERR ( bdpsy - > psy ) ;
}
return bd70528_get_irqs ( pdev , bdpsy ) ;
}
static struct platform_driver bd70528_power = {
. driver = {
. name = " bd70528-power "
} ,
. probe = bd70528_power_probe ,
} ;
module_platform_driver ( bd70528_power ) ;
MODULE_AUTHOR ( " Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> " ) ;
MODULE_DESCRIPTION ( " BD70528 power-supply driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2019-10-23 15:35:24 +03:00
MODULE_ALIAS ( " platform:bd70528-power " ) ;