// SPDX-License-Identifier: GPL-2.0
/*
* Chrager driver for Sgm4154x
*
* Copyright (c) 2022 Rockchip Electronics Co., Ltd.
*
* Author: Xu Shengfei <xsf@rock-chips.com>
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/types.h>
static int dbg_enable;
module_param_named(dbg_level, dbg_enable, int, 0644);
#define DBG(args...) \
do { \
if (dbg_enable) { \
pr_info(args); \
} \
} while (0)
#define SGM4154x_MANUFACTURER "SGMICRO"
#define SGM4154x_NAME "sgm41542"
#define SGM4154x_PN_ID (BIT(6) | BIT(5) | BIT(3))
/* define register */
#define SGM4154x_CHRG_CTRL_0 0x00
#define SGM4154x_CHRG_CTRL_1 0x01
#define SGM4154x_CHRG_CTRL_2 0x02
#define SGM4154x_CHRG_CTRL_3 0x03
#define SGM4154x_CHRG_CTRL_4 0x04
#define SGM4154x_CHRG_CTRL_5 0x05
#define SGM4154x_CHRG_CTRL_6 0x06
#define SGM4154x_CHRG_CTRL_7 0x07
#define SGM4154x_CHRG_STAT 0x08
#define SGM4154x_CHRG_FAULT 0x09
#define SGM4154x_CHRG_CTRL_a 0x0a
#define SGM4154x_CHRG_CTRL_b 0x0b
#define SGM4154x_CHRG_CTRL_c 0x0c
#define SGM4154x_CHRG_CTRL_d 0x0d
#define SGM4154x_INPUT_DET 0x0e
#define SGM4154x_CHRG_CTRL_f 0x0f
/* charge status flags */
#define SGM4154x_CHRG_EN BIT(4)
#define SGM4154x_HIZ_EN BIT(7)
#define SGM4154x_TERM_EN BIT(7)
#define SGM4154x_VAC_OVP_MASK GENMASK(7, 6)
#define SGM4154x_DPDM_ONGOING BIT(7)
#define SGM4154x_VBUS_GOOD BIT(7)
#define SGM4154x_BOOSTV GENMASK(5, 4)
#define SGM4154x_BOOST_LIM BIT(7)
#define SGM4154x_OTG_EN BIT(5)
/* Part ID */
#define SGM4154x_PN_MASK GENMASK(6, 3)
/* WDT TIMER SET */
#define SGM4154x_WDT_TIMER_MASK GENMASK(5, 4)
#define SGM4154x_WDT_TIMER_DISABLE 0
#define SGM4154x_WDT_TIMER_40S BIT(4)
#define SGM4154x_WDT_TIMER_80S BIT(5)
#define SGM4154x_WDT_TIMER_160S (BIT(4) | BIT(5))
#define SGM4154x_WDT_RST_MASK BIT(6)
/* SAFETY TIMER SET */
#define SGM4154x_SAFETY_TIMER_MASK GENMASK(3, 3)
#define SGM4154x_SAFETY_TIMER_DISABLE 0
#define SGM4154x_SAFETY_TIMER_EN BIT(3)
#define SGM4154x_SAFETY_TIMER_5H 0
#define SGM4154x_SAFETY_TIMER_10H BIT(2)
/* recharge voltage */
#define SGM4154x_VRECHARGE BIT(0)
#define SGM4154x_VRECHRG_STEP_mV 100
#define SGM4154x_VRECHRG_OFFSET_mV 100
/* charge status */
#define SGM4154x_VSYS_STAT BIT(0)
#define SGM4154x_THERM_STAT BIT(1)
#define SGM4154x_PG_STAT BIT(2)
#define SGM4154x_CHG_STAT_MASK GENMASK(4, 3)
#define SGM4154x_PRECHRG BIT(3)
#define SGM4154x_FAST_CHRG BIT(4)
#define SGM4154x_TERM_CHRG (BIT(3) | BIT(4))
/* charge type */
#define SGM4154x_VBUS_STAT_MASK GENMASK(7, 5)
#define SGM4154x_NOT_CHRGING 0
#define SGM4154x_USB_SDP BIT(5)
#define SGM4154x_USB_CDP BIT(6)
#define SGM4154x_USB_DCP (BIT(5) | BIT(6))
#define SGM4154x_UNKNOWN (BIT(7) | BIT(5))
#define SGM4154x_NON_STANDARD (BIT(7) | BIT(6))
#define SGM4154x_OTG_MODE (BIT(7) | BIT(6) | BIT(5))
/* TEMP Status */
#define SGM4154x_TEMP_MASK GENMASK(2, 0)
#define SGM4154x_TEMP_NORMAL BIT(0)
#define SGM4154x_TEMP_WARM BIT(1)
#define SGM4154x_TEMP_COOL (BIT(0) | BIT(1))
#define SGM4154x_TEMP_COLD (BIT(0) | BIT(3))
#define SGM4154x_TEMP_HOT (BIT(2) | BIT(3))
/* precharge current */
#define SGM4154x_PRECHRG_CUR_MASK GENMASK(7, 4)
#define SGM4154x_PRECHRG_CURRENT_STEP_uA 60000
#define SGM4154x_PRECHRG_I_MIN_uA 60000
#define SGM4154x_PRECHRG_I_MAX_uA 780000
#define SGM4154x_PRECHRG_I_DEF_uA 180000
/* termination current */
#define SGM4154x_TERMCHRG_CUR_MASK GENMASK(3, 0)
#define SGM4154x_TERMCHRG_CURRENT_STEP_uA 60000
#define SGM4154x_TERMCHRG_I_MIN_uA 60000
#define SGM4154x_TERMCHRG_I_MAX_uA 960000
#define SGM4154x_TERMCHRG_I_DEF_uA 180000
/* charge current */
#define SGM4154x_ICHRG_CUR_MASK GENMASK(5, 0)
#define SGM4154x_ICHRG_I_STEP_uA 60000
#define SGM4154x_ICHRG_I_MIN_uA 0
#define SGM4154x_ICHRG_I_MAX_uA 3780000
#define SGM4154x_ICHRG_I_DEF_uA 2040000
/* charge voltage */
#define SGM4154x_VREG_V_MASK GENMASK(7, 3)
#define SGM4154x_VREG_V_MAX_uV 4624000
#define SGM4154x_VREG_V_MIN_uV 3856000
#define SGM4154x_VREG_V_DEF_uV 4208000
#define SGM4154x_VREG_V_STEP_uV 32000
/* VREG Fine Tuning */
#define SGM4154x_VREG_FT_MASK GENMASK(7, 6)
#define SGM4154x_VREG_FT_UP_8mV BIT(6)
#define SGM4154x_VREG_FT_DN_8mV BIT(7)
#define SGM4154x_VREG_FT_DN_16mV (BIT(7) | BIT(6))
/* iindpm current */
#define SGM4154x_IINDPM_I_MASK GENMASK(4, 0)
#define SGM4154x_IINDPM_I_MIN_uA 100000
#define SGM4154x_IINDPM_I_MAX_uA 3800000
#define SGM4154x_IINDPM_STEP_uA 100000
#define SGM4154x_IINDPM_DEF_uA 2400000
/* vindpm voltage */
#define SGM4154x_VINDPM_V_MASK GENMASK(3, 0)
#define SGM4154x_VINDPM_V_MIN_uV 3900000
#define SGM4154x_VINDPM_V_MAX_uV 12000000
#define SGM4154x_VINDPM_STEP_uV 100000
#define SGM4154x_VINDPM_DEF_uV 4500000
#define SGM4154x_VINDPM_OS_MASK GENMASK(1, 0)
/* DP DM SEL */
#define SGM4154x_DP_VSEL_MASK GENMASK(4, 3)
#define SGM4154x_DM_VSEL_MASK GENMASK(2, 1)
/* PUMPX SET */
#define SGM4154x_EN_PUMPX BIT(7)
#define SGM4154x_PUMPX_UP BIT(6)
#define SGM4154x_PUMPX_DN BIT(5)
struct sgm4154x_init_data {
int ichg; /* charge current */
int ilim; /* input current */
int vreg; /* regulation voltage */
int iterm; /* termination current */
int iprechg; /* precharge current */
int vlim; /* minimum system voltage limit */
int max_ichg;
int max_vreg;
};
struct sgm4154x_state {
bool vsys_stat;
bool therm_stat;
bool online;
u8 chrg_stat;
u8 vbus_status;
bool chrg_en;
bool hiz_en;
bool term_en;
bool vbus_gd;
u8 chrg_type;
u8 health;
u8 chrg_fault;
u8 ntc_fault;
};
struct sgm4154x_device {
struct i2c_client *client;
struct device *dev;
struct power_supply *charger;
struct mutex lock;
struct mutex i2c_rw_lock;
struct regmap *regmap;
char model_name[I2C_NAME_SIZE];
int device_id;
struct sgm4154x_init_data init_data;
struct sgm4154x_state state;
u32 watchdog_timer;
struct regulator_dev *otg_rdev;
struct notifier_block pm_nb;
int input_current;
bool sgm4154x_suspend_flag;
};
/* SGM4154x REG06 BOOST_LIM[5:4], uV */
static const unsigned int BOOST_VOLT_LIMIT[] = {
4850000, 5000000, 5150000, 5300000
};
static const unsigned int BOOST_CURRENT_LIMIT[] = {
1200000, 2000000
};
enum SGM4154x_VINDPM_OS {
VINDPM_OS_3900mV,
VINDPM_OS_5900mV,
VINDPM_OS_7500mV,
VINDPM_OS_10500mV,
};
static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int uA)
{
int reg_val;
int ret;
if (uA < SGM4154x_TERMCHRG_I_MIN_uA)
uA = SGM4154x_TERMCHRG_I_MIN_uA;
else if (uA > SGM4154x_TERMCHRG_I_MAX_uA)
uA = SGM4154x_TERMCHRG_I_MAX_uA;
reg_val = (uA - SGM4154x_TERMCHRG_I_MIN_uA) / SGM4154x_TERMCHRG_CURRENT_STEP_uA;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_3,
SGM4154x_TERMCHRG_CUR_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set term current error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int uA)
{
int reg_val;
int ret;
if (uA < SGM4154x_PRECHRG_I_MIN_uA)
uA = SGM4154x_PRECHRG_I_MIN_uA;
else if (uA > SGM4154x_PRECHRG_I_MAX_uA)
uA = SGM4154x_PRECHRG_I_MAX_uA;
reg_val = (uA - SGM4154x_PRECHRG_I_MIN_uA) / SGM4154x_PRECHRG_CURRENT_STEP_uA;
reg_val = reg_val << 4;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_3,
SGM4154x_PRECHRG_CUR_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set precharge current error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int uA)
{
int reg_val;
int ret;
if (uA < SGM4154x_ICHRG_I_MIN_uA)
uA = SGM4154x_ICHRG_I_MIN_uA;
else if (uA > sgm->init_data.max_ichg)
uA = sgm->init_data.max_ichg;
reg_val = uA / SGM4154x_ICHRG_I_STEP_uA;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_2,
SGM4154x_ICHRG_CUR_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set icharge current error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt)
{
int reg_val;
int ret;
if (chrg_volt < SGM4154x_VREG_V_MIN_uV)
chrg_volt = SGM4154x_VREG_V_MIN_uV;
else if (chrg_volt > sgm->init_data.max_vreg)
chrg_volt = sgm->init_data.max_vreg;
reg_val = (chrg_volt - SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV;
reg_val = reg_val << 3;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_4,
SGM4154x_VREG_V_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set charge voltage error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_vindpm_offset_os(struct sgm4154x_device *sgm,
enum SGM4154x_VINDPM_OS offset_os)
{
int ret;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_f,
SGM4154x_VINDPM_OS_MASK,
offset_os);
if (ret) {
dev_err(sgm->dev, "set vindpm offset os error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm,
unsigned int vindpm)
{
enum SGM4154x_VINDPM_OS os_val;
unsigned int offset;
u8 reg_val;
int ret;
if (vindpm < SGM4154x_VINDPM_V_MIN_uV ||
vindpm > SGM4154x_VINDPM_V_MAX_uV)
return -EINVAL;
if (vindpm < 5900000) {
os_val = VINDPM_OS_3900mV;
offset = 3900000;
} else if (vindpm >= 5900000 && vindpm < 7500000) {
os_val = VINDPM_OS_5900mV;
offset = 5900000;
} else if (vindpm >= 7500000 && vindpm < 10500000) {
os_val = VINDPM_OS_7500mV;
offset = 7500000;
} else {
os_val = VINDPM_OS_10500mV;
offset = 10500000;
}
ret = sgm4154x_set_vindpm_offset_os(sgm, os_val);
if (ret) {
dev_err(sgm->dev, "set vin dpm error!\n");
return ret;
}
reg_val = (vindpm - offset) / SGM4154x_VINDPM_STEP_uV;
ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6,
SGM4154x_VINDPM_V_MASK, reg_val);
if (ret) {
dev_err(sgm->dev, "input voltage error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm)
{
int reg_val;
int ret;
if (iindpm > sgm->init_data.ilim)
iindpm = sgm->init_data.ilim;
sgm->input_current = iindpm;
if (iindpm < SGM4154x_IINDPM_I_MIN_uA ||
iindpm > SGM4154x_IINDPM_I_MAX_uA)
return -EINVAL;
if (iindpm >= SGM4154x_IINDPM_I_MIN_uA && iindpm <= 3100000)
reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA;
else if (iindpm > 3100000 && iindpm < SGM4154x_IINDPM_I_MAX_uA)
reg_val = 0x1E;
else
reg_val = 0x1F;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_0,
SGM4154x_IINDPM_I_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set input current limit error!\n");
return ret;
}
return ret;
}
static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm)
{
int ret;
int ilim;
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &ilim);
if (ret) {
dev_err(sgm->dev, "get input current limit error!\n");
return ret;
}
if (SGM4154x_IINDPM_I_MASK == (ilim & SGM4154x_IINDPM_I_MASK))
return SGM4154x_IINDPM_I_MAX_uA;
ilim = (ilim & SGM4154x_IINDPM_I_MASK) * SGM4154x_IINDPM_STEP_uA + SGM4154x_IINDPM_I_MIN_uA;
return ilim;
}
static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time)
{
u8 reg_val;
int ret;
if (time == 0)
reg_val = SGM4154x_WDT_TIMER_DISABLE;
else if (time == 40)
reg_val = SGM4154x_WDT_TIMER_40S;
else if (time == 80)
reg_val = SGM4154x_WDT_TIMER_80S;
else
reg_val = SGM4154x_WDT_TIMER_160S;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_5,
SGM4154x_WDT_TIMER_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set watchdog timer error!\n");
return ret;
}
return ret;
}
static int sgm4154x_enable_charger(struct sgm4154x_device *sgm)
{
int ret;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_1,
SGM4154x_CHRG_EN,
SGM4154x_CHRG_EN);
if (ret) {
dev_err(sgm->dev, "enable charger error!\n");
return ret;
}
return ret;
}
static int sgm4154x_disable_charger(struct sgm4154x_device *sgm)
{
int ret;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_1,
SGM4154x_CHRG_EN,
0);
if (ret) {
dev_err(sgm->dev, "disable charger error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm)
{
int reg_val;
int ret;
reg_val = 0xFF & SGM4154x_VAC_OVP_MASK;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_6,
SGM4154x_VAC_OVP_MASK,
reg_val);
if (ret) {
dev_err(sgm->dev, "set vac ovp error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt)
{
int reg_val;
int ret;
reg_val = (recharge_volt - SGM4154x_VRECHRG_OFFSET_mV) / SGM4154x_VRECHRG_STEP_mV;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_4,
SGM4154x_VRECHARGE,
reg_val);
if (ret) {
dev_err(sgm->dev, "set recharger error!\n");
return ret;
}
return ret;
}
static int sgm4154x_get_state(struct sgm4154x_device *sgm,
struct sgm4154x_state *state)
{
int chrg_param_0, chrg_param_1, chrg_param_2;
int chrg_stat;
int fault;
int ret;
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat);
if (ret) {
pr_err("read SGM4154x_CHRG_STAT fail\n");
return ret;
}
DBG("SGM4154x_CHRG_STAT[0x%x]: 0x%x\n", SGM4154x_CHRG_STAT, chrg_stat);
state->chrg_type = chrg_stat & SGM4154x_VBUS_STAT_MASK;
state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK;
state->online = !!(chrg_stat & SGM4154x_PG_STAT);
state->therm_stat = !!(chrg_stat & SGM4154x_THERM_STAT);
state->vsys_stat = !!(chrg_stat & SGM4154x_VSYS_STAT);
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_FAULT, &fault);
if (ret) {
pr_err("read SGM4154x_CHRG_FAULT fail\n");
return ret;
}
DBG("SGM4154x_CHRG_FAULT[0x%x]: 0x%x\n", SGM4154x_CHRG_FAULT, fault);
state->chrg_fault = fault;
state->ntc_fault = fault & SGM4154x_TEMP_MASK;
state->health = state->ntc_fault;
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &chrg_param_0);
if (ret) {
pr_err("read SGM4154x_CHRG_CTRL_0 fail\n");
return ret;
}
state->hiz_en = !!(chrg_param_0 & SGM4154x_HIZ_EN);
DBG("SGM4154x_CHRG_CTRL_0[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_0, chrg_param_0);
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_5, &chrg_param_1);
if (ret) {
pr_err("read SGM4154x_CHRG_CTRL_5 fail\n");
return ret;
}
state->term_en = !!(chrg_param_1 & SGM4154x_TERM_EN);
DBG("SGM4154x_CHRG_CTRL_5[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_5, chrg_param_1);
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_a, &chrg_param_2);
if (ret) {
pr_err("read SGM4154x_CHRG_CTRL_a fail\n");
return ret;
}
state->vbus_gd = !!(chrg_param_2 & SGM4154x_VBUS_GOOD);
DBG("SGM4154x_CHRG_CTRL_a[0x%x]: 0x%x\n", SGM4154x_CHRG_CTRL_a, chrg_param_2);
DBG("chrg_type: 0x%x\n", state->chrg_type);
DBG("chrg_stat: 0x%x\n", state->chrg_stat);
DBG("online: 0x%x\n", state->online);
DBG("therm_stat: 0x%x\n", state->therm_stat);
DBG("vsys_stat: 0x%x\n", state->vsys_stat);
DBG("chrg_fault: 0x%x\n", state->chrg_fault);
DBG("ntc_fault: 0x%x\n", state->ntc_fault);
DBG("health: 0x%x\n", state->health);
DBG("hiz_en: 0x%x\n", state->hiz_en);
DBG("term_en: 0x%x\n", state->term_en);
DBG("vbus_gd: 0x%x\n", state->vbus_gd);
return ret;
}
static int sgm4154x_property_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
switch (prop) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_ONLINE:
return true;
default:
return false;
}
}
static int sgm4154x_charger_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
int ret = -EINVAL;
switch (prop) {
case POWER_SUPPLY_PROP_ONLINE:
DBG("ONLINE: %d", val->intval);
if (val->intval)
ret = sgm4154x_enable_charger(sgm);
else
ret = sgm4154x_disable_charger(sgm);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
DBG("INPUT_CURRENT_LIMIT: %d\n", val->intval);
ret = sgm4154x_set_input_curr_lim(sgm, val->intval);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = sgm4154x_set_chrg_volt(sgm, val->intval);
break;
default:
return -EINVAL;
}
return ret;
}
static int sgm4154x_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
struct sgm4154x_state state;
int ret = 0;
mutex_lock(&sgm->lock);
ret = sgm4154x_get_state(sgm, &state);
if (ret) {
dev_err(sgm->dev, "get state error!\n");
mutex_unlock(&sgm->lock);
return ret;
}
sgm->state = state;
mutex_unlock(&sgm->lock);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (!state.chrg_type || (state.chrg_type == SGM4154x_OTG_MODE))
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (!state.chrg_stat)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
else if (state.chrg_stat == SGM4154x_TERM_CHRG)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_CHARGING;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
switch (state.chrg_stat) {
case SGM4154x_PRECHRG:
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case SGM4154x_FAST_CHRG:
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case SGM4154x_TERM_CHRG:
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case SGM4154x_NOT_CHRGING:
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
default:
val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = SGM4154x_MANUFACTURER;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = SGM4154x_NAME;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = state.online;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = state.vbus_gd;
break;
case POWER_SUPPLY_PROP_TYPE:
val->intval = POWER_SUPPLY_TYPE_USB;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
val->intval = sgm->init_data.max_vreg;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
val->intval = SGM4154x_ICHRG_I_MAX_uA;
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
val->intval = 12 * 1000 * 1000;
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
val->intval = sgm4154x_get_input_curr_lim(sgm);
if (val->intval < 0)
return -EINVAL;
break;
default:
return -EINVAL;
}
return ret;
}
static ssize_t registers_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sgm4154x_device *sgm4154x = dev_get_drvdata(dev);
u8 tmpbuf[30];
int idx = 0;
u8 addr;
int val;
int len;
int ret;
for (addr = 0x0; addr <= SGM4154x_CHRG_CTRL_f; addr++) {
ret = regmap_read(sgm4154x->regmap, addr, &val);
if (ret == 0) {
len = snprintf(tmpbuf, 30,
"Reg[%.2X] = 0x%.2x\n",
addr,
val);
memcpy(&buf[idx], tmpbuf, len);
idx += len;
}
}
return idx;
}
static ssize_t registers_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sgm4154x_device *sgm4154x = dev_get_drvdata(dev);
unsigned int reg;
int ret;
int val;
ret = sscanf(buf, "%x %x", ®, &val);
if (ret == 2 && reg <= SGM4154x_CHRG_CTRL_f)
regmap_write(sgm4154x->regmap, (unsigned char)reg, val);
return count;
}
static DEVICE_ATTR_RW(registers);
static void sgm4154x_create_device_node(struct device *dev)
{
device_create_file(dev, &dev_attr_registers);
}
static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private)
{
struct sgm4154x_device *sgm4154x = private;
struct sgm4154x_state state;
int ret;
ret = sgm4154x_get_state(sgm4154x, &state);
if (ret) {
dev_err(sgm4154x->dev, "get state error!\n");
return IRQ_NONE;
}
sgm4154x->state = state;
if (state.vbus_gd) {
ret = sgm4154x_set_input_curr_lim(sgm4154x, sgm4154x->input_current);
if (ret) {
dev_err(sgm4154x->dev, "set input current error!\n");
return IRQ_NONE;
}
}
power_supply_changed(sgm4154x->charger);
return IRQ_HANDLED;
}
static enum power_supply_property sgm4154x_power_supply_props[] = {
POWER_SUPPLY_PROP_TYPE,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_PRESENT
};
static char *sgm4154x_charger_supplied_to[] = {
"usb",
};
static struct power_supply_desc sgm4154x_power_supply_desc = {
.name = "sgm4154x-charger",
.type = POWER_SUPPLY_TYPE_USB,
.properties = sgm4154x_power_supply_props,
.num_properties = ARRAY_SIZE(sgm4154x_power_supply_props),
.get_property = sgm4154x_charger_get_property,
.set_property = sgm4154x_charger_set_property,
.property_is_writeable = sgm4154x_property_is_writeable,
};
static bool sgm4154x_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGM4154x_CHRG_CTRL_0 ... SGM4154x_CHRG_CTRL_f:
return true;
default:
return false;
}
}
static const struct regmap_config sgm4154x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SGM4154x_CHRG_CTRL_f,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = sgm4154x_is_volatile_reg,
};
static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm,
struct device *dev)
{
struct power_supply_config psy_cfg = { .drv_data = sgm,
.of_node = dev->of_node, };
psy_cfg.supplied_to = sgm4154x_charger_supplied_to;
psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to);
psy_cfg.of_node = dev->of_node;
sgm->charger = devm_power_supply_register(sgm->dev,
&sgm4154x_power_supply_desc,
&psy_cfg);
if (IS_ERR(sgm->charger))
return -EINVAL;
return 0;
}
static int sgm4154x_hw_init(struct sgm4154x_device *sgm)
{
struct power_supply_battery_info bat_info = { };
int ret = 0;
ret = power_supply_get_battery_info(sgm->charger, &bat_info);
if (ret) {
pr_info("sgm4154x: no battery information is supplied\n");
/*
* If no battery information is supplied, we should set
* default charge termination current to 120 mA, and default
* charge termination voltage to 4.35V.
*/
bat_info.constant_charge_current_max_ua =
SGM4154x_ICHRG_I_DEF_uA;
bat_info.constant_charge_voltage_max_uv =
SGM4154x_VREG_V_DEF_uV;
bat_info.precharge_current_ua =
SGM4154x_PRECHRG_I_DEF_uA;
bat_info.charge_term_current_ua =
SGM4154x_TERMCHRG_I_DEF_uA;
sgm->init_data.max_ichg =
SGM4154x_ICHRG_I_MAX_uA;
sgm->init_data.max_vreg = SGM4154x_VREG_V_DEF_uV;
}
if (!bat_info.constant_charge_current_max_ua)
bat_info.constant_charge_current_max_ua =
SGM4154x_ICHRG_I_MAX_uA;
if (!bat_info.constant_charge_voltage_max_uv)
bat_info.constant_charge_voltage_max_uv =
SGM4154x_VREG_V_DEF_uV;
if (!bat_info.precharge_current_ua)
bat_info.precharge_current_ua =
SGM4154x_PRECHRG_I_DEF_uA;
if (!bat_info.charge_term_current_ua)
bat_info.charge_term_current_ua =
SGM4154x_TERMCHRG_I_DEF_uA;
if (!sgm->init_data.max_ichg)
sgm->init_data.max_ichg =
SGM4154x_ICHRG_I_MAX_uA;
if (bat_info.constant_charge_voltage_max_uv)
sgm->init_data.max_vreg = bat_info.constant_charge_voltage_max_uv;
ret = sgm4154x_set_watchdog_timer(sgm, 0);
if (ret)
goto err_out;
ret = sgm4154x_set_ichrg_curr(sgm,
bat_info.constant_charge_current_max_ua);
if (ret)
goto err_out;
ret = sgm4154x_set_prechrg_curr(sgm, bat_info.precharge_current_ua);
if (ret)
goto err_out;
ret = sgm4154x_set_chrg_volt(sgm,
sgm->init_data.max_vreg);
if (ret)
goto err_out;
ret = sgm4154x_set_term_curr(sgm,
bat_info.charge_term_current_ua);
if (ret)
goto err_out;
ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim);
if (ret)
goto err_out;
ret = sgm4154x_set_input_curr_lim(sgm, 500 * 1000);
if (ret)
goto err_out;
ret = sgm4154x_set_vac_ovp(sgm);
if (ret)
goto err_out;
ret = sgm4154x_set_recharge_volt(sgm, 200);
if (ret)
goto err_out;
DBG("ichrg_curr:%d\n"
"prechrg_curr:%d\n"
"chrg_vol:%d\n"
"term_curr:%d\n"
"input_curr_lim:%d\n",
bat_info.constant_charge_current_max_ua,
bat_info.precharge_current_ua,
bat_info.constant_charge_voltage_max_uv,
bat_info.charge_term_current_ua,
sgm->init_data.ilim);
return 0;
err_out:
return ret;
}
static int sgm4154x_parse_dt(struct sgm4154x_device *sgm)
{
int ret;
ret = device_property_read_u32(sgm->dev,
"input-voltage-limit-microvolt",
&sgm->init_data.vlim);
if (ret)
sgm->init_data.vlim = SGM4154x_VINDPM_DEF_uV;
if (sgm->init_data.vlim > SGM4154x_VINDPM_V_MAX_uV ||
sgm->init_data.vlim < SGM4154x_VINDPM_V_MIN_uV)
return -EINVAL;
ret = device_property_read_u32(sgm->dev,
"input-current-limit-microamp",
&sgm->init_data.ilim);
if (ret)
sgm->init_data.ilim = SGM4154x_IINDPM_DEF_uA;
if (sgm->init_data.ilim > SGM4154x_IINDPM_I_MAX_uA ||
sgm->init_data.ilim < SGM4154x_IINDPM_I_MIN_uA)
return -EINVAL;
return 0;
}
static int sgm4154x_set_otg_voltage(struct sgm4154x_device *sgm, int uv)
{
int ret = 0;
int reg_val = -1;
int i = 0;
while (i < 4) {
if (uv == BOOST_VOLT_LIMIT[i]) {
reg_val = i;
break;
}
i++;
}
if (reg_val < 0)
return reg_val;
reg_val = reg_val << 4;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_6,
SGM4154x_BOOSTV,
reg_val);
if (ret) {
dev_err(sgm->dev, "set otg voltage error!\n");
return ret;
}
return ret;
}
static int sgm4154x_set_otg_current(struct sgm4154x_device *sgm, int ua)
{
int ret = 0;
if (ua == BOOST_CURRENT_LIMIT[0]) {
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_2,
SGM4154x_BOOST_LIM,
0);
if (ret) {
dev_err(sgm->dev, "set boost current limit error!\n");
return ret;
}
} else if (ua == BOOST_CURRENT_LIMIT[1]) {
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_2,
SGM4154x_BOOST_LIM,
BIT(7));
if (ret) {
dev_err(sgm->dev, "set boost current limit error!\n");
return ret;
}
}
return ret;
}
static int sgm4154x_enable_vbus(struct regulator_dev *rdev)
{
struct sgm4154x_device *sgm = rdev_get_drvdata(rdev);
int ret = 0;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_1,
SGM4154x_OTG_EN,
SGM4154x_OTG_EN);
if (ret) {
dev_err(sgm->dev, "set OTG enable error!\n");
return ret;
}
return ret;
}
static int sgm4154x_disable_vbus(struct regulator_dev *rdev)
{
struct sgm4154x_device *sgm = rdev_get_drvdata(rdev);
int ret = 0;
ret = regmap_update_bits(sgm->regmap,
SGM4154x_CHRG_CTRL_1,
SGM4154x_OTG_EN,
0);
if (ret) {
dev_err(sgm->dev, "set OTG disable error!\n");
return ret;
}
return ret;
}
static int sgm4154x_is_enabled_vbus(struct regulator_dev *rdev)
{
struct sgm4154x_device *sgm = rdev_get_drvdata(rdev);
int temp = 0;
int ret = 0;
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_1, &temp);
if (ret) {
dev_err(sgm->dev, "get vbus status error!\n");
return ret;
}
return (temp & SGM4154x_OTG_EN) ? 1 : 0;
}
static const struct regulator_ops sgm4154x_vbus_ops = {
.enable = sgm4154x_enable_vbus,
.disable = sgm4154x_disable_vbus,
.is_enabled = sgm4154x_is_enabled_vbus,
};
static struct regulator_desc sgm4154x_otg_rdesc = {
.of_match = "otg-vbus",
.name = "otg-vbus",
.regulators_node = of_match_ptr("regulators"),
.ops = &sgm4154x_vbus_ops,
.owner = THIS_MODULE,
.type = REGULATOR_VOLTAGE,
.fixed_uV = 5000000,
.n_voltages = 1,
};
static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm)
{
struct device_node *np;
struct regulator_config config = {};
int ret = 0;
np = of_get_child_by_name(sgm->dev->of_node, "regulators");
if (!np) {
dev_warn(sgm->dev, "cannot find regulators node\n");
return -ENXIO;
}
/* otg regulator */
config.dev = sgm->dev;
config.driver_data = sgm;
sgm->otg_rdev = devm_regulator_register(sgm->dev,
&sgm4154x_otg_rdesc,
&config);
if (IS_ERR(sgm->otg_rdev))
ret = PTR_ERR(sgm->otg_rdev);
return ret;
}
static int sgm4154x_suspend_notifier(struct notifier_block *nb,
unsigned long event,
void *dummy)
{
struct sgm4154x_device *sgm = container_of(nb, struct sgm4154x_device, pm_nb);
switch (event) {
case PM_SUSPEND_PREPARE:
sgm->sgm4154x_suspend_flag = 1;
return NOTIFY_OK;
case PM_POST_SUSPEND:
sgm->sgm4154x_suspend_flag = 0;
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm)
{
int ret = 0;
int val = 0;
ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_b, &val);
if (ret < 0)
return ret;
return val;
}
static int sgm4154x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct sgm4154x_device *sgm;
int ret;
sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL);
if (!sgm)
return -ENOMEM;
sgm->client = client;
sgm->dev = dev;
mutex_init(&sgm->lock);
strncpy(sgm->model_name, id->name, I2C_NAME_SIZE);
sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config);
if (IS_ERR(sgm->regmap)) {
dev_err(dev, "Failed to allocate register map\n");
return PTR_ERR(sgm->regmap);
}
i2c_set_clientdata(client, sgm);
ret = sgm4154x_parse_dt(sgm);
if (ret) {
dev_err(dev, "Failed to read device tree properties%d\n", ret);
return ret;
}
ret = sgm4154x_hw_chipid_detect(sgm);
if ((ret & SGM4154x_PN_MASK) != SGM4154x_PN_ID) {
pr_info("[%s] device not found !\n", __func__);
return ret;
}
device_init_wakeup(dev, 1);
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
sgm4154x_irq_handler_thread,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"sgm41542-irq", sgm);
if (ret)
goto error_out;
enable_irq_wake(client->irq);
}
sgm->pm_nb.notifier_call = sgm4154x_suspend_notifier;
register_pm_notifier(&sgm->pm_nb);
ret = sgm4154x_power_supply_init(sgm, dev);
if (ret) {
dev_err(dev, "Failed to register power supply\n");
goto error_out;
}
ret = sgm4154x_hw_init(sgm);
if (ret) {
dev_err(dev, "Cannot initialize the chip.\n");
goto error_out;
}
/* OTG setting 5V/1.2A */
ret = sgm4154x_set_otg_voltage(sgm, 5000000);
if (ret) {
dev_err(sgm->dev, "set OTG voltage error!\n");
return ret;
}
ret = sgm4154x_set_otg_current(sgm, 1200000);
if (ret) {
dev_err(sgm->dev, "set OTG current error!\n");
return ret;
}
sgm4154x_vbus_regulator_register(sgm);
sgm4154x_create_device_node(sgm->dev);
return ret;
error_out:
return ret;
}
static int sgm4154x_charger_remove(struct i2c_client *client)
{
struct sgm4154x_device *sgm = i2c_get_clientdata(client);
regulator_unregister(sgm->otg_rdev);
power_supply_unregister(sgm->charger);
mutex_destroy(&sgm->lock);
return 0;
}
static void sgm4154x_charger_shutdown(struct i2c_client *client)
{
struct sgm4154x_device *sgm = i2c_get_clientdata(client);
int ret = 0;
ret = sgm4154x_disable_charger(sgm);
if (ret)
pr_err("Failed to disable charger, ret = %d\n", ret);
}
static const struct i2c_device_id sgm4154x_i2c_ids[] = {
{ "sgm41542", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids);
static const struct of_device_id sgm4154x_of_match[] = {
{ .compatible = "sgm,sgm41542", },
{ },
};
MODULE_DEVICE_TABLE(of, sgm4154x_of_match);
static struct i2c_driver sgm4154x_driver = {
.driver = {
.name = "sgm4154x-charger",
.of_match_table = sgm4154x_of_match,
},
.probe = sgm4154x_probe,
.remove = sgm4154x_charger_remove,
.shutdown = sgm4154x_charger_shutdown,
.id_table = sgm4154x_i2c_ids,
};
module_i2c_driver(sgm4154x_driver);
MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
MODULE_DESCRIPTION("sgm4154x charger driver");
MODULE_LICENSE("GPL");