pd: allow policy layer to ask for PR/DR_swap on new contract

Allow policy layer to request a PR or DR swap upon formation of
a power contract. Zinger always asks for a data swap so it can
be a UFP, and Samus asks for a data swap only if it is a UFP to
become a DFP.

BUG=chrome-os-partner:33754, chrome-os-partner:31195
BRANCH=samus
TEST=load onto samus and zinger and make sure they swap roles
upon connect with no collisions

Change-Id: I275c9669549c26f25c58f80845daad8edab11313
Signed-off-by: Alec Berg <alecaberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/229327
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
Alec Berg
2014-11-12 12:51:03 -08:00
committed by chrome-internal-fetch
parent feecd2ac58
commit 9de2ef515f
14 changed files with 244 additions and 84 deletions

View File

@@ -116,13 +116,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always refuse power swap */
return 0;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always refuse data swap */
return 0;
@@ -132,6 +132,11 @@ void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
/* ----------------- Vendor Defined Messages ------------------ */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
0, /* data caps as USB device */

View File

@@ -154,13 +154,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always refuse power swap */
return 0;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always refuse data swap */
return 0;
@@ -170,3 +170,8 @@ void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}

View File

@@ -143,13 +143,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
@@ -159,6 +159,11 @@ void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
/* ----------------- Vendor Defined Messages ------------------ */
const struct svdm_response svdm_rsp = {
.identity = NULL,

View File

@@ -116,13 +116,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always refuse power swap */
return 0;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always refuse data swap */
return 0;
@@ -132,6 +132,11 @@ void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
/* ----------------- Vendor Defined Messages ------------------ */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
0, /* data caps as USB device */

View File

@@ -145,13 +145,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
@@ -162,6 +162,11 @@ void pd_execute_data_swap(int port, int data_role)
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
return 0;

View File

@@ -170,13 +170,13 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
@@ -186,3 +186,8 @@ void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}

View File

@@ -158,19 +158,24 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* TODO: use battery level to decide to accept/reject power swap */
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
void pd_execute_data_swap(int port, int data_role)
{
/* TODO: what do we need to do to change host controller data role? */

View File

@@ -158,19 +158,24 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* TODO: use battery level to decide to accept/reject power swap */
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
void pd_execute_data_swap(int port, int data_role)
{
/* TODO: what do we need to do to change host controller data role? */

View File

@@ -206,14 +206,14 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* TODO: use battery level to decide to accept/reject power swap */
/* Always allow power swap */
return 1;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Allow data swap if we are a UFP, otherwise don't allow */
return (data_role == PD_ROLE_UFP) ? 1 : 0;
@@ -223,6 +223,14 @@ void pd_execute_data_swap(int port, int data_role)
{
/* TODO: when switching to UFP need to open D+/D- switches */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
/* If UFP, try to switch to DFP */
if (partner_dr_swap && dr_role == PD_ROLE_UFP)
pd_request_data_swap(port);
}
/* ----------------- Vendor Defined Messages ------------------ */
const struct svdm_response svdm_rsp = {
.identity = NULL,

View File

@@ -156,18 +156,23 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
int pd_power_swap(int port)
int pd_check_power_swap(int port)
{
/* Always refuse power swap */
return 0;
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Always allow data swap */
return 1;
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
}
void pd_execute_data_swap(int port, int data_role)
{
/* Do nothing */

View File

@@ -253,7 +253,7 @@ void pd_power_supply_reset(int port)
discharge_voltage(voltages[0].ovp);
}
int pd_data_swap(int port, int data_role)
int pd_check_data_swap(int port, int data_role)
{
/* Allow data swap if we are a DFP, otherwise don't allow */
return (data_role == PD_ROLE_DFP) ? 1 : 0;
@@ -264,6 +264,14 @@ void pd_execute_data_swap(int port, int data_role)
/* Do nothing */
}
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap)
{
/* If DFP, try to switch to UFP */
if (partner_dr_swap && dr_role == PD_ROLE_DFP)
pd_request_data_swap(port);
}
int pd_board_checks(void)
{
#ifdef CONFIG_HIBERNATE

View File

@@ -231,11 +231,15 @@ static int pd_src_cap_cnt[PD_PORT_COUNT];
#define PD_FLAGS_PARTNER_DR_DATA (1 << 2) /* port partner is dual-role data */
#define PD_FLAGS_DATA_SWAPPED (1 << 3) /* data swap complete */
#define PD_FLAGS_SNK_CAP_RECVD (1 << 4) /* sink capabilities received */
#define PD_FLAGS_GET_SNK_CAP_SENT (1 << 5) /* get sink cap sent */
#define PD_FLAGS_NEW_CONTRACT (1 << 6) /* new power contract established */
/* Flags to clear on a disconnect */
#define PD_FLAGS_RESET_ON_DISCONNECT_MASK (PD_FLAGS_PARTNER_DR_POWER | \
PD_FLAGS_PARTNER_DR_DATA | \
PD_FLAGS_DATA_SWAPPED | \
PD_FLAGS_SNK_CAP_RECVD)
PD_FLAGS_SNK_CAP_RECVD | \
PD_FLAGS_GET_SNK_CAP_SENT | \
PD_FLAGS_NEW_CONTRACT)
static struct pd_protocol {
/* current port power role (SOURCE or SINK) */
@@ -884,6 +888,28 @@ static void handle_data_request(int port, uint16_t head,
}
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
void pd_request_power_swap(int port)
{
if (pd[port].task_state == PD_STATE_SRC_READY)
set_state(port, PD_STATE_SRC_SWAP_INIT);
else if (pd[port].task_state == PD_STATE_SNK_READY)
set_state(port, PD_STATE_SNK_SWAP_INIT);
task_wake(PORT_TO_TASK_ID(port));
}
#endif
void pd_request_data_swap(int port)
{
if (pd[port].task_state == PD_STATE_SRC_READY)
set_state(port, PD_STATE_SRC_DR_SWAP);
#ifdef CONFIG_USB_PD_DUAL_ROLE
else if (pd[port].task_state == PD_STATE_SNK_READY)
set_state(port, PD_STATE_SNK_DR_SWAP);
#endif
task_wake(PORT_TO_TASK_ID(port));
}
static void pd_dr_swap(int port)
{
pd[port].data_role = !pd[port].data_role;
@@ -987,7 +1013,7 @@ static void handle_ctrl_request(int port, uint16_t head,
break;
case PD_CTRL_PR_SWAP:
#ifdef CONFIG_USB_PD_DUAL_ROLE
if (pd_power_swap(port)) {
if (pd_check_power_swap(port)) {
send_control(port, PD_CTRL_ACCEPT);
if (pd[port].power_role == PD_ROLE_SINK)
set_state(port, PD_STATE_SNK_SWAP_SNK_DISABLE);
@@ -1001,7 +1027,7 @@ static void handle_ctrl_request(int port, uint16_t head,
#endif
break;
case PD_CTRL_DR_SWAP:
if (pd_data_swap(port, pd[port].data_role)) {
if (pd_check_data_swap(port, pd[port].data_role)) {
/* Accept switch and perform data swap */
if (send_control(port, PD_CTRL_ACCEPT) >= 0)
pd_dr_swap(port);
@@ -1419,6 +1445,7 @@ static inline int get_typec_current_limit(int cc_voltage)
void pd_set_new_power_request(int port)
{
pd[port].new_power_request = 1;
task_wake(PORT_TO_TASK_ID(port));
}
#endif /* CONFIG_CHARGE_MANAGER */
@@ -1429,7 +1456,7 @@ void pd_task(void)
uint32_t payload[7];
int timeout = 10*MSEC;
int cc1_volt, cc2_volt;
int res;
int res, incoming_packet;
#ifdef CONFIG_USB_PD_DUAL_ROLE
uint64_t next_role_swap = PD_T_DRP_SNK;
int hard_reset_count = 0;
@@ -1479,12 +1506,15 @@ void pd_task(void)
task_wait_event(timeout);
/* incoming packet ? */
if (pd_rx_started(port) && pd_comm_enabled) {
incoming_packet = 1;
head = analyze_rx(port, payload);
pd_rx_complete(port);
if (head > 0)
handle_request(port, head, payload);
else if (head == PD_ERR_HARD_RESET)
execute_hard_reset(port);
} else {
incoming_packet = 0;
}
/* if nothing to do, verify the state of the world in 500ms */
this_state = pd[port].task_state;
@@ -1550,6 +1580,11 @@ void pd_task(void)
case PD_STATE_SRC_STARTUP:
/* Wait for power source to enable */
if (pd[port].last_state != pd[port].task_state) {
/*
* fake set data role swapped flag so we send
* discover identity when we enter SRC_READY
*/
pd[port].flags |= PD_FLAGS_DATA_SWAPPED;
caps_count = 0;
src_connected = 0;
set_state_timeout(
@@ -1614,12 +1649,8 @@ void pd_task(void)
/* the voltage output is good, notify the source */
res = send_control(port, PD_CTRL_PS_RDY);
if (res >= 0) {
timeout = PD_T_SEND_SOURCE_CAP;
/*
* fake set data role swapped flag so we send
* discover identity when we enter SRC_READY
*/
pd[port].flags |= PD_FLAGS_DATA_SWAPPED;
timeout = 10*MSEC;
pd[port].flags |= PD_FLAGS_NEW_CONTRACT;
/* it'a time to ping regularly the sink */
set_state(port, PD_STATE_SRC_READY);
} else {
@@ -1630,28 +1661,43 @@ void pd_task(void)
break;
case PD_STATE_SRC_READY:
timeout = PD_T_SOURCE_ACTIVITY;
if (pd[port].last_state != pd[port].task_state &&
if (pd[port].last_state != pd[port].task_state)
pd[port].flags |= PD_FLAGS_GET_SNK_CAP_SENT;
/*
* Don't send any PD traffic if we woke up due to
* incoming packet to avoid collisions
*/
if (incoming_packet)
break;
/* Send get sink cap if haven't received it yet */
if ((pd[port].flags & PD_FLAGS_GET_SNK_CAP_SENT) &&
!(pd[port].flags & PD_FLAGS_SNK_CAP_RECVD)) {
/* Get sink cap to know if dual-role device */
send_control(port, PD_CTRL_GET_SINK_CAP);
pd[port].flags &= ~PD_FLAGS_GET_SNK_CAP_SENT;
break;
}
/* Send VDMs once after get sink cap */
/* Check our role policy, which may trigger a swap */
if (pd[port].flags & PD_FLAGS_NEW_CONTRACT) {
pd_new_contract(
port, PD_ROLE_SOURCE,
pd[port].data_role,
pd[port].flags &
PD_FLAGS_PARTNER_DR_POWER,
pd[port].flags &
PD_FLAGS_PARTNER_DR_DATA);
pd[port].flags &= ~PD_FLAGS_NEW_CONTRACT;
break;
}
/* Send discovery SVDMs last */
if (pd[port].data_role == PD_ROLE_DFP &&
(pd[port].flags & PD_FLAGS_DATA_SWAPPED)) {
#ifdef CONFIG_USB_PD_SIMPLE_DFP
/*
* For simple devices that don't support
* alternate mode and are only sources (ie power
* adapters), send custom VDM with info about
* this device once power contract has been
* negotiated.
*/
pd_send_vdm(port, USB_VID_GOOGLE,
VDO_CMD_SEND_INFO,
pd_get_info(), 6);
#else
#ifndef CONFIG_USB_PD_SIMPLE_DFP
pd_send_vdm(port, USB_SID_PD,
CMD_DISCOVER_IDENT, NULL, 0);
#endif
@@ -1813,11 +1859,18 @@ void pd_task(void)
*/
if ((pd[port].last_state != pd[port].task_state)
&& hard_reset_count < PD_HARD_RESET_COUNT
&& pd_comm_enabled)
&& pd_comm_enabled) {
/*
* fake set data role swapped flag so we send
* discover identity when we enter SRC_READY
*/
pd[port].flags |= PD_FLAGS_DATA_SWAPPED;
pd[port].flags |= PD_FLAGS_NEW_CONTRACT;
set_state_timeout(port,
get_time().val +
PD_T_SINK_WAIT_CAP,
PD_STATE_HARD_RESET);
}
break;
case PD_STATE_SNK_REQUESTED:
/* Ensure the power supply actually becomes ready */
@@ -1834,16 +1887,16 @@ void pd_task(void)
PD_STATE_HARD_RESET);
break;
case PD_STATE_SNK_READY:
/* if DFP, send SVDM on entry */
if (pd[port].data_role == PD_ROLE_DFP &&
(pd[port].last_state != pd[port].task_state ||
(pd[port].flags & PD_FLAGS_DATA_SWAPPED))) {
pd_send_vdm(port, USB_SID_PD,
CMD_DISCOVER_IDENT, NULL, 0);
pd[port].flags &= ~PD_FLAGS_DATA_SWAPPED;
}
timeout = 20*MSEC;
/* we have power, check vitals from time to time */
/*
* Don't send any PD traffic if we woke up due to
* incoming packet to avoid collisions
*/
if (incoming_packet)
break;
/* Check for new power to request */
if (pd[port].new_power_request) {
pd[port].new_power_request = 0;
#ifdef CONFIG_CHARGE_MANAGER
@@ -1858,8 +1911,37 @@ void pd_task(void)
#endif
pd_send_request_msg(port,
PD_REQUEST_MAX);
break;
}
timeout = 100*MSEC;
/*
* Give time for source to check role policy first by
* not running this the first time through state.
*/
if ((pd[port].flags & PD_FLAGS_NEW_CONTRACT) &&
pd[port].last_state == pd[port].task_state) {
pd_new_contract(
port, PD_ROLE_SINK,
pd[port].data_role,
pd[port].flags &
PD_FLAGS_PARTNER_DR_POWER,
pd[port].flags &
PD_FLAGS_PARTNER_DR_DATA);
pd[port].flags &= ~PD_FLAGS_NEW_CONTRACT;
break;
}
/* If DFP, send discovery SVDMs */
if (pd[port].data_role == PD_ROLE_DFP &&
(pd[port].flags & PD_FLAGS_DATA_SWAPPED)) {
pd_send_vdm(port, USB_SID_PD,
CMD_DISCOVER_IDENT, NULL, 0);
pd[port].flags &= ~PD_FLAGS_DATA_SWAPPED;
break;
}
/* Sent all messages, don't need to wake very often */
timeout = 200*MSEC;
break;
case PD_STATE_SNK_DR_SWAP:
if (pd[port].last_state != pd[port].task_state) {
@@ -2322,21 +2404,12 @@ static int command_pd(int argc, char **argv)
if (argc < 4)
return EC_ERROR_PARAM_COUNT;
if (!strncasecmp(argv[3], "power", 5)) {
if (pd[port].power_role == PD_ROLE_SINK)
set_state(port, PD_STATE_SNK_SWAP_INIT);
else
set_state(port, PD_STATE_SRC_SWAP_INIT);
task_wake(PORT_TO_TASK_ID(port));
} else if (!strncasecmp(argv[3], "data", 4)) {
if (pd[port].power_role == PD_ROLE_SINK)
set_state(port, PD_STATE_SNK_DR_SWAP);
else
set_state(port, PD_STATE_SRC_DR_SWAP);
task_wake(PORT_TO_TASK_ID(port));
} else {
if (!strncasecmp(argv[3], "power", 5))
pd_request_power_swap(port);
else if (!strncasecmp(argv[3], "data", 4))
pd_request_data_swap(port);
else
return EC_ERROR_PARAM3;
}
} else if (!strncasecmp(argv[2], "ping", 4)) {
int enable;

View File

@@ -1100,7 +1100,7 @@
/* Define if using internal comparator for PD receive */
#undef CONFIG_USB_PD_INTERNAL_COMP
/* Simple DFP, such as power adapter, will send info CVDM on connect */
/* Simple DFP, such as power adapter, will not send discovery VDM on connect */
#undef CONFIG_USB_PD_SIMPLE_DFP
/* Use comparator module for PD RX interrupt */

View File

@@ -689,9 +689,9 @@ void pd_request_source_voltage(int port, int mv);
/**
* Set the PD input current limit.
*
* @port USB-C port number
* @max_ma Maximum current limit
* @supply_voltage Voltage at which current limit is applied
* @param port USB-C port number
* @param max_ma Maximum current limit
* @param supply_voltage Voltage at which current limit is applied
*/
void pd_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage);
@@ -699,9 +699,9 @@ void pd_set_input_current_limit(int port, uint32_t max_ma,
/**
* Set the type-C input current limit.
*
* @port USB-C port number
* @max_ma Maximum current limit
* @supply_voltage Voltage at which current limit is applied
* @param port USB-C port number
* @param max_ma Maximum current limit
* @param supply_voltage Voltage at which current limit is applied
*/
void typec_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage);
@@ -716,25 +716,37 @@ int pd_board_checks(void);
/**
* Check if power swap is allowed.
*
* @port USB-C port number
* @param port USB-C port number
* @return True if power swap is allowed, False otherwise
*/
int pd_power_swap(int port);
int pd_check_power_swap(int port);
/**
* Check if data swap is allowed.
*
* @port USB-C port number
* @data_role current data role
* @param port USB-C port number
* @param data_role current data role
* @return True if data swap is allowed, False otherwise
*/
int pd_data_swap(int port, int data_role);
int pd_check_data_swap(int port, int data_role);
/**
* A new power contract has been established
*
* @param port USB-C port number
* @param pr_role Our power role
* @param dr_role Our data role
* @param partner_pr_swap Partner supports PR_SWAP
* @param partner_dr_swap Partner supports DR_SWAP
*/
void pd_new_contract(int port, int pr_role, int dr_role,
int partner_pr_swap, int partner_dr_swap);
/**
* Execute data swap.
*
* @port USB-C port number
* @data_role new data role
* @param port USB-C port number
* @param data_role new data role
*/
void pd_execute_data_swap(int port, int data_role);
@@ -1046,6 +1058,20 @@ int pd_get_partner_dualrole_capable(int port);
*/
int pd_get_partner_data_swap_capable(int port);
/**
* Request power swap command to be issued
*
* @param port USB-C port number
*/
void pd_request_power_swap(int port);
/**
* Request data swap command to be issued
*
* @param port USB-C port number
*/
void pd_request_data_swap(int port);
/**
* Set the PD communication enabled flag. When communication is disabled,
* the port can still detect connection and source power but will not