summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2017-08-05 13:24:40 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2018-05-01 15:49:56 +0000
commitf3aa4f3e827f4087cb2c2190948e247600af6c82 (patch)
tree5110e220a3c14229230c3bb049b46714cd18907b
parentd4ade7323dc823c469ef76041b8ae2958faf0ea0 (diff)
downloadchrome-ec-f3aa4f3e827f4087cb2c2190948e247600af6c82.tar.gz
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> Signed-off-by: Duncan Laurie <dlaurie@google.com> Original-Commit-Id: c91dbb26d87573b9789a6a8b3e05eac3a9c205fa Original-Change-Id: Ifd77e92ec4e9106236f9221393d2bfb97263d979 Original-Reviewed-on: https://chromium-review.googlesource.com/603003 Original-Commit-Ready: Sam Hurst <shurst@google.com> Original-Tested-by: Sam Hurst <shurst@google.com> Original-Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Original-(cherry picked from commit c91dbb26d87573b9789a6a8b3e05eac3a9c205fa) Change-Id: Icf4bee20f17f583b003fcd7c0fc425151d25c866 Reviewed-on: https://chromium-review.googlesource.com/1036928 Reviewed-by: Duncan Laurie <dlaurie@google.com> Commit-Queue: Duncan Laurie <dlaurie@google.com> Tested-by: Duncan Laurie <dlaurie@google.com> Trybot-Ready: Duncan Laurie <dlaurie@google.com>
-rw-r--r--common/usb_pd_policy.c2
-rw-r--r--common/usb_pd_protocol.c440
-rw-r--r--common/usb_pd_tcpc.c2
-rw-r--r--include/config.h3
-rw-r--r--include/usb_pd.h117
-rw-r--r--test/build.mk2
-rw-r--r--test/test_config.h9
-rw-r--r--test/usb_pd.c229
l---------test/usb_pd_rev30.tasklist1
9 files changed, 749 insertions, 56 deletions
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c
index cf58e7ff07..0d8c6c08c1 100644
--- a/common/usb_pd_policy.c
+++ b/common/usb_pd_policy.c
@@ -725,6 +725,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;
@@ -801,6 +802,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) {
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 00d97f8381..20652aa550 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -4,6 +4,7 @@
*/
#include "battery.h"
+#include "battery_smart.h"
#include "board.h"
#include "charge_manager.h"
#include "charge_state.h"
@@ -102,6 +103,34 @@ static int pd_src_cap_cnt[CONFIG_USB_PD_PORT_COUNT];
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)
{
@@ -309,10 +359,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
@@ -354,6 +421,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)
@@ -371,12 +451,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;
@@ -384,6 +528,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 */
@@ -394,7 +561,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)
@@ -419,10 +587,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)
@@ -431,12 +601,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)
@@ -447,7 +773,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)
@@ -483,7 +810,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);
@@ -547,6 +875,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.
@@ -753,6 +1085,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;
@@ -768,7 +1107,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)
/*
@@ -780,6 +1126,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
@@ -787,6 +1142,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) */
@@ -816,6 +1172,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;
@@ -920,7 +1280,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
@@ -1095,10 +1455,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:
@@ -1112,7 +1472,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:
@@ -1124,18 +1485,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)
{
@@ -1158,6 +1542,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
@@ -1175,6 +1566,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));
@@ -1243,7 +1637,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) {
@@ -1682,6 +2077,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
@@ -1725,6 +2126,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);
@@ -1810,7 +2215,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
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
index 99e3470e18..6f53d8e9e9 100644
--- a/common/usb_pd_tcpc.c
+++ b/common/usb_pd_tcpc.c
@@ -437,7 +437,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)
diff --git a/include/config.h b/include/config.h
index a6c0002b6f..44e0c084b2 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2393,6 +2393,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
diff --git a/include/usb_pd.h b/include/usb_pd.h
index dca4852819..2151bd6d9c 100644
--- a/include/usb_pd.h
+++ b/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 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) \
- ((type) | (PD_REV20 << 6) | \
- ((drole) << 5) | ((prole) << 8) | \
- ((id) << 9) | ((cnt) << 12))
+#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.
*
diff --git a/test/build.mk b/test/build.mk
index b92e263e8f..6debb5e994 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -72,6 +72,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 += x25519
@@ -119,6 +120,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
x25519-y=x25519.o
diff --git a/test/test_config.h b/test/test_config.h
index 4c83bc1914..4e57535ba5 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -127,7 +127,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
@@ -136,10 +137,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 */
#ifdef TEST_CHARGE_MANAGER
#define CONFIG_CHARGE_MANAGER
diff --git a/test/usb_pd.c b/test/usb_pd.c
index 80c5d10885..c689a286b6 100644
--- a/test/usb_pd.c
+++ b/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]));
@@ -640,5 +820,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();
}
diff --git a/test/usb_pd_rev30.tasklist b/test/usb_pd_rev30.tasklist
new file mode 120000
index 0000000000..45cc6c8aa2
--- /dev/null
+++ b/test/usb_pd_rev30.tasklist
@@ -0,0 +1 @@
+usb_pd.tasklist \ No newline at end of file