| /* Copyright 2024 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "drivers/ucsi_v3.h" |
| #include "uart.h" |
| #include "usb_common.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <zephyr/logging/log.h> |
| #include <zephyr/shell/shell.h> |
| |
| #include <drivers/pdc.h> |
| #include <usbc/pdc_power_mgmt.h> |
| #include <usbc/pdc_trace_msg.h> |
| |
| static int cmd_get_pd_port(const struct shell *sh, char *arg_val, uint8_t *port) |
| { |
| char *e; |
| |
| *port = strtoul(arg_val, &e, 0); |
| if (*e || !pdc_power_mgmt_is_pdc_port_valid(*port)) { |
| shell_error(sh, "Invalid port"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Helper to print PDC connection status |
| * |
| * @param sh Pointer to shell instance. |
| * @param port Port number. Must be pre-validated. |
| * @return 0 on success, or error code. |
| */ |
| static int print_get_status(const struct shell *sh, int port) |
| { |
| enum pd_power_role pr; |
| enum pd_data_role dr; |
| enum tcpc_cc_polarity polarity; |
| |
| /* Get PDC Status */ |
| pr = pdc_power_mgmt_get_power_role(port); |
| dr = pdc_power_mgmt_pd_get_data_role(port); |
| polarity = pdc_power_mgmt_pd_get_polarity(port); |
| shell_fprintf(sh, SHELL_INFO, |
| "Port C%d CC%d, %s - Role: %s-%s PDC State: %s" |
| "\n", |
| port, polarity + 1, |
| pdc_power_mgmt_is_connected(port) ? "Enable" : "Disable", |
| pr == PD_ROLE_SINK ? "SNK" : "SRC", |
| dr == PD_ROLE_DFP ? "DFP" : "UFP", |
| pdc_power_mgmt_get_task_state_name(port)); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_get_status(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| return print_get_status(sh, port); |
| } |
| |
| /** |
| * @brief Helper to print connector status field |
| * |
| * @param sh Pointer to shell instance. |
| * @param port Port number. Must be pre-validated. |
| * @return 0 on success, or error code. |
| */ |
| static int print_get_connector_status(const struct shell *sh, int port) |
| { |
| union connector_status_t connector_status; |
| int rv; |
| |
| rv = pdc_power_mgmt_get_connector_status(port, &connector_status); |
| if (rv) |
| return rv; |
| |
| shell_fprintf(sh, SHELL_INFO, "Port %d GET_CONNECTOR_STATUS:\n", port); |
| shell_fprintf(sh, SHELL_INFO, |
| " change bits : 0x%04x\n", |
| connector_status.raw_conn_status_change_bits); |
| shell_fprintf(sh, SHELL_INFO, |
| " power_operation_mode : %d\n", |
| connector_status.power_operation_mode); |
| shell_fprintf(sh, SHELL_INFO, |
| " connect_status : %d\n", |
| connector_status.connect_status); |
| shell_fprintf(sh, SHELL_INFO, |
| " power_direction : %d\n", |
| connector_status.power_direction); |
| shell_fprintf(sh, SHELL_INFO, |
| " conn_partner_flags : 0x%02x\n", |
| connector_status.conn_partner_flags); |
| shell_fprintf(sh, SHELL_INFO, |
| " conn_partner_type : %d\n", |
| connector_status.conn_partner_type); |
| shell_fprintf(sh, SHELL_INFO, |
| " rdo : 0x%08x\n", |
| connector_status.rdo); |
| shell_fprintf(sh, SHELL_INFO, |
| " battery_charging_cap_status : %d\n", |
| connector_status.battery_charging_cap_status); |
| shell_fprintf(sh, SHELL_INFO, |
| " provider_caps_limited_reason : %d\n", |
| connector_status.provider_caps_limited_reason); |
| shell_fprintf(sh, SHELL_INFO, |
| " bcd_pd_version : 0x%04x\n", |
| connector_status.bcd_pd_version); |
| shell_fprintf(sh, SHELL_INFO, |
| " orientation : %d\n", |
| connector_status.orientation); |
| shell_fprintf(sh, SHELL_INFO, |
| " sink_path_status : %d\n", |
| connector_status.sink_path_status); |
| shell_fprintf(sh, SHELL_INFO, |
| " reverse_current_protection_status: %d\n", |
| connector_status.reverse_current_protection_status); |
| shell_fprintf(sh, SHELL_INFO, |
| " power_reading_ready : %d\n", |
| connector_status.power_reading_ready); |
| shell_fprintf(sh, SHELL_INFO, |
| " peak_current : %d\n", |
| connector_status.peak_current); |
| shell_fprintf(sh, SHELL_INFO, |
| " average_current : %d\n", |
| connector_status.average_current); |
| shell_fprintf(sh, SHELL_INFO, |
| " voltage_scale : %d\n", |
| connector_status.voltage_scale); |
| shell_fprintf(sh, SHELL_INFO, |
| " voltage_reading : %d\n", |
| connector_status.voltage_reading); |
| shell_fprintf(sh, SHELL_INFO, |
| " voltage : %d mV\n", |
| (connector_status.voltage_reading * |
| connector_status.voltage_scale * 5)); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_get_connector_status(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| int rv; |
| uint8_t port; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| return print_get_connector_status(sh, port); |
| } |
| |
| static int cmd_pdc_get_cable_prop(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| int rv; |
| uint8_t port; |
| union cable_property_t cable_prop; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| rv = pdc_power_mgmt_get_cable_prop(port, &cable_prop); |
| if (rv) |
| return rv; |
| |
| shell_fprintf(sh, SHELL_INFO, "Port %d GET_CABLE_PROP:\n", port); |
| shell_fprintf(sh, SHELL_INFO, |
| " bm_speed_supported : 0x%04x\n", |
| cable_prop.bm_speed_supported); |
| shell_fprintf(sh, SHELL_INFO, |
| " b_current_capability : %d mA\n", |
| cable_prop.b_current_capability * 50); |
| shell_fprintf(sh, SHELL_INFO, |
| " vbus_in_cable : %d\n", |
| cable_prop.vbus_in_cable); |
| shell_fprintf(sh, SHELL_INFO, |
| " cable_type : %d\n", |
| cable_prop.cable_type); |
| shell_fprintf(sh, SHELL_INFO, |
| " directionality : %d\n", |
| cable_prop.directionality); |
| shell_fprintf(sh, SHELL_INFO, |
| " plug_end_type : %d\n", |
| cable_prop.plug_end_type); |
| shell_fprintf(sh, SHELL_INFO, |
| " mode_support : %d\n", |
| cable_prop.mode_support); |
| shell_fprintf(sh, SHELL_INFO, |
| " cable_pd_revision : %d\n", |
| cable_prop.cable_pd_revision); |
| shell_fprintf(sh, SHELL_INFO, |
| " latency : %d\n", |
| cable_prop.latency); |
| |
| return EC_SUCCESS; |
| } |
| |
| /** |
| * @brief Helper to print PDC info response |
| * |
| * @param sh Pointer to shell instance. |
| * @param port Port number. Must be pre-validated. |
| * @param live True for live read, false for cached value. |
| * @return 0 on success, or error code. |
| */ |
| static int print_pdc_info(const struct shell *sh, int port, bool live) |
| { |
| struct pdc_info_t pdc_info = { 0 }; |
| int rv; |
| |
| /* Get PDC Status */ |
| rv = pdc_power_mgmt_get_info(port, &pdc_info, live); |
| if (rv) { |
| shell_error(sh, "Could not get port %u info (%d)", port, rv); |
| return rv; |
| } |
| |
| /* Check if the FW project name is set. */ |
| bool has_proj_name = pdc_info.project_name[0] != '\0' && |
| (uint8_t)pdc_info.project_name[0] != 0xFF; |
| |
| /* Call 1: Basic and Version Info (7 arguments) */ |
| shell_fprintf(sh, SHELL_INFO, |
| "Live: %d\n" |
| "FW Ver: %u.%u.%u\n" |
| "PD Rev: %u\n" |
| "PD Ver: %u\n" |
| "VID/PID: %04x:%04x\n", |
| live, PDC_FWVER_GET_MAJOR(pdc_info.fw_version), |
| PDC_FWVER_GET_MINOR(pdc_info.fw_version), |
| PDC_FWVER_GET_PATCH(pdc_info.fw_version), |
| pdc_info.pd_revision, pdc_info.pd_version, pdc_info.vid, |
| pdc_info.pid); |
| |
| /* Call 2: Flash and Capabilities Info (8 arguments) */ |
| shell_fprintf(sh, SHELL_INFO, |
| "Running Flash Code: %c\n" |
| "Flash Bank: %u\n" |
| "Project Name: '%s'\n" |
| "Driver Name: '%s'\n" |
| "FW Update: %c\n" |
| "FRS Supported: %c\n" |
| "USB Device Capable: %c\n", |
| pdc_info.is_running_flash_code ? 'Y' : 'N', |
| pdc_info.running_in_flash_bank, |
| has_proj_name ? pdc_info.project_name : "<None>", |
| pdc_info.driver_name, pdc_info.no_fw_update ? 'N' : 'Y', |
| pdc_info.frs_supported ? 'Y' : 'N', |
| pdc_info.usb_comm_capable_as_device ? 'Y' : 'N'); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_get_info(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| bool live = true; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| if (argc > 2) { |
| /* Parse optional live parameter */ |
| char *e; |
| int live_param = strtoul(argv[2], &e, 0); |
| if (*e) { |
| shell_error(sh, "Pass 0/1 for live"); |
| return -EINVAL; |
| } |
| |
| live = !!live_param; |
| } |
| |
| return print_pdc_info(sh, port, live); |
| } |
| |
| static int cmd_lpm_ppm_info(const struct shell *sh, size_t argc, char **argv) |
| { |
| struct lpm_ppm_info_t info; |
| uint8_t port; |
| int rv; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| /* Get PDC info using UCSI GET_LPM_PPM_INFO command */ |
| rv = pdc_power_mgmt_get_lpm_ppm_info(port, &info); |
| if (rv) { |
| shell_error(sh, "Could not get port %u info (%d)", port, rv); |
| return rv; |
| } |
| |
| shell_fprintf(sh, SHELL_INFO, |
| "VID/PID: %04x:%04x\n" |
| "XID: %08x\n" |
| "FW Ver: %u.%u\n" |
| "HW Ver: %08x\n", |
| info.vid, info.pid, info.xid, info.fw_ver, |
| info.fw_ver_sub, info.hw_ver); |
| |
| return 0; |
| } |
| |
| static int cmd_vconn_state(const struct shell *sh, size_t argc, char **argv) |
| { |
| bool vconn_state; |
| uint8_t port; |
| int rv; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| vconn_state = pd_get_vconn_state(port); |
| |
| shell_fprintf(sh, SHELL_INFO, "Vconn state: %d (%s)\n", vconn_state, |
| vconn_state ? "sourcing" : "not sourcing"); |
| |
| return 0; |
| } |
| |
| static int cmd_pdc_prs(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| /* Trigger power role swap request */ |
| pdc_power_mgmt_request_power_swap(port); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_drs(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| /* Verify port partner supports data role swaps */ |
| if (!pdc_power_mgmt_get_partner_data_swap_capable(port)) { |
| shell_error(sh, "Port partner doesn't support drs"); |
| return -EIO; |
| } |
| |
| /* Trigger data role swap request */ |
| pdc_power_mgmt_request_data_swap(port); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_dualrole(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| enum pd_dual_role_states state; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| if (argc >= 3) { |
| /* Set dual role state */ |
| if (!strcmp(argv[2], "on")) { |
| state = PD_DRP_TOGGLE_ON; |
| } else if (!strcmp(argv[2], "off")) { |
| state = PD_DRP_TOGGLE_OFF; |
| } else if (!strcmp(argv[2], "freeze")) { |
| state = PD_DRP_FREEZE; |
| } else if (!strcmp(argv[2], "sink")) { |
| state = PD_DRP_FORCE_SINK; |
| } else if (!strcmp(argv[2], "source")) { |
| state = PD_DRP_FORCE_SOURCE; |
| } else { |
| shell_error(sh, "Invalid dualrole mode"); |
| return -EINVAL; |
| } |
| |
| pdc_power_mgmt_set_dual_role(port, state); |
| } |
| |
| /* Print current state */ |
| const char *state_str; |
| |
| state = pdc_power_mgmt_get_dual_role(port); |
| |
| switch (state) { |
| case PD_DRP_TOGGLE_ON: |
| state_str = "TOGGLE_ON"; |
| break; |
| case PD_DRP_TOGGLE_OFF: |
| state_str = "TOGGLE_OFF"; |
| break; |
| case PD_DRP_FREEZE: |
| state_str = "FREEZE"; |
| break; |
| case PD_DRP_FORCE_SINK: |
| state_str = "FORCE_SINK"; |
| break; |
| case PD_DRP_FORCE_SOURCE: |
| state_str = "FORCE_SOURCE"; |
| break; |
| default: |
| state_str = "Unknown"; |
| break; |
| } |
| |
| shell_info(sh, "Dual role state: %s", state_str); |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_get_drp_mode(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| int rv; |
| uint8_t port; |
| enum drp_mode_t drp_mode; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| rv = pdc_power_mgmt_get_drp_mode(port, &drp_mode); |
| if (rv) { |
| shell_error(sh, "Could not get DRP mode: %d (port %u)", rv, |
| port); |
| return rv; |
| } |
| |
| shell_info(sh, "DRP mode on port %d is %s", port, |
| get_drp_mode_name(drp_mode)); |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_trysrc(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t enable = 0; |
| uint8_t port; |
| char *e; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| enable = strtoul(argv[2], &e, 10); |
| if (*e) { |
| shell_error(sh, "unable to parse TrySrc value"); |
| return -EINVAL; |
| } |
| if (!(enable == 0 || enable == 1)) { |
| shell_error(sh, "expecting [0|1]"); |
| return -EINVAL; |
| } |
| |
| rv = pdc_power_mgmt_set_trysrc(port, enable); |
| if (rv) { |
| shell_error(sh, "Could not set trysrc: %d (port %u)", rv, port); |
| return rv; |
| } |
| shell_info(sh, "Try.SRC Forced %s (port %u)", enable ? "ON" : "OFF", |
| port); |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_reset(const struct shell *sh, size_t argc, char **argv) |
| { |
| uint8_t port; |
| int rv; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| /* Trigger a PDC reset for this port. */ |
| rv = pdc_power_mgmt_reset(port); |
| if (rv) { |
| shell_error(sh, "Could not reset port %u (%d)", port, rv); |
| return rv; |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_pdc_connector_reset(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| int rv; |
| uint8_t port; |
| enum connector_reset reset_type; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| if (!strcmp(argv[2], "hard")) { |
| reset_type = PD_HARD_RESET; |
| } else if (!strcmp(argv[2], "data")) { |
| reset_type = PD_DATA_RESET; |
| } else { |
| shell_error(sh, "Invalid connector reset type"); |
| return -EINVAL; |
| } |
| |
| /* Trigger a PDC connector reset */ |
| rv = pdc_power_mgmt_connector_reset(port, reset_type); |
| if (rv) { |
| shell_error(sh, "CONNECTOR_RESET not sent to port %u (%d)", |
| port, rv); |
| } |
| |
| return rv; |
| } |
| |
| /* LCOV_EXCL_START - No known way to test tab-completion feature */ |
| /** |
| * @brief Tab-completion of "suspend" or "resume" for the comms subcommand |
| */ |
| static void pdc_console_get_suspend_or_resume(size_t idx, |
| struct shell_static_entry *entry) |
| { |
| entry->syntax = NULL; |
| entry->handler = NULL; |
| entry->help = NULL; |
| entry->subcmd = NULL; |
| |
| switch (idx) { |
| case 0: |
| entry->syntax = "suspend"; |
| return; |
| case 1: |
| entry->syntax = "resume"; |
| return; |
| } |
| } |
| |
| SHELL_DYNAMIC_CMD_CREATE(dsub_suspend_or_resume, |
| pdc_console_get_suspend_or_resume); |
| |
| /** |
| * @brief Tab-completion of "normal" or "debug" for the SBU mux subcommand |
| */ |
| static void pdc_console_get_normal_or_debug(size_t idx, |
| struct shell_static_entry *entry) |
| { |
| entry->syntax = NULL; |
| entry->handler = NULL; |
| entry->help = NULL; |
| entry->subcmd = NULL; |
| |
| switch (idx) { |
| case 0: |
| entry->syntax = "normal"; |
| return; |
| case 1: |
| entry->syntax = "debug"; |
| return; |
| } |
| } |
| |
| SHELL_DYNAMIC_CMD_CREATE(dsub_sbu_mux_modes, pdc_console_get_normal_or_debug); |
| /* LCOV_EXCL_STOP */ |
| |
| static int cmd_pdc_comms_state(const struct shell *sh, size_t argc, char **argv) |
| { |
| bool enable; |
| int rv; |
| |
| /* Suspend or resume PDC comms */ |
| if (!strncmp(argv[1], "suspend", strlen("suspend"))) { |
| shell_fprintf(sh, SHELL_INFO, "Suspend port threads\n"); |
| enable = false; |
| } else if (!strncmp(argv[1], "resume", strlen("resume"))) { |
| shell_fprintf(sh, SHELL_INFO, "Resume port threads\n"); |
| enable = true; |
| } else { |
| shell_error(sh, "Invalid value"); |
| return -EINVAL; |
| } |
| |
| /* Apply to all ports |
| * |
| * TODO(b/323371550): This command should take a chip argument and |
| * target only ports serviced by that chip. |
| */ |
| rv = pdc_power_mgmt_set_comms_state(enable); |
| |
| if (rv) { |
| shell_fprintf(sh, SHELL_ERROR, "Could not %s PDC: (%d)\n", |
| argv[1], rv); |
| } |
| |
| return rv; |
| } |
| |
| static int cmd_pdc_src_voltage(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| int mv; |
| uint8_t port; |
| char *e; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| if (argc > 2) { |
| /* Request a particular voltage and convert to mV */ |
| mv = strtol(argv[2], &e, 10) * 1000; |
| if (*e) |
| return EC_ERROR_PARAM2; |
| } else { |
| /* Use max */ |
| mv = pd_get_max_voltage(); |
| shell_fprintf(sh, SHELL_INFO, "Using max voltage (%dmV)\n", mv); |
| } |
| |
| if (mv < PD_MIN_MV) { |
| shell_fprintf(sh, SHELL_ERROR, |
| "Must be >= %umV (USB-PD minimum)\n", PD_MIN_MV); |
| return EC_ERROR_PARAM2; |
| } |
| |
| if (mv > CONFIG_PLATFORM_EC_USB_PD_MAX_VOLTAGE_MV) { |
| shell_fprintf(sh, SHELL_ERROR, |
| "Must be <= %umV (board limit)\n", |
| CONFIG_PLATFORM_EC_USB_PD_MAX_VOLTAGE_MV); |
| return EC_ERROR_PARAM2; |
| } |
| |
| shell_fprintf(sh, SHELL_INFO, "Requesting to source %dmV\n", mv); |
| pd_request_source_voltage(port, mv); |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Helper to print partner source caps |
| * |
| * @param sh Pointer to shell instance. |
| * @param port Port number. Must be pre-validated. |
| * @return 0 on success, or error code. |
| */ |
| static int print_srccaps(const struct shell *sh, int port) |
| { |
| const uint32_t *const src_caps = pdc_power_mgmt_get_src_caps(port); |
| uint8_t src_caps_count = pdc_power_mgmt_get_src_cap_cnt(port); |
| uint32_t rdo = 0; |
| |
| if (src_caps == NULL || src_caps_count == 0) { |
| shell_fprintf(sh, SHELL_ERROR, "No source caps for port %u\n", |
| port); |
| return 0; |
| } |
| |
| if (pdc_power_mgmt_get_rdo(port, &rdo) != 0) { |
| shell_fprintf(sh, SHELL_INFO, "No active RDO on port %u\n", |
| port); |
| } else { |
| shell_fprintf(sh, SHELL_INFO, |
| "RDO: 0x%08x (index=%u, I_op=%umA, I_max=%umA)\n", |
| rdo, RDO_POS(rdo), RDO_FIXED_GET_VAR_OP_CURR(rdo), |
| RDO_FIXED_GET_VAR_MAX_CURR(rdo)); |
| } |
| |
| for (uint8_t i = 0; i < src_caps_count; i++) { |
| uint32_t src_cap = src_caps[i]; |
| uint32_t max_ma = 0, max_mv = 0, min_mv = 0; |
| const char *type_str = NULL; |
| |
| pd_extract_pdo_power_unclamped(src_cap, &max_ma, &max_mv, |
| &min_mv); |
| |
| switch (src_cap & PDO_TYPE_MASK) { |
| case PDO_TYPE_FIXED: |
| type_str = "FIX"; |
| /* Fixed PDOs have flags and a single voltage */ |
| shell_fprintf( |
| sh, SHELL_INFO, |
| "%cSrc %u: %08x %s %13umV, %5umA " |
| "[%s %s %s %s %s]\n", |
| (RDO_POS(rdo) == i + 1 ? '*' : ' '), i, src_cap, |
| type_str, max_mv, max_ma, |
| src_cap & PDO_FIXED_DUAL_ROLE ? "DRP" : " ", |
| src_cap & PDO_FIXED_UNCONSTRAINED ? "UP" : " ", |
| src_cap & PDO_FIXED_COMM_CAP ? "USB" : " ", |
| src_cap & PDO_FIXED_DATA_SWAP ? "DRD" : " ", |
| src_cap & PDO_FIXED_FRS_CURR_MASK ? "FRS" : |
| " "); |
| continue; |
| case PDO_TYPE_BATTERY: |
| type_str = "BAT"; |
| break; |
| case PDO_TYPE_VARIABLE: |
| type_str = "VAR"; |
| break; |
| case PDO_TYPE_AUGMENTED: |
| type_str = "AUG"; |
| break; |
| } |
| |
| /* Battery, variable, and augmented PDOs have voltage |
| * ranges but no flags. |
| */ |
| shell_fprintf(sh, SHELL_INFO, |
| "%cSrc %u: %08x %s %5umV-%5umV, %5umA\n", |
| (RDO_POS(rdo) == i + 1 ? '*' : ' '), i, src_cap, |
| type_str, min_mv, max_mv, max_ma); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_pdc_srccaps(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| return print_srccaps(sh, port); |
| } |
| |
| static int cmd_pdc_set_bbr_cts(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| uint8_t port; |
| bool enable; |
| |
| /* Get PD port number */ |
| rv = cmd_get_pd_port(sh, argv[1], &port); |
| if (rv) |
| return rv; |
| |
| if (!strcmp(argv[2], "on")) { |
| enable = true; |
| } else if (!strcmp(argv[2], "off")) { |
| enable = false; |
| } else { |
| shell_error(sh, "Must be on/off"); |
| return -EINVAL; |
| } |
| |
| rv = pdc_power_mgmt_set_bbr_cts(port, enable); |
| |
| if (rv) { |
| shell_error(sh, "SET_BBR_CTS failed on port %u (%d)", port, rv); |
| } |
| |
| return rv; |
| } |
| |
| #ifdef CONFIG_USBC_PDC_DRIVEN_CCD |
| /** |
| * @brief Return a string representation for an `enum pdc_sbu_mux_mode` value |
| */ |
| static const char *sbu_mux_mode_to_str(enum pdc_sbu_mux_mode mode) |
| { |
| switch (mode) { |
| case PDC_SBU_MUX_MODE_NORMAL: |
| return "NORMAL"; |
| case PDC_SBU_MUX_MODE_FORCE_DBG: |
| return "FORCE_DEBUG"; |
| default: |
| return "Bad Value"; |
| } |
| } |
| |
| /** |
| * @brief Helper to print PDC sbumux mode |
| * |
| * @param sh Pointer to shell instance. |
| * @param port Port number. Must be pre-validated. |
| * @return 0 on success, or error code. |
| */ |
| static int print_sbumux_mode(const struct shell *sh) |
| { |
| enum pdc_sbu_mux_mode mode; |
| int ccd_port; |
| int rv; |
| |
| rv = pdc_power_mgmt_get_sbu_mux_mode(&mode, &ccd_port); |
| |
| if (rv == -ENOTSUP) { |
| shell_error(sh, "No CCD port specified in devicetree"); |
| return rv; |
| } else if (rv < 0) { |
| shell_error(sh, "Error getting SBU mux mode: %d", rv); |
| return rv; |
| } |
| |
| shell_info(sh, "CCD Port: C%d, Mode: %s (%d)", ccd_port, |
| sbu_mux_mode_to_str(mode), mode); |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Get or set the PDC's SBU mux operating mode (normal or forced to debug |
| * for CCD keepalive) |
| */ |
| static int cmd_pdc_sbu_mux_mode(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| enum pdc_sbu_mux_mode mode; |
| int rv; |
| |
| if (argc < 2) { |
| /* Get current mode and exit */ |
| return print_sbumux_mode(sh); |
| } |
| |
| if (!strncmp(argv[1], "normal", strlen("normal"))) { |
| mode = PDC_SBU_MUX_MODE_NORMAL; |
| } else if (!strncmp(argv[1], "debug", strlen("debug"))) { |
| mode = PDC_SBU_MUX_MODE_FORCE_DBG; |
| } else { |
| shell_error(sh, "Invalid value"); |
| return -EINVAL; |
| } |
| |
| rv = pdc_power_mgmt_set_sbu_mux_mode(mode); |
| |
| if (rv == -ENOTSUP) { |
| shell_error(sh, "No CCD port specified in devicetree"); |
| return rv; |
| } else if (rv < 0) { |
| shell_error(sh, "Error setting SBU mux mode: %d", rv); |
| return rv; |
| } |
| |
| shell_info(sh, "Set CCD port (C%d) SBU mux mode to %s (%d)", |
| pdc_power_mgmt_get_ccd_port(), sbu_mux_mode_to_str(mode), |
| mode); |
| return 0; |
| } |
| #endif /* defined(CONFIG_USBC_PDC_DRIVEN_CCD) */ |
| |
| static int cmd_set_ap_power_state(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| enum power_state state; |
| |
| if (!strcmp(argv[1], "s0")) { |
| state = POWER_S0; |
| } else if (!strcmp(argv[1], "s5")) { |
| state = POWER_S5; |
| } else { |
| shell_error(sh, "Must be 's0' or 's5'"); |
| return -EINVAL; |
| } |
| |
| return pdc_power_mgmt_set_ap_power_state(state); |
| } |
| |
| static int cmd_pdc_dump(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| |
| /* Iterate through all USB-C ports */ |
| for (int port = 0; port < pdc_power_mgmt_get_usb_pd_port_count(); |
| port++) { |
| shell_info(sh, "===== Port C%d =====", port); |
| |
| /* pdc status <port> */ |
| rv = print_get_status(sh, port); |
| if (rv) { |
| shell_error(sh, "`pdc status %d` failed: %d", port, rv); |
| } |
| |
| /* pdc info <port> */ |
| rv = print_pdc_info(sh, port, 1); |
| if (rv) { |
| shell_error(sh, "`pdc info %d` failed: %d", port, rv); |
| } |
| |
| /* pdc connector_status <port> */ |
| rv = print_get_connector_status(sh, port); |
| if (rv) { |
| shell_error(sh, "`pdc connector_status %d` failed: %d", |
| port, rv); |
| } |
| |
| /* pdc srccaps <port> */ |
| rv = print_srccaps(sh, port); |
| if (rv) { |
| shell_error(sh, "`pdc srccaps %d` failed: %d", port, |
| rv); |
| } |
| } |
| |
| shell_info(sh, "===== General ====="); |
| |
| #ifdef CONFIG_USBC_PDC_DRIVEN_CCD |
| /* pdc sbumux */ |
| rv = print_sbumux_mode(sh); |
| if (rv) { |
| shell_error(sh, "`pdc sbumux` failed: %d", rv); |
| } |
| #endif /* CONFIG_USBC_PDC_DRIVEN_CCD */ |
| |
| return 0; |
| } |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE( |
| sub_pdc_cmds, |
| SHELL_CMD_ARG(status, NULL, SHELL_HELP("Get PD status", "<port>"), |
| cmd_pdc_get_status, 2, 0), |
| SHELL_CMD_ARG( |
| info, NULL, |
| SHELL_HELP( |
| "Get PDC chip info. Live defaults to 1 to force a new " |
| "read from chip. Pass 0 to use cached info", |
| "<port> [live]"), |
| cmd_pdc_get_info, 2, 1), |
| SHELL_CMD_ARG(prs, NULL, |
| SHELL_HELP("Trigger power role swap", "<port>"), |
| cmd_pdc_prs, 2, 0), |
| SHELL_CMD_ARG(drs, NULL, SHELL_HELP("Trigger data role swap", "<port>"), |
| cmd_pdc_drs, 2, 0), |
| SHELL_CMD_ARG(reset, NULL, SHELL_HELP("Trigger a PDC reset", "<port>"), |
| cmd_pdc_reset, 2, 0), |
| SHELL_CMD_ARG(dualrole, NULL, |
| SHELL_HELP("Set or get dualrole mode", |
| "<port> [on|off|freeze|sink|source]"), |
| cmd_pdc_dualrole, 2, 1), |
| SHELL_CMD_ARG(trysrc, NULL, |
| SHELL_HELP("Set trysrc mode", "<port> [0|1]"), |
| cmd_pdc_trysrc, 3, 0), |
| SHELL_CMD_ARG(drp, NULL, SHELL_HELP("Get DRP mode", "<port>"), |
| cmd_pdc_get_drp_mode, 2, 0), |
| SHELL_CMD_ARG(conn_reset, NULL, |
| SHELL_HELP("Trigger hard or data reset", |
| "<port> [hard|data]"), |
| cmd_pdc_connector_reset, 3, 0), |
| SHELL_CMD_ARG(comms, &dsub_suspend_or_resume, |
| SHELL_HELP("Suspend/resume PDC command communication", |
| "[suspend|resume]"), |
| cmd_pdc_comms_state, 2, 0), |
| SHELL_CMD_ARG(connector_status, NULL, |
| SHELL_HELP("Print the UCSI GET_CONNECTOR_STATUS", |
| "<port>"), |
| cmd_pdc_get_connector_status, 2, 0), |
| SHELL_CMD_ARG(cable_prop, NULL, |
| SHELL_HELP("Print the UCSI GET_CABLE_PROPERTY", "<port>"), |
| cmd_pdc_get_cable_prop, 2, 0), |
| SHELL_CMD_ARG( |
| src_voltage, NULL, |
| SHELL_HELP("Request to source a given voltage from PSU. " |
| "Omit last arg to use maximum supported voltage", |
| "<port> [volts]"), |
| cmd_pdc_src_voltage, 2, 1), |
| SHELL_CMD_ARG( |
| srccaps, NULL, |
| SHELL_HELP( |
| "Print current source capability PDOs received by the " |
| "given port", |
| "<port>"), |
| cmd_pdc_srccaps, 2, 0), |
| SHELL_CMD_ARG( |
| lpm_ppm_info, NULL, |
| SHELL_HELP("Get PDC chip info via GET_LPM_PPM_INFO UCSI cmd", |
| "<port>"), |
| cmd_lpm_ppm_info, 2, 0), |
| SHELL_CMD_ARG(vconn, NULL, |
| SHELL_HELP("Get Vconn state for a port", "<port>"), |
| cmd_vconn_state, 2, 0), |
| SHELL_CMD_ARG(set_bbr_cts, NULL, |
| SHELL_HELP("Enable/disable BBR compliance test mode", |
| "<port> [on|off]"), |
| cmd_pdc_set_bbr_cts, 3, 0), |
| #ifdef CONFIG_USBC_PDC_DRIVEN_CCD |
| SHELL_CMD_ARG(sbumux, &dsub_sbu_mux_modes, |
| SHELL_HELP("Get or set the SBU mux mode " |
| "(for PDC-driven CCD boards only)", |
| "[normal|debug]"), |
| cmd_pdc_sbu_mux_mode, 1, 1), |
| #endif /* defined(CONFIG_USBC_PDC_DRIVEN_CCD) */ |
| SHELL_COND_CMD_ARG(IS_ENABLED(CONFIG_USBC_PDC_TRACE_MSG_CONSOLE_CMD), |
| trace, NULL, |
| SHELL_HELP("Dump accumulated PDC trace messages " |
| "and optionally set trace port", |
| "<Type-C port number>|all|on|none|off"), |
| cmd_pdc_trace, 1, 1), |
| SHELL_CMD_ARG(ap_state, NULL, |
| SHELL_HELP("Notify the PDC of AP power state change", |
| "[s0|s5]"), |
| cmd_set_ap_power_state, 2, 0), |
| SHELL_CMD_ARG(dump, NULL, |
| "Print diagnostic data for all ports\n" |
| "Usage: pdc dump", |
| cmd_pdc_dump, 1, 0), |
| SHELL_SUBCMD_SET_END); |
| |
| SHELL_CMD_REGISTER(pdc, &sub_pdc_cmds, "PDC console commands", NULL); |
| |
| static int cmd_pd_version(const struct shell *sh, size_t argc, char **argv) |
| { |
| shell_fprintf(sh, SHELL_INFO, "3\n"); |
| return EC_SUCCESS; |
| } |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(sub_pd_cmds, |
| SHELL_CMD(version, NULL, |
| SHELL_HELP("Get PD version", NULL), |
| cmd_pd_version), |
| SHELL_SUBCMD_SET_END); |
| |
| SHELL_CMD_REGISTER(pd, &sub_pd_cmds, "PD commands (deprecated)", NULL); |