summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/ppc/syv682x.c124
-rw-r--r--driver/ppc/syv682x.h18
-rw-r--r--include/compile_time_macros.h16
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, &regval);
- 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, &regval);
- 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, &regval);
+ 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, &regval);
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, &regval);
+ /*
+ * 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 */