summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorDiana Z <dzigterman@chromium.org>2020-10-30 14:18:56 -0600
committerCommit Bot <commit-bot@chromium.org>2020-11-17 16:44:16 +0000
commit51484d77533e60cdfe21902eaf759c3be14df40a (patch)
treed28d5db09af798fd2972a4c4f902d770a951fe1a /common
parent15b590a0999752abe28584a0c502fe9435ec7e41 (diff)
downloadchrome-ec-51484d77533e60cdfe21902eaf759c3be14df40a.tar.gz
TCPM: Add OCP module
Currently, the overcurrent protection is tracked in the PPC code. However, as different chips are able to report overcurrent move this code into a generic module. Logic for not sourcing Vbus or Vconn on latched ports moves into the TC layer, and an overridable board overcurrent function is provided for boards which have no special actions to take. BRANCH=None BUG=b:171501161 TEST=make -j buildall; TCPMv2 tested with following drawcia patch Signed-off-by: Diana Z <dzigterman@chromium.org> Change-Id: I75919e345a5b0bce4a0b67432a13515e7716cf6a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2532676 Reviewed-by: Keith Short <keithshort@chromium.org> Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'common')
-rw-r--r--common/build.mk1
-rw-r--r--common/usb_common.c14
-rw-r--r--common/usb_pd_protocol.c36
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c84
-rw-r--r--common/usbc_ocp.c131
-rw-r--r--common/usbc_ppc.c101
6 files changed, 213 insertions, 154 deletions
diff --git a/common/build.mk b/common/build.mk
index 9b35f25642..839207a1a8 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -164,6 +164,7 @@ common-$(CONFIG_USB_PD_ALT_MODE_DFP)+=usb_pd_alt_mode_dfp.o
common-$(CONFIG_USB_PD_LOGGING)+=event_log.o pd_log.o
common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o
common-$(CONFIG_USB_UPDATE)+=usb_update.o update_fw.o
+common-$(CONFIG_USBC_OCP)+=usbc_ocp.o
common-$(CONFIG_USBC_PPC)+=usbc_ppc.o
common-$(CONFIG_VBOOT_EFS)+=vboot/vboot.o
common-$(CONFIG_VBOOT_EFS2)+=vboot/efs2.o
diff --git a/common/usb_common.c b/common/usb_common.c
index c9100d15ad..7d4a0df99e 100644
--- a/common/usb_common.c
+++ b/common/usb_common.c
@@ -25,6 +25,7 @@
#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
+#include "usbc_ocp.h"
#include "usbc_ppc.h"
#include "util.h"
@@ -462,7 +463,7 @@ static void pd_send_hard_reset(int port)
task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET, 0);
}
-#ifdef CONFIG_USBC_PPC
+#ifdef CONFIG_USBC_OCP
static uint32_t port_oc_reset_req;
@@ -493,18 +494,23 @@ DECLARE_DEFERRED(re_enable_ports);
void pd_handle_overcurrent(int port)
{
+ if ((port < 0) || (port >= board_get_usb_pd_port_count())) {
+ CPRINTS("%s(%d) Invalid port!", __func__, port);
+ return;
+ }
+
CPRINTS("C%d: overcurrent!", port);
if (IS_ENABLED(CONFIG_USB_PD_LOGGING))
pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0),
- PS_FAULT_OCP, NULL);
+ PS_FAULT_OCP, NULL);
/* No action to take if disconnected, just log. */
if (pd_is_disconnected(port))
return;
/* Keep track of the overcurrent events. */
- ppc_add_oc_event(port);
+ usbc_ocp_add_event(port);
/* Let the board specific code know about the OC event. */
board_overcurrent_event(port, 1);
@@ -514,7 +520,7 @@ void pd_handle_overcurrent(int port)
hook_call_deferred(&re_enable_ports_data, SECOND);
}
-#endif /* CONFIG_USBC_PPC */
+#endif /* CONFIG_USBC_OCP */
__maybe_unused void pd_handle_cc_overvoltage(int port)
{
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 4b1a79d07c..5bc3e0a1e2 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -30,6 +30,7 @@
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#include "usb_pd_tcpc.h"
+#include "usbc_ocp.h"
#include "usbc_ppc.h"
#include "version.h"
#include "vboot.h"
@@ -717,13 +718,17 @@ static inline void set_state(int port, enum pd_states next_state)
/* If we're entering DRP_AUTO_TOGGLE, there is no sink connected. */
if (next_state == PD_STATE_DRP_AUTO_TOGGLE) {
ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED);
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- ppc_clear_oc_event_counter(port);
/* Disable Auto Discharge Disconnect */
tcpm_enable_auto_discharge_disconnect(port, 0);
+
+ if (IS_ENABLED(CONFIG_USBC_OCP)) {
+ usbc_ocp_snk_is_connected(port, false);
+ /*
+ * Clear the overcurrent event counter
+ * since we've detected a disconnect.
+ */
+ usbc_ocp_clear_event_counter(port);
+ }
}
#endif /* CONFIG_USBC_PPC && CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
@@ -756,13 +761,18 @@ static inline void set_state(int port, enum pd_states next_state)
*/
if (!cc_is_at_least_one_rd(cc1, cc2)) {
ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED);
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- ppc_clear_oc_event_counter(port);
+
+ if (IS_ENABLED(CONFIG_USBC_OCP)) {
+ usbc_ocp_snk_is_connected(port, false);
+ /*
+ * Clear the overcurrent event counter
+ * since we've detected a disconnect.
+ */
+ usbc_ocp_clear_event_counter(port);
+ }
}
#endif /* CONFIG_USBC_PPC */
+
/* Clear the holdoff timer since the port is disconnected. */
pd[port].ready_state_holdoff_timer = 0;
@@ -3343,7 +3353,7 @@ void pd_task(void *u)
* If the port is latched off, just continue to
* monitor for a detach.
*/
- if (ppc_is_port_latched_off(port))
+ if (usbc_ocp_is_port_latched_off(port))
break;
#endif /* CONFIG_USBC_PPC */
@@ -3354,6 +3364,8 @@ void pd_task(void *u)
/* Inform PPC that a sink is connected. */
ppc_dev_is_connected(port, PPC_DEV_SNK);
#endif /* CONFIG_USBC_PPC */
+ if (IS_ENABLED(CONFIG_USBC_OCP))
+ usbc_ocp_snk_is_connected(port, true);
if (new_cc_state == PD_CC_UFP_DEBUG_ACC) {
pd[port].polarity =
board_get_src_dts_polarity(
@@ -4034,6 +4046,8 @@ void pd_task(void *u)
/* Inform PPC that a source is connected. */
ppc_dev_is_connected(port, PPC_DEV_SRC);
#endif /* CONFIG_USBC_PPC */
+ if (IS_ENABLED(CONFIG_USBC_OCP))
+ usbc_ocp_snk_is_connected(port, false);
/* If PD comm is enabled, enable TCPC RX */
if (pd_comm_is_enabled(port))
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
index 2b1da47c79..fd334b6b12 100644
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
@@ -19,6 +19,7 @@
#include "usb_prl_sm.h"
#include "usb_sm.h"
#include "usb_tc_sm.h"
+#include "usbc_ocp.h"
#include "usbc_ppc.h"
#include "vboot.h"
@@ -971,6 +972,15 @@ void tc_snk_power_off(int port)
int tc_src_power_on(int port)
{
+ /*
+ * 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 (IS_ENABLED(CONFIG_USBC_OCP) && usbc_ocp_is_port_latched_off(port))
+ return EC_ERROR_ACCESS_DENIED;
+
if (IS_ATTACHED_SRC(port))
return pd_set_power_supply_ready(port);
@@ -987,6 +997,23 @@ void tc_src_power_off(int port)
CHARGE_CEIL_NONE);
}
+/* Set what role the partner is right now, for the PPC and OCP module */
+static void tc_set_partner_role(int port, enum ppc_device_role role)
+{
+ if (IS_ENABLED(CONFIG_USBC_PPC))
+ ppc_dev_is_connected(port, role);
+
+ if (IS_ENABLED(CONFIG_USBC_OCP)) {
+ usbc_ocp_snk_is_connected(port, role == PPC_DEV_SNK);
+ /*
+ * Clear the overcurrent event counter
+ * since we've detected a disconnect.
+ */
+ if (role == PPC_DEV_DISCONNECTED)
+ usbc_ocp_clear_event_counter(port);
+ }
+}
+
/*
* Depending on the load on the processor and the tasks running
* it can take a while for the task associated with this port
@@ -1625,6 +1652,16 @@ static void set_vconn(int port, int enable)
TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON);
/*
+ * 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 (IS_ENABLED(CONFIG_USBC_OCP) &&
+ enable && usbc_ocp_is_port_latched_off(port))
+ return;
+
+ /*
* Disable PPC Vconn first then TCPC in case the voltage feeds back
* to TCPC and damages.
*/
@@ -2010,16 +2047,7 @@ static void tc_unattached_snk_entry(const int port)
if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
charge_manager_update_dualrole(port, CAP_UNKNOWN);
- if (IS_ENABLED(CONFIG_USBC_PPC)) {
- /* There is no source connected. */
- ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED);
-
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- ppc_clear_oc_event_counter(port);
- }
+ tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
/*
* Indicate that the port is disconnected so the board
@@ -2215,9 +2243,8 @@ static void tc_attached_snk_entry(const int port)
*/
typec_select_pull(port, TYPEC_CC_RD);
- /* Inform PPC that a source is connected. */
- if (IS_ENABLED(CONFIG_USBC_PPC))
- ppc_dev_is_connected(port, PPC_DEV_SRC);
+ /* Inform the PPC and OCP module that a source is connected */
+ tc_set_partner_role(port, PPC_DEV_SRC);
if (IS_ENABLED(CONFIG_USB_PE_SM) &&
TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
@@ -2513,16 +2540,7 @@ static void tc_unattached_src_entry(const int port)
*/
bc12_role_change_handler(port, prev_data_role, tc[port].data_role);
- if (IS_ENABLED(CONFIG_USBC_PPC)) {
- /* There is no sink connected. */
- ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED);
-
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- ppc_clear_oc_event_counter(port);
- }
+ tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
charge_manager_update_dualrole(port, CAP_UNKNOWN);
@@ -2548,12 +2566,12 @@ static void tc_unattached_src_run(const int port)
}
}
- if (IS_ENABLED(CONFIG_USBC_PPC)) {
+ if (IS_ENABLED(CONFIG_USBC_OCP)) {
/*
* If the port is latched off, just continue to
* monitor for a detach.
*/
- if (ppc_is_port_latched_off(port))
+ if (usbc_ocp_is_port_latched_off(port))
return;
}
@@ -2789,9 +2807,8 @@ static void tc_attached_src_entry(const int port)
typec_update_cc(port);
}
- /* Inform PPC that a sink is connected. */
- if (IS_ENABLED(CONFIG_USBC_PPC))
- ppc_dev_is_connected(port, PPC_DEV_SNK);
+ /* Inform PPC and OCP module that a sink is connected. */
+ tc_set_partner_role(port, PPC_DEV_SNK);
/*
* Only notify if we're not performing a power role swap. During a
@@ -3486,16 +3503,7 @@ static void tc_cc_open_entry(const int port)
typec_select_pull(port, TYPEC_CC_OPEN);
typec_update_cc(port);
- if (IS_ENABLED(CONFIG_USBC_PPC)) {
- /* There is no device connected. */
- ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED);
-
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- ppc_clear_oc_event_counter(port);
- }
+ tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
}
void tc_set_debug_level(enum debug_level debug_level)
diff --git a/common/usbc_ocp.c b/common/usbc_ocp.c
new file mode 100644
index 0000000000..e20cf9f1f8
--- /dev/null
+++ b/common/usbc_ocp.c
@@ -0,0 +1,131 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* USB-C Overcurrent Protection Common Code */
+
+#include "atomic.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usbc_ocp.h"
+#include "util.h"
+
+#ifndef TEST_BUILD
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
+#else
+#define CPRINTF(args...)
+#define CPRINTS(args...)
+#endif
+
+/*
+ * Number of times a port may overcurrent before we latch off the port until a
+ * physical disconnect is detected.
+ */
+#define OCP_CNT_THRESH 3
+
+/*
+ * Number of seconds until a latched-off port is re-enabled for sourcing after
+ * detecting a physical disconnect.
+ */
+#define OCP_COOLDOWN_DELAY_US (2 * SECOND)
+
+/*
+ * 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_MAX_COUNT];
+
+/* A flag for ports with sink device connected. */
+static uint32_t snk_connected_ports;
+
+static void clear_oc_tbl(void)
+{
+ int port;
+
+ for (port = 0; port < board_get_usb_pd_port_count(); port++)
+ /*
+ * Only clear the table if the port partner is no longer
+ * attached after debouncing.
+ */
+ if ((!(BIT(port) & snk_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 usbc_ocp_add_event(int port)
+{
+ if ((port < 0) || (port >= board_get_usb_pd_port_count())) {
+ CPRINTS("%s(%d) Invalid port!", __func__, port);
+ return EC_ERROR_INVAL;
+ }
+
+ oc_event_cnt_tbl[port]++;
+
+ /* The port overcurrented, so don't clear it's OC events. */
+ atomic_clear_bits(&snk_connected_ports, 1 << port);
+
+ if (oc_event_cnt_tbl[port] >= OCP_CNT_THRESH)
+ CPRINTS("C%d: OC event limit reached! "
+ "Source path disabled until physical disconnect.",
+ port);
+ return EC_SUCCESS;
+}
+
+
+int usbc_ocp_clear_event_counter(int port)
+{
+ if ((port < 0) || (port >= board_get_usb_pd_port_count())) {
+ CPRINTS("%s(%d) Invalid port!", __func__, port);
+ 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,
+ OCP_COOLDOWN_DELAY_US);
+ }
+ return EC_SUCCESS;
+}
+
+int usbc_ocp_is_port_latched_off(int port)
+{
+ if ((port < 0) || (port >= board_get_usb_pd_port_count())) {
+ CPRINTS("%s(%d) Invalid port!", __func__, port);
+ return 0;
+ }
+
+ return oc_event_cnt_tbl[port] >= OCP_CNT_THRESH;
+}
+
+void usbc_ocp_snk_is_connected(int port, bool connected)
+{
+ if ((port < 0) || (port >= board_get_usb_pd_port_count())) {
+ CPRINTS("%s(%d) Invalid port!", __func__, port);
+ return;
+ }
+
+ if (connected)
+ atomic_or(&snk_connected_ports, 1 << port);
+ else
+ atomic_clear_bits(&snk_connected_ports, 1 << port);
+}
+
+__overridable void board_overcurrent_event(int port, int is_overcurrented)
+{
+ /* Do nothing by default. Boards with overcurrent GPIOs may override */
+}
diff --git a/common/usbc_ppc.c b/common/usbc_ppc.c
index 73dbf9d1c0..0281ceaf64 100644
--- a/common/usbc_ppc.c
+++ b/common/usbc_ppc.c
@@ -40,16 +40,6 @@ int ppc_err_prints(const char *string, int port, int error)
#endif
}
-/*
- * 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_MAX_COUNT];
-
-/* A flag for ports with sink device connected. */
-static uint32_t snk_connected_ports;
-
/* Simple wrappers to dispatch to the drivers. */
int ppc_init(int port)
@@ -74,63 +64,6 @@ int ppc_init(int port)
return rv;
}
-int ppc_add_oc_event(int port)
-{
- if ((port < 0) || (port >= ppc_cnt)) {
- CPRINTS("%s(%d) Invalid port!", __func__, port);
- return EC_ERROR_INVAL;
- }
-
- oc_event_cnt_tbl[port]++;
-
- /* The port overcurrented, so don't clear it's OC events. */
- atomic_clear_bits(&snk_connected_ports, 1 << port);
-
- if (oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH)
- ppc_prints("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 ((!(BIT(port) & snk_connected_ports)) &&
- oc_event_cnt_tbl[port]) {
- oc_event_cnt_tbl[port] = 0;
- ppc_prints("OC events cleared", port);
- }
-}
-DECLARE_DEFERRED(clear_oc_tbl);
-
-int ppc_clear_oc_event_counter(int port)
-{
- if ((port < 0) || (port >= ppc_cnt)) {
- CPRINTS("%s(%d) Invalid port!", __func__, port);
- 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)
{
int rv = 0;
@@ -201,16 +134,6 @@ int ppc_discharge_vbus(int port, int enable)
return rv;
}
-int ppc_is_port_latched_off(int port)
-{
- if ((port < 0) || (port >= ppc_cnt)) {
- CPRINTS("%s(%d) Invalid port!", __func__, port);
- return 0;
- }
-
- return oc_event_cnt_tbl[port] >= PPC_OC_CNT_THRESH;
-}
-
#ifdef CONFIG_USBC_PPC_SBU
int ppc_set_sbu(int port, int enable)
{
@@ -241,15 +164,6 @@ int ppc_set_vconn(int port, int enable)
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;
-
ppc = &ppc_chips[port];
if (ppc->drv->set_vconn)
rv = ppc->drv->set_vconn(port, enable);
@@ -268,12 +182,6 @@ int ppc_dev_is_connected(int port, enum ppc_device_role dev)
return EC_ERROR_INVAL;
}
- if (dev == PPC_DEV_SNK)
- atomic_or(&snk_connected_ports, 1 << port);
- else
- /* clear flag if it transitions to SRC or disconnected */
- atomic_clear_bits(&snk_connected_ports, 1 << port);
-
ppc = &ppc_chips[port];
if (ppc->drv->dev_is_connected)
rv = ppc->drv->dev_is_connected(port, dev);
@@ -325,15 +233,6 @@ int ppc_vbus_source_enable(int port, int enable)
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;
-
ppc = &ppc_chips[port];
if (ppc->drv->vbus_source_enable)
rv = ppc->drv->vbus_source_enable(port, enable);