summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAseda Aboagye <aaboagye@google.com>2017-10-16 14:36:42 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-10-23 04:28:24 -0700
commitdf3820165f8dd8bfb4b0a30ae1ac14372610cae8 (patch)
treeb772f28220126119e59ef3a7eb50b078d0fafffd
parentddaee8e666f37ec37f3db392a91b6eeca018afd7 (diff)
downloadchrome-ec-df3820165f8dd8bfb4b0a30ae1ac14372610cae8.tar.gz
driver: ppc: Add support for TI SN5S330.
This commit adds a basic driver for the TI SN5S330. This driver just sets up the IC and provides an API to turn on or off the PP2 FET. BUG=b:67663166, b:67663124 BRANCH=None TEST=Enable code for zoombini; Flash a board which has the SN5S330 stuffed; Verify that we're able to perform PD negotiation and negotiate all the way up to 20V. TEST=Boot only on AC. sysjump to RW, verify that board does not brownout. Change-Id: I9c147ee8465eed878843cf902db301d62e8f627e Signed-off-by: Aseda Aboagye <aaboagye@google.com> Reviewed-on: https://chromium-review.googlesource.com/722104 Commit-Ready: Aseda Aboagye <aaboagye@chromium.org> Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--driver/build.mk3
-rw-r--r--driver/ppc/sn5s330.c305
-rw-r--r--driver/ppc/sn5s330.h117
-rw-r--r--include/config.h4
4 files changed, 429 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 932d7f9888..21579d44ae 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -104,6 +104,9 @@ driver-$(CONFIG_USB_MUX_PS8740)+=usb_mux_ps874x.o
driver-$(CONFIG_USB_MUX_PS8743)+=usb_mux_ps874x.o
driver-$(CONFIG_USB_MUX_VIRTUAL)+=usb_mux_virtual.o
+# Type-C Power Path Controllers (PPC)
+driver-$(CONFIG_USBC_PPC_SN5S330)+=ppc/sn5s330.o
+
# Firmware Update
driver-$(CONFIG_SB_FIRMWARE_UPDATE)+=battery/sb_fw_update.o
diff --git a/driver/ppc/sn5s330.c b/driver/ppc/sn5s330.c
new file mode 100644
index 0000000000..81efb290b8
--- /dev/null
+++ b/driver/ppc/sn5s330.c
@@ -0,0 +1,305 @@
+/* Copyright 2017 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.
+ */
+
+/* TI SN5S330 USB-C Power Path Controller */
+
+/*
+ * PP1 : Sourcing power path.
+ * PP2 : Sinking power path.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "driver/ppc/sn5s330.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "system.h"
+#include "timer.h"
+#include "util.h"
+
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
+
+#ifdef CONFIG_CMD_PPC_DUMP
+static int command_sn5s330_dump(int argc, char **argv)
+{
+ int i;
+ int data;
+ int chip_idx;
+ int port;
+ int addr;
+
+ if (argc < 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ chip_idx = atoi(argv[1]);
+ if (chip_idx >= sn5s330_cnt)
+ return EC_ERROR_PARAM1;
+
+ port = sn5s330_chips[chip_idx].i2c_port;
+ addr = sn5s330_chips[chip_idx].i2c_addr;
+
+ for (i = SN5S330_FUNC_SET1; i <= SN5S330_FUNC_SET12; i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("FUNC_SET%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_FUNC_SET1 + 1,
+ i,
+ data);
+ }
+
+ for (i = SN5S330_INT_STATUS_REG1; i <= SN5S330_INT_STATUS_REG4; i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("INT_STATUS_REG%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_INT_STATUS_REG1 + 1,
+ i,
+ data);
+ }
+
+ for (i = SN5S330_INT_TRIP_RISE_REG1; i <= SN5S330_INT_TRIP_RISE_REG3;
+ i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("INT_TRIP_RISE_REG%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_INT_TRIP_RISE_REG1 + 1,
+ i,
+ data);
+ }
+
+ for (i = SN5S330_INT_TRIP_FALL_REG1; i <= SN5S330_INT_TRIP_FALL_REG3;
+ i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("INT_TRIP_FALL_REG%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_INT_TRIP_FALL_REG1 + 1,
+ i,
+ data);
+ }
+
+ for (i = SN5S330_INT_MASK_RISE_REG1; i <= SN5S330_INT_MASK_RISE_REG3;
+ i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("INT_MASK_RISE_REG%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_INT_MASK_RISE_REG1 + 1,
+ i,
+ data);
+ }
+
+ for (i = SN5S330_INT_MASK_FALL_REG1; i <= SN5S330_INT_MASK_FALL_REG3;
+ i++) {
+ i2c_read8(port, addr, i, &data);
+ ccprintf("INT_MASK_FALL_REG%d [%02Xh] = 0x%02x\n",
+ i - SN5S330_INT_MASK_FALL_REG1 + 1,
+ i,
+ data);
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ppc_dump, command_sn5s330_dump,
+ "<Type-C port>", "dump the SN5S330 regs");
+#endif /* defined(CONFIG_CMD_PPC_DUMP) */
+
+int sn5s330_pp_fet_enable(uint8_t chip_idx, enum sn5s330_pp_idx pp, int enable)
+{
+ int regval;
+ int status;
+ int pp_bit;
+ int port;
+ int addr;
+
+ if (pp == SN5S330_PP1) {
+ pp_bit = SN5S330_PP1_EN;
+ } else if (pp == SN5S330_PP2) {
+ pp_bit = SN5S330_PP2_EN;
+ } else {
+ CPRINTF("bad PP idx(%d)!", pp);
+ return EC_ERROR_INVAL;
+ }
+
+ port = sn5s330_chips[chip_idx].i2c_port;
+ addr = sn5s330_chips[chip_idx].i2c_addr;
+
+ status = i2c_read8(port, addr, SN5S330_FUNC_SET3, &regval);
+ if (status) {
+ CPRINTS("Failed to read FUNC_SET3!");
+ return status;
+ }
+
+ if (enable)
+ regval |= pp_bit;
+ else
+ regval &= ~pp_bit;
+
+ status = i2c_write8(port, addr, SN5S330_FUNC_SET3, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET3!");
+ return status;
+ }
+
+ return EC_SUCCESS;
+}
+
+static int init_sn5s330(int idx)
+{
+ int regval;
+ int status;
+ int retries;
+ int i2c_port;
+ int i2c_addr;
+
+ i2c_port = sn5s330_chips[idx].i2c_port;
+ i2c_addr = sn5s330_chips[idx].i2c_addr;
+
+ /* Set the sourcing current limit value. */
+#if defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) && \
+ (CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT == TYPEC_RP_3A0)
+ /* Set current limit to ~3A. */
+ regval = SN5S330_ILIM_3_06;
+#else
+ /* Set current limit to ~1.5A. */
+ regval = SN5S330_ILIM_1_62;
+#endif
+
+ /*
+ * It seems that sometimes setting the FUNC_SET1 register fails
+ * initially. Therefore, we'll retry a couple of times.
+ */
+ retries = 0;
+ do {
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET1,
+ regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET1! Retrying...");
+ retries++;
+ msleep(1);
+ } else {
+ break;
+ }
+ } while (retries < 10);
+
+ /* Set Vbus OVP threshold to ~22.325V. */
+ regval = 0x37;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET5, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET5!");
+ return status;
+ }
+
+ /* Set Vbus UVP threshold to ~2.75V. */
+ status = i2c_read8(i2c_port, i2c_addr, SN5S330_FUNC_SET6, &regval);
+ if (status) {
+ CPRINTS("Failed to read FUNC_SET6!");
+ return status;
+ }
+ regval &= ~0x3F;
+ regval |= 1;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET6, regval);
+ if (status) {
+ CPRINTS("Failed to write FUNC_SET6!");
+ return status;
+ }
+
+ /* Enable SBU Fets and set PP2 current limit to ~3A. */
+ regval = SN5S330_SBU_EN | 0xf;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET2, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET2!");
+ return status;
+ }
+
+ /* TODO(aaboagye): What about Vconn */
+
+ /*
+ * Indicate we are using PP2 configuration 2 and enable OVP comparator
+ * for CC lines.
+ */
+ regval = SN5S330_OVP_EN_CC | SN5S330_PP2_CONFIG;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET9, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET9!");
+ return status;
+ }
+
+ /* 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);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET11");
+ return status;
+ }
+
+ /* Turn off dead battery resistors and turn on CC FETs. */
+ status = i2c_read8(i2c_port, i2c_addr, SN5S330_FUNC_SET4, &regval);
+ if (status) {
+ CPRINTS("Failed to read FUNC_SET4!");
+ return status;
+ }
+ regval |= SN5S330_CC_EN;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET4, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET4!");
+ return status;
+ }
+
+ /* Set ideal diode mode for both PP1 and PP2. */
+ status = i2c_read8(i2c_port, i2c_addr, SN5S330_FUNC_SET3, &regval);
+ if (status) {
+ CPRINTS("Failed to read FUNC_SET3!");
+ return status;
+ }
+ regval |= SN5S330_SET_RCP_MODE_PP1 | SN5S330_SET_RCP_MODE_PP2;
+ status = i2c_write8(i2c_port, i2c_addr, SN5S330_FUNC_SET3, regval);
+ if (status) {
+ CPRINTS("Failed to set FUNC_SET3!");
+ return status;
+ }
+
+ /* Turn off PP1 FET. */
+ status = sn5s330_pp_fet_enable(idx, SN5S330_PP1, 0);
+ if (status) {
+ CPRINTS("Failed to turn off PP1 FET!");
+ }
+
+ /* Don't touch the PP2 FET yet if we're sysjumping. */
+ if (system_jumped_to_this_image())
+ return EC_SUCCESS;
+
+ /* For PP2, check to see if we booted in dead battery mode. */
+ status = i2c_read8(i2c_port, i2c_addr, SN5S330_INT_STATUS_REG4,
+ &regval);
+ if (status) {
+ CPRINTS("Failed to read INT_STATUS_REG4!");
+ return status;
+ }
+
+ if (regval & SN5S330_DB_BOOT) {
+ /* Clear the bit. */
+ i2c_write8(i2c_port, i2c_addr, SN5S330_INT_STATUS_REG4,
+ SN5S330_DB_BOOT);
+
+ /* Turn on PP2 FET. */
+ status = sn5s330_pp_fet_enable(idx, SN5S330_PP2, 1);
+ if (status) {
+ CPRINTS("Failed to turn on PP2 FET!");
+ return status;
+ }
+ }
+
+ return EC_SUCCESS;
+}
+
+static void sn5s330_init(void)
+{
+ int i;
+ int rv;
+
+ for (i = 0; i < sn5s330_cnt; i++) {
+ rv = init_sn5s330(i);
+ if (!rv)
+ CPRINTS("C%d: SN5S330 init done.", i);
+ else
+ CPRINTS("C%d: SN5S330 init failed! (%d)", i, rv);
+ }
+}
+DECLARE_HOOK(HOOK_INIT, sn5s330_init, HOOK_PRIO_LAST);
diff --git a/driver/ppc/sn5s330.h b/driver/ppc/sn5s330.h
new file mode 100644
index 0000000000..c4aa5fb81b
--- /dev/null
+++ b/driver/ppc/sn5s330.h
@@ -0,0 +1,117 @@
+/* Copyright 2017 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.
+ */
+
+/* TI SN5S330 Type-C Power Path Controller */
+
+#ifndef __CROS_EC_SN5S330_H
+#define __CROS_EC_SN5S330_H
+
+#include "common.h"
+
+struct sn5s330_config {
+ uint8_t i2c_port;
+ uint8_t i2c_addr;
+};
+
+extern const struct sn5s330_config sn5s330_chips[];
+extern const unsigned int sn5s330_cnt;
+
+/* Power Path Indices */
+enum sn5s330_pp_idx {
+ SN5S330_PP1,
+ SN5S330_PP2,
+ SN5S330_PP_COUNT,
+};
+
+#define SN5S330_ADDR0 0x80
+#define SN5S330_ADDR1 0x82
+#define SN5S330_ADDR2 0x84
+#define SN5S330_ADDR3 0x86
+
+#define SN5S330_FUNC_SET1 0x50
+#define SN5S330_FUNC_SET2 0x51
+#define SN5S330_FUNC_SET3 0x52
+#define SN5S330_FUNC_SET4 0x53
+#define SN5S330_FUNC_SET5 0x54
+#define SN5S330_FUNC_SET6 0x55
+#define SN5S330_FUNC_SET7 0x56
+#define SN5S330_FUNC_SET8 0x57
+#define SN5S330_FUNC_SET9 0x58
+#define SN5S330_FUNC_SET10 0x59
+#define SN5S330_FUNC_SET11 0x5A
+#define SN5S330_FUNC_SET12 0x5B
+
+#define SN5S330_INT_STATUS_REG1 0x2F
+#define SN5S330_INT_STATUS_REG2 0x30
+#define SN5S330_INT_STATUS_REG3 0x31
+#define SN5S330_INT_STATUS_REG4 0x32
+
+#define SN5S330_INT_TRIP_RISE_REG1 0x20
+#define SN5S330_INT_TRIP_RISE_REG2 0x21
+#define SN5S330_INT_TRIP_RISE_REG3 0x22
+#define SN5S330_INT_TRIP_FALL_REG1 0x23
+#define SN5S330_INT_TRIP_FALL_REG2 0x24
+#define SN5S330_INT_TRIP_FALL_REG3 0x25
+
+#define SN5S330_INT_MASK_RISE_REG1 0x26
+#define SN5S330_INT_MASK_RISE_REG2 0x27
+#define SN5S330_INT_MASK_RISE_REG3 0x28
+#define SN5S330_INT_MASK_FALL_REG1 0x29
+#define SN5S330_INT_MASK_FALL_REG2 0x2A
+#define SN5S330_INT_MASK_FALL_REG3 0x2B
+
+#define PPX_ILIM_DEGLITCH_0_US_20 0x1
+#define PPX_ILIM_DEGLITCH_0_US_50 0x2
+#define PPX_ILIM_DEGLITCH_0_US_100 0x3
+#define PPX_ILIM_DEGLITCH_0_US_200 0x4
+#define PPX_ILIM_DEGLITCH_0_US_1000 0x5
+#define PPX_ILIM_DEGLITCH_0_US_2000 0x6
+#define PPX_ILIM_DEGLITCH_0_US_10000 0x7
+
+/* Internal VBUS Switch Current Limit Settings (min) */
+#define SN5S330_ILIM_0_35 0
+#define SN5S330_ILIM_0_63 1
+#define SN5S330_ILIM_0_90 2
+#define SN5S330_ILIM_1_14 3
+#define SN5S330_ILIM_1_38 4
+#define SN5S330_ILIM_1_62 5
+#define SN5S330_ILIM_1_86 6
+#define SN5S330_ILIM_2_10 7
+#define SN5S330_ILIM_2_34 8
+#define SN5S330_ILIM_2_58 9
+#define SN5S330_ILIM_2_82 10
+#define SN5S330_ILIM_3_06 11
+#define SN5S330_ILIM_3_30 12
+
+/* FUNC_SET_2 */
+#define SN5S330_SBU_EN (1 << 4)
+
+/* FUNC_SET_3 */
+#define SN5S330_PP1_EN (1 << 0)
+#define SN5S330_PP2_EN (1 << 1)
+#define SN5S330_SET_RCP_MODE_PP1 (1 << 5)
+#define SN5S330_SET_RCP_MODE_PP2 (1 << 6)
+
+#define SN5S330_CC_EN (1 << 4)
+
+/* FUNC_SET_9 */
+#define SN5S330_PP2_CONFIG (1 << 2)
+#define SN5S330_OVP_EN_CC (1 << 4)
+
+/* INT_STATUS_REG4 */
+#define SN5S330_DB_BOOT (1 << 1)
+
+/**
+ * Turn on/off the PP1 or PP2 FET.
+ *
+ * @param chip_idx: The index into the sn5s330_chips[] table.
+ * @param pp: The power path index (PP1 or PP2).
+ * @param enable: 1 to turn on the FET, 0 to turn off.
+ * @return EC_SUCCESS on success,
+ * otherwise if failed to enable the FET.
+ */
+int sn5s330_pp_fet_enable(uint8_t chip_idx, enum sn5s330_pp_idx pp, int enable);
+
+#endif /* defined(__CROS_EC_SN5S330_H) */
diff --git a/include/config.h b/include/config.h
index 8156ead7f4..c96bfdf446 100644
--- a/include/config.h
+++ b/include/config.h
@@ -759,6 +759,7 @@
#define CONFIG_CMD_POWERINDEBUG
#undef CONFIG_CMD_POWERLED
#define CONFIG_CMD_POWER_AP
+#undef CONFIG_CMD_PPC_DUMP
#define CONFIG_CMD_REGULATOR
#undef CONFIG_CMD_RTC
#undef CONFIG_CMD_RTC_ALARM
@@ -2643,6 +2644,9 @@
/* USB Product ID. */
#undef CONFIG_USB_PID
+/* USB Type-C Power Path Controllers (PPC) */
+#undef CONFIG_USBC_PPC_SN5S330
+
/* Support for USB type-c superspeed mux */
#undef CONFIG_USBC_SS_MUX