summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/usbc/build.mk1
-rw-r--r--common/usbc/usb_pd_host.c96
-rw-r--r--common/usbc/usb_pe_drp_sm.c21
-rw-r--r--include/ec_commands.h37
-rw-r--r--include/usb_pd.h26
-rw-r--r--util/ectool.c61
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},