summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Brockus <dbrockus@google.com>2021-02-17 12:18:28 -0700
committerCommit Bot <commit-bot@chromium.org>2021-03-01 22:49:49 +0000
commit2bdefef51a36f80c56f95278bffba743c501f9a8 (patch)
tree81f6026831e8b46f94758e808925bcebda06e495
parent1aa5f1d725aa778834ef6d92e70c5a479020dc86 (diff)
downloadchrome-ec-2bdefef51a36f80c56f95278bffba743c501f9a8.tar.gz
TCPMv2: PD Timers - Base Timer framework
Add in the basic framework. There are optimizations for using less timers but want to get the basics working before I invest more time in this. BUG=b:178029034 BRANCH=none TEST=make-runtests Signed-off-by: Denis Brockus <dbrockus@google.com> Change-Id: Icf93e3074b02cc3e0f7ee62e026b0d8ba10c7709 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2707804 Reviewed-by: Denis Brockus <dbrockus@chromium.org> Commit-Queue: Denis Brockus <dbrockus@chromium.org> Tested-by: Denis Brockus <dbrockus@chromium.org>
-rw-r--r--common/usbc/build.mk1
-rw-r--r--common/usbc/usb_pd_timer.c238
-rw-r--r--include/config.h1
-rw-r--r--include/usb_pd_timer.h276
-rw-r--r--zephyr/CMakeLists.txt1
5 files changed, 517 insertions, 0 deletions
diff --git a/common/usbc/build.mk b/common/usbc/build.mk
index 3332fba43f..82d9928f8e 100644
--- a/common/usbc/build.mk
+++ b/common/usbc/build.mk
@@ -8,6 +8,7 @@
_usbc_dir:=$(dir $(lastword $(MAKEFILE_LIST)))
ifneq ($(CONFIG_USB_PD_TCPMV2),)
+all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_pd_timer.o
all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_sm.o
all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usbc_task.o
diff --git a/common/usbc/usb_pd_timer.c b/common/usbc/usb_pd_timer.c
new file mode 100644
index 0000000000..0bbf06f0e4
--- /dev/null
+++ b/common/usbc/usb_pd_timer.c
@@ -0,0 +1,238 @@
+/* Copyright 2021 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.
+ */
+
+#include "assert.h"
+#include "common.h"
+#include "console.h"
+#include "limits.h"
+#include "system.h"
+#include "usb_pd_timer.h"
+
+#define MAX_PD_PORTS CONFIG_USB_PD_PORT_MAX_COUNT
+#define MAX_PD_TIMERS PD_TIMER_COUNT
+#define PD_TIMERS_ALL_MASK ((uint32_t)(((uint64_t)1 << PD_TIMER_COUNT) - 1))
+
+#define MAX_EXPIRE (0x7FFFFFFF)
+#define NO_TIMEOUT (-1)
+#define EXPIRE_NOW (0)
+
+#define PD_SET_ACTIVE(p, m) atomic_or(&timer_active[p], (m))
+#define PD_CLR_ACTIVE(p, m) atomic_clear_bits(&timer_active[p], (m))
+#define PD_CHK_ACTIVE(p, m) (timer_active[p] & (m))
+
+#define PD_SET_DISABLED(p, m) atomic_or(&timer_disabled[p], (m))
+#define PD_CLR_DISABLED(p, m) atomic_clear_bits(&timer_disabled[p], (m))
+#define PD_CHK_DISABLED(p, m) (timer_disabled[p] & (m))
+
+static uint32_t timer_active[MAX_PD_PORTS];
+static uint32_t timer_disabled[MAX_PD_PORTS];
+static uint64_t timer_expires[MAX_PD_PORTS][MAX_PD_TIMERS];
+
+/*
+ * CONFIG_CMD_PD_TIMER debug variables
+ */
+static int count[MAX_PD_PORTS];
+static int max_count[MAX_PD_PORTS];
+
+__maybe_unused static __const_data const char * const pd_timer_names[] = {
+ [PE_TIMER_BIST_CONT_MODE] = "PE-BIST_CONT_MODE",
+ [PE_TIMER_CHUNKING_NOT_SUPPORTED] = "PE-CHUNKING_NOT_SUPPORTED",
+ [PE_TIMER_DISCOVER_IDENTITY] = "PE-DISCOVER_IDENTITY",
+ [PE_TIMER_NO_RESPONSE] = "PE-NO_RESPONSE",
+ [PE_TIMER_PR_SWAP_WAIT] = "PE-PR_SWAP_WAIT",
+ [PE_TIMER_PS_HARD_RESET] = "PE-PS_HARD_RESET",
+ [PE_TIMER_PS_SOURCE] = "PE-PS_SOURCE",
+ [PE_TIMER_PS_TRANSITION] = "PE-PS_TRANSITION",
+ [PE_TIMER_SENDER_RESPONSE] = "PE-SENDER_RESPONSE",
+ [PE_TIMER_SINK_REQUEST] = "PE-SINK_REQUEST",
+ [PE_TIMER_SOURCE_CAP] = "PE-SOURCE_CAP",
+ [PE_TIMER_SRC_TRANSITION] = "PE-SRC_TRANSITION",
+ [PE_TIMER_SWAP_SOURCE_START] = "PE-SWAP_SOURCE_START",
+ [PE_TIMER_TIMEOUT] = "PE-TIMEOUT",
+ [PE_TIMER_VCONN_ON] = "PE-VCONN_ON",
+ [PE_TIMER_VDM_RESPONSE] = "PE-VDM_RESPONSE",
+ [PE_TIMER_WAIT_AND_ADD_JITTER] = "PE-WAIT_AND_ADD_JITTER",
+
+ [PR_TIMER_CHUNK_SENDER_REQUEST] = "PR-CHUNK_SENDER_REQUEST",
+ [PR_TIMER_CHUNK_SENDER_RESPONSE] = "PR-CHUNK_SENDER_RESPONSE",
+ [PR_TIMER_HARD_RESET_COMPLETE] = "PR-HARD_RESET_COMPLETE",
+ [PR_TIMER_SINK_TX] = "PR-SINK_TX",
+ [PR_TIMER_TCPC_TX_TIMEOUT] = "PR-TCPC_TX_TIMEOUT",
+
+ [TC_TIMER_CC_DEBOUNCE] = "TC-CC_DEBOUNCE",
+ [TC_TIMER_LOW_POWER_EXIT_TIME] = "TC-LOW_POWER_EXIT_TIME",
+ [TC_TIMER_LOW_POWER_TIME] = "TC-LOW_POWER_TIME",
+ [TC_TIMER_NEXT_ROLE_SWAP] = "TC-NEXT_ROLE_SWAP",
+ [TC_TIMER_PD_DEBOUNCE] = "TC-PD_DEBOUNCE",
+ [TC_TIMER_TIMEOUT] = "TC-TIMEOUT",
+ [TC_TIMER_TRY_WAIT_DEBOUNCE] = "TC-TRY_WAIT_DEBOUNCE",
+ [TC_TIMER_VBUS_DEBOUNCE] = "TC-VBUS_DEBOUNCE",
+};
+
+/*****************************************************************************
+ * PD_TIMER private functions
+ *
+ * The view of timers to the outside world is enabled and disabled. Internally
+ * timers that are enabled are in the active and inactive states. An active
+ * timer has a valid timeout value that gets checked for expiration and can
+ * adjust the task wakeup time. An inactive timer is assumed to have expired
+ * already and will always return that it is still expired. This timer state
+ * will not adjust the task scheduling timeout value.
+ */
+static void pd_timer_inactive(int port, enum pd_task_timer timer)
+{
+ uint32_t mask = 1 << timer;
+
+ if (PD_CHK_ACTIVE(port, mask)) {
+ PD_CLR_ACTIVE(port, mask);
+
+ if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
+ count[port]--;
+ }
+ PD_CLR_DISABLED(port, mask);
+}
+
+static bool pd_timer_is_active(int port, enum pd_task_timer timer)
+{
+ uint32_t mask = 1 << timer;
+
+ return PD_CHK_ACTIVE(port, mask);
+}
+
+static bool pd_timer_is_inactive(int port, enum pd_task_timer timer)
+{
+ uint32_t mask = 1 << timer;
+
+ return !PD_CHK_ACTIVE(port, mask) && !PD_CHK_DISABLED(port, mask);
+}
+
+/*****************************************************************************
+ * PD_TIMER public functions
+ */
+void pd_timer_init(int port)
+{
+ if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
+ count[port] = 0;
+
+ PD_CLR_ACTIVE(port, PD_TIMERS_ALL_MASK);
+ PD_SET_DISABLED(port, PD_TIMERS_ALL_MASK);
+}
+
+void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us)
+{
+ uint32_t mask = 1 << timer;
+
+ if (!PD_CHK_ACTIVE(port, mask)) {
+ PD_SET_ACTIVE(port, mask);
+
+ if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) {
+ count[port]++;
+ if (count[port] > max_count[port])
+ max_count[port] = count[port];
+ }
+ }
+ PD_CLR_DISABLED(port, mask);
+ timer_expires[port][timer] = get_time().val + expires_us;
+}
+
+void pd_timer_disable(int port, enum pd_task_timer timer)
+{
+ uint32_t mask = 1 << timer;
+
+ if (PD_CHK_ACTIVE(port, mask)) {
+ PD_CLR_ACTIVE(port, mask);
+
+ if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
+ count[port]--;
+ }
+ PD_SET_DISABLED(port, mask);
+}
+
+bool pd_timer_is_disabled(int port, enum pd_task_timer timer)
+{
+ uint32_t mask = 1 << timer;
+
+ return PD_CHK_DISABLED(port, mask);
+}
+
+bool pd_timer_is_expired(int port, enum pd_task_timer timer)
+{
+ if (pd_timer_is_active(port, timer)) {
+ if (get_time().val >= timer_expires[port][timer]) {
+ pd_timer_inactive(port, timer);
+ return true;
+ }
+ return false;
+ }
+ return pd_timer_is_inactive(port, timer);
+}
+
+void pd_timer_manage_expired(int port)
+{
+ int timer;
+
+ if (timer_active[port])
+ for (timer = 0; timer < MAX_PD_TIMERS; ++timer)
+ if (pd_timer_is_active(port, timer) &&
+ pd_timer_is_expired(port, timer))
+ pd_timer_inactive(port, timer);
+}
+
+int pd_timer_next_expiration(int port)
+{
+ int timer;
+ int ret_value = MAX_EXPIRE;
+ uint64_t now = get_time().val;
+
+ for (timer = 0; timer < MAX_PD_TIMERS; ++timer) {
+ /* Only use active timers for the next expired value */
+ if (pd_timer_is_active(port, timer)) {
+ int delta;
+ uint64_t t_value = timer_expires[port][timer];
+
+ if (t_value <= now) {
+ ret_value = EXPIRE_NOW;
+ break;
+ }
+
+ delta = t_value - now;
+ if (ret_value > delta)
+ ret_value = delta;
+ }
+ }
+
+ if (ret_value == MAX_EXPIRE)
+ ret_value = NO_TIMEOUT;
+
+ return ret_value;
+}
+
+#ifdef CONFIG_CMD_PD_TIMER
+void pd_timer_dump(int port)
+{
+ int timer;
+ uint64_t now = get_time().val;
+
+ ccprints("Timers(%d): cur=%d max=%d",
+ port, count[port], max_count[port]);
+
+ for (timer = 0; timer < MAX_PD_TIMERS; ++timer) {
+ if (pd_timer_is_disabled(port, timer)) {
+ continue;
+ } else if (pd_timer_is_active(port, timer)) {
+ uint32_t delta = 0;
+
+ if (now < timer_expires[port][timer])
+ delta = timer_expires[port][timer] - now;
+
+ ccprints("[%2d] Active: %s (%d)",
+ timer, pd_timer_names[timer], (uint32_t)delta);
+ } else {
+ ccprints("[%2d] Inactive: %s",
+ timer, pd_timer_names[timer]);
+ }
+ }
+}
+#endif /* CONFIG_CMD_PD_TIMER */
diff --git a/include/config.h b/include/config.h
index 487a65c045..a1775b0ded 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1378,6 +1378,7 @@
#define CONFIG_CMD_PD
#undef CONFIG_CMD_PD_DEV_DUMP_INFO
#undef CONFIG_CMD_PD_FLASH
+#undef CONFIG_CMD_PD_TIMER
#define CONFIG_CMD_PECI
#undef CONFIG_CMD_PLL
#undef CONFIG_CMD_PMU
diff --git a/include/usb_pd_timer.h b/include/usb_pd_timer.h
new file mode 100644
index 0000000000..ea0b302e17
--- /dev/null
+++ b/include/usb_pd_timer.h
@@ -0,0 +1,276 @@
+/* Copyright 2021 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 Power delivery module */
+
+#ifndef __CROS_EC_USB_PD_TIMER_H
+#define __CROS_EC_USB_PD_TIMER_H
+
+#include <stdbool.h>
+
+/*
+ * List of all timers that will be managed by usb_pd_timer
+ */
+enum pd_task_timer {
+ /*
+ * In BIST_TX mode, this timer is used by a UUT to ensure that a
+ * Continuous BIST Mode (i.e. BIST Carrier Mode) is exited in a timely
+ * fashion.
+ *
+ * In BIST_RX mode, this timer is used to give the port partner time
+ * to respond.
+ */
+ PE_TIMER_BIST_CONT_MODE,
+
+ /*
+ * PD 3.0, version 2.0, section 6.6.18.1: The ChunkingNotSupportedTimer
+ * is used by a Source or Sink which does not support multi-chunk
+ * Chunking but has received a Message Chunk. The
+ * ChunkingNotSupportedTimer Shall be started when the last bit of the
+ * EOP of a Message Chunk of a multi-chunk Message is received. The
+ * Policy Engine Shall Not send its Not_Supported Message before the
+ * ChunkingNotSupportedTimer expires.
+ */
+ PE_TIMER_CHUNKING_NOT_SUPPORTED,
+
+ /*
+ * This timer is used during an Explicit Contract when discovering
+ * whether a Port Partner is PD Capable using SOP'.
+ */
+ PE_TIMER_DISCOVER_IDENTITY,
+
+ /*
+ * The NoResponseTimer is used by the Policy Engine in a Source
+ * to determine that its Port Partner is not responding after a
+ * Hard Reset.
+ */
+ PE_TIMER_NO_RESPONSE,
+
+ /*
+ * This timer tracks the time after receiving a Wait message in
+ * response to a PR_Swap message.
+ */
+ PE_TIMER_PR_SWAP_WAIT,
+
+ /*
+ * This timer is used in a Source to ensure that the Sink has had
+ * sufficient time to process Hard Reset Signaling before turning
+ * off its power supply to VBUS.
+ */
+ PE_TIMER_PS_HARD_RESET,
+
+ /*
+ * This timer combines the PSSourceOffTimer and PSSourceOnTimer timers.
+ * For PSSourceOffTimer, when this DRP device is currently acting as a
+ * Sink, this timer times out on a PS_RDY Message during a Power Role
+ * Swap sequence.
+ *
+ * For PSSourceOnTimer, when this DRP device is currently acting as a
+ * Source that has just stopped sourcing power and is waiting to start
+ * sinking power to timeout on a PS_RDY Message during a Power Role
+ * Swap.
+ */
+ PE_TIMER_PS_SOURCE,
+
+ /*
+ * This timer is started when a request for a new Capability has been
+ * accepted and will timeout after PD_T_PS_TRANSITION if a PS_RDY
+ * Message has not been received.
+ */
+ PE_TIMER_PS_TRANSITION,
+
+ /*
+ * This timer is used to ensure that a Message requesting a response
+ * (e.g. Get_Source_Cap Message) is responded to within a bounded time
+ * of PD_T_SENDER_RESPONSE.
+ */
+ PE_TIMER_SENDER_RESPONSE,
+
+ /*
+ * This timer is used to ensure that the time before the next Sink
+ * Request Message, after a Wait Message has been received from the
+ * Source in response to a Sink Request Message.
+ */
+ PE_TIMER_SINK_REQUEST,
+
+ /*
+ * Prior to a successful negotiation, a Source Shall use the
+ * SourceCapabilityTimer to periodically send out a
+ * Source_Capabilities Message.
+ */
+ PE_TIMER_SOURCE_CAP,
+
+ /*
+ * Used to wait for tSrcTransition between sending an Accept for a
+ * Request or receiving a GoToMin and transitioning the power supply.
+ * See PD 3.0, table 7-11 and table 7-22 This is not a named timer in
+ * the spec.
+ */
+ PE_TIMER_SRC_TRANSITION,
+
+ /*
+ * This timer is used by the new Source, after a Power Role Swap or
+ * Fast Role Swap, to ensure that it does not send Source_Capabilities
+ * Message before the new Sink is ready to receive the
+ * Source_Capabilities Message.
+ */
+ PE_TIMER_SWAP_SOURCE_START,
+
+ /* Temporary available timeout timer */
+ PE_TIMER_TIMEOUT,
+
+ /*
+ * This timer is used during a VCONN Swap.
+ */
+ PE_TIMER_VCONN_ON,
+
+ /*
+ * This timer is used by the Initiator’s Policy Engine to ensure that
+ * a Structured VDM Command request needing a response (e.g. Discover
+ * Identity Command request) is responded to within a bounded time of
+ * tVDMSenderResponse.
+ */
+ PE_TIMER_VDM_RESPONSE,
+
+ /*
+ * For PD2.0, this timer is used to wait 400ms and add some
+ * jitter of up to 100ms before sending a message.
+ * NOTE: This timer is not part of the TypeC/PD spec.
+ */
+ PE_TIMER_WAIT_AND_ADD_JITTER,
+
+
+ /* Chunk Sender Response timer */
+ PR_TIMER_CHUNK_SENDER_RESPONSE,
+
+ /* Chunk Sender Request timer */
+ PR_TIMER_CHUNK_SENDER_REQUEST,
+
+ /* Hard Reset Complete timer */
+ PR_TIMER_HARD_RESET_COMPLETE,
+
+ /* Sink TX timer */
+ PR_TIMER_SINK_TX,
+
+ /* timeout to limit waiting on TCPC response (not in spec) */
+ PR_TIMER_TCPC_TX_TIMEOUT,
+
+
+ /* Time a port shall wait before it can determine it is attached */
+ TC_TIMER_CC_DEBOUNCE,
+
+ /* Time to debounce exit low power mode */
+ TC_TIMER_LOW_POWER_EXIT_TIME,
+
+ /* Time to enter low power mode */
+ TC_TIMER_LOW_POWER_TIME,
+
+ /* Role toggle timer */
+ TC_TIMER_NEXT_ROLE_SWAP,
+
+ /*
+ * Time a Sink port shall wait before it can determine it is detached
+ * due to the potential for USB PD signaling on CC as described in
+ * the state definitions.
+ */
+ TC_TIMER_PD_DEBOUNCE,
+
+ /* Generic timer */
+ TC_TIMER_TIMEOUT,
+
+ /*
+ * Time a port shall wait before it can determine it is
+ * re-attached during the try-wait process.
+ */
+ TC_TIMER_TRY_WAIT_DEBOUNCE,
+
+ /*
+ * Time to ignore Vbus absence due to external IC debounce detection
+ * logic immediately after a power role swap.
+ */
+ TC_TIMER_VBUS_DEBOUNCE,
+
+ PD_TIMER_COUNT
+};
+BUILD_ASSERT(PD_TIMER_COUNT <= 32);
+
+/*
+ * pd_timer_init
+ * Initialize Power Delivery Timer module
+ *
+ * @param port USB-C port number
+ */
+void pd_timer_init(int port);
+
+/*
+ * pd_timer_enable
+ * Initiate an enabled timer
+ *
+ * @param port USB-C port number
+ * @param timer Requested pd_task_timer
+ * @param expires_us Expiration time relative to "now"
+ */
+void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us);
+
+/*
+ * pd_timer_disable
+ * Disable a timer
+ *
+ * @param port USB-C port number
+ * @param timer Requested pd_task_timer
+ */
+void pd_timer_disable(int port, enum pd_task_timer timer);
+
+
+/*
+ * pd_timer_is_disabled
+ * Determine if a timer is currently disabled
+ *
+ * @param port USB-C port number
+ * @param timer Requested pd_task_timer
+ * @return True if the timer is disabled, otherwise false
+ */
+bool pd_timer_is_disabled(int port, enum pd_task_timer timer);
+
+/*
+ * pd_timer_is_expired
+ * Determine if a timer is expired
+ *
+ * @param port USB-C port number
+ * @param timer Requested pd_task_timer
+ * @return True if the timer is enabled and expired, otherwise false
+ */
+bool pd_timer_is_expired(int port, enum pd_task_timer timer);
+
+/*
+ * pd_timer_manage_expired
+ * Convert an active/expired timer to be inactive/expired. This will allow
+ * the code to continue to check for expired without having this timer as
+ * part of the pd_timer_next_expiration decision.
+ *
+ * @param port USB-C port number
+ */
+void pd_timer_manage_expired(int port);
+
+/*
+ * pd_timer_next_expiration
+ * Retrieve the next active expiration time
+ *
+ * @param port USB-C port number
+ * @return >= 0 is the number of uSeconds until we should wake up.
+ * -1 no pending timeout
+ */
+int pd_timer_next_expiration(int port);
+
+
+/*
+ * pd_timer_dump
+ * Debug display of the timers for a given port
+ *
+ * @param port USB-C port number
+ */
+void pd_timer_dump(int port);
+
+#endif /* __CROS_EC_USB_PD_TIMER_H */
diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt
index d5b3ed8ddc..d06f56da6a 100644
--- a/zephyr/CMakeLists.txt
+++ b/zephyr/CMakeLists.txt
@@ -276,6 +276,7 @@ zephyr_sources_ifdef(CONFIG_PLATFORM_EC_USB_PORT_POWER_DUMB
zephyr_sources_ifdef(CONFIG_PLATFORM_EC_USB_POWER_DELIVERY
"${PLATFORM_EC}/common/usb_common.c"
"${PLATFORM_EC}/common/usbc/usbc_task.c"
+ "${PLATFORM_EC}/common/usbc/usb_pd_timer.c"
"${PLATFORM_EC}/common/usbc/usb_sm.c"
"${PLATFORM_EC}/common/usbc_intr_task.c")