PD: Make current USB PD Stack REV 3 compliant
Implement the following required features in the USB PD Rev. 3.0 specification. Not_supported control message: Inform a port partner that a particular message is not supported. Battery capabilities extended message: Report battery design capacity and last full charge capacity. Battery status data message: Report battery state of charge Collision avoidance: New scheme to avoid collisions caused when both source and sink want to send messages. Cable communication: Only the VCONN source can communicate with the cable plug. This is NOT implemented because although the drivers have the capability of communicating with a cable plug, the PD stack doesn't currently need to talk to a cable plug. This is okay since the current PD design doesn't source or sink more than 3 amps and all Type-C cables are required to be 3 amp capable. BUG=b:64411727 BRANCH=None TEST=`make -j buildall` Passed relevant PD Rev 2.0 compliance tests Successful PD negotiation with PD Rev 2.0 and 3.0 chargers Tested with low power none PD charger. Modified a Kevin to operate as a PD 3.0 charger and sent all required messages and verified the return messages. Also tested collision avoidance by verifying that a sink only transmits when the source indicates it's okay. Used Twinkie to verify that PD was operating as v3.0. Signed-off-by: Sam Hurst <shurst@chromium.org> Change-Id: Ifd77e92ec4e9106236f9221393d2bfb97263d979 Reviewed-on: https://chromium-review.googlesource.com/603003 Commit-Ready: Sam Hurst <shurst@google.com> Tested-by: Sam Hurst <shurst@google.com> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
parent
5fd1540e62
commit
c91dbb26d8
|
@ -722,6 +722,7 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
|
|||
payload[0] |= VDO_CMDT(CMDT_RSP_BUSY);
|
||||
rsize = 1;
|
||||
}
|
||||
payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port));
|
||||
} else if (cmd_type == CMDT_RSP_ACK) {
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
||||
struct svdm_amode_data *modep;
|
||||
|
@ -798,6 +799,7 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
|
|||
}
|
||||
|
||||
payload[0] |= VDO_CMDT(CMDT_INIT);
|
||||
payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port));
|
||||
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
||||
} else if (cmd_type == CMDT_RSP_BUSY) {
|
||||
switch (cmd) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include "battery.h"
|
||||
#include "battery_smart.h"
|
||||
#include "board.h"
|
||||
#include "charge_manager.h"
|
||||
#include "charge_state.h"
|
||||
|
@ -100,6 +101,34 @@ enum pd_dual_role_states drp_state = CONFIG_USB_PD_INITIAL_DRP_STATE;
|
|||
static uint8_t pd_try_src_enable;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* The spec. revision is used to index into this array.
|
||||
* Rev 0 (PD 1.0) - return PD_CTRL_REJECT
|
||||
* Rev 1 (PD 2.0) - return PD_CTRL_REJECT
|
||||
* Rev 2 (PD 3.0) - return PD_CTRL_NOT_SUPPORTED
|
||||
*/
|
||||
static const uint8_t refuse[] = {
|
||||
PD_CTRL_REJECT, PD_CTRL_REJECT, PD_CTRL_NOT_SUPPORTED};
|
||||
#define REFUSE(r) refuse[r]
|
||||
#else
|
||||
#define REFUSE(r) PD_CTRL_REJECT
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* The spec. revision is used to index into this array.
|
||||
* Rev 0 (VDO 1.0) - return VDM_VER10
|
||||
* Rev 1 (VDO 1.0) - return VDM_VER10
|
||||
* Rev 2 (VDO 2.0) - return VDM_VER20
|
||||
*/
|
||||
static const uint8_t vdo_ver[] = {
|
||||
VDM_VER10, VDM_VER10, VDM_VER20};
|
||||
#define VDO_VER(v) vdo_ver[v]
|
||||
#else
|
||||
#define VDO_VER(v) VDM_VER10
|
||||
#endif
|
||||
|
||||
static struct pd_protocol {
|
||||
/* current port power role (SOURCE or SINK) */
|
||||
uint8_t power_role;
|
||||
|
@ -158,6 +187,15 @@ static struct pd_protocol {
|
|||
uint16_t dev_id;
|
||||
uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4];
|
||||
enum ec_current_image current_image;
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* PD Collision avoidance buffer */
|
||||
uint16_t ca_buffered;
|
||||
uint16_t ca_header;
|
||||
uint32_t ca_buffer[PDO_MAX_OBJECTS];
|
||||
enum tcpm_transmit_type ca_type;
|
||||
/* protocol revision */
|
||||
uint8_t rev;
|
||||
#endif
|
||||
} pd[CONFIG_USB_PD_PORT_COUNT];
|
||||
|
||||
#ifdef CONFIG_COMMON_RUNTIME
|
||||
|
@ -215,6 +253,18 @@ static inline void set_state_timeout(int port,
|
|||
pd[port].timeout_state = timeout_state;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
int pd_get_rev(int port)
|
||||
{
|
||||
return pd[port].rev;
|
||||
}
|
||||
|
||||
int pd_get_vdo_ver(int port)
|
||||
{
|
||||
return vdo_ver[pd[port].rev];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return flag for pd state is connected */
|
||||
int pd_is_connected(int port)
|
||||
{
|
||||
|
@ -303,10 +353,27 @@ static inline void set_state(int port, enum pd_states next_state)
|
|||
#else /* CONFIG_USB_PD_DUAL_ROLE */
|
||||
if (next_state == PD_STATE_SRC_DISCONNECTED) {
|
||||
#endif
|
||||
/* If we are source, make sure VBUS is off */
|
||||
if (pd[port].power_role == PD_ROLE_SOURCE)
|
||||
/*
|
||||
* If we are source, make sure VBUS is off and
|
||||
* if PD REV3.0, restore RP.
|
||||
*/
|
||||
if (pd[port].power_role == PD_ROLE_SOURCE) {
|
||||
/*
|
||||
* Rp is restored by pd_power_supply_reset if
|
||||
* CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT is defined.
|
||||
*/
|
||||
pd_power_supply_reset(port);
|
||||
|
||||
#if !defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) && \
|
||||
defined(CONFIG_USB_PD_REV30)
|
||||
/* Restore Rp */
|
||||
tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP);
|
||||
tcpm_set_cc(port, TYPEC_CC_RP);
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* Adjust rev to highest level*/
|
||||
pd[port].rev = PD_REV30;
|
||||
#endif
|
||||
pd[port].dev_id = 0;
|
||||
pd[port].flags &= ~PD_FLAGS_RESET_ON_DISCONNECT_MASK;
|
||||
#ifdef CONFIG_CHARGE_MANAGER
|
||||
|
@ -348,6 +415,19 @@ static void inc_id(int port)
|
|||
pd[port].msg_id = (pd[port].msg_id + 1) & PD_MESSAGE_ID_COUNT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static void sink_can_xmit(int port, int rp)
|
||||
{
|
||||
tcpm_select_rp_value(port, rp);
|
||||
tcpm_set_cc(port, TYPEC_CC_RP);
|
||||
}
|
||||
|
||||
static inline void pd_ca_reset(int port)
|
||||
{
|
||||
pd[port].ca_buffered = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void pd_transmit_complete(int port, int status)
|
||||
{
|
||||
if (status == TCPC_TX_COMPLETE_SUCCESS)
|
||||
|
@ -365,12 +445,76 @@ static int pd_transmit(int port, enum tcpm_transmit_type type,
|
|||
/* If comms are disabled, do not transmit, return error */
|
||||
if (!pd_comm_is_enabled(port))
|
||||
return -1;
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* Source-coordinated collision avoidance */
|
||||
/*
|
||||
* In order to avoid message collisions due to asynchronous Messaging
|
||||
* sent from the Sink, the Source sets Rp to SinkTxOk to indicate to
|
||||
* the Sink that it is ok to initiate an AMS. When the Source wishes
|
||||
* to initiate an AMS it sets Rp to SinkTxNG. When the Sink detects
|
||||
* that Rp is set to SinkTxOk it May initiate an AMS. When the Sink
|
||||
* detects that Rp is set to SinkTxNG it Shall Not initiate an AMS
|
||||
* and Shall only send Messages that are part of an AMS the Source has
|
||||
* initiated. Note that this restriction applies to SOP* AMS’s i.e.
|
||||
* for both Port to Port and Port to Cable Plug communications.
|
||||
*
|
||||
* This starts after an Explicit Contract is in place
|
||||
* PD R3 V1.1 Section 2.5.2.
|
||||
*
|
||||
* Note: a Sink can still send Hard Reset signaling at any time.
|
||||
*/
|
||||
if ((pd[port].rev == PD_REV30) &&
|
||||
(pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT)) {
|
||||
if (pd[port].power_role == PD_ROLE_SOURCE) {
|
||||
/*
|
||||
* Inform Sink that it can't transmit. If a sink
|
||||
* transmition is in progress and a collsion occurs,
|
||||
* a reset is generated. This should be rare because
|
||||
* all extended messages are chunked. This effectively
|
||||
* defaults to PD REV 2.0 collision avoidance.
|
||||
*/
|
||||
sink_can_xmit(port, SINK_TX_NG);
|
||||
} else if (type != TCPC_TX_HARD_RESET) {
|
||||
int cc1;
|
||||
int cc2;
|
||||
|
||||
tcpm_get_cc(port, &cc1, &cc2);
|
||||
if (cc1 == TYPEC_CC_VOLT_SNK_1_5 ||
|
||||
cc2 == TYPEC_CC_VOLT_SNK_1_5) {
|
||||
/* Sink can't transmit now. */
|
||||
/* Check if message is already buffered. */
|
||||
if (pd[port].ca_buffered)
|
||||
return -1;
|
||||
|
||||
/* Buffer message and send later. */
|
||||
pd[port].ca_type = type;
|
||||
pd[port].ca_header = header;
|
||||
memcpy(pd[port].ca_buffer,
|
||||
data, sizeof(uint32_t) *
|
||||
PD_HEADER_CNT(header));
|
||||
pd[port].ca_buffered = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tcpm_transmit(port, type, header, data);
|
||||
|
||||
/* Wait until TX is complete */
|
||||
evt = task_wait_event_mask(PD_EVENT_TX, PD_T_TCPC_TX_TIMEOUT);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* If the source just completed a transmit, tell
|
||||
* the sink it can transmit if it wants to.
|
||||
*/
|
||||
if ((pd[port].rev == PD_REV30) &&
|
||||
(pd[port].power_role == PD_ROLE_SOURCE) &&
|
||||
(pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT)) {
|
||||
sink_can_xmit(port, SINK_TX_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (evt & TASK_EVENT_TIMER)
|
||||
return -1;
|
||||
|
||||
|
@ -378,6 +522,29 @@ static int pd_transmit(int port, enum tcpm_transmit_type type,
|
|||
return pd[port].tx_status == TCPC_TX_COMPLETE_SUCCESS ? 1 : -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static void pd_ca_send_pending(int port)
|
||||
{
|
||||
int cc1;
|
||||
int cc2;
|
||||
|
||||
/* Check if a message has been buffered. */
|
||||
if (!pd[port].ca_buffered)
|
||||
return;
|
||||
|
||||
tcpm_get_cc(port, &cc1, &cc2);
|
||||
if ((cc1 != TYPEC_CC_VOLT_SNK_1_5) &&
|
||||
(cc2 != TYPEC_CC_VOLT_SNK_1_5))
|
||||
if (pd_transmit(port, pd[port].ca_type,
|
||||
pd[port].ca_header,
|
||||
pd[port].ca_buffer) < 0)
|
||||
return;
|
||||
|
||||
/* Message was sent, so free up the buffer. */
|
||||
pd[port].ca_buffered = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pd_update_roles(int port)
|
||||
{
|
||||
/* Notify TCPC of role update */
|
||||
|
@ -388,7 +555,8 @@ static int send_control(int port, int type)
|
|||
{
|
||||
int bit_len;
|
||||
uint16_t header = PD_HEADER(type, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, 0);
|
||||
pd[port].data_role, pd[port].msg_id, 0,
|
||||
pd_get_rev(port), 0);
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, NULL);
|
||||
if (debug_level >= 2)
|
||||
|
@ -413,10 +581,12 @@ static int send_source_cap(int port)
|
|||
if (src_pdo_cnt == 0)
|
||||
/* No source capabilities defined, sink only */
|
||||
header = PD_HEADER(PD_CTRL_REJECT, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, 0);
|
||||
pd[port].data_role, pd[port].msg_id, 0,
|
||||
pd_get_rev(port), 0);
|
||||
else
|
||||
header = PD_HEADER(PD_DATA_SOURCE_CAP, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, src_pdo_cnt);
|
||||
pd[port].data_role, pd[port].msg_id, src_pdo_cnt,
|
||||
pd_get_rev(port), 0);
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, src_pdo);
|
||||
if (debug_level >= 2)
|
||||
|
@ -425,12 +595,168 @@ static int send_source_cap(int port)
|
|||
return bit_len;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static int send_battery_cap(int port, uint32_t *payload)
|
||||
{
|
||||
int bit_len;
|
||||
uint16_t msg[6] = {0, 0, 0, 0, 0, 0};
|
||||
uint16_t header = PD_HEADER(PD_EXT_BATTERY_CAP,
|
||||
pd[port].power_role,
|
||||
pd[port].data_role,
|
||||
pd[port].msg_id,
|
||||
3, /* Number of Data Objects */
|
||||
pd[port].rev,
|
||||
1 /* This is an exteded message */
|
||||
);
|
||||
|
||||
/* Set extended header */
|
||||
msg[0] = PD_EXT_HEADER(0, /* Chunk Number */
|
||||
0, /* Request Chunk */
|
||||
9 /* Data Size in bytes */
|
||||
);
|
||||
/* Set VID */
|
||||
msg[1] = USB_VID_GOOGLE;
|
||||
|
||||
/* Set PID */
|
||||
msg[2] = CONFIG_USB_PID;
|
||||
|
||||
if (battery_is_present()) {
|
||||
/*
|
||||
* We only have one fixed battery,
|
||||
* so make sure batt cap ref is 0.
|
||||
*/
|
||||
if (BATT_CAP_REF(payload[0]) != 0) {
|
||||
/* Invalid battery reference */
|
||||
msg[5] = 1;
|
||||
} else {
|
||||
uint32_t v;
|
||||
uint32_t c;
|
||||
|
||||
/*
|
||||
* The Battery Design Capacity field shall return the
|
||||
* Battery’s design capacity in tenths of Wh. If the
|
||||
* Battery is Hot Swappable and is not present, the
|
||||
* Battery Design Capacity field shall be set to 0. If
|
||||
* the Battery is unable to report its Design Capacity,
|
||||
* it shall return 0xFFFF
|
||||
*/
|
||||
msg[3] = 0xffff;
|
||||
|
||||
/*
|
||||
* The Battery Last Full Charge Capacity field shall
|
||||
* return the Battery’s last full charge capacity in
|
||||
* tenths of Wh. If the Battery is Hot Swappable and
|
||||
* is not present, the Battery Last Full Charge Capacity
|
||||
* field shall be set to 0. If the Battery is unable to
|
||||
* report its Design Capacity, the Battery Last Full
|
||||
* Charge Capacity field shall be set to 0xFFFF.
|
||||
*/
|
||||
msg[4] = 0xffff;
|
||||
|
||||
if (battery_design_voltage(&v) == 0) {
|
||||
if (battery_design_capacity(&c) == 0) {
|
||||
/*
|
||||
* Wh = (c * v) / 1000000
|
||||
* 10th of a Wh = Wh * 10
|
||||
*/
|
||||
msg[3] = DIV_ROUND_NEAREST((c * v),
|
||||
100000);
|
||||
}
|
||||
|
||||
if (battery_full_charge_capacity(&c) == 0) {
|
||||
/*
|
||||
* Wh = (c * v) / 1000000
|
||||
* 10th of a Wh = Wh * 10
|
||||
*/
|
||||
msg[4] = DIV_ROUND_NEAREST((c * v),
|
||||
100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, (uint32_t *)msg);
|
||||
if (debug_level >= 2)
|
||||
CPRINTF("batCap>%d\n", bit_len);
|
||||
return bit_len;
|
||||
}
|
||||
|
||||
static int send_battery_status(int port, uint32_t *payload)
|
||||
{
|
||||
int bit_len;
|
||||
uint32_t msg = 0;
|
||||
uint16_t header = PD_HEADER(PD_DATA_BATTERY_STATUS,
|
||||
pd[port].power_role,
|
||||
pd[port].data_role,
|
||||
pd[port].msg_id,
|
||||
1, /* Number of Data Objects */
|
||||
pd[port].rev,
|
||||
0 /* This is NOT an extended message */
|
||||
);
|
||||
|
||||
if (battery_is_present()) {
|
||||
/*
|
||||
* We only have one fixed battery,
|
||||
* so make sure batt cap ref is 0.
|
||||
*/
|
||||
if (BATT_CAP_REF(payload[0]) != 0) {
|
||||
/* Invalid battery reference */
|
||||
msg |= BSDO_INVALID;
|
||||
} else {
|
||||
uint32_t v;
|
||||
uint32_t c;
|
||||
|
||||
if (battery_design_voltage(&v) != 0 ||
|
||||
battery_remaining_capacity(&c) != 0) {
|
||||
msg |= BSDO_CAP(BSDO_CAP_UNKNOWN);
|
||||
} else {
|
||||
/*
|
||||
* Wh = (c * v) / 1000000
|
||||
* 10th of a Wh = Wh * 10
|
||||
*/
|
||||
msg |= BSDO_CAP(DIV_ROUND_NEAREST((c * v),
|
||||
100000));
|
||||
}
|
||||
|
||||
/* Battery is present */
|
||||
msg |= BSDO_PRESENT;
|
||||
|
||||
/*
|
||||
* For drivers that are not smart battery compliant,
|
||||
* battery_status() returns EC_ERROR_UNIMPLEMENTED and
|
||||
* the battery is assumed to be idle.
|
||||
*/
|
||||
if (battery_status(&c) != 0) {
|
||||
msg |= BSDO_IDLE; /* assume idle */
|
||||
} else {
|
||||
if (c & STATUS_FULLY_CHARGED)
|
||||
/* Fully charged */
|
||||
msg |= BSDO_IDLE;
|
||||
else if (c & STATUS_DISCHARGING)
|
||||
/* Discharging */
|
||||
msg |= BSDO_DISCHARGING;
|
||||
/* else battery is charging.*/
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg = BSDO_CAP(BSDO_CAP_UNKNOWN);
|
||||
}
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, &msg);
|
||||
if (debug_level >= 2)
|
||||
CPRINTF("batStat>%d\n", bit_len);
|
||||
|
||||
return bit_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
static void send_sink_cap(int port)
|
||||
{
|
||||
int bit_len;
|
||||
uint16_t header = PD_HEADER(PD_DATA_SINK_CAP, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, pd_snk_pdo_cnt);
|
||||
pd[port].data_role, pd[port].msg_id, pd_snk_pdo_cnt,
|
||||
pd_get_rev(port), 0);
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, pd_snk_pdo);
|
||||
if (debug_level >= 2)
|
||||
|
@ -441,7 +767,8 @@ static int send_request(int port, uint32_t rdo)
|
|||
{
|
||||
int bit_len;
|
||||
uint16_t header = PD_HEADER(PD_DATA_REQUEST, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, 1);
|
||||
pd[port].data_role, pd[port].msg_id, 1,
|
||||
pd_get_rev(port), 0);
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, &rdo);
|
||||
if (debug_level >= 2)
|
||||
|
@ -477,7 +804,8 @@ static int send_bist_cmd(int port)
|
|||
uint32_t bdo = BDO(BDO_MODE_CARRIER2, 0);
|
||||
int bit_len;
|
||||
uint16_t header = PD_HEADER(PD_DATA_BIST, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id, 1);
|
||||
pd[port].data_role, pd[port].msg_id, 1,
|
||||
pd_get_rev(port), 0);
|
||||
|
||||
bit_len = pd_transmit(port, TCPC_TX_SOP, header, &bdo);
|
||||
CPRINTF("BIST>%d\n", bit_len);
|
||||
|
@ -541,6 +869,10 @@ void pd_execute_hard_reset(int port)
|
|||
pd_dfp_exit_mode(port, 0, 0);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
pd[port].rev = PD_REV30;
|
||||
pd_ca_reset(port);
|
||||
#endif
|
||||
/*
|
||||
* Fake set last state to hard reset to make sure that the next
|
||||
* state to run knows that we just did a hard reset.
|
||||
|
@ -737,6 +1069,13 @@ static void handle_data_request(int port, uint16_t head,
|
|||
PD_STATE_SNK_HARD_RESET_RECOVER)
|
||||
#endif
|
||||
|| (pd[port].task_state == PD_STATE_SNK_READY)) {
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* Only adjust sink rev if source rev is higher.
|
||||
*/
|
||||
if (PD_HEADER_REV(head) < pd[port].rev)
|
||||
pd[port].rev = PD_HEADER_REV(head);
|
||||
#endif
|
||||
/* Port partner is now known to be PD capable */
|
||||
pd[port].flags |= PD_FLAGS_PREVIOUS_PD_CONN;
|
||||
|
||||
|
@ -751,7 +1090,14 @@ static void handle_data_request(int port, uint16_t head,
|
|||
break;
|
||||
#endif /* CONFIG_USB_PD_DUAL_ROLE */
|
||||
case PD_DATA_REQUEST:
|
||||
if ((pd[port].power_role == PD_ROLE_SOURCE) && (cnt == 1))
|
||||
if ((pd[port].power_role == PD_ROLE_SOURCE) && (cnt == 1)) {
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* Adjust the rev level to what the sink supports. If
|
||||
* they're equal, no harm done.
|
||||
*/
|
||||
pd[port].rev = PD_HEADER_REV(head);
|
||||
#endif
|
||||
if (!pd_check_requested_voltage(payload[0], port)) {
|
||||
if (send_control(port, PD_CTRL_ACCEPT) < 0)
|
||||
/*
|
||||
|
@ -763,6 +1109,15 @@ static void handle_data_request(int port, uint16_t head,
|
|||
|
||||
/* explicit contract is now in place */
|
||||
pd[port].flags |= PD_FLAGS_EXPLICIT_CONTRACT;
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/*
|
||||
* Start Source-coordinated collision
|
||||
* avoidance
|
||||
*/
|
||||
if (pd[port].rev == PD_REV30 &&
|
||||
pd[port].power_role == PD_ROLE_SOURCE)
|
||||
sink_can_xmit(port, SINK_TX_OK);
|
||||
#endif
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
pd_set_saved_active(port, 1);
|
||||
#endif
|
||||
|
@ -770,6 +1125,7 @@ static void handle_data_request(int port, uint16_t head,
|
|||
set_state(port, PD_STATE_SRC_ACCEPTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* the message was incorrect or cannot be satisfied */
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
/* keep last contract in place (whether implicit or explicit) */
|
||||
|
@ -799,6 +1155,10 @@ static void handle_data_request(int port, uint16_t head,
|
|||
if (pd[port].task_state == PD_STATE_SRC_GET_SINK_CAP)
|
||||
set_state(port, PD_STATE_SRC_READY);
|
||||
break;
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
case PD_DATA_BATTERY_STATUS:
|
||||
break;
|
||||
#endif
|
||||
case PD_DATA_VENDOR_DEF:
|
||||
handle_vdm_request(port, cnt, payload);
|
||||
break;
|
||||
|
@ -903,7 +1263,7 @@ static void handle_ctrl_request(int port, uint16_t head,
|
|||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
send_sink_cap(port);
|
||||
#else
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
#endif
|
||||
break;
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
|
@ -1075,10 +1435,10 @@ static void handle_ctrl_request(int port, uint16_t head,
|
|||
PD_STATE_SNK_SWAP_SNK_DISABLE,
|
||||
PD_STATE_SRC_SWAP_SNK_DISABLE));
|
||||
} else {
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
}
|
||||
#else
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
#endif
|
||||
break;
|
||||
case PD_CTRL_DR_SWAP:
|
||||
|
@ -1092,7 +1452,8 @@ static void handle_ctrl_request(int port, uint16_t head,
|
|||
if (send_control(port, PD_CTRL_ACCEPT) >= 0)
|
||||
pd_dr_swap(port);
|
||||
} else {
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
|
||||
}
|
||||
break;
|
||||
case PD_CTRL_VCONN_SWAP:
|
||||
|
@ -1104,18 +1465,41 @@ static void handle_ctrl_request(int port, uint16_t head,
|
|||
set_state(port,
|
||||
PD_STATE_VCONN_SWAP_INIT);
|
||||
} else {
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
}
|
||||
}
|
||||
#else
|
||||
send_control(port, PD_CTRL_REJECT);
|
||||
send_control(port, REFUSE(pd[port].rev));
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
send_control(port, PD_CTRL_NOT_SUPPORTED);
|
||||
#endif
|
||||
CPRINTF("Unhandled ctrl message type %d\n", type);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static void handle_ext_request(int port, uint16_t head, uint32_t *payload)
|
||||
{
|
||||
int type = PD_HEADER_TYPE(head);
|
||||
|
||||
switch (type) {
|
||||
case PD_EXT_GET_BATTERY_CAP:
|
||||
send_battery_cap(port, payload);
|
||||
break;
|
||||
case PD_EXT_GET_BATTERY_STATUS:
|
||||
send_battery_status(port, payload);
|
||||
break;
|
||||
case PD_EXT_BATTERY_CAP:
|
||||
break;
|
||||
default:
|
||||
send_control(port, PD_CTRL_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_request(int port, uint16_t head,
|
||||
uint32_t *payload)
|
||||
{
|
||||
|
@ -1138,6 +1522,13 @@ static void handle_request(int port, uint16_t head,
|
|||
if (!pd_is_connected(port))
|
||||
set_state(port, PD_STATE_HARD_RESET_SEND);
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* Check if this is an extended chunked data message. */
|
||||
if (pd[port].rev == PD_REV30 && PD_HEADER_EXT(head)) {
|
||||
handle_ext_request(port, head, payload);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (cnt)
|
||||
handle_data_request(port, head, payload);
|
||||
else
|
||||
|
@ -1155,6 +1546,9 @@ void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
|
|||
/* set VDM header with VID & CMD */
|
||||
pd[port].vdo_data[0] = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ?
|
||||
1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd);
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
pd[port].vdo_data[0] |= VDO_SVDM_VERS(vdo_ver[pd[port].rev]);
|
||||
#endif
|
||||
queue_vdm(port, pd[port].vdo_data, data, count);
|
||||
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
|
@ -1223,7 +1617,8 @@ static void pd_vdm_send_state_machine(int port)
|
|||
/* Prepare and send VDM */
|
||||
header = PD_HEADER(PD_DATA_VENDOR_DEF, pd[port].power_role,
|
||||
pd[port].data_role, pd[port].msg_id,
|
||||
(int)pd[port].vdo_count);
|
||||
(int)pd[port].vdo_count,
|
||||
pd_get_rev(port), 0);
|
||||
res = pd_transmit(port, TCPC_TX_SOP, header,
|
||||
pd[port].vdo_data);
|
||||
if (res < 0) {
|
||||
|
@ -1664,6 +2059,12 @@ void pd_task(void *u)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* Set Revision to highest */
|
||||
pd[port].rev = PD_REV30;
|
||||
pd_ca_reset(port);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
/*
|
||||
* If VBUS is high, then initialize flag for VBUS has always been
|
||||
|
@ -1707,6 +2108,10 @@ void pd_task(void *u)
|
|||
#endif
|
||||
|
||||
while (1) {
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* send any pending messages */
|
||||
pd_ca_send_pending(port);
|
||||
#endif
|
||||
/* process VDM messages last */
|
||||
pd_vdm_send_state_machine(port);
|
||||
|
||||
|
@ -1796,7 +2201,6 @@ void pd_task(void *u)
|
|||
case PD_STATE_SRC_DISCONNECTED:
|
||||
timeout = 10*MSEC;
|
||||
tcpm_get_cc(port, &cc1, &cc2);
|
||||
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
|
||||
/*
|
||||
* Attempt TCPC auto DRP toggle if it is
|
||||
|
|
|
@ -439,7 +439,7 @@ static int send_validate_message(int port, uint16_t header,
|
|||
static void send_goodcrc(int port, int id)
|
||||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_GOOD_CRC, pd[port].power_role,
|
||||
pd[port].data_role, id, 0);
|
||||
pd[port].data_role, id, 0, 0, 0);
|
||||
int bit_len = prepare_message(port, header, 0, NULL);
|
||||
|
||||
if (pd_start_tx(port, pd[port].polarity, bit_len) < 0)
|
||||
|
|
|
@ -2538,6 +2538,9 @@
|
|||
/* Define if this board, operating as a sink, can give power back to a source */
|
||||
#undef CONFIG_USB_PD_GIVE_BACK
|
||||
|
||||
/* Enable USB PD Rev3.0 features */
|
||||
#undef CONFIG_USB_PD_REV30
|
||||
|
||||
/* Major and Minor ChromeOS specific PD device Hardware IDs. */
|
||||
#undef CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR
|
||||
#undef CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR
|
||||
|
|
119
include/usb_pd.h
119
include/usb_pd.h
|
@ -137,6 +137,9 @@ enum pd_rx_errors {
|
|||
#define SVID_DISCOVERY_MAX 16
|
||||
|
||||
/* Timers */
|
||||
#define PD_T_SINK_TX (18*MSEC) /* between 16ms and 20 */
|
||||
#define PD_T_CHUNK_SENDER_RSP (24*MSEC) /* between 24ms and 30ms */
|
||||
#define PD_T_CHUNK_SENDER_REQ (24*MSEC) /* between 24ms and 30ms */
|
||||
#define PD_T_SEND_SOURCE_CAP (100*MSEC) /* between 100ms and 200ms */
|
||||
#define PD_T_SINK_WAIT_CAP (600*MSEC) /* between 310ms and 620ms */
|
||||
#define PD_T_SINK_TRANSITION (35*MSEC) /* between 20ms and 35ms */
|
||||
|
@ -267,23 +270,27 @@ struct pd_policy {
|
|||
* VDM object is minimum of VDM header + 6 additional data objects.
|
||||
*/
|
||||
|
||||
#define VDO_MAX_SIZE 7
|
||||
|
||||
#define VDM_VER10 0
|
||||
#define VDM_VER20 1
|
||||
|
||||
/*
|
||||
* VDM header
|
||||
* ----------
|
||||
* <31:16> :: SVID
|
||||
* <15> :: VDM type ( 1b == structured, 0b == unstructured )
|
||||
* <14:13> :: Structured VDM version (can only be 00 == 1.0 currently)
|
||||
* <14:13> :: Structured VDM version (00b == Rev 2.0, 01b == Rev 3.0 )
|
||||
* <12:11> :: reserved
|
||||
* <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
|
||||
* <7:6> :: command type (SVDM only?)
|
||||
* <5> :: reserved (SVDM), command type (UVDM)
|
||||
* <4:0> :: command
|
||||
*/
|
||||
#define VDO_MAX_SIZE 7
|
||||
#define VDO(vid, type, custom) \
|
||||
(((vid) << 16) | \
|
||||
((type) << 15) | \
|
||||
((custom) & 0x7FFF))
|
||||
#define VDO(vid, type, custom) \
|
||||
(((vid) << 16) | \
|
||||
((type) << 15) | \
|
||||
((custom) & 0x7FFF))
|
||||
|
||||
#define VDO_SVDM_TYPE (1 << 15)
|
||||
#define VDO_SVDM_VERS(x) (x << 13)
|
||||
|
@ -793,6 +800,46 @@ enum pd_ctrl_msg_type {
|
|||
PD_CTRL_WAIT = 12,
|
||||
PD_CTRL_SOFT_RESET = 13,
|
||||
/* 14-15 Reserved */
|
||||
|
||||
/* Used for REV 3.0 */
|
||||
PD_CTRL_NOT_SUPPORTED = 16,
|
||||
PD_CTRL_GET_SOURCE_CAP_EXT = 17,
|
||||
PD_CTRL_GET_STATUS = 18,
|
||||
PD_CTRL_FR_SWAP = 19,
|
||||
PD_CTRL_GET_PPS_STATUS = 20,
|
||||
PD_CTRL_GET_COUNTRY_CODES = 21,
|
||||
/* 22-31 Reserved */
|
||||
};
|
||||
|
||||
/* Battery Status Data Object fields for REV 3.0 */
|
||||
#define BSDO_CAP_UNKNOWN 0xffff
|
||||
#define BSDO_CAP(n) (((n) & 0xffff) << 16)
|
||||
#define BSDO_INVALID (1 << 8)
|
||||
#define BSDO_PRESENT (1 << 9)
|
||||
#define BSDO_DISCHARGING (1 << 10)
|
||||
#define BSDO_IDLE (1 << 11)
|
||||
|
||||
/* Get Battery Cap Message fields for REV 3.0 */
|
||||
#define BATT_CAP_REF(n) (((n) >> 16) & 0xff)
|
||||
|
||||
/* Extended message type for REV 3.0 */
|
||||
enum pd_ext_msg_type {
|
||||
/* 0 Reserved */
|
||||
PD_EXT_SOURCE_CAP = 1,
|
||||
PD_EXT_STATUS = 2,
|
||||
PD_EXT_GET_BATTERY_CAP = 3,
|
||||
PD_EXT_GET_BATTERY_STATUS = 4,
|
||||
PD_EXT_BATTERY_CAP = 5,
|
||||
PD_EXT_GET_MANUFACTURER_INFO = 6,
|
||||
PD_EXT_MANUFACTURER_INFO = 7,
|
||||
PD_EXT_SECURITY_REQUEST = 8,
|
||||
PD_EXT_SECURITY_RESPONSE = 9,
|
||||
PD_EXT_FIRMWARE_UPDATE_REQUEST = 10,
|
||||
PD_EXT_FIRMWARE_UPDATE_RESPONSE = 11,
|
||||
PD_EXT_PPS_STATUS = 12,
|
||||
PD_EXT_COUNTRY_INFO = 13,
|
||||
PD_EXT_COUNTRY_CODES = 14,
|
||||
/* 15-31 Reserved */
|
||||
};
|
||||
|
||||
/* Data message type */
|
||||
|
@ -802,13 +849,18 @@ enum pd_data_msg_type {
|
|||
PD_DATA_REQUEST = 2,
|
||||
PD_DATA_BIST = 3,
|
||||
PD_DATA_SINK_CAP = 4,
|
||||
/* 5-14 Reserved */
|
||||
/* 5-14 Reserved for REV 2.0 */
|
||||
PD_DATA_BATTERY_STATUS = 5,
|
||||
PD_DATA_ALERT = 6,
|
||||
PD_DATA_GET_COUNTRY_INFO = 7,
|
||||
/* 8-14 Reserved for REV 3.0 */
|
||||
PD_DATA_VENDOR_DEF = 15,
|
||||
};
|
||||
|
||||
/* Protocol revision */
|
||||
#define PD_REV10 0
|
||||
#define PD_REV20 1
|
||||
#define PD_REV30 2
|
||||
|
||||
/* Power role */
|
||||
#define PD_ROLE_SINK 0
|
||||
|
@ -820,6 +872,14 @@ enum pd_data_msg_type {
|
|||
#define PD_ROLE_VCONN_OFF 0
|
||||
#define PD_ROLE_VCONN_ON 1
|
||||
|
||||
/* chunk is a request or response in REV 3.0 */
|
||||
#define CHUNK_RESPONSE 0
|
||||
#define CHUNK_REQUEST 1
|
||||
|
||||
/* collision avoidance Rp values in REV 3.0 */
|
||||
#define SINK_TX_OK TYPEC_RP_3A0
|
||||
#define SINK_TX_NG TYPEC_RP_1A5
|
||||
|
||||
/* Port role at startup */
|
||||
#ifndef PD_ROLE_DEFAULT
|
||||
#ifdef CONFIG_USB_PD_DUAL_ROLE
|
||||
|
@ -838,15 +898,30 @@ enum pd_data_msg_type {
|
|||
#define PD_DEFAULT_STATE(port) PD_STATE_SRC_DISCONNECTED
|
||||
#endif
|
||||
|
||||
/* build message header */
|
||||
#define PD_HEADER(type, prole, drole, id, cnt) \
|
||||
((type) | (PD_REV20 << 6) | \
|
||||
((drole) << 5) | ((prole) << 8) | \
|
||||
((id) << 9) | ((cnt) << 12))
|
||||
/* build extended message header */
|
||||
/* All extended messages are chunked, so set bit 15 */
|
||||
#define PD_EXT_HEADER(cnum, rchk, dsize) \
|
||||
((1 << 15) | ((cnum) << 11) | \
|
||||
((rchk) << 10) | (dsize))
|
||||
|
||||
/* build message header */
|
||||
#define PD_HEADER(type, prole, drole, id, cnt, rev, ext) \
|
||||
((type) | ((rev) << 6) | \
|
||||
((drole) << 5) | ((prole) << 8) | \
|
||||
((id) << 9) | ((cnt) << 12) | ((ext) << 15))
|
||||
|
||||
/* Used for processing pd header */
|
||||
#define PD_HEADER_EXT(header) (((header) >> 15) & 1)
|
||||
#define PD_HEADER_CNT(header) (((header) >> 12) & 7)
|
||||
#define PD_HEADER_TYPE(header) ((header) & 0xF)
|
||||
#define PD_HEADER_ID(header) (((header) >> 9) & 7)
|
||||
#define PD_HEADER_REV(header) (((header) >> 6) & 3)
|
||||
|
||||
/* Used for processing pd extended header */
|
||||
#define PD_EXT_HEADER_CHUNKED(header) (((header) >> 15) & 1)
|
||||
#define PD_EXT_HEADER_CHUNK_NUM(header) (((header) >> 11) & 0xf)
|
||||
#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1)
|
||||
#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff)
|
||||
|
||||
/* K-codes for special symbols */
|
||||
#define PD_SYNC1 0x18
|
||||
|
@ -883,6 +958,26 @@ enum pd_request_type {
|
|||
PD_REQUEST_MAX,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/**
|
||||
* Get current PD Revision
|
||||
*
|
||||
* @param port USB-C port number
|
||||
* @return 0 for PD_REV1.0, 1 for PD_REV2.0, 2 for PD_REV3.0
|
||||
*/
|
||||
int pd_get_rev(int port);
|
||||
|
||||
/**
|
||||
* Get current PD VDO Version
|
||||
*
|
||||
* @param port USB-C port number
|
||||
* @return 0 for PD_REV1.0, 1 for PD_REV2.0
|
||||
*/
|
||||
int pd_get_vdo_ver(int port);
|
||||
#else
|
||||
#define pd_get_rev(n) PD_REV20
|
||||
#define pd_get_vdo_ver(n) VDM_VER10
|
||||
#endif
|
||||
/**
|
||||
* Decide which PDO to choose from the source capabilities.
|
||||
*
|
||||
|
|
|
@ -76,6 +76,7 @@ test-list-host += thermal
|
|||
test-list-host += timer_dos
|
||||
test-list-host += usb_pd
|
||||
test-list-host += usb_pd_giveback
|
||||
test-list-host += usb_pd_rev30
|
||||
test-list-host += utils
|
||||
test-list-host += utils_str
|
||||
test-list-host += vboot
|
||||
|
@ -130,6 +131,7 @@ timer_calib-y=timer_calib.o
|
|||
timer_dos-y=timer_dos.o
|
||||
usb_pd-y=usb_pd.o
|
||||
usb_pd_giveback-y=usb_pd.o
|
||||
usb_pd_rev30-y=usb_pd.o
|
||||
utils-y=utils.o
|
||||
utils_str-y=utils_str.o
|
||||
vboot-y=vboot.o
|
||||
|
|
|
@ -167,7 +167,8 @@ int ncp15wb_calculate_temp(uint16_t adc);
|
|||
#define CONFIG_ALS_LIGHTBAR_DIMMING 0
|
||||
#endif
|
||||
|
||||
#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK)
|
||||
#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \
|
||||
defined(TEST_USB_PD_REV30)
|
||||
#define CONFIG_USB_POWER_DELIVERY
|
||||
#define CONFIG_USB_PD_CUSTOM_VDM
|
||||
#define CONFIG_USB_PD_DUAL_ROLE
|
||||
|
@ -176,10 +177,14 @@ int ncp15wb_calculate_temp(uint16_t adc);
|
|||
#define CONFIG_USB_PD_TCPM_STUB
|
||||
#define CONFIG_SHA256
|
||||
#define CONFIG_SW_CRC
|
||||
#ifdef TEST_USB_PD_REV30
|
||||
#define CONFIG_USB_PD_REV30
|
||||
#define CONFIG_USB_PID 0x5000
|
||||
#endif
|
||||
#ifdef TEST_USB_PD_GIVEBACK
|
||||
#define CONFIG_USB_PD_GIVE_BACK
|
||||
#endif
|
||||
#endif /* TEST_USB_PD || TEST_USB_PD_GIVEBACK */
|
||||
#endif /* TEST_USB_PD || TEST_USB_PD_GIVEBACK || TEST_USB_PD_REV30 */
|
||||
|
||||
#if defined(TEST_CHARGE_MANAGER) || defined(TEST_CHARGE_MANAGER_DRP_CHARGING)
|
||||
#define CONFIG_CHARGE_MANAGER
|
||||
|
|
229
test/usb_pd.c
229
test/usb_pd.c
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Test USB PD module.
|
||||
*/
|
||||
|
||||
#include "battery.h"
|
||||
#include "common.h"
|
||||
#include "crc.h"
|
||||
#include "task.h"
|
||||
|
@ -17,6 +17,11 @@
|
|||
#define PORT0 0
|
||||
#define PORT1 1
|
||||
|
||||
#define BATTERY_DESIGN_VOLTAGE 7600
|
||||
#define BATTERY_DESIGN_CAPACITY 5131
|
||||
#define BATTERY_FULL_CHARGE_CAPACITY 5131
|
||||
#define BATTERY_REMAINING_CAPACITY 2566
|
||||
|
||||
struct pd_port_t {
|
||||
int host_mode;
|
||||
int has_vbus;
|
||||
|
@ -25,11 +30,60 @@ struct pd_port_t {
|
|||
int polarity;
|
||||
int partner_role; /* -1 for none */
|
||||
int partner_polarity;
|
||||
int rev;
|
||||
} pd_port[CONFIG_USB_PD_PORT_COUNT];
|
||||
|
||||
static int give_back_called;
|
||||
|
||||
/* Mock functions */
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
|
||||
uint16_t pd_get_identity_vid(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t pd_get_identity_pid(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum battery_present battery_is_present(void)
|
||||
{
|
||||
return BP_YES;
|
||||
}
|
||||
|
||||
int battery_status(int *status)
|
||||
{
|
||||
*status = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int battery_remaining_capacity(int *capacity)
|
||||
{
|
||||
*capacity = BATTERY_REMAINING_CAPACITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int battery_full_charge_capacity(int *capacity)
|
||||
{
|
||||
*capacity = BATTERY_FULL_CHARGE_CAPACITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int battery_design_capacity(int *capacity)
|
||||
{
|
||||
*capacity = BATTERY_DESIGN_CAPACITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int battery_design_voltage(int *voltage)
|
||||
{
|
||||
*voltage = BATTERY_DESIGN_VOLTAGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int pd_adc_read(int port, int cc)
|
||||
{
|
||||
|
@ -97,6 +151,11 @@ static void init_ports(void)
|
|||
pd_port[i].host_mode = 0;
|
||||
pd_port[i].partner_role = -1;
|
||||
pd_port[i].has_vbus = 0;
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
pd_port[i].rev = PD_REV30;
|
||||
#else
|
||||
pd_port[i].rev = PD_REV20;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +186,7 @@ static void simulate_wait(int port)
|
|||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_WAIT, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
0);
|
||||
0, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, 0, NULL);
|
||||
}
|
||||
|
@ -136,7 +195,7 @@ static void simulate_accept(int port)
|
|||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_ACCEPT, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
0);
|
||||
0, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, 0, NULL);
|
||||
}
|
||||
|
@ -145,35 +204,71 @@ static void simulate_reject(int port)
|
|||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_REJECT, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
0);
|
||||
0, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
static void simulate_get_bat_cap(int port)
|
||||
{
|
||||
uint16_t msg[2];
|
||||
uint16_t header = PD_HEADER(PD_EXT_GET_BATTERY_CAP, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
1, pd_port[port].rev, 1);
|
||||
|
||||
/* set extended header */
|
||||
msg[0] = PD_EXT_HEADER(0, 0, 1);
|
||||
|
||||
/* set battery status ref */
|
||||
msg[1] = 0;
|
||||
|
||||
simulate_rx_msg(port, header, 1, (const uint32_t *)msg);
|
||||
}
|
||||
|
||||
static void simulate_get_bat_status(int port)
|
||||
{
|
||||
uint16_t msg[2];
|
||||
uint16_t header = PD_HEADER(PD_EXT_GET_BATTERY_STATUS, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
1, pd_port[port].rev, 1);
|
||||
|
||||
/* set extended header */
|
||||
msg[0] = PD_EXT_HEADER(0, 0, 1);
|
||||
|
||||
/* set battery status ref */
|
||||
msg[1] = 0;
|
||||
|
||||
simulate_rx_msg(port, header, 1, (const uint32_t *)msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void simulate_source_cap(int port, uint32_t cnt)
|
||||
{
|
||||
uint32_t src_pdo_cnt = (cnt == 0) ? 1 : pd_src_pdo_cnt;
|
||||
|
||||
uint16_t header = PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
src_pdo_cnt);
|
||||
src_pdo_cnt, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, src_pdo_cnt, pd_src_pdo);
|
||||
}
|
||||
|
||||
static void simulate_goodcrc(int port, int role, int id)
|
||||
{
|
||||
simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0),
|
||||
0, NULL);
|
||||
simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
|
||||
pd_port[port].rev, 0), 0, NULL);
|
||||
}
|
||||
|
||||
static int verify_goodcrc(int port, int role, int id)
|
||||
{
|
||||
|
||||
return pd_test_tx_msg_verify_sop(port) &&
|
||||
pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
|
||||
role, role, id, 0)) &&
|
||||
pd_test_tx_msg_verify_crc(port) &&
|
||||
pd_test_tx_msg_verify_eop(port);
|
||||
pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
|
||||
role, role, id, 0, 0, 0)) &&
|
||||
pd_test_tx_msg_verify_crc(port) &&
|
||||
pd_test_tx_msg_verify_eop(port);
|
||||
}
|
||||
|
||||
static void plug_in_source(int port, int polarity)
|
||||
|
@ -210,7 +305,7 @@ static void simulate_ps_rdy(int port)
|
|||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_PS_RDY, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id,
|
||||
0);
|
||||
0, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, 0, NULL);
|
||||
}
|
||||
|
@ -218,13 +313,34 @@ static void simulate_ps_rdy(int port)
|
|||
static void simulate_goto_min(int port)
|
||||
{
|
||||
uint16_t header = PD_HEADER(PD_CTRL_GOTO_MIN, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id, 0);
|
||||
PD_ROLE_DFP, pd_port[port].msg_rx_id, 0, pd_port[port].rev, 0);
|
||||
|
||||
simulate_rx_msg(port, header, 0, NULL);
|
||||
}
|
||||
|
||||
static int test_request_with_wait_and_contract(void)
|
||||
{
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
uint32_t expected_status_bsdo =
|
||||
BSDO_CAP(DIV_ROUND_NEAREST(BATTERY_REMAINING_CAPACITY *
|
||||
BATTERY_DESIGN_VOLTAGE, 100000)) |
|
||||
BSDO_PRESENT;
|
||||
uint16_t expected_cap_hdr = PD_EXT_HEADER(0, 0, 9);
|
||||
uint16_t expected_cap_vid = USB_VID_GOOGLE;
|
||||
#ifdef CONFIG_USB_PID
|
||||
uint16_t expected_cap_pid = CONFIG_USB_PID;
|
||||
#else
|
||||
uint16_t expected_cap_pid = 0;
|
||||
#endif
|
||||
uint16_t expected_cap_des =
|
||||
DIV_ROUND_NEAREST(BATTERY_DESIGN_CAPACITY *
|
||||
BATTERY_DESIGN_VOLTAGE, 100000);
|
||||
uint16_t expected_cap_ful =
|
||||
DIV_ROUND_NEAREST(BATTERY_FULL_CHARGE_CAPACITY *
|
||||
BATTERY_DESIGN_VOLTAGE, 100000);
|
||||
uint16_t expected_cap_type = 0;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PD_GIVE_BACK
|
||||
uint32_t expected_rdo =
|
||||
RDO_FIXED(2, 3000, PD_MIN_CURRENT_MA, RDO_GIVE_BACK);
|
||||
|
@ -253,7 +369,7 @@ static int test_request_with_wait_and_contract(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -294,7 +410,7 @@ static int test_request_with_wait_and_contract(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -326,7 +442,9 @@ static int test_request_with_wait_and_contract(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1,
|
||||
pd_port[port].rev, 0
|
||||
)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -358,6 +476,69 @@ static int test_request_with_wait_and_contract(void)
|
|||
task_wait_event(30 * MSEC);
|
||||
inc_rx_id(port);
|
||||
|
||||
/*
|
||||
* Test Extended Get_Battery_Cap and Get_Battery_Status messages.
|
||||
*/
|
||||
#ifdef CONFIG_USB_PD_REV30
|
||||
/* We're in SNK_READY. Send get battery cap. */
|
||||
simulate_get_bat_cap(port);
|
||||
task_wait_event(30 * MSEC);
|
||||
TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
|
||||
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
inc_rx_id(port);
|
||||
|
||||
/* Process the request */
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_EXT_BATTERY_CAP, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 3, pd_port[port].rev, 1)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_hdr));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_vid));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_pid));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_des));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_ful));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port, expected_cap_type));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
|
||||
/* Request was good. Send GoodCRC */
|
||||
simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
inc_tx_id(port);
|
||||
|
||||
/* Send get battery status. */
|
||||
simulate_get_bat_status(port);
|
||||
task_wait_event(30 * MSEC);
|
||||
TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id));
|
||||
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
inc_rx_id(port);
|
||||
|
||||
/* Process the request */
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_BATTERY_STATUS, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_status_bsdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
|
||||
/* Request was good. Send GoodCRC */
|
||||
simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wait_event(30 * MSEC);
|
||||
inc_tx_id(port);
|
||||
#endif
|
||||
/* We're in SNK_READY. Send goto_min */
|
||||
simulate_goto_min(port);
|
||||
task_wait_event(30 * MSEC);
|
||||
|
@ -372,7 +553,6 @@ static int test_request_with_wait_and_contract(void)
|
|||
#else
|
||||
TEST_ASSERT(!give_back_called);
|
||||
#endif
|
||||
|
||||
/* We're done */
|
||||
unplug(port);
|
||||
|
||||
|
@ -409,7 +589,7 @@ static int test_request_with_wait(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -448,7 +628,7 @@ static int test_request_with_wait(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -497,7 +677,7 @@ static int test_request_with_reject(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -535,7 +715,7 @@ static int test_request_with_reject(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -575,7 +755,7 @@ static int test_request(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_sop(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
|
||||
pd_port[port].msg_tx_id, 1)));
|
||||
pd_port[port].msg_tx_id, 1, pd_port[port].rev, 0)));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||||
|
@ -595,7 +775,6 @@ static int test_request(void)
|
|||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int test_sink(void)
|
||||
{
|
||||
int i;
|
||||
|
@ -611,7 +790,8 @@ static int test_sink(void)
|
|||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||||
PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
|
||||
PD_ROLE_DFP, pd_port[port].msg_tx_id,
|
||||
pd_src_pdo_cnt)));
|
||||
pd_src_pdo_cnt, pd_port[port].rev, 0)));
|
||||
|
||||
for (i = 0; i < pd_src_pdo_cnt; ++i)
|
||||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, pd_src_pdo[i]));
|
||||
|
||||
|
@ -646,5 +826,6 @@ void run_test(void)
|
|||
RUN_TEST(test_request_with_wait);
|
||||
RUN_TEST(test_request_with_wait_and_contract);
|
||||
RUN_TEST(test_request_with_reject);
|
||||
|
||||
test_print_result();
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
usb_pd.tasklist
|
Loading…
Reference in New Issue