summaryrefslogtreecommitdiff
path: root/common/usb_pd_host_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_pd_host_cmd.c')
-rw-r--r--common/usb_pd_host_cmd.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/common/usb_pd_host_cmd.c b/common/usb_pd_host_cmd.c
index 712d6182e7..bb8a1532a7 100644
--- a/common/usb_pd_host_cmd.c
+++ b/common/usb_pd_host_cmd.c
@@ -11,6 +11,7 @@
#include "ec_commands.h"
#include "host_command.h"
#include "tcpm.h"
+#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
@@ -216,6 +217,157 @@ static enum ec_status hc_remote_pd_dev_info(struct host_cmd_handler_args *args)
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO,
hc_remote_pd_dev_info,
EC_VER_MASK(0));
+
+static const enum pd_dual_role_states dual_role_map[USB_PD_CTRL_ROLE_COUNT] = {
+ [USB_PD_CTRL_ROLE_TOGGLE_ON] = PD_DRP_TOGGLE_ON,
+ [USB_PD_CTRL_ROLE_TOGGLE_OFF] = PD_DRP_TOGGLE_OFF,
+ [USB_PD_CTRL_ROLE_FORCE_SINK] = PD_DRP_FORCE_SINK,
+ [USB_PD_CTRL_ROLE_FORCE_SOURCE] = PD_DRP_FORCE_SOURCE,
+ [USB_PD_CTRL_ROLE_FREEZE] = PD_DRP_FREEZE,
+};
+
+static const mux_state_t typec_mux_map[USB_PD_CTRL_MUX_COUNT] = {
+ [USB_PD_CTRL_MUX_NONE] = USB_PD_MUX_NONE,
+ [USB_PD_CTRL_MUX_USB] = USB_PD_MUX_USB_ENABLED,
+ [USB_PD_CTRL_MUX_AUTO] = USB_PD_MUX_DP_ENABLED,
+ [USB_PD_CTRL_MUX_DP] = USB_PD_MUX_DP_ENABLED,
+ [USB_PD_CTRL_MUX_DOCK] = USB_PD_MUX_DOCK,
+};
+
+/*
+ * Combines the following information into a single byte
+ * Bit 0: Active/Passive cable
+ * Bit 1: Optical/Non-optical cable
+ * Bit 2: Legacy Thunderbolt adapter
+ * Bit 3: Active Link Uni-Direction/Bi-Direction
+ */
+static uint8_t get_pd_control_flags(int port)
+{
+ union tbt_mode_resp_cable cable_resp = get_cable_tbt_vdo(port);
+ union tbt_mode_resp_device device_resp = get_dev_tbt_vdo(port);
+
+ /*
+ * Ref: USB Type-C Cable and Connector Specification
+ * Table F-11 TBT3 Cable Discover Mode VDO Responses
+ * For Passive cables, Active Cable Plug link training is set to 0
+ */
+ return (cable_resp.lsrx_comm == UNIDIR_LSRX_COMM ?
+ USB_PD_CTRL_ACTIVE_LINK_UNIDIR : 0) |
+ (device_resp.tbt_adapter == TBT_ADAPTER_TBT2_LEGACY ?
+ USB_PD_CTRL_TBT_LEGACY_ADAPTER : 0) |
+ (cable_resp.tbt_cable == TBT_CABLE_OPTICAL ?
+ USB_PD_CTRL_OPTICAL_CABLE : 0) |
+ (cable_resp.retimer_type == USB_RETIMER ?
+ USB_PD_CTRL_ACTIVE_CABLE : 0);
+}
+
+static uint8_t pd_get_role_flags(int port)
+{
+ return (pd_get_power_role(port) == PD_ROLE_SOURCE ?
+ PD_CTRL_RESP_ROLE_POWER : 0) |
+ (pd_get_data_role(port) == PD_ROLE_DFP ?
+ PD_CTRL_RESP_ROLE_DATA : 0) |
+ (pd_get_vconn_state(port) ?
+ PD_CTRL_RESP_ROLE_VCONN : 0) |
+ (pd_get_partner_dual_role_power(port) ?
+ PD_CTRL_RESP_ROLE_DR_POWER : 0) |
+ (pd_get_partner_data_swap_capable(port) ?
+ PD_CTRL_RESP_ROLE_DR_DATA : 0) |
+ (pd_get_partner_usb_comm_capable(port) ?
+ PD_CTRL_RESP_ROLE_USB_COMM : 0) |
+ (pd_get_partner_unconstr_power(port) ?
+ PD_CTRL_RESP_ROLE_UNCONSTRAINED : 0);
+}
+
+static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_usb_pd_control *p = args->params;
+ struct ec_response_usb_pd_control_v2 *r_v2 = args->response;
+ struct ec_response_usb_pd_control_v1 *r_v1 = args->response;
+ struct ec_response_usb_pd_control *r = args->response;
+ const char *task_state_name;
+
+ if (p->port >= board_get_usb_pd_port_count())
+ return EC_RES_INVALID_PARAM;
+
+ if (p->role >= USB_PD_CTRL_ROLE_COUNT ||
+ p->mux >= USB_PD_CTRL_MUX_COUNT)
+ return EC_RES_INVALID_PARAM;
+
+ if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) {
+ if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE))
+ pd_set_dual_role(p->port, dual_role_map[p->role]);
+ else
+ return EC_RES_INVALID_PARAM;
+ }
+
+ if (IS_ENABLED(CONFIG_USBC_SS_MUX) &&
+ p->mux != USB_PD_CTRL_MUX_NO_CHANGE)
+ usb_mux_set(p->port, typec_mux_map[p->mux],
+ typec_mux_map[p->mux] == USB_PD_MUX_NONE ?
+ USB_SWITCH_DISCONNECT :
+ USB_SWITCH_CONNECT,
+ pd_get_polarity(p->port));
+
+ if (p->swap == USB_PD_CTRL_SWAP_DATA) {
+ pd_request_data_swap(p->port);
+ } else if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) {
+ if (p->swap == USB_PD_CTRL_SWAP_POWER)
+ pd_request_power_swap(p->port);
+ else if (IS_ENABLED(CONFIG_USBC_VCONN_SWAP) &&
+ p->swap == USB_PD_CTRL_SWAP_VCONN)
+ pd_request_vconn_swap(p->port);
+ }
+
+ switch (args->version) {
+ case 0:
+ r->enabled = pd_comm_is_enabled(p->port);
+ r->polarity = pd_get_polarity(p->port);
+ r->role = pd_get_power_role(p->port);
+ r->state = pd_get_task_state(p->port);
+ args->response_size = sizeof(*r);
+ break;
+ case 1:
+ case 2:
+ r_v2->enabled =
+ (pd_comm_is_enabled(p->port) ?
+ PD_CTRL_RESP_ENABLED_COMMS : 0) |
+ (pd_is_connected(p->port) ?
+ PD_CTRL_RESP_ENABLED_CONNECTED : 0) |
+ (pd_capable(p->port) ?
+ PD_CTRL_RESP_ENABLED_PD_CAPABLE : 0);
+ r_v2->role = pd_get_role_flags(p->port);
+ r_v2->polarity = pd_get_polarity(p->port);
+
+ r_v2->cc_state = pd_get_task_cc_state(p->port);
+ task_state_name = pd_get_task_state_name(p->port);
+ if (task_state_name)
+ strzcpy(r_v2->state, task_state_name,
+ sizeof(r_v2->state));
+ else
+ r_v2->state[0] = '\0';
+
+ if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
+ r_v2->dp_mode = get_dp_pin_mode(p->port);
+
+ r_v2->control_flags = get_pd_control_flags(p->port);
+ r_v2->cable_speed = get_tbt_cable_speed(p->port);
+ r_v2->cable_gen = get_tbt_rounded_support(p->port);
+
+ if (args->version == 1)
+ args->response_size = sizeof(*r_v1);
+ else
+ args->response_size = sizeof(*r_v2);
+
+ break;
+ default:
+ return EC_RES_INVALID_PARAM;
+ }
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL,
+ hc_usb_pd_control,
+ EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2));
#endif /* CONFIG_COMMON_RUNTIME */
__overridable enum ec_pd_port_location board_get_pd_port_location(int port)