mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-12 19:04:59 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
393
driver/tcpm/anx7447.c
Normal 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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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 &= ~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
61
driver/tcpm/anx7447.h
Normal 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 */
|
||||
Reference in New Issue
Block a user