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 <aghayal@codeaurora.org>
This commit is contained in:
Anirudh Ghayal 2017-07-26 20:26:20 +05:30
parent 9f462e8a2b
commit 3abd7de15e
3 changed files with 132 additions and 31 deletions

View file

@ -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,

View file

@ -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

View file

@ -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;