diff --git a/board/samus_pd/board.c b/board/samus_pd/board.c index 7c40859fbb..cb6431d6ff 100644 --- a/board/samus_pd/board.c +++ b/board/samus_pd/board.c @@ -11,12 +11,16 @@ #include "gpio.h" #include "hooks.h" #include "i2c.h" +#include "power.h" #include "registers.h" #include "task.h" #include "usb_pd.h" #include "usb_pd_config.h" #include "util.h" +/* Chipset power state */ +static enum power_state ps; + void vbus_evt(enum gpio_signal signal) { ccprintf("VBUS %d, %d!\n", signal, gpio_get_level(signal)); @@ -30,7 +34,32 @@ void bc12_evt(enum gpio_signal signal) void pch_evt(enum gpio_signal signal) { - ccprintf("PCH change %d!\n", signal); + /* Determine new chipset state, trigger corresponding hook */ + switch (ps) { + case POWER_S5: + if (gpio_get_level(GPIO_PCH_SLP_S5_L)) { + hook_notify(HOOK_CHIPSET_STARTUP); + ps = POWER_S3; + } + break; + case POWER_S3: + if (gpio_get_level(GPIO_PCH_SLP_S3_L)) { + hook_notify(HOOK_CHIPSET_RESUME); + ps = POWER_S0; + } else if (!gpio_get_level(GPIO_PCH_SLP_S5_L)) { + hook_notify(HOOK_CHIPSET_SHUTDOWN); + ps = POWER_S5; + } + break; + case POWER_S0: + if (!gpio_get_level(GPIO_PCH_SLP_S3_L)) { + hook_notify(HOOK_CHIPSET_SUSPEND); + ps = POWER_S3; + } + break; + default: + break; + } } void board_config_pre_init(void) @@ -59,6 +88,9 @@ void board_config_pre_init(void) /* Initialize board. */ static void board_init(void) { + int slp_s5 = gpio_get_level(GPIO_PCH_SLP_S5_L); + int slp_s3 = gpio_get_level(GPIO_PCH_SLP_S3_L); + /* * Enable CC lines after all GPIO have been initialized. Note, it is * important that this is enabled after the CC_ODL lines are set low @@ -68,6 +100,22 @@ static void board_init(void) /* Enable interrupts on VBUS transitions. */ gpio_enable_interrupt(GPIO_USB_C0_VBUS_WAKE); + + /* Determine initial chipset state */ + if (slp_s5 && slp_s3) { + hook_notify(HOOK_CHIPSET_RESUME); + ps = POWER_S0; + } else if (slp_s5 && !slp_s3) { + hook_notify(HOOK_CHIPSET_STARTUP); + ps = POWER_S3; + } else { + hook_notify(HOOK_CHIPSET_SHUTDOWN); + ps = POWER_S5; + } + + /* Enable interrupts on PCH state change */ + gpio_enable_interrupt(GPIO_PCH_SLP_S3_L); + gpio_enable_interrupt(GPIO_PCH_SLP_S5_L); } DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); diff --git a/board/samus_pd/usb_pd_policy.c b/board/samus_pd/usb_pd_policy.c index 60f6e1c21f..8e05c7014a 100644 --- a/board/samus_pd/usb_pd_policy.c +++ b/board/samus_pd/usb_pd_policy.c @@ -161,6 +161,28 @@ int pd_power_negotiation_allowed(void) return battery_ok; } +static void dual_role_on(void) +{ + pd_set_dual_role(PD_DRP_TOGGLE_ON); + CPRINTS("PCH -> S0, enable dual-role toggling"); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, dual_role_on, HOOK_PRIO_DEFAULT); + +static void dual_role_off(void) +{ + pd_set_dual_role(PD_DRP_TOGGLE_OFF); + CPRINTS("PCH -> S3, disable dual-role toggling"); +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, dual_role_off, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_STARTUP, dual_role_off, HOOK_PRIO_DEFAULT); + +static void dual_role_force_sink(void) +{ + pd_set_dual_role(PD_DRP_FORCE_SINK); + CPRINTS("PCH -> S5, force dual-role port to sink"); +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, dual_role_force_sink, HOOK_PRIO_DEFAULT); + static int command_ec_int(int argc, char **argv) { pd_send_ec_int(); diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 1e37446a2c..369529b9af 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -179,6 +179,11 @@ static const uint8_t dec4b5b[] = { #define PD_T_SOURCE_ACTIVITY (45*MSEC) /* between 40ms and 50ms */ #define PD_T_SENDER_RESPONSE (30*MSEC) /* between 24ms and 30ms */ #define PD_T_PS_TRANSITION (220*MSEC) /* between 200ms and 220ms */ +#define PD_T_DRP_HOLD (120*MSEC) /* between 100ms and 150ms */ +#define PD_T_DRP_LOCK (120*MSEC) /* between 100ms and 150ms */ +/* DRP_SNK + DRP_SRC must be between 50ms and 100ms with 30%-70% duty cycle */ +#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */ +#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */ /* Port role at startup */ #ifdef CONFIG_USB_PD_DUAL_ROLE @@ -194,6 +199,10 @@ static uint8_t pd_message_id; /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ uint8_t pd_polarity; +#ifdef CONFIG_USB_PD_DUAL_ROLE +static uint8_t pd_dual_role_toggle_on; +#endif + static enum { PD_STATE_DISABLED, #ifdef CONFIG_USB_PD_DUAL_ROLE @@ -771,6 +780,38 @@ static void execute_hard_reset(void) CPRINTF("HARD RESET!\n"); } +#ifdef CONFIG_USB_PD_DUAL_ROLE +void pd_set_dual_role(enum pd_dual_role_states dr_state) +{ + pd_dual_role_toggle_on = (dr_state == PD_DRP_TOGGLE_ON); + + /* Change to sink if in source disconnected state or if force sink */ + if (pd_role == PD_ROLE_SOURCE && + (pd_task_state == PD_STATE_SRC_DISCONNECTED || + dr_state == PD_DRP_FORCE_SINK)) { + pd_role = PD_ROLE_SINK; + pd_task_state = PD_STATE_SNK_DISCONNECTED; + pd_set_host_mode(0); + task_wake(TASK_ID_PD); + } +} +#endif + +/* Return flag for pd state is connected */ +static int pd_is_connected(void) +{ + if (pd_task_state == PD_STATE_DISABLED) + return 0; + +#ifdef CONFIG_USB_PD_DUAL_ROLE + /* Check if sink is connected */ + if (pd_role == PD_ROLE_SINK) + return pd_task_state != PD_STATE_SNK_DISCONNECTED; +#endif + /* Must be a source */ + return pd_task_state != PD_STATE_SRC_DISCONNECTED; +} + void pd_task(void) { int head; @@ -779,13 +820,20 @@ void pd_task(void) int timeout = 10*MSEC; int cc1_volt, cc2_volt; int res; +#ifdef CONFIG_USB_PD_DUAL_ROLE + uint64_t next_role_swap = PD_T_DRP_SNK; +#endif /* Ensure the power supply is in the default state */ pd_power_supply_reset(); while (1) { - /* monitor for incoming packet */ - pd_rx_enable_monitoring(); + /* monitor for incoming packet if in a connected state */ + if (pd_is_connected()) + pd_rx_enable_monitoring(); + else + pd_rx_disable_monitoring(); + /* Verify board specific health status : current, voltages... */ res = pd_board_checks(); if (res != EC_SUCCESS) { @@ -812,6 +860,8 @@ void pd_task(void) /* Nothing to do */ break; case PD_STATE_SRC_DISCONNECTED: + timeout = 10*MSEC; + /* Vnc monitoring */ cc1_volt = pd_adc_read(0); cc2_volt = pd_adc_read(1); @@ -822,8 +872,24 @@ void pd_task(void) /* Enable VBUS */ pd_set_power_supply_ready(); pd_task_state = PD_STATE_SRC_DISCOVERY; +#ifdef CONFIG_USB_PD_DUAL_ROLE + /* Keep VBUS up for the hold period */ + next_role_swap = get_time().val + PD_T_DRP_HOLD; +#endif } - timeout = 10*MSEC; +#ifdef CONFIG_USB_PD_DUAL_ROLE + /* Swap roles if time expired or VBUS is present */ + else if ((get_time().val >= next_role_swap || + pd_snk_is_vbus_provided())) { + pd_role = PD_ROLE_SINK; + pd_task_state = PD_STATE_SNK_DISCONNECTED; + pd_set_host_mode(0); + next_role_swap = get_time().val + PD_T_DRP_SNK; + + /* Swap states quickly */ + timeout = 2*MSEC; + } +#endif break; case PD_STATE_SRC_DISCOVERY: /* Query capabilites of the other side */ @@ -884,6 +950,8 @@ void pd_task(void) pd_hw_init(); break; case PD_STATE_SNK_DISCONNECTED: + timeout = 10*MSEC; + /* Source connection monitoring */ if (pd_snk_is_vbus_provided()) { cc1_volt = pd_adc_read(0); @@ -894,8 +962,18 @@ void pd_task(void) pd_select_polarity(pd_polarity); pd_task_state = PD_STATE_SNK_DISCOVERY; } + } else if (pd_dual_role_toggle_on && + get_time().val >= next_role_swap) { + /* Swap roles to source */ + pd_role = PD_ROLE_SOURCE; + pd_task_state = PD_STATE_SRC_DISCONNECTED; + pd_set_host_mode(1); + next_role_swap = get_time().val + PD_T_DRP_SRC; + + /* Swap states quickly */ + timeout = 2*MSEC; } - timeout = 50*MSEC; + break; case PD_STATE_SNK_DISCOVERY: /* Don't continue if power negotiation is not allowed */ @@ -947,11 +1025,19 @@ void pd_task(void) } /* Check for disconnection */ - if (pd_role == PD_ROLE_SOURCE && - pd_task_state != PD_STATE_SRC_DISCONNECTED) { + if (!pd_is_connected()) + continue; + if (pd_role == PD_ROLE_SOURCE) { /* Source: detect disconnect by monitoring CC */ cc1_volt = pd_adc_read(pd_polarity); +#ifdef CONFIG_USB_PD_DUAL_ROLE + if (cc1_volt > PD_SRC_VNC && + get_time().val >= next_role_swap) { + /* Stay a source port for lock period */ + next_role_swap = get_time().val + PD_T_DRP_LOCK; +#else if (cc1_volt > PD_SRC_VNC) { +#endif pd_power_supply_reset(); pd_task_state = PD_STATE_SRC_DISCONNECTED; /* Debouncing */ @@ -959,12 +1045,11 @@ void pd_task(void) } } #ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd_role == PD_ROLE_SINK && - pd_task_state != PD_STATE_SNK_DISCONNECTED && - !pd_snk_is_vbus_provided()) { + if (pd_role == PD_ROLE_SINK && !pd_snk_is_vbus_provided()) { /* Sink: detect disconnect by monitoring VBUS */ pd_task_state = PD_STATE_SNK_DISCONNECTED; - timeout = 50*MSEC; + /* set timeout small to reconnect fast */ + timeout = 5*MSEC; } #endif /* CONFIG_USB_PD_DUAL_ROLE */ } @@ -1037,6 +1122,21 @@ static int command_pd(int argc, char **argv) pd_set_host_mode(1); pd_task_state = PD_STATE_SRC_READY; task_wake(TASK_ID_PD); + } else if (!strcasecmp(argv[1], "dualrole")) { + int on; + char *e; + + if (argc < 3) { + ccprintf("dual-role toggling: %d\n", + pd_dual_role_toggle_on); + } else { + on = strtoi(argv[2], &e, 10); + if (*e) + return EC_ERROR_PARAM3; + + pd_set_dual_role(on ? PD_DRP_TOGGLE_ON : + PD_DRP_FORCE_SINK); + } } else if (!strncasecmp(argv[1], "state", 5)) { const char * const state_names[] = { "DISABLED", "SUSPENDED", diff --git a/include/usb_pd.h b/include/usb_pd.h index db8d214dc7..d1045980af 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -132,6 +132,23 @@ enum pd_errors { /* USB Vendor ID assigned to Google Inc. */ #define USB_VID_GOOGLE 0x18d1 +/* --- Protocol layer functions --- */ + +#ifdef CONFIG_USB_PD_DUAL_ROLE +enum pd_dual_role_states { + PD_DRP_TOGGLE_ON, + PD_DRP_TOGGLE_OFF, + PD_DRP_FORCE_SINK +}; +/** + * Set dual role state, from among enum pd_dual_role_states + * + * @param dr_state New state of dual-role port, selected from + * enum pd_dual_role_states + */ +void pd_set_dual_role(enum pd_dual_role_states dr_state); +#endif + /* --- Policy layer functions --- */ /**