summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAseda Aboagye <aaboagye@google.com>2017-12-04 15:14:33 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-01-17 20:20:26 -0800
commitb264288756ab77c2ef7950b19bcca99a15e2d5ce (patch)
tree3f00ada91c2adc40fc14709670950a08cec3286d
parent66d7782a7b20fd974b18baeefb5e79feae80fb40 (diff)
downloadchrome-ec-b264288756ab77c2ef7950b19bcca99a15e2d5ce.tar.gz
USB PD: PPC: Add overcurrent handling.
The Type-C Power Path Controllers provide overcurrent protection. This commit adds support into the USB PD task for overcurrent events while we are in source role. The USB PD 3.0 spec recommends that ports issue a hard reset when an overcurrent condition occurs on a port. Additionally, we'll allow a source port to overcurrent 3 times before latching off VBUS from the port entirely. The source path will be re-enabled after ~1s after each overcurrent event. BUG=b:69935262,b:114680657 BRANCH=None TEST=Boot to ChromeOS in grabbiter. No overcurrent events reported when the sink is drawing <= 3.20 A. Overcurrent events are reported when the sink is drawing > 3.25 A. After 3 reports, the port is latched off and power delivery is stopped. The port is re-enabled only after the sink is disconnected. Also when the sink is drawing current at 3.24 A, there is one report of overcurrent. The port gets disabled in response to that event. But the port is re-enabled after 1 second since overcurrent event is reported only once. After the port is re-enabled, the sink is able to draw the set current. When the overcurrent event is reported, I can see in the kernel logs that the overcurrent condition is detected by the kernel. EC Logs: [3391.984462 C1: PPC detected Vbus overcurrent!] [3391.984953 C1: overcurrent!] [3392.044935 C1: PPC detected Vbus overcurrent!] [3392.045425 C1: overcurrent!] [3392.061404 C1: PPC detected Vbus overcurrent!] [3392.061894 C1: overcurrent!] [3392.062142 C1: OC event limit reached! Source path disabled until physical disconnect.] [3392.077226 C1: PPC detected Vbus overcurrent!] [3392.077532 C1: overcurrent!] [3392.077891 C1: OC event limit reached! Source path disabled until physical disconnect.] [3392.092660 C1: PPC detected Vbus overcurrent!] [3392.092966 C1: overcurrent!] [3392.093213 C1: OC event limit reached! Source path disabled until physical disconnect.] Kernel Logs: [ 3356.560456] usb usb2-port1: over-current condition [ 3356.768434] usb usb2-port2: over-current condition [ 3356.976446] usb usb2-port4: over-current condition [ 3357.184441] usb usb2-port5: over-current condition [ 3357.392445] usb usb2-port6: over-current condition Change-Id: Ib070f261e98264cd88725ebce7d10e0798267e3b Signed-off-by: Aseda Aboagye <aaboagye@google.com> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/1286300 Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org> Commit-Queue: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/807633 Commit-Ready: Karthikeyan Ramasubramanian <kramasub@chromium.org> Tested-by: Karthikeyan Ramasubramanian <kramasub@chromium.org> Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
-rw-r--r--board/aleena/board.c6
-rw-r--r--board/ampton/board.c2
-rw-r--r--board/bip/board.c2
-rw-r--r--board/careena/board.c6
-rw-r--r--board/cheza/board.c4
-rw-r--r--board/delan/board.c6
-rw-r--r--board/grunt/board.c6
-rw-r--r--board/liara/board.c6
-rw-r--r--board/nocturne/board.c13
-rw-r--r--common/usb_pd_protocol.c107
-rw-r--r--common/usbc_ppc.c99
-rw-r--r--driver/ppc/sn5s330.c5
-rw-r--r--include/usb_pd.h9
-rw-r--r--include/usbc_ppc.h74
14 files changed, 307 insertions, 38 deletions
diff --git a/board/aleena/board.c b/board/aleena/board.c
index 5ba79d81fa..5c235b7cad 100644
--- a/board/aleena/board.c
+++ b/board/aleena/board.c
@@ -116,12 +116,14 @@ void board_update_sensor_config_from_sku(void)
}
}
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
enum gpio_signal signal = (port == 0) ? GPIO_USB_C0_OC_L
: GPIO_USB_C1_OC_L;
+ /* Note that the levels are inverted because the pin is active low. */
+ int lvl = is_overcurrented ? 0 : 1;
- gpio_set_level(signal, 0);
+ gpio_set_level(signal, lvl);
CPRINTS("p%d: overcurrent!", port);
}
diff --git a/board/ampton/board.c b/board/ampton/board.c
index 5f9c2a41cb..941aab7f00 100644
--- a/board/ampton/board.c
+++ b/board/ampton/board.c
@@ -316,7 +316,7 @@ void board_hibernate_late(void)
gpio_set_flags_by_mask(GPIO_KSI, 0xff, GPIO_INPUT);
}
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
/* TODO(b/78344554): pass this signal upstream once hardware reworked */
cprints(CC_USBPD, "p%d: overcurrent!", port);
diff --git a/board/bip/board.c b/board/bip/board.c
index 318a5bc178..fb4ff5e39a 100644
--- a/board/bip/board.c
+++ b/board/bip/board.c
@@ -125,7 +125,7 @@ const struct spi_device_t spi_devices[] = {
};
const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
/* TODO(b/78344554): pass this signal upstream once hardware reworked */
cprints(CC_USBPD, "p%d: overcurrent!", port);
diff --git a/board/careena/board.c b/board/careena/board.c
index 380a936fd1..0774cd9adc 100644
--- a/board/careena/board.c
+++ b/board/careena/board.c
@@ -101,12 +101,14 @@ const struct pwm_t pwm_channels[] = {
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
enum gpio_signal signal = (port == 0) ? GPIO_USB_C0_OC_L
: GPIO_USB_C1_OC_L;
+ /* Note that the levels are inverted because the pin is active low. */
+ int lvl = is_overcurrented ? 0 : 1;
- gpio_set_level(signal, 0);
+ gpio_set_level(signal, lvl);
CPRINTS("p%d: overcurrent!", port);
}
diff --git a/board/cheza/board.c b/board/cheza/board.c
index fffdedf707..66121fc3bb 100644
--- a/board/cheza/board.c
+++ b/board/cheza/board.c
@@ -129,7 +129,7 @@ static void ppc_interrupt(enum gpio_signal signal)
static void usb1_oc_evt_deferred(void)
{
/* Only port-1 has overcurrent GPIO interrupt */
- board_overcurrent_event(0);
+ board_overcurrent_event(0, 1);
}
DECLARE_DEFERRED(usb1_oc_evt_deferred);
@@ -509,7 +509,7 @@ int board_is_sourcing_vbus(int port)
return EC_ERROR_INVAL;
}
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
/* TODO(b/120231371): Notify AP */
CPRINTS("p%d: overcurrent!", port);
diff --git a/board/delan/board.c b/board/delan/board.c
index f5b5f5b2dd..4304ce25e4 100644
--- a/board/delan/board.c
+++ b/board/delan/board.c
@@ -114,12 +114,14 @@ const struct pwm_t pwm_channels[] = {
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
enum gpio_signal signal = (port == 0) ? GPIO_USB_C0_OC_L
: GPIO_USB_C1_OC_L;
+ /* Note that the levels are inverted because the pin is active low. */
+ int lvl = is_overcurrented ? 0 : 1;
- gpio_set_level(signal, 0);
+ gpio_set_level(signal, lvl);
CPRINTS("p%d: overcurrent!", port);
}
diff --git a/board/grunt/board.c b/board/grunt/board.c
index f60e3be18a..13e062589a 100644
--- a/board/grunt/board.c
+++ b/board/grunt/board.c
@@ -124,12 +124,14 @@ void board_update_sensor_config_from_sku(void)
gpio_enable_interrupt(GPIO_6AXIS_INT_L);
}
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
enum gpio_signal signal = (port == 0) ? GPIO_USB_C0_OC_L
: GPIO_USB_C1_OC_L;
+ /* Note that the levels are inverted because the pin is active low. */
+ int lvl = is_overcurrented ? 0 : 1;
- gpio_set_level(signal, 0);
+ gpio_set_level(signal, lvl);
CPRINTS("p%d: overcurrent!", port);
}
diff --git a/board/liara/board.c b/board/liara/board.c
index 0e8a9abb5e..67e79d76c1 100644
--- a/board/liara/board.c
+++ b/board/liara/board.c
@@ -114,12 +114,14 @@ const struct pwm_t pwm_channels[] = {
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
enum gpio_signal signal = (port == 0) ? GPIO_USB_C0_OC_L
: GPIO_USB_C1_OC_L;
+ /* Note that the levels are inverted because the pin is active low. */
+ int lvl = is_overcurrented ? 0 : 1;
- gpio_set_level(signal, 0);
+ gpio_set_level(signal, lvl);
CPRINTS("p%d: overcurrent!", port);
}
diff --git a/board/nocturne/board.c b/board/nocturne/board.c
index ffa8c65920..20b4b3983b 100644
--- a/board/nocturne/board.c
+++ b/board/nocturne/board.c
@@ -572,28 +572,29 @@ static void board_quirks(void)
}
DECLARE_HOOK(HOOK_INIT, board_quirks, HOOK_PRIO_DEFAULT);
-void board_overcurrent_event(int port)
+void board_overcurrent_event(int port, int is_overcurrented)
{
+ int lvl;
+
/* Sanity check the port. */
if ((port < 0) || (port >= CONFIG_USB_PD_PORT_COUNT))
return;
/* Note that the levels are inverted because the pin is active low. */
+ lvl = is_overcurrented ? 0 : 1;
+
switch (port) {
case 0:
- gpio_set_level(GPIO_USB_C0_OC_ODL, 0);
+ gpio_set_level(GPIO_USB_C0_OC_ODL, lvl);
break;
case 1:
- gpio_set_level(GPIO_USB_C1_OC_ODL, 0);
+ gpio_set_level(GPIO_USB_C1_OC_ODL, lvl);
break;
default:
return;
};
-
- /* TODO(b/69935262): Write a PD log entry for the OC event. */
- CPRINTS("C%d: overcurrent!", port);
}
static int read_gyro_sensor_temp(int idx, int *temp_ptr)
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;
diff --git a/common/usbc_ppc.c b/common/usbc_ppc.c
index 0961ba6e83..9ec33a3ec1 100644
--- a/common/usbc_ppc.c
+++ b/common/usbc_ppc.c
@@ -5,15 +5,26 @@
/* USB-C Power Path Controller Common Code */
+#include "atomic.h"
#include "common.h"
#include "console.h"
#include "hooks.h"
+#include "timer.h"
#include "usbc_ppc.h"
#include "util.h"
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
+/*
+ * A per-port table that indicates how many VBUS overcurrent events have
+ * occurred. This table is cleared after detecting a physical disconnect of the
+ * sink.
+ */
+static uint8_t oc_event_cnt_tbl[CONFIG_USB_PD_PORT_COUNT];
+
+static uint32_t connected_ports;
+
/* Simple wrappers to dispatch to the drivers. */
int ppc_init(int port)
@@ -32,6 +43,59 @@ int ppc_init(int port)
return rv;
}
+int ppc_add_oc_event(int port)
+{
+ if ((port < 0) || (port >= ppc_cnt))
+ return EC_ERROR_INVAL;
+
+ oc_event_cnt_tbl[port]++;
+
+ /* The port overcurrented, so don't clear it's OC events. */
+ atomic_clear(&connected_ports, 1 << port);
+
+ if (oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH)
+ CPRINTS("C%d: OC event limit reached! "
+ "Source path disabled until physical disconnect.",
+ port);
+ return EC_SUCCESS;
+}
+
+static void clear_oc_tbl(void)
+{
+ int port;
+
+ for (port = 0; port < ppc_cnt; port++)
+ /*
+ * Only clear the table if the port partner is no longer
+ * attached after debouncing.
+ */
+ if ((!((1 << port) & connected_ports)) &&
+ oc_event_cnt_tbl[port]) {
+ oc_event_cnt_tbl[port] = 0;
+ CPRINTS("C%d: OC events cleared", port);
+ }
+}
+DECLARE_DEFERRED(clear_oc_tbl);
+
+int ppc_clear_oc_event_counter(int port)
+{
+ if ((port < 0) || (port >= ppc_cnt))
+ return EC_ERROR_INVAL;
+
+ /*
+ * If we are clearing our event table in quick succession, we may be in
+ * an overcurrent loop where we are also detecting a disconnect on the
+ * CC pins. Therefore, let's not clear it just yet and the let the
+ * limit be reached. This way, we won't send the hard reset and
+ * actually detect the physical disconnect.
+ */
+ if (oc_event_cnt_tbl[port]) {
+ hook_call_deferred(&clear_oc_tbl_data,
+ PPC_OC_COOLDOWN_DELAY_US);
+ }
+ return EC_SUCCESS;
+}
+
int ppc_is_sourcing_vbus(int port)
{
if ((port < 0) || (port >= ppc_cnt)) {
@@ -68,16 +132,41 @@ int ppc_discharge_vbus(int port, int enable)
return ppc_chips[port].drv->discharge_vbus(port, enable);
}
+int ppc_is_port_latched_off(int port)
+{
+ if ((port < 0) || (port >= ppc_cnt))
+ return 0;
+
+ return oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH;
+}
+
#ifdef CONFIG_USBC_PPC_VCONN
int ppc_set_vconn(int port, int enable)
{
if ((port < 0) || (port >= ppc_cnt))
return EC_ERROR_INVAL;
+ /*
+ * Check our OC event counter. If we've exceeded our threshold, then
+ * let's latch our source path off to prevent continuous cycling. When
+ * the PD state machine detects a disconnection on the CC lines, we will
+ * reset our OC event counter.
+ */
+ if (enable && ppc_is_port_latched_off(port))
+ return EC_ERROR_ACCESS_DENIED;
+
return ppc_chips[port].drv->set_vconn(port, enable);
}
#endif
+void ppc_sink_is_connected(int port, int is_connected)
+{
+ if (is_connected)
+ atomic_or(&connected_ports, 1 << port);
+ else
+ atomic_clear(&connected_ports, 1 << port);
+}
+
int ppc_vbus_sink_enable(int port, int enable)
{
if ((port < 0) || (port >= ppc_cnt))
@@ -104,6 +193,15 @@ int ppc_vbus_source_enable(int port, int enable)
if ((port < 0) || (port >= ppc_cnt))
return EC_ERROR_INVAL;
+ /*
+ * Check our OC event counter. If we've exceeded our threshold, then
+ * let's latch our source path off to prevent continuous cycling. When
+ * the PD state machine detects a disconnection on the CC lines, we will
+ * reset our OC event counter.
+ */
+ if (enable && ppc_is_port_latched_off(port))
+ return EC_ERROR_ACCESS_DENIED;
+
return ppc_chips[port].drv->vbus_source_enable(port, enable);
}
@@ -119,7 +217,6 @@ int ppc_is_vbus_present(int port)
}
#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */
-
#ifdef CONFIG_CMD_PPC_DUMP
static int command_ppc_dump(int argc, char **argv)
{
diff --git a/driver/ppc/sn5s330.c b/driver/ppc/sn5s330.c
index b90605a3e1..6b48c8650b 100644
--- a/driver/ppc/sn5s330.c
+++ b/driver/ppc/sn5s330.c
@@ -19,6 +19,7 @@
#include "timer.h"
#include "usb_charge.h"
#include "usb_pd_tcpm.h"
+#include "usb_pd.h"
#include "usbc_ppc.h"
#include "util.h"
@@ -641,9 +642,9 @@ static void sn5s330_handle_interrupt(int port)
read_reg(port, SN5S330_INT_TRIP_RISE_REG1, &rise);
read_reg(port, SN5S330_INT_TRIP_FALL_REG1, &fall);
- /* Let the board know about the overcurrent event. */
+ /* Notify the system about the overcurrent event. */
if (rise & SN5S330_ILIM_PP1_MASK)
- board_overcurrent_event(port);
+ pd_handle_overcurrent(port);
/* Clear the interrupt sources. */
write_reg(port, SN5S330_INT_TRIP_RISE_REG1, rise);
diff --git a/include/usb_pd.h b/include/usb_pd.h
index d87fcc8fa4..5ae89696dd 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -48,6 +48,7 @@ enum pd_rx_errors {
*/
#define PD_EVENT_DEVICE_ACCESSED (1<<7)
#define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */
+#define PD_EVENT_SEND_HARD_RESET (1<<9) /* Issue a Hard Reset. */
/* Ensure TCPC is out of low power mode before handling these events. */
#define PD_EXIT_LOW_POWER_EVENT_MASK \
@@ -1769,6 +1770,14 @@ int pd_get_polarity(int port);
int pd_get_partner_data_swap_capable(int port);
/**
+ * Handle an overcurrent protection event. The port acting as a source has
+ * reported an overcurrent event.
+ *
+ * @param port: USB-C port number.
+ */
+void pd_handle_overcurrent(int port);
+
+/**
* Request power swap command to be issued
*
* @param port USB-C port number
diff --git a/include/usbc_ppc.h b/include/usbc_ppc.h
index 06c2ff3f2f..a502ebe91e 100644
--- a/include/usbc_ppc.h
+++ b/include/usbc_ppc.h
@@ -11,6 +11,18 @@
/* Common APIs for USB Type-C Power Path Controllers (PPC) */
+/*
+ * Number of times a port may overcurrent before we latch off the port until a
+ * physical disconnect is detected.
+ */
+#define PPC_OC_CNT_THRESH 3
+
+/*
+ * Number of seconds until a latched-off port is re-enabled for sourcing after
+ * detecting a physical disconnect.
+ */
+#define PPC_OC_COOLDOWN_DELAY_US (2 * SECOND)
+
struct ppc_drv {
/**
* Initialize the PPC.
@@ -127,6 +139,31 @@ extern struct ppc_config_t ppc_chips[];
extern unsigned int ppc_cnt;
/**
+ * Increment the overcurrent event counter.
+ *
+ * @param port: The Type-C port that has overcurrented.
+ * @return EC_SUCCESS on success, EC_ERROR_INVAL if non-existent port.
+ */
+int ppc_add_oc_event(int port);
+
+/**
+ * Clear the overcurrent event counter.
+ *
+ * @param port: The Type-C port's counter to clear.
+ * @return EC_SUCCESS on success, EC_ERROR_INVAL if non-existent port.
+ */
+int ppc_clear_oc_event_counter(int port);
+
+/**
+ * Discharge PD VBUS on src/sink disconnect & power role swap
+ *
+ * @param port: The Type-C port number.
+ * @param enable: 1 -> discharge vbus, 0 -> stop discharging vbus
+ * @return EC_SUCCESS on success, error otherwise.
+ */
+int ppc_discharge_vbus(int port, int enable);
+
+/**
* Initializes the PPC for the specified port.
*
* @param port: The Type-C port number.
@@ -135,12 +172,12 @@ extern unsigned int ppc_cnt;
int ppc_init(int port);
/**
- * Determine if VBUS is present or not.
+ * Is the port latched off due to multiple overcurrent events in succession?
*
* @param port: The Type-C port number.
- * @return 1 if VBUS is present, 0 if not.
+ * @return 1 if the port is latched off, 0 if it is not latched off.
*/
-int ppc_is_vbus_present(int port);
+int ppc_is_port_latched_off(int port);
/**
* Is the port sourcing Vbus?
@@ -151,6 +188,25 @@ int ppc_is_vbus_present(int port);
int ppc_is_sourcing_vbus(int port);
/**
+ * Determine if VBUS is present or not.
+ *
+ * @param port: The Type-C port number.
+ * @return 1 if VBUS is present, 0 if not.
+ */
+int ppc_is_vbus_present(int port);
+
+/**
+ * Inform the PPC module that a sink is connected.
+ *
+ * This is used such that it can determine when to clear the overcurrent events
+ * counter for a port.
+ * @param port: The Type-C port number.
+ * @param is_connected: 1: if sink is connected on this port, 0: if not
+ * connected.
+ */
+void ppc_sink_is_connected(int port, int is_connected);
+
+/**
* Inform the PPC of the polarity of the CC pins.
*
* @param port: The Type-C port number.
@@ -177,15 +233,6 @@ int ppc_set_vbus_source_current_limit(int port, enum tcpc_rp_value rp);
int ppc_set_vconn(int port, int enable);
/**
- * Discharge PD VBUS on src/sink disconnect & power role swap
- *
- * @param port: The Type-C port number.
- * @param enable: 1 -> discharge vbus, 0 -> stop discharging vbus
- * @return EC_SUCCESS on success, error otherwise.
- */
-int ppc_discharge_vbus(int port, int enable);
-
-/**
* Turn on/off the charge path FET, such that current flows into the
* system.
*
@@ -209,8 +256,9 @@ int ppc_vbus_source_enable(int port, int enable);
* Board specific callback when a port overcurrents.
*
* @param port: The Type-C port which overcurrented.
+ * @param is_overcurrented: 1 if port overcurrented, 0 if the condition is gone.
*/
-void board_overcurrent_event(int port);
+void board_overcurrent_event(int port, int is_overcurrented);
/**
* Put the PPC into its lowest power state. In this state it should still fire