diff options
-rw-r--r-- | common/usbc/build.mk | 1 | ||||
-rw-r--r-- | common/usbc/usb_pd_host.c | 96 | ||||
-rw-r--r-- | common/usbc/usb_pe_drp_sm.c | 21 | ||||
-rw-r--r-- | include/ec_commands.h | 37 | ||||
-rw-r--r-- | include/usb_pd.h | 26 | ||||
-rw-r--r-- | util/ectool.c | 61 |
6 files changed, 242 insertions, 0 deletions
diff --git a/common/usbc/build.mk b/common/usbc/build.mk index a4da8a37c9..c39d1a57e6 100644 --- a/common/usbc/build.mk +++ b/common/usbc/build.mk @@ -33,6 +33,7 @@ all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)dp_alt_mode.o all-obj-$(CONFIG_USB_PD_TBT_COMPAT_MODE)+=$(_usbc_dir)tbt_alt_mode.o all-obj-$(CONFIG_USB_PD_USB4)+=$(_usbc_dir)usb_mode.o all-obj-$(CONFIG_CMD_PD)+=$(_usbc_dir)usb_pd_console.o +all-obj-$(CONFIG_USB_PD_HOST_CMD)+=$(_usbc_dir)usb_pd_host.o endif # CONFIG_USB_PE_SM endif # CONFIG_USB_PD_TCPMV2 diff --git a/common/usbc/usb_pd_host.c b/common/usbc/usb_pd_host.c new file mode 100644 index 0000000000..a5e4c52baa --- /dev/null +++ b/common/usbc/usb_pd_host.c @@ -0,0 +1,96 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Host commands for TCPMv2 USB PD module + */ + +#include <string.h> + +#include "console.h" +#include "ec_commands.h" +#include "host_command.h" +#include "usb_pd.h" + +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + +/* Retrieve all discovery results for the given port and transmit type */ +static enum ec_status hc_typec_discovery(struct host_cmd_handler_args *args) +{ + const struct ec_params_typec_discovery *p = args->params; + struct ec_response_typec_discovery *r = args->response; + const struct pd_discovery *disc; + enum tcpm_transmit_type type; + + /* Confirm the number of HC VDOs matches our stored VDOs */ + BUILD_ASSERT(sizeof(r->discovery_vdo) == sizeof(union disc_ident_ack)); + + if (p->port >= board_get_usb_pd_port_count()) + return EC_RES_INVALID_PARAM; + + if (p->partner_type > TYPEC_PARTNER_SOP_PRIME) + return EC_RES_INVALID_PARAM; + + type = p->partner_type == TYPEC_PARTNER_SOP ? + TCPC_TX_SOP : TCPC_TX_SOP_PRIME; + + /* + * Clear out access mask so we can track if tasks have touched data + * since read started. + */ + pd_discovery_access_clear(p->port, type); + + disc = pd_get_am_discovery(p->port, type); + + /* Initialize return size to that of discovery with no SVIDs */ + args->response_size = sizeof(*r); + + if (pd_get_identity_discovery(p->port, type) == PD_DISC_COMPLETE) { + r->identity_count = disc->identity_cnt; + memcpy(r->discovery_vdo, + pd_get_identity_response(p->port, type)->raw_value, + sizeof(r->discovery_vdo)); + } else { + r->identity_count = 0; + return EC_RES_SUCCESS; + } + + if (pd_get_modes_discovery(p->port, type) == PD_DISC_COMPLETE) { + int svid_i; + int max_resp_svids = (args->response_max - args->response_size)/ + sizeof(struct svid_mode_info); + + if (disc->svid_cnt > max_resp_svids) { + CPRINTS("Warn: SVIDS exceeded HC response"); + r->svid_count = max_resp_svids; + } else { + r->svid_count = disc->svid_cnt; + } + + for (svid_i = 0; svid_i < r->svid_count; svid_i++) { + r->svids[svid_i].svid = disc->svids[svid_i].svid; + r->svids[svid_i].mode_count = + disc->svids[svid_i].mode_cnt; + memcpy(r->svids[svid_i].mode_vdo, + disc->svids[svid_i].mode_vdo, + sizeof(r->svids[svid_i].mode_vdo)); + args->response_size += sizeof(struct svid_mode_info); + } + } else { + r->svid_count = 0; + } + + /* + * Verify that another task did not access this data during the duration + * of the copy. If the data was accessed, return BUSY so the AP will + * try retrieving again and get the updated data. + */ + if (!pd_discovery_access_validate(p->port, type)) + return EC_RES_BUSY; + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_TYPEC_DISCOVERY, + hc_typec_discovery, + EC_VER_MASK(0)); diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c index 9c489fccf8..43c4817e7b 100644 --- a/common/usbc/usb_pe_drp_sm.c +++ b/common/usbc/usb_pe_drp_sm.c @@ -5952,6 +5952,10 @@ uint8_t pd_get_src_cap_cnt(int port) return pe[port].src_cap_cnt; } + +/* Track access to the PD discovery structures during HC execution */ +uint32_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT]; + void pd_dfp_discovery_init(int port) { /* @@ -5961,6 +5965,10 @@ void pd_dfp_discovery_init(int port) PE_CLR_FLAG(port, PE_FLAGS_VDM_SETUP_DONE | PE_FLAGS_MODAL_OPERATION); + atomic_or(&task_access[port][TCPC_TX_SOP], BIT(task_get_current())); + atomic_or(&task_access[port][TCPC_TX_SOP_PRIME], + BIT(task_get_current())); + memset(pe[port].discovery, 0, sizeof(pe[port].discovery)); memset(pe[port].partner_amodes, 0, sizeof(pe[port].partner_amodes)); @@ -5976,9 +5984,22 @@ void pd_dfp_discovery_init(int port) } #ifdef CONFIG_USB_PD_ALT_MODE_DFP + +void pd_discovery_access_clear(int port, enum tcpm_transmit_type type) +{ + atomic_clear(&task_access[port][type], 0xFFFFFFFF); +} + +bool pd_discovery_access_validate(int port, enum tcpm_transmit_type type) +{ + return !(task_access[port][type] & ~BIT(task_get_current())); +} + struct pd_discovery *pd_get_am_discovery(int port, enum tcpm_transmit_type type) { ASSERT(type < DISCOVERY_TYPE_COUNT); + + atomic_or(&task_access[port][type], BIT(task_get_current())); return &pe[port].discovery[type]; } diff --git a/include/ec_commands.h b/include/ec_commands.h index ade22a2f99..8e9b8b236f 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -6314,6 +6314,43 @@ struct ec_response_regulator_get_voltage { uint32_t voltage_mv; } __ec_align4; +/* + * Gather all discovery information for the given port and partner type. + * + * Note that if discovery has not yet completed, only the currently completed + * responses will be filled in. If the discovery data structures are changed + * in the process of the command running, BUSY will be returned. + * + * VDO field sizes are set to the maximum possible number of VDOs a VDM may + * contain, while the number of SVIDs here is selected to fit within the PROTO2 + * maximum parameter size. + */ +#define EC_CMD_TYPEC_DISCOVERY 0x0131 + +enum typec_partner_type { + TYPEC_PARTNER_SOP = 0, + TYPEC_PARTNER_SOP_PRIME = 1, +}; + +struct ec_params_typec_discovery { + uint8_t port; + uint8_t partner_type; /* enum typec_partner_type */ +} __ec_align1; + +struct svid_mode_info { + uint16_t svid; + uint16_t mode_count; /* Number of modes partner sent */ + uint32_t mode_vdo[6]; /* Max VDOs allowed after VDM header is 6 */ +}; + +struct ec_response_typec_discovery { + uint8_t identity_count; /* Number of identity VDOs partner sent */ + uint8_t svid_count; /* Number of SVIDs partner sent */ + uint16_t reserved; + uint32_t discovery_vdo[6]; /* Max VDOs allowed after VDM header is 6 */ + struct svid_mode_info svids[0]; +} __ec_align1; + /*****************************************************************************/ /* The command range 0x200-0x2FF is reserved for Rotor. */ diff --git a/include/usb_pd.h b/include/usb_pd.h index 67de78a3ac..b546a286f4 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -1908,9 +1908,35 @@ bool consume_sop_prime_repeat_msg(int port, uint8_t msg_id); bool consume_sop_prime_prime_repeat_msg(int port, uint8_t msg_id); /* + * Clears record of which tasks have accessed discovery data for this port and + * type. + * + * @param port USB-C port number + * @param type Transmit type (SOP, SOP') + */ +void pd_discovery_access_clear(int port, enum tcpm_transmit_type type); + +/* + * Validate that this current task is the only one which has retrieved the + * pointer from pd_get_am_discovery() since last call to + * pd_discovery_access_clear(). + * + * @param port USB-C port number + * @param type Transmit type (SOP, SOP') + * @return True - No other tasks have accessed the data + */ +bool pd_discovery_access_validate(int port, enum tcpm_transmit_type type); + +/* * Returns the pointer to PD alternate mode discovery results + * * Note: Caller function can mutate the data in this structure. * + * TCPMv2 will track all tasks which call this function after the most recent + * pd_discovery_access_clear(), so that the host command task reading out this + * structure may run pd_discovery_access_validate() at the end of its read to + * verify whether data might have changed in that timeframe. + * * @param port USB-C port number * @param type Transmit type (SOP, SOP') for discovered information * @return pointer to PD alternate mode discovery results diff --git a/util/ectool.c b/util/ectool.c index af9870dcf1..be8ceae1db 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -307,6 +307,8 @@ const char help_str[] = " Get/set TMP006 calibration\n" " tmp006raw <tmp006_index>\n" " Get raw TMP006 data\n" + " typecdiscovery <port> <type>\n" + " Get discovery information for port and type\n" " uptimeinfo\n" " Get info about how long the EC has been running and the most\n" " recent AP resets\n" @@ -9364,6 +9366,64 @@ int cmd_pd_write_log(int argc, char *argv[]) return ec_command(EC_CMD_PD_WRITE_LOG_ENTRY, 0, &p, sizeof(p), NULL, 0); } +int cmd_typec_discovery(int argc, char *argv[]) +{ + struct ec_params_typec_discovery p; + struct ec_response_typec_discovery *r = + (struct ec_response_typec_discovery *)ec_inbuf; + char *e; + int rv, i, j; + + if (argc < 3) { + fprintf(stderr, + "Usage: %s <port> <type>\n" + " <port> is the type-c port to query\n" + " <type> is one of:\n" + " 0: SOP\n" + " 1: SOP prime\n", argv[0]); + return -1; + } + + p.port = strtol(argv[1], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad port\n"); + return -1; + } + + p.partner_type = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad type\n"); + return -1; + } + + rv = ec_command(EC_CMD_TYPEC_DISCOVERY, 0, &p, sizeof(p), + ec_inbuf, ec_max_insize); + if (rv < 0) + return -1; + + if (r->identity_count == 0) { + printf("No identity discovered\n"); + return 0; + } + + printf("Identity VDOs:\n"); + for (i = 0; i < r->identity_count; i++) + printf("0x%08x\n", r->discovery_vdo[i]); + + if (r->svid_count == 0) { + printf("No SVIDs discovered\n"); + return 0; + } + + for (i = 0; i < r->svid_count; i++) { + printf("SVID 0x%04x Modes:\n", r->svids[i].svid); + for (j = 0; j < r->svids[i].mode_count; j++) + printf("0x%08x\n", r->svids[i].mode_vdo[j]); + } + + return 0; +} + int cmd_tp_self_test(int argc, char* argv[]) { int rv; @@ -9823,6 +9883,7 @@ const struct command commands[] = { {"tpframeget", cmd_tp_frame_get}, {"tmp006cal", cmd_tmp006cal}, {"tmp006raw", cmd_tmp006raw}, + {"typecdiscovery", cmd_typec_discovery}, {"uptimeinfo", cmd_uptimeinfo}, {"usbchargemode", cmd_usb_charge_set_mode}, {"usbmux", cmd_usb_mux}, |