diff options
author | Shawn Nematbakhsh <shawnn@chromium.org> | 2016-06-27 18:26:22 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-06-28 19:28:04 -0700 |
commit | ac8e2c3c5025dfd2bdac5faa9aebb18b3b48d9ac (patch) | |
tree | 6303f3b5a48f495a28797eb2b27f32ad46ea8916 | |
parent | 268237828a773c5797d3a8fc91acef8edc9b6818 (diff) | |
download | chrome-ec-ac8e2c3c5025dfd2bdac5faa9aebb18b3b48d9ac.tar.gz |
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 <shawnn@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/356513
Commit-Ready: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Reviewed-by: Joe Bauman <joe.bauman@fairchildsemi.com>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | driver/tcpm/fusb302.c | 116 |
1 files 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) { |