summaryrefslogtreecommitdiff
path: root/common/usb_pd_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_pd_protocol.c')
-rw-r--r--common/usb_pd_protocol.c107
1 files changed, 105 insertions, 2 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 542d388a3e..a22d22fd4f 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -630,7 +630,19 @@ static inline void set_state(int port, enum pd_states next_state)
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
if (next_state != PD_STATE_DRP_AUTO_TOGGLE)
exit_low_power_mode(port);
-#endif
+
+#ifdef CONFIG_USBC_PPC
+ /* If we're entering DRP_AUTO_TOGGLE, there is no sink connected. */
+ if (next_state == PD_STATE_DRP_AUTO_TOGGLE) {
+ ppc_sink_is_connected(port, 0);
+ /*
+ * Clear the overcurrent event counter
+ * since we've detected a disconnect.
+ */
+ ppc_clear_oc_event_counter(port);
+ }
+#endif /* CONFIG_USBC_PPC */
+#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
#ifdef CONFIG_USB_PD_DUAL_ROLE
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
@@ -648,6 +660,23 @@ static inline void set_state(int port, enum pd_states next_state)
if (next_state == PD_STATE_SRC_DISCONNECTED ||
next_state == PD_STATE_SNK_DISCONNECTED) {
+#ifdef CONFIG_USBC_PPC
+ int cc1, cc2;
+
+ tcpm_get_cc(port, &cc1, &cc2);
+ /*
+ * Neither a debug accessory nor UFP attached.
+ * Tell the PPC module that there is no sink connected.
+ */
+ if (cc1 != TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) {
+ ppc_sink_is_connected(port, 0);
+ /*
+ * Clear the overcurrent event counter
+ * since we've detected a disconnect.
+ */
+ ppc_clear_oc_event_counter(port);
+ }
+#endif /* CONFIG_USBC_PPC */
/* Clear the input current limit */
pd_set_input_current_limit(port, 0, 0);
#ifdef CONFIG_CHARGE_MANAGER
@@ -2841,6 +2870,15 @@ void pd_task(void *u)
}
#endif
+#ifdef CONFIG_USBC_PPC
+ /*
+ * TODO: Useful for non-PPC cases as well, but only needed
+ * for PPC cases right now. Revisit later.
+ */
+ if (evt & PD_EVENT_SEND_HARD_RESET)
+ set_state(port, PD_STATE_HARD_RESET_SEND);
+#endif /* defined(CONFIG_USBC_PPC) */
+
/* process any potential incoming message */
incoming_packet = tcpm_has_pending_message(port);
if (incoming_packet) {
@@ -2985,9 +3023,23 @@ void pd_task(void *u)
if (get_time().val < pd[port].cc_debounce)
break;
- /* Debounce complete. UFP is attached */
+ /* Debounce complete */
+#ifdef CONFIG_USBC_PPC
+ /*
+ * If the port is latched off, just continue to
+ * monitor for a detach.
+ */
+ if (ppc_is_port_latched_off(port))
+ break;
+#endif /* CONFIG_USBC_PPC */
+
+ /* UFP is attached */
if (new_cc_state == PD_CC_UFP_ATTACHED ||
new_cc_state == PD_CC_DEBUG_ACC) {
+#ifdef CONFIG_USBC_PPC
+ /* Inform PPC that a sink is connected. */
+ ppc_sink_is_connected(port, 1);
+#endif /* CONFIG_USBC_PPC */
pd[port].polarity = (cc1 != TYPEC_CC_VOLT_RD);
set_polarity(port, pd[port].polarity);
@@ -4453,6 +4505,57 @@ void pd_update_contract(int port)
#endif /* CONFIG_USB_PD_DUAL_ROLE */
+#ifdef CONFIG_USBC_PPC
+static void pd_send_hard_reset(int port)
+{
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET, 0);
+}
+
+static uint32_t port_oc_reset_req;
+
+static void re_enable_ports(void)
+{
+ uint32_t ports = atomic_read_clear(&port_oc_reset_req);
+
+ while (ports) {
+ int port = __fls(ports);
+
+ ports &= ~(1 << port);
+
+ /*
+ * Let the board know that the overcurrent is
+ * over since we're going to attempt re-enabling
+ * the port.
+ */
+ board_overcurrent_event(port, 0);
+
+ pd_send_hard_reset(port);
+ /*
+ * TODO(b/117854867): PD3.0 to send an alert message
+ * indicating OCP after explicit contract.
+ */
+ }
+}
+DECLARE_DEFERRED(re_enable_ports);
+
+void pd_handle_overcurrent(int port)
+{
+ /* Keep track of the overcurrent events. */
+ CPRINTS("C%d: overcurrent!", port);
+#ifdef CONFIG_USB_PD_LOGGING
+ pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), PS_FAULT_OCP,
+ NULL);
+#endif /* defined(CONFIG_USB_PD_LOGGING) */
+ ppc_add_oc_event(port);
+ /* Let the board specific code know about the OC event. */
+ board_overcurrent_event(port, 1);
+
+ /* Wait 1s before trying to re-enable the port. */
+ atomic_or(&port_oc_reset_req, (1 << port));
+ hook_call_deferred(&re_enable_ports_data, SECOND);
+}
+#endif /* defined(CONFIG_USBC_PPC) */
+
static int command_pd(int argc, char **argv)
{
int port;