summaryrefslogtreecommitdiff
path: root/common/usb_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_common.c')
-rw-r--r--common/usb_common.c1027
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;
-}