From c3e73c5a50ddd8a320b330d5751d0cab977246c2 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Wed, 15 Mar 2017 10:05:51 -0700 Subject: [PATCH 1/5] power: pmic-voter: add is_client_vote_enabled API A client vote can be enabled or disabled. Add an API which allows consumers to check the enable/disable status of a client vote. Change-Id: Ic4e9224c19e63fb88216da0cb775994e3e87c1f7 Signed-off-by: Nicholas Troast --- drivers/power/supply/qcom/pmic-voter.c | 32 ++++++++++++++++++++++++++ drivers/power/supply/qcom/pmic-voter.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index e1a92fb23912..c07e9f083204 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -187,6 +187,38 @@ void unlock_votable(struct votable *votable) mutex_unlock(&votable->vote_lock); } +/** + * is_client_vote_enabled() - + * is_client_vote_enabled_locked() - + * The unlocked and locked variants of getting whether a client's + vote is enabled. + * @votable: the votable object + * @client_str: client of interest + * + * Returns: + * True if the client's vote is enabled; false otherwise. + */ +bool is_client_vote_enabled_locked(struct votable *votable, + const char *client_str) +{ + int client_id = get_client_id(votable, client_str); + + if (client_id < 0) + return false; + + return votable->votes[client_id].enabled; +} + +bool is_client_vote_enabled(struct votable *votable, const char *client_str) +{ + bool enabled; + + lock_votable(votable); + enabled = is_client_vote_enabled_locked(votable, client_str); + unlock_votable(votable); + return enabled; +} + /** * get_client_vote() - * get_client_vote_locked() - diff --git a/drivers/power/supply/qcom/pmic-voter.h b/drivers/power/supply/qcom/pmic-voter.h index 031b9a010a42..f202bf704055 100644 --- a/drivers/power/supply/qcom/pmic-voter.h +++ b/drivers/power/supply/qcom/pmic-voter.h @@ -24,6 +24,9 @@ enum votable_type { NUM_VOTABLE_TYPES, }; +bool is_client_vote_enabled(struct votable *votable, const char *client_str); +bool is_client_vote_enabled_locked(struct votable *votable, + const char *client_str); int get_client_vote(struct votable *votable, const char *client_str); int get_client_vote_locked(struct votable *votable, const char *client_str); int get_effective_result(struct votable *votable); From e7f294565ffb1b9f4b1c4af03e672ed68610ad2d Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Tue, 14 Mar 2017 09:20:55 -0700 Subject: [PATCH 2/5] power: smb-lib: prevent unnecessary APSD re-runs When APSD_START_ON_CC_BIT is set both VBUS and CC must be attached before APSD runs. This eliminates all issues related to slow plugin. Unfortunately this means that if CC is re-asserted anytime after APSD finishes, then it will rerun again. Fix this by disabling APSD_START_ON_CC_BIT right after CC is asserted, and enable it after USB removal. Change-Id: I27d3727647635b78392b925f0881dc3a4ef41623 Signed-off-by: Nicholas Troast --- drivers/power/supply/qcom/smb-lib.c | 43 ++++++++++------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 4f126854728f..6300284727ee 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -1098,16 +1098,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, int rc; if (apsd_disable) { - /* Don't run APSD on CC debounce when APSD is disabled */ - rc = smblib_masked_write(chg, TYPE_C_CFG_REG, - APSD_START_ON_CC_BIT, - 0); - if (rc < 0) { - smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", - rc); - return rc; - } - rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, AUTO_SRC_DETECT_BIT, 0); @@ -1123,15 +1113,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, smblib_err(chg, "Couldn't enable APSD rc=%d\n", rc); return rc; } - - rc = smblib_masked_write(chg, TYPE_C_CFG_REG, - APSD_START_ON_CC_BIT, - APSD_START_ON_CC_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", - rc); - return rc; - } } return 0; @@ -2684,12 +2665,6 @@ int smblib_reg_block_restore(struct smb_charger *chg, } static struct reg_info cc2_detach_settings[] = { - { - .reg = TYPE_C_CFG_REG, - .mask = APSD_START_ON_CC_BIT, - .val = 0, - .desc = "TYPE_C_CFG_REG", - }, { .reg = TYPE_C_CFG_2_REG, .mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT, @@ -3568,6 +3543,8 @@ static void typec_sink_removal(struct smb_charger *chg) static void smblib_handle_typec_removal(struct smb_charger *chg) { + int rc; + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0); @@ -3589,11 +3566,15 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->otg_attempts = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; - chg->usb_ever_removed = true; - smblib_update_usb_type(chg); + /* enable APSD CC trigger for next insertion */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, + APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc); + smblib_update_usb_type(chg); typec_source_removal(chg); typec_sink_removal(chg); } @@ -3601,12 +3582,18 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) static void smblib_handle_typec_insertion(struct smb_charger *chg, bool sink_attached, bool legacy_cable) { - int rp; + int rp, rc; bool vbus_cc_short = false; bool valid_legacy_cable; vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); + /* disable APSD CC trigger since CC is attached */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", + rc); + if (sink_attached) { typec_source_removal(chg); typec_sink_insertion(chg); From 13bdd014a766538028e85a3cfe184b0d6a16d022 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Tue, 14 Mar 2017 09:50:28 -0700 Subject: [PATCH 3/5] power: smb-lib: let userspace rerun AICL for PD and PPS Currently AICL will be rerun whenever PD requests a voltage increase. While this works in most cases it can become problematic if PD requests the same voltage twice, and the ICL may fall to ICL_MIN. Since the voltage requests originate in the userspace it would be less error prone to allow the userspace to rerun AICL instead. Do it. Change-Id: Id190564e28bcffd72a1de70fa1327fce3e40299e Signed-off-by: Nicholas Troast --- drivers/power/supply/qcom/smb-lib.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 6300284727ee..0a8c9c308989 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -2491,10 +2491,6 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, } chg->voltage_max_uv = max_uv; - rc = smblib_rerun_aicl(chg); - if (rc < 0) - smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc); - return rc; } From fa12cb53c06784e285874d2c746d7fe3e2d758c8 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Wed, 15 Mar 2017 10:45:28 -0700 Subject: [PATCH 4/5] power: smb-lib: use updated ICL override bit Currently to override the ICL the self-clearing ICL override bit is used. The problem with this bit is that it is self-clearing and a separate register needs to be read to get the override status. Furthermore, the hardware will automatically clear this bit on USB removal. A new ICL override bit was added in PMI hardware revision 2.0. This bit is not self-clearing, and can be set prior to USB insertion. Use this new bit. Change-Id: I30a601b6aacba3c404ebdfb82e529504a694a048 Signed-off-by: Nicholas Troast --- drivers/power/supply/qcom/smb-lib.c | 37 +++++------------------------ drivers/power/supply/qcom/smb-reg.h | 1 + 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 0a8c9c308989..8cefc73e8741 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -161,39 +161,14 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) int smblib_icl_override(struct smb_charger *chg, bool override) { int rc; - bool override_status; - u8 stat; - u16 reg; - switch (chg->smb_version) { - case PMI8998_SUBTYPE: - reg = APSD_RESULT_STATUS_REG; - break; - case PM660_SUBTYPE: - reg = AICL_STATUS_REG; - break; - default: - smblib_dbg(chg, PR_MISC, "Unknown chip version=%x\n", - chg->smb_version); - return -EINVAL; - } + rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0); + if (rc < 0) + smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); - rc = smblib_read(chg, reg, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read reg=%x rc=%d\n", reg, rc); - return rc; - } - override_status = (bool)(stat & ICL_OVERRIDE_LATCH_BIT); - - if (override != override_status) { - rc = smblib_masked_write(chg, CMD_APSD_REG, - ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); - return rc; - } - } - return 0; + return rc; } /******************** diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 54b6b38d134b..f7c13390d477 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -628,6 +628,7 @@ enum { #define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) #define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) #define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) #define CFG_USB3P0_SEL_BIT BIT(2) From 11a54fcfa4688a7e823cc04fb4b991cab595aa04 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Tue, 14 Mar 2017 08:20:31 -0700 Subject: [PATCH 5/5] power: smb-lib: always assume legacy cable to prevent CC OV While in DRP the legacy cable detection may fail. Legacy cable detection is essential to preventing CC OV damage. Always assume a legacy cable since the legacy cable detection will fail in some cases. As a side effect, non-legacy HVDCP adapters will stay at 5V if they have a 10k ohm Rp. To realize this: - Remove disallowing PD based on the legacy bit being set. That bit set or unset is not reliable and it is safe to try PD. - Remove the workaround which tries to fix legacy cable being set incorrectly at boot. That bit set or unset is not reliable. Change-Id: I37879866592f63906a7c688f51c309b4e2fee48d Signed-off-by: Nicholas Troast --- drivers/power/supply/qcom/smb-lib.c | 49 +++++++---------------------- drivers/power/supply/qcom/smb-lib.h | 2 -- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 8cefc73e8741..5b9b350893b2 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -702,21 +702,6 @@ static void smblib_uusb_removal(struct smb_charger *chg) rc); } -static bool smblib_sysok_reason_usbin(struct smb_charger *chg) -{ - int rc; - u8 stat; - - rc = smblib_read(chg, SYSOK_REASON_STATUS_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't get SYSOK_REASON_STATUS rc=%d\n", rc); - /* assuming 'not usbin' in case of read failure */ - return false; - } - - return stat & SYSOK_REASON_USBIN_BIT; -} - void smblib_suspend_on_debug_battery(struct smb_charger *chg) { int rc; @@ -3518,8 +3503,6 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); - vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0); - vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); /* reset votes from vbus_cc_short */ @@ -3537,7 +3520,6 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->otg_attempts = 0; chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; - chg->usb_ever_removed = true; /* enable APSD CC trigger for next insertion */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, @@ -3554,8 +3536,6 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, bool sink_attached, bool legacy_cable) { int rp, rc; - bool vbus_cc_short = false; - bool valid_legacy_cable; vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); @@ -3573,25 +3553,18 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, typec_sink_removal(chg); } - valid_legacy_cable = legacy_cable && - (chg->usb_ever_removed || !smblib_sysok_reason_usbin(chg)); - vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, - valid_legacy_cable, 0); - - if (valid_legacy_cable) { - rp = smblib_get_prop_ufp_mode(chg); - if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH - || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { - vbus_cc_short = true; - smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n", - rp); - } + rp = smblib_get_prop_ufp_mode(chg); + if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH + || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { + smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n"); + /* HVDCP is not going to be enabled; enable parallel */ + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + true, 0); + } else { + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + false, 0); } - - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - vbus_cc_short, 0); - vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, - vbus_cc_short, 0); } static void smblib_handle_typec_debounce_done(struct smb_charger *chg, diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 22ef78fd9641..2b2106b81a81 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -47,7 +47,6 @@ enum print_reason { #define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER" #define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER" #define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER" -#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER" #define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER" #define BOOST_BACK_VOTER "BOOST_BACK_VOTER" #define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER" @@ -316,7 +315,6 @@ struct smb_charger { /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; - bool usb_ever_removed; int icl_reduction_ua;