From 3abd7de15eef5717b3af260af42c360abf6398e4 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Wed, 26 Jul 2017 20:26:20 +0530 Subject: [PATCH] power: smb-lib: Update the FLOAT detection logic An SDP may get detected as a FLOAT charger by PMIC APSD. To handle this case do the following steps when a FLOAT charger is detected - 1. Limit the ICL to 100mA and start USB enumeration 2. If enumeration succeeds, USB notifies a valid ICL and the charger updates ICL and charger-type to SDP. 3. If enumeration fails, USB notifies -ETIMEDOUT and charger applies ICL based on the Rp value. CRs-Fixed: 2081457 Change-Id: I2747a42ed9f9531e83c53d781a8ae9baa9aa74d0 Signed-off-by: Anirudh Ghayal --- drivers/power/supply/qcom/qpnp-smb2.c | 7 ++ drivers/power/supply/qcom/smb-lib.c | 155 ++++++++++++++++++++------ drivers/power/supply/qcom/smb-lib.h | 1 + 3 files changed, 132 insertions(+), 31 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 7de9429cae8b..630d02eb7a67 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -1641,6 +1641,13 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + rc = smblib_read(chg, USBIN_OPTIONS_2_CFG_REG, &chg->float_cfg); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read float charger options rc=%d\n", + rc); + return rc; + } + switch (chip->dt.chg_inhibit_thr_mv) { case 50: rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG, diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 52775a3bdf1b..ab6ba03e3556 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -514,10 +514,17 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* if PD is active, APSD is disabled so won't have a valid result */ - if (chg->pd_active) + if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; - else + } else { + /* + * Update real charger type only if its not FLOAT + * detected as as SDP + */ + if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) chg->real_charger_type = apsd_result->pst; + } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); @@ -785,6 +792,7 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) { int rc; u8 icl_options; + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* power source is SDP */ switch (icl_ua) { @@ -809,6 +817,21 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return -EINVAL; } + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && + apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT) { + /* + * change the float charger configuration to SDP, if this + * is the case of SDP being detected as FLOAT + */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FORCE_FLOAT_SDP_CFG_BIT, FORCE_FLOAT_SDP_CFG_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't set float ICL options rc=%d\n", + rc); + return rc; + } + } + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options); if (rc < 0) { @@ -2402,6 +2425,31 @@ int smblib_get_prop_die_health(struct smb_charger *chg, return 0; } +#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 +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; +} + /******************* * USB PSY SETTERS * * *****************/ @@ -2419,14 +2467,54 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, return rc; } +static int smblib_handle_usb_current(struct smb_charger *chg, + int usb_current) +{ + int rc = 0, rp_ua, typec_mode; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + if (usb_current == -ETIMEDOUT) { + /* + * Valid FLOAT charger, report the current based + * of Rp + */ + typec_mode = smblib_get_prop_typec_mode(chg); + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, + true, rp_ua); + if (rc < 0) + return rc; + } else { + /* + * FLOAT charger detected as SDP by USB driver, + * charge with the requested current and update the + * real_charger_type + */ + chg->real_charger_type = POWER_SUPPLY_TYPE_USB; + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, usb_current); + if (rc < 0) + return rc; + rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, + false, 0); + if (rc < 0) + return rc; + } + } else { + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, usb_current); + } + + return rc; +} + int smblib_set_prop_usb_current_max(struct smb_charger *chg, const union power_supply_propval *val) { int rc = 0; if (!chg->pd_active) { - rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, - true, val->intval); + rc = smblib_handle_usb_current(chg, val->intval); } else if (chg->system_suspend_supported) { if (val->intval <= USBIN_25MA) rc = vote(chg->usb_icl_votable, @@ -2875,14 +2963,6 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, /*********************** * USB MAIN PSY SETTERS * *************************/ - -#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) { @@ -3438,24 +3518,6 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, 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; @@ -3481,11 +3543,17 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); break; case POWER_SUPPLY_TYPE_USB_DCP: - 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_FLOAT: + /* + * limit ICL to 100mA, the USB driver will enumerate to check + * if this is a SDP and appropriately set the current + */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000); @@ -3680,6 +3748,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pd_hard_reset = 0; chg->typec_legacy_valid = false; + /* write back the default FLOAT charger configuration */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); + if (rc < 0) + smblib_err(chg, "Couldn't write float charger options rc=%d\n", + rc); + /* reset back to 120mS tCC debounce */ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0); if (rc < 0) @@ -3774,6 +3849,24 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT)) return; + /* + * if APSD indicates FLOAT and the USB stack had detected SDP, + * do not respond to Rp changes as we do not confirm that its + * a legacy cable + */ + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + return; + /* + * We want the ICL vote @ 100mA for a FLOAT charger + * until the detection by the USB stack is complete. + * Ignore the Rp changes unless there is a + * pre-existing valid vote. + */ + if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + get_client_vote(chg->usb_icl_votable, + LEGACY_UNKNOWN_VOTER) <= 100000) + return; + /* * handle Rp change for DCP/FLOAT/OCP. * Update the current only if the Rp is different from diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 43a795085755..8c810352f78f 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -326,6 +326,7 @@ struct smb_charger { int typec_mode; int usb_icl_change_irq_enabled; u32 jeita_status; + u8 float_cfg; /* workaround flag */ u32 wa_flags;