diff options
author | Denis Brockus <dbrockus@google.com> | 2021-02-17 12:18:28 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-03-01 22:49:49 +0000 |
commit | 2bdefef51a36f80c56f95278bffba743c501f9a8 (patch) | |
tree | 81f6026831e8b46f94758e808925bcebda06e495 | |
parent | 1aa5f1d725aa778834ef6d92e70c5a479020dc86 (diff) | |
download | chrome-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.mk | 1 | ||||
-rw-r--r-- | common/usbc/usb_pd_timer.c | 238 | ||||
-rw-r--r-- | include/config.h | 1 | ||||
-rw-r--r-- | include/usb_pd_timer.h | 276 | ||||
-rw-r--r-- | zephyr/CMakeLists.txt | 1 |
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") |