summaryrefslogtreecommitdiff
path: root/driver/ppc
diff options
context:
space:
mode:
authorDenis Brockus <dbrockus@chromium.org>2019-06-25 12:44:16 -0600
committerCommit Bot <commit-bot@chromium.org>2019-08-23 00:12:30 +0000
commitee8d481a027b78796ce30a48224e043fdf5f7491 (patch)
treebe23edd0a16a6548302c45da04740ba19809a40e /driver/ppc
parent220205c8480433cb72f7c738ba141d5afa6d1968 (diff)
downloadchrome-ec-ee8d481a027b78796ce30a48224e043fdf5f7491.tar.gz
Use 7bit I2C/SPI slave addresses in EC
Opt for 7bit slave addresses in EC code. If 8bit is expected by a driver, make it local and show this in the naming. Use __7b, __7bf and __8b as name extensions for i2c/spi addresses used in the EC codebase. __7b indicates a 7bit address by itself. __7bf indicates a 7bit address with optional flags attached. __8b indicates a 8bit address by itself. Allow space for 10bit addresses, even though this is not currently being used by any of our attached devices. These extensions are for verification purposes only and will be removed in the last pass of this ticket. I want to make sure the variable names reflect the type to help eliminate future 7/8/7-flags confusion. BUG=chromium:971296 BRANCH=none TEST=make buildall -j Change-Id: I2fc3d1b52ce76184492b2aaff3060f486ca45f45 Signed-off-by: Denis Brockus <dbrockus@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1699893 Reviewed-by: Jack Rosenthal <jrosenth@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1767525 Reviewed-by: Edward Hill <ecgh@chromium.org> Commit-Queue: Edward Hill <ecgh@chromium.org> Tested-by: Edward Hill <ecgh@chromium.org>
Diffstat (limited to 'driver/ppc')
-rw-r--r--driver/ppc/nx20p348x.c8
-rw-r--r--driver/ppc/nx20p348x.h13
-rw-r--r--driver/ppc/sn5s330.c92
-rw-r--r--driver/ppc/sn5s330.h10
-rw-r--r--driver/ppc/syv682x.c351
-rw-r--r--driver/ppc/syv682x.h68
6 files changed, 487 insertions, 55 deletions
diff --git a/driver/ppc/nx20p348x.c b/driver/ppc/nx20p348x.c
index a5ee9d5e31..e8d2f52563 100644
--- a/driver/ppc/nx20p348x.c
+++ b/driver/ppc/nx20p348x.c
@@ -31,16 +31,16 @@ static uint8_t flags[CONFIG_USB_PD_PORT_COUNT];
static int read_reg(uint8_t port, int reg, int *regval)
{
- return i2c_read8(ppc_chips[port].i2c_port,
- ppc_chips[port].i2c_addr,
+ return i2c_read8__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
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,
+ return i2c_write8__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
reg,
regval);
}
diff --git a/driver/ppc/nx20p348x.h b/driver/ppc/nx20p348x.h
index 52224b6a8e..e34c5a2b56 100644
--- a/driver/ppc/nx20p348x.h
+++ b/driver/ppc/nx20p348x.h
@@ -8,10 +8,15 @@
#ifndef __CROS_EC_NX20P348X_H
#define __CROS_EC_NX20P348X_H
-#define NX20P3483_ADDR0 0xE0
-#define NX20P3483_ADDR1 0xE2
-#define NX20P3483_ADDR2 0xE4
-#define NX20P3483_ADDR3 0xE6
+#define NX20P3483_ADDR0__7bf (0x70)
+#define NX20P3483_ADDR1__7bf (0x71)
+#define NX20P3483_ADDR2__7bf (0x72)
+#define NX20P3483_ADDR3__7bf (0x73)
+
+#define NX20P3481_ADDR0__7bf (0x74)
+#define NX20P3481_ADDR1__7bf (0x75)
+#define NX20P3481_ADDR2__7bf (0x76)
+#define NX20P3481_ADDR3__7bf (0x77)
/*
* This PPC hard-codes the over voltage protect of Vbus at 6.8V in dead-battery
diff --git a/driver/ppc/sn5s330.c b/driver/ppc/sn5s330.c
index ef63402b6b..13834a8bf3 100644
--- a/driver/ppc/sn5s330.c
+++ b/driver/ppc/sn5s330.c
@@ -31,16 +31,16 @@ static int source_enabled[CONFIG_USB_PD_PORT_COUNT];
static int read_reg(uint8_t port, int reg, int *regval)
{
- return i2c_read8(ppc_chips[port].i2c_port,
- ppc_chips[port].i2c_addr,
+ return i2c_read8__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
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,
+ return i2c_write8__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
reg,
regval);
}
@@ -78,12 +78,12 @@ static int sn5s330_dump(int port)
int i;
int data;
const int i2c_port = ppc_chips[port].i2c_port;
- const int i2c_addr = ppc_chips[port].i2c_addr;
+ const uint16_t i2c_addr__7bf = ppc_chips[port].i2c_addr__7bf;
/* Flush after every set otherwise console buffer may get full. */
for (i = SN5S330_FUNC_SET1; i <= SN5S330_FUNC_SET12; i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("FUNC_SET%d [%02Xh] = 0x%02x\n",
i - SN5S330_FUNC_SET1 + 1,
i,
@@ -93,7 +93,7 @@ static int sn5s330_dump(int port)
cflush();
for (i = SN5S330_INT_STATUS_REG1; i <= SN5S330_INT_STATUS_REG4; i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("INT_STATUS_REG%d [%02Xh] = 0x%02x\n",
i - SN5S330_INT_STATUS_REG1 + 1,
i,
@@ -104,7 +104,7 @@ static int sn5s330_dump(int port)
for (i = SN5S330_INT_TRIP_RISE_REG1; i <= SN5S330_INT_TRIP_RISE_REG3;
i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("INT_TRIP_RISE_REG%d [%02Xh] = 0x%02x\n",
i - SN5S330_INT_TRIP_RISE_REG1 + 1,
i,
@@ -115,7 +115,7 @@ static int sn5s330_dump(int port)
for (i = SN5S330_INT_TRIP_FALL_REG1; i <= SN5S330_INT_TRIP_FALL_REG3;
i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("INT_TRIP_FALL_REG%d [%02Xh] = 0x%02x\n",
i - SN5S330_INT_TRIP_FALL_REG1 + 1,
i,
@@ -126,7 +126,7 @@ static int sn5s330_dump(int port)
for (i = SN5S330_INT_MASK_RISE_REG1; i <= SN5S330_INT_MASK_RISE_REG3;
i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("INT_MASK_RISE_REG%d [%02Xh] = 0x%02x\n",
i - SN5S330_INT_MASK_RISE_REG1 + 1,
i,
@@ -137,7 +137,7 @@ static int sn5s330_dump(int port)
for (i = SN5S330_INT_MASK_FALL_REG1; i <= SN5S330_INT_MASK_FALL_REG3;
i++) {
- i2c_read8(i2c_port, i2c_addr, i, &data);
+ i2c_read8__7bf(i2c_port, i2c_addr__7bf, i, &data);
ccprintf("INT_MASK_FALL_REG%d [%02Xh] = 0x%02x\n",
i - SN5S330_INT_MASK_FALL_REG1 + 1,
i,
@@ -184,7 +184,7 @@ static int sn5s330_init(int port)
int retries;
int reg;
const int i2c_port = ppc_chips[port].i2c_port;
- const int i2c_addr = ppc_chips[port].i2c_addr;
+ const uint16_t i2c_addr__7bf = ppc_chips[port].i2c_addr__7bf;
#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
/* Set the sourcing current limit value. */
@@ -211,8 +211,8 @@ static int sn5s330_init(int port)
*/
retries = 0;
do {
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET1,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET1, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET1! Retrying..",
port);
@@ -225,21 +225,24 @@ static int sn5s330_init(int port)
/* Set Vbus OVP threshold to ~22.325V. */
regval = 0x37;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET5, regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET5, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET5!", port);
return status;
}
/* Set Vbus UVP threshold to ~2.75V. */
- status = i2c_read8(i2c_port, i2c_addr, SN5S330_FUNC_SET6, &regval);
+ status = i2c_read8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET6, &regval);
if (status) {
CPRINTS("ppc p%d: Failed to read FUNC_SET6!", port);
return status;
}
regval &= ~0x3F;
regval |= 1;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET6, regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET6, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write FUNC_SET6!", port);
return status;
@@ -247,7 +250,8 @@ static int sn5s330_init(int port)
/* Enable SBU Fets and set PP2 current limit to ~3A. */
regval = SN5S330_SBU_EN | 0x8;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET2, regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET2, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET2!", port);
return status;
@@ -266,7 +270,8 @@ static int sn5s330_init(int port)
* low voltage protection).
*/
regval = SN5S330_OVP_EN_CC | SN5S330_PP2_CONFIG | SN5S330_CONFIG_UVP;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET9, regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET9, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET9!", port);
return status;
@@ -274,8 +279,8 @@ static int sn5s330_init(int port)
/* Set analog current limit delay to 200 us for both PP1 & PP2. */
regval = (PPX_ILIM_DEGLITCH_0_US_200 << 3) | PPX_ILIM_DEGLITCH_0_US_200;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET11,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET11, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET11", port);
return status;
@@ -288,14 +293,16 @@ static int sn5s330_init(int port)
* reset default (20 us).
*/
regval = 0;
- status = i2c_read8(i2c_port, i2c_addr, SN5S330_FUNC_SET8, &regval);
+ status = i2c_read8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET8, &regval);
if (status) {
CPRINTS("ppc p%d: Failed to read FUNC_SET8!", port);
return status;
}
regval &= ~SN5S330_VCONN_DEGLITCH_MASK;
regval |= SN5S330_VCONN_DEGLITCH_640_US;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET8, regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_FUNC_SET8, regval);
if (status) {
CPRINTS("ppc p%d: Failed to set FUNC_SET8!", port);
return status;
@@ -347,8 +354,8 @@ static int sn5s330_init(int port)
* is checked below.
*/
regval = SN5S330_DIG_RES | SN5S330_VSAFE0V_MASK;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_STATUS_REG4,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_STATUS_REG4, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_STATUS_REG4!", port);
return status;
@@ -365,30 +372,30 @@ static int sn5s330_init(int port)
*/
regval = ~SN5S330_ILIM_PP1_MASK;
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_RISE_REG1,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_RISE_REG1, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_RISE1!", port);
return status;
}
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_FALL_REG1,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_FALL_REG1, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_FALL1!", port);
return status;
}
/* Now mask all the other interrupts. */
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_RISE_REG2,
- 0xFF);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_RISE_REG2, 0xFF);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_RISE2!", port);
return status;
}
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_FALL_REG2,
- 0xFF);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_FALL_REG2, 0xFF);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_FALL2!", port);
return status;
@@ -401,15 +408,15 @@ static int sn5s330_init(int port)
regval = 0xFF;
#endif /* CONFIG_USB_PD_VBUS_DETECT_PPC && CONFIG_USB_CHARGER */
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_RISE_REG3,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_RISE_REG3, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_RISE3!", port);
return status;
}
- status = i2c_write8(i2c_port, i2c_addr, SN5S330_INT_MASK_FALL_REG3,
- regval);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_MASK_FALL_REG3, regval);
if (status) {
CPRINTS("ppc p%d: Failed to write INT_MASK_FALL3!", port);
return status;
@@ -419,7 +426,8 @@ static int sn5s330_init(int port)
for (reg = SN5S330_INT_TRIP_RISE_REG1;
reg <= SN5S330_INT_TRIP_FALL_REG3;
reg++) {
- status = i2c_write8(i2c_port, i2c_addr, reg, 0xFF);
+ status = i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ reg, 0xFF);
if (status) {
CPRINTS("ppc p%d: Failed to write reg 0x%2x!", port);
return status;
@@ -431,8 +439,8 @@ static int sn5s330_init(int port)
* For PP2, check to see if we booted in dead battery mode. If we
* booted in dead battery mode, the PP2 FET will already be enabled.
*/
- status = i2c_read8(i2c_port, i2c_addr, SN5S330_INT_STATUS_REG4,
- &regval);
+ status = i2c_read8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_STATUS_REG4, &regval);
if (status) {
CPRINTS("ppc p%d: Failed to read INT_STATUS_REG4!", port);
return status;
@@ -443,8 +451,8 @@ static int sn5s330_init(int port)
* Clear the bit by writing 1 and keep vSafe0V_MASK
* unchanged.
*/
- i2c_write8(i2c_port, i2c_addr, SN5S330_INT_STATUS_REG4,
- regval);
+ i2c_write8__7bf(i2c_port, i2c_addr__7bf,
+ SN5S330_INT_STATUS_REG4, regval);
/* Turn on PP2 FET. */
status = sn5s330_pp_fet_enable(port, SN5S330_PP2, 1);
diff --git a/driver/ppc/sn5s330.h b/driver/ppc/sn5s330.h
index c5b16ea73b..a153b9d25a 100644
--- a/driver/ppc/sn5s330.h
+++ b/driver/ppc/sn5s330.h
@@ -12,7 +12,7 @@
struct sn5s330_config {
uint8_t i2c_port;
- uint8_t i2c_addr;
+ uint8_t i2c_addr__7bf;
};
extern const struct sn5s330_config sn5s330_chips[];
@@ -25,10 +25,10 @@ enum sn5s330_pp_idx {
SN5S330_PP_COUNT,
};
-#define SN5S330_ADDR0 0x80
-#define SN5S330_ADDR1 0x82
-#define SN5S330_ADDR2 0x84
-#define SN5S330_ADDR3 0x86
+#define SN5S330_ADDR0__7bf (0x40)
+#define SN5S330_ADDR1__7bf (0x41)
+#define SN5S330_ADDR2__7bf (0x42)
+#define SN5S330_ADDR3__7bf (0x43)
#define SN5S330_FUNC_SET1 0x50
#define SN5S330_FUNC_SET2 0x51
diff --git a/driver/ppc/syv682x.c b/driver/ppc/syv682x.c
new file mode 100644
index 0000000000..a8f5c146bb
--- /dev/null
+++ b/driver/ppc/syv682x.c
@@ -0,0 +1,351 @@
+/* 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_charge.h"
+#include "usb_pd_tcpm.h"
+#include "usbc_ppc.h"
+#include "util.h"
+
+#define SYV682X_FLAGS_SOURCE_ENABLED BIT(0)
+/* 0 -> CC1, 1 -> CC2 */
+#define SYV682X_FLAGS_CC_POLARITY BIT(1)
+#define SYV682X_FLAGS_VBUS_PRESENT BIT(2)
+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__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
+ reg,
+ regval);
+}
+
+static int write_reg(uint8_t port, int reg, int regval)
+{
+ return i2c_write8__7bf(ppc_chips[port].i2c_port,
+ ppc_chips[port].i2c_addr__7bf,
+ 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, &regval);
+ 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;
+#ifdef CONFIG_USB_CHARGER
+ if (!!(flags[port] & SYV682X_FLAGS_VBUS_PRESENT) != vbus)
+ usb_charger_vbus_change(port, vbus);
+
+ if (vbus)
+ flags[port] |= SYV682X_FLAGS_VBUS_PRESENT;
+ else
+ flags[port] &= ~SYV682X_FLAGS_VBUS_PRESENT;
+#endif
+
+ 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, &regval);
+ 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 if (flags[port] & SYV682X_FLAGS_SOURCE_ENABLED) {
+ /*
+ * For the disable case, make sure that VBUS was being sourced
+ * prior to disabling the source path. Because the source/sink
+ * paths can't be independently disabled, and this function will
+ * get called as part of USB PD initialization, setting the
+ * PWR_ENB always can lead to broken dead battery behavior.
+ *
+ * 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;
+
+#if defined(CONFIG_USB_CHARGER) && defined(CONFIG_USB_PD_VBUS_DETECT_PPC)
+ /*
+ * Since the VBUS state could be changing here, need to wake the
+ * USB_CHG_N task so that BC 1.2 detection will be triggered.
+ */
+ usb_charger_vbus_change(port, enable);
+#endif
+
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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__7bf = ppc_chips[port].i2c_addr__7bf;
+
+ for (reg_addr = SYV682X_STATUS_REG; reg_addr <= SYV682X_CONTROL_4_REG;
+ reg_addr++) {
+ rv = i2c_read8__7bf(i2c_port, i2c_addr__7bf, 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, &regval);
+ 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, &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);
+ if (rv)
+ return rv;
+
+ /* Check if this if dead battery case */
+ rv = read_reg(port, SYV682X_CONTROL_1_REG, &regval);
+ 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, &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;
+ 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..f355fe1cf9
--- /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
+
+/* I2C addresses */
+#define SYV682X_ADDR0__7bf 0x40
+#define SYV682X_ADDR1__7bf 0x41
+#define SYV682X_ADDR2__7bf 0x42
+#define SYV682x_ADDR3__7bf 0x43
+
+/* 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 BIT(1)
+#define SYV682X_STATUS_VSAFE_0V BIT(0)
+
+/* Control Register 1 */
+#define SYV682X_CONTROL_1_CH_SEL BIT(1)
+#define SYV682X_CONTROL_1_HV_DR BIT(2)
+#define SYV682X_CONTROL_1_PWR_ENB BIT(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 BIT(1)
+#define SYV682X_CONTROL_2_FDSG BIT(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 BIT(7)
+#define SYV682X_CONTROL_4_CC2_BPS BIT(6)
+#define SYV682X_CONTROL_4_VCONN1 BIT(5)
+#define SYV682X_CONTROL_4_VCONN2 BIT(4)
+#define SYV682X_CONTROL_4_VBAT_OVP BIT(3)
+#define SYV682X_CONTROL_4_VCONN_OCP BIT(2)
+#define SYV682X_CONTROL_4_CC_FRS BIT(1)
+
+struct ppc_drv;
+extern const struct ppc_drv syv682x_drv;
+
+#endif /* defined(__CROS_EC_SYV682X_H) */