summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAman Kumar <akumar@analogixsemi.com>2016-03-17 17:22:07 +0530
committerchrome-bot <chrome-bot@chromium.org>2016-05-18 18:11:32 -0700
commite9501105c29100769151c75dbb3d037c5f133921 (patch)
tree49d7001dc1742fdbe06101414dcb5fb9445c4761
parent4e889ab4a0c77973c729c70283c26286fd876370 (diff)
downloadchrome-ec-e9501105c29100769151c75dbb3d037c5f133921.tar.gz
TCPM : Added driver for Analogix's anx74xx chips
Driver implements TCPC for ANX74xx chips. Enables Type C port for USB and DP alt mode. Enable port role swap feature. Driver implements TCPC for ANX74xx chips firmware version 1.0 and later. Please update to ANX74xx firmware to V1.0 or later version to work. Change list: 1, modify the position of define and struct declare which response the comment for patch 22. BUG=chrome-os-partner:49510 BRANCH=none TEST=tested compiled binary for pdeval-stm32f072 board with this patch. Power contract establishment, port role swap, DP alt mode works fine. Change-Id: Iae6322510605a08d3bdd08446116ef5f9e4f7a7c Signed-off-by: Aman Kumar <akumar@analogixsemi.com> Signed-off-by: Junhua Xia <jxia@analogixsemi.com> Reviewed-on: https://chromium-review.googlesource.com/322433 Commit-Ready: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--driver/build.mk1
-rw-r--r--driver/tcpm/anx74xx.c799
-rw-r--r--driver/tcpm/anx74xx.h154
-rw-r--r--include/config.h1
4 files changed, 955 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 6f3f9b82fd..fc571af018 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -78,6 +78,7 @@ driver-$(CONFIG_USB_PD_TCPM_STUB)+=tcpm/stub.o
driver-$(CONFIG_USB_PD_TCPM_TCPCI)+=tcpm/tcpci.o
driver-$(CONFIG_USB_PD_TCPM_FUSB302)+=tcpm/fusb302.o
driver-$(CONFIG_USB_PD_TCPM_ITE83XX)+=tcpm/it83xx.o
+driver-$(CONFIG_USB_PD_TCPM_ANX74XX)+=tcpm/anx74xx.o
# USB switches
driver-$(CONFIG_USB_SWITCH_PI3USB9281)+=usb_switch_pi3usb9281.o
diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c
new file mode 100644
index 0000000000..b2b33e8576
--- /dev/null
+++ b/driver/tcpm/anx74xx.c
@@ -0,0 +1,799 @@
+/* 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 "anx74xx.h"
+#include "task.h"
+#include "tcpm.h"
+#include "timer.h"
+#include "usb_charge.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpc.h"
+#include "util.h"
+
+struct anx_state {
+ int polarity;
+ int pull;
+ int vconn_en;
+ int mux_state;
+};
+static struct anx_state anx[CONFIG_USB_PD_PORT_COUNT];
+
+static void anx74xx_tcpm_set_auto_good_crc(int port, int enable)
+{
+ int reg;
+
+ if (enable) {
+ /* Set default header for Good CRC auto reply */
+ tcpc_read(port, ANX74XX_REG_TX_MSG_HEADER, &reg);
+ reg |= (PD_REV20 << ANX74XX_REG_SPEC_REV_BIT_POS);
+ reg |= ANX74XX_REG_AUTO_GOODCRC_EN;
+ tcpc_write(port, ANX74XX_REG_TX_MSG_HEADER, reg);
+
+ reg = ANX74XX_REG_ENABLE_GOODCRC;
+ tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, reg);
+ /* Set bit-0 if enable, reset bit-0 if disable */
+ tcpc_read(port, ANX74XX_REG_TX_AUTO_GOODCRC_1, &reg);
+ reg |= ANX74XX_REG_AUTO_GOODCRC_EN;
+ } else {
+ reg &= ~ANX74XX_REG_AUTO_GOODCRC_EN;
+ tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_2, 0);
+ }
+ tcpc_write(port, ANX74XX_REG_TX_AUTO_GOODCRC_1, reg);
+}
+
+static void anx74xx_set_power_mode(int port, int mode)
+{
+ switch (mode) {
+ case ANX74XX_NORMAL_MODE:
+ /* Set PWR_EN and RST_N GPIO pins high */
+ board_set_tcpc_power_mode(port, 1);
+ break;
+ case ANX74XX_STANDBY_MODE:
+ /* Disable PWR_EN, keep Digital and analog block
+ * ON for cable detection
+ */
+ board_set_tcpc_power_mode(port, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+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);
+}
+
+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);
+}
+
+void anx74xx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq)
+{
+ int reg;
+
+ tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, &reg);
+ if (hpd_lvl)
+ reg |= ANX74XX_REG_HPD_OUT_DATA;
+ else
+ reg &= ~ANX74XX_REG_HPD_OUT_DATA;
+ tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg);
+
+ if (hpd_irq) {
+ tcpc_read(port, ANX74XX_REG_HPD_CTRL_0, &reg);
+ reg &= ~ANX74XX_REG_HPD_OUT_DATA;
+ tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg);
+ msleep(1);
+ reg |= ANX74XX_REG_HPD_OUT_DATA;
+ tcpc_write(port, ANX74XX_REG_HPD_CTRL_0, reg);
+ }
+}
+
+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(int i2c_addr)
+{
+ int port = i2c_addr;
+
+ /* Nothing to do here, ANX initializes its muxes
+ * as (MUX_USB_ENABLED | MUX_DP_ENABLED)
+ */
+ anx[port].mux_state = MUX_USB_ENABLED | MUX_DP_ENABLED;
+
+ return EC_SUCCESS;
+}
+
+static int anx74xx_tcpm_mux_set(int i2c_addr, mux_state_t mux_state)
+{
+ int reg = 0, val = 0;
+ int rv;
+ int port = i2c_addr;
+
+ rv = tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_5, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ reg &= 0x0f;
+
+ if (mux_state & MUX_USB_ENABLED) {
+ /* Set pin assignment D */
+ if (mux_state & MUX_POLARITY_INVERTED) {
+ val = ANX74XX_REG_MUX_DP_MODE_BDF_CC2;
+ reg |= ANX74XX_REG_MUX_SSTX_B;
+ } else {
+ val = ANX74XX_REG_MUX_DP_MODE_BDF_CC1;
+ reg |= ANX74XX_REG_MUX_SSTX_A;
+ }
+ } else if (mux_state & MUX_DP_ENABLED) {
+ /* Set pin assignment C */
+ if (mux_state & MUX_POLARITY_INVERTED) {
+ val = ANX74XX_REG_MUX_DP_MODE_ACE_CC2;
+ reg |= ANX74XX_REG_MUX_ML2_B;
+ } else {
+ val = ANX74XX_REG_MUX_DP_MODE_ACE_CC1;
+ reg |= ANX74XX_REG_MUX_ML2_A;
+ }
+ } else {
+ return EC_ERROR_UNIMPLEMENTED;
+ }
+
+ rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_1, val);
+ rv |= tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_5, reg);
+ anx[port].mux_state = mux_state;
+
+ return rv;
+}
+
+/* current mux state */
+static int anx74xx_tcpm_mux_get(int i2c_addr, mux_state_t *mux_state)
+{
+ int port = i2c_addr;
+
+ *mux_state = anx[port].mux_state;
+
+ return EC_SUCCESS;
+}
+
+static int anx74xx_tcpm_mux_exit(int port)
+{
+ int rv = EC_SUCCESS;
+
+ rv |= tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_2,
+ ANX74XX_REG_MODE_TRANS);
+ rv |= tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_1,
+ 0x0);
+ rv |= tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_5,
+ 0x04);
+ rv |= tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_2,
+ 0x0);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ return rv;
+}
+
+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_set_mux(int port, int polarity)
+{
+ int reg, rv = EC_SUCCESS;
+
+ rv = tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_2, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ if (polarity) {
+ reg |= ANX74XX_REG_AUX_SWAP_SET_CC2;
+ reg &= ~ANX74XX_REG_AUX_SWAP_SET_CC1;
+ } else {
+ reg |= ANX74XX_REG_AUX_SWAP_SET_CC1;
+ reg &= ~ANX74XX_REG_AUX_SWAP_SET_CC2;
+ }
+ rv = tcpc_write(port, ANX74XX_REG_ANALOG_CTRL_2, 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;
+
+
+ /* 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 receive message interrupt bit(bit-0) */
+ rv |= tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg);
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & (~0x01));
+
+ return rv;
+}
+
+static int anx74xx_tcpm_get_cc(int port, int *cc1, int *cc2)
+{
+ int rv = EC_SUCCESS;
+ int reg = 0;
+ int status = 0;
+
+ rv |= tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, &reg);
+
+ if (!anx[port].pull) {/* get CC in sink mode */
+ rv |= tcpc_read(port, ANX74XX_REG_POWER_DOWN_CTRL, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ /* CC1 */
+ if (reg & ANX74XX_REG_STATUS_CC1_VRD_USB)
+ *cc1 = TYPEC_CC_VOLT_SNK_DEF;
+ else if (reg & ANX74XX_REG_STATUS_CC1_VRD_1P5)
+ *cc1 = TYPEC_CC_VOLT_SNK_1_5;
+ else if (reg & ANX74XX_REG_STATUS_CC1_VRD_3P0)
+ *cc1 = TYPEC_CC_VOLT_SNK_3_0;
+ else
+ *cc1 = TYPEC_CC_VOLT_OPEN;
+ /* CC2 */
+ if (reg & ANX74XX_REG_STATUS_CC2_VRD_USB)
+ *cc2 = TYPEC_CC_VOLT_SNK_DEF;
+ else if (reg & ANX74XX_REG_STATUS_CC2_VRD_1P5)
+ *cc2 = TYPEC_CC_VOLT_SNK_1_5;
+ else if (reg & ANX74XX_REG_STATUS_CC2_VRD_3P0)
+ *cc2 = TYPEC_CC_VOLT_SNK_3_0;
+ else
+ *cc2 = TYPEC_CC_VOLT_OPEN;
+ } else {/* get CC in source mode */
+ rv |= tcpc_read(port, ANX74XX_REG_ANALOG_CTRL_7, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ /* CC1 */
+ status = ANX74XX_REG_STATUS_CC1(reg);
+ if (status) {
+ if ((status & ANX74XX_REG_STATUS_CC_RA) ==
+ ANX74XX_REG_STATUS_CC_RA)
+ *cc1 = TYPEC_CC_VOLT_RA;
+ else if (status & ANX74XX_REG_STATUS_CC_RD)
+ *cc1 = TYPEC_CC_VOLT_RD;
+ } else {
+ *cc1 = TYPEC_CC_VOLT_OPEN;
+ }
+ /* CC2 */
+ status = ANX74XX_REG_STATUS_CC2(reg);
+ if (status) {
+ if ((status & ANX74XX_REG_STATUS_CC_RA) ==
+ ANX74XX_REG_STATUS_CC_RA)
+ *cc2 = TYPEC_CC_VOLT_RA;
+ else if (status & ANX74XX_REG_STATUS_CC_RD)
+ *cc2 = TYPEC_CC_VOLT_RD;
+ } else {
+ *cc2 = TYPEC_CC_VOLT_OPEN;
+ }
+ }
+ /* 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_tcpm_set_cc(int port, int pull)
+{
+ int rv = EC_SUCCESS;
+ int reg;
+
+ /* Enable CC software Control */
+ rv |= tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+ reg |= ANX74XX_REG_CC_SW_CTRL_ENABLE;
+ rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg);
+ 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);
+ anx[port].pull = 1;
+ 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);
+ anx[port].pull = 0;
+ break;
+ default:
+ rv = EC_ERROR_UNKNOWN;
+ break;
+ }
+
+ return rv;
+}
+
+static int anx74xx_tcpm_set_polarity(int port, int polarity)
+{
+ int reg, rv = EC_SUCCESS;
+
+ rv |= tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
+ if (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;
+ rv |= anx74xx_set_mux(port, polarity);
+
+ /* Default DP pin mux D */
+#ifdef CONFIG_USB_PD_TCPM_MUX
+ anx74xx_tcpm_mux_set(port, TYPEC_MUX_DOCK);
+#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;
+
+ return rv;
+}
+
+static int anx74xx_tcpm_set_msg_header(int port, int power_role, int data_role)
+{
+ int rv = 0, reg;
+
+ rv |= tcpc_read(port, ANX74XX_REG_TX_MSG_HEADER, &reg);
+ reg |= ((!!power_role) << ANX74XX_REG_PWR_ROLE_BIT_POS);
+ reg |= ((!!data_role) << ANX74XX_REG_DATA_ROLE_BIT_POS);
+ rv |= tcpc_write(port, ANX74XX_REG_TX_MSG_HEADER, reg);
+
+ return rv;
+}
+
+static int anx74xx_alert_status(int port, int *alert)
+{
+ int reg, rv = EC_SUCCESS;
+
+ /* Clear soft irq bit */
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_3,
+ ANX74XX_REG_CLEAR_SOFT_IRQ);
+
+ /* Read TCPC Alert register1 */
+ rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+
+ /* Clears interrupt bits */
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, reg);
+
+ *alert = reg;
+ rv = tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg);
+ if (rv)
+ return EC_ERROR_UNKNOWN;
+
+ if (reg & ANX74XX_REG_IRQ_CC_MSG_INT)
+ *alert |= ANX74XX_REG_ALERT_MSG_RECV;
+ else
+ *alert &= (~ANX74XX_REG_ALERT_MSG_RECV);
+
+ if (reg & ANX74XX_REG_IRQ_CC_STATUS_INT) {
+ *alert |= ANX74XX_REG_ALERT_CC_CHANGE;
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & 0xfd);
+ } else {
+ *alert &= (~ANX74XX_REG_ALERT_CC_CHANGE);
+ }
+
+ if (reg & ANX74XX_REG_IRQ_GOOD_CRC_INT) {
+ *alert |= ANX74XX_REG_ALERT_TX_ACK_RECV;
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & 0xfb);
+ } else {
+ *alert &= (~ANX74XX_REG_ALERT_TX_ACK_RECV);
+ }
+
+ if (reg & ANX74XX_REG_IRQ_TX_FAIL_INT) {
+ *alert |= ANX74XX_REG_ALERT_TX_MSG_ERROR;
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & 0xf7);
+ }
+ /* Read TCPC Alert register2 */
+ rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, &reg);
+
+ /* Clears interrupt bits */
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, reg);
+
+ if (reg & ANX74XX_REG_EXT_HARD_RST)
+ *alert |= ANX74XX_REG_ALERT_HARD_RST_RECV;
+ else
+ *alert &= (~ANX74XX_REG_ALERT_HARD_RST_RECV);
+
+ return rv;
+}
+
+static int anx74xx_tcpm_set_rx_enable(int port, int enable)
+{
+ int reg, rv = 0;
+
+ rv |= tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, &reg);
+ if (enable)
+ reg &= ~(ANX74XX_REG_IRQ_CC_MSG_INT);
+ else/* Disable RX message by masking interrupt */
+ reg |= (ANX74XX_REG_IRQ_CC_MSG_INT);
+ anx74xx_tcpm_set_auto_good_crc(port, enable);
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK, reg);
+
+ return rv;
+}
+
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+static int anx74xx_tcpm_get_vbus_level(int port)
+{
+ int reg = 0;
+
+ tcpc_read(port, ANX74XX_REG_ANALOG_STATUS, &reg);
+ return ((reg & ANX74XX_REG_VBUS_STATUS) ? 1 : 0);
+}
+#endif
+
+static int anx74xx_tcpm_get_message(int port, uint32_t *payload, int *head)
+{
+ int reg = 0, rv = EC_SUCCESS;
+ int len = 0;
+
+ /* Fetch the header */
+ rv |= tcpc_read16(port, ANX74XX_REG_PD_HEADER, &reg);
+ if (rv) {
+ *head = 0;
+ /* Clear receive message interrupt bit(bit-0) */
+ tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg);
+ tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & (~0x01));
+
+ return EC_ERROR_UNKNOWN;
+ }
+ *head = reg;
+ len = PD_HEADER_CNT(*head) * 4;
+ if (!len) {
+ /* Clear receive message interrupt bit(bit-0) */
+ tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, &reg);
+ tcpc_write(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG,
+ reg & (~0x01));
+ return EC_SUCCESS;
+ }
+
+ /* Receive message : assuming payload have enough
+ * memory allocated
+ */
+ rv |= anx74xx_read_pd_obj(port, (uint8_t *)payload, len);
+ if (rv) {
+ *head = 0;
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return rv;
+}
+
+static int anx74xx_tcpm_transmit(int port, enum tcpm_transmit_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 TCPC_TX_SOP:
+ case TCPC_TX_SOP_PRIME:
+ case TCPC_TX_SOP_PRIME_PRIME:
+ len = PD_HEADER_CNT(header) * 4 + 2;
+ ret = anx74xx_send_message(port, header,
+ data, type, len);
+ break;
+ case TCPC_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);
+ break;
+ case TCPC_TX_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 TCPC_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;
+}
+
+void anx74xx_tcpc_alert(int port)
+{
+ int status;
+
+ /* Check the alert status */
+ if (anx74xx_alert_status(port, &status))
+ status = 0;
+ if (status) {
+
+ if (status & ANX74XX_REG_ALERT_CC_CHANGE) {
+ /* CC status changed, wake task */
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_CC, 0);
+ }
+
+ /* If alert is to receive a message */
+ if (status & ANX74XX_REG_ALERT_MSG_RECV) {
+ /* Set a PD_EVENT_RX */
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_RX, 0);
+ }
+ if (status & ANX74XX_REG_ALERT_TX_ACK_RECV) {
+ /* Inform PD about this TX success */
+ pd_transmit_complete(port,
+ TCPC_TX_COMPLETE_SUCCESS);
+ }
+ if (status & ANX74XX_REG_ALERT_TX_MSG_ERROR) {
+ /* let PD does not wait for this */
+ pd_transmit_complete(port,
+ TCPC_TX_COMPLETE_FAILED);
+ }
+ if (status & ANX74XX_REG_ALERT_TX_CABLE_RESETOK) {
+ /* ANX hardware clears the request bit */
+ pd_transmit_complete(port,
+ TCPC_TX_COMPLETE_SUCCESS);
+ }
+ if (status & ANX74XX_REG_ALERT_TX_HARD_RESETOK) {
+ /* ANX hardware clears the request bit */
+ pd_transmit_complete(port,
+ TCPC_TX_COMPLETE_SUCCESS);
+ }
+ if (status & ANX74XX_REG_ALERT_HARD_RST_RECV) {
+ /* hard reset received */
+ pd_execute_hard_reset(port);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ }
+ }
+}
+
+int anx74xx_tcpm_init(int port)
+{
+ int rv = 0, reg;
+
+ memset(anx, 0, CONFIG_USB_PD_PORT_COUNT*sizeof(struct anx_state));
+ /* Bring chip in normal mode to work */
+ anx74xx_set_power_mode(port, ANX74XX_NORMAL_MODE);
+
+ /* Set Pd dual role mode */
+ pd_set_dual_role(PD_DRP_TOGGLE_ON);
+
+ /* 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 polarity */
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_STATUS,
+ ANX74XX_INT_ACTIVE_POLARITY);
+
+ /* 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;
+
+ return EC_SUCCESS;
+}
+
+const struct tcpm_drv anx74xx_tcpm_drv = {
+ .init = &anx74xx_tcpm_init,
+ .get_cc = &anx74xx_tcpm_get_cc,
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+ .get_vbus_level = &anx74xx_tcpm_get_vbus_level,
+#endif
+ .set_cc = &anx74xx_tcpm_set_cc,
+ .set_polarity = &anx74xx_tcpm_set_polarity,
+ .set_vconn = &anx74xx_tcpm_set_vconn,
+ .set_msg_header = &anx74xx_tcpm_set_msg_header,
+ .set_rx_enable = &anx74xx_tcpm_set_rx_enable,
+ .get_message = &anx74xx_tcpm_get_message,
+ .transmit = &anx74xx_tcpm_transmit,
+ .tcpc_alert = &anx74xx_tcpc_alert,
+};
diff --git a/driver/tcpm/anx74xx.h b/driver/tcpm/anx74xx.h
new file mode 100644
index 0000000000..b25de49bf4
--- /dev/null
+++ b/driver/tcpm/anx74xx.h
@@ -0,0 +1,154 @@
+/* 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.
+ */
+
+/* USB Power delivery port management */
+
+#ifndef __CROS_EC_USB_PD_TCPM_ANX74XX_H
+#define __CROS_EC_USB_PD_TCPM_ANX74XX_H
+
+#define ANX74XX_REG_IRQ_POL_LOW 0x00
+#define ANX74XX_REG_IRQ_POL_HIGH 0x02
+#define ANX74XX_INT_ACTIVE_POLARITY ANX74XX_REG_IRQ_POL_HIGH
+
+#define ANX74XX_REG_IRQ_STATUS 0x53
+
+#define ANX74XX_REG_INTP_VCONN_CTRL 0x33
+#define ANX74XX_REG_VCONN_DISABLE 0x0f
+#define ANX74XX_REG_VCONN_1_ENABLE (1 << 4)
+#define ANX74XX_REG_VCONN_2_ENABLE (1 << 5)
+
+#define ANX74XX_STANDBY_MODE (0)
+#define ANX74XX_NORMAL_MODE (1)
+
+#define ANX74XX_REG_TX_CTRL_1 0x81
+#define ANX74XX_REG_TX_HARD_RESET_REQ (1 << 1)
+#define ANX74XX_REG_TX_CABLE_RESET_REQ (1 << 2)
+
+#define ANX74XX_REG_TX_CTRL_2 0x82
+#define ANX74XX_REG_TX_WR_FIFO 0x83
+#define ANX74XX_REG_TX_FIFO_CTRL 0x9a
+#define ANX74XX_REG_TX_HEADER_L 0x2c
+#define ANX74XX_REG_TX_HEADER_H 0x2d
+#define ANX74XX_REG_TX_START_ADDR_0 0x6d
+#define ANX74XX_REG_TX_START_ADDR_1 0xd0
+
+#define ANX74XX_REG_CTRL_COMMAND 0xdb
+#define ANX74XX_REG_TX_SEND_DATA_REQ (1 << 0)
+#define ANX74XX_REG_TX_HARD_RST_REQ (1 << 1)
+
+#define ANX74XX_REG_TX_BIST_CTRL 0x9D
+#define ANX74XX_REG_TX_BIST_MODE (1 << 4)
+#define ANX74XX_REG_TX_BIST_STOP (1 << 3)
+#define ANX74XX_REG_TX_BIXT_FOREVER (1 << 2)
+#define ANX74XX_REG_TX_BIST_ENABLE (1 << 1)
+#define ANX74XX_REG_TX_BIST_START (1 << 0)
+
+#define ANX74XX_REG_PD_HEADER 0x69
+#define ANX74XX_REG_PD_RX_DATA_OBJ 0x11
+#define ANX74XX_REG_PD_RX_DATA_OBJ_M 0x4d
+
+#define ANX74XX_REG_ANALOG_STATUS 0x40
+#define ANX74XX_REG_VBUS_STATUS (1 << 4)
+#define ANX74XX_REG_CC_PULL_RD 0xfd
+#define ANX74XX_REG_CC_PULL_RP 0x02
+
+#define ANX74XX_REG_TX_AUTO_GOODCRC_1 0x9c
+#define ANX74XX_REG_TX_AUTO_GOODCRC_2 0x94
+#define ANX74XX_REG_AUTO_GOODCRC_EN 0x01
+#define ANX74XX_REG_ENABLE_GOODCRC 0x38
+
+#define ANX74XX_REG_ANALOG_CTRL_1 0x42
+#define ANX74XX_REG_ANALOG_CTRL_5 0x46
+#define ANX74XX_REG_R_SWITCH_CC_CLR 0x0f
+#define ANX74XX_REG_R_SWITCH_CC2_SET 0x10
+#define ANX74XX_REG_R_SWITCH_CC1_SET 0x20
+#define ANX74XX_REG_AUX_SWAP_SET_CC1 0x30
+#define ANX74XX_REG_AUX_SWAP_SET_CC2 0xc0
+
+#define ANX74XX_REG_MUX_DP_MODE_ACE_CC1 0x49
+#define ANX74XX_REG_MUX_DP_MODE_ACE_CC2 0x86
+#define ANX74XX_REG_MUX_DP_MODE_BDF_CC1 0x61
+#define ANX74XX_REG_MUX_DP_MODE_BDF_CC2 0x92
+
+#define ANX74XX_REG_MUX_ML2_B (1 << 4)
+#define ANX74XX_REG_MUX_ML2_A (1 << 5)
+#define ANX74XX_REG_MUX_SSTX_B (1 << 6)
+#define ANX74XX_REG_MUX_SSTX_A (1 << 7)
+
+#define ANX74XX_REG_CC_SOFTWARE_CTRL 0x4a
+#define ANX74XX_REG_CC_SW_CTRL_ENABLE 0x01
+#define ANX74XX_REG_TX_MODE_ENABLE 0x04
+
+#define ANX74XX_REG_SELECT_CC1 0x02
+
+#define ANX74XX_REG_GPIO_CTRL_4_5 0x3f
+#define ANX74XX_REG_VBUS_OP_ENABLE 0x04
+#define ANX74XX_REG_VBUS_GPIO_MODE 0xfe
+
+#define ANX74XX_REG_IRQ_EXT_MASK_1 0x3b
+#define ANX74XX_REG_IRQ_EXT_MASK_2 0x3c
+#define ANX74XX_REG_IRQ_EXT_SOURCE_1 0x3e
+#define ANX74XX_REG_IRQ_EXT_SOURCE_2 0x4e
+#define ANX74XX_REG_EXT_HARD_RST (1 << 2)
+#define ANX74XX_REG_IRQ_EXT_SOURCE_3 0x4f
+#define ANX74XX_REG_CLEAR_SOFT_IRQ (1 << 2)
+
+#define ANX74XX_REG_IRQ_SOURCE_RECV_MSG 0x6b
+#define ANX74XX_REG_IRQ_CC_MSG_INT (1 << 0)
+#define ANX74XX_REG_IRQ_CC_STATUS_INT (1 << 1)
+#define ANX74XX_REG_IRQ_GOOD_CRC_INT (1 << 2)
+#define ANX74XX_REG_IRQ_TX_FAIL_INT (1 << 3)
+#define ANX74XX_REG_IRQ_SOURCE_RECV_MSG_MASK 0x6c
+
+#define ANX74XX_REG_CLEAR_SET_BITS 0xff
+#define ANX74XX_REG_ALERT_HARD_RST_RECV (1 << 6)
+#define ANX74XX_REG_ALERT_MSG_RECV (1 << 5)
+#define ANX74XX_REG_ALERT_TX_MSG_ERROR (1 << 4)
+#define ANX74XX_REG_ALERT_TX_ACK_RECV (1 << 3)
+#define ANX74XX_REG_ALERT_TX_CABLE_RESETOK (1 << 2)
+#define ANX74XX_REG_ALERT_TX_HARD_RESETOK (1 << 1)
+#define ANX74XX_REG_ALERT_CC_CHANGE (1 << 0)
+
+#define ANX74XX_REG_ANALOG_CTRL_2 0x43
+#define ANX74XX_REG_MODE_TRANS 0x01
+
+#define ANX74XX_REG_SET_VBUS 0x20
+
+#define ANX74XX_REG_ANALOG_CTRL_7 0x48
+#define ANX74XX_REG_STATUS_CC_RD 0x01
+#define ANX74XX_REG_STATUS_CC_RA 0x03
+#define ANX74XX_REG_STATUS_CC1(reg) ((reg & 0x0C) >> 2)
+#define ANX74XX_REG_STATUS_CC2(reg) ((reg & 0x03) >> 0)
+
+#define ANX74XX_REG_HPD_CONTROL 0xfd
+
+#define ANX74XX_REG_HPD_CTRL_0 0x36
+#define ANX74XX_REG_DISCHARGE_CTRL 0x80
+#define ANX74XX_REG_HPD_OP_MODE 0x08
+#define ANX74XX_REG_HPD_DEFAULT 0x00
+#define ANX74XX_REG_HPD_OUT_DATA 0x10
+
+#define ANX74XX_REG_TX_MSG_HEADER 0x9c
+#define ANX74XX_REG_SPEC_REV_BIT_POS (3)
+#define ANX74XX_REG_PWR_ROLE_BIT_POS (2)
+#define ANX74XX_REG_DATA_ROLE_BIT_POS (1)
+
+#define ANX74XX_REG_POWER_DOWN_CTRL 0x0d
+#define ANX74XX_REG_STATUS_CC1_VRD_USB (1 << 7)
+#define ANX74XX_REG_STATUS_CC1_VRD_1P5 (1 << 6)
+#define ANX74XX_REG_STATUS_CC1_VRD_3P0 (1 << 5)
+#define ANX74XX_REG_STATUS_CC2_VRD_USB (1 << 4)
+#define ANX74XX_REG_STATUS_CC2_VRD_1P5 (1 << 3)
+#define ANX74XX_REG_STATUS_CC2_VRD_3P0 (1 << 2)
+
+extern const struct tcpm_drv anx74xx_tcpm_drv;
+extern const struct usb_mux_driver anx74xx_tcpm_usb_mux_driver;
+void anx74xx_tcpc_set_vbus(int port, int enable);
+void anx74xx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq);
+void anx74xx_tcpc_clear_hpd_status(int port);
+
+#endif /* __CROS_EC_USB_PD_TCPM_ANX74XX_H */
diff --git a/include/config.h b/include/config.h
index 709ee2e41e..eed5b34302 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1875,6 +1875,7 @@
#undef CONFIG_USB_PD_TCPM_TCPCI
#undef CONFIG_USB_PD_TCPM_FUSB302
#undef CONFIG_USB_PD_TCPM_ITE83XX
+#undef CONFIG_USB_PD_TCPM_ANX74XX
/*
* Use this option if the TCPC port controller supports the optional register