diff options
author | Jett Rink <jettrink@chromium.org> | 2018-06-28 15:31:40 -0600 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-07-18 18:15:26 -0700 |
commit | 16a8f8f4fa5bce677f202ff78892f3f50978065b (patch) | |
tree | 5b2b2b9bbe9c79632b00dc1c1d893a598add556a /common | |
parent | d22bdeeaecd864835eacb9c22e05f09050fb0237 (diff) | |
download | chrome-ec-16a8f8f4fa5bce677f202ff78892f3f50978065b.tar.gz |
tcpc: debounce entry into low-power mode
We need to keep track of the low-power mode hardware state for each TCPC
so we can put a TCPC back into low power mode when it exits low power
mode before the software TCPM state machine wants it out of low power
mode.
This change also breaks the low power mode entry out of the drp_toggle
method into its own method: enter_low_power_mode.
BRANCH=none
BUG=b:77544959
TEST=Verified Analogix does not get into low-power mode loop. Tested
other SRC/SNK capabilities as well. Tested the device will go back into
low power mode if the AP access the TCPC via the 'ectool usbpdmuxinfo'
command.
Change-Id: I2fdefeda2bf13c2b79d988f0017629115438d313
Signed-off-by: Jett Rink <jettrink@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1119255
Reviewed-by: Scott Collyer <scollyer@chromium.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/usb_pd_protocol.c | 95 |
1 files changed, 86 insertions, 9 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 42742888a2..03ed5b777c 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -351,6 +351,54 @@ static void set_vconn(int port, int enable) } #endif /* defined(CONFIG_USBC_VCONN) */ +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + +/* 10 ms is enough time for any TCPC transaction to complete. */ +#define PD_LPM_DEBOUNCE_US (10 * MSEC) +static timestamp_t lpm_debounce_deadlines[CONFIG_USB_PD_PORT_COUNT]; + +/* This is only called from the PD tasks that owns the port. */ +static void handle_device_access(int port) +{ + lpm_debounce_deadlines[port].val = get_time().val + PD_LPM_DEBOUNCE_US; + if (pd[port].flags & PD_FLAGS_LPM_ENGAGED) { + CPRINTS("TCPC p%d Exited Low Power Mode via bus access", port); + pd[port].flags &= ~PD_FLAGS_LPM_ENGAGED; + } +} + +/* This can be called from any task. */ +void pd_device_accessed(int port) +{ + /* If not in the PD TASK that owns data, marshal to that task */ + if (task_get_current() == PD_PORT_TO_TASK_ID(port)) + handle_device_access(port); + else + task_set_event(PD_PORT_TO_TASK_ID(port), + PD_EVENT_DEVICE_ACCESSED, 0); +} + +/* This is only called from the PD tasks that owns the port. */ +static void request_low_power_mode(int port, int enable) +{ + if (enable) + pd[port].flags |= PD_FLAGS_LPM_REQUESTED; + else + pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; +} +#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ + +/* Local convenience method for two method currently always called together. */ +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE +static void pd_set_drp_toggle(int port, int enable) +{ + tcpm_set_drp_toggle(port, enable); +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + request_low_power_mode(port, enable); +#endif +} +#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ + #ifdef CONFIG_USB_PD_DUAL_ROLE static int get_bbram_idx(int port) { @@ -1920,7 +1968,7 @@ void pd_update_dual_role_config(int port) #if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \ defined(CONFIG_USB_PD_TCPC_LOW_POWER) /* When switching drp mode, make sure tcpc is out of standby mode */ - tcpm_set_drp_toggle(port, 0); + pd_set_drp_toggle(port, 0); #endif } @@ -2333,6 +2381,11 @@ void pd_task(void *u) /* wait for next event/packet or timeout expiration */ evt = task_wait_event(timeout); +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + if (evt & PD_EVENT_DEVICE_ACCESSED) + handle_device_access(port); +#endif + #ifdef CONFIG_USB_PD_DUAL_ROLE if (evt & PD_EVENT_UPDATE_DUAL_ROLE) pd_update_dual_role_config(port); @@ -2347,6 +2400,10 @@ void pd_task(void *u) #else /* if TCPC has reset, then need to initialize it again */ if (evt & PD_EVENT_TCPC_RESET) { +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + /* Reset LPM software state after a reset */ + request_low_power_mode(port, 0); +#endif CPRINTS("TCPC p%d reset!", port); if (tcpm_init(port) != EC_SUCCESS) CPRINTS("TCPC p%d init failed", port); @@ -3508,6 +3565,15 @@ void pd_task(void *u) assert(auto_toggle_supported); + /* + * If SW decided we should be in a low power state and + * the CC lines did not change, then don't talk with the + * TCPC otherwise we might wake it up. + */ + if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && + !(evt & PD_EVENT_CC)) + break; + /* Check for connection */ tcpm_get_cc(port, &cc1, &cc2); @@ -3533,10 +3599,7 @@ void pd_task(void *u) next_state = PD_STATE_DRP_AUTO_TOGGLE; if (next_state != PD_STATE_DRP_AUTO_TOGGLE) { - tcpm_set_drp_toggle(port, 0); -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - CPRINTS("TCPC p%d Exit Low Power Mode", port); -#endif + pd_set_drp_toggle(port, 0); } if (next_state == PD_STATE_SNK_DISCONNECTED) { @@ -3548,12 +3611,9 @@ void pd_task(void *u) pd_set_power_role(port, PD_ROLE_SOURCE); timeout = 2*MSEC; } else { - tcpm_set_drp_toggle(port, 1); + pd_set_drp_toggle(port, 1); pd[port].flags |= PD_FLAGS_TCPC_DRP_TOGGLE; timeout = -1; -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - CPRINTS("TCPC p%d Low Power Mode", port); -#endif } set_state(port, next_state); @@ -3581,6 +3641,23 @@ void pd_task(void *u) } } +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + /* Determine if we need to put the TCPC in low power mode */ + if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && + !(pd[port].flags & PD_FLAGS_LPM_ENGAGED)) { + const int64_t time_left = + lpm_debounce_deadlines[port].val - now.val; + if (time_left <= 0) { + pd[port].flags |= PD_FLAGS_LPM_ENGAGED; + tcpm_enter_low_power_mode(port); + CPRINTS("TCPC p%d Enter Low Power Mode", port); + timeout = -1; + } else if (timeout < 0 || timeout > time_left) { + timeout = time_left; + } + } +#endif + /* Check for disconnection if we're connected */ if (!pd_is_connected(port)) continue; |