summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Lai <dylai@analogixsemi.com>2018-03-09 13:54:08 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-04-03 21:40:51 -0700
commit82a357a385f9eee75bfbca9911e404d54b0c3217 (patch)
tree7509a8b541a6541b1d56fce2a5d9008a9c4323c9
parenta9c7d6b0d73eaf0c48438124b40fc054183701aa (diff)
downloadchrome-ec-82a357a385f9eee75bfbca9911e404d54b0c3217.tar.gz
TCPM: Add TCPM driver for Analogix anx7447 chip
Driver implements TCPC for ANX7447 chip. Enable Type C port for USB and DP alt mode. BUG=b:73793947 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: Ic11e499fc5fb4aba7732c75e4cb2fee54828c616 Reviewed-on: https://chromium-review.googlesource.com/956790 Commit-Ready: Scott Collyer <scollyer@chromium.org> Tested-by: Scott Collyer <scollyer@chromium.org> Reviewed-by: Scott Collyer <scollyer@chromium.org>
-rw-r--r--board/pdeval-stm32f072/board.c11
-rw-r--r--board/pdeval-stm32f072/board.h22
-rw-r--r--board/pdeval-stm32f072/ec.tasklist3
-rw-r--r--board/pdeval-stm32f072/gpio.inc3
-rw-r--r--board/pdeval-stm32f072/usb_pd_policy.c128
-rw-r--r--driver/build.mk1
-rw-r--r--driver/tcpm/anx7447.c393
-rw-r--r--driver/tcpm/anx7447.h61
8 files changed, 599 insertions, 23 deletions
diff --git a/board/pdeval-stm32f072/board.c b/board/pdeval-stm32f072/board.c
index f5aa26d10b..a306d853ac 100644
--- a/board/pdeval-stm32f072/board.c
+++ b/board/pdeval-stm32f072/board.c
@@ -4,6 +4,7 @@
*/
/* STM32F072-discovery board based USB PD evaluation configuration */
+#include "anx7447.h"
#include "common.h"
#include "ec_version.h"
#include "gpio.h"
@@ -52,15 +53,12 @@ void board_reset_pd_mcu(void)
/* I2C ports */
const struct i2c_port_t i2c_ports[] = {
- {"tcpc", I2C_PORT_TCPC, 100 /* kHz */, GPIO_I2C0_SCL, GPIO_I2C0_SDA}
+ {"tcpc", I2C_PORT_TCPC, 400 /* kHz */, GPIO_I2C0_SCL, GPIO_I2C0_SDA}
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {I2C_PORT_TCPC, TCPC1_I2C_ADDR, &tcpci_tcpm_drv},
-#if CONFIG_USB_PD_PORT_COUNT >= 2
- {I2C_PORT_TCPC, TCPC2_I2C_ADDR, &tcpci_tcpm_drv},
-#endif
+ {I2C_PORT_TCPC, AN7447_TCPC3_I2C_ADDR, &anx7447_tcpm_drv}
};
uint16_t tcpc_get_alert_status(void)
@@ -69,9 +67,6 @@ uint16_t tcpc_get_alert_status(void)
if (!gpio_get_level(GPIO_PD_MCU_INT)) {
status = PD_STATUS_TCPC_ALERT_0;
-#if CONFIG_USB_PD_PORT_COUNT >= 2
- status |= PD_STATUS_TCPC_ALERT_1;
-#endif
}
return status;
diff --git a/board/pdeval-stm32f072/board.h b/board/pdeval-stm32f072/board.h
index df32ec2683..9100ab1a94 100644
--- a/board/pdeval-stm32f072/board.h
+++ b/board/pdeval-stm32f072/board.h
@@ -26,12 +26,21 @@
#define CONFIG_USB_PD_ALT_MODE_DFP
#define CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_USB_PD_DUAL_ROLE
-#define CONFIG_USB_PD_PORT_COUNT 2
+#define CONFIG_USB_PD_PORT_COUNT 1
#define CONFIG_USB_PD_TCPM_TCPCI
+#define CONFIG_USB_PD_VBUS_DETECT_TCPC
+#define CONFIG_USB_PD_TCPM_ANX7447
+#define CONFIG_USB_PD_TCPM_MUX
+
+#undef CONFIG_USB_PD_INITIAL_DRP_STATE
+#define CONFIG_USB_PD_INITIAL_DRP_STATE PD_DRP_TOGGLE_ON
+
+#undef CONFIG_USB_PD_PULLUP
+#define CONFIG_USB_PD_PULLUP TYPEC_RP_USB
/* fake board specific type-C power constants */
#define PD_POWER_SUPPLY_TURN_ON_DELAY 30000 /* us */
-#define PD_POWER_SUPPLY_TURN_OFF_DELAY 250000 /* us */
+#define PD_POWER_SUPPLY_TURN_OFF_DELAY 650000 /* us */
/* Define typical operating power and max power */
#define PD_OPERATING_POWER_MW 15000
@@ -43,12 +52,13 @@
#define I2C_PORT_TCPC 0
#define I2C_PORT_PD_MCU 0
-/* TCPC I2C slave addresses */
-#define TCPC1_I2C_ADDR 0x9c
-#define TCPC2_I2C_ADDR 0x9e
-
/* Timer selection */
+#define CONFIG_USBC_VCONN
+#define CONFIG_USBC_VCONN_SWAP
+/* delay to turn on/off vconn */
+#define PD_VCONN_SWAP_DELAY 5000 /* us */
+
/* USB Configuration */
#define CONFIG_USB
#define CONFIG_USB_PID 0x500f
diff --git a/board/pdeval-stm32f072/ec.tasklist b/board/pdeval-stm32f072/ec.tasklist
index fd87fc0237..f58f5e3a7c 100644
--- a/board/pdeval-stm32f072/ec.tasklist
+++ b/board/pdeval-stm32f072/ec.tasklist
@@ -20,5 +20,4 @@
TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(PDCMD, pd_command_task,NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \
- TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE)
+ TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/board/pdeval-stm32f072/gpio.inc b/board/pdeval-stm32f072/gpio.inc
index cd3c601a0a..f71d69441e 100644
--- a/board/pdeval-stm32f072/gpio.inc
+++ b/board/pdeval-stm32f072/gpio.inc
@@ -16,6 +16,9 @@ GPIO(LED_U, PIN(C, 6), GPIO_OUT_LOW)
GPIO(LED_D, PIN(C, 7), GPIO_OUT_LOW)
GPIO(LED_L, PIN(C, 8), GPIO_OUT_LOW)
GPIO(LED_R, PIN(C, 9), GPIO_OUT_LOW)
+GPIO(USB_C0_DVDDIO, PIN(C, 14), GPIO_OUT_HIGH)
+GPIO(USB_C0_AVDD33, PIN(C, 15), GPIO_OUT_HIGH)
+GPIO(VBUS_PMIC_CTRL, PIN(A, 4), GPIO_OUT_LOW)
/*
* I2C pins should be configured as inputs until I2C module is
diff --git a/board/pdeval-stm32f072/usb_pd_policy.c b/board/pdeval-stm32f072/usb_pd_policy.c
index 73add50ca4..9b81222d58 100644
--- a/board/pdeval-stm32f072/usb_pd_policy.c
+++ b/board/pdeval-stm32f072/usb_pd_policy.c
@@ -3,6 +3,7 @@
* found in the LICENSE file.
*/
+#include "anx7447.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
@@ -12,6 +13,7 @@
#include "task.h"
#include "timer.h"
#include "util.h"
+#include "usb_mux.h"
#include "usb_pd.h"
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
@@ -23,15 +25,25 @@
static int vbus_present;
const uint32_t pd_src_pdo[] = {
- PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS),
+ PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS),
};
const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
const uint32_t pd_snk_pdo[] = {
- PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
+ PDO_FIXED(5000, 900, PDO_FIXED_FLAGS),
+ PDO_BATT(5000, 21000, 30000),
};
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
+#if defined(CONFIG_USB_PD_TCPM_MUX) && defined(CONFIG_USB_PD_TCPM_ANX7447)
+struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
+ {
+ .port_addr = 0,
+ .driver = &anx7447_usb_mux_driver,
+ },
+};
+#endif
+
int pd_is_valid_input_voltage(int mv)
{
return 1;
@@ -42,6 +54,34 @@ void pd_transition_voltage(int idx)
/* No-operation: we are always 5V */
}
+#ifdef CONFIG_USB_PD_TCPM_ANX7447
+int pd_set_power_supply_ready(int port)
+{
+ /* Disable charging */
+ anx7447_board_charging_enable(port, 0);
+
+ /* Provide VBUS */
+ gpio_set_level(GPIO_VBUS_PMIC_CTRL, 1);
+ anx7447_set_power_supply_ready(port);
+
+ /* notify host of power info change */
+
+ CPRINTS("Enable VBUS, port%d", port);
+
+ return EC_SUCCESS;
+}
+
+void pd_power_supply_reset(int port)
+{
+ /* Disable VBUS */
+ anx7447_power_supply_reset(port);
+ gpio_set_level(GPIO_VBUS_PMIC_CTRL, 0);
+ CPRINTS("Disable VBUS, port%d", port);
+
+ /* Enable charging */
+ anx7447_board_charging_enable(port, 1);
+}
+#else
int pd_set_power_supply_ready(int port)
{
/* Turn on the "up" LED when we output VBUS */
@@ -57,6 +97,7 @@ void pd_power_supply_reset(int port)
/* Disable VBUS */
CPRINTS("Disable VBUS", port);
}
+#endif /* CONFIG_USB_PD_TCPM_ANX7447 */
void pd_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage)
@@ -117,7 +158,7 @@ int pd_check_power_swap(int port)
* otherwise assume our role is fixed (not in S0 or console command
* to fix our role).
*/
- return pd_get_dual_role() == PD_DRP_TOGGLE_ON ? 1 : 0;
+ return pd_get_dual_role() == PD_DRP_TOGGLE_ON;
}
int pd_check_data_swap(int port, int data_role)
@@ -126,6 +167,18 @@ int pd_check_data_swap(int port, int data_role)
return 1;
}
+#ifdef CONFIG_USBC_VCONN_SWAP
+int pd_check_vconn_swap(int port)
+{
+ /*
+ * Allow vconn swap as long as we are acting as a dual role device,
+ * otherwise assume our role is fixed (not in S0 or console command
+ * to fix our role).
+ */
+ return pd_get_dual_role() == PD_DRP_TOGGLE_ON;
+}
+#endif
+
void pd_execute_data_swap(int port, int data_role)
{
}
@@ -138,8 +191,24 @@ void pd_check_dr_role(int port, int dr_role, int flags)
{
}
/* ----------------- Vendor Defined Messages ------------------ */
+const uint32_t vdo_idh = VDO_IDH(1, /* data caps as USB host */
+ 0, /* data caps as USB device */
+ IDH_PTYPE_PERIPH,
+ 0, /* supports alt modes */
+ 0x0000);
+
+const uint32_t vdo_product = VDO_PRODUCT(0x0000, 0x0000);
+
+static int svdm_response_identity(int port, uint32_t *payload)
+{
+ payload[VDO_I(IDH)] = vdo_idh;
+ payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
+ payload[VDO_I(PRODUCT)] = vdo_product;
+ return VDO_I(PRODUCT) + 1;
+}
+
const struct svdm_response svdm_rsp = {
- .identity = NULL,
+ .identity = &svdm_response_identity,
.svids = NULL,
.modes = NULL,
};
@@ -186,6 +255,7 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload,
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
static int dp_flags[CONFIG_USB_PD_PORT_COUNT];
+static uint32_t dp_status[CONFIG_USB_PD_PORT_COUNT];
static void svdm_safe_dp_mode(int port)
{
@@ -224,24 +294,66 @@ static int svdm_dp_status(int port, uint32_t *payload)
static int svdm_dp_config(int port, uint32_t *payload)
{
int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
+ int pin_mode = pd_dfp_dp_get_pin_mode(port, dp_status[port]);
+
+#ifdef CONFIG_USB_PD_TCPM_ANX7447
+ mux_state_t mux_state = TYPEC_MUX_NONE;
+ if (pd_get_polarity(port))
+ mux_state |= MUX_POLARITY_INVERTED;
+#endif
+
+ CPRINTS("pin_mode = %d\n", pin_mode);
+ if (!pin_mode)
+ return 0;
+
+#if defined(CONFIG_USB_PD_TCPM_MUX) && defined(CONFIG_USB_PD_TCPM_ANX7447)
+ switch (pin_mode) {
+ case MODE_DP_PIN_A:
+ case MODE_DP_PIN_C:
+ case MODE_DP_PIN_E:
+ mux_state |= TYPEC_MUX_DP;
+ usb_muxes[port].driver->set(port, mux_state);
+ break;
+ case MODE_DP_PIN_B:
+ case MODE_DP_PIN_D:
+ case MODE_DP_PIN_F:
+ mux_state |= TYPEC_MUX_DOCK;
+ usb_muxes[port].driver->set(port, mux_state);
+ break;
+ }
+#endif
+
/* board_set_usb_mux(port, TYPEC_MUX_DP, pd_get_polarity(port)); */
payload[0] = VDO(USB_SID_DISPLAYPORT, 1,
CMD_DP_CONFIG | VDO_OPOS(opos));
- payload[1] = VDO_DP_CFG(MODE_DP_PIN_E, /* pin mode */
+ payload[1] = VDO_DP_CFG(pin_mode, /* pin mode */
1, /* DPv1.3 signaling */
2); /* UFP connected */
return 2;
-};
+}
static void svdm_dp_post_config(int port)
{
dp_flags[port] |= DP_FLAGS_DP_ON;
if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING))
return;
+
+#ifdef CONFIG_USB_PD_TCPM_ANX7447
+ anx7447_tcpc_update_hpd_status(port, 1, 0);
+#endif
}
static int svdm_dp_attention(int port, uint32_t *payload)
{
+#ifdef CONFIG_USB_PD_TCPM_ANX7447
+ int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]);
+ int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]);
+
+ CPRINTS("Attention: 0x%x\n", payload[1]);
+ anx7447_tcpc_update_hpd_status(port, lvl, irq);
+#endif
+ dp_status[port] = payload[1];
+
/* ack */
return 1;
}
@@ -249,7 +361,9 @@ static int svdm_dp_attention(int port, uint32_t *payload)
static void svdm_exit_dp_mode(int port)
{
svdm_safe_dp_mode(port);
- /* gpio_set_level(PORT_TO_HPD(port), 0); */
+#ifdef CONFIG_USB_PD_TCPM_ANX7447
+ anx7447_tcpc_clear_hpd_status(port);
+#endif
}
static int svdm_enter_gfu_mode(int port, uint32_t mode_caps)
diff --git a/driver/build.mk b/driver/build.mk
index 541b8df235..ca04d48e60 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -104,6 +104,7 @@ driver-$(CONFIG_USB_PD_TCPM_ANX3429)+=tcpm/anx74xx.o
driver-$(CONFIG_USB_PD_TCPM_ANX740X)+=tcpm/anx74xx.o
driver-$(CONFIG_USB_PD_TCPM_ANX741X)+=tcpm/anx74xx.o
driver-$(CONFIG_USB_PD_TCPM_ANX7688)+=tcpm/anx7688.o
+driver-$(CONFIG_USB_PD_TCPM_ANX7447)+=tcpm/anx7447.o
driver-$(CONFIG_USB_PD_TCPM_PS8751)+=tcpm/ps8xxx.o
driver-$(CONFIG_USB_PD_TCPM_PS8805)+=tcpm/ps8xxx.o
diff --git a/driver/tcpm/anx7447.c b/driver/tcpm/anx7447.c
new file mode 100644
index 0000000000..30266a3b6b
--- /dev/null
+++ b/driver/tcpm/anx7447.c
@@ -0,0 +1,393 @@
+/* Copyright 2018 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.
+ */
+
+/* ANX7447 port manager */
+
+#include "anx7447.h"
+#include "console.h"
+#include "ec_version.h"
+#include "hooks.h"
+#include "tcpci.h"
+#include "tcpm.h"
+#include "timer.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "util.h"
+
+#define ANX7447_VENDOR_ALERT (1 << 15)
+
+#define ANX7447_REG_STATUS 0x82
+#define ANX7447_REG_STATUS_LINK (1 << 0)
+
+#define ANX7447_REG_HPD 0x83
+#define ANX7447_REG_HPD_HIGH (1 << 0)
+#define ANX7447_REG_HPD_IRQ (1 << 1)
+#define ANX7447_REG_HPD_ENABLE (1 << 2)
+
+#define vsafe5v_min (3800/25)
+#define vsafe0v_max (800/25)
+#define is_equal_greater_safe5v(port) \
+ (((anx7447_get_vbus_voltage(port))) > vsafe5v_min)
+#define is_equal_greater_safe0v(port) \
+ (((anx7447_get_vbus_voltage(port))) > vsafe0v_max)
+
+struct anx_state {
+ int i2c_slave_addr;
+ int mux_state;
+};
+
+static struct anx_state anx[CONFIG_USB_PD_PORT_COUNT];
+
+/*
+ * ANX7447 has two co-existence I2C slave addresses, TCPC slave address and
+ * SPI slave address. The registers of TCPC slave address are partly compliant
+ * with standard USB TCPC specification, and the registers in SPI slave
+ * address controls the other functions (ex, hpd_level, mux_switch, and
+ * so on). It can't use tcpc_read() and tcpc_write() to access SPI slave
+ * address because its slave address has been set as TCPC in the structure
+ * tcpc_config_t.
+ * anx7447_reg_write() and anx7447_reg_read() are implemented here to access
+ * ANX7447 SPI slave address.
+ */
+const struct anx7447_i2c_addr anx7447_i2c_addrs[] = {
+ {AN7447_TCPC0_I2C_ADDR, AN7447_SPI0_I2C_ADDR},
+ {AN7447_TCPC1_I2C_ADDR, AN7447_SPI1_I2C_ADDR},
+ {AN7447_TCPC2_I2C_ADDR, AN7447_SPI2_I2C_ADDR},
+ {AN7447_TCPC3_I2C_ADDR, AN7447_SPI3_I2C_ADDR}
+};
+
+static inline int anx7447_reg_write(int port, int reg, int val)
+{
+ return i2c_write8(tcpc_config[port].i2c_host_port,
+ anx[port].i2c_slave_addr,
+ reg, val);
+}
+
+static inline int anx7447_reg_read(int port, int reg, int *val)
+{
+ return i2c_read8(tcpc_config[port].i2c_host_port,
+ anx[port].i2c_slave_addr,
+ reg, val);
+}
+
+void anx7447_hpd_mode_en(int port)
+{
+ int reg, rv;
+
+ rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, &reg);
+ if (rv)
+ return;
+
+ reg |= ANX7447_REG_HPD_MODE;
+ anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg);
+}
+
+void anx7447_hpd_output_en(int port)
+{
+ int reg, rv;
+
+ rv = anx7447_reg_read(port, ANX7447_REG_HPD_DEGLITCH_H, &reg);
+ if (rv)
+ return;
+
+ reg |= ANX7447_REG_HPD_OEN;
+ anx7447_reg_write(port, ANX7447_REG_HPD_DEGLITCH_H, reg);
+}
+
+void anx7447_set_hpd_level(int port, int hpd_lvl)
+{
+ int reg, rv;
+
+ rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, &reg);
+ if (rv)
+ return;
+
+ if (hpd_lvl)
+ reg |= ANX7447_REG_HPD_OUT;
+ else
+ reg &= ~ANX7447_REG_HPD_OUT;
+ anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg);
+}
+
+static int anx7447_init(int port)
+{
+ int rv, reg, i;
+
+ memset(&anx[port], 0, sizeof(struct anx_state));
+
+ /*
+ * find corresponding anx7447 SPI slave address according to
+ * specified TCPC slave address
+ */
+ for (i = 0; i < ARRAY_SIZE(anx7447_i2c_addrs); i++) {
+ if (tcpc_config[port].i2c_slave_addr ==
+ anx7447_i2c_addrs[i].tcpc_slave_addr) {
+ anx[port].i2c_slave_addr =
+ anx7447_i2c_addrs[i].spi_slave_addr;
+ break;
+ }
+ }
+ if (!anx[port].i2c_slave_addr) {
+ ccprintf("TCPC I2C slave addr 0x%x is invalid for ANX7447\n",
+ tcpc_config[port].i2c_slave_addr);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ rv = tcpci_tcpm_init(port);
+ if (rv)
+ return rv;
+
+ /*
+ * 7447 has a physical pin to detect the presence of VBUS, VBUS_SENSE
+ * , and 7447 has a VBUS current protection mechanism through another
+ * pin input VBUS_OCP. To enable VBUS OCP, OVP protection, driver needs
+ * to set the threshold to the registers VBUS_VOLTAGE_ALARM_HI_CFG
+ * (0x76 & 0x77) and VBUS_OCP_HI_THRESHOLD (0xDD &0xDE). These values
+ * could be customized based on different platform design.
+ * Disable VBUS protection here since the default values of
+ * VBUS_VOLTAGE_ALARM_HI_CFG and VBUS_OCP_HI_THRESHOLD are zero.
+ */
+ rv = tcpc_read(port, ANX7447_REG_TCPC_CTRL_2, &reg);
+ if (rv)
+ return rv;
+ reg &= ~ANX7447_REG_ENABLE_VBUS_PROTECT;
+ rv = tcpc_write(port, ANX7447_REG_TCPC_CTRL_2, reg);
+ if (rv)
+ return rv;
+
+ /* ADC enable, use to monitor VBUS voltage */
+ rv = tcpc_read(port, ANX7447_REG_ADC_CTRL_1, &reg);
+ if (rv)
+ return rv;
+ reg |= ANX7447_REG_ADCFSM_EN;
+ rv = tcpc_write(port, ANX7447_REG_ADC_CTRL_1, reg);
+ if (rv)
+ return rv;
+
+ /* init hpd status */
+ anx7447_hpd_mode_en(port);
+ anx7447_set_hpd_level(port, 0);
+ anx7447_hpd_output_en(port);
+
+ return rv;
+}
+
+static int anx7447_release(int port)
+{
+ return EC_SUCCESS;
+}
+
+static void anx7447_update_hpd_enable(int port)
+{
+ int status, reg, rv;
+
+ rv = tcpc_read(port, ANX7447_REG_STATUS, &status);
+ rv |= tcpc_read(port, ANX7447_REG_HPD, &reg);
+ if (rv)
+ return;
+
+ if (!(reg & ANX7447_REG_HPD_ENABLE) ||
+ !(status & ANX7447_REG_STATUS_LINK)) {
+ reg &= ~ANX7447_REG_HPD_IRQ;
+ tcpc_write(port, ANX7447_REG_HPD,
+ (status & ANX7447_REG_STATUS_LINK)
+ ? reg | ANX7447_REG_HPD_ENABLE
+ : reg & ~ANX7447_REG_HPD_ENABLE);
+ }
+}
+
+#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
+static int anx7447_get_vbus_voltage(int port)
+{
+ int vbus_volt = 0;
+
+ tcpc_read16(port, TCPC_REG_VBUS_VOLTAGE, &vbus_volt);
+
+ return vbus_volt;
+}
+
+static int anx7447_tcpm_get_vbus_level(int port)
+{
+ return is_equal_greater_safe5v(port);
+}
+
+int anx7447_set_power_supply_ready(int port)
+{
+ int count = 0;
+
+ while (is_equal_greater_safe0v(port)) {
+ if (count >= 10)
+ break;
+ msleep(100);
+ count++;
+ }
+
+ return tcpc_write(port, TCPC_REG_COMMAND, 0x77);
+}
+#endif /* CONFIG_USB_PD_VBUS_DETECT_TCPC */
+
+int anx7447_power_supply_reset(int port)
+{
+ return tcpc_write(port, TCPC_REG_COMMAND, 0x66);
+}
+
+int anx7447_board_charging_enable(int port, int enable)
+{
+ return tcpc_write(port, TCPC_REG_COMMAND, enable ? 0x55 : 0x44);
+}
+
+static void anx7447_tcpc_alert(int port)
+{
+ int alert, rv;
+
+ rv = tcpc_read16(port, TCPC_REG_ALERT, &alert);
+ /* process and clear alert status */
+ tcpci_tcpc_alert(port);
+
+ if (!rv && (alert & ANX7447_VENDOR_ALERT))
+ anx7447_update_hpd_enable(port);
+}
+
+/*
+ * timestamp of the next possible toggle to ensure the 2-ms spacing
+ * between IRQ_HPD.
+ */
+static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_COUNT];
+
+void anx7447_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq)
+{
+ int reg = 0;
+
+ anx7447_set_hpd_level(port, hpd_lvl);
+
+ 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);
+
+ anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, &reg);
+ reg &= ~ANX7447_REG_HPD_OUT;
+ anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg);
+ usleep(HPD_DSTREAM_DEBOUNCE_IRQ);
+ reg |= ANX7447_REG_HPD_OUT;
+ anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg);
+ }
+ /* enforce 2-ms delay between HPD pulses */
+ hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL;
+}
+
+void anx7447_tcpc_clear_hpd_status(int port)
+{
+ anx7447_hpd_output_en(port);
+ anx7447_set_hpd_level(port, 0);
+}
+
+#ifdef CONFIG_USB_PD_TCPM_MUX
+static int anx7447_mux_init(int port)
+{
+ /* 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 anx7447_mux_set(int port, mux_state_t mux_state)
+{
+ int cc_direction;
+ mux_state_t mux_type;
+ int sw_sel = 0x30, aux_sw = 0x00;
+ int rv = EC_SUCCESS;
+
+ cc_direction = mux_state & MUX_POLARITY_INVERTED;
+ mux_type = mux_state & TYPEC_MUX_DOCK;
+ ccprintf("mux_state = 0x%x, mux_type = 0x%x\n", mux_state, mux_type);
+
+ if (mux_type == TYPEC_MUX_NONE) {
+ /* set MUX control as no connection */
+ sw_sel = 0x00;
+ }
+
+ /* type-C interface detect cable plug direction
+ * is positive orientation
+ */
+ /* CC1_CONNECTED */
+ if (cc_direction == 0) {
+ /* cc1 connection */
+ if (mux_type == TYPEC_MUX_DOCK) {
+ /* L0-a10/11,L1-b2/b3, sstx-a2/a3, ssrx-b10/11 */
+ sw_sel = 0x21;
+ aux_sw = 0x03;
+ } else if (mux_type == TYPEC_MUX_DP) {
+ /* L0-a10/11,L1-b2/b3, L2-a2/a3, L3-b10/11 */
+ sw_sel = 0x09;
+ aux_sw = 0x03;
+ }
+ } else {
+ /* cc2 connection */
+ if (mux_type == TYPEC_MUX_DOCK) {
+ /* L0-b10/11,L1-a2/b3, sstx-b2/a3, ssrx-a10/11 */
+ sw_sel = 0x12;
+ aux_sw = 0x0C;
+ } else if (mux_type == TYPEC_MUX_DP) {
+ /* L0-b10/11,L1-a2/b3, L2-b2/a3, L3-a10/11 */
+ sw_sel = 0x06;
+ aux_sw = 0x0C;
+ }
+ }
+ rv = tcpc_write(port, ANX7447_REG_TCPC_SWITCH_0, sw_sel);
+ rv |= tcpc_write(port, ANX7447_REG_TCPC_SWITCH_1, sw_sel);
+ rv |= tcpc_write(port, ANX7447_REG_TCPC_AUX_SWITCH, aux_sw);
+
+ anx[port].mux_state = mux_state;
+
+ return rv;
+}
+
+/* current mux state */
+static int anx7447_mux_get(int port, mux_state_t *mux_state)
+{
+ *mux_state = anx[port].mux_state;
+
+ return EC_SUCCESS;
+}
+#endif /* CONFIG_USB_PD_TCPM_MUX */
+
+/* ANX7447 is a TCPCI compatible port controller */
+const struct tcpm_drv anx7447_tcpm_drv = {
+ .init = &anx7447_init,
+ .release = &anx7447_release,
+ .get_cc = &tcpci_tcpm_get_cc,
+#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
+ .get_vbus_level = &anx7447_tcpm_get_vbus_level,
+#endif
+ .select_rp_value = &tcpci_tcpm_select_rp_value,
+ .set_cc = &tcpci_tcpm_set_cc,
+ .set_polarity = &tcpci_tcpm_set_polarity,
+ .set_vconn = &tcpci_tcpm_set_vconn,
+ .set_msg_header = &tcpci_tcpm_set_msg_header,
+ .set_rx_enable = &tcpci_tcpm_set_rx_enable,
+ .get_message = &tcpci_tcpm_get_message,
+ .transmit = &tcpci_tcpm_transmit,
+ .tcpc_alert = &anx7447_tcpc_alert,
+#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
+ .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus,
+#endif
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ .drp_toggle = &tcpci_tcpc_drp_toggle,
+#endif
+ .get_chip_info = &tcpci_get_chip_info,
+};
+
+#ifdef CONFIG_USB_PD_TCPM_MUX
+const struct usb_mux_driver anx7447_usb_mux_driver = {
+ .init = anx7447_mux_init,
+ .set = anx7447_mux_set,
+ .get = anx7447_mux_get,
+};
+#endif /* CONFIG_USB_PD_TCPM_MUX */
+
diff --git a/driver/tcpm/anx7447.h b/driver/tcpm/anx7447.h
new file mode 100644
index 0000000000..a2fe128f77
--- /dev/null
+++ b/driver/tcpm/anx7447.h
@@ -0,0 +1,61 @@
+/* Copyright 2018 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.
+ */
+
+/* USB Power delivery port management */
+
+#ifndef __CROS_EC_USB_PD_TCPM_ANX7447_H
+#define __CROS_EC_USB_PD_TCPM_ANX7447_H
+
+/* Registers: TCPC slave address used */
+#define ANX7447_REG_TCPC_SWITCH_0 0xB4
+#define ANX7447_REG_TCPC_SWITCH_1 0xB5
+#define ANX7447_REG_TCPC_AUX_SWITCH 0xB6
+
+#define ANX7447_REG_INTR_ALERT_MASK_0 0xC9
+
+#define ANX7447_REG_TCPC_CTRL_2 0xCD
+#define ANX7447_REG_ENABLE_VBUS_PROTECT 0x20
+
+#define ANX7447_REG_ADC_CTRL_1 0xBF
+#define ANX7447_REG_ADCFSM_EN 0x20
+
+/* Registers: SPI slave address used */
+#define ANX7447_REG_HPD_CTRL_0 0x7E
+#define ANX7447_REG_HPD_MODE 0x01
+#define ANX7447_REG_HPD_OUT 0x02
+
+#define ANX7447_REG_HPD_DEGLITCH_H 0x80
+#define ANX7447_REG_HPD_OEN 0x40
+
+#define ANX7447_REG_INTP_CTRL_0 0x9E
+
+struct anx7447_i2c_addr {
+ int tcpc_slave_addr;
+ int spi_slave_addr;
+};
+
+#define AN7447_TCPC0_I2C_ADDR 0x58
+#define AN7447_TCPC1_I2C_ADDR 0x56
+#define AN7447_TCPC2_I2C_ADDR 0x54
+#define AN7447_TCPC3_I2C_ADDR 0x52
+
+#define AN7447_SPI0_I2C_ADDR 0x7E
+#define AN7447_SPI1_I2C_ADDR 0x6E
+#define AN7447_SPI2_I2C_ADDR 0x64
+#define AN7447_SPI3_I2C_ADDR 0x62
+
+int anx7447_set_power_supply_ready(int port);
+int anx7447_power_supply_reset(int port);
+int anx7447_board_charging_enable(int port, int enable);
+
+void anx7447_hpd_mode_en(int port);
+void anx7447_hpd_output_en(int port);
+
+extern const struct tcpm_drv anx7447_tcpm_drv;
+extern const struct usb_mux_driver anx7447_usb_mux_driver;
+void anx7447_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq);
+void anx7447_tcpc_clear_hpd_status(int port);
+
+#endif /* __CROS_EC_USB_PD_TCPM_ANX7688_H */