diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 464006d576..2d0e1962d9 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -8,8 +8,10 @@ #include "common.h" #include "console.h" #include "crc.h" +#include "ec_commands.h" #include "gpio.h" #include "hooks.h" +#include "host_command.h" #include "registers.h" #include "task.h" #include "timer.h" @@ -1333,4 +1335,59 @@ DECLARE_CONSOLE_COMMAND(typec, command_typec, NULL); #endif /* CONFIG_USBC_SS_MUX */ +static int hc_usb_pd_control(struct host_cmd_handler_args *args) +{ + const struct ec_params_usb_pd_control *p = args->params; + + if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) { + enum pd_dual_role_states role; + switch (p->role) { + case USB_PD_CTRL_ROLE_TOGGLE_ON: + role = PD_DRP_TOGGLE_ON; + break; + case USB_PD_CTRL_ROLE_TOGGLE_OFF: + role = PD_DRP_TOGGLE_OFF; + break; + case USB_PD_CTRL_ROLE_FORCE_SINK: + role = PD_DRP_FORCE_SINK; + break; + case USB_PD_CTRL_ROLE_FORCE_SOURCE: + role = PD_DRP_FORCE_SOURCE; + break; + default: + return EC_RES_INVALID_PARAM; + } + pd_set_dual_role(role); + } + +#ifdef CONFIG_USBC_SS_MUX + if (p->mux != USB_PD_CTRL_MUX_NO_CHANGE) { + enum typec_mux mux; + switch (p->mux) { + case USB_PD_CTRL_MUX_NONE: + mux = TYPEC_MUX_NONE; + break; + case USB_PD_CTRL_MUX_USB: + mux = TYPEC_MUX_USB; + break; + case USB_PD_CTRL_MUX_AUTO: + case USB_PD_CTRL_MUX_DP: + mux = TYPEC_MUX_DP; + break; + case USB_PD_CTRL_MUX_DOCK: + mux = TYPEC_MUX_DOCK; + break; + default: + return EC_RES_INVALID_PARAM; + } + board_set_usb_mux(p->port, mux, pd_get_polarity(p->port)); + } +#endif /* CONFIG_USBC_SS_MUX */ + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, + hc_usb_pd_control, + EC_VER_MASK(0)); + #endif /* CONFIG_COMMON_RUNTIME */ diff --git a/include/ec_commands.h b/include/ec_commands.h index 7233d43bd3..1756a11247 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -2500,6 +2500,32 @@ struct ec_response_pd_status { int8_t status; /* currently empty */ } __packed; +/* Set USB type-C port role and muxes */ +#define EC_CMD_USB_PD_CONTROL 0x101 + +enum usb_pd_control_role { + USB_PD_CTRL_ROLE_NO_CHANGE = 0, + USB_PD_CTRL_ROLE_TOGGLE_ON = 1, /* == AUTO */ + USB_PD_CTRL_ROLE_TOGGLE_OFF = 2, + USB_PD_CTRL_ROLE_FORCE_SINK = 3, + USB_PD_CTRL_ROLE_FORCE_SOURCE = 4, +}; + +enum usb_pd_control_mux { + USB_PD_CTRL_MUX_NO_CHANGE = 0, + USB_PD_CTRL_MUX_NONE = 1, + USB_PD_CTRL_MUX_USB = 2, + USB_PD_CTRL_MUX_DP = 3, + USB_PD_CTRL_MUX_DOCK = 4, + USB_PD_CTRL_MUX_AUTO = 5, +}; + +struct ec_params_usb_pd_control { + uint8_t port; + uint8_t role; + uint8_t mux; +} __packed; + /*****************************************************************************/ /* * Passthru commands diff --git a/util/ectool.c b/util/ectool.c index 89c88104a1..4e2f9ff1f2 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -177,6 +177,9 @@ const char help_str[] = " Set USB charging mode\n" " usbmux \n" " Set USB mux switch state\n" + " usbpd \n" + " Control USB PD/type-C\n" " version\n" " Prints EC version\n" " wireless [ [ ]]\n" @@ -2413,6 +2416,81 @@ int cmd_usb_mux(int argc, char *argv[]) } +int cmd_usb_pd(int argc, char *argv[]) +{ + const char *role_str[] = {"", "toggle", "toggle-off", "sink", "source"}; + const char *mux_str[] = {"", "none", "usb", "dp", "dock"}; + struct ec_params_usb_pd_control p; + int rv, i, j; + int option_ok; + char *e; + + p.role = USB_PD_CTRL_ROLE_NO_CHANGE; + p.mux = USB_PD_CTRL_MUX_NO_CHANGE; + + if (argc <= 2) { + fprintf(stderr, "No option specified.\n"); + return -1; + } + + p.port = strtol(argv[1], &e, 0); + if (e && *e) { + fprintf(stderr, "Invalid param (port)\n"); + return -1; + } + + for (i = 2; i < argc; ++i) { + option_ok = 0; + if (!strcmp(argv[i], "auto")) { + if (argc != 3) { + fprintf(stderr, "\"auto\" may not be used " + "with other options.\n"); + return -1; + } + p.role = USB_PD_CTRL_ROLE_TOGGLE_ON; + p.mux = USB_PD_CTRL_MUX_AUTO; + continue; + } + + for (j = 0; j < ARRAY_SIZE(role_str); ++j) { + if (!strcmp(argv[i], role_str[j])) { + if (p.role != USB_PD_CTRL_ROLE_NO_CHANGE) { + fprintf(stderr, + "Only one role allowed.\n"); + return -1; + } + p.role = j; + option_ok = 1; + break; + } + } + if (option_ok) + continue; + + for (j = 0; j < ARRAY_SIZE(mux_str); ++j) { + if (!strcmp(argv[i], mux_str[j])) { + if (p.mux != USB_PD_CTRL_MUX_NO_CHANGE) { + fprintf(stderr, + "Only one mux type allowed.\n"); + return -1; + } + p.mux = j; + option_ok = 1; + break; + } + } + + if (!option_ok) { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + return -1; + } + } + + rv = ec_command(EC_CMD_USB_PD_CONTROL, 0, &p, sizeof(p), NULL, 0); + return (rv < 0 ? rv : 0); +} + + int cmd_kbpress(int argc, char *argv[]) { struct ec_params_mkbp_simulate_key p; @@ -4533,6 +4611,7 @@ const struct command commands[] = { {"tmp006raw", cmd_tmp006raw}, {"usbchargemode", cmd_usb_charge_set_mode}, {"usbmux", cmd_usb_mux}, + {"usbpd", cmd_usb_pd}, {"version", cmd_version}, {"wireless", cmd_wireless}, {NULL, NULL}