summaryrefslogtreecommitdiff
path: root/board/servo_v4p1/fusb302b.c
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2020-06-26 12:54:36 -0700
committerCommit Bot <commit-bot@chromium.org>2020-07-30 03:30:00 +0000
commit31146a9a0acdf42ef5148a473242f77886fcbd2e (patch)
treeb38b16d6bcd16688b9aaac96851eda030b9a5f1b /board/servo_v4p1/fusb302b.c
parentbcc2c00d5628e70a4f5c8094df22daef96c9c6c2 (diff)
downloadchrome-ec-31146a9a0acdf42ef5148a473242f77886fcbd2e.tar.gz
servo_v4p1: Add support for alternate charger port
A TypeC charger can be attached to the alternate charger port to supply up to 15W to the servoV4.1. BRANCH=none BUG=b:146793000 TEST=make -j buildall Signed-off-by: Sam Hurst <shurst@google.com> Change-Id: Ia88c783d23d09bfc12b34d416af98445db2d75c2 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2270696 Reviewed-by: Brian Nemec <bnemec@chromium.org>
Diffstat (limited to 'board/servo_v4p1/fusb302b.c')
-rw-r--r--board/servo_v4p1/fusb302b.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/board/servo_v4p1/fusb302b.c b/board/servo_v4p1/fusb302b.c
new file mode 100644
index 0000000000..fabb5b80ad
--- /dev/null
+++ b/board/servo_v4p1/fusb302b.c
@@ -0,0 +1,219 @@
+/* Copyright 2020 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.
+ */
+
+#include "i2c.h"
+#include "fusb302b.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "ioexpanders.h"
+#include "util.h"
+#include "task.h"
+#include "time.h"
+#include "usb_pd.h"
+
+static int port;
+static int status0;
+static int status1;
+static int interrupt;
+static struct mutex measure_lock;
+
+static int tcpc_write(int reg, int val)
+{
+ return i2c_write8(port, FUSB302_I2C_SLAVE_ADDR_FLAGS, reg, val);
+}
+
+static int tcpc_read(int reg, int *val)
+{
+ return i2c_read8(port, FUSB302_I2C_SLAVE_ADDR_FLAGS, reg, val);
+}
+
+int init_fusb302b(int p)
+{
+ int ret;
+ int reg;
+ int interrupta;
+ int interruptb;
+
+ /* Configure fusb302b for SNK only operation */
+ port = p;
+
+ ret = tcpc_write(TCPC_REG_RESET, TCPC_REG_RESET_SW_RESET);
+ if (ret)
+ return ret;
+
+ /* Create interrupt masks */
+ reg = 0xFF;
+ /* CC level changes */
+ reg &= ~TCPC_REG_MASK_BC_LVL;
+ /* misc alert */
+ reg &= ~TCPC_REG_MASK_ALERT;
+ /* VBUS threshold crossed (~4.0V) */
+ reg &= ~TCPC_REG_MASK_VBUSOK;
+ tcpc_write(TCPC_REG_MASK, reg);
+
+ /* Interrupt Enable */
+ ret = tcpc_read(TCPC_REG_CONTROL0, &reg);
+ if (ret)
+ return ret;
+
+ reg &= ~TCPC_REG_CONTROL0_INT_MASK;
+ ret = tcpc_write(TCPC_REG_CONTROL0, reg);
+ if (ret)
+ return ret;
+
+ ret = tcpc_write(TCPC_REG_POWER, TCPC_REG_POWER_PWR_ALL);
+ if (ret)
+ return ret;
+
+ /* reading interrupt registers clears them */
+ ret = tcpc_read(TCPC_REG_INTERRUPT, &interrupt);
+ if (ret)
+ return ret;
+
+
+ ret = tcpc_read(TCPC_REG_INTERRUPTA, &interrupta);
+ if (ret)
+ return ret;
+
+ ret = tcpc_read(TCPC_REG_INTERRUPTB, &interruptb);
+ if (ret)
+ return ret;
+
+ /* Call this, will detect a charger that's already plugged in */
+ update_status_fusb302b();
+
+ /* Enable interrupt */
+ gpio_enable_interrupt(GPIO_CHGSRV_TCPC_INT_ODL);
+
+ return EC_SUCCESS;
+}
+
+void fusb302b_irq(void)
+{
+ tcpc_read(TCPC_REG_INTERRUPT, &interrupt);
+ tcpc_read(TCPC_REG_STATUS0, &status0);
+ tcpc_read(TCPC_REG_STATUS1, &status1);
+
+ task_wake(TASK_ID_PD_C2);
+}
+DECLARE_DEFERRED(fusb302b_irq);
+
+int update_status_fusb302b(void)
+{
+ hook_call_deferred(&fusb302b_irq_data, 0);
+ return EC_SUCCESS;
+}
+
+int is_vbus_present(void)
+{
+ return (status0 & 0x80);
+}
+
+/* Convert BC LVL values (in FUSB302) to Type-C CC Voltage Status */
+static int convert_bc_lvl(int bc_lvl)
+{
+ int ret;
+
+ switch (bc_lvl) {
+ case 1:
+ ret = TYPEC_CC_VOLT_RP_DEF;
+ break;
+ case 2:
+ ret = TYPEC_CC_VOLT_RP_1_5;
+ break;
+ case 3:
+ ret = TYPEC_CC_VOLT_RP_3_0;
+ break;
+ default:
+ ret = TYPEC_CC_VOLT_OPEN;
+ }
+
+ return ret;
+}
+
+int get_cc(int *cc1, int *cc2)
+{
+ int reg;
+ int orig_meas_cc1;
+ int orig_meas_cc2;
+ int bc_lvl_cc1;
+ int bc_lvl_cc2;
+
+ mutex_lock(&measure_lock);
+
+ /*
+ * Measure CC1 first.
+ */
+ tcpc_read(TCPC_REG_SWITCHES0, &reg);
+
+ /* save original state to be returned to later... */
+ if (reg & TCPC_REG_SWITCHES0_MEAS_CC1)
+ orig_meas_cc1 = 1;
+ else
+ orig_meas_cc1 = 0;
+
+ if (reg & TCPC_REG_SWITCHES0_MEAS_CC2)
+ orig_meas_cc2 = 1;
+ else
+ orig_meas_cc2 = 0;
+
+
+ /* Disable CC2 measurement switch, enable CC1 measurement switch */
+ reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
+ reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
+
+ tcpc_write(TCPC_REG_SWITCHES0, reg);
+
+ /* CC1 is now being measured by FUSB302. */
+
+ /* Wait on measurement */
+ usleep(250);
+
+ tcpc_read(TCPC_REG_STATUS0, &bc_lvl_cc1);
+
+ /* mask away unwanted bits */
+ bc_lvl_cc1 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
+
+ /*
+ * Measure CC2 next.
+ */
+
+ tcpc_read(TCPC_REG_SWITCHES0, &reg);
+
+ /* Disable CC1 measurement switch, enable CC2 measurement switch */
+ reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
+ reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
+
+ tcpc_write(TCPC_REG_SWITCHES0, reg);
+
+ /* CC2 is now being measured by FUSB302. */
+
+ /* Wait on measurement */
+ usleep(250);
+
+ tcpc_read(TCPC_REG_STATUS0, &bc_lvl_cc2);
+
+ /* mask away unwanted bits */
+ bc_lvl_cc2 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
+
+ *cc1 = convert_bc_lvl(bc_lvl_cc1);
+ *cc2 = convert_bc_lvl(bc_lvl_cc2);
+
+ /* return MEAS_CC1/2 switches to original state */
+ tcpc_read(TCPC_REG_SWITCHES0, &reg);
+ if (orig_meas_cc1)
+ reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
+ else
+ reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
+ if (orig_meas_cc2)
+ reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
+ else
+ reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
+
+ tcpc_write(TCPC_REG_SWITCHES0, reg);
+
+ mutex_unlock(&measure_lock);
+ return 0;
+}