diff options
author | Sam Hurst <shurst@google.com> | 2020-06-26 12:54:36 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-07-30 03:30:00 +0000 |
commit | 31146a9a0acdf42ef5148a473242f77886fcbd2e (patch) | |
tree | b38b16d6bcd16688b9aaac96851eda030b9a5f1b /board/servo_v4p1/fusb302b.c | |
parent | bcc2c00d5628e70a4f5c8094df22daef96c9c6c2 (diff) | |
download | chrome-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.c | 219 |
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, ®); + 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, ®); + + /* 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, ®); + + /* 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, ®); + 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; +} |