diff options
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/ppc/syv682x.c | 326 | ||||
-rw-r--r-- | driver/ppc/syv682x.h | 68 | ||||
-rw-r--r-- | include/config.h | 1 |
4 files changed, 396 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index ad3283ce3c..f95d80748d 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -126,6 +126,7 @@ driver-$(CONFIG_USB_MUX_VIRTUAL)+=usb_mux_virtual.o # Type-C Power Path Controllers (PPC) driver-$(CONFIG_USBC_PPC_SN5S330)+=ppc/sn5s330.o +driver-$(CONFIG_USBC_PPC_SYV682X)+=ppc/syv682x.o driver-$(CONFIG_USBC_PPC_NX20P3483)+=ppc/nx20p348x.o # video converters diff --git a/driver/ppc/syv682x.c b/driver/ppc/syv682x.c new file mode 100644 index 0000000000..c15bb3d19b --- /dev/null +++ b/driver/ppc/syv682x.c @@ -0,0 +1,326 @@ +/* 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. + */ + +/* Silergy SYV682x USB-C Power Path Controller */ +#include "common.h" +#include "console.h" +#include "driver/ppc/syv682x.h" +#include "i2c.h" +#include "usb_pd_tcpm.h" +#include "usbc_ppc.h" +#include "util.h" + +#define SYV682X_FLAGS_SOURCE_ENABLED (1 << 0) +/* 0 -> CC1, 1 -> CC2 */ +#define SYV682X_FLAGS_CC_POLARITY (1 << 1) +static uint8_t flags[CONFIG_USB_PD_PORT_COUNT]; + +#define SYV682X_VBUS_DET_THRESH_MV 4000 + +static int read_reg(uint8_t port, int reg, int *regval) +{ + return i2c_read8(ppc_chips[port].i2c_port, + ppc_chips[port].i2c_addr, + reg, + regval); +} + +static int write_reg(uint8_t port, int reg, int regval) +{ + return i2c_write8(ppc_chips[port].i2c_port, + ppc_chips[port].i2c_addr, + reg, + regval); +} + +static int syv682x_is_sourcing_vbus(int port) +{ + return flags[port] & SYV682X_FLAGS_SOURCE_ENABLED; +} + +static int syv682x_vbus_sink_enable(int port, int enable) +{ + int regval; + int rv; + + /* + * For sink mode need to make sure high voltage power path is connected + * and sink mode is selected. + */ + rv = read_reg(port, SYV682X_CONTROL_1_REG, ®val); + if (rv) + return rv; + + if (enable) { + /* Select high voltage path */ + regval |= SYV682X_CONTROL_1_CH_SEL; + /* Select Sink mode and turn on the channel */ + regval &= ~(SYV682X_CONTROL_1_HV_DR | + SYV682X_CONTROL_1_PWR_ENB); + } else { + /* + * No need to change the voltage path or channel direction. But, + * turn both paths off. + */ + regval |= SYV682X_CONTROL_1_PWR_ENB; + } + + return write_reg(port, SYV682X_CONTROL_1_REG, regval); +} + +#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC +static int syv682x_is_vbus_present(int port) +{ + int val; + int vbus = 0; + + /* + * TODO (b/112661747): This PPC doesn't fully support VBUS detection. + * It can detect both VSafe5V and VSafe0V. This function is intended + * here until detecting VBUS differently per channel is supported. + */ + if (read_reg(port, SYV682X_STATUS_REG, &val)) + return vbus; + + /* + * VBUS is considered present if VSafe5V is detected or neither VSafe5V + * or VSafe0V is detected, which implies VBUS > 5V. + */ + if ((val & SYV682X_STATUS_VSAFE_5V) || + !(val & (SYV682X_STATUS_VSAFE_5V | SYV682X_STATUS_VSAFE_0V))) + vbus = 1; + + return vbus; +} +#endif + +static int syv682x_vbus_source_enable(int port, int enable) +{ + int regval; + int rv; + + /* + * For source mode need to make sure 5V power path is connected + * and source mode is selected. + */ + rv = read_reg(port, SYV682X_CONTROL_1_REG, ®val); + if (rv) + return rv; + + if (enable) { + /* Select 5V path and turn on channel */ + regval &= ~(SYV682X_CONTROL_1_CH_SEL | + SYV682X_CONTROL_1_PWR_ENB); + /* Disable HV Sink path */ + regval |= SYV682X_CONTROL_1_HV_DR; + } else { + /* + * No need to change the voltage path or channel direction. But, + * turn both paths off. + */ + regval |= SYV682X_CONTROL_1_PWR_ENB; + } + + rv = write_reg(port, SYV682X_CONTROL_1_REG, regval); + if (rv) + return rv; + + if (enable) + flags[port] |= SYV682X_FLAGS_SOURCE_ENABLED; + else + flags[port] &= ~SYV682X_FLAGS_SOURCE_ENABLED; + + return EC_SUCCESS; +} + +static int syv682x_set_vbus_source_current_limit(int port, + enum tcpc_rp_value rp) +{ + int rv; + int limit; + int regval; + + rv = read_reg(port, SYV682X_CONTROL_1_REG, ®val); + if (rv) + return rv; + + /* We need buffer room for all current values. */ + switch (rp) { + case TYPEC_RP_3A0: + limit = SYV682X_ILIM_3_30; + break; + + case TYPEC_RP_1A5: + limit = SYV682X_ILIM_1_75; + break; + + case TYPEC_RP_USB: + default: + /* 1.25 A is lowest current limit setting for SVY682 */ + limit = SYV682X_ILIM_1_25; + break; + }; + + regval &= ~SYV682X_ILIM_MASK; + regval |= (limit << SYV682X_ILIM_BIT_SHIFT); + return write_reg(port, SYV682X_CONTROL_1_REG, regval); +} + +static int syv682x_discharge_vbus(int port, int enable) +{ + int regval; + int rv; + + rv = read_reg(port, SYV682X_CONTROL_2_REG, ®val); + if (rv) + return rv; + + if (enable) + regval |= SYV682X_CONTROL_2_FDSG; + else + regval &= ~SYV682X_CONTROL_2_FDSG; + + return write_reg(port, SYV682X_CONTROL_2_REG, regval); +} + +#ifdef CONFIG_USBC_PPC_POLARITY +static int syv682x_set_polarity(int port, int polarity) +{ + /* + * The SYV682x does not explicitly set CC polarity. However, if VCONN is + * being used then the polarity is required to connect 5V to the correct + * CC line. So this function saves the CC polarity as a bit in the flags + * variable so VCONN is connected the correct CC line. The flag bit + * being set means polarity = CC2, the flag bit clear means + * polarity = CC1. + */ + if (polarity) + flags[port] |= SYV682X_FLAGS_CC_POLARITY; + else + flags[port] &= ~SYV682X_FLAGS_CC_POLARITY; + + return EC_SUCCESS; +} +#endif + +#ifdef CONFIG_USBC_PPC_VCONN +static int syv682x_set_vconn(int port, int enable) +{ + int regval; + int rv; + + rv = read_reg(port, SYV682X_CONTROL_4_REG, ®val); + if (rv) + return rv; + + if (enable) + regval |= flags[port] & SYV682X_FLAGS_CC_POLARITY ? + SYV682X_CONTROL_4_VCONN1 : SYV682X_CONTROL_4_VCONN2; + else + regval &= ~(SYV682X_CONTROL_4_VCONN2 | + SYV682X_CONTROL_4_VCONN1); + + return write_reg(port, SYV682X_CONTROL_4_REG, regval); +} +#endif + +#ifdef CONFIG_CMD_PPC_DUMP +static int syv682x_dump(int port) +{ + int reg_addr; + int data; + int rv; + const int i2c_port = ppc_chips[port].i2c_port; + const int i2c_addr = ppc_chips[port].i2c_addr; + + for (reg_addr = SYV682X_STATUS_REG; reg_addr <= SYV682X_CONTROL_4_REG; + reg_addr++) { + rv = i2c_read8(i2c_port, i2c_addr, reg_addr, &data); + if (rv) + ccprintf("ppc_syv682[p%d]: Failed to read reg 0x%02x\n", + port, reg_addr); + else + ccprintf("ppc_syv682[p%d]: reg 0x%02x = 0x%02x\n", + port, reg_addr, data); + } + + cflush(); + + return EC_SUCCESS; +} +#endif /* defined(CONFIG_CMD_PPC_DUMP) */ + +static int syv682x_init(int port) +{ + int rv; + int regval; + + /* Set VBUS discharge to manual mode */ + rv = read_reg(port, SYV682X_CONTROL_2_REG, ®val); + if (rv) + return rv; + regval &= ~SYV682X_CONTROL_2_SDSG; + rv = write_reg(port, SYV682X_CONTROL_2_REG, regval); + if (rv) + return rv; + + /* Select max voltage for OVP */ + rv = read_reg(port, SYV682X_CONTROL_3_REG, ®val); + if (rv) + return rv; + regval &= ~SYV682X_OVP_MASK; + regval |= (SYV682X_OVP_23_7 << SYV682X_OVP_BIT_SHIFT); + rv = write_reg(port, SYV682X_CONTROL_3_REG, regval); + if (rv) + return rv; + + /* Check if this if dead battery case */ + rv = read_reg(port, SYV682X_CONTROL_1_REG, ®val); + if (rv) + return rv; + if (regval & SYV682X_STATUS_VSAFE_0V) { + /* Not dead battery case, so disable channel */ + regval |= SYV682X_CONTROL_1_PWR_ENB; + rv = write_reg(port, SYV682X_CONTROL_1_REG, regval); + if (rv) + return rv; + } else { + syv682x_vbus_sink_enable(port, 1); + } + + rv = read_reg(port, SYV682X_CONTROL_4_REG, ®val); + if (rv) + return rv; + /* Remove Rd and connect CC1/CC2 lines to TCPC */ + regval |= SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS; + /* Disable Fast Role Swap (FRS) */ + regval |= SYV682X_CONTROL_4_CC_FRS; + rv = write_reg(port, SYV682X_CONTROL_4_REG, regval); + if (rv) + return rv; + + return EC_SUCCESS; +} + +const struct ppc_drv syv682x_drv = { + .init = &syv682x_init, + .is_sourcing_vbus = &syv682x_is_sourcing_vbus, + .vbus_sink_enable = &syv682x_vbus_sink_enable, + .vbus_source_enable = &syv682x_vbus_source_enable, +#ifdef CONFIG_CMD_PPC_DUMP + .reg_dump = &syv682x_dump, +#endif /* defined(CONFIG_CMD_PPC_DUMP) */ +#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC + .is_vbus_present = &syv682x_is_vbus_present, +#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ + .set_vbus_source_current_limit = &syv682x_set_vbus_source_current_limit, + .discharge_vbus = &syv682x_discharge_vbus, +#ifdef CONFIG_USBC_PPC_POLARITY + .set_polarity = &syv682x_set_polarity, +#endif +#ifdef CONFIG_USBC_PPC_VCONN + .set_vconn = &syv682x_set_vconn, +#endif +}; diff --git a/driver/ppc/syv682x.h b/driver/ppc/syv682x.h new file mode 100644 index 0000000000..5188a75b79 --- /dev/null +++ b/driver/ppc/syv682x.h @@ -0,0 +1,68 @@ +/* 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. + */ + +/* Silergy SYV682x Type-C Power Path Controller */ + +#ifndef __CROS_EC_SYV682X_H +#define __CROS_EC_SYV682X_H + +/* 8 bit I2C addresses */ +#define SYV682X_ADDR0 0x80 +#define SYV682X_ADDR1 0x82 +#define SYV682X_ADDR2 0x84 +#define SYV682x_ADDR3 0x86 + +/* SYV682x register addresses */ +#define SYV682X_STATUS_REG 0x00 +#define SYV682X_CONTROL_1_REG 0x01 +#define SYV682X_CONTROL_2_REG 0x02 +#define SYV682X_CONTROL_3_REG 0x03 +#define SYV682X_CONTROL_4_REG 0x04 + +/* Status Register */ +#define SYV682X_STATUS_VSAFE_5V (1 << 1) +#define SYV682X_STATUS_VSAFE_0V (1 << 0) + +/* Control Register 1 */ +#define SYV682X_CONTROL_1_CH_SEL (1 << 1) +#define SYV682X_CONTROL_1_HV_DR (1 << 2) +#define SYV682X_CONTROL_1_PWR_ENB (1 << 7) + +#define SYV682X_ILIM_MASK 0x18 +#define SYV682X_ILIM_BIT_SHIFT 3 +#define SYV682X_ILIM_1_25 0 +#define SYV682X_ILIM_1_75 1 +#define SYV682X_ILIM_2_25 2 +#define SYV682X_ILIM_3_30 3 + +/* Control Register 2 */ +#define SYV682X_CONTROL_2_SDSG (1 << 1) +#define SYV682X_CONTROL_2_FDSG (1 << 0) + +/* Control Register 3 */ +#define SYV682X_OVP_MASK 0x70 +#define SYV682X_OVP_BIT_SHIFT 4 +#define SYV682X_OVP_06_0 0 +#define SYV682X_OVP_08_0 1 +#define SYV682X_OVP_11_1 2 +#define SYV682X_OVP_12_1 3 +#define SYV682X_OVP_14_2 4 +#define SYV682X_OVP_17_9 5 +#define SYV682X_OVP_21_6 6 +#define SYV682X_OVP_23_7 7 + +/* Control Register 4 */ +#define SYV682X_CONTROL_4_CC1_BPS (1 << 7) +#define SYV682X_CONTROL_4_CC2_BPS (1 << 6) +#define SYV682X_CONTROL_4_VCONN1 (1 << 5) +#define SYV682X_CONTROL_4_VCONN2 (1 << 4) +#define SYV682X_CONTROL_4_VBAT_OVP (1 << 3) +#define SYV682X_CONTROL_4_VCONN_OCP (1 << 2) +#define SYV682X_CONTROL_4_CC_FRS (1 << 1) + +struct ppc_drv; +extern const struct ppc_drv syv682x_drv; + +#endif /* defined(__CROS_EC_SYV682X_H) */ diff --git a/include/config.h b/include/config.h index 261628c996..0aedd97799 100644 --- a/include/config.h +++ b/include/config.h @@ -3252,6 +3252,7 @@ /* USB Type-C Power Path Controllers (PPC) */ #undef CONFIG_USBC_PPC_NX20P3483 #undef CONFIG_USBC_PPC_SN5S330 +#undef CONFIG_USBC_PPC_SYV682X /* PPC is capable of providing VCONN */ #undef CONFIG_USBC_PPC_VCONN |