summaryrefslogtreecommitdiff
path: root/driver/tcpm/anx74xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/tcpm/anx74xx.c')
-rw-r--r--driver/tcpm/anx74xx.c1210
1 files changed, 0 insertions, 1210 deletions
diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c
deleted file mode 100644
index 567005920e..0000000000
--- a/driver/tcpm/anx74xx.c
+++ /dev/null
@@ -1,1210 +0,0 @@
-/* Copyright 2016 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.
- *
- * Author : Analogix Semiconductor.
- */
-
-/* Type-C port manager for Analogix's anx74xx chips */
-
-#include "console.h"
-#include "anx74xx.h"
-#include "task.h"
-#include "tcpm/tcpci.h"
-#include "tcpm/tcpm.h"
-#include "timer.h"
-#include "usb_charge.h"
-#include "usb_mux.h"
-#include "usb_pd.h"
-#include "usb_pd_tcpc.h"
-#include "usb_pd_tcpm.h"
-#include "util.h"
-
-#if !defined(CONFIG_USB_PD_TCPM_TCPCI)
-#error "ANX74xx is using part of standard TCPCI control"
-#error "Please upgrade your board configuration"
-#endif
-
-#if defined(CONFIG_USB_PD_REV30)
-#error "ANX74xx chips were developed before PD 3.0 and aren't PD 3.0 compliant"
-#error "Please undefine PD 3.0. See b/159253723 for details"
-#endif
-
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-
-struct anx_state {
- int polarity;
- int vconn_en;
- int mux_state;
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
- int prev_mode;
-#endif
-};
-#define clear_recvd_msg_int(port) do {\
- int reg, rv; \
- rv = tcpc_read(port, ANX74XX_REG_RECVD_MSG_INT, &reg); \
- if (!rv) \
- tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, \
- reg | 0x01); \
- } while (0)
-
-static struct anx_state anx[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
-/* Save the message address */
-static int msg_sop[CONFIG_USB_PD_PORT_MAX_COUNT];
-#endif
-
-static int anx74xx_tcpm_init(int port);
-
-static void anx74xx_tcpm_set_auto_good_crc(int port, int enable)
-{
- int reply_sop_en = 0;
-
- if (enable) {
- reply_sop_en = ANX74XX_REG_REPLY_SOP_EN;
-#ifdef CONFIG_USB_PD_DECODE_SOP
- /*
- * Only the VCONN Source is allowed to communicate
- * with the Cable Plugs.
- */
- if (anx[port].vconn_en) {
- reply_sop_en |= ANX74XX_REG_REPLY_SOP_1_EN |
- ANX74XX_REG_REPLY_SOP_2_EN;
- }
-#endif
- }
-
- tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, reply_sop_en);
-}
-
-static void anx74xx_update_cable_det(int port, int mode)
-{
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
- int reg;
-
- if (anx[port].prev_mode == mode)
- return;
-
- /* Update power mode */
- anx[port].prev_mode = mode;
-
- /* Get ANALOG_CTRL_0 for cable det bit */
- if (tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_0, &reg))
- return;
-
- if (mode == ANX74XX_STANDBY_MODE) {
- int cc_reg;
-
- /*
- * The ANX4329 enters standby mode by setting PWR_EN signal
- * low. In addition, RESET_L must be set low to keep the ANX3429
- * in standby mode.
- *
- * Clearing bit 7 of ANX74XX_REG_ANALOG_CTRL_0 will cause the
- * ANX3429 to clear the cable_det signal that goes from the
- * ANX3429 to the EC. If this bit is cleared when a cable is
- * attached then cable_det will go high once standby is entered.
- *
- * In some cases, such as when the chipset power state is
- * S3/S5/G3 and a sink only adapter is connected to the port,
- * this behavior is undesirable. The constant toggling between
- * standby and normal mode means that effectively the ANX3429 is
- * not in standby mode only consumes ~1 mW less than just
- * remaining in normal mode. However when an E mark cable is
- * connected, clearing bit 7 is required so that while the E
- * mark cable configuration happens, the USB PD state machine
- * will continue to wake up until the USB PD attach event can be
- * regtistered.
- *
- * Therefore, the decision to clear bit 7 is based on the
- * current CC status of the port. If the CC status is open for
- * both CC lines OR if either CC line is showing Ra, then clear
- * bit 7. Not clearing bit 7 has no impact for normal cables and
- * prevents the constant toggle of standby<->normal when an
- * adapter is connected that isn't allowed to attach. Clearing
- * bit 7 when CC status reads Ra for either CC line allows the
- * USB PD state machine to be woken until the attach event can
- * happen. Note that in the case an E mark cable is connected
- * and can't attach (i.e. sink only port <- Emark cable -> sink
- * only adapter), then the ANX3429 will toggle indefinitely,
- * until either the cable is removed, or the port drp status
- * changes so the attach event can occur.
- * .
- */
-
- /* Read CC status to see if cable_det bit should be cleared */
- if (tcpc_read(port, ANX74XX_REG_CC_STATUS, &cc_reg))
- return;
- /* If open or either CC line is Ra, then clear cable_det */
- if (!cc_reg || (cc_reg & ANX74XX_CC_RA_MASK &&
- !(cc_reg & ANX74XX_CC_RD_MASK)))
- reg &= ~ANX74XX_REG_R_PIN_CABLE_DET;
- } else {
- reg |= ANX74XX_REG_R_PIN_CABLE_DET;
- }
-
- tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_0, reg);
-#endif
-}
-
-static void anx74xx_set_power_mode(int port, int mode)
-{
- /*
- * Update PWR_EN and RESET_N signals to the correct level. High for
- * Normal mode and low for Standby mode. When transitioning from standby
- * to normal mode, must set the PWR_EN and RESET_N before attempting to
- * modify cable_det bit of analog_ctrl_0. If going from Normal to
- * Standby, updating analog_ctrl_0 must happen before setting PWR_EN and
- * RESET_N low.
- */
- if (mode == ANX74XX_NORMAL_MODE) {
- /* Take chip out of standby mode */
- board_set_tcpc_power_mode(port, mode);
- /* Update the cable det signal */
- anx74xx_update_cable_det(port, mode);
- } else {
- /* Update cable cable det signal */
- anx74xx_update_cable_det(port, mode);
- /*
- * Delay between setting cable_det low and setting RESET_L low
- * as recommended the ANX3429 datasheet.
- */
- msleep(1);
- /* Put chip into standby mode */
- board_set_tcpc_power_mode(port, mode);
- }
-}
-
-#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
- defined(CONFIG_USB_PD_TCPC_LOW_POWER)
-
-static int anx74xx_tcpc_drp_toggle(int port)
-{
- /*
- * The ANX3429 always auto-toggles when in low power mode. Since this is
- * not configurable, there is nothing to do here. DRP auto-toggle will
- * happen once low power mode is set via anx74xx_enter_low_power_mode().
- * Note: this means the ANX3429 auto-toggles in PD_DRP_FORCE_SINK mode,
- * which is undesirable (b/72007056).
- */
- return EC_SUCCESS;
-}
-
-static int anx74xx_enter_low_power_mode(int port)
-{
- anx74xx_set_power_mode(port, ANX74XX_STANDBY_MODE);
- return EC_SUCCESS;
-}
-
-#endif
-
-void anx74xx_tcpc_set_vbus(int port, int enable)
-{
- int reg;
-
- tcpc_read(port, ANX74XX_REG_GPIO_CTRL_4_5, &reg);
- if (enable)
- reg |= ANX74XX_REG_SET_VBUS;
- else
- reg &= ~ANX74XX_REG_SET_VBUS;
- tcpc_write(port, ANX74XX_REG_GPIO_CTRL_4_5, reg);
-}
-
-#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
-static void anx74xx_tcpc_discharge_vbus(int port, int enable)
-{
- int reg;
-
- tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, &reg);
- if (enable)
- reg |= ANX74XX_REG_DISCHARGE_CTRL;
- else
- reg &= ~ANX74XX_REG_DISCHARGE_CTRL;
- tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg);
-}
-#endif
-
-/*
- * timestamp of the next possible toggle to ensure the 2-ms spacing
- * between IRQ_HPD.
- */
-static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-void anx74xx_tcpc_update_hpd_status(const struct usb_mux *me,
- mux_state_t mux_state)
-{
- int reg;
- int port = me->usb_port;
- int hpd_lvl = (mux_state & USB_PD_MUX_HPD_LVL) ? 1 : 0;
- int hpd_irq = (mux_state & USB_PD_MUX_HPD_IRQ) ? 1 : 0;
-
- mux_read(me, ANX74XX_REG_HPD_CTRL_0, &reg);
- if (hpd_lvl)
- reg |= ANX74XX_REG_HPD_OUT_DATA;
- else
- reg &= ~ANX74XX_REG_HPD_OUT_DATA;
- mux_write(me, ANX74XX_REG_HPD_CTRL_0, reg);
-
- if (hpd_irq) {
- uint64_t now = get_time().val;
- /* wait for the minimum spacing between IRQ_HPD if needed */
- if (now < hpd_deadline[port])
- usleep(hpd_deadline[port] - now);
-
- mux_read(me, ANX74XX_REG_HPD_CTRL_0, &reg);
- reg &= ~ANX74XX_REG_HPD_OUT_DATA;
- mux_write(me, ANX74XX_REG_HPD_CTRL_0, reg);
- usleep(HPD_DSTREAM_DEBOUNCE_IRQ);
- reg |= ANX74XX_REG_HPD_OUT_DATA;
- mux_write(me, ANX74XX_REG_HPD_CTRL_0, reg);
- }
- /* enforce 2-ms delay between HPD pulses */
- hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL;
-}
-
-void anx74xx_tcpc_clear_hpd_status(int port)
-{
- int reg;
-
- tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, &reg);
- reg &= 0xcf;
- tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg);
-}
-
-#ifdef CONFIG_USB_PD_TCPM_MUX
-static int anx74xx_tcpm_mux_init(const struct usb_mux *me)
-{
- /* Nothing to do here, ANX initializes its muxes
- * as (USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED)
- */
- anx[me->usb_port].mux_state = USB_PD_MUX_USB_ENABLED |
- USB_PD_MUX_DP_ENABLED;
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_tcpm_mux_enter_safe_mode(int port)
-{
- int reg;
- const struct usb_mux *me = &usb_muxes[port];
-
- if (mux_read(me, ANX74XX_REG_ANALOG_CTRL_2, &reg))
- return EC_ERROR_UNKNOWN;
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_2, reg |
- ANX74XX_REG_MODE_TRANS))
- return EC_ERROR_UNKNOWN;
-
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_tcpm_mux_exit_safe_mode(int port)
-{
- int reg;
- const struct usb_mux *me = &usb_muxes[port];
-
- if (mux_read(me, ANX74XX_REG_ANALOG_CTRL_2, &reg))
- return EC_ERROR_UNKNOWN;
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_2, reg &
- ~ANX74XX_REG_MODE_TRANS))
- return EC_ERROR_UNKNOWN;
-
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_tcpm_mux_exit(int port)
-{
- int reg;
- const struct usb_mux *me = &usb_muxes[port];
-
- /*
- * Safe mode must be entered before any changes are made to the mux
- * settings used to enable ALT_DP mode. This function is called either
- * from anx74xx_tcpm_mux_set when USB_PD_MUX_NONE is selected as the
- * new mux state, or when both cc lines are determined to be
- * TYPEC_CC_VOLT_OPEN. Therefore, safe mode must be entered and exited
- * here so that both entry paths are handled.
- */
- if (anx74xx_tcpm_mux_enter_safe_mode(port))
- return EC_ERROR_UNKNOWN;
-
- /* Disconnect aux from sbu */
- if (mux_read(me, ANX74XX_REG_ANALOG_CTRL_2, &reg))
- return EC_ERROR_UNKNOWN;
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_2, reg & 0xf))
- return EC_ERROR_UNKNOWN;
-
- /* Clear Bit[7:0] R_SWITCH */
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_1, 0x0))
- return EC_ERROR_UNKNOWN;
- /* Clear Bit[7:4] R_SWITCH_H */
- if (mux_read(me, ANX74XX_REG_ANALOG_CTRL_5, &reg))
- return EC_ERROR_UNKNOWN;
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_5, reg & 0x0f))
- return EC_ERROR_UNKNOWN;
-
- /* Exit safe mode */
- if (anx74xx_tcpm_mux_exit_safe_mode(port))
- return EC_ERROR_UNKNOWN;
-
- return EC_SUCCESS;
-}
-
-
-static int anx74xx_mux_aux_to_sbu(int port, int polarity, int enabled)
-{
- int reg;
- const int aux_mask = ANX74XX_REG_AUX_SWAP_SET_CC2 |
- ANX74XX_REG_AUX_SWAP_SET_CC1;
- const struct usb_mux *me = &usb_muxes[port];
-
- /*
- * Get the current value of analog_ctrl_2 register. Note, that safe mode
- * is enabled and exited by the calling function, so only have to worry
- * about setting the correct value for the upper 4 bits of analog_ctrl_2
- * here.
- */
- if (mux_read(me, ANX74XX_REG_ANALOG_CTRL_2, &reg))
- return EC_ERROR_UNKNOWN;
-
- /* Assume aux_p/n lines are not connected */
- reg &= ~aux_mask;
-
- if (enabled) {
- /* If enabled, connect aux to sbu based on desired polarity */
- if (polarity)
- reg |= ANX74XX_REG_AUX_SWAP_SET_CC2;
- else
- reg |= ANX74XX_REG_AUX_SWAP_SET_CC1;
- }
- /* Write new aux <-> sbu settings */
- if (mux_write(me, ANX74XX_REG_ANALOG_CTRL_2, reg))
- return EC_ERROR_UNKNOWN;
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_tcpm_mux_set(const struct usb_mux *me,
- mux_state_t mux_state,
- bool *ack_required)
-{
- int ctrl5;
- int ctrl1 = 0;
- int rv;
- int port = me->usb_port;
-
- /* This driver does not use host command ACKs */
- *ack_required = false;
-
- if (!(mux_state & ~USB_PD_MUX_POLARITY_INVERTED)) {
- anx[port].mux_state = mux_state;
- return anx74xx_tcpm_mux_exit(port);
- }
-
- rv = mux_read(me, ANX74XX_REG_ANALOG_CTRL_5, &ctrl5);
- if (rv)
- return EC_ERROR_UNKNOWN;
- ctrl5 &= 0x0f;
-
- if (mux_state & USB_PD_MUX_USB_ENABLED) {
- /* Connect USB SS switches */
- if (mux_state & USB_PD_MUX_POLARITY_INVERTED) {
- ctrl1 = ANX74XX_REG_MUX_SSRX_RX2;
- ctrl5 |= ANX74XX_REG_MUX_SSTX_TX2;
- } else {
- ctrl1 = ANX74XX_REG_MUX_SSRX_RX1;
- ctrl5 |= ANX74XX_REG_MUX_SSTX_TX1;
- }
- if (mux_state & USB_PD_MUX_DP_ENABLED) {
- /* Set pin assignment D */
- if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
- ctrl1 |= (ANX74XX_REG_MUX_ML0_RX1 |
- ANX74XX_REG_MUX_ML1_TX1);
- else
- ctrl1 |= (ANX74XX_REG_MUX_ML0_RX2 |
- ANX74XX_REG_MUX_ML1_TX2);
- }
- /* Keep ML0/ML1 unconnected if DP is not enabled */
- } else if (mux_state & USB_PD_MUX_DP_ENABLED) {
- /* Set pin assignment C */
- if (mux_state & USB_PD_MUX_POLARITY_INVERTED) {
- ctrl1 = (ANX74XX_REG_MUX_ML0_RX1 |
- ANX74XX_REG_MUX_ML1_TX1 |
- ANX74XX_REG_MUX_ML3_RX2);
- ctrl5 |= ANX74XX_REG_MUX_ML2_TX2;
- } else {
- ctrl1 = (ANX74XX_REG_MUX_ML0_RX2 |
- ANX74XX_REG_MUX_ML1_TX2 |
- ANX74XX_REG_MUX_ML3_RX1);
- ctrl5 |= ANX74XX_REG_MUX_ML2_TX1;
- }
- } else if (!mux_state) {
- return anx74xx_tcpm_mux_exit(port);
- } else {
- return EC_ERROR_UNIMPLEMENTED;
- }
-
- /*
- * Safe mode must be entererd prior to any changes to the mux related to
- * ALT_DP mode. Therefore, first enable safe mode prior to updating the
- * values for analog_ctrl_1, analog_ctrl_5, and analog_ctrl_2.
- */
- if (anx74xx_tcpm_mux_enter_safe_mode(port))
- return EC_ERROR_UNKNOWN;
-
- /* Write updated pin assignment */
- rv = mux_write(me, ANX74XX_REG_ANALOG_CTRL_1, ctrl1);
- /* Write Rswitch config bits */
- rv |= mux_write(me, ANX74XX_REG_ANALOG_CTRL_5, ctrl5);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* Configure DP aux to sbu settings */
- if (anx74xx_mux_aux_to_sbu(port,
- mux_state & USB_PD_MUX_POLARITY_INVERTED,
- mux_state & USB_PD_MUX_DP_ENABLED))
- return EC_ERROR_UNKNOWN;
-
- /* Exit safe mode */
- if (anx74xx_tcpm_mux_exit_safe_mode(port))
- return EC_ERROR_UNKNOWN;
-
- anx[port].mux_state = mux_state;
-
- return EC_SUCCESS;
-}
-
-/* current mux state */
-static int anx74xx_tcpm_mux_get(const struct usb_mux *me,
- mux_state_t *mux_state)
-{
- *mux_state = anx[me->usb_port].mux_state;
-
- return EC_SUCCESS;
-}
-
-const struct usb_mux_driver anx74xx_tcpm_usb_mux_driver = {
- .init = &anx74xx_tcpm_mux_init,
- .set = &anx74xx_tcpm_mux_set,
- .get = &anx74xx_tcpm_mux_get,
-};
-#endif /* CONFIG_USB_PD_TCPM_MUX */
-
-static int anx74xx_init_analog(int port)
-{
- int reg, rv = EC_SUCCESS;
-
- /* Analog settings for chip */
- rv |= tcpc_write(port, ANX74XX_REG_HPD_CONTROL,
- ANX74XX_REG_HPD_OP_MODE);
- rv |= tcpc_write(port, ANX74XX_REG_HPD_CTRL_0,
- ANX74XX_REG_HPD_DEFAULT);
- if (rv)
- return rv;
- rv = tcpc_read(port, ANX74XX_REG_GPIO_CTRL_4_5, &reg);
- if (rv)
- return rv;
- reg &= ANX74XX_REG_VBUS_GPIO_MODE;
- reg |= ANX74XX_REG_VBUS_OP_ENABLE;
- rv = tcpc_write(port, ANX74XX_REG_GPIO_CTRL_4_5, reg);
- if (rv)
- return rv;
- rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
- if (rv)
- return rv;
- reg |= ANX74XX_REG_TX_MODE_ENABLE;
- rv = tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg);
-
- return rv;
-}
-
-static int anx74xx_send_message(int port, uint16_t header,
- const uint32_t *payload,
- int type,
- uint8_t len)
-{
- int reg, rv = EC_SUCCESS;
- uint8_t *buf = NULL;
- int num_retry = 0, i = 0;
- /* If sending Soft_reset, clear received message */
- /* Soft Reset Message type = 1101 and Number of Data Object = 0 */
- if ((header & 0x700f) == 0x000d) {
- /*
- * When sending soft reset,
- * the Rx buffer of ANX3429 shall be clear
- */
- rv = tcpc_read(port, ANX74XX_REG_CTRL_FW, &reg);
- rv |= tcpc_write(
- port, ANX74XX_REG_CTRL_FW, reg | CLEAR_RX_BUFFER);
- if (rv)
- return EC_ERROR_UNKNOWN;
- tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, 0xFF);
- }
- /* Inform chip about message length and TX type
- * type->bit-0..2, len->bit-3..7
- */
- reg = (len << 3) & 0xf8;
- reg |= type & 0x07;
- rv |= tcpc_write(port, ANX74XX_REG_TX_CTRL_2, reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* Enqueue Header */
- rv = tcpc_write(port, ANX74XX_REG_TX_HEADER_L, (header & 0xff));
- if (rv)
- return EC_ERROR_UNKNOWN;
- rv = tcpc_write(port, ANX74XX_REG_TX_HEADER_H, (header >> 8));
- if (rv)
- return EC_ERROR_UNKNOWN;
- /* Enqueue payload */
- if (len > 2) {
- len -= 2;
- buf = (uint8_t *)payload;
- while (1) {
- if (i < 18)
- rv = tcpc_write(port,
- ANX74XX_REG_TX_START_ADDR_0 + i,
- *buf);
- else
- rv = tcpc_write(port,
- ANX74XX_REG_TX_START_ADDR_1 + i - 18,
- *buf);
- if (rv) {
- num_retry++;
- } else {
- buf++;
- len--;
- num_retry = 0;
- i++;
- }
- if (len == 0 || num_retry >= 3)
- break;
- }
- /* If enqueue failed, do not request anx to transmit
- * messages, FIFO will get cleared in next call
- * before enqueue.
- * num_retry = 0, refer to success
- */
- if (num_retry)
- return EC_ERROR_UNKNOWN;
- }
- /* Request a data transmission
- * This bit will be cleared by ANX after TX success
- */
- rv = tcpc_read(port, ANX74XX_REG_CTRL_COMMAND, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- reg |= ANX74XX_REG_TX_SEND_DATA_REQ;
- rv |= tcpc_write(port, ANX74XX_REG_CTRL_COMMAND, reg);
-
- return rv;
-}
-
-static int anx74xx_read_pd_obj(int port,
- uint8_t *buf,
- int plen)
-{
- int rv = EC_SUCCESS, i;
- int reg, addr = ANX74XX_REG_PD_RX_DATA_OBJ;
-
- /* Read PD data objects from ANX */
- for (i = 0; i < plen ; i++) {
- /* Register sequence changes for last two bytes, if
- * plen is greater than 26
- */
- if (i == 26)
- addr = ANX74XX_REG_PD_RX_DATA_OBJ_M;
- rv = tcpc_read(port, addr + i, &reg);
- if (rv)
- break;
- buf[i] = reg;
- }
- clear_recvd_msg_int(port);
- return rv;
-}
-
-static int anx74xx_check_cc_type(int cc_reg)
-{
- int cc;
-
- switch (cc_reg & ANX74XX_REG_CC_STATUS_MASK) {
- case BIT_VALUE_OF_SRC_CC_RD:
- cc = TYPEC_CC_VOLT_RD;
- break;
-
- case BIT_VALUE_OF_SRC_CC_RA:
- cc = TYPEC_CC_VOLT_RA;
- break;
-
- case BIT_VALUE_OF_SNK_CC_DEFAULT:
- cc = TYPEC_CC_VOLT_RP_DEF;
- break;
-
- case BIT_VALUE_OF_SNK_CC_1_P_5:
- cc = TYPEC_CC_VOLT_RP_1_5;
- break;
-
- case BIT_VALUE_OF_SNK_CC_3_P_0:
- cc = TYPEC_CC_VOLT_RP_3_0;
- break;
-
- default:
- /* If no bits are set, then nothing is attached */
- cc = TYPEC_CC_VOLT_OPEN;
- }
-
- return cc;
-}
-
-static int anx74xx_tcpm_get_cc(int port, enum tcpc_cc_voltage_status *cc1,
- enum tcpc_cc_voltage_status *cc2)
-{
- int rv = EC_SUCCESS;
- int reg = 0;
-
- /* Read tcpc cc status register */
- rv |= tcpc_read(port, ANX74XX_REG_CC_STATUS, &reg);
- /* Check for cc1 type */
- *cc1 = anx74xx_check_cc_type(reg);
- /*
- * Check for cc2 type (note cc2 bits are upper 4 of cc status
- * register.
- */
- *cc2 = anx74xx_check_cc_type(reg >> 4);
-
- /* clear HPD status*/
- if (!(*cc1) && !(*cc2)) {
- anx74xx_tcpc_clear_hpd_status(port);
-#ifdef CONFIG_USB_PD_TCPM_MUX
- anx74xx_tcpm_mux_exit(port);
-#endif
- }
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_rp_control(int port, int rp)
-{
- int reg;
- int rv;
-
- rv = tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_6, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* clear Bit[0,1] R_RP to default Rp's value */
- reg &= ~0x03;
-
- switch (rp) {
- case TYPEC_RP_1A5:
- /* Set Rp strength to 12K for presenting 1.5A */
- reg |= ANX74XX_REG_CC_PULL_RP_12K;
- break;
- case TYPEC_RP_3A0:
- /* Set Rp strength to 4K for presenting 3A */
- reg |= ANX74XX_REG_CC_PULL_RP_4K;
- break;
- case TYPEC_RP_USB:
- default:
- /* default: Set Rp strength to 36K */
- break;
- }
-
- return tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_6, reg);
-}
-
-static int anx74xx_tcpm_select_rp_value(int port, int rp)
-{
- /* Keep track of current RP value */
- tcpci_set_cached_rp(port, rp);
-
- /* For ANX3429 cannot get cc correctly when Rp != USB_Default */
- return EC_SUCCESS;
-}
-
-
-static int anx74xx_cc_software_ctrl(int port, int enable)
-{
- int rv;
- int reg;
-
- rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- if (enable)
- reg |= ANX74XX_REG_CC_SW_CTRL_ENABLE;
- else
- reg &= ~ANX74XX_REG_CC_SW_CTRL_ENABLE;
-
- rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg);
- return rv;
-}
-
-static int anx74xx_tcpm_set_cc(int port, int pull)
-{
- int rv = EC_SUCCESS;
- int reg;
-
- /* Enable CC software Control */
- rv = anx74xx_cc_software_ctrl(port, 1);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- switch (pull) {
- case TYPEC_CC_RP:
- /* Enable Rp */
- rv |= tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- reg |= ANX74XX_REG_CC_PULL_RP;
- rv |= tcpc_write(port, ANX74XX_REG_ANALOG_STATUS, reg);
- break;
- case TYPEC_CC_RD:
- /* Enable Rd */
- rv |= tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- reg &= ANX74XX_REG_CC_PULL_RD;
- rv |= tcpc_write(port, ANX74XX_REG_ANALOG_STATUS, reg);
- break;
- default:
- rv = EC_ERROR_UNKNOWN;
- break;
- }
-
- return rv;
-}
-
-static int anx74xx_tcpm_set_polarity(int port, enum tcpc_cc_polarity polarity)
-{
- int reg, mux_state, rv = EC_SUCCESS;
- const struct usb_mux *me = &usb_muxes[port];
- bool unused;
-
- rv |= tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
- if (polarity_rm_dts(polarity)) /* Inform ANX to use CC2 */
- reg &= ~ANX74XX_REG_SELECT_CC1;
- else /* Inform ANX to use CC1 */
- reg |= ANX74XX_REG_SELECT_CC1;
- rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg);
-
- anx[port].polarity = polarity;
-
- /* Update mux polarity */
-#ifdef CONFIG_USB_PD_TCPM_MUX
- mux_state = anx[port].mux_state & ~USB_PD_MUX_POLARITY_INVERTED;
- if (polarity_rm_dts(polarity))
- mux_state |= USB_PD_MUX_POLARITY_INVERTED;
- anx74xx_tcpm_mux_set(me, mux_state, &unused);
-#endif
- return rv;
-}
-
-static int anx74xx_tcpm_set_vconn(int port, int enable)
-{
- int reg, rv = EC_SUCCESS;
-
- /* switch VCONN to Non CC line */
- rv |= tcpc_read(port, ANX74XX_REG_INTP_VCONN_CTRL, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- if (enable) {
- if (anx[port].polarity)
- reg |= ANX74XX_REG_VCONN_1_ENABLE;
- else
- reg |= ANX74XX_REG_VCONN_2_ENABLE;
- } else {
- reg &= ANX74XX_REG_VCONN_DISABLE;
- }
- rv |= tcpc_write(port, ANX74XX_REG_INTP_VCONN_CTRL, reg);
- anx[port].vconn_en = enable;
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
- rv |= tcpc_read(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- if (reg & ANX74XX_REG_REPLY_SOP_EN) {
- if (enable) {
- reg |= ANX74XX_REG_REPLY_SOP_1_EN |
- ANX74XX_REG_REPLY_SOP_2_EN;
- } else {
- reg &= ~(ANX74XX_REG_REPLY_SOP_1_EN |
- ANX74XX_REG_REPLY_SOP_2_EN);
- }
-
- tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, reg);
- }
-#endif
- return rv;
-}
-
-static int anx74xx_tcpm_set_msg_header(int port, int power_role, int data_role)
-{
- return tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_1,
- ANX74XX_REG_AUTO_GOODCRC_SET(!!data_role, !!power_role));
-}
-
-static int anx74xx_tcpm_set_rx_enable(int port, int enable)
-{
- int reg, rv;
-
- rv = tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, &reg);
- if (rv)
- return rv;
- if (enable) {
- reg &= ~(ANX74XX_REG_IRQ_CC_MSG_INT);
- anx74xx_tcpm_set_auto_good_crc(port, 1);
- anx74xx_rp_control(port, tcpci_get_cached_rp(port));
- } else {
- /* Disable RX message by masking interrupt */
- reg |= (ANX74XX_REG_IRQ_CC_MSG_INT);
- anx74xx_tcpm_set_auto_good_crc(port, 0);
- anx74xx_rp_control(port, TYPEC_RP_USB);
- }
- /*When this function was call, the interrupt status shall be cleared*/
- tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, 0);
-
- return tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, reg);
-}
-
-#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
-static bool anx74xx_tcpm_check_vbus_level(int port, enum vbus_level level)
-{
- int reg = 0;
-
- tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, &reg);
- if (level == VBUS_PRESENT)
- return ((reg & ANX74XX_REG_VBUS_STATUS) ? 1 : 0);
- else
- return ((reg & ANX74XX_REG_VBUS_STATUS) ? 0 : 1);
-}
-#endif
-
-static int anx74xx_tcpm_get_message_raw(int port, uint32_t *payload, int *head)
-{
- int reg;
- int len;
-
- /* Fetch the header */
- if (tcpc_read16(port, ANX74XX_REG_PD_HEADER, &reg)) {
- clear_recvd_msg_int(port);
- return EC_ERROR_UNKNOWN;
- }
- *head = reg;
-#ifdef CONFIG_USB_PD_DECODE_SOP
- *head |= PD_HEADER_SOP(msg_sop[port]);
-#endif
-
- len = PD_HEADER_CNT(*head) * 4;
- if (!len) {
- clear_recvd_msg_int(port);
- return EC_SUCCESS;
- }
-
- /* Receive message : assuming payload have enough
- * memory allocated
- */
- return anx74xx_read_pd_obj(port, (uint8_t *)payload, len);
-}
-
-static int anx74xx_tcpm_transmit(int port, enum tcpci_msg_type type,
- uint16_t header,
- const uint32_t *data)
-{
- uint8_t len = 0;
- int ret = 0, reg = 0;
-
- switch (type) {
- /* ANX is aware of type */
- case TCPCI_MSG_SOP:
- case TCPCI_MSG_SOP_PRIME:
- case TCPCI_MSG_SOP_PRIME_PRIME:
- len = PD_HEADER_CNT(header) * 4 + 2;
- ret = anx74xx_send_message(port, header,
- data, type, len);
- break;
- case TCPCI_MSG_TX_HARD_RESET:
- /* Request HARD RESET */
- tcpc_read(port, ANX74XX_REG_TX_CTRL_1, &reg);
- reg |= ANX74XX_REG_TX_HARD_RESET_REQ;
- ret = tcpc_write(port, ANX74XX_REG_TX_CTRL_1, reg);
- /*After Hard Reset, TCPM shall disable goodCRC*/
- anx74xx_tcpm_set_auto_good_crc(port, 0);
- break;
- case TCPCI_MSG_CABLE_RESET:
- /* Request CABLE RESET */
- tcpc_read(port, ANX74XX_REG_TX_CTRL_1, &reg);
- reg |= ANX74XX_REG_TX_CABLE_RESET_REQ;
- ret = tcpc_write(port, ANX74XX_REG_TX_CTRL_1, reg);
- break;
- case TCPCI_MSG_TX_BIST_MODE_2:
- /* Request BIST MODE 2 */
- reg = ANX74XX_REG_TX_BIST_START
- | ANX74XX_REG_TX_BIXT_FOREVER | (0x02 << 4);
- ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, reg);
- msleep(1);
- ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL,
- reg | ANX74XX_REG_TX_BIST_ENABLE);
- msleep(30);
- tcpc_read(port, ANX74XX_REG_TX_BIST_CTRL, &reg);
- ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL,
- reg | ANX74XX_REG_TX_BIST_STOP);
- ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL,
- reg & (~ANX74XX_REG_TX_BIST_STOP));
- ret = tcpc_write(port, ANX74XX_REG_TX_BIST_CTRL, 0);
- break;
- default:
- return EC_ERROR_UNIMPLEMENTED;
- }
-
- return ret;
-}
-
-/*
- * 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 anx74xx_tcpc_alert(int port)
-{
- int reg;
- int failed_attempts;
-
- /* Clear soft irq bit */
- tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_3,
- ANX74XX_REG_CLEAR_SOFT_IRQ);
-
- /* Read main alert register for pending alerts */
- reg = 0;
- tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg);
-
- /* Prioritize TX completion because PD state machine is waiting */
- if (reg & ANX74XX_REG_IRQ_GOOD_CRC_INT)
- pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS);
-
- if (reg & ANX74XX_REG_IRQ_TX_FAIL_INT)
- pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED);
-
- /* Pull all RX messages from TCPC into EC memory */
- failed_attempts = 0;
- while (reg & ANX74XX_REG_IRQ_CC_MSG_INT) {
- if (tcpm_enqueue_message(port))
- ++failed_attempts;
- if (tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg))
- ++failed_attempts;
-
- /* Ensure we don't loop endlessly */
- if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) {
- CPRINTF("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 alerts */
- tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, reg);
-
- if (reg & ANX74XX_REG_IRQ_CC_STATUS_INT)
- /* CC status changed, wake task */
- task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC);
-
- /* Read and clear extended alert register 1 */
- reg = 0;
- tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, &reg);
- tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, reg);
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
- if (reg & ANX74XX_REG_EXT_SOP)
- msg_sop[port] = TCPCI_MSG_SOP;
- else if (reg & ANX74XX_REG_EXT_SOP_PRIME)
- msg_sop[port] = TCPCI_MSG_SOP_PRIME;
-#endif
-
- /* Check for Hard Reset done bit */
- if (reg & ANX74XX_REG_ALERT_TX_HARD_RESETOK)
- /* ANX hardware clears the request bit */
- pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS);
-
- /* Read and clear TCPC extended alert register 2 */
- reg = 0;
- tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, &reg);
- tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, reg);
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
- if (reg & ANX74XX_REG_EXT_SOP_PRIME_PRIME)
- msg_sop[port] = TCPCI_MSG_SOP_PRIME_PRIME;
-#endif
-
- if (reg & ANX74XX_REG_EXT_HARD_RST) {
- /* hard reset received */
- task_set_event(PD_PORT_TO_TASK_ID(port),
- PD_EVENT_RX_HARD_RESET);
- }
-}
-
-static int anx74xx_tcpm_init(int port)
-{
- int rv = 0, reg;
-
- memset(&anx[port], 0, sizeof(struct anx_state));
- /* Bring chip in normal mode to work */
- anx74xx_set_power_mode(port, ANX74XX_NORMAL_MODE);
-
- /* Initialize analog section of ANX */
- rv |= anx74xx_init_analog(port);
-
- /* disable all interrupts */
- rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_1,
- ANX74XX_REG_CLEAR_SET_BITS);
-
- /* Initialize interrupt open-drain */
- rv |= tcpc_read(port, ANX74XX_REG_INTP_VCONN_CTRL, &reg);
- if (tcpc_config[port].flags & TCPC_FLAGS_ALERT_OD)
- reg |= ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN;
- else
- reg &= ~ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN;
- rv |= tcpc_write(port, ANX74XX_REG_INTP_VCONN_CTRL, reg);
-
- /* Initialize interrupt polarity */
- reg = tcpc_config[port].flags & TCPC_FLAGS_ALERT_ACTIVE_HIGH ?
- ANX74XX_REG_IRQ_POL_HIGH : ANX74XX_REG_IRQ_POL_LOW;
- rv |= tcpc_write(port, ANX74XX_REG_IRQ_STATUS, reg);
-
- /* unmask interrupts */
- rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_MASK_1, &reg);
- reg &= (~ANX74XX_REG_ALERT_TX_MSG_ERROR);
- reg &= (~ANX74XX_REG_ALERT_TX_CABLE_RESETOK);
- reg &= (~ANX74XX_REG_ALERT_TX_HARD_RESETOK);
- rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_1, reg);
-
- rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_MASK_2, &reg);
- reg &= (~ANX74XX_REG_EXT_HARD_RST);
- rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_MASK_2, reg);
-
- /* HPD pin output enable*/
- rv |= tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, ANX74XX_REG_HPD_DEFAULT);
-
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* Set AVDD10_BMC to 1.08 */
- rv |= tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_5, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_5, (reg & 0xf3));
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* Decrease BMC TX lowest swing voltage */
- rv |= tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_11, &reg);
- if (rv)
- return EC_ERROR_UNKNOWN;
- rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_11, (reg & 0x3f) | 0x40);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- /* Set BMC TX cap slew rate to 400ns */
- rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_12, 0x4);
- if (rv)
- return EC_ERROR_UNKNOWN;
-
- tcpm_get_chip_info(port, 1, NULL);
-
- return EC_SUCCESS;
-}
-
-static int anx74xx_get_chip_info(int port, int live,
- struct ec_response_pd_chip_info_v1 *chip_info)
-{
- int rv = tcpci_get_chip_info(port, live, chip_info);
- int val;
-
- if (rv)
- return rv;
-
- if (chip_info->fw_version_number == 0 ||
- chip_info->fw_version_number == -1 || live) {
- rv = tcpc_read(port, ANX74XX_REG_FW_VERSION, &val);
-
- if (rv)
- return rv;
-
- chip_info->fw_version_number = val;
- }
-
-#ifdef CONFIG_USB_PD_TCPM_ANX3429
- /*
- * Min firmware version of ANX3429 to ensure that false SOP' detection
- * doesn't occur for e-marked cables. See b/116255749#comment8 and
- * b/64752060#comment11
- */
- chip_info->min_req_fw_version_number = 0x16;
-#endif
-
- return rv;
-}
-
-/*
- * Dissociate from the TCPC.
- */
-
-static int anx74xx_tcpm_release(int port)
-{
- return EC_SUCCESS;
-}
-
-const struct tcpm_drv anx74xx_tcpm_drv = {
- .init = &anx74xx_tcpm_init,
- .release = &anx74xx_tcpm_release,
- .get_cc = &anx74xx_tcpm_get_cc,
-#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
- .check_vbus_level = &anx74xx_tcpm_check_vbus_level,
-#endif
- .select_rp_value = &anx74xx_tcpm_select_rp_value,
- .set_cc = &anx74xx_tcpm_set_cc,
- .set_polarity = &anx74xx_tcpm_set_polarity,
-#ifdef CONFIG_USB_PD_DECODE_SOP
- .sop_prime_enable = &tcpci_tcpm_sop_prime_enable,
-#endif
- .set_vconn = &anx74xx_tcpm_set_vconn,
- .set_msg_header = &anx74xx_tcpm_set_msg_header,
- .set_rx_enable = &anx74xx_tcpm_set_rx_enable,
- .get_message_raw = &anx74xx_tcpm_get_message_raw,
- .transmit = &anx74xx_tcpm_transmit,
- .tcpc_alert = &anx74xx_tcpc_alert,
-#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
- .tcpc_discharge_vbus = &anx74xx_tcpc_discharge_vbus,
-#endif
- .get_chip_info = &anx74xx_get_chip_info,
-#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
- defined(CONFIG_USB_PD_TCPC_LOW_POWER)
- .drp_toggle = &anx74xx_tcpc_drp_toggle,
- .enter_low_power_mode = &anx74xx_enter_low_power_mode,
-#endif
- .set_bist_test_mode = &tcpci_set_bist_test_mode,
-};
-
-#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC
-struct i2c_stress_test_dev anx74xx_i2c_stress_test_dev = {
- .reg_info = {
- .read_reg = ANX74XX_REG_VENDOR_ID_L,
- .read_val = ANX74XX_VENDOR_ID & 0xFF,
- .write_reg = ANX74XX_REG_CC_SOFTWARE_CTRL,
- },
- .i2c_read = &tcpc_i2c_read,
- .i2c_write = &tcpc_i2c_write,
-};
-#endif /* CONFIG_CMD_I2C_STRESS_TEST_TCPC */