Merge "power: smb-lib: WA to fix legacy cable detection"
This commit is contained in:
commit
aadfc6d284
3 changed files with 341 additions and 301 deletions
|
@ -540,6 +540,12 @@ static int smb2_usb_set_prop(struct power_supply *psy,
|
||||||
struct smb_charger *chg = &chip->chg;
|
struct smb_charger *chg = &chip->chg;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
mutex_lock(&chg->lock);
|
||||||
|
if (!chg->typec_present) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
switch (psp) {
|
switch (psp) {
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||||
rc = smblib_set_prop_usb_voltage_min(chg, val);
|
rc = smblib_set_prop_usb_voltage_min(chg, val);
|
||||||
|
@ -578,6 +584,8 @@ static int smb2_usb_set_prop(struct power_supply *psy,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&chg->lock);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,10 +1358,12 @@ static int smb2_configure_typec(struct smb_charger *chg)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disable try.SINK mode */
|
/* disable try.SINK mode and legacy cable IRQs */
|
||||||
rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0);
|
rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT |
|
||||||
|
TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT |
|
||||||
|
TYPEC_LEGACY_CABLE_INT_EN_BIT, 0);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
|
dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1509,6 +1519,8 @@ static int smb2_init_hw(struct smb2 *chip)
|
||||||
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
|
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
|
||||||
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
|
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
|
||||||
true, 0);
|
true, 0);
|
||||||
|
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
|
||||||
|
true, 0);
|
||||||
vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
|
vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
|
||||||
chip->dt.hvdcp_disable, 0);
|
chip->dt.hvdcp_disable, 0);
|
||||||
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
|
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
|
||||||
|
|
|
@ -543,30 +543,6 @@ static void smblib_rerun_apsd(struct smb_charger *chg)
|
||||||
smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
|
smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
|
|
||||||
{
|
|
||||||
const struct apsd_result *apsd_result;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether
|
|
||||||
* apsd rerun was tried earlier
|
|
||||||
*/
|
|
||||||
if (get_client_vote(chg->hvdcp_disable_votable_indirect,
|
|
||||||
PD_INACTIVE_VOTER)) {
|
|
||||||
vote(chg->hvdcp_disable_votable_indirect,
|
|
||||||
PD_INACTIVE_VOTER, false, 0);
|
|
||||||
/* ensure hvdcp is enabled */
|
|
||||||
if (!get_effective_result(
|
|
||||||
chg->hvdcp_disable_votable_indirect)) {
|
|
||||||
apsd_result = smblib_get_apsd_result(chg);
|
|
||||||
if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
|
|
||||||
smblib_rerun_apsd(chg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
|
static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
|
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
|
||||||
|
@ -1023,6 +999,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
|
||||||
struct smb_charger *chg = data;
|
struct smb_charger *chg = data;
|
||||||
int rc;
|
int rc;
|
||||||
u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;
|
u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;
|
||||||
|
u8 stat;
|
||||||
|
|
||||||
/* vote to enable/disable HW autonomous INOV */
|
/* vote to enable/disable HW autonomous INOV */
|
||||||
vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0);
|
vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0);
|
||||||
|
@ -1044,6 +1021,16 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = smblib_read(chg, APSD_STATUS_REG, &stat);
|
||||||
|
if (rc < 0) {
|
||||||
|
smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* re-run APSD if HVDCP was detected */
|
||||||
|
if (stat & QC_CHARGER_BIT)
|
||||||
|
smblib_rerun_apsd(chg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1137,6 +1124,22 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int smblib_typec_irq_disable_vote_callback(struct votable *votable,
|
||||||
|
void *data, int disable, const char *client)
|
||||||
|
{
|
||||||
|
struct smb_charger *chg = data;
|
||||||
|
|
||||||
|
if (!chg->irq_info[TYPE_C_CHANGE_IRQ].irq)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (disable)
|
||||||
|
disable_irq_nosync(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
|
||||||
|
else
|
||||||
|
enable_irq(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************
|
/*******************
|
||||||
* VCONN REGULATOR *
|
* VCONN REGULATOR *
|
||||||
* *****************/
|
* *****************/
|
||||||
|
@ -1145,7 +1148,7 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
|
||||||
static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
|
static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
struct smb_charger *chg = rdev_get_drvdata(rdev);
|
struct smb_charger *chg = rdev_get_drvdata(rdev);
|
||||||
u8 otg_stat, stat4;
|
u8 otg_stat, val;
|
||||||
int rc = 0, i;
|
int rc = 0, i;
|
||||||
|
|
||||||
if (!chg->external_vconn) {
|
if (!chg->external_vconn) {
|
||||||
|
@ -1176,17 +1179,12 @@ static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
|
||||||
* VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used
|
* VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used
|
||||||
* for Vconn, and it should be set with reverse polarity of CC_OUT.
|
* for Vconn, and it should be set with reverse polarity of CC_OUT.
|
||||||
*/
|
*/
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
|
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
|
||||||
stat4 = stat4 & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
|
val = chg->typec_status[3] &
|
||||||
|
CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
|
||||||
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
|
VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
|
||||||
VCONN_EN_VALUE_BIT | stat4);
|
VCONN_EN_VALUE_BIT | val);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
|
smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -2147,23 +2145,13 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
|
||||||
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
|
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
if (chg->typec_status[3] & CC_ATTACHED_BIT)
|
||||||
u8 stat;
|
val->intval =
|
||||||
|
(bool)(chg->typec_status[3] & CC_ORIENTATION_BIT) + 1;
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n",
|
|
||||||
stat);
|
|
||||||
|
|
||||||
if (stat & CC_ATTACHED_BIT)
|
|
||||||
val->intval = (bool)(stat & CC_ORIENTATION_BIT) + 1;
|
|
||||||
else
|
else
|
||||||
val->intval = 0;
|
val->intval = 0;
|
||||||
|
|
||||||
return rc;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * const smblib_typec_mode_name[] = {
|
static const char * const smblib_typec_mode_name[] = {
|
||||||
|
@ -2181,17 +2169,7 @@ static const char * const smblib_typec_mode_name[] = {
|
||||||
|
|
||||||
static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
|
static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
int rc;
|
switch (chg->typec_status[0]) {
|
||||||
u8 stat;
|
|
||||||
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc);
|
|
||||||
return POWER_SUPPLY_TYPEC_NONE;
|
|
||||||
}
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat);
|
|
||||||
|
|
||||||
switch (stat) {
|
|
||||||
case 0:
|
case 0:
|
||||||
return POWER_SUPPLY_TYPEC_NONE;
|
return POWER_SUPPLY_TYPEC_NONE;
|
||||||
case UFP_TYPEC_RDSTD_BIT:
|
case UFP_TYPEC_RDSTD_BIT:
|
||||||
|
@ -2209,17 +2187,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
|
||||||
|
|
||||||
static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
|
static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
int rc;
|
switch (chg->typec_status[1] & DFP_TYPEC_MASK) {
|
||||||
u8 stat;
|
|
||||||
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc);
|
|
||||||
return POWER_SUPPLY_TYPEC_NONE;
|
|
||||||
}
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat);
|
|
||||||
|
|
||||||
switch (stat & DFP_TYPEC_MASK) {
|
|
||||||
case DFP_RA_RA_BIT:
|
case DFP_RA_RA_BIT:
|
||||||
return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
|
return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
|
||||||
case DFP_RD_RD_BIT:
|
case DFP_RD_RD_BIT:
|
||||||
|
@ -2240,28 +2208,17 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
|
||||||
int smblib_get_prop_typec_mode(struct smb_charger *chg,
|
int smblib_get_prop_typec_mode(struct smb_charger *chg,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
int rc;
|
if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
|
||||||
u8 stat;
|
|
||||||
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
|
|
||||||
val->intval = POWER_SUPPLY_TYPEC_NONE;
|
val->intval = POWER_SUPPLY_TYPEC_NONE;
|
||||||
return rc;
|
return 0;
|
||||||
}
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat);
|
|
||||||
|
|
||||||
if (!(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
|
|
||||||
val->intval = POWER_SUPPLY_TYPEC_NONE;
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat & UFP_DFP_MODE_STATUS_BIT)
|
if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT)
|
||||||
val->intval = smblib_get_prop_dfp_mode(chg);
|
val->intval = smblib_get_prop_dfp_mode(chg);
|
||||||
else
|
else
|
||||||
val->intval = smblib_get_prop_ufp_mode(chg);
|
val->intval = smblib_get_prop_ufp_mode(chg);
|
||||||
|
|
||||||
return rc;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
|
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
|
||||||
|
@ -2549,53 +2506,60 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
|
||||||
const union power_supply_propval *val)
|
const union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 stat = 0;
|
bool orientation, cc_debounced, sink_attached, hvdcp;
|
||||||
bool cc_debounced;
|
u8 stat;
|
||||||
bool orientation;
|
|
||||||
bool pd_active = val->intval;
|
|
||||||
|
|
||||||
if (!get_effective_result(chg->pd_allowed_votable)) {
|
if (!get_effective_result(chg->pd_allowed_votable))
|
||||||
smblib_err(chg, "PD is not allowed\n");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
|
rc = smblib_read(chg, APSD_STATUS_REG, &stat);
|
||||||
vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
|
|
||||||
vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
|
|
||||||
* when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set
|
|
||||||
* or when VCONN_EN_VALUE_BIT is set.
|
|
||||||
*/
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
|
smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pd_active) {
|
cc_debounced = (bool)
|
||||||
orientation = stat & CC_ORIENTATION_BIT;
|
(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
|
||||||
|
sink_attached = (bool)
|
||||||
|
(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
|
||||||
|
hvdcp = stat & QC_CHARGER_BIT;
|
||||||
|
|
||||||
|
chg->pd_active = val->intval;
|
||||||
|
if (chg->pd_active) {
|
||||||
|
vote(chg->apsd_disable_votable, PD_VOTER, true, 0);
|
||||||
|
vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
|
||||||
|
vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2
|
||||||
|
* line when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override)
|
||||||
|
* is set or when VCONN_EN_VALUE_BIT is set.
|
||||||
|
*/
|
||||||
|
orientation = chg->typec_status[3] & CC_ORIENTATION_BIT;
|
||||||
rc = smblib_masked_write(chg,
|
rc = smblib_masked_write(chg,
|
||||||
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
VCONN_EN_ORIENTATION_BIT,
|
VCONN_EN_ORIENTATION_BIT,
|
||||||
orientation ? 0 : VCONN_EN_ORIENTATION_BIT);
|
orientation ? 0 : VCONN_EN_ORIENTATION_BIT);
|
||||||
if (rc < 0) {
|
if (rc < 0)
|
||||||
smblib_err(chg,
|
smblib_err(chg,
|
||||||
"Couldn't enable vconn on CC line rc=%d\n", rc);
|
"Couldn't enable vconn on CC line rc=%d\n", rc);
|
||||||
return rc;
|
|
||||||
}
|
/* SW controlled CC_OUT */
|
||||||
|
rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
|
||||||
|
TYPEC_SPARE_CFG_BIT, TYPEC_SPARE_CFG_BIT);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't enable SW cc_out rc=%d\n",
|
||||||
|
rc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enforce 500mA for PD until the real vote comes in later.
|
* Enforce 500mA for PD until the real vote comes in later.
|
||||||
* It is guaranteed that pd_active is set prior to
|
* It is guaranteed that pd_active is set prior to
|
||||||
* pd_current_max
|
* pd_current_max
|
||||||
*/
|
*/
|
||||||
rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
|
rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
|
||||||
if (rc < 0) {
|
if (rc < 0)
|
||||||
smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
|
smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* since PD was found the cable must be non-legacy */
|
/* since PD was found the cable must be non-legacy */
|
||||||
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
|
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
|
||||||
|
@ -2603,36 +2567,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
|
||||||
/* clear USB ICL vote for DCP_VOTER */
|
/* clear USB ICL vote for DCP_VOTER */
|
||||||
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
|
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
smblib_err(chg,
|
smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
|
||||||
"Couldn't un-vote DCP from USB ICL rc=%d\n",
|
rc);
|
||||||
rc);
|
|
||||||
|
|
||||||
/* remove USB_PSY_VOTER */
|
/* remove USB_PSY_VOTER */
|
||||||
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
|
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
|
||||||
if (rc < 0) {
|
if (rc < 0)
|
||||||
smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
|
smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
|
||||||
return rc;
|
} else {
|
||||||
}
|
vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
|
||||||
|
vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
|
||||||
|
vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
|
||||||
|
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
|
||||||
|
false, 0);
|
||||||
|
|
||||||
|
/* HW controlled CC_OUT */
|
||||||
|
rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
|
||||||
|
TYPEC_SPARE_CFG_BIT, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n",
|
||||||
|
rc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This WA should only run for HVDCP. Non-legacy SDP/CDP could
|
||||||
|
* draw more, but this WA will remove Rd causing VBUS to drop,
|
||||||
|
* and data could be interrupted. Non-legacy DCP could also draw
|
||||||
|
* more, but it may impact compliance.
|
||||||
|
*/
|
||||||
|
if (!chg->typec_legacy_valid && cc_debounced &&
|
||||||
|
!sink_attached && hvdcp)
|
||||||
|
schedule_work(&chg->legacy_detection_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CC pin selection s/w override in PD session; h/w otherwise. */
|
|
||||||
rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
|
|
||||||
TYPEC_SPARE_CFG_BIT,
|
|
||||||
pd_active ? TYPEC_SPARE_CFG_BIT : 0);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n",
|
|
||||||
pd_active ? "SW" : "HW", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
|
|
||||||
if (!pd_active && cc_debounced)
|
|
||||||
try_rerun_apsd_for_hvdcp(chg);
|
|
||||||
|
|
||||||
chg->pd_active = pd_active;
|
|
||||||
smblib_update_usb_type(chg);
|
smblib_update_usb_type(chg);
|
||||||
power_supply_changed(chg->usb_psy);
|
power_supply_changed(chg->usb_psy);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3107,7 +3075,11 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
|
||||||
|
|
||||||
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
|
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
|
||||||
{
|
{
|
||||||
if (!vbus_rising) {
|
if (vbus_rising) {
|
||||||
|
/* use the typec flag even though its not typec */
|
||||||
|
chg->typec_present = 1;
|
||||||
|
} else {
|
||||||
|
chg->typec_present = 0;
|
||||||
smblib_update_usb_type(chg);
|
smblib_update_usb_type(chg);
|
||||||
extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
|
extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
|
||||||
smblib_uusb_removal(chg);
|
smblib_uusb_removal(chg);
|
||||||
|
@ -3123,18 +3095,16 @@ static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PL_DELAY_MS 30000
|
#define PL_DELAY_MS 30000
|
||||||
irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
|
void smblib_usb_plugin_locked(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
struct smb_irq_data *irq_data = data;
|
|
||||||
struct smb_charger *chg = irq_data->parent_data;
|
|
||||||
int rc;
|
int rc;
|
||||||
u8 stat;
|
u8 stat;
|
||||||
bool vbus_rising;
|
bool vbus_rising;
|
||||||
|
|
||||||
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
|
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
|
smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
|
||||||
return IRQ_HANDLED;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
|
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
|
||||||
|
@ -3184,8 +3154,18 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
|
||||||
smblib_typec_usb_plugin(chg, vbus_rising);
|
smblib_typec_usb_plugin(chg, vbus_rising);
|
||||||
|
|
||||||
power_supply_changed(chg->usb_psy);
|
power_supply_changed(chg->usb_psy);
|
||||||
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
|
smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
|
||||||
irq_data->name, vbus_rising ? "attached" : "detached");
|
vbus_rising ? "attached" : "detached");
|
||||||
|
}
|
||||||
|
|
||||||
|
irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct smb_irq_data *irq_data = data;
|
||||||
|
struct smb_charger *chg = irq_data->parent_data;
|
||||||
|
|
||||||
|
mutex_lock(&chg->lock);
|
||||||
|
smblib_usb_plugin_locked(chg);
|
||||||
|
mutex_unlock(&chg->lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3354,9 +3334,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
|
||||||
if (rising) {
|
if (rising) {
|
||||||
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
|
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
|
||||||
false, 0);
|
false, 0);
|
||||||
if (get_effective_result(chg->pd_disallowed_votable_indirect))
|
|
||||||
/* could be a legacy cable, try doing hvdcp */
|
|
||||||
try_rerun_apsd_for_hvdcp(chg);
|
|
||||||
|
|
||||||
/* enable HDC and ICL irq for QC2/3 charger */
|
/* enable HDC and ICL irq for QC2/3 charger */
|
||||||
if (qc_charger)
|
if (qc_charger)
|
||||||
|
@ -3391,6 +3368,10 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
|
||||||
|
|
||||||
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
|
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
|
||||||
{
|
{
|
||||||
|
/* while PD is active it should have complete ICL control */
|
||||||
|
if (chg->pd_active)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (pst) {
|
switch (pst) {
|
||||||
case POWER_SUPPLY_TYPE_USB:
|
case POWER_SUPPLY_TYPE_USB:
|
||||||
/*
|
/*
|
||||||
|
@ -3430,7 +3411,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
|
||||||
|
|
||||||
apsd_result = smblib_update_usb_type(chg);
|
apsd_result = smblib_update_usb_type(chg);
|
||||||
|
|
||||||
if (!chg->pd_active)
|
if (!chg->typec_legacy_valid)
|
||||||
smblib_force_legacy_icl(chg, apsd_result->pst);
|
smblib_force_legacy_icl(chg, apsd_result->pst);
|
||||||
|
|
||||||
switch (apsd_result->bit) {
|
switch (apsd_result->bit) {
|
||||||
|
@ -3516,73 +3497,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void typec_source_removal(struct smb_charger *chg)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* reset legacy unknown vote */
|
|
||||||
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
|
|
||||||
|
|
||||||
/* reset both usbin current and voltage votes */
|
|
||||||
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
|
|
||||||
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
|
|
||||||
|
|
||||||
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
|
|
||||||
|
|
||||||
if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
|
|
||||||
/* re-enable AUTH_IRQ_EN_CFG_BIT */
|
|
||||||
rc = smblib_masked_write(chg,
|
|
||||||
USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
|
|
||||||
AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
|
|
||||||
if (rc < 0)
|
|
||||||
smblib_err(chg,
|
|
||||||
"Couldn't enable QC auth setting rc=%d\n", rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reconfigure allowed voltage for HVDCP */
|
|
||||||
rc = smblib_set_adapter_allowance(chg,
|
|
||||||
USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
|
|
||||||
if (rc < 0)
|
|
||||||
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
|
|
||||||
rc);
|
|
||||||
|
|
||||||
chg->voltage_min_uv = MICRO_5V;
|
|
||||||
chg->voltage_max_uv = MICRO_5V;
|
|
||||||
|
|
||||||
/* clear USB ICL vote for PD_VOTER */
|
|
||||||
rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0);
|
|
||||||
if (rc < 0)
|
|
||||||
smblib_err(chg, "Couldn't un-vote PD from USB ICL rc=%d\n", rc);
|
|
||||||
|
|
||||||
/* clear USB ICL vote for USB_PSY_VOTER */
|
|
||||||
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
|
|
||||||
if (rc < 0)
|
|
||||||
smblib_err(chg,
|
|
||||||
"Couldn't un-vote USB_PSY from USB ICL rc=%d\n", rc);
|
|
||||||
|
|
||||||
/* clear USB ICL vote for DCP_VOTER */
|
|
||||||
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
|
|
||||||
if (rc < 0)
|
|
||||||
smblib_err(chg,
|
|
||||||
"Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void typec_source_insertion(struct smb_charger *chg)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
|
|
||||||
* ICL, so vote LEGACY_UNKNOWN here if none of the above three have
|
|
||||||
* casted their votes
|
|
||||||
*/
|
|
||||||
if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
|
|
||||||
&& !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
|
|
||||||
&& !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
|
|
||||||
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
|
|
||||||
|
|
||||||
smblib_set_opt_freq_buck(chg, chg->chg_freq.freq_5V);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void typec_sink_insertion(struct smb_charger *chg)
|
static void typec_sink_insertion(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
/* when a sink is inserted we should not wait on hvdcp timeout to
|
/* when a sink is inserted we should not wait on hvdcp timeout to
|
||||||
|
@ -3605,30 +3519,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
|
||||||
|
|
||||||
chg->cc2_detach_wa_active = false;
|
chg->cc2_detach_wa_active = false;
|
||||||
|
|
||||||
cancel_delayed_work_sync(&chg->pl_enable_work);
|
/* reset APSD voters */
|
||||||
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
|
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
|
||||||
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
|
vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&chg->pl_enable_work);
|
||||||
|
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
|
||||||
|
|
||||||
|
/* reset input current limit voters */
|
||||||
|
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
|
||||||
|
vote(chg->usb_icl_votable, PD_VOTER, false, 0);
|
||||||
|
vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
|
||||||
|
vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
|
||||||
|
vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
|
||||||
|
|
||||||
|
/* reset hvdcp voters */
|
||||||
|
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
|
||||||
|
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0);
|
||||||
|
|
||||||
|
/* reset power delivery voters */
|
||||||
|
vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
|
||||||
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
|
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, HVDCP_TIMEOUT_VOTER, true, 0);
|
||||||
|
|
||||||
|
/* reset usb irq voters */
|
||||||
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
|
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
|
||||||
vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
|
vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
|
||||||
|
|
||||||
/* reset votes from vbus_cc_short */
|
/* reset parallel voters */
|
||||||
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
|
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
|
||||||
true, 0);
|
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
|
||||||
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
|
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
|
||||||
true, 0);
|
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
|
||||||
/*
|
|
||||||
* cable could be removed during hard reset, remove its vote to
|
|
||||||
* disable apsd
|
|
||||||
*/
|
|
||||||
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
|
|
||||||
|
|
||||||
chg->vconn_attempts = 0;
|
chg->vconn_attempts = 0;
|
||||||
chg->otg_attempts = 0;
|
chg->otg_attempts = 0;
|
||||||
chg->pulse_cnt = 0;
|
chg->pulse_cnt = 0;
|
||||||
chg->usb_icl_delta_ua = 0;
|
chg->usb_icl_delta_ua = 0;
|
||||||
|
chg->voltage_min_uv = MICRO_5V;
|
||||||
|
chg->voltage_max_uv = MICRO_5V;
|
||||||
|
chg->pd_active = 0;
|
||||||
|
chg->pd_hard_reset = 0;
|
||||||
|
chg->typec_legacy_valid = false;
|
||||||
|
|
||||||
/* enable APSD CC trigger for next insertion */
|
/* enable APSD CC trigger for next insertion */
|
||||||
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
|
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
|
||||||
|
@ -3636,15 +3568,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
|
smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
|
||||||
|
|
||||||
smblib_update_usb_type(chg);
|
if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
|
||||||
typec_source_removal(chg);
|
/* re-enable AUTH_IRQ_EN_CFG_BIT */
|
||||||
|
rc = smblib_masked_write(chg,
|
||||||
|
USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
|
||||||
|
AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg,
|
||||||
|
"Couldn't enable QC auth setting rc=%d\n", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reconfigure allowed voltage for HVDCP */
|
||||||
|
rc = smblib_set_adapter_allowance(chg,
|
||||||
|
USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
|
||||||
|
rc);
|
||||||
|
|
||||||
|
/* enable DRP */
|
||||||
|
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
|
TYPEC_POWER_ROLE_CMD_MASK, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
|
||||||
|
|
||||||
|
/* HW controlled CC_OUT */
|
||||||
|
rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
|
||||||
|
TYPEC_SPARE_CFG_BIT, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
|
||||||
|
|
||||||
|
/* restore crude sensor */
|
||||||
|
rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc);
|
||||||
|
|
||||||
typec_sink_removal(chg);
|
typec_sink_removal(chg);
|
||||||
|
smblib_update_usb_type(chg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smblib_handle_typec_insertion(struct smb_charger *chg,
|
static void smblib_handle_typec_insertion(struct smb_charger *chg,
|
||||||
bool sink_attached, bool legacy_cable)
|
bool sink_attached)
|
||||||
{
|
{
|
||||||
int rp, rc;
|
int rc;
|
||||||
|
|
||||||
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
|
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
|
||||||
|
|
||||||
|
@ -3654,36 +3619,31 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
|
||||||
smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
|
smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
|
||||||
rc);
|
rc);
|
||||||
|
|
||||||
if (sink_attached) {
|
if (sink_attached)
|
||||||
typec_source_removal(chg);
|
|
||||||
typec_sink_insertion(chg);
|
typec_sink_insertion(chg);
|
||||||
} else {
|
else
|
||||||
typec_sink_removal(chg);
|
typec_sink_removal(chg);
|
||||||
typec_source_insertion(chg);
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
|
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
|
||||||
bool rising, bool sink_attached, bool legacy_cable)
|
bool rising, bool sink_attached)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
union power_supply_propval pval = {0, };
|
union power_supply_propval pval = {0, };
|
||||||
|
|
||||||
if (rising)
|
if (rising) {
|
||||||
smblib_handle_typec_insertion(chg, sink_attached, legacy_cable);
|
if (!chg->typec_present) {
|
||||||
else
|
chg->typec_present = true;
|
||||||
smblib_handle_typec_removal(chg);
|
smblib_dbg(chg, PR_MISC, "TypeC insertion\n");
|
||||||
|
smblib_handle_typec_insertion(chg, sink_attached);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chg->typec_present) {
|
||||||
|
chg->typec_present = false;
|
||||||
|
smblib_dbg(chg, PR_MISC, "TypeC removal\n");
|
||||||
|
smblib_handle_typec_removal(chg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = smblib_get_prop_typec_mode(chg, &pval);
|
rc = smblib_get_prop_typec_mode(chg, &pval);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
@ -3714,53 +3674,54 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void smblib_usb_typec_change(struct smb_charger *chg)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
bool debounce_done, sink_attached;
|
||||||
|
|
||||||
|
rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG,
|
||||||
|
chg->typec_status, 5);
|
||||||
|
if (rc < 0) {
|
||||||
|
smblib_err(chg, "Couldn't cache USB Type-C status rc=%d\n", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debounce_done =
|
||||||
|
(bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
|
||||||
|
sink_attached =
|
||||||
|
(bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
|
||||||
|
|
||||||
|
smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached);
|
||||||
|
|
||||||
|
if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT)
|
||||||
|
smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
|
||||||
|
|
||||||
|
if (chg->typec_status[3] & TYPEC_VCONN_OVERCURR_STATUS_BIT)
|
||||||
|
schedule_work(&chg->vconn_oc_work);
|
||||||
|
|
||||||
|
power_supply_changed(chg->usb_psy);
|
||||||
|
}
|
||||||
|
|
||||||
irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
|
irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct smb_irq_data *irq_data = data;
|
struct smb_irq_data *irq_data = data;
|
||||||
struct smb_charger *chg = irq_data->parent_data;
|
struct smb_charger *chg = irq_data->parent_data;
|
||||||
int rc;
|
|
||||||
u8 stat4, stat5;
|
|
||||||
bool debounce_done, sink_attached, legacy_cable;
|
|
||||||
|
|
||||||
if (chg->micro_usb_mode)
|
if (chg->micro_usb_mode) {
|
||||||
return smblib_handle_usb_typec_change_for_uusb(chg);
|
smblib_handle_usb_typec_change_for_uusb(chg);
|
||||||
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
|
|
||||||
if (rc < 0) {
|
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
|
if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) {
|
||||||
if (rc < 0) {
|
smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
|
||||||
smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc);
|
chg->cc2_detach_wa_active ?
|
||||||
|
"cc2_detach_wa" : "typec_en_dis");
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
debounce_done = (bool)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
|
mutex_lock(&chg->lock);
|
||||||
sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT);
|
smblib_usb_typec_change(chg);
|
||||||
legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT);
|
mutex_unlock(&chg->lock);
|
||||||
|
|
||||||
if (chg->cc2_detach_wa_active) {
|
|
||||||
smblib_dbg(chg, PR_INTERRUPT, "Ignoring cc2_wrkarnd=%d dd=%d\n",
|
|
||||||
chg->cc2_detach_wa_active,
|
|
||||||
debounce_done);
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
smblib_handle_typec_debounce_done(chg,
|
|
||||||
debounce_done, sink_attached, legacy_cable);
|
|
||||||
|
|
||||||
if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT)
|
|
||||||
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n",
|
|
||||||
irq_data->name);
|
|
||||||
|
|
||||||
if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT)
|
|
||||||
schedule_work(&chg->vconn_oc_work);
|
|
||||||
|
|
||||||
power_supply_changed(chg->usb_psy);
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat4);
|
|
||||||
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat5);
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3788,7 +3749,7 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct smb_irq_data *irq_data = data;
|
struct smb_irq_data *irq_data = data;
|
||||||
struct smb_charger *chg = irq_data->parent_data;
|
struct smb_charger *chg = irq_data->parent_data;
|
||||||
int rc;
|
int rc, usb_icl;
|
||||||
u8 stat;
|
u8 stat;
|
||||||
|
|
||||||
if (!(chg->wa_flags & BOOST_BACK_WA))
|
if (!(chg->wa_flags & BOOST_BACK_WA))
|
||||||
|
@ -3800,8 +3761,9 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((stat & USE_USBIN_BIT) &&
|
/* skip suspending input if its already suspended by some other voter */
|
||||||
get_effective_result(chg->usb_icl_votable) < USBIN_25MA)
|
usb_icl = get_effective_result(chg->usb_icl_votable);
|
||||||
|
if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
if (stat & USE_DCIN_BIT)
|
if (stat & USE_DCIN_BIT)
|
||||||
|
@ -3839,12 +3801,7 @@ static void smblib_hvdcp_detect_work(struct work_struct *work)
|
||||||
|
|
||||||
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
|
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
|
||||||
false, 0);
|
false, 0);
|
||||||
if (get_effective_result(chg->pd_disallowed_votable_indirect))
|
power_supply_changed(chg->usb_psy);
|
||||||
/* pd is still disabled, try hvdcp */
|
|
||||||
try_rerun_apsd_for_hvdcp(chg);
|
|
||||||
else
|
|
||||||
/* notify pd now that pd is allowed */
|
|
||||||
power_supply_changed(chg->usb_psy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bms_update_work(struct work_struct *work)
|
static void bms_update_work(struct work_struct *work)
|
||||||
|
@ -3886,7 +3843,6 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
u8 stat4, stat5;
|
u8 stat4, stat5;
|
||||||
struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"};
|
|
||||||
struct smb_charger *chg = container_of(work, struct smb_charger,
|
struct smb_charger *chg = container_of(work, struct smb_charger,
|
||||||
rdstd_cc2_detach_work);
|
rdstd_cc2_detach_work);
|
||||||
|
|
||||||
|
@ -3949,8 +3905,9 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
|
||||||
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
EXIT_SNK_BASED_ON_CC_BIT, 0);
|
EXIT_SNK_BASED_ON_CC_BIT, 0);
|
||||||
smblib_reg_block_restore(chg, cc2_detach_settings);
|
smblib_reg_block_restore(chg, cc2_detach_settings);
|
||||||
irq_data.parent_data = chg;
|
mutex_lock(&chg->lock);
|
||||||
smblib_handle_usb_typec_change(0, &irq_data);
|
smblib_usb_typec_change(chg);
|
||||||
|
mutex_unlock(&chg->lock);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rerun:
|
rerun:
|
||||||
|
@ -4173,6 +4130,56 @@ static void smblib_pl_enable_work(struct work_struct *work)
|
||||||
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
|
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void smblib_legacy_detection_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct smb_charger *chg = container_of(work, struct smb_charger,
|
||||||
|
legacy_detection_work);
|
||||||
|
int rc;
|
||||||
|
u8 stat;
|
||||||
|
bool legacy, rp_high;
|
||||||
|
|
||||||
|
mutex_lock(&chg->lock);
|
||||||
|
chg->typec_en_dis_active = 1;
|
||||||
|
smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n");
|
||||||
|
rc = smblib_masked_write(chg,
|
||||||
|
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
|
TYPEC_DISABLE_CMD_BIT,
|
||||||
|
TYPEC_DISABLE_CMD_BIT);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
|
||||||
|
|
||||||
|
/* wait for the adapter to turn off VBUS */
|
||||||
|
msleep(500);
|
||||||
|
|
||||||
|
rc = smblib_masked_write(chg,
|
||||||
|
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
|
||||||
|
TYPEC_DISABLE_CMD_BIT, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
|
||||||
|
|
||||||
|
/* wait for type-c detection to complete */
|
||||||
|
msleep(100);
|
||||||
|
|
||||||
|
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
|
||||||
|
if (rc < 0) {
|
||||||
|
smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
chg->typec_legacy_valid = true;
|
||||||
|
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
|
||||||
|
legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
|
||||||
|
rp_high = smblib_get_prop_ufp_mode(chg) ==
|
||||||
|
POWER_SUPPLY_TYPEC_SOURCE_HIGH;
|
||||||
|
if (!legacy || !rp_high)
|
||||||
|
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
|
||||||
|
false, 0);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
chg->typec_en_dis_active = 0;
|
||||||
|
mutex_unlock(&chg->lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int smblib_create_votables(struct smb_charger *chg)
|
static int smblib_create_votables(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -4305,6 +4312,15 @@ static int smblib_create_votables(struct smb_charger *chg)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chg->typec_irq_disable_votable = create_votable("TYPEC_IRQ_DISABLE",
|
||||||
|
VOTE_SET_ANY,
|
||||||
|
smblib_typec_irq_disable_vote_callback,
|
||||||
|
chg);
|
||||||
|
if (IS_ERR(chg->typec_irq_disable_votable)) {
|
||||||
|
rc = PTR_ERR(chg->typec_irq_disable_votable);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4330,6 +4346,8 @@ static void smblib_destroy_votables(struct smb_charger *chg)
|
||||||
destroy_votable(chg->apsd_disable_votable);
|
destroy_votable(chg->apsd_disable_votable);
|
||||||
if (chg->hvdcp_hw_inov_dis_votable)
|
if (chg->hvdcp_hw_inov_dis_votable)
|
||||||
destroy_votable(chg->hvdcp_hw_inov_dis_votable);
|
destroy_votable(chg->hvdcp_hw_inov_dis_votable);
|
||||||
|
if (chg->typec_irq_disable_votable)
|
||||||
|
destroy_votable(chg->typec_irq_disable_votable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smblib_iio_deinit(struct smb_charger *chg)
|
static void smblib_iio_deinit(struct smb_charger *chg)
|
||||||
|
@ -4350,6 +4368,7 @@ int smblib_init(struct smb_charger *chg)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
mutex_init(&chg->lock);
|
||||||
mutex_init(&chg->write_lock);
|
mutex_init(&chg->write_lock);
|
||||||
mutex_init(&chg->otg_oc_lock);
|
mutex_init(&chg->otg_oc_lock);
|
||||||
INIT_WORK(&chg->bms_update_work, bms_update_work);
|
INIT_WORK(&chg->bms_update_work, bms_update_work);
|
||||||
|
@ -4362,6 +4381,7 @@ int smblib_init(struct smb_charger *chg)
|
||||||
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
|
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
|
||||||
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
|
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
|
||||||
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
|
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
|
||||||
|
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
|
||||||
chg->fake_capacity = -EINVAL;
|
chg->fake_capacity = -EINVAL;
|
||||||
|
|
||||||
switch (chg->mode) {
|
switch (chg->mode) {
|
||||||
|
|
|
@ -61,6 +61,7 @@ enum print_reason {
|
||||||
#define SW_QC3_VOTER "SW_QC3_VOTER"
|
#define SW_QC3_VOTER "SW_QC3_VOTER"
|
||||||
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
|
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
|
||||||
#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
|
#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
|
||||||
|
#define CC2_WA_VOTER "CC2_WA_VOTER"
|
||||||
|
|
||||||
#define VCONN_MAX_ATTEMPTS 3
|
#define VCONN_MAX_ATTEMPTS 3
|
||||||
#define OTG_MAX_ATTEMPTS 3
|
#define OTG_MAX_ATTEMPTS 3
|
||||||
|
@ -229,6 +230,7 @@ struct smb_charger {
|
||||||
int smb_version;
|
int smb_version;
|
||||||
|
|
||||||
/* locks */
|
/* locks */
|
||||||
|
struct mutex lock;
|
||||||
struct mutex write_lock;
|
struct mutex write_lock;
|
||||||
struct mutex ps_change_lock;
|
struct mutex ps_change_lock;
|
||||||
struct mutex otg_oc_lock;
|
struct mutex otg_oc_lock;
|
||||||
|
@ -269,6 +271,7 @@ struct smb_charger {
|
||||||
struct votable *apsd_disable_votable;
|
struct votable *apsd_disable_votable;
|
||||||
struct votable *hvdcp_hw_inov_dis_votable;
|
struct votable *hvdcp_hw_inov_dis_votable;
|
||||||
struct votable *usb_irq_enable_votable;
|
struct votable *usb_irq_enable_votable;
|
||||||
|
struct votable *typec_irq_disable_votable;
|
||||||
|
|
||||||
/* work */
|
/* work */
|
||||||
struct work_struct bms_update_work;
|
struct work_struct bms_update_work;
|
||||||
|
@ -282,6 +285,7 @@ struct smb_charger {
|
||||||
struct delayed_work otg_ss_done_work;
|
struct delayed_work otg_ss_done_work;
|
||||||
struct delayed_work icl_change_work;
|
struct delayed_work icl_change_work;
|
||||||
struct delayed_work pl_enable_work;
|
struct delayed_work pl_enable_work;
|
||||||
|
struct work_struct legacy_detection_work;
|
||||||
|
|
||||||
/* cached status */
|
/* cached status */
|
||||||
int voltage_min_uv;
|
int voltage_min_uv;
|
||||||
|
@ -307,10 +311,14 @@ struct smb_charger {
|
||||||
int otg_cl_ua;
|
int otg_cl_ua;
|
||||||
bool uusb_apsd_rerun_done;
|
bool uusb_apsd_rerun_done;
|
||||||
bool pd_hard_reset;
|
bool pd_hard_reset;
|
||||||
|
bool typec_present;
|
||||||
|
u8 typec_status[5];
|
||||||
|
bool typec_legacy_valid;
|
||||||
|
|
||||||
/* workaround flag */
|
/* workaround flag */
|
||||||
u32 wa_flags;
|
u32 wa_flags;
|
||||||
bool cc2_detach_wa_active;
|
bool cc2_detach_wa_active;
|
||||||
|
bool typec_en_dis_active;
|
||||||
int boost_current_ua;
|
int boost_current_ua;
|
||||||
int temp_speed_reading_count;
|
int temp_speed_reading_count;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue