summaryrefslogtreecommitdiff
path: root/driver/tcpm/tcpci.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /driver/tcpm/tcpci.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14469.9.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'driver/tcpm/tcpci.c')
-rw-r--r--driver/tcpm/tcpci.c1838
1 files changed, 0 insertions, 1838 deletions
diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c
deleted file mode 100644
index 1e08e0967d..0000000000
--- a/driver/tcpm/tcpci.c
+++ /dev/null
@@ -1,1838 +0,0 @@
-/* Copyright 2015 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.
- */
-
-/* Type-C port manager */
-
-#include "atomic.h"
-#include "anx74xx.h"
-#include "compile_time_macros.h"
-#include "console.h"
-#include "ec_commands.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "ps8xxx.h"
-#include "task.h"
-#include "tcpm/tcpci.h"
-#include "tcpm/tcpm.h"
-#include "timer.h"
-#include "usb_charge.h"
-#include "usb_common.h"
-#include "usb_mux.h"
-#include "usb_pd.h"
-#include "usb_pd_flags.h"
-#include "usb_pd_tcpc.h"
-#include "usb_pd_tcpm.h"
-#include "util.h"
-
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-
-STATIC_IF(CONFIG_USB_PD_DECODE_SOP)
- bool sop_prime_en[CONFIG_USB_PD_PORT_MAX_COUNT];
-STATIC_IF(CONFIG_USB_PD_DECODE_SOP)
- int rx_en[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-#define TCPC_FLAGS_VSAFE0V(_flags) \
- ((_flags & TCPC_FLAGS_TCPCI_REV2_0) && \
- !(_flags & TCPC_FLAGS_TCPCI_REV2_0_NO_VSAFE0V))
-
-/****************************************************************************
- * TCPCI DEBUG Helpers
- */
-
-/* TCPCI FAULT-0x01 is an invalid I2C operation was performed. This tends
- * to have to do with the state of registers and the last write operation.
- * Defining DEBUG_I2C_FAULT_LAST_WRITE_OP will track the write operations,
- * excluding XFER and BlockWrites, in an attempt to give clues as to what
- * was written to the TCPCI that caused the issue.
- */
-#undef DEBUG_I2C_FAULT_LAST_WRITE_OP
-
-struct i2c_wrt_op {
- int addr;
- int reg;
- int val;
- int mask;
-};
-STATIC_IF(DEBUG_I2C_FAULT_LAST_WRITE_OP)
- struct i2c_wrt_op last_write_op[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/*
- * AutoDischargeDisconnect has caused a number of issues with the
- * feature not being correctly enabled/disabled. Defining
- * DEBUG_AUTO_DISCHARGE_DISCONNECT will output a line for each enable
- * and disable to help better understand any AutoDischargeDisconnect
- * issues.
- */
-#undef DEBUG_AUTO_DISCHARGE_DISCONNECT
-
-/*
- * ForcedDischarge debug to help coordinate with AutoDischarge.
- * Defining DEBUG_FORCED_DISCHARGE will output a line for each enable
- * and disable to help better understand any Discharge issues.
- */
-#undef DEBUG_FORCED_DISCHARGE
-
-/*
- * Seeing the CC Status and ROLE Control registers as well as the
- * CC that is being determined from this information can be
- * helpful. Defining DEBUG_GET_CC will output a line that gives
- * this useful information
- */
-#undef DEBUG_GET_CC
-
-struct get_cc_values {
- int cc1;
- int cc2;
- int cc_sts;
- int role;
-};
-STATIC_IF(DEBUG_GET_CC)
- struct get_cc_values last_get_cc[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/*
- * Seeing RoleCtrl updates can help determine why GetCC is not
- * working as it should be.
- */
-#undef DEBUG_ROLE_CTRL_UPDATES
-
-/****************************************************************************/
-
-/*
- * Last reported VBus Level
- *
- * BIT(VBUS_SAFE0V) will indicate if in SAFE0V
- * BIT(VBUS_PRESENT) will indicate if in PRESENT in the TCPCI POWER_STATUS
- *
- * Note that VBUS_REMOVED cannot be distinguished from !VBUS_PRESENT with
- * this interface, but the trigger thresholds for Vbus Present should allow the
- * same bit to be used safely for both.
- *
- * TODO(b/149530538): Some TCPCs may be able to implement
- * VBUS_SINK_DISCONNECT_THRESHOLD to support vSinkDisconnectPD
- */
-static int tcpc_vbus[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/* Cached RP role values */
-static int cached_rp[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/* Cache our Device Capabilities at init for later reference */
-static int dev_cap_1[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-int tcpc_addr_write(int port, int i2c_addr, int reg, int val)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- if (IS_ENABLED(DEBUG_I2C_FAULT_LAST_WRITE_OP)) {
- last_write_op[port].addr = i2c_addr;
- last_write_op[port].reg = reg;
- last_write_op[port].val = val & 0xFF;
- last_write_op[port].mask = 0;
- }
-
- rv = i2c_write8(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, val);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_addr_write16(int port, int i2c_addr, int reg, int val)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- if (IS_ENABLED(DEBUG_I2C_FAULT_LAST_WRITE_OP)) {
- last_write_op[port].addr = i2c_addr;
- last_write_op[port].reg = reg;
- last_write_op[port].val = val & 0xFFFF;
- last_write_op[port].mask = 0;
- }
-
- rv = i2c_write16(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, val);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_addr_read(int port, int i2c_addr, int reg, int *val)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- rv = i2c_read8(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, val);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_addr_read16(int port, int i2c_addr, int reg, int *val)
-{
- pd_wait_exit_low_power(port);
-
- return tcpc_addr_read16_no_lpm_exit(port, i2c_addr, reg, val);
-}
-
-int tcpc_addr_read16_no_lpm_exit(int port, int i2c_addr, int reg, int *val)
-{
- int rv;
-
- rv = i2c_read16(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, val);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_read_block(int port, int reg, uint8_t *in, int size)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- rv = i2c_read_block(tcpc_config[port].i2c_info.port,
- tcpc_config[port].i2c_info.addr_flags,
- reg, in, size);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_write_block(int port, int reg, const uint8_t *out, int size)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- rv = i2c_write_block(tcpc_config[port].i2c_info.port,
- tcpc_config[port].i2c_info.addr_flags,
- reg, out, size);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_xfer(int port, const uint8_t *out, int out_size,
- uint8_t *in, int in_size)
-{
- int rv;
- /* Dispatching to tcpc_xfer_unlocked reduces code size growth. */
- tcpc_lock(port, 1);
- rv = tcpc_xfer_unlocked(port, out, out_size, in, in_size,
- I2C_XFER_SINGLE);
- tcpc_lock(port, 0);
- return rv;
-}
-
-int tcpc_xfer_unlocked(int port, const uint8_t *out, int out_size,
- uint8_t *in, int in_size, int flags)
-{
- int rv;
-
- pd_wait_exit_low_power(port);
-
- rv = i2c_xfer_unlocked(tcpc_config[port].i2c_info.port,
- tcpc_config[port].i2c_info.addr_flags,
- out, out_size, in, in_size, flags);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_update8(int port, int reg,
- uint8_t mask,
- enum mask_update_action action)
-{
- int rv;
- const int i2c_addr = tcpc_config[port].i2c_info.addr_flags;
-
- pd_wait_exit_low_power(port);
-
- if (IS_ENABLED(DEBUG_I2C_FAULT_LAST_WRITE_OP)) {
- last_write_op[port].addr = i2c_addr;
- last_write_op[port].reg = reg;
- last_write_op[port].val = 0;
- last_write_op[port].mask = (mask & 0xFF) | (action << 16);
- }
-
- rv = i2c_update8(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, mask, action);
-
- pd_device_accessed(port);
- return rv;
-}
-
-int tcpc_update16(int port, int reg,
- uint16_t mask,
- enum mask_update_action action)
-{
- int rv;
- const int i2c_addr = tcpc_config[port].i2c_info.addr_flags;
-
- pd_wait_exit_low_power(port);
-
- if (IS_ENABLED(DEBUG_I2C_FAULT_LAST_WRITE_OP)) {
- last_write_op[port].addr = i2c_addr;
- last_write_op[port].reg = reg;
- last_write_op[port].val = 0;
- last_write_op[port].mask = (mask & 0xFFFF) | (action << 16);
- }
-
- rv = i2c_update16(tcpc_config[port].i2c_info.port,
- i2c_addr, reg, mask, action);
-
- pd_device_accessed(port);
- return rv;
-}
-
-#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
-
-/*
- * TCPCI maintains and uses cached values for the RP and
- * last used PULL values. Since TCPC drivers are allowed
- * to use some of the TCPCI functionality, these global
- * cached values need to be maintained in case part of the
- * used TCPCI functionality relies on these values
- */
-void tcpci_set_cached_rp(int port, int rp)
-{
- cached_rp[port] = rp;
-}
-
-int tcpci_get_cached_rp(int port)
-{
- return cached_rp[port];
-}
-
-static int init_alert_mask(int port)
-{
- int rv;
- uint16_t mask;
-
- /*
- * Create mask of alert events that will cause the TCPC to
- * signal the TCPM via the Alert# gpio line.
- */
- if (get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_TCPC) {
- mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED |
- TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS |
- TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS |
- TCPC_REG_ALERT_FAULT
- | TCPC_REG_ALERT_POWER_STATUS
- ;
- } else {
- mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED |
- TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS |
- TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS |
- TCPC_REG_ALERT_FAULT
- ;
- }
-
- /* TCPCI Rev2 includes SAFE0V alerts */
- if (TCPC_FLAGS_VSAFE0V(tcpc_config[port].flags))
- mask |= TCPC_REG_ALERT_EXT_STATUS;
-
- if (IS_ENABLED(CONFIG_USB_PD_FRS_TCPC))
- mask |= TCPC_REG_ALERT_ALERT_EXT;
-
- /* Set the alert mask in TCPC */
- rv = tcpc_write16(port, TCPC_REG_ALERT_MASK, mask);
-
- if (IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) {
- if (rv)
- return rv;
-
- /* Sink FRS allowed */
- mask = TCPC_REG_ALERT_EXT_SNK_FRS;
- rv = tcpc_write(port, TCPC_REG_ALERT_EXTENDED_MASK, mask);
- }
- return rv;
-}
-
-static int clear_alert_mask(int port)
-{
- return tcpc_write16(port, TCPC_REG_ALERT_MASK, 0);
-}
-
-static int init_power_status_mask(int port)
-{
- uint8_t mask;
- int rv;
-
- if (get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_TCPC)
- mask = TCPC_REG_POWER_STATUS_VBUS_PRES;
- else
- mask = 0;
-
- rv = tcpc_write(port, TCPC_REG_POWER_STATUS_MASK , mask);
-
- return rv;
-}
-
-static int clear_power_status_mask(int port)
-{
- return tcpc_write(port, TCPC_REG_POWER_STATUS_MASK, 0);
-}
-
-static int tcpci_tcpm_get_power_status(int port, int *status)
-{
- return tcpc_read(port, TCPC_REG_POWER_STATUS, status);
-}
-
-int tcpci_tcpm_select_rp_value(int port, int rp)
-{
- /* Keep track of current RP value */
- tcpci_set_cached_rp(port, rp);
-
- return EC_SUCCESS;
-}
-
-void tcpci_tcpc_discharge_vbus(int port, int enable)
-{
- if (IS_ENABLED(DEBUG_FORCED_DISCHARGE))
- CPRINTS("C%d: ForceDischarge %sABLED",
- port, enable ? "EN" : "DIS");
-
- tcpc_update8(port,
- TCPC_REG_POWER_CTRL,
- TCPC_REG_POWER_CTRL_FORCE_DISCHARGE,
- (enable) ? MASK_SET : MASK_CLR);
-}
-
-/*
- * Auto Discharge Disconnect is supposed to be enabled when we
- * are connected and disabled after we are disconnected and
- * VBus is at SafeV0
- */
-void tcpci_tcpc_enable_auto_discharge_disconnect(int port, int enable)
-{
- if (IS_ENABLED(DEBUG_AUTO_DISCHARGE_DISCONNECT))
- CPRINTS("C%d: AutoDischargeDisconnect %sABLED",
- port, enable ? "EN" : "DIS");
-
- tcpc_update8(port,
- TCPC_REG_POWER_CTRL,
- TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT,
- (enable) ? MASK_SET : MASK_CLR);
-}
-
-int tcpci_tcpc_debug_accessory(int port, bool enable)
-{
- return tcpc_update8(port, TCPC_REG_CONFIG_STD_OUTPUT,
- TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N,
- enable ? MASK_CLR : MASK_SET);
-}
-
-int tcpci_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1,
- enum tcpc_cc_voltage_status *cc2)
-{
- int role;
- int status;
- int cc1_present_rd, cc2_present_rd;
- int rv;
-
- /* errors will return CC as open */
- *cc1 = TYPEC_CC_VOLT_OPEN;
- *cc2 = TYPEC_CC_VOLT_OPEN;
-
- /* Get the ROLE CONTROL and CC STATUS values */
- rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, &role);
- if (rv)
- return rv;
-
- rv = tcpc_read(port, TCPC_REG_CC_STATUS, &status);
- if (rv)
- return rv;
-
- /* Get the current CC values from the CC STATUS */
- *cc1 = TCPC_REG_CC_STATUS_CC1(status);
- *cc2 = TCPC_REG_CC_STATUS_CC2(status);
-
- /* Determine if we are presenting Rd */
- cc1_present_rd = 0;
- cc2_present_rd = 0;
- if (role & TCPC_REG_ROLE_CTRL_DRP_MASK) {
- /*
- * We are doing DRP. We will use the CC STATUS
- * ConnectResult to determine if we are presenting
- * Rd or Rp.
- */
- int term;
-
- term = TCPC_REG_CC_STATUS_TERM(status);
-
- if (*cc1 != TYPEC_CC_VOLT_OPEN)
- cc1_present_rd = term;
- if (*cc2 != TYPEC_CC_VOLT_OPEN)
- cc2_present_rd = term;
- } else {
- /*
- * We are not doing DRP. We will use the ROLE CONTROL
- * CC values to determine if we are presenting Rd or Rp.
- */
- int role_cc1, role_cc2;
-
- role_cc1 = TCPC_REG_ROLE_CTRL_CC1(role);
- role_cc2 = TCPC_REG_ROLE_CTRL_CC2(role);
-
- if (*cc1 != TYPEC_CC_VOLT_OPEN)
- cc1_present_rd = !!(role_cc1 == TYPEC_CC_RD);
- if (*cc2 != TYPEC_CC_VOLT_OPEN)
- cc2_present_rd = !!(role_cc2 == TYPEC_CC_RD);
- }
- *cc1 |= cc1_present_rd << 2;
- *cc2 |= cc2_present_rd << 2;
-
- if (IS_ENABLED(DEBUG_GET_CC) &&
- (last_get_cc[port].cc1 != *cc1 ||
- last_get_cc[port].cc2 != *cc2 ||
- last_get_cc[port].cc_sts != status ||
- last_get_cc[port].role != role)) {
-
- CPRINTS("C%d: GET_CC cc1=%d cc2=%d cc_sts=0x%X role=0x%X",
- port, *cc1, *cc2, status, role);
-
- last_get_cc[port].cc1 = *cc1;
- last_get_cc[port].cc2 = *cc2;
- last_get_cc[port].cc_sts = status;
- last_get_cc[port].role = role;
- }
- return rv;
-}
-
-int tcpci_tcpm_set_cc(int port, int pull)
-{
- int role = TCPC_REG_ROLE_CTRL_SET(TYPEC_NO_DRP,
- tcpci_get_cached_rp(port),
- pull, pull);
-
- if (IS_ENABLED(DEBUG_ROLE_CTRL_UPDATES))
- CPRINTS("C%d: SET_CC pull=%d role=0x%X", port, pull, role);
-
- return tcpc_write(port, TCPC_REG_ROLE_CTRL, role);
-}
-
-#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
-int tcpci_set_role_ctrl(int port, enum tcpc_drp drp, enum tcpc_rp_value rp,
- enum tcpc_cc_pull pull)
-{
- int role = TCPC_REG_ROLE_CTRL_SET(drp, rp, pull, pull);
-
- if (IS_ENABLED(DEBUG_ROLE_CTRL_UPDATES))
- CPRINTS("C%d: SET_ROLE_CTRL drp=%d rp=%d pull=%d role=0x%X",
- port, drp, rp, pull, role);
-
- return tcpc_write(port, TCPC_REG_ROLE_CTRL, role);
-}
-
-int tcpci_tcpc_drp_toggle(int port)
-{
- int rv;
- enum tcpc_cc_pull pull;
-
- /*
- * Set auto drp toggle
- *
- * Set RC.DRP=1b (DRP)
- * Set RC.RpValue=00b (smallest Rp to save power)
- * Set RC.CC1=(Rp) or (Rd)
- * Set RC.CC2=(Rp) or (Rd)
- *
- * TCPCI r1 wants both lines to be set to Rd
- * TCPCI r2 wants both lines to be set to Rp
- *
- * Set the Rp Value to be the minimal to save power
- */
- pull = (tcpc_config[port].flags & TCPC_FLAGS_TCPCI_REV2_0)
- ? TYPEC_CC_RP : TYPEC_CC_RD;
-
- rv = tcpci_set_role_ctrl(port, TYPEC_DRP, TYPEC_RP_USB, pull);
- if (rv)
- return rv;
-
- /* Set up to catch LOOK4CONNECTION alerts */
- rv = tcpc_update8(port,
- TCPC_REG_TCPC_CTRL,
- TCPC_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT,
- MASK_SET);
- if (rv)
- return rv;
-
- /* Set Look4Connection command */
- rv = tcpc_write(port, TCPC_REG_COMMAND,
- TCPC_REG_COMMAND_LOOK4CONNECTION);
-
- return rv;
-}
-#endif
-
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-int tcpci_enter_low_power_mode(int port)
-{
- return tcpc_write(port, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE);
-}
-#endif
-
-int tcpci_tcpm_set_polarity(int port, enum tcpc_cc_polarity polarity)
-{
- return tcpc_update8(port,
- TCPC_REG_TCPC_CTRL,
- TCPC_REG_TCPC_CTRL_SET(1),
- polarity_rm_dts(polarity)
- ? MASK_SET : MASK_CLR);
-}
-
-#ifdef CONFIG_USB_PD_PPC
-bool tcpci_tcpm_get_snk_ctrl(int port)
-{
- int rv;
- int pwr_sts;
-
- rv = tcpci_tcpm_get_power_status(port, &pwr_sts);
-
- return rv == EC_SUCCESS &&
- pwr_sts & TCPC_REG_POWER_STATUS_SINKING_VBUS;
-}
-
-int tcpci_tcpm_set_snk_ctrl(int port, int enable)
-{
- int cmd = enable ? TCPC_REG_COMMAND_SNK_CTRL_HIGH :
- TCPC_REG_COMMAND_SNK_CTRL_LOW;
-
- return tcpc_write(port, TCPC_REG_COMMAND, cmd);
-}
-
-bool tcpci_tcpm_get_src_ctrl(int port)
-{
- int rv;
- int pwr_sts;
-
- rv = tcpci_tcpm_get_power_status(port, &pwr_sts);
-
- return rv == EC_SUCCESS &&
- pwr_sts & TCPC_REG_POWER_STATUS_SOURCING_VBUS;
-}
-
-int tcpci_tcpm_set_src_ctrl(int port, int enable)
-{
- int cmd = enable ? TCPC_REG_COMMAND_SRC_CTRL_HIGH :
- TCPC_REG_COMMAND_SRC_CTRL_LOW;
-
- return tcpc_write(port, TCPC_REG_COMMAND, cmd);
-}
-#endif
-
-__maybe_unused int tcpci_tcpm_sop_prime_enable(int port, bool enable)
-{
- /* save SOP'/SOP'' enable state */
- sop_prime_en[port] = enable;
-
- if (rx_en[port]) {
- int detect_sop_en = TCPC_REG_RX_DETECT_SOP_HRST_MASK;
-
- if (enable) {
- detect_sop_en =
- TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK;
- }
-
- return tcpc_write(port, TCPC_REG_RX_DETECT, detect_sop_en);
- }
-
- return EC_SUCCESS;
-}
-
-int tcpci_tcpm_set_vconn(int port, int enable)
-{
- int reg, rv;
-
- rv = tcpc_read(port, TCPC_REG_POWER_CTRL, &reg);
- if (rv)
- return rv;
-
- reg &= ~TCPC_REG_POWER_CTRL_VCONN(1);
- reg |= TCPC_REG_POWER_CTRL_VCONN(enable);
-
- /*
- * Add delay of writing TCPC_REG_POWER_CTRL makes
- * CC status being judged correctly when disable VCONN.
- * This may be a PS8XXX firmware issue, Parade is still trying.
- * https://partnerissuetracker.corp.google.com/issues/185202064
- */
- if (!enable)
- msleep(PS8XXX_VCONN_TURN_OFF_DELAY_US);
-
- return tcpc_write(port, TCPC_REG_POWER_CTRL, reg);
-}
-
-int tcpci_tcpm_set_msg_header(int port, int power_role, int data_role)
-{
- return tcpc_write(port, TCPC_REG_MSG_HDR_INFO,
- TCPC_REG_MSG_HDR_INFO_SET(data_role, power_role));
-}
-
-static int tcpm_alert_status(int port, int *alert)
-{
- /* Read TCPC Alert register */
- return tcpc_read16(port, TCPC_REG_ALERT, alert);
-}
-
-static int tcpm_alert_ext_status(int port, int *alert_ext)
-{
- /* Read TCPC Extended Alert register */
- return tcpc_read(port, TCPC_REG_ALERT_EXT, alert_ext);
-}
-
-static int tcpm_ext_status(int port, int *ext_status)
-{
- /* Read TCPC Extended Status register */
- return tcpc_read(port, TCPC_REG_EXT_STATUS, ext_status);
-}
-
-int tcpci_tcpm_set_rx_enable(int port, int enable)
-{
- int detect_sop_en = 0;
-
- if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) {
- /* save rx_on */
- rx_en[port] = enable;
- }
-
-
- if (enable) {
- detect_sop_en = TCPC_REG_RX_DETECT_SOP_HRST_MASK;
-
- if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP) &&
- sop_prime_en[port]) {
- /*
- * Only the VCONN Source is allowed to communicate
- * with the Cable Plugs.
- */
- detect_sop_en =
- TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK;
- }
- }
-
- /* If enable, then set RX detect for SOP and HRST */
- return tcpc_write(port, TCPC_REG_RX_DETECT, detect_sop_en);
-}
-
-#ifdef CONFIG_USB_PD_FRS_TCPC
-int tcpci_tcpc_fast_role_swap_enable(int port, int enable)
-{
- return tcpc_update8(port,
- TCPC_REG_POWER_CTRL,
- TCPC_REG_POWER_CTRL_FRS_ENABLE,
- (enable) ? MASK_SET : MASK_CLR);
-}
-#endif
-
-#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
-bool tcpci_tcpm_check_vbus_level(int port, enum vbus_level level)
-{
- if (level == VBUS_SAFE0V)
- return !!(tcpc_vbus[port] & BIT(VBUS_SAFE0V));
- else if (level == VBUS_PRESENT)
- return !!(tcpc_vbus[port] & BIT(VBUS_PRESENT));
- else
- return !(tcpc_vbus[port] & BIT(VBUS_PRESENT));
-}
-#endif
-
-struct cached_tcpm_message {
- uint32_t header;
- uint32_t payload[7];
-};
-
-static int tcpci_rev2_0_tcpm_get_message_raw(int port, uint32_t *payload,
- int *head)
-{
- int rv = 0, cnt, reg = TCPC_REG_RX_BUFFER;
- int frm;
- uint8_t tmp[2];
- /*
- * Register 0x30 is Readable Byte Count, Buffer frame type, and RX buf
- * byte X.
- */
- tcpc_lock(port, 1);
- rv = tcpc_xfer_unlocked(port, (uint8_t *)&reg, 1, tmp, 2,
- I2C_XFER_START);
- if (rv) {
- rv = EC_ERROR_UNKNOWN;
- goto clear;
- }
- cnt = tmp[0];
- frm = tmp[1];
-
- /*
- * READABLE_BYTE_COUNT includes 3 bytes for frame type and header, and
- * may be 0 if the TCPC saw a disconnect before the message read
- */
- cnt -= 3;
- if ((cnt < 0) ||
- (cnt > member_size(struct cached_tcpm_message, payload))) {
- /* Continue to send the stop bit with the header read */
- rv = EC_ERROR_UNKNOWN;
- cnt = 0;
- }
-
- /* The next two bytes are the header */
- rv |= tcpc_xfer_unlocked(port, NULL, 0, (uint8_t *)head, 2,
- cnt ? 0 : I2C_XFER_STOP);
-
- /* Encode message address in bits 31 to 28 */
- *head &= 0x0000ffff;
- *head |= PD_HEADER_SOP(frm);
-
- /* Execute read and I2C_XFER_STOP, even if header read failed */
- if (cnt > 0) {
- tcpc_xfer_unlocked(port, NULL, 0, (uint8_t *)payload, cnt,
- I2C_XFER_STOP);
- }
-
-clear:
- tcpc_lock(port, 0);
- /* Read complete, clear RX status alert bit */
- tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_STATUS);
-
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- return EC_SUCCESS;
-}
-
-static int tcpci_rev1_0_tcpm_get_message_raw(int port, uint32_t *payload,
- int *head)
-{
- int rv, cnt, reg = TCPC_REG_RX_DATA;
- int frm;
-
- rv = tcpc_read(port, TCPC_REG_RX_BYTE_CNT, &cnt);
-
- /* RX_BYTE_CNT includes 3 bytes for frame type and header */
- if (rv != EC_SUCCESS || cnt < 3) {
- rv = EC_ERROR_UNKNOWN;
- goto clear;
- }
- cnt -= 3;
- if (cnt > member_size(struct cached_tcpm_message, payload)) {
- rv = EC_ERROR_UNKNOWN;
- goto clear;
- }
-
- if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) {
- rv = tcpc_read(port, TCPC_REG_RX_BUF_FRAME_TYPE, &frm);
- if (rv != EC_SUCCESS) {
- rv = EC_ERROR_UNKNOWN;
- goto clear;
- }
- }
-
- rv = tcpc_read16(port, TCPC_REG_RX_HDR, (int *)head);
-
- if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) {
- /* Encode message address in bits 31 to 28 */
- *head &= 0x0000ffff;
- *head |= PD_HEADER_SOP(frm);
- }
-
- if (rv == EC_SUCCESS && cnt > 0) {
- tcpc_read_block(port, reg, (uint8_t *)payload, cnt);
- }
-
-clear:
- /* Read complete, clear RX status alert bit */
- tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_STATUS);
-
- return rv;
-}
-
-int tcpci_tcpm_get_message_raw(int port, uint32_t *payload, int *head)
-{
- if (tcpc_config[port].flags & TCPC_FLAGS_TCPCI_REV2_0)
- return tcpci_rev2_0_tcpm_get_message_raw(port, payload, head);
-
- return tcpci_rev1_0_tcpm_get_message_raw(port, payload, head);
-}
-
-/* Cache depth needs to be power of 2 */
-/* TODO: Keep track of the high water mark */
-#define CACHE_DEPTH BIT(3)
-#define CACHE_DEPTH_MASK (CACHE_DEPTH - 1)
-
-struct queue {
- /*
- * Head points to the index of the first empty slot to put a new RX
- * message. Must be masked before used in lookup.
- */
- uint32_t head;
- /*
- * Tail points to the index of the first message for the PD task to
- * consume. Must be masked before used in lookup.
- */
- uint32_t tail;
- struct cached_tcpm_message buffer[CACHE_DEPTH];
-};
-static struct queue cached_messages[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/* Note this method can be called from an interrupt context. */
-int tcpm_enqueue_message(const int port)
-{
- int rv;
- struct queue *const q = &cached_messages[port];
- struct cached_tcpm_message *const head =
- &q->buffer[q->head & CACHE_DEPTH_MASK];
-
- if (q->head - q->tail == CACHE_DEPTH) {
- CPRINTS("C%d RX EC Buffer full!", port);
- return EC_ERROR_OVERFLOW;
- }
-
- /* Blank any old message, just in case. */
- memset(head, 0, sizeof(*head));
- /* Call the raw driver without caching */
- rv = tcpc_config[port].drv->get_message_raw(port, head->payload,
- &head->header);
- if (rv) {
- CPRINTS("C%d: Could not retrieve RX message (%d)", port, rv);
- return rv;
- }
-
- /* Increment atomically to ensure get_message_raw happens-before */
- atomic_add(&q->head, 1);
-
- /* Wake PD task up so it can process incoming RX messages */
- task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE);
-
- return EC_SUCCESS;
-}
-
-int tcpm_has_pending_message(const int port)
-{
- const struct queue *const q = &cached_messages[port];
-
- return q->head != q->tail;
-}
-
-int tcpm_dequeue_message(const int port, uint32_t *const payload,
- int *const header)
-{
- struct queue *const q = &cached_messages[port];
- struct cached_tcpm_message *const tail =
- &q->buffer[q->tail & CACHE_DEPTH_MASK];
-
- if (!tcpm_has_pending_message(port)) {
- CPRINTS("C%d No message in RX buffer!", port);
- return EC_ERROR_BUSY;
- }
-
- /* Copy cache data in to parameters */
- *header = tail->header;
- memcpy(payload, tail->payload, sizeof(tail->payload));
-
- /* Increment atomically to ensure memcpy happens-before */
- atomic_add(&q->tail, 1);
-
- return EC_SUCCESS;
-}
-
-void tcpm_clear_pending_messages(int port)
-{
- struct queue *const q = &cached_messages[port];
-
- q->tail = q->head;
-}
-
-int tcpci_tcpm_transmit(int port, enum tcpci_msg_type type,
- uint16_t header, const uint32_t *data)
-{
- int reg = TCPC_REG_TX_DATA;
- int rv, cnt = 4*PD_HEADER_CNT(header);
-
- /* If not SOP* transmission, just write to the transmit register */
- if (type >= NUM_SOP_STAR_TYPES) {
- /*
- * Per TCPCI spec, do not specify retry (although the TCPC
- * should ignore retry field for these 3 types).
- */
- return tcpc_write(port, TCPC_REG_TRANSMIT,
- TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type));
- }
-
- if (tcpc_config[port].flags & TCPC_FLAGS_TCPCI_REV2_0) {
- /*
- * In TCPCI Rev 2.0, TX_BYTE_CNT and TX_BUF_BYTE_X are the same
- * register.
- */
- reg = TCPC_REG_TX_BUFFER;
- /* TX_BYTE_CNT includes extra bytes for message header */
- cnt += sizeof(header);
- tcpc_lock(port, 1);
- rv = tcpc_xfer_unlocked(port, (uint8_t *)&reg, 1, NULL, 0,
- I2C_XFER_START);
- rv |= tcpc_xfer_unlocked(port, (uint8_t *)&cnt, 1, NULL, 0, 0);
- if (cnt > sizeof(header)) {
- rv |= tcpc_xfer_unlocked(port, (uint8_t *)&header,
- sizeof(header), NULL, 0, 0);
- rv |= tcpc_xfer_unlocked(port, (uint8_t *)data,
- cnt-sizeof(header), NULL, 0,
- I2C_XFER_STOP);
- } else {
- rv |= tcpc_xfer_unlocked(port, (uint8_t *)&header,
- sizeof(header), NULL, 0, I2C_XFER_STOP);
- }
- tcpc_lock(port, 0);
-
- /* If tcpc write fails, return error */
- if (rv)
- return rv;
- } else {
- /* TX_BYTE_CNT includes extra bytes for message header */
- rv = tcpc_write(port, TCPC_REG_TX_BYTE_CNT,
- cnt + sizeof(header));
-
- rv |= tcpc_write16(port, TCPC_REG_TX_HDR, header);
-
- /* If tcpc write fails, return error */
- if (rv)
- return rv;
-
- if (cnt > 0) {
- rv = tcpc_write_block(port, reg, (const uint8_t *)data,
- cnt);
-
- /* If tcpc write fails, return error */
- if (rv)
- return rv;
- }
- }
-
- /*
- * We always retry in TCPC hardware since the TCPM is too slow to
- * respond within tRetry (~195 usec).
- *
- * The retry count used is dependent on the maximum PD revision
- * supported at build time.
- */
- return tcpc_write(port, TCPC_REG_TRANSMIT,
- TCPC_REG_TRANSMIT_SET_WITH_RETRY(
- pd_get_retry_count(port, type), type));
-}
-
-/*
- * Returns true if TCPC has reset based on reading mask registers.
- */
-static int register_mask_reset(int port)
-{
- int mask;
-
- mask = 0;
- tcpc_read16(port, TCPC_REG_ALERT_MASK, &mask);
- if (mask == TCPC_REG_ALERT_MASK_ALL)
- return 1;
-
- mask = 0;
- tcpc_read(port, TCPC_REG_POWER_STATUS_MASK, &mask);
- if (mask == TCPC_REG_POWER_STATUS_MASK_ALL)
- return 1;
-
- return 0;
-}
-
-static int tcpci_get_fault(int port, int *fault)
-{
- return tcpc_read(port, TCPC_REG_FAULT_STATUS, fault);
-}
-
-static int tcpci_handle_fault(int port, int fault)
-{
- int rv = EC_SUCCESS;
-
- CPRINTS("C%d FAULT 0x%02X detected", port, fault);
-
- if (IS_ENABLED(DEBUG_I2C_FAULT_LAST_WRITE_OP) &&
- fault & TCPC_REG_FAULT_STATUS_I2C_INTERFACE_ERR) {
- if (last_write_op[port].mask == 0)
- CPRINTS("C%d I2C WR 0x%02X 0x%02X value=0x%X",
- port,
- last_write_op[port].addr,
- last_write_op[port].reg,
- last_write_op[port].val);
- else
- CPRINTS("C%d I2C UP 0x%02X 0x%02X op=%d mask=0x%X",
- port,
- last_write_op[port].addr,
- last_write_op[port].reg,
- last_write_op[port].mask >> 16,
- last_write_op[port].mask & 0xFFFF);
- }
-
- /* Report overcurrent to the OCP module if enabled */
- if ((dev_cap_1[port] & TCPC_REG_DEV_CAP_1_VBUS_OCP_REPORTING) &&
- IS_ENABLED(CONFIG_USBC_OCP) &&
- (fault & TCPC_REG_FAULT_STATUS_VBUS_OVER_CURRENT))
- pd_handle_overcurrent(port);
-
- if (tcpc_config[port].drv->handle_fault)
- rv = tcpc_config[port].drv->handle_fault(port, fault);
-
- return rv;
-}
-
-enum ec_error_list tcpci_set_bist_test_mode(const int port,
- const bool enable)
-{
- int rv;
-
- rv = tcpc_update8(port, TCPC_REG_TCPC_CTRL,
- TCPC_REG_TCPC_CTRL_BIST_TEST_MODE,
- enable ? MASK_SET : MASK_CLR);
- rv |= tcpc_update16(port, TCPC_REG_ALERT_MASK,
- TCPC_REG_ALERT_RX_STATUS, enable ? MASK_CLR : MASK_SET);
- return rv;
-}
-
-static int tcpci_clear_fault(int port, int fault)
-{
- int rv;
-
- rv = tcpc_write(port, TCPC_REG_FAULT_STATUS, fault);
- if (rv)
- return rv;
-
- return tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_FAULT);
-}
-
-static void tcpci_check_vbus_changed(int port, int alert, uint32_t *pd_event)
-{
- /*
- * Check for VBus change
- */
- /* TCPCI Rev2 includes Safe0V detection */
- if (TCPC_FLAGS_VSAFE0V(tcpc_config[port].flags) &&
- (alert & TCPC_REG_ALERT_EXT_STATUS)) {
- int ext_status = 0;
-
- /* Determine if Safe0V was detected */
- tcpm_ext_status(port, &ext_status);
- if (ext_status & TCPC_REG_EXT_STATUS_SAFE0V)
- /* Safe0V=1 and Present=0 */
- tcpc_vbus[port] = BIT(VBUS_SAFE0V);
- }
-
- if (alert & TCPC_REG_ALERT_POWER_STATUS) {
- int pwr_status = 0;
-
- /* Determine reason for power status change */
- tcpci_tcpm_get_power_status(port, &pwr_status);
- if (pwr_status & TCPC_REG_POWER_STATUS_VBUS_PRES)
- /* Safe0V=0 and Present=1 */
- tcpc_vbus[port] = BIT(VBUS_PRESENT);
- else if (TCPC_FLAGS_VSAFE0V(tcpc_config[port].flags))
- /* TCPCI Rev2 detects Safe0V, so Present=0 */
- tcpc_vbus[port] &= ~BIT(VBUS_PRESENT);
- else {
- /*
- * TCPCI Rev1 can not detect Safe0V, so treat this
- * like a Safe0V detection.
- *
- * Safe0V=1 and Present=0
- */
- tcpc_vbus[port] = BIT(VBUS_SAFE0V);
- }
-
- if ((get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_TCPC) &&
- IS_ENABLED(CONFIG_USB_CHARGER)) {
- /* Update charge manager with new VBUS state */
- usb_charger_vbus_change(port,
- !!(tcpc_vbus[port] & BIT(VBUS_PRESENT)));
-
- if (pd_event)
- *pd_event |= TASK_EVENT_WAKE;
- }
- }
-}
-
-/*
- * Don't let the TCPC try to pull from the RX buffer forever. We typical only
- * have 1 or 2 messages waiting.
- */
-#define MAX_ALLOW_FAILED_RX_READS 10
-
-void tcpci_tcpc_alert(int port)
-{
- int alert = 0;
- int alert_ext = 0;
- int failed_attempts;
- uint32_t pd_event = 0;
- int retval = 0;
-
- /* Read the Alert register from the TCPC */
- if (tcpm_alert_status(port, &alert)) {
- CPRINTS("C%d: Failed to read alert register", port);
- return;
- }
-
- /* Get Extended Alert register if needed */
- if (alert & TCPC_REG_ALERT_ALERT_EXT)
- tcpm_alert_ext_status(port, &alert_ext);
-
- /* Clear any pending faults */
- if (alert & TCPC_REG_ALERT_FAULT) {
- int fault;
-
- if (tcpci_get_fault(port, &fault) == EC_SUCCESS &&
- fault != 0 &&
- tcpci_handle_fault(port, fault) == EC_SUCCESS &&
- tcpci_clear_fault(port, fault) == EC_SUCCESS)
- CPRINTS("C%d FAULT 0x%02X handled", port, fault);
- }
-
- /*
- * Check for TX complete first b/c PD state machine waits on TX
- * completion events. This will send an event to the PD tasks
- * immediately
- */
- if (alert & TCPC_REG_ALERT_TX_COMPLETE)
- pd_transmit_complete(port, alert & TCPC_REG_ALERT_TX_SUCCESS ?
- TCPC_TX_COMPLETE_SUCCESS :
- TCPC_TX_COMPLETE_FAILED);
-
- /* Pull all RX messages from TCPC into EC memory */
- failed_attempts = 0;
- while (alert & TCPC_REG_ALERT_RX_STATUS) {
- retval = tcpm_enqueue_message(port);
- if (retval)
- ++failed_attempts;
- if (tcpm_alert_status(port, &alert))
- ++failed_attempts;
-
-
- /*
- * EC RX FIFO is full. Deassert ALERT# line to exit interrupt
- * handler by discarding pending message from TCPC RX FIFO.
- */
- if (retval == EC_ERROR_OVERFLOW) {
- CPRINTS("C%d: PD RX OVF!", port);
- tcpc_write16(port, TCPC_REG_ALERT,
- TCPC_REG_ALERT_RX_STATUS |
- TCPC_REG_ALERT_RX_BUF_OVF);
- }
-
- /* Ensure we don't loop endlessly */
- if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) {
- CPRINTS("C%d Cannot consume RX buffer after %d failed attempts!",
- port, failed_attempts);
- /*
- * The port is in a bad state, we don't want to consume
- * all EC resources so suspend the port for a little
- * while.
- */
- pd_set_suspend(port, 1);
- pd_deferred_resume(port);
- return;
- }
- }
-
- /*
- * Clear all pending alert bits. Ext first because ALERT.AlertExtended
- * is set if any bit of ALERT_EXTENDED is set.
- */
- if (alert_ext)
- tcpc_write(port, TCPC_REG_ALERT_EXT, alert_ext);
- if (alert)
- tcpc_write16(port, TCPC_REG_ALERT, alert);
-
- if (alert & TCPC_REG_ALERT_CC_STATUS) {
- if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) {
- enum tcpc_cc_voltage_status cc1;
- enum tcpc_cc_voltage_status cc2;
-
- /*
- * Some TCPCs generate CC Alerts when
- * drp auto toggle is active and nothing
- * is connected to the port. So, get the
- * CC line status and only generate a
- * PD_EVENT_CC if something is connected.
- */
- tcpci_tcpm_get_cc(port, &cc1, &cc2);
- if (cc1 != TYPEC_CC_VOLT_OPEN ||
- cc2 != TYPEC_CC_VOLT_OPEN)
- /* CC status cchanged, wake task */
- pd_event |= PD_EVENT_CC;
- } else {
- /* CC status changed, wake task */
- pd_event |= PD_EVENT_CC;
- }
- }
-
- tcpci_check_vbus_changed(port, alert, &pd_event);
-
- /* Check for Hard Reset received */
- if (alert & TCPC_REG_ALERT_RX_HARD_RST) {
- /* hard reset received */
- CPRINTS("C%d Hard Reset received", port);
- pd_event |= PD_EVENT_RX_HARD_RESET;
- }
-
- /* USB TCPCI Spec R2 V1.1 Section 4.7.3 Step 2
- *
- * The TCPC asserts both ALERT.TransmitSOP*MessageSuccessful and
- * ALERT.TransmitSOP*MessageFailed regardless of the outcome of the
- * transmission and asserts the Alert# pin.
- */
- if (alert & TCPC_REG_ALERT_TX_SUCCESS &&
- alert & TCPC_REG_ALERT_TX_FAILED)
- CPRINTS("C%d Hard Reset sent", port);
-
- if (IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)
- && (alert_ext & TCPC_REG_ALERT_EXT_SNK_FRS))
- pd_got_frs_signal(port);
-
- /*
- * Check registers to see if we can tell that the TCPC has reset. If
- * so, perform a tcpc_init.
- */
- if (register_mask_reset(port))
- pd_event |= PD_EVENT_TCPC_RESET;
-
- /*
- * Wait until all possible TCPC accesses in this function are complete
- * prior to setting events and/or waking the pd task. When the PD
- * task is woken and runs (which will happen during I2C transactions in
- * this function), the pd task may put the TCPC into low power mode and
- * the next I2C transaction to the TCPC will cause it to wake again.
- */
- if (pd_event)
- task_set_event(PD_PORT_TO_TASK_ID(port), pd_event);
-}
-
-/*
- * This call will wake up the TCPC if it is in low power mode upon accessing the
- * i2c bus (but the pd state machine should put it back into low power mode).
- *
- * Once it's called, the chip info will be stored in cache, which can be
- * accessed by tcpm_get_chip_info without worrying about chip states.
- */
-int tcpci_get_chip_info(int port, int live,
- struct ec_response_pd_chip_info_v1 *chip_info)
-{
- static struct ec_response_pd_chip_info_v1
- cached_info[CONFIG_USB_PD_PORT_MAX_COUNT];
- struct ec_response_pd_chip_info_v1 *i;
- int error;
- int val;
-
- if (port >= board_get_usb_pd_port_count())
- return EC_ERROR_INVAL;
-
- i = &cached_info[port];
-
-
- /* If already cached && live data is not asked, return cached value */
- if (i->vendor_id && !live) {
- /*
- * If chip_info is NULL, chip info will be stored in cache and
- * can be read later by another call.
- */
- if (chip_info)
- memcpy(chip_info, i, sizeof(*i));
-
- return EC_SUCCESS;
- }
-
- error = tcpc_read16(port, TCPC_REG_VENDOR_ID, &val);
- if (error)
- return error;
- i->vendor_id = val;
-
- error = tcpc_read16(port, TCPC_REG_PRODUCT_ID, &val);
- if (error)
- return error;
- i->product_id = val;
-
- error = tcpc_read16(port, TCPC_REG_BCD_DEV, &val);
- if (error)
- return error;
- i->device_id = val;
-
- /*
- * This varies chip to chip; more specific driver code is expected to
- * override this value if it can.
- */
- i->fw_version_number = -1;
-
- /* Copy the cached value to return if chip_info is not NULL */
- if (chip_info)
- memcpy(chip_info, i, sizeof(*i));
-
- return EC_SUCCESS;
-}
-
-/*
- * Dissociate from the TCPC.
- */
-
-int tcpci_tcpm_release(int port)
-{
- int error;
-
- error = clear_alert_mask(port);
- if (error)
- return error;
- error = clear_power_status_mask(port);
- if (error)
- return error;
- /* Clear pending interrupts */
- error = tcpc_write16(port, TCPC_REG_ALERT, 0xffff);
- if (error)
- return error;
-
- return EC_SUCCESS;
-}
-
-/*
- * On TCPC i2c failure, make 30 tries (at least 300ms) before giving up
- * in order to allow the TCPC time to boot / reset.
- */
-#define TCPM_INIT_TRIES 30
-
-int tcpci_tcpm_init(int port)
-{
- int error;
- int power_status;
- int tries = TCPM_INIT_TRIES;
-
- if (port >= board_get_usb_pd_port_count())
- return EC_ERROR_INVAL;
-
- while (1) {
- error = tcpci_tcpm_get_power_status(port, &power_status);
- /*
- * If read succeeds and the uninitialized bit is clear, then
- * initialization is complete, clear all alert bits and write
- * the initial alert mask.
- */
- if (!error && !(power_status & TCPC_REG_POWER_STATUS_UNINIT))
- break;
- if (--tries <= 0)
- return error ? error : EC_ERROR_TIMEOUT;
- msleep(10);
- }
-
- /*
- * For TCPCI Rev 2.0, unless the TCPM sets
- * TCPC_CONTROL.EnableLooking4ConnectionAlert bit, TCPC by default masks
- * Alert assertion when CC_STATUS.Looking4Connection changes state.
- */
- if (tcpc_config[port].flags & TCPC_FLAGS_TCPCI_REV2_0) {
- error = tcpc_update8(port, TCPC_REG_TCPC_CTRL,
- TCPC_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT,
- MASK_SET);
- if (error)
- CPRINTS("C%d: Failed to init TCPC_CTRL!", port);
- }
-
- /*
- * Handle and clear any alerts, since we might be coming out of low
- * power mode in response to an alert interrupt from the TCPC.
- */
- tcpc_alert(port);
- /* Initialize power_status_mask */
- init_power_status_mask(port);
-
- if (TCPC_FLAGS_VSAFE0V(tcpc_config[port].flags)) {
- int ext_status = 0;
-
- /* Read Extended Status register */
- tcpm_ext_status(port, &ext_status);
- /* Initial level, set appropriately */
- if (power_status & TCPC_REG_POWER_STATUS_VBUS_PRES)
- tcpc_vbus[port] = BIT(VBUS_PRESENT);
- else if (ext_status & TCPC_REG_EXT_STATUS_SAFE0V)
- tcpc_vbus[port] = BIT(VBUS_SAFE0V);
- else
- tcpc_vbus[port] = 0;
- } else {
- /* Initial level, set appropriately */
- tcpc_vbus[port] = (power_status &
- TCPC_REG_POWER_STATUS_VBUS_PRES)
- ? BIT(VBUS_PRESENT)
- : BIT(VBUS_SAFE0V);
- }
-
- /*
- * Force an update to the VBUS status in case the TCPC doesn't send a
- * power status changed interrupt later.
- */
- tcpci_check_vbus_changed(port,
- TCPC_REG_ALERT_POWER_STATUS | TCPC_REG_ALERT_EXT_STATUS,
- NULL);
-
- error = init_alert_mask(port);
- if (error)
- return error;
-
- /* Read chip info here when we know the chip is awake. */
- tcpm_get_chip_info(port, 1, NULL);
-
- /* Cache our device capabilities for future reference */
- tcpc_read16(port, TCPC_REG_DEV_CAP_1, &dev_cap_1[port]);
-
- return EC_SUCCESS;
-}
-
-#ifdef CONFIG_USB_PD_TCPM_MUX
-
-/*
- * When the TCPC/MUX device is only used for the MUX, we need to initialize it
- * via mux init because tcpc_init won't run for the device. This is borrowed
- * from tcpc_init.
- */
-int tcpci_tcpm_mux_init(const struct usb_mux *me)
-{
- int error;
- int power_status;
- int tries = TCPM_INIT_TRIES;
-
- /* If this MUX is also the TCPC, then skip init */
- if (!(me->flags & USB_MUX_FLAG_NOT_TCPC))
- return EC_SUCCESS;
-
- /* Wait for the device to exit low power state */
- while (1) {
- error = mux_read(me, TCPC_REG_POWER_STATUS, &power_status);
- /*
- * If read succeeds and the uninitialized bit is clear, then
- * initialization is complete.
- */
- if (!error && !(power_status & TCPC_REG_POWER_STATUS_UNINIT))
- break;
- if (--tries <= 0)
- return error ? error : EC_ERROR_TIMEOUT;
- msleep(10);
- }
-
- /* Turn off all alerts and acknowledge any pending IRQ */
- error = mux_write16(me, TCPC_REG_ALERT_MASK, 0);
- error |= mux_write16(me, TCPC_REG_ALERT, 0xffff);
-
- return error ? EC_ERROR_UNKNOWN : EC_SUCCESS;
-}
-
-int tcpci_tcpm_mux_enter_low_power(const struct usb_mux *me)
-{
- /* If this MUX is also the TCPC, then skip low power */
- if (!(me->flags & USB_MUX_FLAG_NOT_TCPC))
- return EC_SUCCESS;
-
- return mux_write(me, TCPC_REG_COMMAND, TCPC_REG_COMMAND_I2CIDLE);
-}
-
-int tcpci_tcpm_mux_set(const struct usb_mux *me, mux_state_t mux_state,
- bool *ack_required)
-{
- int rv;
- int reg = 0;
-
- /* This driver does not use host command ACKs */
- *ack_required = false;
-
- /* Parameter is port only */
- rv = mux_read(me, TCPC_REG_CONFIG_STD_OUTPUT, &reg);
- if (rv != EC_SUCCESS)
- return rv;
-
- reg &= ~(TCPC_REG_CONFIG_STD_OUTPUT_MUX_MASK |
- TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED);
- if (mux_state & USB_PD_MUX_USB_ENABLED)
- reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB;
- if (mux_state & USB_PD_MUX_DP_ENABLED)
- reg |= TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP;
- if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
- reg |= TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED;
-
- /* Parameter is port only */
- return mux_write(me, TCPC_REG_CONFIG_STD_OUTPUT, reg);
-}
-
-/* Reads control register and updates mux_state accordingly */
-int tcpci_tcpm_mux_get(const struct usb_mux *me, mux_state_t *mux_state)
-{
- int rv;
- int reg = 0;
-
- *mux_state = 0;
-
- /* Parameter is port only */
- rv = mux_read(me, TCPC_REG_CONFIG_STD_OUTPUT, &reg);
- if (rv != EC_SUCCESS)
- return rv;
-
- if (reg & TCPC_REG_CONFIG_STD_OUTPUT_MUX_USB)
- *mux_state |= USB_PD_MUX_USB_ENABLED;
- if (reg & TCPC_REG_CONFIG_STD_OUTPUT_MUX_DP)
- *mux_state |= USB_PD_MUX_DP_ENABLED;
- if (reg & TCPC_REG_CONFIG_STD_OUTPUT_CONNECTOR_FLIPPED)
- *mux_state |= USB_PD_MUX_POLARITY_INVERTED;
-
- return EC_SUCCESS;
-}
-
-const struct usb_mux_driver tcpci_tcpm_usb_mux_driver = {
- .init = &tcpci_tcpm_mux_init,
- .set = &tcpci_tcpm_mux_set,
- .get = &tcpci_tcpm_mux_get,
- .enter_low_power_mode = &tcpci_tcpm_mux_enter_low_power,
-};
-
-#endif /* CONFIG_USB_PD_TCPM_MUX */
-
-#ifdef CONFIG_CMD_TCPC_DUMP
-static const struct tcpc_reg_dump_map tcpc_regs[] = {
- {
- .addr = TCPC_REG_VENDOR_ID,
- .name = "VENDOR_ID",
- .size = 2,
- },
- {
- .addr = TCPC_REG_PRODUCT_ID,
- .name = "PRODUCT_ID",
- .size = 2,
- },
- {
- .addr = TCPC_REG_BCD_DEV,
- .name = "BCD_DEV",
- .size = 2,
- },
- {
- .addr = TCPC_REG_TC_REV,
- .name = "TC_REV",
- .size = 2,
- },
- {
- .addr = TCPC_REG_PD_REV,
- .name = "PD_REV",
- .size = 2,
- },
- {
- .addr = TCPC_REG_PD_INT_REV,
- .name = "PD_INT_REV",
- .size = 2,
- },
- {
- .addr = TCPC_REG_ALERT,
- .name = "ALERT",
- .size = 2,
- },
- {
- .addr = TCPC_REG_ALERT_MASK,
- .name = "ALERT_MASK",
- .size = 2,
- },
- {
- .addr = TCPC_REG_POWER_STATUS_MASK,
- .name = "POWER_STATUS_MASK",
- .size = 1,
- },
- {
- .addr = TCPC_REG_FAULT_STATUS_MASK,
- .name = "FAULT_STATUS_MASK",
- .size = 1,
- },
- {
- .addr = TCPC_REG_EXT_STATUS_MASK,
- .name = "EXT_STATUS_MASK",
- .size = 1
- },
- {
- .addr = TCPC_REG_ALERT_EXTENDED_MASK,
- .name = "ALERT_EXTENDED_MASK",
- .size = 1,
- },
- {
- .addr = TCPC_REG_CONFIG_STD_OUTPUT,
- .name = "CONFIG_STD_OUTPUT",
- .size = 1,
- },
- {
- .addr = TCPC_REG_TCPC_CTRL,
- .name = "TCPC_CTRL",
- .size = 1,
- },
- {
- .addr = TCPC_REG_ROLE_CTRL,
- .name = "ROLE_CTRL",
- .size = 1,
- },
- {
- .addr = TCPC_REG_FAULT_CTRL,
- .name = "FAULT_CTRL",
- .size = 1,
- },
- {
- .addr = TCPC_REG_POWER_CTRL,
- .name = "POWER_CTRL",
- .size = 1,
- },
- {
- .addr = TCPC_REG_CC_STATUS,
- .name = "CC_STATUS",
- .size = 1,
- },
- {
- .addr = TCPC_REG_POWER_STATUS,
- .name = "POWER_STATUS",
- .size = 1,
- },
- {
- .addr = TCPC_REG_FAULT_STATUS,
- .name = "FAULT_STATUS",
- .size = 1,
- },
- {
- .addr = TCPC_REG_EXT_STATUS,
- .name = "EXT_STATUS",
- .size = 1,
- },
- {
- .addr = TCPC_REG_ALERT_EXT,
- .name = "ALERT_EXT",
- .size = 1,
- },
- {
- .addr = TCPC_REG_DEV_CAP_1,
- .name = "DEV_CAP_1",
- .size = 2,
- },
- {
- .addr = TCPC_REG_DEV_CAP_2,
- .name = "DEV_CAP_2",
- .size = 2,
- },
- {
- .addr = TCPC_REG_STD_INPUT_CAP,
- .name = "STD_INPUT_CAP",
- .size = 1,
- },
- {
- .addr = TCPC_REG_STD_OUTPUT_CAP,
- .name = "STD_OUTPUT_CAP",
- .size = 1,
- },
- {
- .addr = TCPC_REG_CONFIG_EXT_1,
- .name = "CONFIG_EXT_1",
- .size = 1,
- },
- {
- .addr = TCPC_REG_MSG_HDR_INFO,
- .name = "MSG_HDR_INFO",
- .size = 1,
- },
- {
- .addr = TCPC_REG_RX_DETECT,
- .name = "RX_DETECT",
- .size = 1,
- },
- {
- .addr = TCPC_REG_RX_BYTE_CNT,
- .name = "RX_BYTE_CNT",
- .size = 1,
- },
- {
- .addr = TCPC_REG_RX_BUF_FRAME_TYPE,
- .name = "RX_BUF_FRAME_TYPE",
- .size = 1,
- },
- {
- .addr = TCPC_REG_TRANSMIT,
- .name = "TRANSMIT",
- .size = 1,
- },
- {
- .addr = TCPC_REG_VBUS_VOLTAGE,
- .name = "VBUS_VOLTAGE",
- .size = 2,
- },
- {
- .addr = TCPC_REG_VBUS_SINK_DISCONNECT_THRESH,
- .name = "VBUS_SINK_DISCONNECT_THRESH",
- .size = 2,
- },
- {
- .addr = TCPC_REG_VBUS_STOP_DISCHARGE_THRESH,
- .name = "VBUS_STOP_DISCHARGE_THRESH",
- .size = 2,
- },
- {
- .addr = TCPC_REG_VBUS_VOLTAGE_ALARM_HI_CFG,
- .name = "VBUS_VOLTAGE_ALARM_HI_CFG",
- .size = 2,
- },
- {
- .addr = TCPC_REG_VBUS_VOLTAGE_ALARM_LO_CFG,
- .name = "VBUS_VOLTAGE_ALARM_LO_CFG",
- .size = 2,
- },
-};
-
-/*
- * Dump standard TCPC registers.
- */
-void tcpc_dump_std_registers(int port)
-{
- tcpc_dump_registers(port, tcpc_regs, ARRAY_SIZE(tcpc_regs));
-}
-#endif
-
-const struct tcpm_drv tcpci_tcpm_drv = {
- .init = &tcpci_tcpm_init,
- .release = &tcpci_tcpm_release,
- .get_cc = &tcpci_tcpm_get_cc,
-#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
- .check_vbus_level = &tcpci_tcpm_check_vbus_level,
-#endif
- .select_rp_value = &tcpci_tcpm_select_rp_value,
- .set_cc = &tcpci_tcpm_set_cc,
- .set_polarity = &tcpci_tcpm_set_polarity,
-#ifdef CONFIG_USB_PD_DECODE_SOP
- .sop_prime_enable = &tcpci_tcpm_sop_prime_enable,
-#endif
- .set_vconn = &tcpci_tcpm_set_vconn,
- .set_msg_header = &tcpci_tcpm_set_msg_header,
- .set_rx_enable = &tcpci_tcpm_set_rx_enable,
- .get_message_raw = &tcpci_tcpm_get_message_raw,
- .transmit = &tcpci_tcpm_transmit,
- .tcpc_alert = &tcpci_tcpc_alert,
-#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
- .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus,
-#endif
- .tcpc_enable_auto_discharge_disconnect =
- &tcpci_tcpc_enable_auto_discharge_disconnect,
-#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
- .drp_toggle = &tcpci_tcpc_drp_toggle,
-#endif
- .get_chip_info = &tcpci_get_chip_info,
-#ifdef CONFIG_USB_PD_PPC
- .get_snk_ctrl = &tcpci_tcpm_get_snk_ctrl,
- .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl,
- .get_src_ctrl = &tcpci_tcpm_get_src_ctrl,
- .set_src_ctrl = &tcpci_tcpm_set_src_ctrl,
-#endif
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
- .enter_low_power_mode = &tcpci_enter_low_power_mode,
-#endif
- .set_bist_test_mode = &tcpci_set_bist_test_mode,
-#ifdef CONFIG_CMD_TCPC_DUMP
- .dump_registers = &tcpc_dump_std_registers,
-#endif
-};