blob: 558d697fd1b389c148c98c4844461a67d8c3fe67 [file] [log] [blame]
/* Copyright 2021 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "assert.h"
#include "atomic.h"
#include "atomic_bit.h"
#include "common.h"
#include "console.h"
#include "limits.h"
#include "math_util.h"
#include "system.h"
#include "usb_pd_timer.h"
#include "usb_tc_sm.h"
#define MAX_PD_PORTS CONFIG_USB_PD_PORT_MAX_COUNT
#define MAX_PD_TIMERS PD_TIMER_COUNT
#define PD_TIMERS_ALL_MASK (UINT64_MAX >> (64 - PD_TIMER_COUNT))
#define MAX_EXPIRE (0x7FFFFFFF)
#define NO_TIMEOUT (-1)
#define EXPIRE_NOW (0)
#define PD_SET_ACTIVE(p, bit) \
atomic_set_bit(timer_active, (p) * PD_TIMER_COUNT + (bit))
#define PD_CLR_ACTIVE(p, bit) \
atomic_clear_bit(timer_active, (p) * PD_TIMER_COUNT + (bit))
#define PD_CHK_ACTIVE(p, bit) \
atomic_test_bit(timer_active, (p) * PD_TIMER_COUNT + (bit))
#define PD_SET_DISABLED(p, bit) \
atomic_set_bit(timer_disabled, (p) * PD_TIMER_COUNT + (bit))
#define PD_CLR_DISABLED(p, bit) \
atomic_clear_bit(timer_disabled, (p) * PD_TIMER_COUNT + (bit))
#define PD_CHK_DISABLED(p, bit) \
atomic_test_bit(timer_disabled, (p) * PD_TIMER_COUNT + (bit))
test_mockable_static ATOMIC_DEFINE(timer_active, PD_TIMER_COUNT *MAX_PD_PORTS);
test_mockable_static ATOMIC_DEFINE(timer_disabled,
PD_TIMER_COUNT *MAX_PD_PORTS);
static uint64_t timer_expires[MAX_PD_PORTS][PD_TIMER_COUNT];
/*
* 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[] = {
[DPM_TIMER_PD_BUTTON_LONG_PRESS] = "DPM-PD_BUTTON_LONG_PRESS",
[DPM_TIMER_PD_BUTTON_SHORT_PRESS] = "DPM-PD_BUTTON_SHORT_PRESS",
[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",
[PE_TIMER_VCONN_DISCHARGE] = "PE-VCONN_DISCHARGE",
[PE_TIMER_VCONN_REAPPLIED] = "PE-VCONN_REAPPLIED",
[PE_TIMER_DATA_RESET_FAIL] = "PE-DATA_RESET_FAIL",
[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)
{
if (PD_CHK_ACTIVE(port, timer)) {
PD_CLR_ACTIVE(port, timer);
if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
count[port]--;
}
PD_CLR_DISABLED(port, timer);
}
static bool pd_timer_is_active(int port, enum pd_task_timer timer)
{
return PD_CHK_ACTIVE(port, timer);
}
static bool pd_timer_is_inactive(int port, enum pd_task_timer timer)
{
return !PD_CHK_ACTIVE(port, timer) && !PD_CHK_DISABLED(port, timer);
}
/*****************************************************************************
* PD_TIMER public functions
*/
void pd_timer_init(int port)
{
if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
count[port] = 0;
/*
* Set timers to init state for "port".
*/
for (int bit = 0; bit < PD_TIMER_COUNT; bit++) {
PD_CLR_ACTIVE(port, bit);
PD_SET_DISABLED(port, bit);
}
}
void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us)
{
if (!PD_CHK_ACTIVE(port, timer)) {
PD_SET_ACTIVE(port, timer);
if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) {
count[port]++;
if (count[port] > max_count[port])
max_count[port] = count[port];
}
}
PD_CLR_DISABLED(port, timer);
timer_expires[port][timer] = get_time().val + expires_us;
}
void pd_timer_disable(int port, enum pd_task_timer timer)
{
if (PD_CHK_ACTIVE(port, timer)) {
PD_CLR_ACTIVE(port, timer);
if (IS_ENABLED(CONFIG_CMD_PD_TIMER))
count[port]--;
}
PD_SET_DISABLED(port, timer);
}
void pd_timer_disable_range(int port, enum pd_timer_range range)
{
int start, end;
enum pd_task_timer timer;
switch (range) {
case DPM_TIMER_RANGE:
start = DPM_TIMER_START;
end = DPM_TIMER_END;
break;
case PE_TIMER_RANGE:
start = PE_TIMER_START;
end = PE_TIMER_END;
break;
case PR_TIMER_RANGE:
start = PR_TIMER_START;
end = PR_TIMER_END;
break;
case TC_TIMER_RANGE:
start = TC_TIMER_START;
end = TC_TIMER_END;
break;
default:
return;
}
for (timer = start; timer <= end; ++timer)
pd_timer_disable(port, timer);
}
bool pd_timer_is_disabled(int port, enum pd_task_timer timer)
{
return PD_CHK_DISABLED(port, timer);
}
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;
for (timer = 0; timer < PD_TIMER_COUNT; ++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 < PD_TIMER_COUNT; ++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
test_mockable_static 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 < PD_TIMER_COUNT; ++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%s)", timer,
pd_timer_names[timer], (uint32_t)delta,
tc_event_loop_is_paused(port) ? "-PAUSED" :
"");
} else {
ccprints("[%2d] Inactive: %s", timer,
pd_timer_names[timer]);
}
}
}
#endif /* CONFIG_CMD_PD_TIMER */