mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 22:23:14 -04:00
power: qpnp-smbcharger-mmi: Add Basic Charge SM
Basic Charge State Machine to handle Temperature and Step Charging of a Single battery. Kernel DTS must be updated for the correct profile to be loaded. Change-Id: Ie86c01dfa97003f01504d62a85c3c5a52ff26001 Signed-off-by: Kenny Kessler <kenny.kessler@motorola.com> Reviewed-on: https://gerrit.mot.com/1254237 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Ryan Lattrel <ryanl@motorola.com> Reviewed-by: Kenneth Kessler <kennykessler@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
d7054a4e81
commit
70eec688af
1 changed files with 700 additions and 27 deletions
|
|
@ -9,6 +9,7 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/alarmtimer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
|
|
@ -33,6 +34,11 @@
|
|||
#define TYPEC_BASE 0X1500
|
||||
#define MISC_BASE 0x1600
|
||||
|
||||
#define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06)
|
||||
#define BATTERY_CHARGER_STATUS_MASK (GENMASK(2, 0))
|
||||
|
||||
#define USBIN_INT_RT_STS (USBIN_BASE + 0x10)
|
||||
#define USBIN_PLUGIN_RT_STS_BIT BIT(4)
|
||||
#define USBIN_CMD_ICL_OVERRIDE_REG (USBIN_BASE + 0x42)
|
||||
#define USBIN_ICL_OVERRIDE_BIT BIT(0)
|
||||
#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66)
|
||||
|
|
@ -59,6 +65,83 @@ enum {
|
|||
#define REV_BST_DROP 150
|
||||
#define REV_BST_MA -10
|
||||
#define BOOST_BACK_VOTER "BOOST_BACK_VOTER"
|
||||
#define MMI_HB_VOTER "MMI_HB_VOTER"
|
||||
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
|
||||
#define HYST_STEP_MV 50
|
||||
#define DEMO_MODE_HYS_SOC 5
|
||||
#define DEMO_MODE_VOLTAGE 4000
|
||||
#define WARM_TEMP 45
|
||||
#define COOL_TEMP 0
|
||||
|
||||
enum {
|
||||
TRICKLE_CHARGE = 0,
|
||||
PRE_CHARGE,
|
||||
FAST_CHARGE,
|
||||
FULLON_CHARGE,
|
||||
TAPER_CHARGE,
|
||||
TERMINATE_CHARGE,
|
||||
INHIBIT_CHARGE,
|
||||
DISABLE_CHARGE,
|
||||
};
|
||||
|
||||
enum {
|
||||
INHIBIT_CHARGE_V5 = 0,
|
||||
TRICKLE_CHARGE_V5,
|
||||
PRE_CHARGE_V5,
|
||||
FAST_CHARGE_V5,
|
||||
FULLON_CHARGE_V5,
|
||||
TAPER_CHARGE_V5,
|
||||
TERMINATE_CHARGE_V5,
|
||||
PAUSE_CHARGE_V5,
|
||||
DISABLE_CHARGE_V5,
|
||||
};
|
||||
|
||||
struct mmi_temp_zone {
|
||||
int temp_c;
|
||||
int norm_mv;
|
||||
int fcc_max_ma;
|
||||
int fcc_norm_ma;
|
||||
};
|
||||
|
||||
#define MAX_NUM_STEPS 10
|
||||
enum mmi_temp_zones {
|
||||
ZONE_FIRST = 0,
|
||||
/* states 0-9 are reserved for zones */
|
||||
ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1,
|
||||
ZONE_HOT,
|
||||
ZONE_COLD,
|
||||
ZONE_NONE = 0xFF,
|
||||
};
|
||||
|
||||
enum mmi_chrg_step {
|
||||
STEP_MAX,
|
||||
STEP_NORM,
|
||||
STEP_FULL,
|
||||
STEP_FLOAT,
|
||||
STEP_DEMO,
|
||||
STEP_STOP,
|
||||
STEP_NONE = 0xFF,
|
||||
};
|
||||
|
||||
static char *stepchg_str[] = {
|
||||
[STEP_MAX] = "MAX",
|
||||
[STEP_NORM] = "NORMAL",
|
||||
[STEP_FULL] = "FULL",
|
||||
[STEP_FLOAT] = "FLOAT",
|
||||
[STEP_DEMO] = "DEMO",
|
||||
[STEP_STOP] = "STOP",
|
||||
[STEP_NONE] = "NONE",
|
||||
};
|
||||
|
||||
struct smb_mmi_chg_status {
|
||||
int batt_mv;
|
||||
int batt_ma;
|
||||
int batt_soc;
|
||||
int batt_temp;
|
||||
int usb_mv;
|
||||
int charger_present;
|
||||
int vbus_present;
|
||||
};
|
||||
|
||||
struct smb_mmi_chg_param {
|
||||
const char *name;
|
||||
|
|
@ -116,6 +199,21 @@ struct smb_mmi_charger {
|
|||
int last_iusb_ua;
|
||||
bool factory_kill_armed;
|
||||
int gen_log_rate_s;
|
||||
|
||||
/* Charge Profile */
|
||||
int num_temp_zones;
|
||||
struct mmi_temp_zone *temp_zones;
|
||||
enum mmi_temp_zones pres_temp_zone;
|
||||
enum mmi_chrg_step pres_chrg_step;
|
||||
int chrg_taper_cnt;
|
||||
int batt_health;
|
||||
int base_fv_mv;
|
||||
int chrg_iterm;
|
||||
int vfloat_comp_mv;
|
||||
struct wakeup_source smb_mmi_hb_wake_source;
|
||||
struct alarm heartbeat_alarm;
|
||||
bool suspended;
|
||||
bool awake;
|
||||
};
|
||||
|
||||
#define CHGR_FAST_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x61)
|
||||
|
|
@ -1039,28 +1137,120 @@ static int smb_mmi_set_property(struct power_supply *psy,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define MIN_TEMP_C -20
|
||||
#define MAX_TEMP_C 60
|
||||
#define MIN_MAX_TEMP_C 47
|
||||
#define HYSTERISIS_DEGC 2
|
||||
static bool mmi_find_temp_zone(struct smb_mmi_charger *chip, int temp_c)
|
||||
{
|
||||
int prev_zone, num_zones;
|
||||
struct mmi_temp_zone *zones;
|
||||
int hotter_t, hotter_fcc;
|
||||
int colder_t, colder_fcc;
|
||||
int i;
|
||||
int max_temp;
|
||||
|
||||
if (!chip) {
|
||||
pr_debug("called before chip valid!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
zones = chip->temp_zones;
|
||||
num_zones = chip->num_temp_zones;
|
||||
prev_zone = chip->pres_temp_zone;
|
||||
|
||||
if (chip->max_chrg_temp >= MIN_MAX_TEMP_C)
|
||||
max_temp = chip->max_chrg_temp;
|
||||
else
|
||||
max_temp = zones[num_zones - 1].temp_c;
|
||||
|
||||
if (prev_zone == ZONE_NONE) {
|
||||
for (i = num_zones - 1; i >= 0; i--) {
|
||||
if (temp_c >= zones[i].temp_c) {
|
||||
if (i == num_zones - 1)
|
||||
chip->pres_temp_zone = ZONE_HOT;
|
||||
else
|
||||
chip->pres_temp_zone = i + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
chip->pres_temp_zone = ZONE_COLD;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prev_zone == ZONE_COLD) {
|
||||
if (temp_c >= MIN_TEMP_C + HYSTERISIS_DEGC)
|
||||
chip->pres_temp_zone = ZONE_FIRST;
|
||||
} else if (prev_zone == ZONE_HOT) {
|
||||
if (temp_c <= max_temp - HYSTERISIS_DEGC)
|
||||
chip->pres_temp_zone = num_zones - 1;
|
||||
} else {
|
||||
if (prev_zone == ZONE_FIRST) {
|
||||
hotter_fcc = zones[prev_zone + 1].fcc_max_ma;
|
||||
colder_fcc = 0;
|
||||
hotter_t = zones[prev_zone].temp_c;
|
||||
colder_t = MIN_TEMP_C;
|
||||
} else if (prev_zone == num_zones - 1) {
|
||||
hotter_fcc = 0;
|
||||
colder_fcc = zones[prev_zone - 1].fcc_max_ma;
|
||||
hotter_t = zones[prev_zone].temp_c;
|
||||
colder_t = zones[prev_zone - 1].temp_c;
|
||||
} else {
|
||||
hotter_fcc = zones[prev_zone + 1].fcc_max_ma;
|
||||
colder_fcc = zones[prev_zone - 1].fcc_max_ma;
|
||||
hotter_t = zones[prev_zone].temp_c;
|
||||
colder_t = zones[prev_zone - 1].temp_c;
|
||||
}
|
||||
|
||||
if (zones[prev_zone].fcc_max_ma < hotter_fcc)
|
||||
hotter_t += HYSTERISIS_DEGC;
|
||||
|
||||
if (zones[prev_zone].fcc_max_ma < colder_fcc)
|
||||
colder_t -= HYSTERISIS_DEGC;
|
||||
|
||||
if (temp_c < MIN_TEMP_C)
|
||||
chip->pres_temp_zone = ZONE_COLD;
|
||||
else if (temp_c >= max_temp)
|
||||
chip->pres_temp_zone = ZONE_HOT;
|
||||
else if (temp_c >= hotter_t)
|
||||
chip->pres_temp_zone++;
|
||||
else if (temp_c < colder_t)
|
||||
chip->pres_temp_zone--;
|
||||
}
|
||||
|
||||
if (prev_zone != chip->pres_temp_zone) {
|
||||
pr_debug("Entered Temp Zone %d!\n",
|
||||
chip->pres_temp_zone);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int get_prop_batt_voltage_now(struct smb_mmi_charger *chg,
|
||||
struct power_supply *psy,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!chg->bms_psy)
|
||||
if (!psy)
|
||||
return -EINVAL;
|
||||
|
||||
rc = power_supply_get_property(chg->bms_psy,
|
||||
rc = power_supply_get_property(psy,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW, val);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_prop_batt_current_now(struct smb_mmi_charger *chg,
|
||||
struct power_supply *psy,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!chg->bms_psy)
|
||||
if (!psy)
|
||||
return -EINVAL;
|
||||
|
||||
rc = power_supply_get_property(chg->bms_psy,
|
||||
rc = power_supply_get_property(psy,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW, val);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -1078,9 +1268,360 @@ static int get_prop_usb_voltage_now(struct smb_mmi_charger *chg,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int get_prop_batt_temp(struct smb_mmi_charger *chg,
|
||||
struct power_supply *psy,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!psy)
|
||||
return -EINVAL;
|
||||
|
||||
rc = power_supply_get_property(psy,
|
||||
POWER_SUPPLY_PROP_TEMP, val);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_prop_batt_capacity(struct smb_mmi_charger *chg,
|
||||
struct power_supply *psy,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (psy)
|
||||
rc = power_supply_get_property(psy,
|
||||
POWER_SUPPLY_PROP_CAPACITY, val);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_prop_usb_present(struct smb_mmi_charger *chg,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc;
|
||||
u8 stat;
|
||||
|
||||
rc = smblib_read_mmi(chg, USBIN_INT_RT_STS, &stat);
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read USBIN_RT_STS rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_prop_charger_present(struct smb_mmi_charger *chg,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
|
||||
val->intval = 0;
|
||||
|
||||
if (chg->usb_psy)
|
||||
rc = power_supply_get_property(chg->usb_psy,
|
||||
POWER_SUPPLY_PROP_TYPEC_MODE, val);
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read TypeC Mode rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
|
||||
case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
|
||||
case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
|
||||
val->intval = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define TAPER_COUNT 2
|
||||
#define TAPER_DROP_MA 100
|
||||
static bool mmi_has_current_tapered(struct smb_mmi_charger *chip,
|
||||
int batt_ma, int taper_ma)
|
||||
{
|
||||
bool change_state = false;
|
||||
int allowed_fcc, target_ma;
|
||||
|
||||
if (!chip) {
|
||||
pr_debug("called before chip valid!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
allowed_fcc = get_effective_result(chip->fcc_votable) / 1000;
|
||||
|
||||
if (allowed_fcc >= taper_ma)
|
||||
target_ma = taper_ma;
|
||||
else
|
||||
target_ma = allowed_fcc - TAPER_DROP_MA;
|
||||
|
||||
if (batt_ma < 0) {
|
||||
batt_ma *= -1;
|
||||
if (batt_ma <= target_ma)
|
||||
if (chip->chrg_taper_cnt >= TAPER_COUNT) {
|
||||
change_state = true;
|
||||
chip->chrg_taper_cnt = 0;
|
||||
} else
|
||||
chip->chrg_taper_cnt++;
|
||||
else
|
||||
chip->chrg_taper_cnt = 0;
|
||||
} else {
|
||||
if (chip->chrg_taper_cnt >= TAPER_COUNT) {
|
||||
change_state = true;
|
||||
chip->chrg_taper_cnt = 0;
|
||||
} else
|
||||
chip->chrg_taper_cnt++;
|
||||
}
|
||||
|
||||
return change_state;
|
||||
}
|
||||
|
||||
bool mmi_charge_halted(struct smb_mmi_charger *chg)
|
||||
{
|
||||
u8 stat;
|
||||
int rc;
|
||||
bool flag = false;
|
||||
|
||||
rc = smblib_read_mmi(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
|
||||
rc);
|
||||
return flag;
|
||||
}
|
||||
stat = stat & BATTERY_CHARGER_STATUS_MASK;
|
||||
|
||||
if (chg->smb_version == PM8150B_SUBTYPE) {
|
||||
switch (stat) {
|
||||
case TERMINATE_CHARGE_V5:
|
||||
case INHIBIT_CHARGE_V5:
|
||||
flag = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (stat) {
|
||||
case TERMINATE_CHARGE:
|
||||
case INHIBIT_CHARGE:
|
||||
flag = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm,
|
||||
ktime_t now)
|
||||
{
|
||||
struct smb_mmi_charger *chip = container_of(alarm,
|
||||
struct smb_mmi_charger,
|
||||
heartbeat_alarm);
|
||||
|
||||
pr_err("SMBMMI: HB alarm fired\n");
|
||||
|
||||
__pm_stay_awake(&chip->smb_mmi_hb_wake_source);
|
||||
cancel_delayed_work(&chip->heartbeat_work);
|
||||
/* Delay by 500 ms to allow devices to resume. */
|
||||
schedule_delayed_work(&chip->heartbeat_work,
|
||||
msecs_to_jiffies(500));
|
||||
|
||||
return ALARMTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void mmi_basic_charge_sm(struct smb_mmi_charger *chip,
|
||||
struct smb_mmi_chg_status *stat)
|
||||
{
|
||||
int target_fcc;
|
||||
int target_fv;
|
||||
int max_fv_mv;
|
||||
struct mmi_temp_zone *zone;
|
||||
|
||||
pr_info("SMBMMI: batt_mv = %d, batt_ma %d, batt_soc %d,"
|
||||
" batt_temp %d, usb_mv %d, cp %d, vp %d\n",
|
||||
stat->batt_mv,
|
||||
stat->batt_ma,
|
||||
stat->batt_soc,
|
||||
stat->batt_temp,
|
||||
stat->usb_mv,
|
||||
stat->charger_present,
|
||||
stat->vbus_present);
|
||||
|
||||
if (!chip->temp_zones) {
|
||||
pr_debug("SMBMMI: Skipping SM since No Temp Zone Defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (chip->base_fv_mv == 0) {
|
||||
chip->base_fv_mv = get_effective_result(chip->fv_votable);
|
||||
chip->base_fv_mv /= 1000;
|
||||
vote(chip->fv_votable,
|
||||
BATT_PROFILE_VOTER, false, 0);
|
||||
}
|
||||
max_fv_mv = chip->base_fv_mv;
|
||||
|
||||
mmi_find_temp_zone(chip, stat->batt_temp);
|
||||
zone = &chip->temp_zones[chip->pres_temp_zone];
|
||||
|
||||
if (!stat->charger_present) {
|
||||
chip->pres_chrg_step = STEP_NONE;
|
||||
} else if ((chip->pres_temp_zone == ZONE_HOT) ||
|
||||
(chip->pres_temp_zone == ZONE_COLD)) {
|
||||
chip->pres_chrg_step = STEP_STOP;
|
||||
} else if (chip->demo_mode) {
|
||||
/* TODO: Ignore Demo Mode for now */
|
||||
} else if ((chip->pres_chrg_step == STEP_NONE) ||
|
||||
(chip->pres_chrg_step == STEP_STOP)) {
|
||||
if (zone->norm_mv && (stat->batt_mv >= zone->norm_mv)) {
|
||||
if (zone->fcc_norm_ma)
|
||||
chip->pres_chrg_step = STEP_NORM;
|
||||
else
|
||||
chip->pres_chrg_step = STEP_STOP;
|
||||
} else
|
||||
chip->pres_chrg_step = STEP_MAX;
|
||||
} else if (chip->pres_chrg_step == STEP_MAX) {
|
||||
if (!zone->norm_mv) {
|
||||
/* No Step in this Zone */
|
||||
chip->chrg_taper_cnt = 0;
|
||||
if ((stat->batt_mv + HYST_STEP_MV) >= max_fv_mv)
|
||||
chip->pres_chrg_step = STEP_NORM;
|
||||
else
|
||||
chip->pres_chrg_step = STEP_MAX;
|
||||
} else if ((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv) {
|
||||
chip->chrg_taper_cnt = 0;
|
||||
chip->pres_chrg_step = STEP_MAX;
|
||||
} else if (!zone->fcc_norm_ma)
|
||||
chip->pres_chrg_step = STEP_FLOAT;
|
||||
else if (mmi_has_current_tapered(chip, stat->batt_ma,
|
||||
zone->fcc_norm_ma)) {
|
||||
chip->chrg_taper_cnt = 0;
|
||||
|
||||
if (mmi_charge_halted(chip)) {
|
||||
vote(chip->chg_dis_votable,
|
||||
MMI_HB_VOTER, true, 0);
|
||||
pr_err("SMBMMI: Charge Halt..Toggle\n");
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
chip->pres_chrg_step = STEP_NORM;
|
||||
}
|
||||
} else if (chip->pres_chrg_step == STEP_NORM) {
|
||||
if (!zone->fcc_norm_ma)
|
||||
chip->pres_chrg_step = STEP_STOP;
|
||||
else if ((stat->batt_soc < 100) ||
|
||||
(stat->batt_mv + HYST_STEP_MV) < max_fv_mv) {
|
||||
chip->chrg_taper_cnt = 0;
|
||||
chip->pres_chrg_step = STEP_NORM;
|
||||
} else if (mmi_has_current_tapered(chip, stat->batt_ma,
|
||||
chip->chrg_iterm)) {
|
||||
chip->pres_chrg_step = STEP_FULL;
|
||||
}
|
||||
} else if (chip->pres_chrg_step == STEP_FULL) {
|
||||
if (stat->batt_soc <= 99) {
|
||||
chip->chrg_taper_cnt = 0;
|
||||
chip->pres_chrg_step = STEP_NORM;
|
||||
}
|
||||
} else if (chip->pres_chrg_step == STEP_FLOAT) {
|
||||
if ((zone->fcc_norm_ma) ||
|
||||
((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv))
|
||||
chip->pres_chrg_step = STEP_MAX;
|
||||
}
|
||||
|
||||
/* Take State actions */
|
||||
switch (chip->pres_chrg_step) {
|
||||
case STEP_FLOAT:
|
||||
case STEP_MAX:
|
||||
if (!zone->norm_mv)
|
||||
target_fv = max_fv_mv;
|
||||
else
|
||||
target_fv = zone->norm_mv;
|
||||
target_fcc = zone->fcc_max_ma;
|
||||
break;
|
||||
case STEP_FULL:
|
||||
target_fv = max_fv_mv;
|
||||
target_fcc = -EINVAL;
|
||||
break;
|
||||
case STEP_NORM:
|
||||
target_fv = max_fv_mv + chip->vfloat_comp_mv;
|
||||
target_fcc = zone->fcc_norm_ma;
|
||||
break;
|
||||
case STEP_NONE:
|
||||
target_fv = max_fv_mv;
|
||||
target_fcc = zone->fcc_norm_ma;
|
||||
break;
|
||||
case STEP_STOP:
|
||||
target_fv = max_fv_mv;
|
||||
target_fcc = -EINVAL;
|
||||
break;
|
||||
case STEP_DEMO:
|
||||
target_fv = DEMO_MODE_VOLTAGE;
|
||||
target_fcc = zone->fcc_max_ma;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Votes for State */
|
||||
vote(chip->fv_votable, MMI_HB_VOTER, true, target_fv * 1000);
|
||||
|
||||
vote(chip->chg_dis_votable, MMI_HB_VOTER,
|
||||
(target_fcc < 0), 0);
|
||||
|
||||
vote(chip->fcc_votable, MMI_HB_VOTER,
|
||||
true, (target_fcc >= 0) ? (target_fcc * 1000) : 0);
|
||||
|
||||
if (chip->pres_temp_zone == ZONE_HOT) {
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
} else if (chip->pres_temp_zone == ZONE_COLD) {
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_COLD;
|
||||
} else if (stat->batt_temp >= WARM_TEMP) {
|
||||
if (chip->pres_chrg_step == STEP_STOP)
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
} else if (stat->batt_temp <= COOL_TEMP) {
|
||||
if (chip->pres_chrg_step == STEP_STOP)
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_COLD;
|
||||
else
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
} else
|
||||
chip->batt_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
pr_info("SMBMMI: Step State = %s, Temp Zone %d, Health %d\n",
|
||||
stepchg_str[(int)chip->pres_chrg_step],
|
||||
chip->pres_temp_zone,
|
||||
chip->batt_health);
|
||||
pr_info("SMBMMI: IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n",
|
||||
get_effective_result(chip->fv_votable),
|
||||
get_effective_result(chip->chg_dis_votable),
|
||||
get_effective_result(chip->fcc_votable),
|
||||
get_effective_result(chip->usb_icl_votable));
|
||||
}
|
||||
|
||||
static void smb_mmi_awake_vote(struct smb_mmi_charger *chip, bool awake)
|
||||
{
|
||||
if (awake == chip->awake)
|
||||
return;
|
||||
|
||||
chip->awake = awake;
|
||||
if (awake)
|
||||
pm_stay_awake(chip->dev);
|
||||
else
|
||||
pm_relax(chip->dev);
|
||||
}
|
||||
|
||||
static int factory_kill_disable;
|
||||
module_param(factory_kill_disable, int, 0644);
|
||||
#define TWO_VOLT 2000000
|
||||
#define SMBCHG_HEARTBEAT_INTERVAL_NS 70000000000
|
||||
#define HEARTBEAT_DELAY_MS 60000
|
||||
#define HEARTBEAT_FACTORY_MS 1000
|
||||
static void mmi_heartbeat_work(struct work_struct *work)
|
||||
{
|
||||
struct smb_mmi_charger *chip = container_of(work,
|
||||
|
|
@ -1096,40 +1637,76 @@ static void mmi_heartbeat_work(struct work_struct *work)
|
|||
int flip_cap_full = 0;
|
||||
int report_cap;
|
||||
static int prev_vbus_mv = -1;
|
||||
int batt_mv;
|
||||
int batt_ma;
|
||||
int usb_mv;
|
||||
struct smb_mmi_chg_status chg_stat;
|
||||
|
||||
pr_err("SMBMMI: Heartbeat!\n");
|
||||
/* Have not been resumed so wait another 100 ms */
|
||||
if (chip->suspended) {
|
||||
pr_err("SMBMMI: HB running before Resume\n");
|
||||
schedule_delayed_work(&chip->heartbeat_work,
|
||||
msecs_to_jiffies(100));
|
||||
return;
|
||||
}
|
||||
|
||||
smb_mmi_awake_vote(chip, true);
|
||||
alarm_try_to_cancel(&chip->heartbeat_alarm);
|
||||
|
||||
pr_debug("SMBMMI: Heartbeat!\n");
|
||||
if (chip->factory_mode)
|
||||
hb_resch_time = 1000;
|
||||
hb_resch_time = HEARTBEAT_FACTORY_MS;
|
||||
else
|
||||
hb_resch_time = 60000;
|
||||
hb_resch_time = HEARTBEAT_DELAY_MS;
|
||||
|
||||
rc = get_prop_batt_voltage_now(chip, &pval);
|
||||
rc = get_prop_batt_voltage_now(chip, chip->bms_psy, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting Batt Voltage rc = %d\n", rc);
|
||||
goto sch_hb;
|
||||
} else
|
||||
batt_mv = pval.intval / 1000;
|
||||
chg_stat.batt_mv = pval.intval / 1000;
|
||||
|
||||
rc = get_prop_batt_current_now(chip, &pval);
|
||||
rc = get_prop_batt_current_now(chip, chip->bms_psy, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting Batt Current rc = %d\n", rc);
|
||||
goto sch_hb;
|
||||
} else
|
||||
batt_ma = pval.intval / 1000;
|
||||
chg_stat.batt_ma = pval.intval / 1000;
|
||||
|
||||
rc = get_prop_usb_voltage_now(chip, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting USB Voltage rc = %d\n", rc);
|
||||
goto sch_hb;
|
||||
} else
|
||||
usb_mv = pval.intval / 1000;
|
||||
chg_stat.usb_mv = pval.intval / 1000;
|
||||
|
||||
if (prev_vbus_mv == -1)
|
||||
prev_vbus_mv = usb_mv;
|
||||
prev_vbus_mv = chg_stat.usb_mv;
|
||||
|
||||
rc = get_prop_batt_capacity(chip, chip->bms_psy, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting Batt Capacity rc = %d\n", rc);
|
||||
return;
|
||||
} else
|
||||
chg_stat.batt_soc = pval.intval;
|
||||
|
||||
rc = get_prop_batt_temp(chip, chip->bms_psy, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting Batt Temperature rc = %d\n", rc);
|
||||
return;
|
||||
} else
|
||||
chg_stat.batt_temp = pval.intval / 10;
|
||||
|
||||
rc = get_prop_usb_present(chip, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting USB Present rc = %d\n", rc);
|
||||
return;
|
||||
} else
|
||||
chg_stat.vbus_present = pval.intval;
|
||||
|
||||
rc = get_prop_charger_present(chip, &pval);
|
||||
if (rc < 0) {
|
||||
pr_err("Error getting Charger Present rc = %d\n", rc);
|
||||
return;
|
||||
} else
|
||||
chg_stat.charger_present = pval.intval & chg_stat.vbus_present;
|
||||
|
||||
if (chip->max_main_psy && chip->max_flip_psy) {
|
||||
rc = power_supply_get_property(chip->max_main_psy,
|
||||
|
|
@ -1186,12 +1763,16 @@ static void mmi_heartbeat_work(struct work_struct *work)
|
|||
if (rc < 0)
|
||||
pr_err("SMBMMI: Couldn't set batt psy cap\n");
|
||||
}
|
||||
} else {
|
||||
/* Fall here for Basic Step and Thermal Charging */
|
||||
mmi_basic_charge_sm(chip, &chg_stat);
|
||||
}
|
||||
|
||||
if ((batt_mv >= usb_mv) && ((usb_mv*1000) > TWO_VOLT)) {
|
||||
if (((usb_mv < REV_BST_THRESH) &&
|
||||
((prev_vbus_mv - REV_BST_DROP) > usb_mv)) ||
|
||||
(batt_ma > REV_BST_MA)) {
|
||||
if ((chg_stat.batt_mv >= chg_stat.usb_mv) &&
|
||||
((chg_stat.usb_mv*1000) > TWO_VOLT)) {
|
||||
if (((chg_stat.usb_mv < REV_BST_THRESH) &&
|
||||
((prev_vbus_mv - REV_BST_DROP) > chg_stat.usb_mv)) ||
|
||||
(chg_stat.batt_ma > REV_BST_MA)) {
|
||||
pr_err("Reverse Boosted: Clear, USB Suspend\n");
|
||||
if (chip->factory_mode)
|
||||
smblib_set_usb_suspend(chip, true);
|
||||
|
|
@ -1206,10 +1787,10 @@ static void mmi_heartbeat_work(struct work_struct *work)
|
|||
false, 0);
|
||||
} else {
|
||||
pr_err("Reverse Boosted: USB %d mV PUSB %d mV\n",
|
||||
usb_mv, prev_vbus_mv);
|
||||
chg_stat.usb_mv, prev_vbus_mv);
|
||||
}
|
||||
}
|
||||
prev_vbus_mv = usb_mv;
|
||||
prev_vbus_mv = chg_stat.usb_mv;
|
||||
|
||||
if (chip->factory_mode) {
|
||||
rc = smblib_get_usb_suspend(chip, &usb_suspend);
|
||||
|
|
@ -1223,7 +1804,7 @@ static void mmi_heartbeat_work(struct work_struct *work)
|
|||
goto sch_hb;
|
||||
|
||||
if (pval.intval ||
|
||||
(usb_suspend && ((usb_mv*1000) > TWO_VOLT))) {
|
||||
(usb_suspend && ((chg_stat.usb_mv*1000) > TWO_VOLT))) {
|
||||
pr_debug("SMBMMI: Factory Kill Armed\n");
|
||||
chip->factory_kill_armed = true;
|
||||
} else if (chip->factory_kill_armed && !factory_kill_disable) {
|
||||
|
|
@ -1237,7 +1818,15 @@ sch_hb:
|
|||
schedule_delayed_work(&chip->heartbeat_work,
|
||||
msecs_to_jiffies(hb_resch_time));
|
||||
|
||||
alarm_start_relative(&chip->heartbeat_alarm,
|
||||
ns_to_ktime(SMBCHG_HEARTBEAT_INTERVAL_NS));
|
||||
|
||||
if (!chg_stat.charger_present)
|
||||
smb_mmi_awake_vote(chip, false);
|
||||
|
||||
__pm_relax(&chip->smb_mmi_hb_wake_source);
|
||||
}
|
||||
|
||||
static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
|
|
@ -1253,8 +1842,7 @@ static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val,
|
|||
if (val != PSY_EVENT_PROP_CHANGED)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (chip->factory_mode &&
|
||||
psy &&
|
||||
if (psy &&
|
||||
(strcmp(psy->desc->name, "usb") == 0)) {
|
||||
cancel_delayed_work(&chip->heartbeat_work);
|
||||
schedule_delayed_work(&chip->heartbeat_work,
|
||||
|
|
@ -1323,6 +1911,70 @@ static const struct power_supply_desc mmi_psy_desc = {
|
|||
.num_properties = ARRAY_SIZE(smb_mmi_battery_props),
|
||||
};
|
||||
|
||||
static int parse_mmi_dt(struct smb_mmi_charger *chg)
|
||||
{
|
||||
struct device_node *node = chg->dev->of_node;
|
||||
int rc = 0;
|
||||
int byte_len;
|
||||
int i;
|
||||
|
||||
if (!node) {
|
||||
pr_err("mmi dtree info. missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
if (of_find_property(node, "qcom,mmi-temp-zones", &byte_len)) {
|
||||
if ((byte_len / sizeof(u32)) % 4) {
|
||||
pr_err("DT error wrong mmi temp zones\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chg->temp_zones = (struct mmi_temp_zone *)
|
||||
devm_kzalloc(chg->dev, byte_len, GFP_KERNEL);
|
||||
|
||||
if (chg->temp_zones == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
chg->num_temp_zones =
|
||||
byte_len / sizeof(struct mmi_temp_zone);
|
||||
|
||||
rc = of_property_read_u32_array(node,
|
||||
"qcom,mmi-temp-zones",
|
||||
(u32 *)chg->temp_zones,
|
||||
byte_len / sizeof(u32));
|
||||
if (rc < 0) {
|
||||
pr_err("Couldn't read mmi temp zones rc = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_err("mmi temp zones: Num: %d\n", chg->num_temp_zones);
|
||||
for (i = 0; i < chg->num_temp_zones; i++) {
|
||||
pr_err("mmi temp zones: Zone %d, Temp %d C, " \
|
||||
"Step Volt %d mV, Full Rate %d mA, " \
|
||||
"Taper Rate %d mA\n", i,
|
||||
chg->temp_zones[i].temp_c,
|
||||
chg->temp_zones[i].norm_mv,
|
||||
chg->temp_zones[i].fcc_max_ma,
|
||||
chg->temp_zones[i].fcc_norm_ma);
|
||||
}
|
||||
chg->pres_temp_zone = ZONE_NONE;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,iterm-ma",
|
||||
&chg->chrg_iterm);
|
||||
if (rc)
|
||||
chg->chrg_iterm = 150;
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,vfloat-comp-uv",
|
||||
&chg->vfloat_comp_mv);
|
||||
if (rc)
|
||||
chg->vfloat_comp_mv = 0;
|
||||
chg->vfloat_comp_mv /= 1000;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smb_mmi_chg_config_init(struct smb_mmi_charger *chip)
|
||||
{
|
||||
struct pmic_revid_data *pmic_rev_id;
|
||||
|
|
@ -1387,6 +2039,9 @@ static int smb_mmi_probe(struct platform_device *pdev)
|
|||
chip->dev = &pdev->dev;
|
||||
psy_cfg.drv_data = chip;
|
||||
platform_set_drvdata(pdev, chip);
|
||||
chip->suspended = false;
|
||||
chip->awake = false;
|
||||
device_init_wakeup(chip->dev, true);
|
||||
|
||||
smb_mmi_chg_config_init(chip);
|
||||
|
||||
|
|
@ -1396,7 +2051,13 @@ static int smb_mmi_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
parse_mmi_dt(chip);
|
||||
|
||||
INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work);
|
||||
wakeup_source_init(&chip->smb_mmi_hb_wake_source,
|
||||
"smb_mmi_hb_wake");
|
||||
alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME,
|
||||
mmi_heartbeat_alarm_cb);
|
||||
|
||||
chip->mmi_psy = devm_power_supply_register(chip->dev, &mmi_psy_desc,
|
||||
&psy_cfg);
|
||||
|
|
@ -1610,22 +2271,34 @@ static void smb_mmi_shutdown(struct platform_device *pdev)
|
|||
if (chip->max_flip_psy)
|
||||
power_supply_put(chip->max_flip_psy);
|
||||
|
||||
wakeup_source_trash(&chip->smb_mmi_hb_wake_source);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int smb_mmi_suspend(struct device *device)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(device);
|
||||
struct smb_mmi_charger *chip = platform_get_drvdata(pdev);
|
||||
|
||||
chip->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_mmi_resume(struct device *device)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(device);
|
||||
struct smb_mmi_charger *chip = platform_get_drvdata(pdev);
|
||||
|
||||
chip->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define smb2_suspend NULL
|
||||
#define smb2_resume NULL
|
||||
#define smb_mmi_suspend NULL
|
||||
#define smb_mmi_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops smb_mmi_dev_pm_ops = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue