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>
This commit is contained in:
Dylan Lai
2018-03-09 13:54:08 +08:00
committed by chrome-bot
parent a9c7d6b0d7
commit 82a357a385
8 changed files with 599 additions and 23 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

393
driver/tcpm/anx7447.c Normal file
View File

@@ -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 */

61
driver/tcpm/anx7447.h Normal file
View File

@@ -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 */