diff options
Diffstat (limited to 'common/usb_common.c')
-rw-r--r-- | common/usb_common.c | 1027 |
1 files changed, 0 insertions, 1027 deletions
diff --git a/common/usb_common.c b/common/usb_common.c deleted file mode 100644 index 786bd118cf..0000000000 --- a/common/usb_common.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * Contains common USB functions shared between the old (i.e. usb_pd_protocol) - * and the new (i.e. usb_sm_*) USB-C PD stacks. - */ - -#include "atomic.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "stdbool.h" -#include "host_command.h" -#include "system.h" -#include "task.h" -#include "usb_api.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_flags.h" -#include "usb_pd_tcpm.h" -#include "usbc_ocp.h" -#include "usbc_ppc.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -/* - * If we are trying to upgrade PD firmwares (TCPC chips, retimer, etc), we - * need to ensure the battery has enough charge for this process. 100mAh - * is about 5% of most batteries, and it should be enough charge to get us - * through the EC jump to RW and PD upgrade. - */ -#define MIN_BATTERY_FOR_PD_UPGRADE_MAH 100 /* mAH */ - -#if defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) -int hex8tou32(char *str, uint32_t *val) -{ - char *ptr = str; - uint32_t tmp = 0; - - while (*ptr) { - char c = *ptr++; - - if (c >= '0' && c <= '9') - tmp = (tmp << 4) + (c - '0'); - else if (c >= 'A' && c <= 'F') - tmp = (tmp << 4) + (c - 'A' + 10); - else if (c >= 'a' && c <= 'f') - tmp = (tmp << 4) + (c - 'a' + 10); - else - return EC_ERROR_INVAL; - } - if (ptr != str + 8) - return EC_ERROR_INVAL; - *val = tmp; - return EC_SUCCESS; -} - -int remote_flashing(int argc, char **argv) -{ - int port, cnt, cmd; - uint32_t data[VDO_MAX_SIZE-1]; - char *e; - static int flash_offset[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (argc < 4 || argc > (VDO_MAX_SIZE + 4 - 1)) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &e, 10); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - cnt = 0; - if (!strcasecmp(argv[3], "erase")) { - cmd = VDO_CMD_FLASH_ERASE; - flash_offset[port] = 0; - ccprintf("ERASE ..."); - } else if (!strcasecmp(argv[3], "reboot")) { - cmd = VDO_CMD_REBOOT; - ccprintf("REBOOT ..."); - } else if (!strcasecmp(argv[3], "signature")) { - cmd = VDO_CMD_ERASE_SIG; - ccprintf("ERASE SIG ..."); - } else if (!strcasecmp(argv[3], "info")) { - cmd = VDO_CMD_READ_INFO; - ccprintf("INFO..."); - } else if (!strcasecmp(argv[3], "version")) { - cmd = VDO_CMD_VERSION; - ccprintf("VERSION..."); - } else { - int i; - - argc -= 3; - for (i = 0; i < argc; i++) - if (hex8tou32(argv[i+3], data + i)) - return EC_ERROR_INVAL; - cmd = VDO_CMD_FLASH_WRITE; - cnt = argc; - ccprintf("WRITE %d @%04x ...", argc * 4, - flash_offset[port]); - flash_offset[port] += argc * 4; - } - - pd_send_vdm(port, USB_VID_GOOGLE, cmd, data, cnt); - - /* Wait until VDM is done */ - while (pd[port].vdm_state > 0) - task_wait_event(100*MSEC); - - ccprintf("DONE %d\n", pd[port].vdm_state); - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) */ - -bool pd_firmware_upgrade_check_power_readiness(int port) -{ - if (IS_ENABLED(HAS_TASK_CHARGER)) { - struct batt_params batt = { 0 }; - /* - * Cannot rely on the EC's active charger data as the - * EC may just rebooted into RW and has not necessarily - * picked the active charger yet. Charger task may not - * initialized, so check battery directly. - * Prevent the upgrade if the battery doesn't have enough - * charge to finish the upgrade. - */ - battery_get_params(&batt); - if (batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY || - batt.remaining_capacity < - MIN_BATTERY_FOR_PD_UPGRADE_MAH) { - CPRINTS("C%d: Cannot suspend for upgrade, not " - "enough battery (%dmAh)!", - port, batt.remaining_capacity); - return false; - } - } else { - /* VBUS is present on the port (it is either a - * source or sink) to provide power, so don't allow - * PD firmware upgrade on the port. - */ - if (pd_is_vbus_present(port)) - return false; - } - - return true; -} - -int usb_get_battery_soc(void) -{ -#if defined(CONFIG_CHARGER) - return charge_get_percent(); -#elif defined(CONFIG_BATTERY) - return board_get_battery_soc(); -#else - return 0; -#endif -} - -#if defined(CONFIG_USB_PD_PREFER_MV) && defined(PD_PREFER_LOW_VOLTAGE) + \ - defined(PD_PREFER_HIGH_VOLTAGE) > 1 -#error "PD preferred voltage strategy should be mutually exclusive." -#endif - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -typec_current_t usb_get_typec_current_limit(enum tcpc_cc_polarity polarity, - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - typec_current_t charge = 0; - enum tcpc_cc_voltage_status cc; - enum tcpc_cc_voltage_status cc_alt; - - cc = polarity_rm_dts(polarity) ? cc2 : cc1; - cc_alt = polarity_rm_dts(polarity) ? cc1 : cc2; - - switch (cc) { - case TYPEC_CC_VOLT_RP_3_0: - if (!cc_is_rp(cc_alt) || cc_alt == TYPEC_CC_VOLT_RP_DEF) - charge = 3000; - else if (cc_alt == TYPEC_CC_VOLT_RP_1_5) - charge = 500; - break; - case TYPEC_CC_VOLT_RP_1_5: - charge = 1500; - break; - case TYPEC_CC_VOLT_RP_DEF: - charge = 500; - break; - default: - break; - } - - if (IS_ENABLED(CONFIG_USBC_DISABLE_CHARGE_FROM_RP_DEF) && charge == 500) - charge = 0; - - if (cc_is_rp(cc_alt)) - charge |= TYPEC_CURRENT_DTS_MASK; - - return charge; -} - -enum tcpc_cc_polarity get_snk_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - /* The following assumes: - * - * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5 - * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF - * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN - */ - if (cc_is_src_dbg_acc(cc1, cc2)) - return (cc1 > cc2) ? POLARITY_CC1_DTS : POLARITY_CC2_DTS; - - return (cc1 > cc2) ? POLARITY_CC1 : POLARITY_CC2; -} - -enum tcpc_cc_polarity get_src_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return (cc1 == TYPEC_CC_VOLT_RD) ? POLARITY_CC1 : POLARITY_CC2; -} - -enum pd_cc_states pd_get_cc_state( - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - /* Port partner is a SNK */ - if (cc_is_snk_dbg_acc(cc1, cc2)) - return PD_CC_UFP_DEBUG_ACC; - if (cc_is_at_least_one_rd(cc1, cc2)) - return PD_CC_UFP_ATTACHED; - if (cc_is_audio_acc(cc1, cc2)) - return PD_CC_UFP_AUDIO_ACC; - - /* Port partner is a SRC */ - if (cc_is_rp(cc1) && cc_is_rp(cc2)) - return PD_CC_DFP_DEBUG_ACC; - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - return PD_CC_DFP_ATTACHED; - - /* - * 1) Both lines are Vopen or - * 2) Only an e-marked cabled without a partner on the other side - */ - return PD_CC_NONE; -} - -/** - * This function checks the current CC status of the port partner - * and returns true if the attached partner is debug accessory. - */ -bool pd_is_debug_acc(int port) -{ - enum pd_cc_states cc_state = pd_get_task_cc_state(port); - - return cc_state == PD_CC_UFP_DEBUG_ACC || - cc_state == PD_CC_DFP_DEBUG_ACC; -} - -void pd_set_polarity(int port, enum tcpc_cc_polarity polarity) -{ - tcpm_set_polarity(port, polarity); - - if (IS_ENABLED(CONFIG_USBC_PPC_POLARITY)) - ppc_set_polarity(port, polarity); -} - -__overridable int pd_board_check_request(uint32_t rdo, int pdo_cnt) -{ - return EC_SUCCESS; -} - -int pd_check_requested_voltage(uint32_t rdo, const int port) -{ - int max_ma = rdo & 0x3FF; - int op_ma = (rdo >> 10) & 0x3FF; - int idx = RDO_POS(rdo); - uint32_t pdo; - uint32_t pdo_ma; -#if defined(CONFIG_USB_PD_TCPMV2) && defined(CONFIG_USB_PE_SM) - const uint32_t *src_pdo; - const int pdo_cnt = dpm_get_source_pdo(&src_pdo, port); -#elif defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int pdo_cnt = pd_src_pdo_cnt; -#endif - - /* Check for invalid index */ - if (!idx || idx > pdo_cnt) - return EC_ERROR_INVAL; - - /* Board specific check for this request */ - if (pd_board_check_request(rdo, pdo_cnt)) - return EC_ERROR_INVAL; - - /* check current ... */ - pdo = src_pdo[idx - 1]; - pdo_ma = (pdo & 0x3ff); - - if (op_ma > pdo_ma) - return EC_ERROR_INVAL; /* too much op current */ - - if (max_ma > pdo_ma && !(rdo & RDO_CAP_MISMATCH)) - return EC_ERROR_INVAL; /* too much max current */ - - CPRINTF("Requested %d mV %d mA (for %d/%d mA)\n", - ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, - op_ma * 10, max_ma * 10); - - /* Accept the requested voltage */ - return EC_SUCCESS; -} - -__overridable uint8_t board_get_usb_pd_port_count(void) -{ - return CONFIG_USB_PD_PORT_MAX_COUNT; -} - -__overridable bool board_is_usb_pd_port_present(int port) -{ - /* - * Use board_get_usb_pd_port_count() instead of checking - * CONFIG_USB_PD_PORT_MAX_COUNT directly here for legacy boards - * that implement board_get_usb_pd_port_count() but do not - * implement board_is_usb_pd_port_present(). - */ - - return (port >= 0) && (port < board_get_usb_pd_port_count()); -} - -__overridable bool board_is_dts_port(int port) -{ - return true; -} - -int pd_get_retry_count(int port, enum tcpci_msg_type type) -{ - /* PD 3.0 6.7.7: nRetryCount = 2; PD 2.0 6.6.9: nRetryCount = 3 */ - return pd_get_rev(port, type) == PD_REV30 ? 2 : 3; -} - -enum pd_drp_next_states drp_auto_toggle_next_state( - uint64_t *drp_sink_time, - enum pd_power_role power_role, - enum pd_dual_role_states drp_state, - enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2, - bool auto_toggle_supported) -{ - const bool hardware_debounced_unattached = - ((drp_state == PD_DRP_TOGGLE_ON) && - auto_toggle_supported); - - /* Set to appropriate port state */ - if (cc_is_open(cc1, cc2)) { - /* - * If nothing is attached then use drp_state to determine next - * state. If DRP auto toggle is still on, then remain in the - * DRP_AUTO_TOGGLE state. Otherwise, stop dual role toggling - * and go to a disconnected state. - */ - switch (drp_state) { - case PD_DRP_TOGGLE_OFF: - return DRP_TC_DEFAULT; - case PD_DRP_FREEZE: - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_FORCE_SINK: - return DRP_TC_UNATTACHED_SNK; - case PD_DRP_FORCE_SOURCE: - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_TOGGLE_ON: - default: - if (!auto_toggle_supported) { - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - } - - return DRP_TC_DRP_AUTO_TOGGLE; - } - } else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && - drp_state != PD_DRP_FORCE_SOURCE) { - /* SNK allowed unless ForceSRC */ - if (hardware_debounced_unattached) - return DRP_TC_ATTACHED_WAIT_SNK; - return DRP_TC_UNATTACHED_SNK; - } else if (cc_is_at_least_one_rd(cc1, cc2) || - cc_is_audio_acc(cc1, cc2)) { - /* - * SRC allowed unless ForceSNK or Toggle Off - * - * Ideally we wouldn't use auto-toggle when drp_state is - * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't - * be prevented in low power mode. Try being a sink in case the - * connected device is dual-role (this ensures reliable charging - * from a hub, b/72007056). 100 ms is enough time for a - * dual-role partner to switch from sink to source. If the - * connected device is sink-only, then we will attempt - * TC_UNATTACHED_SNK twice (due to debounce time), then return - * to low power mode (and stay there). After 200 ms, reset - * ready for a new connection. - */ - if (drp_state == PD_DRP_TOGGLE_OFF || - drp_state == PD_DRP_FORCE_SINK) { - if (get_time().val > *drp_sink_time + 200*MSEC) - *drp_sink_time = get_time().val; - if (get_time().val < *drp_sink_time + 100*MSEC) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_DRP_AUTO_TOGGLE; - } else { - if (hardware_debounced_unattached) - return DRP_TC_ATTACHED_WAIT_SRC; - return DRP_TC_UNATTACHED_SRC; - } - } else { - /* Anything else, keep toggling */ - if (!auto_toggle_supported) { - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - } - - return DRP_TC_DRP_AUTO_TOGGLE; - } -} - -__overridable bool usb_ufp_check_usb3_enable(int port) -{ - return false; -} - -mux_state_t get_mux_mode_to_set(int port) -{ - /* - * If the SoC is down, then we disconnect the MUX to save power since - * no one cares about the data lines. - */ - if (IS_ENABLED(CONFIG_POWER_COMMON) && - chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - return USB_PD_MUX_NONE; - - /* - * When PD stack is disconnected, then mux should be disconnected, which - * is also what happens in the set_state disconnection code. Once the - * PD state machine progresses out of disconnect, the MUX state will - * be set correctly again. - */ - if (pd_is_disconnected(port)) - return USB_PD_MUX_NONE; - - /* - * For type-c only connections, there may be a need to enable USB3.1 - * mode when the port is in a UFP data role, independent of any other - * conditions which are checked below. The default function returns - * false, so only boards that override this check will be affected. - */ - if (usb_ufp_check_usb3_enable(port) && pd_get_data_role(port) - == PD_ROLE_UFP) - return USB_PD_MUX_USB_ENABLED; - - /* If new data role isn't DFP & we only support DFP, also disconnect. */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_SS_MUX_DFP_ONLY) && - pd_get_data_role(port) != PD_ROLE_DFP) - return USB_PD_MUX_NONE; - - /* If new data role isn't UFP & we only support UFP then disconnect. */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_SS_MUX_UFP_ONLY) && - pd_get_data_role(port) != PD_ROLE_UFP) - return USB_PD_MUX_NONE; - - /* - * If the power role is sink and the PD partner device is not capable - * of USB communication then disconnect. - * - * On an entry into Unattached.SNK, the partner may be PD capable but - * hasn't yet sent source capabilities. In this case, hold off enabling - * USB3 termination until the PD capability is resolved. - * - * TODO(b/188588458): TCPMv2: Delay enabling USB3 termination when USB4 - * is supported. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - pd_get_power_role(port) == PD_ROLE_SINK && - (pd_capable(port) || pd_waiting_on_partner_src_caps(port)) && - !pd_get_partner_usb_comm_capable(port)) - return USB_PD_MUX_NONE; - - /* Otherwise connect mux since we are in S3+ */ - return USB_PD_MUX_USB_ENABLED; -} - -void set_usb_mux_with_current_data_role(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) { - mux_state_t mux_mode = get_mux_mode_to_set(port); - enum usb_switch usb_switch_mode = - (mux_mode == USB_PD_MUX_NONE) ? - USB_SWITCH_DISCONNECT : USB_SWITCH_CONNECT; - - usb_mux_set(port, mux_mode, usb_switch_mode, - polarity_rm_dts(pd_get_polarity(port))); - } -} - -void usb_mux_set_safe_mode(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) { - usb_mux_set(port, IS_ENABLED(CONFIG_USB_MUX_VIRTUAL) ? - USB_PD_MUX_SAFE_MODE : USB_PD_MUX_NONE, - USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - } - - /* Isolate the SBU lines. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 0); -} - -void usb_mux_set_safe_mode_exit(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - /* Isolate the SBU lines. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 0); -} - -static void pd_send_hard_reset(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET); -} - -#ifdef CONFIG_USBC_OCP - -static uint32_t port_oc_reset_req; - -static void re_enable_ports(void) -{ - uint32_t ports = atomic_clear(&port_oc_reset_req); - - while (ports) { - int port = __fls(ports); - - ports &= ~BIT(port); - - /* - * Let the board know that the overcurrent is - * over since we're going to attempt re-enabling - * the port. - */ - board_overcurrent_event(port, 0); - - pd_send_hard_reset(port); - /* - * TODO(b/117854867): PD3.0 to send an alert message - * indicating OCP after explicit contract. - */ - } -} -DECLARE_DEFERRED(re_enable_ports); - -void pd_handle_overcurrent(int port) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return; - } - - CPRINTS("C%d: overcurrent!", port); - - if (IS_ENABLED(CONFIG_USB_PD_LOGGING)) - pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), - PS_FAULT_OCP, NULL); - - /* No action to take if disconnected, just log. */ - if (pd_is_disconnected(port)) - return; - - /* Keep track of the overcurrent events. */ - usbc_ocp_add_event(port); - - /* Let the board specific code know about the OC event. */ - board_overcurrent_event(port, 1); - - /* Wait 1s before trying to re-enable the port. */ - atomic_or(&port_oc_reset_req, BIT(port)); - hook_call_deferred(&re_enable_ports_data, SECOND); -} - -#endif /* CONFIG_USBC_OCP */ - -__maybe_unused void pd_handle_cc_overvoltage(int port) -{ - pd_send_hard_reset(port); -} - -__overridable int pd_board_checks(void) -{ - return EC_SUCCESS; -} - -__overridable int pd_check_data_swap(int port, - enum pd_data_role data_role) -{ - /* Allow data swap if we are a UFP, otherwise don't allow. */ - return (data_role == PD_ROLE_UFP) ? 1 : 0; -} - -__overridable int pd_check_power_swap(int port) -{ - /* - * Allow power swap if we are acting as a dual role device. If we are - * not acting as dual role (ex. suspended), then only allow power swap - * if we are sourcing when we could be sinking. - */ - if (pd_get_dual_role(port) == PD_DRP_TOGGLE_ON) - return 1; - else if (pd_get_power_role(port) == PD_ROLE_SOURCE) - return 1; - - return 0; -} - -__overridable void pd_execute_data_swap(int port, - enum pd_data_role data_role) -{ -} - -__overridable enum pd_dual_role_states pd_get_drp_state_in_suspend(void) -{ - /* Disable dual role when going to suspend */ - return PD_DRP_TOGGLE_OFF; -} - -__overridable void pd_try_execute_vconn_swap(int port, int flags) -{ - /* - * If partner is dual-role power and vconn swap is enabled, consider - * if vconn swapping is necessary. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_VCONN_SWAP)) - pd_try_vconn_src(port); -} - -__overridable int pd_is_valid_input_voltage(int mv) -{ - return 1; -} - -__overridable void pd_transition_voltage(int idx) -{ - /* Most devices are fixed 5V output. */ -} - -__overridable void typec_set_source_current_limit(int p, enum tcpc_rp_value rp) -{ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_set_vbus_source_current_limit(p, rp); -} - -/* ---------------- Power Data Objects (PDOs) ----------------- */ -#ifndef CONFIG_USB_PD_CUSTOM_PDO -#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\ - PDO_FIXED_COMM_CAP) - -const uint32_t pd_src_pdo[] = { - PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); -const uint32_t pd_src_pdo_max[] = { - PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_max_cnt = ARRAY_SIZE(pd_src_pdo_max); - -const uint32_t pd_snk_pdo[] = { - PDO_FIXED(5000, - GENERIC_MIN((PD_OPERATING_POWER_MW / 5), PD_MAX_CURRENT_MA), - PDO_FIXED_FLAGS), - PDO_BATT(4750, PD_MAX_VOLTAGE_MV, PD_OPERATING_POWER_MW), - PDO_VAR(4750, PD_MAX_VOLTAGE_MV, PD_MAX_CURRENT_MA), -}; -const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); -#endif /* CONFIG_USB_PD_CUSTOM_PDO */ - -/* ----------------- Vendor Defined Messages ------------------ */ -#if defined(CONFIG_USB_PE_SM) && !defined(CONFIG_USB_VPD) && \ - !defined(CONFIG_USB_CTVPD) -__overridable int pd_custom_vdm(int port, int cnt, uint32_t *payload, - uint32_t **rpayload) -{ - int cmd = PD_VDO_CMD(payload[0]); - uint16_t dev_id = 0; - int is_rw, is_latest; - - /* make sure we have some payload */ - if (cnt == 0) - return 0; - - /* Only handle custom requests for SVID Google */ - if (PD_VDO_VID(*payload) != USB_VID_GOOGLE) - return 0; - - switch (cmd) { - case VDO_CMD_VERSION: - /* guarantee last byte of payload is null character */ - *(payload + cnt - 1) = 0; - CPRINTF("version: %s\n", (char *)(payload+1)); - break; - case VDO_CMD_READ_INFO: - case VDO_CMD_SEND_INFO: - /* copy hash */ - if (cnt == 7) { - dev_id = VDO_INFO_HW_DEV_ID(payload[6]); - is_rw = VDO_INFO_IS_RW(payload[6]); - - is_latest = pd_dev_store_rw_hash( - port, dev_id, payload + 1, - is_rw ? EC_IMAGE_RW : EC_IMAGE_RO); - - /* - * Send update host event unless our RW hash is - * already known to be the latest update RW. - */ - if (!is_rw || !is_latest) - pd_send_host_event(PD_EVENT_UPDATE_DEVICE); - - CPRINTF("DevId:%d.%d SW:%d RW:%d\n", - HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id), - VDO_INFO_SW_DBG_VER(payload[6]), - is_rw); - } else if (cnt == 6) { - /* really old devices don't have last byte */ - pd_dev_store_rw_hash(port, dev_id, payload + 1, - EC_IMAGE_UNKNOWN); - } - break; - case VDO_CMD_CURRENT: - CPRINTF("Current: %dmA\n", payload[1]); - break; - case VDO_CMD_FLIP: - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_flip(port); - break; -#ifdef CONFIG_USB_PD_LOGGING - case VDO_CMD_GET_LOG: - pd_log_recv_vdm(port, cnt, payload); - break; -#endif /* CONFIG_USB_PD_LOGGING */ - } - - return 0; -} -#endif /* CONFIG_USB_PE_SM && !CONFIG_USB_VPD && !CONFIG_USB_CTVPD */ - -__overridable bool vboot_allow_usb_pd(void) -{ - return false; -} - -/* VDM utility functions */ -static void pd_usb_billboard_deferred(void) -{ - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE) && - !IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP) && - !IS_ENABLED(CONFIG_USB_PD_SIMPLE_DFP) && - IS_ENABLED(CONFIG_USB_BOS)) { - /* - * TODO(tbroch) - * 1. Will we have multiple type-C port UFPs - * 2. Will there be other modes applicable to DFPs besides DP - */ - if (!pd_alt_mode(0, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT)) - usb_connect(); - } -} -DECLARE_DEFERRED(pd_usb_billboard_deferred); - -#ifdef CONFIG_USB_PD_DISCHARGE -static void gpio_discharge_vbus(int port, int enable) -{ -#ifdef CONFIG_USB_PD_DISCHARGE_GPIO - enum gpio_signal dischg_gpio[] = { - GPIO_USB_C0_DISCHARGE, -#if CONFIG_USB_PD_PORT_MAX_COUNT > 1 - GPIO_USB_C1_DISCHARGE, -#endif -#if CONFIG_USB_PD_PORT_MAX_COUNT > 2 - GPIO_USB_C2_DISCHARGE, -#endif - }; - BUILD_ASSERT(ARRAY_SIZE(dischg_gpio) == CONFIG_USB_PD_PORT_MAX_COUNT); - - gpio_set_level(dischg_gpio[port], enable); -#endif /* CONFIG_USB_PD_DISCHARGE_GPIO */ -} - -void pd_set_vbus_discharge(int port, int enable) -{ - static mutex_t discharge_lock[CONFIG_USB_PD_PORT_MAX_COUNT]; -#ifdef CONFIG_ZEPHYR - static bool inited[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (!inited[port]) { - (void)k_mutex_init(&discharge_lock[port]); - inited[port] = true; - } -#endif - if (port >= board_get_usb_pd_port_count()) - return; - - mutex_lock(&discharge_lock[port]); - enable &= !board_vbus_source_enabled(port); - - if (get_usb_pd_discharge() == USB_PD_DISCHARGE_GPIO) { - gpio_discharge_vbus(port, enable); - } else if (get_usb_pd_discharge() == USB_PD_DISCHARGE_TCPC) { -#ifdef CONFIG_USB_PD_DISCHARGE_PPC - tcpc_discharge_vbus(port, enable); -#endif - } else if (get_usb_pd_discharge() == USB_PD_DISCHARGE_PPC) { -#ifdef CONFIG_USB_PD_DISCHARGE_PPC - ppc_discharge_vbus(port, enable); -#endif - } - - mutex_unlock(&discharge_lock[port]); -} -#endif /* CONFIG_USB_PD_DISCHARGE */ - -#ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; -static void resume_pd_port(void) -{ - uint32_t port; - uint32_t suspended_ports = atomic_clear(&pd_ports_to_resume); - - while (suspended_ports) { - port = __builtin_ctz(suspended_ports); - suspended_ports &= ~BIT(port); - pd_set_suspend(port, 0); - } -} -DECLARE_DEFERRED(resume_pd_port); - -void pd_deferred_resume(int port) -{ - atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, 5 * SECOND); -} -#endif /* CONFIG_USB_PD_TCPM_TCPCI */ - -__overridable int pd_snk_is_vbus_provided(int port) -{ - return EC_SUCCESS; -} - -/* - * Check the specified Vbus level - * - * Note that boards may override this function if they have a method outside the - * TCPCI driver to verify vSafe0V. - */ -__overridable bool pd_check_vbus_level(int port, enum vbus_level level) -{ - if (IS_ENABLED(CONFIG_USB_PD_VBUS_DETECT_TCPC) && - (get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_TCPC)) { - return tcpm_check_vbus_level(port, level); - } - else if (level == VBUS_PRESENT) - return pd_snk_is_vbus_provided(port); - else - return !pd_snk_is_vbus_provided(port); -} - -int pd_is_vbus_present(int port) -{ - return pd_check_vbus_level(port, VBUS_PRESENT); -} - -#ifdef CONFIG_USB_PD_FRS -__overridable int board_pd_set_frs_enable(int port, int enable) -{ - return EC_SUCCESS; -} - -int pd_set_frs_enable(int port, int enable) -{ - int rv = EC_SUCCESS; - - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) - rv = ppc_set_frs_enable(port, enable); - if (rv == EC_SUCCESS && IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) - rv = tcpm_set_frs_enable(port, enable); - if (rv == EC_SUCCESS) - rv = board_pd_set_frs_enable(port, enable); - return rv; -} -#endif /* defined(CONFIG_USB_PD_FRS) */ - -#ifdef CONFIG_CMD_TCPC_DUMP -/* - * Dump TCPC registers. - */ -void tcpc_dump_registers(int port, const struct tcpc_reg_dump_map *reg, - int count) -{ - int i, val; - - for (i = 0; i < count; i++, reg++) { - switch (reg->size) { - case 1: - tcpc_read(port, reg->addr, &val); - ccprintf(" %-30s(0x%02x) = 0x%02x\n", - reg->name, reg->addr, (uint8_t)val); - break; - case 2: - tcpc_read16(port, reg->addr, &val); - ccprintf(" %-30s(0x%02x) = 0x%04x\n", - reg->name, reg->addr, (uint16_t)val); - break; - } - cflush(); - } - -} - -static int command_tcpc_dump(int argc, char **argv) -{ - int port; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - port = atoi(argv[1]); - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - /* Dump TCPC registers. */ - tcpm_dump_registers(port); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(tcpci_dump, command_tcpc_dump, "<Type-C port>", - "dump the TCPC regs"); -#endif /* defined(CONFIG_CMD_TCPC_DUMP) */ - -void pd_srccaps_dump(int port) -{ - int i; - const uint32_t *const srccaps = pd_get_src_caps(port); - - for (i = 0; i < pd_get_src_cap_cnt(port); ++i) { - uint32_t max_ma, max_mv, min_mv; - - pd_extract_pdo_power(srccaps[i], &max_ma, &max_mv, &min_mv); - - if ((srccaps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - ccprintf("%d: %dmV-%dmV/%dmA\n", i, min_mv, - max_mv, max_ma); - } else { - ccprintf("%d: %dmV/%dmA\n", i, max_mv, max_ma); - } - } -} - -int pd_build_alert_msg(uint32_t *msg, uint32_t *len, enum pd_power_role pr) -{ - if (msg == NULL || len == NULL) - return EC_ERROR_INVAL; - - /* - * SOURCE: currently only supports OCP - * SINK: currently only supports OVP - */ - if (pr == PD_ROLE_SOURCE) - *msg = ADO_OCP_EVENT; - else - *msg = ADO_OVP_EVENT; - - /* Alert data is 4 bytes */ - *len = 4; - - return EC_SUCCESS; -} |