diff options
Diffstat (limited to 'driver/tcpm/tcpci.c')
-rw-r--r-- | driver/tcpm/tcpci.c | 1838 |
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, ®); - 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 *)®, 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 *)®, 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, ®); - 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, ®); - 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 -}; |