// 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)
|
#define SGM4154x_WDT_RST 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 3000000
|
#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
|
|
#define SGM4154x_VINDPM_INT_MASK BIT(1)
|
#define SGM4154x_VINDPM_INT_DIS BIT(1)
|
#define SGM4154x_IINDPM_INT_MASK BIT(0)
|
#define SGM4154x_IINDPM_INT_DIS BIT(0)
|
|
/* 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)
|
|
#define SGM4154x_BATFET_DIS_MSK BIT(5)
|
#define SGM4154x_BATFET_DIS_DIS BIT(5)
|
#define SGM4154x_BATFET_DLY_MSK BIT(3)
|
#define SGM4154x_BATFET_DLY_EN BIT(3)
|
|
#define DEFAULT_INPUT_CURRENT (500 * 1000)
|
|
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;
|
struct regulator_dev *otg_rdev;
|
struct notifier_block pm_nb;
|
int input_current;
|
bool sgm4154x_suspend_flag;
|
bool watchdog_enable;
|
struct workqueue_struct *sgm_monitor_wq;
|
struct delayed_work sgm_delay_work;
|
};
|
|
/* 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;
|
}
|
|
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;
|
}
|
|
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 ((sgm->init_data.max_ichg > 0) && (uA > sgm->init_data.max_ichg))
|
uA = sgm->init_data.max_ichg;
|
uA = min(uA, SGM4154x_ICHRG_I_MAX_uA);
|
}
|
|
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;
|
}
|
|
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 ((sgm->init_data.max_vreg > 0) && (chrg_volt > sgm->init_data.max_vreg))
|
chrg_volt = sgm->init_data.max_vreg;
|
chrg_volt = min(chrg_volt, SGM4154x_VREG_V_MAX_uV);
|
}
|
|
reg_val = (chrg_volt - SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV;
|
if (chrg_volt == 4200 * 1000)
|
reg_val = 0x0b;
|
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;
|
}
|
|
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;
|
}
|
|
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 < SGM4154x_IINDPM_I_MIN_uA)
|
return -EINVAL;
|
|
if ((iindpm > SGM4154x_IINDPM_I_MAX_uA) || (iindpm > sgm->init_data.ilim))
|
iindpm = min(SGM4154x_IINDPM_I_MAX_uA, sgm->init_data.ilim);
|
|
sgm->input_current = iindpm;
|
reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA;
|
|
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;
|
}
|
|
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_watchdog_timer_reset(struct sgm4154x_device *sgm)
|
{
|
int ret;
|
|
ret = regmap_update_bits(sgm->regmap,
|
SGM4154x_CHRG_CTRL_1,
|
SGM4154x_WDT_RST_MASK,
|
SGM4154x_WDT_RST);
|
|
if (ret)
|
dev_err(sgm->dev, "set watchdog timer error!\n");
|
|
return ret;
|
}
|
|
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;
|
}
|
|
if (time) {
|
DBG("sgm41542: enable watchdog\n");
|
if (!sgm->watchdog_enable)
|
queue_delayed_work(sgm->sgm_monitor_wq,
|
&sgm->sgm_delay_work,
|
msecs_to_jiffies(1000 * 5));
|
sgm->watchdog_enable = true;
|
} else {
|
DBG("sgm41542: disable watchdog\n");
|
sgm->watchdog_enable = false;
|
sgm4154x_watchdog_timer_reset(sgm);
|
}
|
|
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;
|
}
|
|
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;
|
}
|
|
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;
|
}
|
|
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;
|
}
|
|
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:
|
if (val->intval) {
|
ret = sgm4154x_enable_charger(sgm);
|
sgm4154x_set_watchdog_timer(sgm, SGM4154x_WDT_TIMER_40S);
|
} else {
|
sgm4154x_set_watchdog_timer(sgm, 0);
|
ret = sgm4154x_disable_charger(sgm);
|
}
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
ret = sgm4154x_set_input_curr_lim(sgm, val->intval);
|
break;
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
ret = sgm4154x_set_ichrg_curr(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_MAX:
|
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;
|
u8 addr;
|
int val;
|
|
for (addr = 0x0; addr <= SGM4154x_CHRG_CTRL_f; addr++) {
|
ret = regmap_read(sgm4154x->regmap, addr, &val);
|
if (ret)
|
dev_err(sgm4154x->dev, "read addr[0x%x] error!\n", addr);
|
DBG("[0x%x]: 0x%x\n", addr, val);
|
}
|
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) {
|
if (sgm4154x->input_current >= DEFAULT_INPUT_CURRENT) {
|
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_MAX,
|
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 chrg_stat, 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_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 = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat);
|
if (ret) {
|
pr_err("read SGM4154x_CHRG_STAT fail\n");
|
goto err_out;
|
}
|
|
if (!(chrg_stat & SGM4154x_PG_STAT)) {
|
ret = sgm4154x_set_input_curr_lim(sgm, DEFAULT_INPUT_CURRENT);
|
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_disable_charger(sgm);
|
if (ret)
|
goto err_out;
|
}
|
ret = sgm4154x_set_vac_ovp(sgm);
|
if (ret)
|
goto err_out;
|
|
regmap_update_bits(sgm->regmap,
|
SGM4154x_CHRG_CTRL_d,
|
0x01,
|
0x00);
|
|
regmap_update_bits(sgm->regmap,
|
SGM4154x_CHRG_CTRL_a,
|
SGM4154x_IINDPM_INT_MASK,
|
SGM4154x_IINDPM_INT_DIS);
|
|
regmap_update_bits(sgm->regmap,
|
SGM4154x_CHRG_CTRL_a,
|
SGM4154x_VINDPM_INT_MASK,
|
SGM4154x_VINDPM_INT_DIS);
|
|
|
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 void sgm_charger_work(struct work_struct *work)
|
{
|
struct sgm4154x_device *sgm =
|
container_of(work,
|
struct sgm4154x_device,
|
sgm_delay_work.work);
|
|
sgm4154x_watchdog_timer_reset(sgm);
|
if (sgm->watchdog_enable)
|
queue_delayed_work(sgm->sgm_monitor_wq,
|
&sgm->sgm_delay_work,
|
msecs_to_jiffies(1000 * 5));
|
}
|
|
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;
|
}
|
|
sgm->sgm_monitor_wq = alloc_ordered_workqueue("%s",
|
WQ_MEM_RECLAIM | WQ_FREEZABLE, "sgm-monitor-wq");
|
INIT_DELAYED_WORK(&sgm->sgm_delay_work, sgm_charger_work);
|
|
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);
|
|
destroy_workqueue(sgm->sgm_monitor_wq);
|
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;
|
|
sgm4154x_set_prechrg_curr(sgm, SGM4154x_PRECHRG_I_DEF_uA);
|
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");
|