diff options
-rw-r--r-- | driver/ppc/syv682x.c | 124 | ||||
-rw-r--r-- | driver/ppc/syv682x.h | 18 | ||||
-rw-r--r-- | include/compile_time_macros.h | 16 |
3 files changed, 120 insertions, 38 deletions
diff --git a/driver/ppc/syv682x.c b/driver/ppc/syv682x.c index e8f7f4e243..cd88b55b16 100644 --- a/driver/ppc/syv682x.c +++ b/driver/ppc/syv682x.c @@ -18,15 +18,15 @@ #include "usb_pd.h" #include "util.h" -#define SYV682X_FLAGS_SOURCE_ENABLED (1 << 0) +#define SYV682X_FLAGS_SOURCE_ENABLED (1 << 0) /* 0 -> CC1, 1 -> CC2 */ -#define SYV682X_FLAGS_CC_POLARITY (1 << 1) -#define SYV682X_FLAGS_VBUS_PRESENT (1 << 2) -#define SYV682X_FLAGS_OCP BIT(3) -#define SYV682X_FLAGS_OVP BIT(4) -#define SYV682X_FLAGS_TSD BIT(5) -#define SYV682X_FLAGS_RVS BIT(6) -#define SYV682X_FLAGS_VCONN_OCP BIT(7) +#define SYV682X_FLAGS_CC_POLARITY (1 << 1) +#define SYV682X_FLAGS_VBUS_PRESENT (1 << 2) +#define SYV682X_FLAGS_OCP BIT(3) +#define SYV682X_FLAGS_OVP BIT(4) +#define SYV682X_FLAGS_TSD BIT(5) +#define SYV682X_FLAGS_RVS BIT(6) +#define SYV682X_FLAGS_VCONN_OCP BIT(7) static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */ static uint8_t flags[CONFIG_USB_PD_PORT_MAX_COUNT]; @@ -95,7 +95,7 @@ static int write_reg(uint8_t port, int reg, int regval) static int syv682x_is_sourcing_vbus(int port) { - return flags[port] & SYV682X_FLAGS_SOURCE_ENABLED; + return !!(flags[port] & SYV682X_FLAGS_SOURCE_ENABLED); } static int syv682x_discharge_vbus(int port, int enable) @@ -195,6 +195,7 @@ static int syv682x_vbus_sink_enable(int port, int enable) /* Select Sink mode and turn on the channel */ regval &= ~(SYV682X_CONTROL_1_HV_DR | SYV682X_CONTROL_1_PWR_ENB); + flags[port] &= ~SYV682X_FLAGS_SOURCE_ENABLED; } else { /* * No need to change the voltage path or channel direction. But, @@ -456,11 +457,11 @@ void syv682x_interrupt(int port) syv682x_interrupt_delayed(port, 0); } -static int syv682x_init(int port) +static int syv682x_reset(int port) { int rv; - int regval; + CPRINTS("p%d: PPC SW reset", port); /* * Reset all I2C registers to default values because the SYV682x does * not provide a pin reset. The SYV682X_RST_REG bit is self-clearing. @@ -474,35 +475,56 @@ static int syv682x_init(int port) if (rv) return rv; - rv = read_reg(port, SYV682X_CONTROL_2_REG, ®val); - if (rv) - return rv; + return EC_SUCCESS; +} + +static bool syv682x_is_sink(uint8_t control_1) +{ /* - * Enable smart discharge mode. The SYV682 automatically discharges - * under the following conditions: UVLO (under voltage lockout), channel - * shutdown, over current, over voltage, thermal shutdown + * The SYV682 integrates power paths: 5V and HV (high voltage). + * The SYV682 can source either 5V or HV, but only sinks on the HV path. + * + * PD analyzer without a device connected confirms the SYV682 acts as + * a source under these conditions: + * HV_DR && !CH_SEL: source 5V + * HV_DR && CH_SEL: source 15V + * !HV_DR && !CH_SEL: source 5V + * + * The SYV682 is only a sink when !HV_DR && CH_SEL */ - regval |= SYV682X_CONTROL_2_SDSG; - rv = write_reg(port, SYV682X_CONTROL_2_REG, regval); - if (rv) - return rv; + if (!(control_1 & SYV682X_CONTROL_1_PWR_ENB) + && !(control_1 & SYV682X_CONTROL_1_HV_DR) + && (control_1 & SYV682X_CONTROL_1_CH_SEL)) + return true; - /* 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); + return false; +} + +static int syv682x_init(int port) +{ + int rv; + int regval; + int status, control_1; + + rv = read_reg(port, SYV682X_STATUS_REG, &status); if (rv) return rv; - /* Check if this if dead battery case */ - rv = read_reg(port, SYV682X_STATUS_REG, ®val); + rv = read_reg(port, SYV682X_CONTROL_1_REG, &control_1); if (rv) return rv; - if (regval & SYV682X_STATUS_VSAFE_0V) { - /* Not dead battery case, so disable channel */ + + if (!syv682x_is_sink(control_1) + || (status & SYV682X_STATUS_VSAFE_0V)) { + /* + * PPC is not configured as a sink or there is no VBUS present. + * It's safe to perform a full register reset. + */ + rv = syv682x_reset(port); + if (rv) + return rv; + + /* Disable both power paths */ rv = read_reg(port, SYV682X_CONTROL_1_REG, ®val); if (rv) return rv; @@ -511,16 +533,42 @@ static int syv682x_init(int port) if (rv) return rv; } else { - syv682x_vbus_sink_enable(port, 1); + /* Dead battery mode, or an existing PD contract is in place */ + rv = syv682x_vbus_sink_enable(port, 1); + if (rv) + return rv; } - rv = read_reg(port, SYV682X_CONTROL_4_REG, ®val); + /* + * Set Control Reg 2 to defaults, plus enable smart discharge mode. + * The SYV682 automatically discharges under the following conditions: + * UVLO (under voltage lockout), channel shutdown, over current, over + * voltage, and thermal shutdown + */ + regval = (SYV682X_OC_DELAY_10MS << SYV682X_OC_DELAY_SHIFT) + | (SYV682X_DSG_TIME_200MS << SYV682X_DSG_TIME_SHIFT) + | (SYV682X_DSG_RON_200_OHM << SYV682X_DSG_RON_SHIFT) + | SYV682X_CONTROL_2_SDSG; + rv = write_reg(port, SYV682X_CONTROL_2_REG, regval); 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; + + /* + * Always set the over voltage setting to the maximum to support + * sinking from a 20V PD charger. The common PPC code doesn't provide + * any hooks for indicating what the currently negotiated voltage is. + */ + regval = (SYV682X_OVP_23_7 << SYV682X_OVP_BIT_SHIFT); + rv = write_reg(port, SYV682X_CONTROL_3_REG, regval); + if (rv) + return rv; + + /* + * Remove Rd, connect CC1/CC2 lines to TCPC, and disable fast role + * swap. + */ + regval = SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS + | SYV682X_CONTROL_4_CC_FRS; rv = write_reg(port, SYV682X_CONTROL_4_REG, regval); if (rv) return rv; diff --git a/driver/ppc/syv682x.h b/driver/ppc/syv682x.h index ad57b814dc..b628dd268c 100644 --- a/driver/ppc/syv682x.h +++ b/driver/ppc/syv682x.h @@ -45,6 +45,24 @@ #define SYV682X_ILIM_3_30 3 /* Control Register 2 */ +#define SYV682X_OC_DELAY_MASK GENMASK(7, 6) +#define SYV682X_OC_DELAY_SHIFT 6 +#define SYV682X_OC_DELAY_1MS 0 +#define SYV682X_OC_DELAY_10MS 1 +#define SYV682X_OC_DELAY_50MS 2 +#define SYV682X_OC_DELAY_100MS 3 +#define SYV682X_DSG_TIME_MASK GENMASK(5, 4) +#define SYV682X_DSG_TIME_SHIFT 4 +#define SYV682X_DSG_TIME_50MS 0 +#define SYV682X_DSG_TIME_100MS 1 +#define SYV682X_DSG_TIME_200MS 2 +#define SYV682X_DSG_TIME_400MS 3 +#define SYV682X_DSG_RON_MASK GENMASK(3, 2) +#define SYV682X_DSG_RON_SHIFT 2 +#define SYV682X_DSG_RON_200_OHM 0 +#define SYV682X_DSG_RON_400_OHM 1 +#define SYV682X_DSG_RON_800_OHM 2 +#define SYV682X_DSG_RON_1600_OHM 3 #define SYV682X_CONTROL_2_SDSG (1 << 1) #define SYV682X_CONTROL_2_FDSG (1 << 0) diff --git a/include/compile_time_macros.h b/include/compile_time_macros.h index 30a3e901b3..b05e2569e1 100644 --- a/include/compile_time_macros.h +++ b/include/compile_time_macros.h @@ -41,4 +41,20 @@ #define BIT(nr) (1UL << (nr)) #define BIT_ULL(nr) (1ULL << (nr)) +/* + * Create a bit mask from least significant bit |l| + * to bit |h|, inclusive. + * + * Examples: + * GENMASK(31, 0) ==> 0xFF_FF_FF_FF + * GENMASK(3, 0) ==> 0x00_00_00_0F + * GENMASK(7, 4) ==> 0x00_00_00_F0 + * GENMASK(b, b) ==> BIT(b) + * + * Note that we shift after using BIT() to avoid compiler + * warnings for BIT(31+1). + */ +#define GENMASK(h, l) (((BIT(h)<<1) - 1) ^ (BIT(l) - 1)) +#define GENMASK_ULL(h, l) (((BIT_ULL(h)<<1) - 1) ^ (BIT_ULL(l) - 1)) + #endif /* __CROS_EC_COMPILE_TIME_MACROS_H */ |