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:
Jett Rink 2019-10-17 10:14:24 -06:00 committed by Commit Bot
parent 664d4e7810
commit cb2fa8b437
4 changed files with 232 additions and 80 deletions

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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)