summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2018-08-22 12:12:39 -0600
committerchrome-bot <chrome-bot@chromium.org>2018-09-07 22:27:38 -0700
commit39ff41a291d16b794c98f0c371492786738f2c0a (patch)
tree10dd5dedef876f3c2090ecc6c5b3010aa894668e
parent74e613842277cf0d13af3fbad6105055bc039a04 (diff)
downloadchrome-ec-39ff41a291d16b794c98f0c371492786738f2c0a.tar.gz
usb-pd: disconnect USB SS MUX when S5 or lower
Especially for SS MUX that have redrivers in them, we should disable the MUX while the chipset is off because the data line will not be used. This give decent power savings for redriver MUXs (e.g. PS8751) BRANCH=none BUG=b:112136208,b:111196155 TEST=On Phaser the 3300_pd_a drops from 92mW to 32 mW when the charger is plugged into C1 and the SoC is in S5. The rail also says at 32mW after removing and plugging the power back in while the SoC is in S5. Also ensured that power is low upon first insertion and AP does not come on automatically. Change-Id: I0601fbb506ad3eff902cf6562a6408292ef70e3a Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1185485 Reviewed-by: Justin TerAvest <teravest@chromium.org>
-rw-r--r--common/usb_pd_protocol.c122
-rw-r--r--include/usb_pd.h4
2 files changed, 85 insertions, 41 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 3a4a2951b2..5c46774d85 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -1106,21 +1106,27 @@ static void handle_vdm_request(int port, int cnt, uint32_t *payload)
port, PD_VDO_VID(payload[0]), payload[0] & 0xFFFF);
}
-static void pd_set_data_role(int port, int role)
+static void set_usb_mux_with_current_data_role(int port)
{
- pd[port].data_role = role;
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role);
-#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */
- pd_execute_data_swap(port, role);
-
#ifdef CONFIG_USBC_SS_MUX
+ /*
+ * If the SoC is down, then we disconnect the MUX to save power since
+ * no one cares about the data lines.
+ */
+#ifdef CONFIG_POWER_COMMON
+ if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) {
+ usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT,
+ pd[port].polarity);
+ return;
+ }
+#endif /* CONFIG_POWER_COMMON */
+
#ifdef CONFIG_USBC_SS_MUX_DFP_ONLY
/*
* Need to connect SS mux for if new data role is DFP.
* If new data role is UFP, then disconnect the SS mux.
*/
- if (role == PD_ROLE_DFP)
+ if (pd[port].data_role == PD_ROLE_DFP)
usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
pd[port].polarity);
else
@@ -1129,8 +1135,19 @@ static void pd_set_data_role(int port, int role)
#else
usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
pd[port].polarity);
-#endif
-#endif
+#endif /* CONFIG_USBC_SS_MUX_DFP_ONLY */
+#endif /* CONFIG_USBC_SS_MUX */
+}
+
+static void pd_set_data_role(int port, int role)
+{
+ pd[port].data_role = role;
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+ pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role);
+#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */
+ pd_execute_data_swap(port, role);
+
+ set_usb_mux_with_current_data_role(port);
pd_update_roles(port);
}
@@ -1997,6 +2014,38 @@ 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 */
+static void exit_dp_mode(int port)
+{
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
+ int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
+
+ if (opos <= 0)
+ return;
+
+ CPRINTS("C%d Exiting DP mode", port);
+ if (!pd_dfp_exit_mode(port, USB_SID_DISPLAYPORT, opos))
+ return;
+ pd_send_vdm(port, USB_SID_DISPLAYPORT,
+ CMD_EXIT_MODE | VDO_OPOS(opos), NULL, 0);
+ pd_vdm_send_state_machine(port);
+ /* Have to wait for ACK */
+#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
+}
+#endif /* CONFIG_POWER_COMMON */
+
+#ifdef CONFIG_POWER_COMMON
+static void handle_new_power_state(int port)
+{
+ if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF))
+ /* The SoC will negotiated DP mode again when it boots up */
+ exit_dp_mode(port);
+
+ /* Ensure mux is set properly after chipset transition */
+ set_usb_mux_with_current_data_role(port);
+}
+#endif /* CONFIG_POWER_COMMON */
+
#ifdef CONFIG_USB_PD_DUAL_ROLE
enum pd_dual_role_states pd_get_dual_role(int port)
{
@@ -2047,39 +2096,26 @@ static void pd_update_try_source(void)
DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT);
#endif
-void pd_set_dual_role(int port, enum pd_dual_role_states state)
+static inline void pd_set_dual_role_no_wakeup(int port,
+ enum pd_dual_role_states state)
{
- int i;
-
drp_state[port] = state;
#ifdef CONFIG_USB_PD_TRY_SRC
pd_update_try_source();
#endif
-
- /* Inform PD tasks of dual role change. */
- for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++)
- task_set_event(PD_PORT_TO_TASK_ID(i),
- PD_EVENT_UPDATE_DUAL_ROLE, 0);
}
-static void exit_dp_mode(int port)
+void pd_set_dual_role(int port, enum pd_dual_role_states state)
{
-#ifdef CONFIG_USB_PD_ALT_MODE_DFP
- int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
- if (opos <= 0)
- return;
- CPRINTS("C%d Exiting DP mode", port);
- if (!pd_dfp_exit_mode(port, USB_SID_DISPLAYPORT, opos))
- return;
- pd_send_vdm(port, USB_SID_DISPLAYPORT,
- CMD_EXIT_MODE | VDO_OPOS(opos), NULL, 0);
- pd_vdm_send_state_machine(port);
- /* Have to wait for ACK */
-#endif
+ pd_set_dual_role_no_wakeup(port, state);
+
+ /* Wake task up to process change */
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_UPDATE_DUAL_ROLE, 0);
}
-void pd_update_dual_role_config(int port)
+static void pd_update_dual_role_config(int port)
{
/*
* Change to sink if port is currently a source AND (new DRP
@@ -2470,7 +2506,8 @@ void pd_task(void *u)
* present. This flag is used to maintain a PD connection after a
* reset by sending a soft reset.
*/
- pd[port].flags = pd_is_vbus_present(port) ? PD_FLAGS_VBUS_NEVER_LOW : 0;
+ pd[port].flags |=
+ pd_is_vbus_present(port) ? PD_FLAGS_VBUS_NEVER_LOW : 0;
#endif
/* Disable TCPC RX until connection is established */
@@ -2580,10 +2617,11 @@ void pd_task(void *u)
if (evt & PD_EVENT_DEVICE_ACCESSED)
handle_device_access(port);
#endif
-
+#ifdef CONFIG_POWER_COMMON
+ if (evt & PD_EVENT_POWER_STATE_CHANGE)
+ handle_new_power_state(port);
+#endif
#ifdef CONFIG_USB_PD_DUAL_ROLE
- if (evt & PD_EVENT_DP_DISCONNECT)
- exit_dp_mode(port);
if (evt & PD_EVENT_UPDATE_DUAL_ROLE)
pd_update_dual_role_config(port);
#endif
@@ -3944,8 +3982,12 @@ static void pd_chipset_startup(void)
int i;
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) {
- pd_set_dual_role(i, PD_DRP_TOGGLE_OFF);
+ pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF);
pd[i].flags |= PD_FLAGS_CHECK_IDENTITY;
+ task_set_event(PD_PORT_TO_TASK_ID(i),
+ PD_EVENT_POWER_STATE_CHANGE |
+ PD_EVENT_UPDATE_DUAL_ROLE,
+ 0);
}
CPRINTS("PD:S5->S3");
}
@@ -3956,9 +3998,11 @@ static void pd_chipset_shutdown(void)
int i;
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) {
+ pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK);
task_set_event(PD_PORT_TO_TASK_ID(i),
- PD_EVENT_DP_DISCONNECT, 0);
- pd_set_dual_role(i, PD_DRP_FORCE_SINK);
+ PD_EVENT_POWER_STATE_CHANGE |
+ PD_EVENT_UPDATE_DUAL_ROLE,
+ 0);
}
CPRINTS("PD:S3->S5");
}
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 64b9d51c82..0bf3b15ffe 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -46,8 +46,8 @@ enum pd_rx_errors {
* A task, other than the task owning the PD port, accessed the TCPC. The task
* that owns the port does not send itself this event.
*/
-#define PD_EVENT_DEVICE_ACCESSED (1<<7)
-#define PD_EVENT_DP_DISCONNECT (1<<8) /* DisplayPort disconnect requested */
+#define PD_EVENT_DEVICE_ACCESSED (1<<7)
+#define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */
/* --- PD data message helpers --- */
#define PDO_MAX_OBJECTS 7