usb: call pd_execute_data_swap within tc_set_data
We need to let board specific code run any time we change the data role on our USB-C connection. When looking at all of the calls to tc_set_data_role, I realized that we don't really reset any data role until we start a new contract with SNK/SRC ready. We will do need to call into the code that disables the MUX lines when we detach. To do this, I created a super state for SNK/SRC unattached. BRANCH=none BUG=none TEST=builds. No board has an OTG signal using the new stack yet Change-Id: I017d20b2e1973b31ebf2b8925a7f8c5488a8ee24 Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1864427
This commit is contained in:
parent
664d4e7810
commit
cb2fa8b437
|
@ -87,6 +87,7 @@ enum usb_tc_state {
|
|||
TC_CC_OPEN,
|
||||
TC_CC_RD,
|
||||
TC_CC_RP,
|
||||
TC_UNATTACHED,
|
||||
};
|
||||
/* Forward declare the full list of states. This is indexed by usb_tc_state */
|
||||
static const struct usb_state tc_states[];
|
||||
|
@ -708,12 +709,6 @@ test_export_static enum usb_tc_state get_state_tc(const int port)
|
|||
return tc[port].ctx.current - &tc_states[0];
|
||||
}
|
||||
|
||||
/* Get the previous TypeC state. */
|
||||
static enum usb_tc_state get_last_state_tc(const int port)
|
||||
{
|
||||
return tc[port].ctx.previous - &tc_states[0];
|
||||
}
|
||||
|
||||
static void print_current_state(const int port)
|
||||
{
|
||||
CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]);
|
||||
|
@ -794,6 +789,12 @@ void tc_set_data_role(int port, int role)
|
|||
if (IS_ENABLED(CONFIG_USBC_SS_MUX))
|
||||
set_usb_mux_with_current_data_role(port);
|
||||
|
||||
/*
|
||||
* Run any board-specific code for role swap (e.g. setting OTG signals
|
||||
* to SoC).
|
||||
*/
|
||||
pd_execute_data_swap(port, role);
|
||||
|
||||
/* Notify TCPC of role update */
|
||||
tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
|
||||
}
|
||||
|
@ -1630,29 +1631,34 @@ static void tc_error_recovery_run(const int port)
|
|||
}
|
||||
}
|
||||
|
||||
/* Set the CC resistors to Rd and update the TCPC power role header */
|
||||
static void set_rd(const int port)
|
||||
{
|
||||
/*
|
||||
* Both CC1 and CC2 pins shall be independently terminated to
|
||||
* ground through Rd. Reset last cc change time.
|
||||
*/
|
||||
tcpm_set_cc(port, TYPEC_CC_RD);
|
||||
tc[port].cc_last_change = get_time().val;
|
||||
|
||||
/* Set power role to sink */
|
||||
tc_set_power_role(port, PD_ROLE_SINK);
|
||||
tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unattached.SNK
|
||||
*
|
||||
* Super State Entry Actions:
|
||||
* Vconn Off
|
||||
* Place Rd on CC
|
||||
* Set power role to SINK
|
||||
* Super State is Unattached state
|
||||
*/
|
||||
static void tc_unattached_snk_entry(const int port)
|
||||
{
|
||||
if (get_last_state_tc(port) != TC_UNATTACHED_SRC)
|
||||
print_current_state(port);
|
||||
/* Set Rd since we are not in the Rd superstate */
|
||||
set_rd(port);
|
||||
|
||||
if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
|
||||
charge_manager_update_dualrole(port, CAP_UNKNOWN);
|
||||
|
||||
/*
|
||||
* Indicate that the port is disconnected so the board
|
||||
* can restore state from any previous data swap.
|
||||
*
|
||||
* NOTE: This is no-op change. This is cleaned up further in a child CL
|
||||
*/
|
||||
pd_execute_data_swap(port, PD_ROLE_DFP);
|
||||
tc[port].next_role_swap = get_time().val + PD_T_DRP_SNK;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
|
||||
|
@ -2002,18 +2008,31 @@ static void tc_dbg_acc_snk_run(const int port)
|
|||
}
|
||||
}
|
||||
|
||||
/* Set the CC resistors to Rp and update the TCPC power role header */
|
||||
static void set_rp(const int port)
|
||||
{
|
||||
/*
|
||||
* Both CC1 and CC2 pins shall be independently pulled
|
||||
* up through Rp. Reset last cc change time.
|
||||
*/
|
||||
tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP);
|
||||
tcpm_set_cc(port, TYPEC_CC_RP);
|
||||
tc[port].cc_last_change = get_time().val;
|
||||
|
||||
/* Set power role to source */
|
||||
tc_set_power_role(port, PD_ROLE_SOURCE);
|
||||
tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unattached.SRC
|
||||
*
|
||||
* Super State Entry Actions:
|
||||
* Vconn Off
|
||||
* Place Rp on CC
|
||||
* Set power role to SOURCE
|
||||
* Super State is Unattached state
|
||||
*/
|
||||
static void tc_unattached_src_entry(const int port)
|
||||
{
|
||||
if (get_last_state_tc(port) != TC_UNATTACHED_SNK)
|
||||
print_current_state(port);
|
||||
/* Set Rd since we are not in the Rd superstate */
|
||||
set_rp(port);
|
||||
|
||||
if (IS_ENABLED(CONFIG_USBC_PPC)) {
|
||||
/* There is no sink connected. */
|
||||
|
@ -2029,16 +2048,6 @@ static void tc_unattached_src_entry(const int port)
|
|||
if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
|
||||
charge_manager_update_dualrole(port, CAP_UNKNOWN);
|
||||
|
||||
tc_set_data_role(port, PD_ROLE_DFP);
|
||||
|
||||
/*
|
||||
* Indicate that the port is disconnected so the board
|
||||
* can restore state from any previous data swap.
|
||||
*
|
||||
* NOTE: This is no-op change. This is cleaned up further in a child CL
|
||||
*/
|
||||
pd_execute_data_swap(port, PD_ROLE_DFP);
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
|
||||
tc[port].flags = 0;
|
||||
tc[port].pd_enable = 0;
|
||||
|
@ -2556,42 +2565,6 @@ static void tc_ct_attached_snk_exit(int port)
|
|||
}
|
||||
#endif /* CONFIG_USB_PE_SM */
|
||||
|
||||
/**
|
||||
* Super State CC_RD
|
||||
*/
|
||||
static void tc_cc_rd_entry(const int port)
|
||||
{
|
||||
/*
|
||||
* Both CC1 and CC2 pins shall be independently terminated to
|
||||
* ground through Rd. Reset last cc change time.
|
||||
*/
|
||||
tcpm_set_cc(port, TYPEC_CC_RD);
|
||||
tc[port].cc_last_change = get_time().val;
|
||||
|
||||
/* Set power role to sink */
|
||||
tc_set_power_role(port, PD_ROLE_SINK);
|
||||
tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Super State CC_RP
|
||||
*/
|
||||
static void tc_cc_rp_entry(const int port)
|
||||
{
|
||||
/* Set power role to source */
|
||||
tc_set_power_role(port, PD_ROLE_SOURCE);
|
||||
tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
|
||||
|
||||
/*
|
||||
* Both CC1 and CC2 pins shall be independently pulled
|
||||
* up through Rp. Reset last cc change time.
|
||||
*/
|
||||
tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP);
|
||||
tcpm_set_cc(port, TYPEC_CC_RP);
|
||||
tc[port].cc_last_change = get_time().val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Super State CC_OPEN
|
||||
*/
|
||||
|
@ -2619,6 +2592,38 @@ static void tc_cc_open_entry(const int port)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Super State CC_RD
|
||||
*/
|
||||
static void tc_cc_rd_entry(const int port)
|
||||
{
|
||||
set_rd(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super State CC_RP
|
||||
*/
|
||||
static void tc_cc_rp_entry(const int port)
|
||||
{
|
||||
set_rp(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super State Unattached
|
||||
*
|
||||
* Ensures that any time we unattached we can perform an action without
|
||||
* repeating it during DRP toggle
|
||||
*/
|
||||
static void tc_unattached_entry(const int port)
|
||||
{
|
||||
/* This only prints the first time we enter a unattached state */
|
||||
print_current_state(port);
|
||||
|
||||
/* This disables the mux when we disconnect on a port */
|
||||
if (IS_ENABLED(CONFIG_USBC_SS_MUX))
|
||||
set_usb_mux_with_current_data_role(port);
|
||||
}
|
||||
|
||||
void tc_run(const int port)
|
||||
{
|
||||
run_state(port, &tc[port].ctx);
|
||||
|
@ -2627,9 +2632,14 @@ void tc_run(const int port)
|
|||
/*
|
||||
* Type-C State Hierarchy (Sub-States are listed inside the boxes)
|
||||
*
|
||||
* |TC_UNATTACHED ---------|
|
||||
* | |
|
||||
* | TC_UNATTACHED_SNK |
|
||||
* | TC_UNATTACHED_SRC |
|
||||
* |-----------------------|
|
||||
*
|
||||
* |TC_CC_RD --------------| |TC_CC_RP ------------------------|
|
||||
* | | | |
|
||||
* | TC_UNATTACHED_SNK | | TC_UNATTACHED_SRC |
|
||||
* | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC |
|
||||
* | TC_TRY_WAIT_SNK | | TC_TRY_SRC |
|
||||
* | TC_DBG_ACC_SNK | | TC_UNORIENTED_DBG_ACC_SRC |
|
||||
|
@ -2654,6 +2664,9 @@ static const struct usb_state tc_states[] = {
|
|||
[TC_CC_RP] = {
|
||||
.entry = tc_cc_rp_entry,
|
||||
},
|
||||
[TC_UNATTACHED] = {
|
||||
.entry = tc_unattached_entry,
|
||||
},
|
||||
/* Normal States */
|
||||
[TC_DISABLED] = {
|
||||
.entry = tc_disabled_entry,
|
||||
|
@ -2669,7 +2682,7 @@ static const struct usb_state tc_states[] = {
|
|||
[TC_UNATTACHED_SNK] = {
|
||||
.entry = tc_unattached_snk_entry,
|
||||
.run = tc_unattached_snk_run,
|
||||
.parent = &tc_states[TC_CC_RD],
|
||||
.parent = &tc_states[TC_UNATTACHED],
|
||||
},
|
||||
[TC_ATTACH_WAIT_SNK] = {
|
||||
.entry = tc_attach_wait_snk_entry,
|
||||
|
@ -2695,7 +2708,7 @@ static const struct usb_state tc_states[] = {
|
|||
[TC_UNATTACHED_SRC] = {
|
||||
.entry = tc_unattached_src_entry,
|
||||
.run = tc_unattached_src_run,
|
||||
.parent = &tc_states[TC_CC_RP],
|
||||
.parent = &tc_states[TC_UNATTACHED],
|
||||
},
|
||||
[TC_ATTACH_WAIT_SRC] = {
|
||||
.entry = tc_attach_wait_src_entry,
|
||||
|
|
|
@ -299,8 +299,7 @@ int ncp15wb_calculate_temp(uint16_t adc);
|
|||
|
||||
/* Common TypeC tests defines */
|
||||
#if defined(TEST_USB_TYPEC_VPD) || \
|
||||
defined(TEST_USB_TYPEC_CTVPD) || \
|
||||
defined(TEST_USB_TYPEC_DRP_ACC_TRYSRC)
|
||||
defined(TEST_USB_TYPEC_CTVPD)
|
||||
#define CONFIG_USB_PID 0x5036
|
||||
#define VPD_HW_VERSION 0x0001
|
||||
#define VPD_FW_VERSION 0x0001
|
||||
|
@ -335,6 +334,12 @@ int ncp15wb_calculate_temp(uint16_t adc);
|
|||
#define CONFIG_USB_TYPEC_DRP_ACC_TRYSRC
|
||||
#define CONFIG_USB_PD_DUAL_ROLE
|
||||
#define CONFIG_USB_PD_TRY_SRC
|
||||
#define CONFIG_USB_TYPEC_SM
|
||||
#define CONFIG_USB_SM_FRAMEWORK
|
||||
#define CONFIG_USB_PD_PORT_COUNT 1
|
||||
#define CONFIG_USBC_SS_MUX
|
||||
#define CONFIG_USB_PD_VBUS_DETECT_TCPC
|
||||
#define CONFIG_USB_POWER_DELIVERY
|
||||
#undef CONFIG_USB_PRL_SM
|
||||
#undef CONFIG_USB_PE_SM
|
||||
#endif
|
||||
|
|
|
@ -4,11 +4,133 @@
|
|||
*
|
||||
* Test USB Type-C VPD and CTVPD module.
|
||||
*/
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "test_util.h"
|
||||
#include "usb_sm_checks.h"
|
||||
#include "charge_manager.h"
|
||||
#include "mock/tcpc_mock.h"
|
||||
#include "mock/usb_mux_mock.h"
|
||||
#include "task.h"
|
||||
#include "test_util.h"
|
||||
#include "timer.h"
|
||||
#include "usb_mux.h"
|
||||
#include "usb_pd_tcpm.h"
|
||||
#include "usb_sm_checks.h"
|
||||
|
||||
#define PORT0 0
|
||||
|
||||
/* Install Mock TCPC and MUX drivers */
|
||||
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
|
||||
{
|
||||
.drv = &mock_tcpc_driver,
|
||||
},
|
||||
};
|
||||
|
||||
struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
|
||||
{
|
||||
.driver = &mock_usb_mux_driver,
|
||||
}
|
||||
};
|
||||
|
||||
void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil)
|
||||
{
|
||||
/* Do Nothing, but needed for linking */
|
||||
}
|
||||
|
||||
__maybe_unused static int test_mux_con_dis_as_src(void)
|
||||
{
|
||||
/* Update CC lines send state machine event to process */
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_RD;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
|
||||
/* This wait trainsitions through AttachWait.SRC then Attached.SRC */
|
||||
task_wait_event(SECOND);
|
||||
|
||||
/* We are in Attached.SRC now */
|
||||
TEST_EQ(mock_usb_mux.state, TYPEC_MUX_USB, "%d");
|
||||
TEST_EQ(mock_usb_mux.num_set_calls, 1, "%d");
|
||||
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
|
||||
/* This wait will go through TryWait.SNK then to Unattached.SNK */
|
||||
task_wait_event(10 * SECOND);
|
||||
|
||||
/* We are in Unattached.SNK. The mux should have detached */
|
||||
TEST_EQ(mock_usb_mux.state, TYPEC_MUX_NONE, "%d");
|
||||
TEST_EQ(mock_usb_mux.num_set_calls, 2, "%d");
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
__maybe_unused static int test_mux_con_dis_as_snk(void)
|
||||
{
|
||||
/* Update CC lines send state machine event to process */
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_RP_3_0;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.vbus_level = 1;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
|
||||
/* This wait will go through AttachWait.SNK to Attached.SNK */
|
||||
task_wait_event(5 * SECOND);
|
||||
|
||||
/* We are in Attached.SNK now */
|
||||
TEST_EQ(mock_usb_mux.state, TYPEC_MUX_USB, "%d");
|
||||
TEST_EQ(mock_usb_mux.num_set_calls, 1, "%d");
|
||||
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.vbus_level = 0;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
|
||||
/* This wait will go through TryWait.SNK then to Unattached.SNK */
|
||||
task_wait_event(10 * SECOND);
|
||||
|
||||
/* We are in Unattached.SNK. The mux should have detached */
|
||||
TEST_EQ(mock_usb_mux.state, TYPEC_MUX_NONE, "%d");
|
||||
TEST_EQ(mock_usb_mux.num_set_calls, 2, "%d");
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
__maybe_unused static int test_power_role_set(void)
|
||||
{
|
||||
/* Print out header changes for easier debugging */
|
||||
mock_tcpc.should_print_header_changes = true;
|
||||
|
||||
/* Update CC lines send state machine event to process */
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_RD;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
task_wait_event(10 * SECOND);
|
||||
|
||||
/* We are in Attached.SRC now */
|
||||
TEST_EQ(mock_tcpc.power_role, PD_ROLE_SOURCE, "%d");
|
||||
TEST_EQ(mock_tcpc.data_role, PD_ROLE_DFP, "%d");
|
||||
|
||||
/*
|
||||
* We allow 2 separate calls to update the header since power and data
|
||||
* role updates can be separate calls depending on the state is came
|
||||
* from.
|
||||
*/
|
||||
TEST_LE(mock_tcpc.num_calls_to_set_header, 2, "%d");
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/* Reset the mocks before each test */
|
||||
void before_test(void)
|
||||
{
|
||||
mock_usb_mux_reset();
|
||||
mock_tcpc_reset();
|
||||
}
|
||||
|
||||
void after_test(void)
|
||||
{
|
||||
/* Disconnect any CC lines */
|
||||
mock_tcpc.cc1 = TYPEC_CC_VOLT_OPEN;
|
||||
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
|
||||
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
|
||||
}
|
||||
|
||||
void run_test(void)
|
||||
{
|
||||
|
@ -18,6 +140,10 @@ void run_test(void)
|
|||
task_wake(TASK_ID_PD_C0);
|
||||
task_wait_event(5 * MSEC);
|
||||
|
||||
RUN_TEST(test_mux_con_dis_as_src);
|
||||
RUN_TEST(test_mux_con_dis_as_snk);
|
||||
RUN_TEST(test_power_role_set);
|
||||
|
||||
/* Do basic state machine sanity checks last. */
|
||||
RUN_TEST(test_tc_no_parent_cycles);
|
||||
RUN_TEST(test_tc_no_empty_state);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#define CONFIG_TEST_MOCK_LIST \
|
||||
MOCK(USB_MUX) \
|
||||
MOCK(TCPC)
|
Loading…
Reference in New Issue