/* 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, ®); \ 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, ®)) 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, ®); 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, ®); 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, ®); 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 &= ~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 &= 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, ®)) 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, ®)) 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, ®)) 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, ®)) 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, ®)) 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); /* 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®)) { 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 |= 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 |= 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, ®); 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, ®); /* 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, ®)) ++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, ®); 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, ®); 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, ®); 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 &= (~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 &= (~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, ®); 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, ®); 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 */