2022-02-13 22:22:59 -06:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/power_supply.h>
# include <linux/regmap.h>
# define IP5XXX_SYS_CTL0 0x01
# define IP5XXX_SYS_CTL0_WLED_DET_EN BIT(4)
# define IP5XXX_SYS_CTL0_WLED_EN BIT(3)
# define IP5XXX_SYS_CTL0_BOOST_EN BIT(2)
# define IP5XXX_SYS_CTL0_CHARGER_EN BIT(1)
# define IP5XXX_SYS_CTL1 0x02
# define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN BIT(1)
# define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN BIT(0)
# define IP5XXX_SYS_CTL2 0x0c
# define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH GENMASK(7, 3)
# define IP5XXX_SYS_CTL3 0x03
# define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL GENMASK(7, 6)
# define IP5XXX_SYS_CTL3_BTN_SHDN_EN BIT(5)
# define IP5XXX_SYS_CTL4 0x04
# define IP5XXX_SYS_CTL4_SHDN_TIME_SEL GENMASK(7, 6)
# define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN BIT(5)
# define IP5XXX_SYS_CTL5 0x07
# define IP5XXX_SYS_CTL5_NTC_DIS BIT(6)
# define IP5XXX_SYS_CTL5_WLED_MODE_SEL BIT(1)
# define IP5XXX_SYS_CTL5_BTN_SHDN_SEL BIT(0)
# define IP5XXX_CHG_CTL1 0x22
# define IP5XXX_CHG_CTL1_BOOST_UVP_SEL GENMASK(3, 2)
# define IP5XXX_CHG_CTL2 0x24
# define IP5XXX_CHG_CTL2_BAT_TYPE_SEL GENMASK(6, 5)
# define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V (0x0 << 5)
# define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V (0x1 << 5)
# define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V (0x2 << 5)
# define IP5XXX_CHG_CTL2_CONST_VOLT_SEL GENMASK(2, 1)
# define IP5XXX_CHG_CTL4 0x26
# define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN BIT(6)
# define IP5XXX_CHG_CTL4A 0x25
# define IP5XXX_CHG_CTL4A_CONST_CUR_SEL GENMASK(4, 0)
# define IP5XXX_MFP_CTL0 0x51
# define IP5XXX_MFP_CTL1 0x52
# define IP5XXX_GPIO_CTL2 0x53
# define IP5XXX_GPIO_CTL2A 0x54
# define IP5XXX_GPIO_CTL3 0x55
# define IP5XXX_READ0 0x71
# define IP5XXX_READ0_CHG_STAT GENMASK(7, 5)
# define IP5XXX_READ0_CHG_STAT_IDLE (0x0 << 5)
# define IP5XXX_READ0_CHG_STAT_TRICKLE (0x1 << 5)
# define IP5XXX_READ0_CHG_STAT_CONST_VOLT (0x2 << 5)
# define IP5XXX_READ0_CHG_STAT_CONST_CUR (0x3 << 5)
# define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP (0x4 << 5)
# define IP5XXX_READ0_CHG_STAT_FULL (0x5 << 5)
# define IP5XXX_READ0_CHG_STAT_TIMEOUT (0x6 << 5)
# define IP5XXX_READ0_CHG_OP BIT(4)
# define IP5XXX_READ0_CHG_END BIT(3)
# define IP5XXX_READ0_CONST_VOLT_TIMEOUT BIT(2)
# define IP5XXX_READ0_CHG_TIMEOUT BIT(1)
# define IP5XXX_READ0_TRICKLE_TIMEOUT BIT(0)
# define IP5XXX_READ0_TIMEOUT GENMASK(2, 0)
# define IP5XXX_READ1 0x72
# define IP5XXX_READ1_WLED_PRESENT BIT(7)
# define IP5XXX_READ1_LIGHT_LOAD BIT(6)
# define IP5XXX_READ1_VIN_OVERVOLT BIT(5)
# define IP5XXX_READ2 0x77
# define IP5XXX_READ2_BTN_PRESS BIT(3)
# define IP5XXX_READ2_BTN_LONG_PRESS BIT(1)
# define IP5XXX_READ2_BTN_SHORT_PRESS BIT(0)
# define IP5XXX_BATVADC_DAT0 0xa2
# define IP5XXX_BATVADC_DAT1 0xa3
# define IP5XXX_BATIADC_DAT0 0xa4
# define IP5XXX_BATIADC_DAT1 0xa5
# define IP5XXX_BATOCV_DAT0 0xa8
# define IP5XXX_BATOCV_DAT1 0xa9
struct ip5xxx {
struct regmap * regmap ;
bool initialized ;
} ;
/*
* The IP5xxx charger only responds on I2C when it is " awake " . The charger is
* generally only awake when VIN is powered or when its boost converter is
* enabled . Going into shutdown resets all register values . To handle this :
* 1 ) When any bus error occurs , assume the charger has gone into shutdown .
* 2 ) Attempt the initialization sequence on each subsequent register access
* until it succeeds .
*/
static int ip5xxx_read ( struct ip5xxx * ip5xxx , unsigned int reg ,
unsigned int * val )
{
int ret ;
ret = regmap_read ( ip5xxx - > regmap , reg , val ) ;
if ( ret )
ip5xxx - > initialized = false ;
return ret ;
}
static int ip5xxx_update_bits ( struct ip5xxx * ip5xxx , unsigned int reg ,
unsigned int mask , unsigned int val )
{
int ret ;
ret = regmap_update_bits ( ip5xxx - > regmap , reg , mask , val ) ;
if ( ret )
ip5xxx - > initialized = false ;
return ret ;
}
static int ip5xxx_initialize ( struct power_supply * psy )
{
struct ip5xxx * ip5xxx = power_supply_get_drvdata ( psy ) ;
int ret ;
if ( ip5xxx - > initialized )
return 0 ;
/*
* Disable shutdown under light load .
* Enable power on when under load .
*/
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL1 ,
IP5XXX_SYS_CTL1_LIGHT_SHDN_EN |
IP5XXX_SYS_CTL1_LOAD_PWRUP_EN ,
IP5XXX_SYS_CTL1_LOAD_PWRUP_EN ) ;
if ( ret )
return ret ;
/*
* Enable shutdown after a long button press ( as configured below ) .
*/
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL3 ,
IP5XXX_SYS_CTL3_BTN_SHDN_EN ,
IP5XXX_SYS_CTL3_BTN_SHDN_EN ) ;
if ( ret )
return ret ;
/*
* Power on automatically when VIN is removed .
*/
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL4 ,
IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN ,
IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN ) ;
if ( ret )
return ret ;
/*
* Enable the NTC .
* Configure the button for two presses = > LED , long press = > shutdown .
*/
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL5 ,
IP5XXX_SYS_CTL5_NTC_DIS |
IP5XXX_SYS_CTL5_WLED_MODE_SEL |
IP5XXX_SYS_CTL5_BTN_SHDN_SEL ,
IP5XXX_SYS_CTL5_WLED_MODE_SEL |
IP5XXX_SYS_CTL5_BTN_SHDN_SEL ) ;
if ( ret )
return ret ;
ip5xxx - > initialized = true ;
dev_dbg ( psy - > dev . parent , " Initialized after power on \n " ) ;
return 0 ;
}
static const enum power_supply_property ip5xxx_battery_properties [ ] = {
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_CHARGE_TYPE ,
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ,
POWER_SUPPLY_PROP_VOLTAGE_NOW ,
POWER_SUPPLY_PROP_VOLTAGE_OCV ,
POWER_SUPPLY_PROP_CURRENT_NOW ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE ,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX ,
} ;
static int ip5xxx_battery_get_status ( struct ip5xxx * ip5xxx , int * val )
{
unsigned int rval ;
int ret ;
ret = ip5xxx_read ( ip5xxx , IP5XXX_READ0 , & rval ) ;
if ( ret )
return ret ;
switch ( rval & IP5XXX_READ0_CHG_STAT ) {
case IP5XXX_READ0_CHG_STAT_IDLE :
* val = POWER_SUPPLY_STATUS_DISCHARGING ;
break ;
case IP5XXX_READ0_CHG_STAT_TRICKLE :
case IP5XXX_READ0_CHG_STAT_CONST_CUR :
case IP5XXX_READ0_CHG_STAT_CONST_VOLT :
* val = POWER_SUPPLY_STATUS_CHARGING ;
break ;
case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP :
case IP5XXX_READ0_CHG_STAT_FULL :
* val = POWER_SUPPLY_STATUS_FULL ;
break ;
case IP5XXX_READ0_CHG_STAT_TIMEOUT :
* val = POWER_SUPPLY_STATUS_NOT_CHARGING ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int ip5xxx_battery_get_charge_type ( struct ip5xxx * ip5xxx , int * val )
{
unsigned int rval ;
int ret ;
ret = ip5xxx_read ( ip5xxx , IP5XXX_READ0 , & rval ) ;
if ( ret )
return ret ;
switch ( rval & IP5XXX_READ0_CHG_STAT ) {
case IP5XXX_READ0_CHG_STAT_IDLE :
case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP :
case IP5XXX_READ0_CHG_STAT_FULL :
case IP5XXX_READ0_CHG_STAT_TIMEOUT :
* val = POWER_SUPPLY_CHARGE_TYPE_NONE ;
break ;
case IP5XXX_READ0_CHG_STAT_TRICKLE :
* val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE ;
break ;
case IP5XXX_READ0_CHG_STAT_CONST_CUR :
case IP5XXX_READ0_CHG_STAT_CONST_VOLT :
* val = POWER_SUPPLY_CHARGE_TYPE_STANDARD ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int ip5xxx_battery_get_health ( struct ip5xxx * ip5xxx , int * val )
{
unsigned int rval ;
int ret ;
ret = ip5xxx_read ( ip5xxx , IP5XXX_READ0 , & rval ) ;
if ( ret )
return ret ;
if ( rval & IP5XXX_READ0_TIMEOUT )
* val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE ;
else
* val = POWER_SUPPLY_HEALTH_GOOD ;
return 0 ;
}
static int ip5xxx_battery_get_voltage_max ( struct ip5xxx * ip5xxx , int * val )
{
unsigned int rval ;
int ret ;
ret = ip5xxx_read ( ip5xxx , IP5XXX_CHG_CTL2 , & rval ) ;
if ( ret )
return ret ;
/*
* It is not clear what this will return if
* IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set . . .
*/
switch ( rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL ) {
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V :
* val = 4200000 ;
break ;
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V :
* val = 4300000 ;
break ;
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V :
* val = 4350000 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int ip5xxx_battery_read_adc ( struct ip5xxx * ip5xxx ,
u8 lo_reg , u8 hi_reg , int * val )
{
unsigned int hi , lo ;
int ret ;
ret = ip5xxx_read ( ip5xxx , lo_reg , & lo ) ;
if ( ret )
return ret ;
ret = ip5xxx_read ( ip5xxx , hi_reg , & hi ) ;
if ( ret )
return ret ;
* val = sign_extend32 ( hi < < 8 | lo , 13 ) ;
return 0 ;
}
static int ip5xxx_battery_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct ip5xxx * ip5xxx = power_supply_get_drvdata ( psy ) ;
int raw , ret , vmax ;
unsigned int rval ;
ret = ip5xxx_initialize ( psy ) ;
if ( ret )
return ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
return ip5xxx_battery_get_status ( ip5xxx , & val - > intval ) ;
case POWER_SUPPLY_PROP_CHARGE_TYPE :
return ip5xxx_battery_get_charge_type ( ip5xxx , & val - > intval ) ;
case POWER_SUPPLY_PROP_HEALTH :
return ip5xxx_battery_get_health ( ip5xxx , & val - > intval ) ;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN :
return ip5xxx_battery_get_voltage_max ( ip5xxx , & val - > intval ) ;
case POWER_SUPPLY_PROP_VOLTAGE_NOW :
ret = ip5xxx_battery_read_adc ( ip5xxx , IP5XXX_BATVADC_DAT0 ,
IP5XXX_BATVADC_DAT1 , & raw ) ;
val - > intval = 2600000 + DIV_ROUND_CLOSEST ( raw * 26855 , 100 ) ;
return 0 ;
case POWER_SUPPLY_PROP_VOLTAGE_OCV :
ret = ip5xxx_battery_read_adc ( ip5xxx , IP5XXX_BATOCV_DAT0 ,
IP5XXX_BATOCV_DAT1 , & raw ) ;
val - > intval = 2600000 + DIV_ROUND_CLOSEST ( raw * 26855 , 100 ) ;
return 0 ;
case POWER_SUPPLY_PROP_CURRENT_NOW :
ret = ip5xxx_battery_read_adc ( ip5xxx , IP5XXX_BATIADC_DAT0 ,
IP5XXX_BATIADC_DAT1 , & raw ) ;
2022-10-29 00:40:52 +02:00
val - > intval = DIV_ROUND_CLOSEST ( raw * 149197 , 200 ) ;
2022-02-13 22:22:59 -06:00
return 0 ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
ret = ip5xxx_read ( ip5xxx , IP5XXX_CHG_CTL4A , & rval ) ;
if ( ret )
return ret ;
rval & = IP5XXX_CHG_CTL4A_CONST_CUR_SEL ;
val - > intval = 100000 * rval ;
return 0 ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX :
val - > intval = 100000 * 0x1f ;
return 0 ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
ret = ip5xxx_battery_get_voltage_max ( ip5xxx , & vmax ) ;
if ( ret )
return ret ;
ret = ip5xxx_read ( ip5xxx , IP5XXX_CHG_CTL2 , & rval ) ;
if ( ret )
return ret ;
rval & = IP5XXX_CHG_CTL2_CONST_VOLT_SEL ;
val - > intval = vmax + 14000 * ( rval > > 1 ) ;
return 0 ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX :
ret = ip5xxx_battery_get_voltage_max ( ip5xxx , & vmax ) ;
if ( ret )
return ret ;
val - > intval = vmax + 14000 * 3 ;
return 0 ;
default :
return - EINVAL ;
}
}
static int ip5xxx_battery_set_voltage_max ( struct ip5xxx * ip5xxx , int val )
{
unsigned int rval ;
int ret ;
switch ( val ) {
case 4200000 :
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V ;
break ;
case 4300000 :
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V ;
break ;
case 4350000 :
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V ;
break ;
default :
return - EINVAL ;
}
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_CHG_CTL2 ,
IP5XXX_CHG_CTL2_BAT_TYPE_SEL , rval ) ;
if ( ret )
return ret ;
ret = ip5xxx_update_bits ( ip5xxx , IP5XXX_CHG_CTL4 ,
IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN ,
IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN ) ;
if ( ret )
return ret ;
return 0 ;
}
static int ip5xxx_battery_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
struct ip5xxx * ip5xxx = power_supply_get_drvdata ( psy ) ;
unsigned int rval ;
int ret , vmax ;
ret = ip5xxx_initialize ( psy ) ;
if ( ret )
return ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
switch ( val - > intval ) {
case POWER_SUPPLY_STATUS_CHARGING :
rval = IP5XXX_SYS_CTL0_CHARGER_EN ;
break ;
case POWER_SUPPLY_STATUS_DISCHARGING :
case POWER_SUPPLY_STATUS_NOT_CHARGING :
rval = 0 ;
break ;
default :
return - EINVAL ;
}
return ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL0 ,
IP5XXX_SYS_CTL0_CHARGER_EN , rval ) ;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN :
return ip5xxx_battery_set_voltage_max ( ip5xxx , val - > intval ) ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT :
rval = val - > intval / 100000 ;
return ip5xxx_update_bits ( ip5xxx , IP5XXX_CHG_CTL4A ,
IP5XXX_CHG_CTL4A_CONST_CUR_SEL , rval ) ;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE :
ret = ip5xxx_battery_get_voltage_max ( ip5xxx , & vmax ) ;
if ( ret )
return ret ;
rval = ( ( val - > intval - vmax ) / 14000 ) < < 1 ;
return ip5xxx_update_bits ( ip5xxx , IP5XXX_CHG_CTL2 ,
IP5XXX_CHG_CTL2_CONST_VOLT_SEL , rval ) ;
default :
return - EINVAL ;
}
}
static int ip5xxx_battery_property_is_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
return psp = = POWER_SUPPLY_PROP_STATUS | |
psp = = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN | |
psp = = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT | |
psp = = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE ;
}
static const struct power_supply_desc ip5xxx_battery_desc = {
. name = " ip5xxx-battery " ,
. type = POWER_SUPPLY_TYPE_BATTERY ,
. properties = ip5xxx_battery_properties ,
. num_properties = ARRAY_SIZE ( ip5xxx_battery_properties ) ,
. get_property = ip5xxx_battery_get_property ,
. set_property = ip5xxx_battery_set_property ,
. property_is_writeable = ip5xxx_battery_property_is_writeable ,
} ;
static const enum power_supply_property ip5xxx_boost_properties [ ] = {
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ,
} ;
static int ip5xxx_boost_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct ip5xxx * ip5xxx = power_supply_get_drvdata ( psy ) ;
unsigned int rval ;
int ret ;
ret = ip5xxx_initialize ( psy ) ;
if ( ret )
return ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
ret = ip5xxx_read ( ip5xxx , IP5XXX_SYS_CTL0 , & rval ) ;
if ( ret )
return ret ;
val - > intval = ! ! ( rval & IP5XXX_SYS_CTL0_BOOST_EN ) ;
return 0 ;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN :
ret = ip5xxx_read ( ip5xxx , IP5XXX_CHG_CTL1 , & rval ) ;
if ( ret )
return ret ;
rval & = IP5XXX_CHG_CTL1_BOOST_UVP_SEL ;
val - > intval = 4530000 + 100000 * ( rval > > 2 ) ;
return 0 ;
default :
return - EINVAL ;
}
}
static int ip5xxx_boost_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
struct ip5xxx * ip5xxx = power_supply_get_drvdata ( psy ) ;
unsigned int rval ;
int ret ;
ret = ip5xxx_initialize ( psy ) ;
if ( ret )
return ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
rval = val - > intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0 ;
return ip5xxx_update_bits ( ip5xxx , IP5XXX_SYS_CTL0 ,
IP5XXX_SYS_CTL0_BOOST_EN , rval ) ;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN :
rval = ( ( val - > intval - 4530000 ) / 100000 ) < < 2 ;
return ip5xxx_update_bits ( ip5xxx , IP5XXX_CHG_CTL1 ,
IP5XXX_CHG_CTL1_BOOST_UVP_SEL , rval ) ;
default :
return - EINVAL ;
}
}
static int ip5xxx_boost_property_is_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
return true ;
}
static const struct power_supply_desc ip5xxx_boost_desc = {
. name = " ip5xxx-boost " ,
. type = POWER_SUPPLY_TYPE_USB ,
. properties = ip5xxx_boost_properties ,
. num_properties = ARRAY_SIZE ( ip5xxx_boost_properties ) ,
. get_property = ip5xxx_boost_get_property ,
. set_property = ip5xxx_boost_set_property ,
. property_is_writeable = ip5xxx_boost_property_is_writeable ,
} ;
static const struct regmap_config ip5xxx_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = IP5XXX_BATOCV_DAT1 ,
} ;
static int ip5xxx_power_probe ( struct i2c_client * client )
{
struct power_supply_config psy_cfg = { } ;
struct device * dev = & client - > dev ;
struct power_supply * psy ;
struct ip5xxx * ip5xxx ;
ip5xxx = devm_kzalloc ( dev , sizeof ( * ip5xxx ) , GFP_KERNEL ) ;
if ( ! ip5xxx )
return - ENOMEM ;
ip5xxx - > regmap = devm_regmap_init_i2c ( client , & ip5xxx_regmap_config ) ;
if ( IS_ERR ( ip5xxx - > regmap ) )
return PTR_ERR ( ip5xxx - > regmap ) ;
psy_cfg . of_node = dev - > of_node ;
psy_cfg . drv_data = ip5xxx ;
psy = devm_power_supply_register ( dev , & ip5xxx_battery_desc , & psy_cfg ) ;
if ( IS_ERR ( psy ) )
return PTR_ERR ( psy ) ;
psy = devm_power_supply_register ( dev , & ip5xxx_boost_desc , & psy_cfg ) ;
if ( IS_ERR ( psy ) )
return PTR_ERR ( psy ) ;
return 0 ;
}
static const struct of_device_id ip5xxx_power_of_match [ ] = {
{ . compatible = " injoinic,ip5108 " } ,
{ . compatible = " injoinic,ip5109 " } ,
{ . compatible = " injoinic,ip5207 " } ,
{ . compatible = " injoinic,ip5209 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ip5xxx_power_of_match ) ;
static struct i2c_driver ip5xxx_power_driver = {
. probe_new = ip5xxx_power_probe ,
. driver = {
. name = " ip5xxx-power " ,
. of_match_table = ip5xxx_power_of_match ,
}
} ;
module_i2c_driver ( ip5xxx_power_driver ) ;
MODULE_AUTHOR ( " Samuel Holland <samuel@sholland.org> " ) ;
MODULE_DESCRIPTION ( " Injoinic IP5xxx power bank IC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;