2018-08-07 18:21:06 +02:00
// SPDX-License-Identifier: GPL-2.0+
//
// max8997_charger.c - Power supply consumer driver for the Maxim 8997/8966
//
// Copyright (C) 2011 Samsung Electronics
// MyungJoo Ham <myungjoo.ham@samsung.com>
2011-06-22 19:40:06 +09:00
# include <linux/err.h>
2020-12-30 20:52:15 +00:00
# include <linux/extcon.h>
2011-08-01 07:29:31 +08:00
# include <linux/module.h>
2011-06-22 19:40:06 +09:00
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
# include <linux/mfd/max8997.h>
# include <linux/mfd/max8997-private.h>
2020-12-30 20:52:15 +00:00
# include <linux/regulator/consumer.h>
2022-02-13 12:19:31 +01:00
# include <linux/devm-helpers.h>
2011-06-22 19:40:06 +09:00
2020-11-09 19:43:55 +00:00
/* MAX8997_REG_STATUS4 */
# define DCINOK_SHIFT 1
# define DCINOK_MASK (1 << DCINOK_SHIFT)
# define DETBAT_SHIFT 2
# define DETBAT_MASK (1 << DETBAT_SHIFT)
/* MAX8997_REG_MBCCTRL1 */
# define TFCH_SHIFT 4
# define TFCH_MASK (7 << TFCH_SHIFT)
/* MAX8997_REG_MBCCTRL5 */
# define ITOPOFF_SHIFT 0
# define ITOPOFF_MASK (0xF << ITOPOFF_SHIFT)
2011-06-22 19:40:06 +09:00
struct charger_data {
struct device * dev ;
struct max8997_dev * iodev ;
2015-03-12 08:44:11 +01:00
struct power_supply * battery ;
2020-12-30 20:52:15 +00:00
struct regulator * reg ;
struct extcon_dev * edev ;
struct notifier_block extcon_nb ;
struct work_struct extcon_work ;
2011-06-22 19:40:06 +09:00
} ;
static enum power_supply_property max8997_battery_props [ ] = {
2020-11-09 19:43:55 +00:00
POWER_SUPPLY_PROP_STATUS , /* "FULL", "CHARGING" or "DISCHARGING". */
2011-06-22 19:40:06 +09:00
POWER_SUPPLY_PROP_PRESENT , /* the presence of battery */
POWER_SUPPLY_PROP_ONLINE , /* charger is active or not */
} ;
/* Note that the charger control is done by a current regulator "CHARGER" */
static int max8997_battery_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
2015-03-12 08:44:11 +01:00
struct charger_data * charger = power_supply_get_drvdata ( psy ) ;
2011-06-22 19:40:06 +09:00
struct i2c_client * i2c = charger - > iodev - > i2c ;
int ret ;
u8 reg ;
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
val - > intval = 0 ;
ret = max8997_read_reg ( i2c , MAX8997_REG_STATUS4 , & reg ) ;
if ( ret )
return ret ;
if ( ( reg & ( 1 < < 0 ) ) = = 0x1 )
val - > intval = POWER_SUPPLY_STATUS_FULL ;
2020-11-09 19:43:55 +00:00
else if ( ( reg & DCINOK_MASK ) )
val - > intval = POWER_SUPPLY_STATUS_CHARGING ;
else
val - > intval = POWER_SUPPLY_STATUS_DISCHARGING ;
2011-06-22 19:40:06 +09:00
break ;
case POWER_SUPPLY_PROP_PRESENT :
val - > intval = 0 ;
ret = max8997_read_reg ( i2c , MAX8997_REG_STATUS4 , & reg ) ;
if ( ret )
return ret ;
2020-11-09 19:43:55 +00:00
if ( ( reg & DETBAT_MASK ) = = 0x0 )
2011-06-22 19:40:06 +09:00
val - > intval = 1 ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = 0 ;
ret = max8997_read_reg ( i2c , MAX8997_REG_STATUS4 , & reg ) ;
if ( ret )
return ret ;
2020-11-09 19:43:55 +00:00
if ( reg & DCINOK_MASK )
2011-06-22 19:40:06 +09:00
val - > intval = 1 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2020-12-30 20:52:15 +00:00
static void max8997_battery_extcon_evt_worker ( struct work_struct * work )
{
struct charger_data * charger =
container_of ( work , struct charger_data , extcon_work ) ;
struct extcon_dev * edev = charger - > edev ;
int current_limit ;
if ( extcon_get_state ( edev , EXTCON_CHG_USB_SDP ) > 0 ) {
dev_dbg ( charger - > dev , " USB SDP charger is connected \n " ) ;
current_limit = 450000 ;
} else if ( extcon_get_state ( edev , EXTCON_CHG_USB_DCP ) > 0 ) {
dev_dbg ( charger - > dev , " USB DCP charger is connected \n " ) ;
current_limit = 650000 ;
} else if ( extcon_get_state ( edev , EXTCON_CHG_USB_FAST ) > 0 ) {
dev_dbg ( charger - > dev , " USB FAST charger is connected \n " ) ;
current_limit = 650000 ;
} else if ( extcon_get_state ( edev , EXTCON_CHG_USB_SLOW ) > 0 ) {
dev_dbg ( charger - > dev , " USB SLOW charger is connected \n " ) ;
current_limit = 650000 ;
} else if ( extcon_get_state ( edev , EXTCON_CHG_USB_CDP ) > 0 ) {
dev_dbg ( charger - > dev , " USB CDP charger is connected \n " ) ;
current_limit = 650000 ;
} else {
2021-01-04 13:05:13 +00:00
dev_dbg ( charger - > dev , " USB charger is disconnected \n " ) ;
2020-12-30 20:52:15 +00:00
current_limit = - 1 ;
}
if ( current_limit > 0 ) {
int ret = regulator_set_current_limit ( charger - > reg , current_limit , current_limit ) ;
if ( ret ) {
dev_err ( charger - > dev , " failed to set current limit: %d \n " , ret ) ;
return ;
}
ret = regulator_enable ( charger - > reg ) ;
if ( ret )
dev_err ( charger - > dev , " failed to enable regulator: %d \n " , ret ) ;
} else {
int ret = regulator_disable ( charger - > reg ) ;
if ( ret )
dev_err ( charger - > dev , " failed to disable regulator: %d \n " , ret ) ;
}
}
static int max8997_battery_extcon_evt ( struct notifier_block * nb ,
unsigned long event , void * param )
{
struct charger_data * charger =
container_of ( nb , struct charger_data , extcon_nb ) ;
schedule_work ( & charger - > extcon_work ) ;
return NOTIFY_OK ;
}
2015-03-12 08:44:11 +01:00
static const struct power_supply_desc max8997_battery_desc = {
. name = " max8997_pmic " ,
. type = POWER_SUPPLY_TYPE_BATTERY ,
. get_property = max8997_battery_get_property ,
. properties = max8997_battery_props ,
. num_properties = ARRAY_SIZE ( max8997_battery_props ) ,
} ;
2012-11-19 13:22:23 -05:00
static int max8997_battery_probe ( struct platform_device * pdev )
2011-06-22 19:40:06 +09:00
{
int ret = 0 ;
struct charger_data * charger ;
struct max8997_dev * iodev = dev_get_drvdata ( pdev - > dev . parent ) ;
2021-03-29 14:38:02 +00:00
struct device_node * np = pdev - > dev . of_node ;
2020-11-09 19:43:55 +00:00
struct i2c_client * i2c = iodev - > i2c ;
2020-11-09 19:43:28 +00:00
struct max8997_platform_data * pdata = iodev - > pdata ;
2015-03-12 08:44:11 +01:00
struct power_supply_config psy_cfg = { } ;
2011-06-22 19:40:06 +09:00
2020-11-09 19:43:55 +00:00
if ( ! pdata ) {
dev_err ( & pdev - > dev , " No platform data supplied. \n " ) ;
2011-06-22 19:40:06 +09:00
return - EINVAL ;
2020-11-09 19:43:55 +00:00
}
2011-06-22 19:40:06 +09:00
if ( pdata - > eoc_mA ) {
2011-10-29 07:56:32 +09:00
int val = ( pdata - > eoc_mA - 50 ) / 10 ;
2011-06-22 19:40:06 +09:00
if ( val < 0 )
val = 0 ;
if ( val > 0xf )
val = 0xf ;
2020-11-09 19:43:55 +00:00
ret = max8997_update_reg ( i2c , MAX8997_REG_MBCCTRL5 ,
val < < ITOPOFF_SHIFT , ITOPOFF_MASK ) ;
2011-06-22 19:40:06 +09:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Cannot use i2c bus. \n " ) ;
return ret ;
}
}
switch ( pdata - > timeout ) {
case 5 :
2020-11-09 19:43:55 +00:00
ret = max8997_update_reg ( i2c , MAX8997_REG_MBCCTRL1 ,
0x2 < < TFCH_SHIFT , TFCH_MASK ) ;
2011-06-22 19:40:06 +09:00
break ;
case 6 :
2020-11-09 19:43:55 +00:00
ret = max8997_update_reg ( i2c , MAX8997_REG_MBCCTRL1 ,
0x3 < < TFCH_SHIFT , TFCH_MASK ) ;
2011-06-22 19:40:06 +09:00
break ;
case 7 :
2020-11-09 19:43:55 +00:00
ret = max8997_update_reg ( i2c , MAX8997_REG_MBCCTRL1 ,
0x4 < < TFCH_SHIFT , TFCH_MASK ) ;
2011-06-22 19:40:06 +09:00
break ;
case 0 :
2020-11-09 19:43:55 +00:00
ret = max8997_update_reg ( i2c , MAX8997_REG_MBCCTRL1 ,
0x7 < < TFCH_SHIFT , TFCH_MASK ) ;
2011-06-22 19:40:06 +09:00
break ;
default :
dev_err ( & pdev - > dev , " incorrect timeout value (%d) \n " ,
pdata - > timeout ) ;
return - EINVAL ;
}
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Cannot use i2c bus. \n " ) ;
return ret ;
}
2017-11-06 13:46:10 +01:00
charger = devm_kzalloc ( & pdev - > dev , sizeof ( * charger ) , GFP_KERNEL ) ;
2016-12-08 14:42:37 +05:30
if ( ! charger )
2011-06-22 19:40:06 +09:00
return - ENOMEM ;
platform_set_drvdata ( pdev , charger ) ;
charger - > dev = & pdev - > dev ;
charger - > iodev = iodev ;
2015-03-12 08:44:11 +01:00
psy_cfg . drv_data = charger ;
2016-12-08 14:42:37 +05:30
charger - > battery = devm_power_supply_register ( & pdev - > dev ,
2015-03-12 08:44:11 +01:00
& max8997_battery_desc ,
& psy_cfg ) ;
if ( IS_ERR ( charger - > battery ) ) {
2011-06-22 19:40:06 +09:00
dev_err ( & pdev - > dev , " failed: power supply register \n " ) ;
2015-03-12 08:44:11 +01:00
return PTR_ERR ( charger - > battery ) ;
2011-06-22 19:40:06 +09:00
}
2021-03-29 14:38:02 +00:00
// grab regulator from parent device's node
pdev - > dev . of_node = iodev - > dev - > of_node ;
2020-12-30 20:52:15 +00:00
charger - > reg = devm_regulator_get_optional ( & pdev - > dev , " charger " ) ;
2021-03-29 14:38:02 +00:00
pdev - > dev . of_node = np ;
2020-12-30 20:52:15 +00:00
if ( IS_ERR ( charger - > reg ) ) {
if ( PTR_ERR ( charger - > reg ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
dev_info ( & pdev - > dev , " couldn't get charger regulator \n " ) ;
}
2021-03-29 14:38:02 +00:00
charger - > edev = extcon_get_extcon_dev ( " max8997-muic " ) ;
2021-12-17 09:28:46 +03:00
if ( IS_ERR ( charger - > edev ) ) {
dev_err_probe ( charger - > dev , PTR_ERR ( charger - > edev ) ,
" couldn't get extcon device: max8997-muic \n " ) ;
return PTR_ERR ( charger - > edev ) ;
2020-12-30 20:52:15 +00:00
}
2021-03-29 14:38:02 +00:00
if ( ! IS_ERR ( charger - > reg ) & & ! IS_ERR_OR_NULL ( charger - > edev ) ) {
2022-02-13 12:19:31 +01:00
ret = devm_work_autocancel ( & pdev - > dev , & charger - > extcon_work ,
max8997_battery_extcon_evt_worker ) ;
2020-12-30 20:52:15 +00:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add extcon evt stop action: %d \n " , ret ) ;
return ret ;
}
charger - > extcon_nb . notifier_call = max8997_battery_extcon_evt ;
ret = devm_extcon_register_notifier_all ( & pdev - > dev , charger - > edev ,
& charger - > extcon_nb ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register extcon notifier \n " ) ;
return ret ;
2021-02-24 15:15:10 +08:00
}
2020-12-30 20:52:15 +00:00
}
2011-06-22 19:40:06 +09:00
return 0 ;
}
static const struct platform_device_id max8997_battery_id [ ] = {
{ " max8997-battery " , 0 } ,
2011-08-01 07:23:00 +08:00
{ }
2011-06-22 19:40:06 +09:00
} ;
2016-10-17 16:13:35 -03:00
MODULE_DEVICE_TABLE ( platform , max8997_battery_id ) ;
2011-06-22 19:40:06 +09:00
static struct platform_driver max8997_battery_driver = {
. driver = {
. name = " max8997-battery " ,
} ,
. probe = max8997_battery_probe ,
. id_table = max8997_battery_id ,
} ;
2020-11-09 19:43:12 +00:00
module_platform_driver ( max8997_battery_driver ) ;
2011-06-22 19:40:06 +09:00
MODULE_DESCRIPTION ( " MAXIM 8997/8966 battery control driver " ) ;
MODULE_AUTHOR ( " MyungJoo Ham <myungjoo.ham@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;