diff options
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; +} |