2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2012-02-06 15:59:01 +00:00
/*
* Summit Microelectronics SMB347 Battery Charger Driver
*
* Copyright ( C ) 2011 , Intel Corporation
*
* Authors : Bruce E . Robertson < bruce . e . robertson @ intel . com >
* Mika Westerberg < mika . westerberg @ linux . intel . com >
*/
2020-03-29 18:15:45 +02:00
# include <linux/delay.h>
2012-05-07 11:26:37 +03:00
# include <linux/err.h>
2012-02-06 15:59:01 +00:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
# include <linux/power_supply.h>
2020-08-26 16:41:59 +02:00
# include <linux/property.h>
2012-04-16 11:48:41 +03:00
# include <linux/regmap.h>
2021-07-31 20:38:39 +03:00
# include <linux/regulator/driver.h>
2012-02-06 15:59:01 +00:00
2020-08-26 16:41:58 +02:00
# include <dt-bindings/power/summit,smb347-charger.h>
/* Use the default compensation method */
# define SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT -1
/* Use default factory programmed value for hard/soft temperature limit */
# define SMB3XX_TEMP_USE_DEFAULT -273
2012-02-06 15:59:01 +00:00
/*
* Configuration registers . These are mirrored to volatile RAM and can be
* written once % CMD_A_ALLOW_WRITE is set in % CMD_A register . They will be
* reloaded from non - volatile registers after POR .
*/
# define CFG_CHARGE_CURRENT 0x00
# define CFG_CHARGE_CURRENT_FCC_MASK 0xe0
# define CFG_CHARGE_CURRENT_FCC_SHIFT 5
# define CFG_CHARGE_CURRENT_PCC_MASK 0x18
# define CFG_CHARGE_CURRENT_PCC_SHIFT 3
# define CFG_CHARGE_CURRENT_TC_MASK 0x07
# define CFG_CURRENT_LIMIT 0x01
# define CFG_CURRENT_LIMIT_DC_MASK 0xf0
# define CFG_CURRENT_LIMIT_DC_SHIFT 4
# define CFG_CURRENT_LIMIT_USB_MASK 0x0f
# define CFG_FLOAT_VOLTAGE 0x03
2012-04-16 11:48:41 +03:00
# define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f
2012-02-06 15:59:01 +00:00
# define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0
# define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6
# define CFG_STAT 0x05
# define CFG_STAT_DISABLED BIT(5)
# define CFG_STAT_ACTIVE_HIGH BIT(7)
# define CFG_PIN 0x06
# define CFG_PIN_EN_CTRL_MASK 0x60
# define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40
# define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
# define CFG_PIN_EN_APSD_IRQ BIT(1)
# define CFG_PIN_EN_CHARGER_ERROR BIT(2)
2021-07-31 20:38:38 +03:00
# define CFG_PIN_EN_CTRL BIT(4)
2012-02-06 15:59:01 +00:00
# define CFG_THERM 0x07
# define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
# define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
# define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c
# define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
# define CFG_THERM_MONITOR_DISABLED BIT(4)
# define CFG_SYSOK 0x08
2021-07-31 20:38:39 +03:00
# define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0)
2012-02-06 15:59:01 +00:00
# define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
# define CFG_OTHER 0x09
# define CFG_OTHER_RID_MASK 0xc0
# define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
# define CFG_OTG 0x0a
# define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
2021-07-31 20:38:39 +03:00
# define CFG_OTG_CURRENT_LIMIT_250mA BIT(2)
# define CFG_OTG_CURRENT_LIMIT_750mA BIT(3)
2012-02-06 15:59:01 +00:00
# define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
# define CFG_OTG_CC_COMPENSATION_MASK 0xc0
# define CFG_OTG_CC_COMPENSATION_SHIFT 6
# define CFG_TEMP_LIMIT 0x0b
# define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03
# define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0
# define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c
# define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2
# define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30
# define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4
# define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0
# define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6
# define CFG_FAULT_IRQ 0x0c
# define CFG_FAULT_IRQ_DCIN_UV BIT(2)
# define CFG_STATUS_IRQ 0x0d
# define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4)
2012-09-18 21:58:07 +05:30
# define CFG_STATUS_IRQ_CHARGE_TIMEOUT BIT(7)
2012-02-06 15:59:01 +00:00
# define CFG_ADDRESS 0x0e
/* Command registers */
# define CMD_A 0x30
# define CMD_A_CHG_ENABLED BIT(1)
# define CMD_A_SUSPEND_ENABLED BIT(2)
2021-07-31 20:38:39 +03:00
# define CMD_A_OTG_ENABLED BIT(4)
2012-02-06 15:59:01 +00:00
# define CMD_A_ALLOW_WRITE BIT(7)
# define CMD_B 0x31
# define CMD_C 0x33
/* Interrupt Status registers */
# define IRQSTAT_A 0x35
# define IRQSTAT_C 0x37
# define IRQSTAT_C_TERMINATION_STAT BIT(0)
# define IRQSTAT_C_TERMINATION_IRQ BIT(1)
# define IRQSTAT_C_TAPER_IRQ BIT(3)
2012-09-18 21:58:07 +05:30
# define IRQSTAT_D 0x38
# define IRQSTAT_D_CHARGE_TIMEOUT_STAT BIT(2)
# define IRQSTAT_D_CHARGE_TIMEOUT_IRQ BIT(3)
2012-02-06 15:59:01 +00:00
# define IRQSTAT_E 0x39
# define IRQSTAT_E_USBIN_UV_STAT BIT(0)
# define IRQSTAT_E_USBIN_UV_IRQ BIT(1)
# define IRQSTAT_E_DCIN_UV_STAT BIT(4)
# define IRQSTAT_E_DCIN_UV_IRQ BIT(5)
# define IRQSTAT_F 0x3a
/* Status registers */
# define STAT_A 0x3b
# define STAT_A_FLOAT_VOLTAGE_MASK 0x3f
# define STAT_B 0x3c
# define STAT_C 0x3d
# define STAT_C_CHG_ENABLED BIT(0)
2012-09-18 21:58:07 +05:30
# define STAT_C_HOLDOFF_STAT BIT(3)
2012-02-06 15:59:01 +00:00
# define STAT_C_CHG_MASK 0x06
# define STAT_C_CHG_SHIFT 1
2012-09-18 21:58:07 +05:30
# define STAT_C_CHG_TERM BIT(5)
2012-02-06 15:59:01 +00:00
# define STAT_C_CHARGER_ERROR BIT(6)
# define STAT_E 0x3f
2012-04-16 11:48:41 +03:00
# define SMB347_MAX_REGISTER 0x3f
2012-02-06 15:59:01 +00:00
/**
* struct smb347_charger - smb347 charger instance
2012-04-16 11:48:41 +03:00
* @ dev : pointer to device
* @ regmap : pointer to driver regmap
2012-02-06 15:59:01 +00:00
* @ mains : power_supply instance for AC / DC power
* @ usb : power_supply instance for USB power
2021-07-31 20:38:39 +03:00
* @ usb_rdev : USB VBUS regulator device
2020-08-14 00:34:05 +03:00
* @ id : SMB charger ID
2012-02-06 15:59:01 +00:00
* @ mains_online : is AC / DC input connected
* @ usb_online : is USB input connected
2021-01-22 22:17:34 +03:00
* @ irq_unsupported : is interrupt unsupported by SMB hardware
2021-07-31 20:38:39 +03:00
* @ usb_vbus_enabled : is USB VBUS powered by SMB charger
2020-08-26 16:41:58 +02:00
* @ max_charge_current : maximum current ( in uA ) the battery can be charged
* @ max_charge_voltage : maximum voltage ( in uV ) the battery can be charged
* @ pre_charge_current : current ( in uA ) to use in pre - charging phase
* @ termination_current : current ( in uA ) used to determine when the
* charging cycle terminates
* @ pre_to_fast_voltage : voltage ( in uV ) treshold used for transitioning to
* pre - charge to fast charge mode
* @ mains_current_limit : maximum input current drawn from AC / DC input ( in uA )
* @ usb_hc_current_limit : maximum input high current ( in uA ) drawn from USB
* input
* @ chip_temp_threshold : die temperature where device starts limiting charge
* current [ % 100 - % 130 ] ( in degree C )
* @ soft_cold_temp_limit : soft cold temperature limit [ % 0 - % 15 ] ( in degree C ) ,
* granularity is 5 deg C .
* @ soft_hot_temp_limit : soft hot temperature limit [ % 40 - % 55 ] ( in degree C ) ,
* granularity is 5 deg C .
* @ hard_cold_temp_limit : hard cold temperature limit [ % - 5 - % 10 ] ( in degree C ) ,
* granularity is 5 deg C .
* @ hard_hot_temp_limit : hard hot temperature limit [ % 50 - % 65 ] ( in degree C ) ,
* granularity is 5 deg C .
* @ suspend_on_hard_temp_limit : suspend charging when hard limit is hit
* @ soft_temp_limit_compensation : compensation method when soft temperature
* limit is hit
* @ charge_current_compensation : current ( in uA ) for charging compensation
* current when temperature hits soft limits
* @ use_mains : AC / DC input can be used
* @ use_usb : USB input can be used
* @ use_usb_otg : USB OTG output can be used ( not implemented yet )
* @ enable_control : how charging enable / disable is controlled
* ( driver / pin controls )
2021-07-31 20:38:39 +03:00
* @ inok_polarity : polarity of INOK signal which denotes presence of external
* power supply
2020-08-26 16:41:58 +02:00
*
* @ use_main , @ use_usb , and @ use_usb_otg are means to enable / disable
* hardware support for these . This is useful when we want to have for
* example OTG charging controlled via OTG transceiver driver and not by
* the SMB347 hardware .
*
* Hard and soft temperature limit values are given as described in the
* device data sheet and assuming NTC beta value is % 3750. Even if this is
* not the case , these values should be used . They can be mapped to the
* corresponding NTC beta values with the help of table % 2 in the data
* sheet . So for example if NTC beta is % 3375 and we want to program hard
* hot limit to be % 53 deg C , @ hard_hot_temp_limit should be set to % 50.
*
* If zero value is given in any of the current and voltage values , the
* factory programmed default will be used . For soft / hard temperature
* values , pass in % SMB3XX_TEMP_USE_DEFAULT instead .
2012-02-06 15:59:01 +00:00
*/
struct smb347_charger {
2012-04-16 11:48:41 +03:00
struct device * dev ;
struct regmap * regmap ;
2015-03-12 08:44:11 +01:00
struct power_supply * mains ;
struct power_supply * usb ;
2021-07-31 20:38:39 +03:00
struct regulator_dev * usb_rdev ;
2020-08-14 00:34:05 +03:00
unsigned int id ;
2012-02-06 15:59:01 +00:00
bool mains_online ;
bool usb_online ;
2021-01-22 22:17:34 +03:00
bool irq_unsupported ;
2021-07-31 20:38:39 +03:00
bool usb_vbus_enabled ;
2020-08-26 16:41:58 +02:00
unsigned int max_charge_current ;
unsigned int max_charge_voltage ;
unsigned int pre_charge_current ;
unsigned int termination_current ;
unsigned int pre_to_fast_voltage ;
unsigned int mains_current_limit ;
unsigned int usb_hc_current_limit ;
unsigned int chip_temp_threshold ;
int soft_cold_temp_limit ;
int soft_hot_temp_limit ;
int hard_cold_temp_limit ;
int hard_hot_temp_limit ;
bool suspend_on_hard_temp_limit ;
unsigned int soft_temp_limit_compensation ;
unsigned int charge_current_compensation ;
bool use_mains ;
bool use_usb ;
bool use_usb_otg ;
unsigned int enable_control ;
2021-07-31 20:38:39 +03:00
unsigned int inok_polarity ;
2012-02-06 15:59:01 +00:00
} ;
2020-08-14 00:34:05 +03:00
enum smb_charger_chipid {
SMB345 ,
SMB347 ,
SMB358 ,
NUM_CHIP_TYPES ,
2012-02-06 15:59:01 +00:00
} ;
2020-08-14 00:34:05 +03:00
/* Fast charge current in uA */
static const unsigned int fcc_tbl [ NUM_CHIP_TYPES ] [ 8 ] = {
[ SMB345 ] = { 200000 , 450000 , 600000 , 900000 ,
1300000 , 1500000 , 1800000 , 2000000 } ,
[ SMB347 ] = { 700000 , 900000 , 1200000 , 1500000 ,
1800000 , 2000000 , 2200000 , 2500000 } ,
[ SMB358 ] = { 200000 , 450000 , 600000 , 900000 ,
1300000 , 1500000 , 1800000 , 2000000 } ,
} ;
2012-02-06 15:59:01 +00:00
/* Pre-charge current in uA */
2020-08-14 00:34:05 +03:00
static const unsigned int pcc_tbl [ NUM_CHIP_TYPES ] [ 4 ] = {
[ SMB345 ] = { 150000 , 250000 , 350000 , 450000 } ,
[ SMB347 ] = { 100000 , 150000 , 200000 , 250000 } ,
[ SMB358 ] = { 150000 , 250000 , 350000 , 450000 } ,
2012-02-06 15:59:01 +00:00
} ;
/* Termination current in uA */
2020-08-14 00:34:05 +03:00
static const unsigned int tc_tbl [ NUM_CHIP_TYPES ] [ 8 ] = {
[ SMB345 ] = { 30000 , 40000 , 60000 , 80000 ,
100000 , 125000 , 150000 , 200000 } ,
[ SMB347 ] = { 37500 , 50000 , 100000 , 150000 ,
200000 , 250000 , 500000 , 600000 } ,
[ SMB358 ] = { 30000 , 40000 , 60000 , 80000 ,
100000 , 125000 , 150000 , 200000 } ,
2012-02-06 15:59:01 +00:00
} ;
/* Input current limit in uA */
2020-08-14 00:34:05 +03:00
static const unsigned int icl_tbl [ NUM_CHIP_TYPES ] [ 10 ] = {
[ SMB345 ] = { 300000 , 500000 , 700000 , 1000000 , 1500000 ,
1800000 , 2000000 , 2000000 , 2000000 , 2000000 } ,
[ SMB347 ] = { 300000 , 500000 , 700000 , 900000 , 1200000 ,
1500000 , 1800000 , 2000000 , 2200000 , 2500000 } ,
[ SMB358 ] = { 300000 , 500000 , 700000 , 1000000 , 1500000 ,
1800000 , 2000000 , 2000000 , 2000000 , 2000000 } ,
2012-02-06 15:59:01 +00:00
} ;
/* Charge current compensation in uA */
2020-08-14 00:34:05 +03:00
static const unsigned int ccc_tbl [ NUM_CHIP_TYPES ] [ 4 ] = {
[ SMB345 ] = { 200000 , 450000 , 600000 , 900000 } ,
[ SMB347 ] = { 250000 , 700000 , 900000 , 1200000 } ,
[ SMB358 ] = { 200000 , 450000 , 600000 , 900000 } ,
2012-02-06 15:59:01 +00:00
} ;
2012-06-19 16:45:05 -07:00
/* Convert register value to current using lookup table */
static int hw_to_current ( const unsigned int * tbl , size_t size , unsigned int val )
{
if ( val > = size )
return - EINVAL ;
return tbl [ val ] ;
}
2012-02-06 15:59:01 +00:00
/* Convert current to register value using lookup table */
static int current_to_hw ( const unsigned int * tbl , size_t size , unsigned int val )
{
size_t i ;
for ( i = 0 ; i < size ; i + + )
if ( val < tbl [ i ] )
break ;
return i > 0 ? i - 1 : - EINVAL ;
}
/**
2012-04-16 11:48:39 +03:00
* smb347_update_ps_status - refreshes the power source status
2012-02-06 15:59:01 +00:00
* @ smb : pointer to smb347 charger instance
*
2012-04-16 11:48:39 +03:00
* Function checks whether any power source is connected to the charger and
* updates internal state accordingly . If there is a change to previous state
* function returns % 1 , otherwise % 0 and negative errno in case of errror .
2012-02-06 15:59:01 +00:00
*/
2012-04-16 11:48:39 +03:00
static int smb347_update_ps_status ( struct smb347_charger * smb )
2012-02-06 15:59:01 +00:00
{
bool usb = false ;
bool dc = false ;
2012-04-16 11:48:41 +03:00
unsigned int val ;
2012-02-06 15:59:01 +00:00
int ret ;
2012-04-16 11:48:41 +03:00
ret = regmap_read ( smb - > regmap , IRQSTAT_E , & val ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
/*
* Dc and usb are set depending on whether they are enabled in
* platform data _and_ whether corresponding undervoltage is set .
*/
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains )
2012-04-16 11:48:41 +03:00
dc = ! ( val & IRQSTAT_E_DCIN_UV_STAT ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb )
2012-04-16 11:48:41 +03:00
usb = ! ( val & IRQSTAT_E_USBIN_UV_STAT ) ;
2012-02-06 15:59:01 +00:00
ret = smb - > mains_online ! = dc | | smb - > usb_online ! = usb ;
smb - > mains_online = dc ;
smb - > usb_online = usb ;
return ret ;
}
/*
2012-04-16 11:48:39 +03:00
* smb347_is_ps_online - returns whether input power source is connected
2012-02-06 15:59:01 +00:00
* @ smb : pointer to smb347 charger instance
*
* Returns % true if input power source is connected . Note that this is
* dependent on what platform has configured for usable power sources . For
2012-04-16 11:48:39 +03:00
* example if USB is disabled , this will return % false even if the USB cable
* is connected .
2012-02-06 15:59:01 +00:00
*/
2012-04-16 11:48:39 +03:00
static bool smb347_is_ps_online ( struct smb347_charger * smb )
2012-02-06 15:59:01 +00:00
{
2020-08-14 00:34:07 +03:00
return smb - > usb_online | | smb - > mains_online ;
2012-02-06 15:59:01 +00:00
}
/**
* smb347_charging_status - returns status of charging
* @ smb : pointer to smb347 charger instance
*
* Function returns charging status . % 0 means no charging is in progress ,
* % 1 means pre - charging , % 2 fast - charging and % 3 taper - charging .
*/
static int smb347_charging_status ( struct smb347_charger * smb )
{
2012-04-16 11:48:41 +03:00
unsigned int val ;
2012-02-06 15:59:01 +00:00
int ret ;
2012-04-16 11:48:39 +03:00
if ( ! smb347_is_ps_online ( smb ) )
2012-02-06 15:59:01 +00:00
return 0 ;
2012-04-16 11:48:41 +03:00
ret = regmap_read ( smb - > regmap , STAT_C , & val ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return 0 ;
2012-04-16 11:48:41 +03:00
return ( val & STAT_C_CHG_MASK ) > > STAT_C_CHG_SHIFT ;
2012-02-06 15:59:01 +00:00
}
static int smb347_charging_set ( struct smb347_charger * smb , bool enable )
{
2020-08-26 16:41:58 +02:00
if ( smb - > enable_control ! = SMB3XX_CHG_ENABLE_SW ) {
2012-04-16 11:48:41 +03:00
dev_dbg ( smb - > dev , " charging enable/disable in SW disabled \n " ) ;
2012-02-06 15:59:01 +00:00
return 0 ;
}
2021-07-31 20:38:39 +03:00
if ( enable & & smb - > usb_vbus_enabled ) {
dev_dbg ( smb - > dev , " charging not enabled because USB is in host mode \n " ) ;
return 0 ;
}
2021-07-31 20:38:37 +03:00
return regmap_update_bits ( smb - > regmap , CMD_A , CMD_A_CHG_ENABLED ,
enable ? CMD_A_CHG_ENABLED : 0 ) ;
2012-02-06 15:59:01 +00:00
}
static inline int smb347_charging_enable ( struct smb347_charger * smb )
{
return smb347_charging_set ( smb , true ) ;
}
static inline int smb347_charging_disable ( struct smb347_charger * smb )
{
return smb347_charging_set ( smb , false ) ;
}
2012-04-16 11:48:39 +03:00
static int smb347_start_stop_charging ( struct smb347_charger * smb )
2012-02-06 15:59:01 +00:00
{
int ret ;
/*
* Depending on whether valid power source is connected or not , we
* disable or enable the charging . We do it manually because it
* depends on how the platform has configured the valid inputs .
*/
2012-04-16 11:48:39 +03:00
if ( smb347_is_ps_online ( smb ) ) {
2012-02-06 15:59:01 +00:00
ret = smb347_charging_enable ( smb ) ;
if ( ret < 0 )
2012-04-16 11:48:41 +03:00
dev_err ( smb - > dev , " failed to enable charging \n " ) ;
2012-02-06 15:59:01 +00:00
} else {
ret = smb347_charging_disable ( smb ) ;
if ( ret < 0 )
2012-04-16 11:48:41 +03:00
dev_err ( smb - > dev , " failed to disable charging \n " ) ;
2012-02-06 15:59:01 +00:00
}
return ret ;
}
static int smb347_set_charge_current ( struct smb347_charger * smb )
{
2020-08-14 00:34:05 +03:00
unsigned int id = smb - > id ;
2012-04-16 11:48:41 +03:00
int ret ;
2012-02-06 15:59:01 +00:00
2020-08-26 16:41:58 +02:00
if ( smb - > max_charge_current ) {
2020-08-14 00:34:05 +03:00
ret = current_to_hw ( fcc_tbl [ id ] , ARRAY_SIZE ( fcc_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > max_charge_current ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_CHARGE_CURRENT ,
CFG_CHARGE_CURRENT_FCC_MASK ,
ret < < CFG_CHARGE_CURRENT_FCC_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2020-08-26 16:41:58 +02:00
if ( smb - > pre_charge_current ) {
2020-08-14 00:34:05 +03:00
ret = current_to_hw ( pcc_tbl [ id ] , ARRAY_SIZE ( pcc_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > pre_charge_current ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_CHARGE_CURRENT ,
CFG_CHARGE_CURRENT_PCC_MASK ,
ret < < CFG_CHARGE_CURRENT_PCC_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2020-08-26 16:41:58 +02:00
if ( smb - > termination_current ) {
2020-08-14 00:34:05 +03:00
ret = current_to_hw ( tc_tbl [ id ] , ARRAY_SIZE ( tc_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > termination_current ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_CHARGE_CURRENT ,
CFG_CHARGE_CURRENT_TC_MASK , ret ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
return 0 ;
2012-02-06 15:59:01 +00:00
}
static int smb347_set_current_limits ( struct smb347_charger * smb )
{
2020-08-14 00:34:05 +03:00
unsigned int id = smb - > id ;
2012-04-16 11:48:41 +03:00
int ret ;
2012-02-06 15:59:01 +00:00
2020-08-26 16:41:58 +02:00
if ( smb - > mains_current_limit ) {
2020-08-14 00:34:05 +03:00
ret = current_to_hw ( icl_tbl [ id ] , ARRAY_SIZE ( icl_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > mains_current_limit ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_CURRENT_LIMIT ,
CFG_CURRENT_LIMIT_DC_MASK ,
ret < < CFG_CURRENT_LIMIT_DC_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2020-08-26 16:41:58 +02:00
if ( smb - > usb_hc_current_limit ) {
2020-08-14 00:34:05 +03:00
ret = current_to_hw ( icl_tbl [ id ] , ARRAY_SIZE ( icl_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > usb_hc_current_limit ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_CURRENT_LIMIT ,
CFG_CURRENT_LIMIT_USB_MASK , ret ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
return 0 ;
2012-02-06 15:59:01 +00:00
}
static int smb347_set_voltage_limits ( struct smb347_charger * smb )
{
2012-04-16 11:48:41 +03:00
int ret ;
2012-02-06 15:59:01 +00:00
2020-08-26 16:41:58 +02:00
if ( smb - > pre_to_fast_voltage ) {
ret = smb - > pre_to_fast_voltage ;
2012-02-06 15:59:01 +00:00
/* uV */
2012-04-16 11:48:41 +03:00
ret = clamp_val ( ret , 2400000 , 3000000 ) - 2400000 ;
ret / = 200000 ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_FLOAT_VOLTAGE ,
CFG_FLOAT_VOLTAGE_THRESHOLD_MASK ,
ret < < CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2020-08-26 16:41:58 +02:00
if ( smb - > max_charge_voltage ) {
ret = smb - > max_charge_voltage ;
2012-02-06 15:59:01 +00:00
/* uV */
2012-04-16 11:48:41 +03:00
ret = clamp_val ( ret , 3500000 , 4500000 ) - 3500000 ;
ret / = 20000 ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_FLOAT_VOLTAGE ,
CFG_FLOAT_VOLTAGE_FLOAT_MASK , ret ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
return 0 ;
2012-02-06 15:59:01 +00:00
}
static int smb347_set_temp_limits ( struct smb347_charger * smb )
{
2020-08-14 00:34:05 +03:00
unsigned int id = smb - > id ;
2012-02-06 15:59:01 +00:00
bool enable_therm_monitor = false ;
2012-04-16 11:48:41 +03:00
int ret = 0 ;
int val ;
2012-02-06 15:59:01 +00:00
2020-08-26 16:41:58 +02:00
if ( smb - > chip_temp_threshold ) {
val = smb - > chip_temp_threshold ;
2012-02-06 15:59:01 +00:00
/* degree C */
val = clamp_val ( val , 100 , 130 ) - 100 ;
val / = 10 ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_OTG ,
CFG_OTG_TEMP_THRESHOLD_MASK ,
val < < CFG_OTG_TEMP_THRESHOLD_SHIFT ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > soft_cold_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT ) {
val = smb - > soft_cold_temp_limit ;
2012-02-06 15:59:01 +00:00
val = clamp_val ( val , 0 , 15 ) ;
val / = 5 ;
/* this goes from higher to lower so invert the value */
val = ~ val & 0x3 ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_TEMP_LIMIT ,
CFG_TEMP_LIMIT_SOFT_COLD_MASK ,
val < < CFG_TEMP_LIMIT_SOFT_COLD_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
enable_therm_monitor = true ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > soft_hot_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT ) {
val = smb - > soft_hot_temp_limit ;
2012-02-06 15:59:01 +00:00
val = clamp_val ( val , 40 , 55 ) - 40 ;
val / = 5 ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_TEMP_LIMIT ,
CFG_TEMP_LIMIT_SOFT_HOT_MASK ,
val < < CFG_TEMP_LIMIT_SOFT_HOT_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
enable_therm_monitor = true ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > hard_cold_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT ) {
val = smb - > hard_cold_temp_limit ;
2012-02-06 15:59:01 +00:00
val = clamp_val ( val , - 5 , 10 ) + 5 ;
val / = 5 ;
/* this goes from higher to lower so invert the value */
val = ~ val & 0x3 ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_TEMP_LIMIT ,
CFG_TEMP_LIMIT_HARD_COLD_MASK ,
val < < CFG_TEMP_LIMIT_HARD_COLD_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
enable_therm_monitor = true ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > hard_hot_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT ) {
val = smb - > hard_hot_temp_limit ;
2012-02-06 15:59:01 +00:00
val = clamp_val ( val , 50 , 65 ) - 50 ;
val / = 5 ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_TEMP_LIMIT ,
CFG_TEMP_LIMIT_HARD_HOT_MASK ,
val < < CFG_TEMP_LIMIT_HARD_HOT_SHIFT ) ;
if ( ret < 0 )
return ret ;
2012-02-06 15:59:01 +00:00
enable_therm_monitor = true ;
}
/*
* If any of the temperature limits are set , we also enable the
* thermistor monitoring .
*
* When soft limits are hit , the device will start to compensate
* current and / or voltage depending on the configuration .
*
* When hard limit is hit , the device will suspend charging
* depending on the configuration .
*/
if ( enable_therm_monitor ) {
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_THERM ,
CFG_THERM_MONITOR_DISABLED , 0 ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > suspend_on_hard_temp_limit ) {
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_SYSOK ,
CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED , 0 ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > soft_temp_limit_compensation ! =
SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT ) {
val = smb - > soft_temp_limit_compensation & 0x3 ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_THERM ,
CFG_THERM_SOFT_HOT_COMPENSATION_MASK ,
val < < CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_THERM ,
CFG_THERM_SOFT_COLD_COMPENSATION_MASK ,
val < < CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
}
2020-08-26 16:41:58 +02:00
if ( smb - > charge_current_compensation ) {
2020-08-14 00:34:05 +03:00
val = current_to_hw ( ccc_tbl [ id ] , ARRAY_SIZE ( ccc_tbl [ id ] ) ,
2020-08-26 16:41:58 +02:00
smb - > charge_current_compensation ) ;
2012-02-06 15:59:01 +00:00
if ( val < 0 )
return val ;
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_OTG ,
CFG_OTG_CC_COMPENSATION_MASK ,
( val & 0x3 ) < < CFG_OTG_CC_COMPENSATION_SHIFT ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
}
return ret ;
}
/*
* smb347_set_writable - enables / disables writing to non - volatile registers
* @ smb : pointer to smb347 charger instance
*
* You can enable / disable writing to the non - volatile configuration
* registers by calling this function .
*
* Returns % 0 on success and negative errno in case of failure .
*/
2021-07-31 20:38:36 +03:00
static int smb347_set_writable ( struct smb347_charger * smb , bool writable ,
bool irq_toggle )
2012-02-06 15:59:01 +00:00
{
2021-07-31 20:38:36 +03:00
struct i2c_client * client = to_i2c_client ( smb - > dev ) ;
int ret ;
if ( writable & & irq_toggle & & ! smb - > irq_unsupported )
disable_irq ( client - > irq ) ;
ret = regmap_update_bits ( smb - > regmap , CMD_A , CMD_A_ALLOW_WRITE ,
writable ? CMD_A_ALLOW_WRITE : 0 ) ;
if ( ( ! writable | | ret ) & & irq_toggle & & ! smb - > irq_unsupported )
enable_irq ( client - > irq ) ;
return ret ;
2012-02-06 15:59:01 +00:00
}
static int smb347_hw_init ( struct smb347_charger * smb )
{
2012-04-16 11:48:41 +03:00
unsigned int val ;
2012-02-06 15:59:01 +00:00
int ret ;
2021-07-31 20:38:36 +03:00
ret = smb347_set_writable ( smb , true , false ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
/*
* Program the platform specific configuration values to the device
* first .
*/
ret = smb347_set_charge_current ( smb ) ;
if ( ret < 0 )
goto fail ;
ret = smb347_set_current_limits ( smb ) ;
if ( ret < 0 )
goto fail ;
ret = smb347_set_voltage_limits ( smb ) ;
if ( ret < 0 )
goto fail ;
ret = smb347_set_temp_limits ( smb ) ;
if ( ret < 0 )
goto fail ;
/* If USB charging is disabled we put the USB in suspend mode */
2020-08-26 16:41:58 +02:00
if ( ! smb - > use_usb ) {
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CMD_A ,
CMD_A_SUSPEND_ENABLED ,
CMD_A_SUSPEND_ENABLED ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
goto fail ;
}
/*
* If configured by platform data , we enable hardware Auto - OTG
* support for driving VBUS . Otherwise we disable it .
*/
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_OTHER , CFG_OTHER_RID_MASK ,
2020-08-26 16:41:58 +02:00
smb - > use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0 ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
goto fail ;
2021-07-31 20:38:38 +03:00
/* Activate pin control, making it writable. */
switch ( smb - > enable_control ) {
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW :
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH :
ret = regmap_set_bits ( smb - > regmap , CFG_PIN , CFG_PIN_EN_CTRL ) ;
if ( ret < 0 )
goto fail ;
}
2012-02-06 15:59:01 +00:00
/*
* Make the charging functionality controllable by a write to the
* command register unless pin control is specified in the platform
* data .
*/
2020-08-26 16:41:58 +02:00
switch ( smb - > enable_control ) {
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW :
2012-04-16 11:48:41 +03:00
val = CFG_PIN_EN_CTRL_ACTIVE_LOW ;
2012-02-06 15:59:01 +00:00
break ;
2020-08-26 16:41:58 +02:00
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH :
2012-04-16 11:48:41 +03:00
val = CFG_PIN_EN_CTRL_ACTIVE_HIGH ;
break ;
default :
val = 0 ;
2012-02-06 15:59:01 +00:00
break ;
}
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_PIN , CFG_PIN_EN_CTRL_MASK ,
val ) ;
if ( ret < 0 )
goto fail ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
/* Disable Automatic Power Source Detection (APSD) interrupt. */
ret = regmap_update_bits ( smb - > regmap , CFG_PIN , CFG_PIN_EN_APSD_IRQ , 0 ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
goto fail ;
2012-04-16 11:48:39 +03:00
ret = smb347_update_ps_status ( smb ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
goto fail ;
2012-04-16 11:48:39 +03:00
ret = smb347_start_stop_charging ( smb ) ;
2012-02-06 15:59:01 +00:00
fail :
2021-07-31 20:38:36 +03:00
smb347_set_writable ( smb , false , false ) ;
2012-02-06 15:59:01 +00:00
return ret ;
}
static irqreturn_t smb347_interrupt ( int irq , void * data )
{
struct smb347_charger * smb = data ;
2012-09-18 21:58:07 +05:30
unsigned int stat_c , irqstat_c , irqstat_d , irqstat_e ;
2012-04-16 11:48:41 +03:00
bool handled = false ;
int ret ;
2012-02-06 15:59:01 +00:00
2020-03-29 18:15:45 +02:00
/* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */
usleep_range ( 25000 , 35000 ) ;
2012-04-16 11:48:41 +03:00
ret = regmap_read ( smb - > regmap , STAT_C , & stat_c ) ;
if ( ret < 0 ) {
dev_warn ( smb - > dev , " reading STAT_C failed \n " ) ;
2012-02-06 15:59:01 +00:00
return IRQ_NONE ;
}
2012-04-16 11:48:41 +03:00
ret = regmap_read ( smb - > regmap , IRQSTAT_C , & irqstat_c ) ;
if ( ret < 0 ) {
dev_warn ( smb - > dev , " reading IRQSTAT_C failed \n " ) ;
2012-02-06 15:59:01 +00:00
return IRQ_NONE ;
}
2012-09-18 21:58:07 +05:30
ret = regmap_read ( smb - > regmap , IRQSTAT_D , & irqstat_d ) ;
if ( ret < 0 ) {
dev_warn ( smb - > dev , " reading IRQSTAT_D failed \n " ) ;
return IRQ_NONE ;
}
2012-04-16 11:48:41 +03:00
ret = regmap_read ( smb - > regmap , IRQSTAT_E , & irqstat_e ) ;
if ( ret < 0 ) {
dev_warn ( smb - > dev , " reading IRQSTAT_E failed \n " ) ;
2012-02-06 15:59:01 +00:00
return IRQ_NONE ;
}
/*
2012-09-18 21:58:07 +05:30
* If we get charger error we report the error back to user .
* If the error is recovered charging will resume again .
2012-02-06 15:59:01 +00:00
*/
if ( stat_c & STAT_C_CHARGER_ERROR ) {
2012-09-18 21:58:07 +05:30
dev_err ( smb - > dev , " charging stopped due to charger error \n " ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > mains ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > usb ) ;
2012-04-16 11:48:41 +03:00
handled = true ;
2012-02-06 15:59:01 +00:00
}
/*
* If we reached the termination current the battery is charged and
* we can update the status now . Charging is automatically
* disabled by the hardware .
*/
if ( irqstat_c & ( IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ ) ) {
2020-08-14 00:34:06 +03:00
if ( irqstat_c & IRQSTAT_C_TERMINATION_STAT ) {
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > mains ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > usb ) ;
}
2012-09-18 21:58:07 +05:30
dev_dbg ( smb - > dev , " going to HW maintenance mode \n " ) ;
handled = true ;
}
/*
* If we got a charger timeout INT that means the charge
* full is not detected with in charge timeout value .
*/
if ( irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ ) {
dev_dbg ( smb - > dev , " total Charge Timeout INT received \n " ) ;
if ( irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT )
dev_warn ( smb - > dev , " charging stopped due to timeout \n " ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > mains ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb )
2020-08-14 00:34:06 +03:00
power_supply_changed ( smb - > usb ) ;
2012-04-16 11:48:41 +03:00
handled = true ;
2012-02-06 15:59:01 +00:00
}
/*
* If we got an under voltage interrupt it means that AC / USB input
* was connected or disconnected .
*/
if ( irqstat_e & ( IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ ) ) {
2012-04-16 11:48:39 +03:00
if ( smb347_update_ps_status ( smb ) > 0 ) {
smb347_start_stop_charging ( smb ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains )
2015-03-12 08:44:11 +01:00
power_supply_changed ( smb - > mains ) ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb )
2015-03-12 08:44:11 +01:00
power_supply_changed ( smb - > usb ) ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
handled = true ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
return handled ? IRQ_HANDLED : IRQ_NONE ;
2012-02-06 15:59:01 +00:00
}
static int smb347_irq_set ( struct smb347_charger * smb , bool enable )
{
int ret ;
2021-01-22 22:17:34 +03:00
if ( smb - > irq_unsupported )
return 0 ;
2021-07-31 20:38:36 +03:00
ret = smb347_set_writable ( smb , true , true ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
return ret ;
/*
* Enable / disable interrupts for :
* - under voltage
* - termination current reached
2012-09-18 21:58:07 +05:30
* - charger timeout
2012-02-06 15:59:01 +00:00
* - charger error
*/
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_FAULT_IRQ , 0xff ,
enable ? CFG_FAULT_IRQ_DCIN_UV : 0 ) ;
if ( ret < 0 )
goto fail ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_STATUS_IRQ , 0xff ,
2012-09-18 21:58:07 +05:30
enable ? ( CFG_STATUS_IRQ_TERMINATION_OR_TAPER |
CFG_STATUS_IRQ_CHARGE_TIMEOUT ) : 0 ) ;
2012-04-16 11:48:41 +03:00
if ( ret < 0 )
goto fail ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_PIN , CFG_PIN_EN_CHARGER_ERROR ,
enable ? CFG_PIN_EN_CHARGER_ERROR : 0 ) ;
2012-02-06 15:59:01 +00:00
fail :
2021-07-31 20:38:36 +03:00
smb347_set_writable ( smb , false , true ) ;
2012-02-06 15:59:01 +00:00
return ret ;
}
static inline int smb347_irq_enable ( struct smb347_charger * smb )
{
return smb347_irq_set ( smb , true ) ;
}
static inline int smb347_irq_disable ( struct smb347_charger * smb )
{
return smb347_irq_set ( smb , false ) ;
}
2012-04-16 11:48:41 +03:00
static int smb347_irq_init ( struct smb347_charger * smb ,
struct i2c_client * client )
2012-02-06 15:59:01 +00:00
{
2020-08-14 00:34:03 +03:00
int ret ;
2012-02-06 15:59:01 +00:00
2021-03-02 13:10:26 +03:00
smb - > irq_unsupported = true ;
/*
* Interrupt pin is optional . If it is connected , we setup the
* interrupt support here .
*/
if ( ! client - > irq )
return 0 ;
2012-02-06 15:59:01 +00:00
2021-07-31 20:38:36 +03:00
ret = smb347_set_writable ( smb , true , false ) ;
2012-02-06 15:59:01 +00:00
if ( ret < 0 )
2020-08-14 00:34:03 +03:00
return ret ;
2012-02-06 15:59:01 +00:00
/*
* Configure the STAT output to be suitable for interrupts : disable
* all other output ( except interrupts ) and make it active low .
*/
2012-04-16 11:48:41 +03:00
ret = regmap_update_bits ( smb - > regmap , CFG_STAT ,
CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED ,
CFG_STAT_DISABLED ) ;
2012-02-06 15:59:01 +00:00
2021-07-31 20:38:36 +03:00
smb347_set_writable ( smb , false , false ) ;
2012-02-06 15:59:01 +00:00
2021-03-02 13:10:26 +03:00
if ( ret < 0 ) {
dev_warn ( smb - > dev , " failed to initialize IRQ: %d \n " , ret ) ;
dev_warn ( smb - > dev , " disabling IRQ support \n " ) ;
return 0 ;
}
ret = devm_request_threaded_irq ( smb - > dev , client - > irq , NULL ,
smb347_interrupt , IRQF_ONESHOT ,
client - > name , smb ) ;
if ( ret )
return ret ;
smb - > irq_unsupported = false ;
ret = smb347_irq_enable ( smb ) ;
if ( ret < 0 )
return ret ;
return 0 ;
2012-02-06 15:59:01 +00:00
}
2012-06-19 16:45:05 -07:00
/*
* Returns the constant charge current programmed
* into the charger in uA .
*/
static int get_const_charge_current ( struct smb347_charger * smb )
{
2020-08-14 00:34:05 +03:00
unsigned int id = smb - > id ;
2012-06-19 16:45:05 -07:00
int ret , intval ;
unsigned int v ;
if ( ! smb347_is_ps_online ( smb ) )
return - ENODATA ;
ret = regmap_read ( smb - > regmap , STAT_B , & v ) ;
if ( ret < 0 )
return ret ;
/*
* The current value is composition of FCC and PCC values
* and we can detect which table to use from bit 5.
*/
if ( v & 0x20 ) {
2020-08-14 00:34:05 +03:00
intval = hw_to_current ( fcc_tbl [ id ] ,
ARRAY_SIZE ( fcc_tbl [ id ] ) , v & 7 ) ;
2012-06-19 16:45:05 -07:00
} else {
v > > = 3 ;
2020-08-14 00:34:05 +03:00
intval = hw_to_current ( pcc_tbl [ id ] ,
ARRAY_SIZE ( pcc_tbl [ id ] ) , v & 7 ) ;
2012-06-19 16:45:05 -07:00
}
return intval ;
}
/*
* Returns the constant charge voltage programmed
* into the charger in uV .
*/
static int get_const_charge_voltage ( struct smb347_charger * smb )
{
int ret , intval ;
unsigned int v ;
if ( ! smb347_is_ps_online ( smb ) )
return - ENODATA ;
ret = regmap_read ( smb - > regmap , STAT_A , & v ) ;
if ( ret < 0 )
return ret ;
v & = STAT_A_FLOAT_VOLTAGE_MASK ;
if ( v > 0x3d )
v = 0x3d ;
intval = 3500000 + v * 20000 ;
return intval ;
}
2020-08-14 00:34:06 +03:00
static int smb347_get_charging_status ( struct smb347_charger * smb ,
struct power_supply * psy )
2012-09-18 21:58:07 +05:30
{
int ret , status ;
unsigned int val ;
2020-08-14 00:34:06 +03:00
if ( psy - > desc - > type = = POWER_SUPPLY_TYPE_USB ) {
if ( ! smb - > usb_online )
return POWER_SUPPLY_STATUS_DISCHARGING ;
} else {
if ( ! smb - > mains_online )
return POWER_SUPPLY_STATUS_DISCHARGING ;
}
2012-09-18 21:58:07 +05:30
ret = regmap_read ( smb - > regmap , STAT_C , & val ) ;
if ( ret < 0 )
return ret ;
if ( ( val & STAT_C_CHARGER_ERROR ) | |
( val & STAT_C_HOLDOFF_STAT ) ) {
/*
* set to NOT CHARGING upon charger error
* or charging has stopped .
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING ;
} else {
if ( ( val & STAT_C_CHG_MASK ) > > STAT_C_CHG_SHIFT ) {
/*
* set to charging if battery is in pre - charge ,
* fast charge or taper charging mode .
*/
status = POWER_SUPPLY_STATUS_CHARGING ;
} else if ( val & STAT_C_CHG_TERM ) {
/*
* set the status to FULL if battery is not in pre
* charge , fast charge or taper charging mode AND
* charging is terminated at least once .
*/
status = POWER_SUPPLY_STATUS_FULL ;
} else {
/*
* in this case no charger error or termination
* occured but charging is not in progress ! ! !
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING ;
}
}
return status ;
}
2020-08-14 00:34:07 +03:00
static int smb347_get_property_locked ( struct power_supply * psy ,
enum power_supply_property prop ,
union power_supply_propval * val )
2012-02-06 15:59:01 +00:00
{
2015-03-12 08:44:11 +01:00
struct smb347_charger * smb = power_supply_get_drvdata ( psy ) ;
2012-02-06 15:59:01 +00:00
int ret ;
switch ( prop ) {
case POWER_SUPPLY_PROP_STATUS :
2020-08-14 00:34:06 +03:00
ret = smb347_get_charging_status ( smb , psy ) ;
2012-09-18 21:58:07 +05:30
if ( ret < 0 )
return ret ;
val - > intval = ret ;
2012-02-06 15:59:01 +00:00
break ;
case POWER_SUPPLY_PROP_CHARGE_TYPE :
2020-08-14 00:34:06 +03:00
if ( psy - > desc - > type = = POWER_SUPPLY_TYPE_USB ) {
if ( ! smb - > usb_online )
return - ENODATA ;
} else {
if ( ! smb - > mains_online )
return - ENODATA ;
}
2012-02-06 15:59:01 +00:00
/*
* We handle trickle and pre - charging the same , and taper
* and none the same .
*/
switch ( smb347_charging_status ( smb ) ) {
case 1 :
val - > intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE ;
break ;
case 2 :
val - > intval = POWER_SUPPLY_CHARGE_TYPE_FAST ;
break ;
default :
val - > intval = POWER_SUPPLY_CHARGE_TYPE_NONE ;
break ;
}
break ;
2020-08-14 00:34:06 +03:00
case POWER_SUPPLY_PROP_ONLINE :
if ( psy - > desc - > type = = POWER_SUPPLY_TYPE_USB )
val - > intval = smb - > usb_online ;
else
val - > intval = smb - > mains_online ;
2012-02-06 15:59:01 +00:00
break ;
2020-08-14 00:34:06 +03:00
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
ret = get_const_charge_voltage ( smb ) ;
if ( ret < 0 )
return ret ;
val - > intval = ret ;
2012-02-06 15:59:01 +00:00
break ;
2020-08-14 00:34:06 +03:00
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
ret = get_const_charge_current ( smb ) ;
if ( ret < 0 )
return ret ;
val - > intval = ret ;
2012-02-06 15:59:01 +00:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2020-08-14 00:34:07 +03:00
static int smb347_get_property ( struct power_supply * psy ,
enum power_supply_property prop ,
union power_supply_propval * val )
{
struct smb347_charger * smb = power_supply_get_drvdata ( psy ) ;
struct i2c_client * client = to_i2c_client ( smb - > dev ) ;
int ret ;
2021-03-02 13:10:26 +03:00
if ( ! smb - > irq_unsupported )
disable_irq ( client - > irq ) ;
2020-08-14 00:34:07 +03:00
ret = smb347_get_property_locked ( psy , prop , val ) ;
2021-03-02 13:10:26 +03:00
if ( ! smb - > irq_unsupported )
enable_irq ( client - > irq ) ;
2020-08-14 00:34:07 +03:00
return ret ;
}
2020-08-14 00:34:06 +03:00
static enum power_supply_property smb347_properties [ ] = {
2012-02-06 15:59:01 +00:00
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_CHARGE_TYPE ,
2020-08-14 00:34:06 +03:00
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE ,
2012-02-06 15:59:01 +00:00
} ;
2012-04-16 11:48:41 +03:00
static bool smb347_volatile_reg ( struct device * dev , unsigned int reg )
2012-02-06 15:59:01 +00:00
{
2012-04-16 11:48:41 +03:00
switch ( reg ) {
case IRQSTAT_A :
case IRQSTAT_C :
2020-03-29 18:15:44 +02:00
case IRQSTAT_D :
2012-04-16 11:48:41 +03:00
case IRQSTAT_E :
case IRQSTAT_F :
case STAT_A :
case STAT_B :
case STAT_C :
case STAT_E :
return true ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
return false ;
2012-02-06 15:59:01 +00:00
}
2012-04-16 11:48:41 +03:00
static bool smb347_readable_reg ( struct device * dev , unsigned int reg )
2012-02-06 15:59:01 +00:00
{
2012-04-16 11:48:41 +03:00
switch ( reg ) {
case CFG_CHARGE_CURRENT :
case CFG_CURRENT_LIMIT :
case CFG_FLOAT_VOLTAGE :
case CFG_STAT :
case CFG_PIN :
case CFG_THERM :
case CFG_SYSOK :
case CFG_OTHER :
case CFG_OTG :
case CFG_TEMP_LIMIT :
case CFG_FAULT_IRQ :
case CFG_STATUS_IRQ :
case CFG_ADDRESS :
case CMD_A :
case CMD_B :
case CMD_C :
return true ;
}
return smb347_volatile_reg ( dev , reg ) ;
2012-02-06 15:59:01 +00:00
}
2020-08-26 16:41:58 +02:00
static void smb347_dt_parse_dev_info ( struct smb347_charger * smb )
2020-08-14 00:34:04 +03:00
{
2020-08-26 16:41:59 +02:00
struct device * dev = smb - > dev ;
2020-08-26 16:41:58 +02:00
smb - > soft_temp_limit_compensation =
SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT ;
2020-08-14 00:34:04 +03:00
/*
* These properties come from the battery info , still we need to
* pre - initialize the values . See smb347_get_battery_info ( ) below .
*/
2020-08-26 16:41:58 +02:00
smb - > soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT ;
smb - > hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT ;
smb - > soft_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT ;
smb - > hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT ;
2020-08-14 00:34:04 +03:00
/* Charging constraints */
2020-08-26 16:41:59 +02:00
device_property_read_u32 ( dev , " summit,fast-voltage-threshold-microvolt " ,
& smb - > pre_to_fast_voltage ) ;
device_property_read_u32 ( dev , " summit,mains-current-limit-microamp " ,
& smb - > mains_current_limit ) ;
device_property_read_u32 ( dev , " summit,usb-current-limit-microamp " ,
& smb - > usb_hc_current_limit ) ;
2020-08-14 00:34:04 +03:00
/* For thermometer monitoring */
2020-08-26 16:41:59 +02:00
device_property_read_u32 ( dev , " summit,chip-temperature-threshold-celsius " ,
& smb - > chip_temp_threshold ) ;
device_property_read_u32 ( dev , " summit,soft-compensation-method " ,
& smb - > soft_temp_limit_compensation ) ;
device_property_read_u32 ( dev , " summit,charge-current-compensation-microamp " ,
& smb - > charge_current_compensation ) ;
2020-08-14 00:34:04 +03:00
/* Supported charging mode */
2020-08-26 16:41:59 +02:00
smb - > use_mains = device_property_read_bool ( dev , " summit,enable-mains-charging " ) ;
smb - > use_usb = device_property_read_bool ( dev , " summit,enable-usb-charging " ) ;
smb - > use_usb_otg = device_property_read_bool ( dev , " summit,enable-otg-charging " ) ;
2020-08-14 00:34:04 +03:00
/* Select charging control */
2020-08-26 16:41:59 +02:00
device_property_read_u32 ( dev , " summit,enable-charge-control " ,
& smb - > enable_control ) ;
2021-07-31 20:38:39 +03:00
/*
* Polarity of INOK signal indicating presence of external power
* supply connected to the charger .
*/
device_property_read_u32 ( dev , " summit,inok-polarity " ,
& smb - > inok_polarity ) ;
2020-08-14 00:34:04 +03:00
}
static int smb347_get_battery_info ( struct smb347_charger * smb )
{
2021-12-15 02:01:18 +01:00
struct power_supply_battery_info * info ;
2020-08-14 00:34:04 +03:00
struct power_supply * supply ;
int err ;
if ( smb - > mains )
supply = smb - > mains ;
else
supply = smb - > usb ;
err = power_supply_get_battery_info ( supply , & info ) ;
if ( err = = - ENXIO | | err = = - ENODEV )
return 0 ;
if ( err )
return err ;
2021-12-15 02:01:18 +01:00
if ( info - > constant_charge_current_max_ua ! = - EINVAL )
smb - > max_charge_current = info - > constant_charge_current_max_ua ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > constant_charge_voltage_max_uv ! = - EINVAL )
smb - > max_charge_voltage = info - > constant_charge_voltage_max_uv ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > precharge_current_ua ! = - EINVAL )
smb - > pre_charge_current = info - > precharge_current_ua ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > charge_term_current_ua ! = - EINVAL )
smb - > termination_current = info - > charge_term_current_ua ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > temp_alert_min ! = INT_MIN )
smb - > soft_cold_temp_limit = info - > temp_alert_min ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > temp_alert_max ! = INT_MAX )
smb - > soft_hot_temp_limit = info - > temp_alert_max ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > temp_min ! = INT_MIN )
smb - > hard_cold_temp_limit = info - > temp_min ;
2020-08-14 00:34:04 +03:00
2021-12-15 02:01:18 +01:00
if ( info - > temp_max ! = INT_MAX )
smb - > hard_hot_temp_limit = info - > temp_max ;
2020-08-14 00:34:04 +03:00
/* Suspend when battery temperature is outside hard limits */
2020-08-26 16:41:58 +02:00
if ( smb - > hard_cold_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT | |
smb - > hard_hot_temp_limit ! = SMB3XX_TEMP_USE_DEFAULT )
smb - > suspend_on_hard_temp_limit = true ;
2020-08-14 00:34:04 +03:00
return 0 ;
}
2021-07-31 20:38:39 +03:00
static int smb347_usb_vbus_get_current_limit ( struct regulator_dev * rdev )
{
struct smb347_charger * smb = rdev_get_drvdata ( rdev ) ;
unsigned int val ;
int ret ;
ret = regmap_read ( smb - > regmap , CFG_OTG , & val ) ;
if ( ret < 0 )
return ret ;
/*
* It ' s unknown what happens if this bit is unset due to lack of
* access to the datasheet , assume it ' s limit - enable .
*/
if ( ! ( val & CFG_OTG_CURRENT_LIMIT_250mA ) )
return 0 ;
return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000 ;
}
static int smb347_usb_vbus_set_new_current_limit ( struct smb347_charger * smb ,
int max_uA )
{
const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA |
CFG_OTG_CURRENT_LIMIT_250mA ;
unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA ;
int ret ;
if ( max_uA > = 750000 )
val | = CFG_OTG_CURRENT_LIMIT_750mA ;
ret = regmap_update_bits ( smb - > regmap , CFG_OTG , mask , val ) ;
if ( ret < 0 )
dev_err ( smb - > dev , " failed to change USB current limit \n " ) ;
return ret ;
}
static int smb347_usb_vbus_set_current_limit ( struct regulator_dev * rdev ,
int min_uA , int max_uA )
{
struct smb347_charger * smb = rdev_get_drvdata ( rdev ) ;
int ret ;
ret = smb347_set_writable ( smb , true , true ) ;
if ( ret < 0 )
return ret ;
ret = smb347_usb_vbus_set_new_current_limit ( smb , max_uA ) ;
smb347_set_writable ( smb , false , true ) ;
return ret ;
}
static int smb347_usb_vbus_regulator_enable ( struct regulator_dev * rdev )
{
struct smb347_charger * smb = rdev_get_drvdata ( rdev ) ;
int ret , max_uA ;
ret = smb347_set_writable ( smb , true , true ) ;
if ( ret < 0 )
return ret ;
smb347_charging_disable ( smb ) ;
if ( device_property_read_bool ( & rdev - > dev , " summit,needs-inok-toggle " ) ) {
unsigned int sysok = 0 ;
if ( smb - > inok_polarity = = SMB3XX_SYSOK_INOK_ACTIVE_LOW )
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH ;
/*
* VBUS won ' t be powered if INOK is active , so we need to
* manually disable INOK on some platforms .
*/
ret = regmap_update_bits ( smb - > regmap , CFG_SYSOK ,
CFG_SYSOK_INOK_ACTIVE_HIGH , sysok ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to disable INOK \n " ) ;
goto done ;
}
}
ret = smb347_usb_vbus_get_current_limit ( rdev ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to get USB VBUS current limit \n " ) ;
goto done ;
}
max_uA = ret ;
ret = smb347_usb_vbus_set_new_current_limit ( smb , 250000 ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to preset USB VBUS current limit \n " ) ;
goto done ;
}
ret = regmap_set_bits ( smb - > regmap , CMD_A , CMD_A_OTG_ENABLED ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to enable USB VBUS \n " ) ;
goto done ;
}
smb - > usb_vbus_enabled = true ;
ret = smb347_usb_vbus_set_new_current_limit ( smb , max_uA ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to restore USB VBUS current limit \n " ) ;
goto done ;
}
done :
smb347_set_writable ( smb , false , true ) ;
return ret ;
}
static int smb347_usb_vbus_regulator_disable ( struct regulator_dev * rdev )
{
struct smb347_charger * smb = rdev_get_drvdata ( rdev ) ;
int ret ;
ret = smb347_set_writable ( smb , true , true ) ;
if ( ret < 0 )
return ret ;
ret = regmap_clear_bits ( smb - > regmap , CMD_A , CMD_A_OTG_ENABLED ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to disable USB VBUS \n " ) ;
goto done ;
}
smb - > usb_vbus_enabled = false ;
if ( device_property_read_bool ( & rdev - > dev , " summit,needs-inok-toggle " ) ) {
unsigned int sysok = 0 ;
if ( smb - > inok_polarity = = SMB3XX_SYSOK_INOK_ACTIVE_HIGH )
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH ;
ret = regmap_update_bits ( smb - > regmap , CFG_SYSOK ,
CFG_SYSOK_INOK_ACTIVE_HIGH , sysok ) ;
if ( ret < 0 ) {
dev_err ( smb - > dev , " failed to enable INOK \n " ) ;
goto done ;
}
}
smb347_start_stop_charging ( smb ) ;
done :
smb347_set_writable ( smb , false , true ) ;
return ret ;
}
2012-04-16 11:48:41 +03:00
static const struct regmap_config smb347_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = SMB347_MAX_REGISTER ,
. volatile_reg = smb347_volatile_reg ,
. readable_reg = smb347_readable_reg ,
2022-02-22 21:43:31 +00:00
. cache_type = REGCACHE_RBTREE ,
2012-02-06 15:59:01 +00:00
} ;
2021-07-31 20:38:39 +03:00
static const struct regulator_ops smb347_usb_vbus_regulator_ops = {
. is_enabled = regulator_is_enabled_regmap ,
. enable = smb347_usb_vbus_regulator_enable ,
. disable = smb347_usb_vbus_regulator_disable ,
. get_current_limit = smb347_usb_vbus_get_current_limit ,
. set_current_limit = smb347_usb_vbus_set_current_limit ,
} ;
2015-03-12 08:44:11 +01:00
static const struct power_supply_desc smb347_mains_desc = {
. name = " smb347-mains " ,
. type = POWER_SUPPLY_TYPE_MAINS ,
2020-08-14 00:34:06 +03:00
. get_property = smb347_get_property ,
. properties = smb347_properties ,
. num_properties = ARRAY_SIZE ( smb347_properties ) ,
2015-03-12 08:44:11 +01:00
} ;
static const struct power_supply_desc smb347_usb_desc = {
. name = " smb347-usb " ,
. type = POWER_SUPPLY_TYPE_USB ,
2020-08-14 00:34:06 +03:00
. get_property = smb347_get_property ,
. properties = smb347_properties ,
. num_properties = ARRAY_SIZE ( smb347_properties ) ,
2015-03-12 08:44:11 +01:00
} ;
2021-07-31 20:38:39 +03:00
static const struct regulator_desc smb347_usb_vbus_regulator_desc = {
. name = " smb347-usb-vbus " ,
. of_match = of_match_ptr ( " usb-vbus " ) ,
. ops = & smb347_usb_vbus_regulator_ops ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. enable_reg = CMD_A ,
. enable_mask = CMD_A_OTG_ENABLED ,
. enable_val = CMD_A_OTG_ENABLED ,
. fixed_uV = 5000000 ,
. n_voltages = 1 ,
} ;
2022-11-18 23:44:28 +01:00
static int smb347_probe ( struct i2c_client * client )
2012-02-06 15:59:01 +00:00
{
2022-11-18 23:44:28 +01:00
const struct i2c_device_id * id = i2c_client_get_device_id ( client ) ;
2020-08-14 00:34:06 +03:00
struct power_supply_config mains_usb_cfg = { } ;
2021-07-31 20:38:39 +03:00
struct regulator_config usb_rdev_cfg = { } ;
2012-02-06 15:59:01 +00:00
struct device * dev = & client - > dev ;
struct smb347_charger * smb ;
int ret ;
smb = devm_kzalloc ( dev , sizeof ( * smb ) , GFP_KERNEL ) ;
if ( ! smb )
return - ENOMEM ;
2012-04-16 11:48:41 +03:00
smb - > dev = & client - > dev ;
2020-08-14 00:34:05 +03:00
smb - > id = id - > driver_data ;
2020-08-26 16:41:58 +02:00
i2c_set_clientdata ( client , smb ) ;
smb347_dt_parse_dev_info ( smb ) ;
if ( ! smb - > use_mains & & ! smb - > use_usb )
return - EINVAL ;
2012-02-06 15:59:01 +00:00
2012-04-16 11:48:41 +03:00
smb - > regmap = devm_regmap_init_i2c ( client , & smb347_regmap ) ;
if ( IS_ERR ( smb - > regmap ) )
return PTR_ERR ( smb - > regmap ) ;
2015-03-12 08:44:11 +01:00
mains_usb_cfg . drv_data = smb ;
2020-08-14 00:34:04 +03:00
mains_usb_cfg . of_node = dev - > of_node ;
2020-08-26 16:41:58 +02:00
if ( smb - > use_mains ) {
2020-08-14 00:34:03 +03:00
smb - > mains = devm_power_supply_register ( dev , & smb347_mains_desc ,
& mains_usb_cfg ) ;
2015-03-12 08:44:11 +01:00
if ( IS_ERR ( smb - > mains ) )
return PTR_ERR ( smb - > mains ) ;
2012-04-19 10:00:18 +05:30
}
2020-08-26 16:41:58 +02:00
if ( smb - > use_usb ) {
2020-08-14 00:34:03 +03:00
smb - > usb = devm_power_supply_register ( dev , & smb347_usb_desc ,
& mains_usb_cfg ) ;
if ( IS_ERR ( smb - > usb ) )
2015-03-12 08:44:11 +01:00
return PTR_ERR ( smb - > usb ) ;
2012-04-19 10:00:18 +05:30
}
2012-02-06 15:59:01 +00:00
2020-08-14 00:34:04 +03:00
ret = smb347_get_battery_info ( smb ) ;
if ( ret )
return ret ;
ret = smb347_hw_init ( smb ) ;
if ( ret < 0 )
return ret ;
2021-03-02 13:10:26 +03:00
ret = smb347_irq_init ( smb , client ) ;
if ( ret )
return ret ;
2012-02-06 15:59:01 +00:00
2021-07-31 20:38:39 +03:00
usb_rdev_cfg . dev = dev ;
usb_rdev_cfg . driver_data = smb ;
usb_rdev_cfg . regmap = smb - > regmap ;
smb - > usb_rdev = devm_regulator_register ( dev ,
& smb347_usb_vbus_regulator_desc ,
& usb_rdev_cfg ) ;
if ( IS_ERR ( smb - > usb_rdev ) ) {
smb347_irq_disable ( smb ) ;
return PTR_ERR ( smb - > usb_rdev ) ;
}
2012-02-06 15:59:01 +00:00
return 0 ;
}
2022-08-15 10:02:30 +02:00
static void smb347_remove ( struct i2c_client * client )
2012-02-06 15:59:01 +00:00
{
struct smb347_charger * smb = i2c_get_clientdata ( client ) ;
2021-07-31 20:38:39 +03:00
smb347_usb_vbus_regulator_disable ( smb - > usb_rdev ) ;
2021-01-22 22:17:34 +03:00
smb347_irq_disable ( smb ) ;
2012-02-06 15:59:01 +00:00
}
2021-07-31 20:38:39 +03:00
static void smb347_shutdown ( struct i2c_client * client )
{
smb347_remove ( client ) ;
}
2012-02-06 15:59:01 +00:00
static const struct i2c_device_id smb347_id [ ] = {
2020-08-14 00:34:05 +03:00
{ " smb345 " , SMB345 } ,
{ " smb347 " , SMB347 } ,
{ " smb358 " , SMB358 } ,
{ } ,
2012-02-06 15:59:01 +00:00
} ;
MODULE_DEVICE_TABLE ( i2c , smb347_id ) ;
2020-08-14 00:34:04 +03:00
static const struct of_device_id smb3xx_of_match [ ] = {
2020-08-14 00:34:05 +03:00
{ . compatible = " summit,smb345 " } ,
2020-08-14 00:34:04 +03:00
{ . compatible = " summit,smb347 " } ,
2020-08-14 00:34:05 +03:00
{ . compatible = " summit,smb358 " } ,
2020-08-14 00:34:04 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , smb3xx_of_match ) ;
2012-02-06 15:59:01 +00:00
static struct i2c_driver smb347_driver = {
. driver = {
. name = " smb347 " ,
2020-08-14 00:34:04 +03:00
. of_match_table = smb3xx_of_match ,
2012-02-06 15:59:01 +00:00
} ,
2022-11-18 23:44:28 +01:00
. probe_new = smb347_probe ,
2021-03-02 13:10:27 +03:00
. remove = smb347_remove ,
2021-07-31 20:38:39 +03:00
. shutdown = smb347_shutdown ,
2021-03-02 13:10:27 +03:00
. id_table = smb347_id ,
2012-02-06 15:59:01 +00:00
} ;
2012-04-16 11:48:38 +03:00
module_i2c_driver ( smb347_driver ) ;
2012-02-06 15:59:01 +00:00
MODULE_AUTHOR ( " Bruce E. Robertson <bruce.e.robertson@intel.com> " ) ;
MODULE_AUTHOR ( " Mika Westerberg <mika.westerberg@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " SMB347 battery charger driver " ) ;
MODULE_LICENSE ( " GPL " ) ;