From f8206fabf1cefaccd4e4393588ae2e3613bfb97d Mon Sep 17 00:00:00 2001 From: Diana Z Date: Wed, 6 Mar 2019 11:40:27 -0700 Subject: USB PD: Re-discover port partner when a contract was in place When the pd_task starts up with an explicit contract previously in place, re-check the partner's identity. This will happen automatically when we EC reset into RO since pd_chipset_startup sets the flag, but for a RO->RW jump the flag needs to be set again. Additionally, exit DP modes before sysjumping, in order to not confuse the port partner with a second enter mode when it had previously been in that mode. BUG=b:125552060 BRANCH=octopus TEST=on unlocked octopus board, plugged in powered HDMI dongle from hibernate state and confirmed display worked after RO->RW jump. Also turned off software sync and confirmed console "sysjump" worked. Change-Id: Idcde6f04deeb8f409a9b4d0a4b3fc924bdb644c7 Signed-off-by: Diana Z Reviewed-on: https://chromium-review.googlesource.com/1506434 Reviewed-by: Aseda Aboagye --- common/system.c | 5 +++++ common/usb_pd_protocol.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++- include/task.h | 5 ++++- include/usb_pd.h | 12 ++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/common/system.c b/common/system.c index 7b91b84eba..813c0b6399 100644 --- a/common/system.c +++ b/common/system.c @@ -554,6 +554,11 @@ static void jump_to_image(uintptr_t init_addr) usleep(MSEC); gpio_set_level(GPIO_ENTERING_RW, 0); +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + /* Note: must be before i2c module is locked down */ + pd_prepare_sysjump(); +#endif + #ifdef CONFIG_I2C_MASTER /* Prepare I2C module for sysjump */ i2c_prepare_sysjump(); diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 389c43a8a2..ff9d710e64 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -152,6 +152,11 @@ static const uint8_t vdo_ver[] = { #define VDO_VER(v) VDM_VER10 #endif +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +/* Tracker for which task is waiting on sysjump prep to finish */ +static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; +#endif + static struct pd_protocol { /* current port power role (SOURCE or SINK) */ uint8_t power_role; @@ -2165,7 +2170,7 @@ int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, return 0; } -#ifdef CONFIG_POWER_COMMON /* Needed b/c CONFIG_POWER_COMMON is only caller */ +#if defined(CONFIG_POWER_COMMON) || defined(CONFIG_USB_PD_ALT_MODE_DFP) static void exit_dp_mode(int port) { #ifdef CONFIG_USB_PD_ALT_MODE_DFP @@ -2767,6 +2772,12 @@ void pd_task(void *u) */ this_state = PD_STATE_SOFT_RESET; + /* + * Re-discover any alternate modes we may have been + * using with this port partner. + */ + pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; + /* * Set the TCPC reset event such that we can set our CC * terminations, determine polarity, and enable RX so we @@ -2859,6 +2870,20 @@ void pd_task(void *u) if (evt & PD_EVENT_POWER_STATE_CHANGE) handle_new_power_state(port); #endif + +#if defined(CONFIG_USB_PD_ALT_MODE_DFP) + if (evt & PD_EVENT_SYSJUMP) { + exit_dp_mode(port); + /* + * If event was set from pd_prepare_sysjump, wake the + * task waiting on us to complete. + */ + if (sysjump_task_waiting != TASK_ID_INVALID) + task_set_event(sysjump_task_waiting, + TASK_EVENT_SYSJUMP_READY, 0); + } +#endif + #ifdef CONFIG_USB_PD_DUAL_ROLE if (evt & PD_EVENT_UPDATE_DUAL_ROLE) pd_update_dual_role_config(port); @@ -4371,6 +4396,28 @@ DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); #endif /* CONFIG_USB_PD_DUAL_ROLE */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +void pd_prepare_sysjump(void) +{ + int i; + + /* Exit modes before sysjump so we can cleanly enter again later */ + for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) { + /* + * We can't be in an alternate mode if PD comm is disabled, so + * no need to send the event + */ + if (!pd_comm_is_enabled(i)) + continue; + + sysjump_task_waiting = task_get_current(); + task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_SYSJUMP, 0); + task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1); + sysjump_task_waiting = TASK_ID_INVALID; + } +} +#endif + #ifdef CONFIG_COMMON_RUNTIME /* diff --git a/include/task.h b/include/task.h index 46a340b898..32e62a8384 100644 --- a/include/task.h +++ b/include/task.h @@ -14,7 +14,10 @@ /* Task event bitmasks */ /* Tasks may use the bits in TASK_EVENT_CUSTOM for their own events */ -#define TASK_EVENT_CUSTOM(x) (x & 0x0001ffff) +#define TASK_EVENT_CUSTOM(x) (x & 0x0000ffff) + +/* Used to signal that sysjump preparation has completed */ +#define TASK_EVENT_SYSJUMP_READY BIT(16) /* Used to signal that IPC layer is available for sending new data */ #define TASK_EVENT_IPC_READY BIT(17) diff --git a/include/usb_pd.h b/include/usb_pd.h index b3888dae7f..a8f1dab008 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -50,6 +50,7 @@ enum pd_rx_errors { #define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */ #define PD_EVENT_SEND_HARD_RESET (1<<9) /* Issue a Hard Reset. */ #define PD_EVENT_SM (1<<10) /* PD State machine event */ +#define PD_EVENT_SYSJUMP (1<<11) /* Prepare for sysjump */ /* Ensure TCPC is out of low power mode before handling these events. */ #define PD_EXIT_LOW_POWER_EVENT_MASK \ @@ -1978,4 +1979,15 @@ static inline void pd_log_event(uint8_t type, uint8_t size_port, static inline int pd_vdm_get_log_entry(uint32_t *payload) { return 0; } #endif /* CONFIG_USB_PD_LOGGING */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +/** + * Prepare for a sysjump by exiting any alternate modes, if PD communication is + * allowed. + * + * Note: this call will block until the PD task has finished its exit mode and + * re-awoken the calling task. + */ +void pd_prepare_sysjump(void); +#endif + #endif /* __CROS_EC_USB_PD_H */ -- cgit v1.2.1