Merge "power: smb-lib: start CC2 removal WA when VBUS is low"

This commit is contained in:
Linux Build Service Account 2017-04-18 13:56:55 -07:00 committed by Gerrit - the friendly Code Review server
commit fe484ee85a
2 changed files with 93 additions and 117 deletions

View file

@ -2353,16 +2353,7 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
union power_supply_propval *val)
{
int rc;
u8 ctrl;
rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl);
if (rc < 0) {
smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG rc=%d\n",
rc);
return rc;
}
val->intval = ctrl & EXIT_SNK_BASED_ON_CC_BIT;
val->intval = chg->pd_hard_reset;
return 0;
}
@ -2744,88 +2735,70 @@ static struct reg_info cc2_detach_settings[] = {
static int smblib_cc2_sink_removal_enter(struct smb_charger *chg)
{
int rc = 0;
union power_supply_propval cc2_val = {0, };
int rc, ccout, ufp_mode;
u8 stat;
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
return rc;
return 0;
if (chg->cc2_sink_detach_flag != CC2_SINK_NONE)
return rc;
if (chg->cc2_detach_wa_active)
return 0;
rc = smblib_get_prop_typec_cc_orientation(chg, &cc2_val);
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't get cc orientation rc=%d\n", rc);
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return rc;
}
if (cc2_val.intval == 1)
return rc;
ccout = (stat & CC_ATTACHED_BIT) ?
(!!(stat & CC_ORIENTATION_BIT) + 1) : 0;
ufp_mode = (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT) ?
!(stat & UFP_DFP_MODE_STATUS_BIT) : 0;
rc = smblib_get_prop_typec_mode(chg, &cc2_val);
if (rc < 0) {
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
return rc;
}
if (ccout != 2)
return 0;
switch (cc2_val.intval) {
case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
smblib_reg_block_update(chg, cc2_detach_settings);
chg->cc2_sink_detach_flag = CC2_SINK_STD;
schedule_work(&chg->rdstd_cc2_detach_work);
break;
case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
chg->cc2_sink_detach_flag = CC2_SINK_MEDIUM_HIGH;
break;
default:
break;
}
if (!ufp_mode)
return 0;
chg->cc2_detach_wa_active = true;
/* The CC2 removal WA will cause a type-c-change IRQ storm */
smblib_reg_block_update(chg, cc2_detach_settings);
schedule_work(&chg->rdstd_cc2_detach_work);
return rc;
}
static int smblib_cc2_sink_removal_exit(struct smb_charger *chg)
{
int rc = 0;
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
return rc;
return 0;
if (chg->cc2_sink_detach_flag == CC2_SINK_STD) {
cancel_work_sync(&chg->rdstd_cc2_detach_work);
smblib_reg_block_restore(chg, cc2_detach_settings);
}
if (!chg->cc2_detach_wa_active)
return 0;
chg->cc2_sink_detach_flag = CC2_SINK_NONE;
return rc;
chg->cc2_detach_wa_active = false;
cancel_work_sync(&chg->rdstd_cc2_detach_work);
smblib_reg_block_restore(chg, cc2_detach_settings);
return 0;
}
int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc;
int rc = 0;
if (chg->pd_hard_reset == val->intval)
return rc;
chg->pd_hard_reset = val->intval;
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
EXIT_SNK_BASED_ON_CC_BIT,
(val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
if (rc < 0) {
smblib_err(chg, "Could not set EXIT_SNK_BASED_ON_CC rc=%d\n",
EXIT_SNK_BASED_ON_CC_BIT,
(chg->pd_hard_reset) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
if (rc < 0)
smblib_err(chg, "Couldn't set EXIT_SNK_BASED_ON_CC rc=%d\n",
rc);
return rc;
}
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0);
if (val->intval)
rc = smblib_cc2_sink_removal_enter(chg);
else
rc = smblib_cc2_sink_removal_exit(chg);
if (rc < 0) {
smblib_err(chg, "Could not detect cc2 removal rc=%d\n", rc);
return rc;
}
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER,
chg->pd_hard_reset, 0);
return rc;
}
@ -3132,6 +3105,23 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
return IRQ_HANDLED;
}
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
{
if (!vbus_rising) {
smblib_update_usb_type(chg);
extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
smblib_uusb_removal(chg);
}
}
static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising)
{
if (vbus_rising)
smblib_cc2_sink_removal_exit(chg);
else
smblib_cc2_sink_removal_enter(chg);
}
#define PL_DELAY_MS 30000
irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
{
@ -3148,9 +3138,8 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
}
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
smblib_set_opt_freq_buck(chg,
vbus_rising ? chg->chg_freq.freq_5V :
chg->chg_freq.freq_removal);
smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V :
chg->chg_freq.freq_removal);
/* fetch the DPDM regulator */
if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
@ -3187,14 +3176,13 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
rc);
}
if (chg->micro_usb_mode) {
smblib_update_usb_type(chg);
extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
smblib_uusb_removal(chg);
}
}
if (chg->micro_usb_mode)
smblib_micro_usb_plugin(chg, vbus_rising);
else
smblib_typec_usb_plugin(chg, vbus_rising);
power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
irq_data->name, vbus_rising ? "attached" : "detached");
@ -3615,6 +3603,8 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
{
int rc;
chg->cc2_detach_wa_active = false;
cancel_delayed_work_sync(&chg->pl_enable_work);
vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
@ -3699,24 +3689,6 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
if (rc < 0)
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
/*
* HW BUG - after cable is removed, medium or high rd reading
* falls to std. Use it for signal of typec cc detachment in
* software WA.
*/
if (chg->cc2_sink_detach_flag == CC2_SINK_MEDIUM_HIGH
&& pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
rc = smblib_masked_write(chg,
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
EXIT_SNK_BASED_ON_CC_BIT, 0);
if (rc < 0)
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n",
rc);
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n",
rising ? "rising" : "falling",
smblib_typec_mode_name[pval.intval]);
@ -3753,10 +3725,6 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
if (chg->micro_usb_mode)
return smblib_handle_usb_typec_change_for_uusb(chg);
/* WA - not when PD hard_reset WIP on cc2 in sink mode */
if (chg->cc2_sink_detach_flag == CC2_SINK_STD)
return IRQ_HANDLED;
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);
@ -3773,6 +3741,13 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT);
legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT);
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);
@ -3910,11 +3885,14 @@ static void clear_hdc_work(struct work_struct *work)
static void rdstd_cc2_detach_work(struct work_struct *work)
{
int rc;
u8 stat;
u8 stat4, stat5;
struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"};
struct smb_charger *chg = container_of(work, struct smb_charger,
rdstd_cc2_detach_work);
if (!chg->cc2_detach_wa_active)
return;
/*
* WA steps -
* 1. Enable both UFP and DFP, wait for 10ms.
@ -3922,7 +3900,7 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
* 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS
* and TIMER_STAGE bits are gone, otherwise repeat all by
* work rescheduling.
* Note, work will be cancelled when pd_hard_reset is 0.
* Note, work will be cancelled when USB_PLUGIN rises.
*/
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
@ -3945,30 +3923,34 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
usleep_range(30000, 31000);
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
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);
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return;
}
if (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
goto rerun;
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
if (rc < 0) {
smblib_err(chg,
"Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc);
return;
}
if (stat & TIMER_STAGE_2_BIT)
goto rerun;
/* Bingo, cc2 removal detected */
if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
|| (stat5 & TIMER_STAGE_2_BIT)) {
smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n",
(int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT),
(int)(stat5 & TIMER_STAGE_2_BIT));
goto rerun;
}
smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n");
chg->cc2_detach_wa_active = false;
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
EXIT_SNK_BASED_ON_CC_BIT, 0);
smblib_reg_block_restore(chg, cc2_detach_settings);
chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
irq_data.parent_data = chg;
smblib_handle_usb_typec_change(0, &irq_data);
return;
rerun:

View file

@ -71,13 +71,6 @@ enum smb_mode {
NUM_MODES,
};
enum cc2_sink_type {
CC2_SINK_NONE = 0,
CC2_SINK_STD,
CC2_SINK_MEDIUM_HIGH,
CC2_SINK_WA_DONE,
};
enum {
QC_CHARGER_DETECTION_WA_BIT = BIT(0),
BOOST_BACK_WA = BIT(1),
@ -313,10 +306,11 @@ struct smb_charger {
int default_icl_ua;
int otg_cl_ua;
bool uusb_apsd_rerun_done;
bool pd_hard_reset;
/* workaround flag */
u32 wa_flags;
enum cc2_sink_type cc2_sink_detach_flag;
bool cc2_detach_wa_active;
int boost_current_ua;
int temp_speed_reading_count;