diff options
author | Sam Hurst <shurst@google.com> | 2017-08-05 13:24:40 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-10-24 15:30:15 -0700 |
commit | c91dbb26d87573b9789a6a8b3e05eac3a9c205fa (patch) | |
tree | 145ef633cb7ebaf387da040557399afecc44188f | |
parent | 5fd1540e62be47747c4fe234a0b348e58e0cfd3f (diff) | |
download | chrome-ec-c91dbb26d87573b9789a6a8b3e05eac3a9c205fa.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>
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>
-rw-r--r-- | common/usb_pd_policy.c | 2 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 440 | ||||
-rw-r--r-- | common/usb_pd_tcpc.c | 2 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/usb_pd.h | 117 | ||||
-rw-r--r-- | test/build.mk | 2 | ||||
-rw-r--r-- | test/test_config.h | 9 | ||||
-rw-r--r-- | test/usb_pd.c | 229 | ||||
l--------- | test/usb_pd_rev30.tasklist | 1 |
9 files changed, 749 insertions, 56 deletions
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c index 806c2611c5..936ad6980f 100644 --- a/common/usb_pd_policy.c +++ b/common/usb_pd_policy.c @@ -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) { diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 61a329f3ed..c1628beff0 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" @@ -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 diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c index a540d6f67f..9feb0ef2b1 100644 --- a/common/usb_pd_tcpc.c +++ b/common/usb_pd_tcpc.c @@ -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) diff --git a/include/config.h b/include/config.h index c96bfdf446..ccae71bd79 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/usb_pd.h b/include/usb_pd.h index f6354eb546..d068e2e4ce 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 20a55fb3fc..ed22aea4d5 100644 --- a/test/build.mk +++ b/test/build.mk @@ -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 diff --git a/test/test_config.h b/test/test_config.h index a912e08a0d..5dd714f98b 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -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 diff --git a/test/usb_pd.c b/test/usb_pd.c index 7133af28e9..69b7448ea6 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])); @@ -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(); } 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 |