Merge "power: qcom: smb-lib: handle dynamic Rp change"
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 8c43c4e..08e1505 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -46,7 +46,7 @@
static char *type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP",
"USB_CDP", "USB_ACA", "USB_HVDCP", "USB_HVDCP_3", "USB_PD",
- "Wireless", "BMS", "Parallel", "Main", "Wipower",
+ "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "Wipower",
"TYPEC", "TYPEC_UFP", "TYPEC_DFP"
};
static char *status_text[] = {
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 7e6a4e8..cdd09dd 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -51,9 +51,12 @@
#define PROFILE_LOAD "fg_profile_load"
#define DELTA_SOC "fg_delta_soc"
-/* Delta BSOC votable reasons */
+/* Delta BSOC irq votable reasons */
#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq"
+/* Battery missing irq votable reasons */
+#define BATT_MISS_IRQ_VOTER "fg_batt_miss_irq"
+
#define DEBUG_PRINT_BUFFER_SIZE 64
/* 3 byte address + 1 space character */
#define ADDR_LEN 4
@@ -361,6 +364,7 @@
struct fg_irq_info *irqs;
struct votable *awake_votable;
struct votable *delta_bsoc_irq_en_votable;
+ struct votable *batt_miss_irq_en_votable;
struct fg_sram_param *sp;
struct fg_alg_flag *alg_flags;
int *debug_mask;
@@ -467,6 +471,7 @@
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
extern bool is_input_present(struct fg_chip *chip);
+extern bool is_qnovo_en(struct fg_chip *chip);
extern void fg_circ_buf_add(struct fg_circ_buf *buf, int val);
extern void fg_circ_buf_clr(struct fg_circ_buf *buf);
extern int fg_circ_buf_avg(struct fg_circ_buf *buf, int *avg);
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index 839a771..38d9594 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -106,14 +106,17 @@
static bool is_usb_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->usb_psy)
chip->usb_psy = power_supply_get_by_name("usb");
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->usb_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -122,14 +125,17 @@
static bool is_dc_present(struct fg_chip *chip)
{
union power_supply_propval pval = {0, };
+ int rc;
if (!chip->dc_psy)
chip->dc_psy = power_supply_get_by_name("dc");
- if (chip->dc_psy)
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- else
+ if (!chip->dc_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0)
return false;
return pval.intval != 0;
@@ -140,6 +146,25 @@
return is_usb_present(chip) || is_dc_present(chip);
}
+bool is_qnovo_en(struct fg_chip *chip)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &pval);
+ if (rc < 0)
+ return false;
+
+ return pval.intval != 0;
+}
+
#define EXPONENT_SHIFT 11
#define EXPONENT_OFFSET -9
#define MANTISSA_SIGN_BIT 10
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 75e79bb..e5a3a07 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -904,6 +904,7 @@
return ret;
}
+ vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, true, 0);
return rc;
}
@@ -1103,6 +1104,25 @@
fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
}
+static int fg_batt_miss_irq_en_cb(struct votable *votable, void *data,
+ int enable, const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (!chip->irqs[BATT_MISSING_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+ enable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
+ } else {
+ disable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
+ disable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+ }
+
+ return 0;
+}
+
static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data,
int enable, const char *client)
{
@@ -1402,6 +1422,7 @@
static void fg_cap_learning_update(struct fg_chip *chip)
{
int rc, batt_soc, batt_soc_msb;
+ bool input_present = is_input_present(chip);
mutex_lock(&chip->cl.lock);
@@ -1442,11 +1463,29 @@
chip->cl.init_cc_uah = 0;
}
+ if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (!input_present) {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
+ }
+
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
- fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc_msb);
- chip->cl.active = false;
- chip->cl.init_cc_uah = 0;
+ if (is_qnovo_en(chip) && input_present) {
+ /*
+ * Don't abort the capacity learning when qnovo
+ * is enabled and input is present where the
+ * charging status can go to "not charging"
+ * intermittently.
+ */
+ } else {
+ fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ chip->cl.active = false;
+ chip->cl.init_cc_uah = 0;
+ }
}
}
@@ -1981,7 +2020,7 @@
{
union power_supply_propval prop = {0, };
int rc;
- bool parallel_en = false, qnovo_en = false;
+ bool parallel_en = false, qnovo_en;
if (is_parallel_charger_available(chip)) {
rc = power_supply_get_property(chip->parallel_psy,
@@ -1994,10 +2033,7 @@
parallel_en = prop.intval;
}
- rc = power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop);
- if (!rc)
- qnovo_en = prop.intval;
+ qnovo_en = is_qnovo_en(chip);
fg_dbg(chip, FG_POWER_SUPPLY, "chg_sts: %d par_en: %d qnov_en: %d esr_fcc_ctrl_en: %d\n",
chip->charge_status, parallel_en, qnovo_en,
@@ -2498,6 +2534,23 @@
int rc;
vote(chip->awake_votable, PROFILE_LOAD, true, 0);
+
+ rc = fg_get_batt_id(chip);
+ if (rc < 0) {
+ pr_err("Error in getting battery id, rc:%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_get_batt_profile(chip);
+ if (rc < 0) {
+ pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
+ chip->batt_id_ohms / 1000, rc);
+ goto out;
+ }
+
+ if (!chip->profile_available)
+ goto out;
+
if (!is_profile_load_required(chip))
goto done;
@@ -2562,9 +2615,9 @@
batt_psy_initialized(chip);
fg_notify_charger(chip);
chip->profile_loaded = true;
- chip->soc_reporting_ready = true;
fg_dbg(chip, FG_STATUS, "profile loaded successfully");
out:
+ chip->soc_reporting_ready = true;
vote(chip->awake_votable, PROFILE_LOAD, false, 0);
}
@@ -3550,20 +3603,6 @@
return IRQ_HANDLED;
}
- rc = fg_get_batt_id(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_err("Error in getting battery id, rc:%d\n", rc);
- return IRQ_HANDLED;
- }
-
- rc = fg_get_batt_profile(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_err("Error in getting battery profile, rc:%d\n", rc);
- return IRQ_HANDLED;
- }
-
clear_battery_profile(chip);
schedule_delayed_work(&chip->profile_load_work, 0);
@@ -4330,6 +4369,9 @@
if (chip->delta_bsoc_irq_en_votable)
destroy_votable(chip->delta_bsoc_irq_en_votable);
+ if (chip->batt_miss_irq_en_votable)
+ destroy_votable(chip->batt_miss_irq_en_votable);
+
if (chip->batt_id_chan)
iio_channel_release(chip->batt_id_chan);
@@ -4387,6 +4429,7 @@
chip);
if (IS_ERR(chip->awake_votable)) {
rc = PTR_ERR(chip->awake_votable);
+ chip->awake_votable = NULL;
goto exit;
}
@@ -4395,6 +4438,16 @@
fg_delta_bsoc_irq_en_cb, chip);
if (IS_ERR(chip->delta_bsoc_irq_en_votable)) {
rc = PTR_ERR(chip->delta_bsoc_irq_en_votable);
+ chip->delta_bsoc_irq_en_votable = NULL;
+ goto exit;
+ }
+
+ chip->batt_miss_irq_en_votable = create_votable("FG_BATT_MISS_IRQ",
+ VOTE_SET_ANY,
+ fg_batt_miss_irq_en_cb, chip);
+ if (IS_ERR(chip->batt_miss_irq_en_votable)) {
+ rc = PTR_ERR(chip->batt_miss_irq_en_votable);
+ chip->batt_miss_irq_en_votable = NULL;
goto exit;
}
@@ -4419,19 +4472,6 @@
INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
- rc = fg_get_batt_id(chip);
- if (rc < 0) {
- pr_err("Error in getting battery id, rc:%d\n", rc);
- goto exit;
- }
-
- rc = fg_get_batt_profile(chip);
- if (rc < 0) {
- chip->soc_reporting_ready = true;
- pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
- chip->batt_id_ohms / 1000, rc);
- }
-
rc = fg_memif_init(chip);
if (rc < 0) {
dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n",
@@ -4475,12 +4515,15 @@
goto exit;
}
- /* Keep SOC_UPDATE irq disabled until we require it */
+ /* Keep SOC_UPDATE_IRQ disabled until we require it */
if (fg_irqs[SOC_UPDATE_IRQ].irq)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
- /* Keep BSOC_DELTA_IRQ irq disabled until we require it */
- rerun_election(chip->delta_bsoc_irq_en_votable);
+ /* Keep BSOC_DELTA_IRQ disabled until we require it */
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
+
+ /* Keep BATT_MISSING_IRQ disabled until we require it */
+ vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);
rc = fg_debugfs_create(chip);
if (rc < 0) {
@@ -4505,8 +4548,7 @@
}
device_init_wakeup(chip->dev, true);
- if (chip->profile_available)
- schedule_delayed_work(&chip->profile_load_work, 0);
+ schedule_delayed_work(&chip->profile_load_work, 0);
pr_debug("FG GEN3 driver probed successfully\n");
return 0;
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index becce31..e94873c 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -266,6 +266,10 @@
debug_mask, __debug_mask, int, 0600
);
+static int __weak_chg_icl_ua = 500000;
+module_param_named(
+ weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600);
+
#define MICRO_1P5A 1500000
#define MICRO_P1A 100000
#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
@@ -461,6 +465,8 @@
val->intval = 0;
else
val->intval = 1;
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
+ val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
val->intval = chg->voltage_min_uv;
@@ -1466,15 +1472,6 @@
return rc;
}
- /* configure power role for dual-role */
- rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure power role for DRP rc=%d\n", rc);
- return rc;
- }
-
/*
* disable Type-C factory mode and stay in Attached.SRC state when VCONN
* over-current happens
@@ -1852,6 +1849,16 @@
static int smb2_post_init(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
+ int rc;
+
+ /* configure power role for dual-role */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure power role for DRP rc=%d\n", rc);
+ return rc;
+ }
rerun_election(chg->usb_irq_enable_votable);
@@ -2113,7 +2120,7 @@
[SWITCH_POWER_OK_IRQ] = {
.name = "switcher-power-ok",
.handler = smblib_handle_switcher_power_ok,
- .storm_data = {true, 1000, 3},
+ .storm_data = {true, 1000, 8},
},
};
@@ -2307,6 +2314,7 @@
chg->dev = &pdev->dev;
chg->param = v1_params;
chg->debug_mask = &__debug_mask;
+ chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
chg->mode = PARALLEL_MASTER;
chg->irq_info = smb2_irqs;
chg->name = "PMI";
@@ -2418,7 +2426,11 @@
goto cleanup;
}
- smb2_post_init(chip);
+ rc = smb2_post_init(chip);
+ if (rc < 0) {
+ pr_err("Failed in post init rc=%d\n", rc);
+ goto cleanup;
+ }
smb2_create_debugfs(chip);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 6ead522..4c67c80 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -260,7 +260,7 @@
[FLOAT] = {
.name = "FLOAT",
.bit = FLOAT_CHARGER_BIT,
- .pst = POWER_SUPPLY_TYPE_USB_DCP
+ .pst = POWER_SUPPLY_TYPE_USB_FLOAT
},
[HVDCP2] = {
.name = "HVDCP2",
@@ -629,8 +629,29 @@
static void smblib_uusb_removal(struct smb_charger *chg)
{
int rc;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
cancel_delayed_work_sync(&chg->pl_enable_work);
+
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
@@ -736,7 +757,7 @@
return 0;
}
-static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
+static int smblib_get_hw_pulse_cnt(struct smb_charger *chg, int *count)
{
int rc;
u8 val[2];
@@ -770,6 +791,24 @@
return 0;
}
+static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
+{
+ int rc;
+
+ /* Use software based pulse count if HW INOV is disabled */
+ if (get_effective_result(chg->hvdcp_hw_inov_dis_votable) > 0) {
+ *count = chg->pulse_cnt;
+ return 0;
+ }
+
+ /* Use h/w pulse count if autonomous mode is enabled */
+ rc = smblib_get_hw_pulse_cnt(chg, count);
+ if (rc < 0)
+ smblib_err(chg, "failed to read h/w pulse count rc=%d\n", rc);
+
+ return rc;
+}
+
#define USBIN_25MA 25000
#define USBIN_100MA 100000
#define USBIN_150MA 150000
@@ -1126,7 +1165,7 @@
* the pulse count register get zeroed when autonomous mode is
* disabled. Track that in variables before disabling
*/
- rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt);
+ rc = smblib_get_hw_pulse_cnt(chg, &chg->pulse_cnt);
if (rc < 0) {
pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n",
rc);
@@ -2309,7 +2348,6 @@
{
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
int rc, pulses;
- u8 stat;
val->intval = MICRO_5V;
if (apsd_result == NULL) {
@@ -2319,13 +2357,12 @@
switch (apsd_result->pst) {
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
- rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
+ rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
smblib_err(chg,
"Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
return 0;
}
- pulses = (stat & QC_PULSE_COUNT_MASK);
val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses;
break;
default:
@@ -2830,13 +2867,13 @@
* USB MAIN PSY SETTERS *
************************/
-#define SDP_CURRENT_MA 500000
-#define CDP_CURRENT_MA 1500000
-#define DCP_CURRENT_MA 1500000
-#define HVDCP_CURRENT_MA 3000000
-#define TYPEC_DEFAULT_CURRENT_MA 900000
-#define TYPEC_MEDIUM_CURRENT_MA 1500000
-#define TYPEC_HIGH_CURRENT_MA 3000000
+#define SDP_CURRENT_UA 500000
+#define CDP_CURRENT_UA 1500000
+#define DCP_CURRENT_UA 1500000
+#define HVDCP_CURRENT_UA 3000000
+#define TYPEC_DEFAULT_CURRENT_UA 900000
+#define TYPEC_MEDIUM_CURRENT_UA 1500000
+#define TYPEC_HIGH_CURRENT_UA 3000000
int smblib_get_charge_current(struct smb_charger *chg,
int *total_current_ua)
{
@@ -2870,19 +2907,19 @@
/* QC 2.0/3.0 adapter */
if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) {
- *total_current_ua = HVDCP_CURRENT_MA;
+ *total_current_ua = HVDCP_CURRENT_UA;
return 0;
}
if (non_compliant) {
switch (apsd_result->bit) {
case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_MA;
+ current_ua = CDP_CURRENT_UA;
break;
case DCP_CHARGER_BIT:
case OCP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
- current_ua = DCP_CURRENT_MA;
+ current_ua = DCP_CURRENT_UA;
break;
default:
current_ua = 0;
@@ -2897,7 +2934,7 @@
case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
switch (apsd_result->bit) {
case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_MA;
+ current_ua = CDP_CURRENT_UA;
break;
case DCP_CHARGER_BIT:
case OCP_CHARGER_BIT:
@@ -2910,10 +2947,10 @@
}
break;
case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- current_ua = TYPEC_MEDIUM_CURRENT_MA;
+ current_ua = TYPEC_MEDIUM_CURRENT_UA;
break;
case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- current_ua = TYPEC_HIGH_CURRENT_MA;
+ current_ua = TYPEC_HIGH_CURRENT_UA;
break;
case POWER_SUPPLY_TYPEC_NON_COMPLIANT:
case POWER_SUPPLY_TYPEC_NONE:
@@ -3117,6 +3154,8 @@
int rc;
u8 stat;
bool vbus_rising;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
@@ -3126,10 +3165,23 @@
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- if (vbus_rising)
+ if (vbus_rising) {
smblib_cc2_sink_removal_exit(chg);
- else
+ } else {
smblib_cc2_sink_removal_enter(chg);
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata,
+ WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
+ false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
+ }
power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
@@ -3142,6 +3194,8 @@
int rc;
u8 stat;
bool vbus_rising;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
@@ -3178,8 +3232,18 @@
schedule_delayed_work(&chg->pl_enable_work,
msecs_to_jiffies(PL_DELAY_MS));
} else {
- if (chg->wa_flags & BOOST_BACK_WA)
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata,
+ WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
+ false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
@@ -3302,13 +3366,12 @@
}
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
- rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
+ rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
smblib_err(chg,
"Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
return;
}
- pulses = (stat & QC_PULSE_COUNT_MASK);
if (pulses < QC3_PULSES_FOR_6V)
smblib_set_opt_freq_buck(chg,
@@ -3409,8 +3472,29 @@
rising ? "rising" : "falling");
}
+static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode)
+{
+ int rp_ua;
+
+ switch (typec_mode) {
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ rp_ua = TYPEC_HIGH_CURRENT_UA;
+ break;
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ /* fall through */
+ default:
+ rp_ua = DCP_CURRENT_UA;
+ }
+
+ return rp_ua;
+}
+
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
+ int typec_mode;
+ int rp_ua;
+
/* while PD is active it should have complete ICL control */
if (chg->pd_active)
return;
@@ -3431,7 +3515,10 @@
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
break;
case POWER_SUPPLY_TYPE_USB_DCP:
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ case POWER_SUPPLY_TYPE_USB_FLOAT:
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
break;
case POWER_SUPPLY_TYPE_USB_HVDCP:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
@@ -3563,9 +3650,30 @@
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
int rc;
+ struct smb_irq_data *data;
+ struct storm_watch *wdata;
chg->cc2_detach_wa_active = false;
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
+ rc = regulator_disable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
+ if (data) {
+ wdata = &data->storm_data;
+ update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ false, 0);
+ }
+ }
+
/* reset APSD voters */
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
@@ -3695,12 +3803,40 @@
typec_sink_removal(chg);
}
+static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
+{
+ int rp_ua;
+ const struct apsd_result *apsd = smblib_get_apsd_result(chg);
+
+ if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP)
+ && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT))
+ return;
+
+ /*
+ * handle Rp change for DCP/FLOAT/OCP.
+ * Update the current only if the Rp is different from
+ * the last Rp value.
+ */
+ smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
+ chg->typec_mode, typec_mode);
+
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
+}
+
static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
{
+ int typec_mode;
+
if (chg->pr_swap_in_progress)
return;
- chg->typec_mode = smblib_get_prop_typec_mode(chg);
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (chg->typec_present && (typec_mode != chg->typec_mode))
+ smblib_handle_rp_change(chg, typec_mode);
+
+ chg->typec_mode = typec_mode;
+
if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
chg->typec_present = true;
smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
@@ -3786,10 +3922,23 @@
return IRQ_HANDLED;
}
+static void smblib_bb_removal_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ bb_removal_work.work);
+
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
+ vote(chg->awake_votable, BOOST_BACK_VOTER, false, 0);
+}
+
+#define BOOST_BACK_UNVOTE_DELAY_MS 750
+#define BOOST_BACK_STORM_COUNT 3
+#define WEAK_CHG_STORM_COUNT 8
irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
+ struct storm_watch *wdata = &irq_data->storm_data;
int rc, usb_icl;
u8 stat;
@@ -3811,8 +3960,32 @@
return IRQ_HANDLED;
if (is_storming(&irq_data->storm_data)) {
- smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n");
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
+ /* This could be a weak charger reduce ICL */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ WEAK_CHARGER_VOTER)) {
+ smblib_err(chg,
+ "Weak charger detected: voting %dmA ICL\n",
+ *chg->weak_chg_icl_ua / 1000);
+ vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
+ true, *chg->weak_chg_icl_ua);
+ /*
+ * reset storm data and set the storm threshold
+ * to 3 for reverse boost detection.
+ */
+ update_storm_count(wdata, BOOST_BACK_STORM_COUNT);
+ } else {
+ smblib_err(chg,
+ "Reverse boost detected: voting 0mA to suspend input\n");
+ vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
+ vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0);
+ /*
+ * Remove the boost-back vote after a delay, to avoid
+ * permanently suspending the input if the boost-back
+ * condition is unintentionally hit.
+ */
+ schedule_delayed_work(&chg->bb_removal_work,
+ msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
+ }
}
return IRQ_HANDLED;
@@ -4467,6 +4640,7 @@
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
+ INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);
chg->fake_capacity = -EINVAL;
chg->fake_input_current_limited = -EINVAL;
@@ -4522,6 +4696,7 @@
cancel_delayed_work_sync(&chg->pl_enable_work);
cancel_work_sync(&chg->legacy_detection_work);
cancel_delayed_work_sync(&chg->uusb_otg_work);
+ cancel_delayed_work_sync(&chg->bb_removal_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
qcom_batt_deinit();
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index f39f2c9..a2168f0 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -64,9 +64,12 @@
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
#define OTG_DELAY_VOTER "OTG_DELAY_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
+#define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
+#define BOOST_BACK_STORM_COUNT 3
+#define WEAK_CHG_STORM_COUNT 8
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -230,6 +233,7 @@
struct smb_chg_freq chg_freq;
int smb_version;
int otg_delay_ms;
+ int *weak_chg_icl_ua;
/* locks */
struct mutex lock;
@@ -292,6 +296,7 @@
struct delayed_work pl_enable_work;
struct work_struct legacy_detection_work;
struct delayed_work uusb_otg_work;
+ struct delayed_work bb_removal_work;
/* cached status */
int voltage_min_uv;
diff --git a/drivers/power/supply/qcom/storm-watch.c b/drivers/power/supply/qcom/storm-watch.c
index 5275079..21ac669 100644
--- a/drivers/power/supply/qcom/storm-watch.c
+++ b/drivers/power/supply/qcom/storm-watch.c
@@ -64,3 +64,13 @@
data->storm_count = 0;
mutex_unlock(&data->storm_lock);
}
+
+void update_storm_count(struct storm_watch *data, int max_count)
+{
+ if (!data)
+ return;
+
+ mutex_lock(&data->storm_lock);
+ data->max_storm_count = max_count;
+ mutex_unlock(&data->storm_lock);
+}
diff --git a/drivers/power/supply/qcom/storm-watch.h b/drivers/power/supply/qcom/storm-watch.h
index ff05c4a..5275d73 100644
--- a/drivers/power/supply/qcom/storm-watch.h
+++ b/drivers/power/supply/qcom/storm-watch.h
@@ -37,4 +37,5 @@
bool is_storming(struct storm_watch *data);
void reset_storm_count(struct storm_watch *data);
+void update_storm_count(struct storm_watch *data, int max_count);
#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 4381570..8e7a431 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -271,6 +271,7 @@
POWER_SUPPLY_TYPE_USB_HVDCP_3, /* Efficient High Voltage DCP */
POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery */
POWER_SUPPLY_TYPE_WIRELESS, /* Accessory Charger Adapters */
+ POWER_SUPPLY_TYPE_USB_FLOAT, /* Floating charger */
POWER_SUPPLY_TYPE_BMS, /* Battery Monitor System */
POWER_SUPPLY_TYPE_PARALLEL, /* Parallel Path */
POWER_SUPPLY_TYPE_MAIN, /* Main Path */