diff options
Diffstat (limited to 'common/usb_pd_host_cmd.c')
-rw-r--r-- | common/usb_pd_host_cmd.c | 152 |
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) |