From ac8e2c3c5025dfd2bdac5faa9aebb18b3b48d9ac Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Mon, 27 Jun 2016 18:26:22 -0700 Subject: tcpm: fusb302: Avoid unwanted CC PU changes If set_cc() is called, our toggle interrupt may still be active. Since alert() is called from the pdcmd task and set_cc() is called from the pd tasks, an unwanted interrupt may fire and override our desired CC settings. BUG=chrome-os-partner:54786 BRANCH=None TEST=Manual on gru. Rapidly attach + detach DP dongle, verify we don't get stuck in SNK_DISCONNECTED_DEBOUNCE. Change-Id: Ib60123c45d9a3a78243a3347377fb2190cbdf94b Signed-off-by: Shawn Nematbakhsh Reviewed-on: https://chromium-review.googlesource.com/356513 Commit-Ready: Shawn N Tested-by: Shawn N Reviewed-by: Joe Bauman Reviewed-by: Vincent Palatin --- driver/tcpm/fusb302.c | 116 +++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/driver/tcpm/fusb302.c b/driver/tcpm/fusb302.c index ac9c0f3ac2..d2c6ee3e04 100644 --- a/driver/tcpm/fusb302.c +++ b/driver/tcpm/fusb302.c @@ -29,6 +29,7 @@ static struct fusb302_chip_state { int togdone_pullup_cc2; int tx_hard_reset_req; int device_id; + struct mutex set_cc_lock; } state[CONFIG_USB_PD_PORT_COUNT]; /* bring the FUSB302 out of reset after Hard Reset signaling */ @@ -444,6 +445,14 @@ static int fusb302_tcpm_set_cc(int port, int pull) { int reg; + /* + * Ensure we aren't in the process of changing CC from the alert + * handler, then cancel any pending toggle-triggered CC change. + */ + mutex_lock(&state[port].set_cc_lock); + state[port].dfp_toggling_on = 0; + mutex_unlock(&state[port].set_cc_lock); + state[port].previous_pull = pull; /* NOTE: FUSB302 toggles a single pull-up between CC1 and CC2 */ @@ -858,66 +867,67 @@ void fusb302_tcpc_alert(int port) } if (interrupta & TCPC_REG_INTERRUPTA_TOGDONE) { - /* toggle done */ - state[port].dfp_toggling_on = 0; - - /* read what 302 settled on for an answer...*/ - tcpc_read(port, TCPC_REG_STATUS1A, ®); - reg = reg >> TCPC_REG_STATUS1A_TOGSS_POS; - reg = reg & TCPC_REG_STATUS1A_TOGSS_MASK; - - toggle_answer = reg; + /* Don't allow other tasks to change CC PUs */ + mutex_lock(&state[port].set_cc_lock); + /* If our toggle request is obsolete then we're done */ + if (state[port].dfp_toggling_on) { + /* read what 302 settled on for an answer...*/ + tcpc_read(port, TCPC_REG_STATUS1A, ®); + reg = reg >> TCPC_REG_STATUS1A_TOGSS_POS; + reg = reg & TCPC_REG_STATUS1A_TOGSS_MASK; + + toggle_answer = reg; + + /* Turn off toggle so we can take over the switches */ + tcpc_read(port, TCPC_REG_CONTROL2, ®); + reg &= ~TCPC_REG_CONTROL2_TOGGLE; + tcpc_write(port, TCPC_REG_CONTROL2, reg); - /* Turn off toggle so we can take over the switches again */ - tcpc_read(port, TCPC_REG_CONTROL2, ®); - reg &= ~TCPC_REG_CONTROL2_TOGGLE; - tcpc_write(port, TCPC_REG_CONTROL2, reg); + switch (toggle_answer) { + case TCPC_REG_STATUS1A_TOGSS_SRC1: + state[port].togdone_pullup_cc1 = 1; + state[port].togdone_pullup_cc2 = 0; + break; + case TCPC_REG_STATUS1A_TOGSS_SRC2: + state[port].togdone_pullup_cc1 = 0; + state[port].togdone_pullup_cc2 = 1; + break; + case TCPC_REG_STATUS1A_TOGSS_SNK1: + case TCPC_REG_STATUS1A_TOGSS_SNK2: + case TCPC_REG_STATUS1A_TOGSS_AA: + state[port].togdone_pullup_cc1 = 0; + state[port].togdone_pullup_cc2 = 0; + break; + default: + /* TODO: should never get here, but? */ + ASSERT(0); + break; + } - switch (toggle_answer) { - case TCPC_REG_STATUS1A_TOGSS_SRC1: - state[port].togdone_pullup_cc1 = 1; - state[port].togdone_pullup_cc2 = 0; - break; - case TCPC_REG_STATUS1A_TOGSS_SRC2: - state[port].togdone_pullup_cc1 = 0; - state[port].togdone_pullup_cc2 = 1; - break; - case TCPC_REG_STATUS1A_TOGSS_SNK1: - state[port].togdone_pullup_cc1 = 0; - state[port].togdone_pullup_cc2 = 0; - break; - case TCPC_REG_STATUS1A_TOGSS_SNK2: - state[port].togdone_pullup_cc1 = 0; - state[port].togdone_pullup_cc2 = 0; - break; - case TCPC_REG_STATUS1A_TOGSS_AA: - state[port].togdone_pullup_cc1 = 0; - state[port].togdone_pullup_cc2 = 0; - break; - default: - /* TODO: should never get here, but? */ - break; - } + /* enable the pull-up we know to be necessary */ + tcpc_read(port, TCPC_REG_SWITCHES0, ®); - /* enable the pull-up we know to be necessary */ - tcpc_read(port, TCPC_REG_SWITCHES0, ®); + reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN | + TCPC_REG_SWITCHES0_CC1_PU_EN | + TCPC_REG_SWITCHES0_CC1_PD_EN | + TCPC_REG_SWITCHES0_CC2_PD_EN); - reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN); - reg &= ~(TCPC_REG_SWITCHES0_CC1_PU_EN); - reg &= ~TCPC_REG_SWITCHES0_CC1_PD_EN; - reg &= ~TCPC_REG_SWITCHES0_CC2_PD_EN; + if (state[port].device_id == FUSB302_DEVID_302A) { + if (state[port].togdone_pullup_cc1) + reg |= TCPC_REG_SWITCHES0_CC1_PU_EN; + else + reg |= TCPC_REG_SWITCHES0_CC2_PU_EN; + } else { + reg |= TCPC_REG_SWITCHES0_CC1_PU_EN | + TCPC_REG_SWITCHES0_CC2_PU_EN; + } - if (state[port].device_id == FUSB302_DEVID_302A) { - if (state[port].togdone_pullup_cc1) - reg |= TCPC_REG_SWITCHES0_CC1_PU_EN; - else - reg |= TCPC_REG_SWITCHES0_CC2_PU_EN; - } else { - reg |= TCPC_REG_SWITCHES0_CC1_PU_EN | - TCPC_REG_SWITCHES0_CC2_PU_EN; + tcpc_write(port, TCPC_REG_SWITCHES0, reg); + /* toggle done */ + state[port].dfp_toggling_on = 0; } - tcpc_write(port, TCPC_REG_SWITCHES0, reg); + mutex_unlock(&state[port].set_cc_lock); } if (interrupta & TCPC_REG_INTERRUPTA_RETRYFAIL) { -- cgit v1.2.1