summaryrefslogtreecommitdiff
path: root/common/usbc_ppc.c
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 /common/usbc_ppc.c
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>
Diffstat (limited to 'common/usbc_ppc.c')
-rw-r--r--common/usbc_ppc.c99
1 files changed, 98 insertions, 1 deletions
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)
{