diff options
Diffstat (limited to 'driver/ppc')
-rw-r--r-- | driver/ppc/aoz1380.c | 162 | ||||
-rw-r--r-- | driver/ppc/aoz1380.h | 44 | ||||
-rw-r--r-- | driver/ppc/nx20p348x.c | 566 | ||||
-rw-r--r-- | driver/ppc/nx20p348x.h | 132 | ||||
-rw-r--r-- | driver/ppc/rt1718s.c | 237 | ||||
-rw-r--r-- | driver/ppc/rt1718s.h | 17 | ||||
-rw-r--r-- | driver/ppc/sn5s330.c | 752 | ||||
-rw-r--r-- | driver/ppc/sn5s330.h | 161 | ||||
-rw-r--r-- | driver/ppc/syv682x.c | 837 | ||||
-rw-r--r-- | driver/ppc/syv682x.h | 112 |
10 files changed, 0 insertions, 3020 deletions
diff --git a/driver/ppc/aoz1380.c b/driver/ppc/aoz1380.c deleted file mode 100644 index 4e2188c60d..0000000000 --- a/driver/ppc/aoz1380.c +++ /dev/null @@ -1,162 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * AOZ1380 USB-C Power Path Controller - * - * This is a basic TCPM controlled PPC driver. It could easily be - * renamed and repurposed to be generic, if there are other TCPM - * controlled PPC chips that are similar to the AOZ1380 - */ - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "aoz1380.h" -#include "hooks.h" -#include "system.h" -#include "tcpm/tcpm.h" -#include "usb_pd.h" -#include "usb_pd_tcpc.h" -#include "usbc_ppc.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */ - -#define AOZ1380_FLAGS_SOURCE_ENABLED BIT(0) -#define AOZ1380_FLAGS_SINK_ENABLED BIT(1) -#define AOZ1380_FLAGS_INT_ON_DISCONNECT BIT(2) -static uint32_t flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define AOZ1380_SET_FLAG(port, flag) atomic_or(&flags[port], (flag)) -#define AOZ1380_CLR_FLAG(port, flag) atomic_clear_bits(&flags[port], (flag)) - -static int aoz1380_init(int port) -{ - flags[port] = 0; - - if (tcpm_get_snk_ctrl(port)) - AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_SINK_ENABLED); - - if (tcpm_get_src_ctrl(port)) - AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_SOURCE_ENABLED); - - return EC_SUCCESS; -} - -static int aoz1380_vbus_sink_enable(int port, int enable) -{ - int rv; - - rv = tcpm_set_snk_ctrl(port, enable); - if (rv) - return rv; - - /* - * On enable, we want to indicate connection as a SINK. - * On disable, clear SINK and that we have interrupted. - */ - if (enable) - AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_SINK_ENABLED); - else - AOZ1380_CLR_FLAG(port, (AOZ1380_FLAGS_SINK_ENABLED | - AOZ1380_FLAGS_INT_ON_DISCONNECT)); - - return EC_SUCCESS; -} - -static int aoz1380_vbus_source_enable(int port, int enable) -{ - int rv; - - rv = tcpm_set_src_ctrl(port, enable); - if (rv) - return rv; - - /* - * On enable, we want to indicate connection as a SOURCE. - * On disable, clear SOURCE and that we have interrupted. - */ - if (enable) - AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_SOURCE_ENABLED); - else - AOZ1380_CLR_FLAG(port, (AOZ1380_FLAGS_SOURCE_ENABLED | - AOZ1380_FLAGS_INT_ON_DISCONNECT)); - - return EC_SUCCESS; -} - -static int aoz1380_is_sourcing_vbus(int port) -{ - return flags[port] & AOZ1380_FLAGS_SOURCE_ENABLED; -} - -static int aoz1380_set_vbus_source_current_limit(int port, - enum tcpc_rp_value rp) -{ - return board_aoz1380_set_vbus_source_current_limit(port, rp); -} - -/* - * AOZ1380 Interrupt Handler - * - * This device only has a single over current/temperature interrupt. - * TODO(b/141939343) Determine how to clear the interrupt - * TODO(b/142076004) Test this to verify we shut off vbus current - * TODO(b/147359722) Verify correct fault functionality - */ -static void aoz1380_handle_interrupt(int port) -{ - /* - * We can get a false positive on disconnect that we - * had an over current/temperature event when we are no - * longer connected as sink or source. Ignore it if - * that is the case. - */ - if (flags[port] != 0) { - /* - * This is a over current/temperature condition - */ - ppc_prints("Vbus overcurrent/temperature", port); - pd_handle_overcurrent(port); - } else { - /* - * Just in case there is a condition that we will - * continue an interrupt storm, track that we have - * already been here once and will take the other - * path if we do this again before setting the - * sink/source as enabled or disabled again. - */ - AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_INT_ON_DISCONNECT); - } -} - -static void aoz1380_irq_deferred(void) -{ - int i; - uint32_t pending = atomic_clear(&irq_pending); - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - if (BIT(i) & pending) - aoz1380_handle_interrupt(i); -} -DECLARE_DEFERRED(aoz1380_irq_deferred); - -void aoz1380_interrupt(int port) -{ - atomic_or(&irq_pending, BIT(port)); - hook_call_deferred(&aoz1380_irq_deferred_data, 0); -} - -const struct ppc_drv aoz1380_drv = { - .init = &aoz1380_init, - .is_sourcing_vbus = &aoz1380_is_sourcing_vbus, - .vbus_sink_enable = &aoz1380_vbus_sink_enable, - .vbus_source_enable = &aoz1380_vbus_source_enable, - .set_vbus_source_current_limit = - &aoz1380_set_vbus_source_current_limit, -}; diff --git a/driver/ppc/aoz1380.h b/driver/ppc/aoz1380.h deleted file mode 100644 index 94f2b804b7..0000000000 --- a/driver/ppc/aoz1380.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * AOZ1380 USB-C Power Path Controller - * - * This is a basic TCPM controlled PPC driver. It could easily be - * renamed and repurposed to be generic, if there are other TCPM - * controlled PPC chips that are similar to the AOZ1380 - */ - -#ifndef __CROS_EC_AOZ1380_H -#define __CROS_EC_AOZ1380_H - -#include "usb_pd_tcpm.h" - -struct ppc_drv; -extern const struct ppc_drv aoz1380_drv; - -/** - * AOZ1380 Set VBus Source Current Limit. - * - * Using this driver requires a board_aoz1380_set_vbus_source_limit - * function due to the lack of programability of this device and - * requirement for hardware specific code to handle setting this limit. - * - * @param port The Type-C port - * @param rp The Type-C RP value - * @return EC_SUCCESS for success, otherwise error - */ -int board_aoz1380_set_vbus_source_current_limit(int port, - enum tcpc_rp_value rp); - - -/** - * Interrupt Handler for the AOZ1380. - * - * @param port: The Type-C port which triggered the interrupt. - */ -void aoz1380_interrupt(int port); - -#endif /* defined(__CROS_EC_AOZ1380_H) */ diff --git a/driver/ppc/nx20p348x.c b/driver/ppc/nx20p348x.c deleted file mode 100644 index a5136bbf23..0000000000 --- a/driver/ppc/nx20p348x.c +++ /dev/null @@ -1,566 +0,0 @@ -/* 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. - */ - -/* NX20P348x USB-C Power Path Controller */ - -#include "common.h" -#include "console.h" -#include "nx20p348x.h" -#include "gpio.h" -#include "hooks.h" -#include "i2c.h" -#include "system.h" -#include "tcpm/tcpm.h" -#include "usb_charge.h" -#include "usb_pd_tcpm.h" -#include "usb_pd.h" -#include "usbc_ppc.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */ - -#define NX20P348X_DB_EXIT_FAIL_THRESHOLD 10 -static int db_exit_fail_count[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define NX20P348X_FLAGS_SOURCE_ENABLED BIT(0) -static uint8_t flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#if !defined(CONFIG_USBC_PPC_NX20P3481) && !defined(CONFIG_USBC_PPC_NX20P3483) -#error "Either the NX20P3481 or NX20P3483 must be selected" -#endif - -static int read_reg(uint8_t port, int reg, int *regval) -{ - return i2c_read8(ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - 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_flags, - reg, - regval); -} - -static int nx20p348x_set_ovp_limit(int port) -{ - int rv; - int reg; - - /* Set VBUS over voltage threshold (OVLO) */ - rv = read_reg(port, NX20P348X_OVLO_THRESHOLD_REG, ®); - if (rv) - return rv; - /* OVLO threshold is 3 bit field */ - reg &= ~NX20P348X_OVLO_THRESHOLD_MASK; - /* Set SNK OVP to 23.0 V */ - reg |= NX20P348X_OVLO_23_0; - rv = write_reg(port, NX20P348X_OVLO_THRESHOLD_REG, reg); - if (rv) - return rv; - - return EC_SUCCESS; -} - -static int nx20p348x_is_sourcing_vbus(int port) -{ - return flags[port] & NX20P348X_FLAGS_SOURCE_ENABLED; -} - -static int nx20p348x_set_vbus_source_current_limit(int port, - enum tcpc_rp_value rp) -{ - int regval; - int status; - - status = read_reg(port, NX20P348X_5V_SRC_OCP_THRESHOLD_REG, ®val); - if (status) - return status; - - regval &= ~NX20P348X_ILIM_MASK; - - /* We need buffer room for all current values. */ - switch (rp) { - case TYPEC_RP_3A0: - regval |= NX20P348X_ILIM_3_200; - break; - - case TYPEC_RP_1A5: - regval |= NX20P348X_ILIM_1_600; - break; - - case TYPEC_RP_USB: - default: - regval |= NX20P348X_ILIM_0_600; - break; - }; - - - return write_reg(port, NX20P348X_5V_SRC_OCP_THRESHOLD_REG, regval); -} - -static int nx20p348x_discharge_vbus(int port, int enable) -{ - int regval; - int newval; - int status; - - status = read_reg(port, NX20P348X_DEVICE_CONTROL_REG, ®val); - if (status) - return status; - - if (enable) - newval = regval | NX20P348X_CTRL_VBUSDIS_EN; - else - newval = regval & ~NX20P348X_CTRL_VBUSDIS_EN; - - if (newval == regval) - return EC_SUCCESS; - - status = write_reg(port, NX20P348X_DEVICE_CONTROL_REG, newval); - if (status) { - CPRINTS("Failed to %s VBUS discharge", - enable ? "enable" : "disable"); - return status; - } - - return EC_SUCCESS; -} - -__maybe_unused static int nx20p3481_vbus_sink_enable(int port, int enable) -{ - int status; - int rv; - int control = enable ? NX20P3481_SWITCH_CONTROL_HVSNK : 0; - - if (enable) { - /* - * VBUS Discharge must be off in sink mode. - */ - rv = nx20p348x_discharge_vbus(port, 0); - if (rv) - return rv; - } - - rv = write_reg(port, NX20P348X_SWITCH_CONTROL_REG, control); - if (rv) - return rv; - - /* - * Read switch status register. The bit definitions for switch control - * and switch status resister are identical, so the control value can be - * compared against the status value. The control switch has a debounce - * (15 msec) before the status will reflect the control command. - */ - msleep(NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC); - rv = read_reg(port, NX20P348X_SWITCH_STATUS_REG, &status); - if (rv) - return rv; - - return (status & NX20P348X_SWITCH_STATUS_HVSNK) == control ? - EC_SUCCESS : EC_ERROR_UNKNOWN; -} - -__maybe_unused static int nx20p3481_vbus_source_enable(int port, int enable) -{ - int status; - int rv; - uint8_t previous_flags = flags[port]; - int control = enable ? NX20P3481_SWITCH_CONTROL_5VSRC : 0; - - rv = write_reg(port, NX20P348X_SWITCH_CONTROL_REG, control); - if (rv) - return rv; - - /* Cache the anticipated Vbus state */ - if (enable) - flags[port] |= NX20P348X_FLAGS_SOURCE_ENABLED; - else - flags[port] &= ~NX20P348X_FLAGS_SOURCE_ENABLED; - - /* - * Read switch status register. The bit definitions for switch control - * and switch status resister are identical, so the control value can be - * compared against the status value. The control switch has a debounce - * (15 msec) before the status will reflect the control command. - */ - msleep(NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC); - - if (IS_ENABLED(CONFIG_USBC_PPC_NX20P3481)) { - rv = read_reg(port, NX20P348X_SWITCH_STATUS_REG, &status); - if (rv) { - flags[port] = previous_flags; - return rv; - } - if ((status & NX20P348X_SWITCH_STATUS_MASK) != control) { - flags[port] = previous_flags; - return EC_ERROR_UNKNOWN; - } - } - - return EC_SUCCESS; -} - -__maybe_unused static int nx20p3483_vbus_sink_enable(int port, int enable) -{ - int rv; - - enable = !!enable; - - if (enable) { - /* - * VBUS Discharge must be off in sink mode. - */ - rv = nx20p348x_discharge_vbus(port, 0); - if (rv) - return rv; - } - - /* - * We cannot use an EC GPIO for EN_SNK since an EC reset - * will float the GPIO thus browning out the board (without - * a battery). - */ - rv = tcpm_set_snk_ctrl(port, enable); - if (rv) - return rv; - - for (int i = 0; i < NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC; ++i) { - int ds; - bool is_sink; - - rv = read_reg(port, NX20P348X_DEVICE_STATUS_REG, &ds); - if (rv != EC_SUCCESS) - return rv; - - is_sink = (ds & NX20P3483_DEVICE_MODE_MASK) == - NX20P3483_MODE_HV_SNK; - if (enable == is_sink) - return EC_SUCCESS; - - msleep(1); - } - - return EC_ERROR_TIMEOUT; -} - -__maybe_unused static int nx20p3483_vbus_source_enable(int port, int enable) -{ - int rv; - - enable = !!enable; - - /* - * For parity's sake, we should not use an EC GPIO for - * EN_SRC since we cannot use it for EN_SNK (for brown - * out reason listed above). - */ - rv = tcpm_set_src_ctrl(port, enable); - if (rv) - return rv; - - /* - * Wait up to NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC for the status - * to reflect the control command. - */ - - for (int i = 0; i < NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC; ++i) { - int s; - - rv = read_reg(port, NX20P348X_SWITCH_STATUS_REG, &s); - if (rv != EC_SUCCESS) - return rv; - - if (!!(s & (NX20P348X_SWITCH_STATUS_5VSRC | - NX20P348X_SWITCH_STATUS_HVSRC)) == enable) { - if (enable) - flags[port] |= NX20P348X_FLAGS_SOURCE_ENABLED; - else - flags[port] &= ~NX20P348X_FLAGS_SOURCE_ENABLED; - return EC_SUCCESS; - } - msleep(1); - } - - return EC_ERROR_TIMEOUT; -} - -static int nx20p348x_init(int port) -{ - int reg; - int mask; - int mode; - int rv; - enum tcpc_rp_value initial_current_limit; - - /* Mask interrupts for interrupt 2 register */ - mask = ~NX20P348X_INT2_EN_ERR; - rv = write_reg(port, NX20P348X_INTERRUPT2_MASK_REG, mask); - if (rv) - return rv; - - /* Mask interrupts for interrupt 1 register */ - mask = ~(NX20P348X_INT1_OC_5VSRC | NX20P348X_INT1_SC_5VSRC | - NX20P348X_INT1_RCP_5VSRC | NX20P348X_INT1_DBEXIT_ERR); - if (IS_ENABLED(CONFIG_USBC_PPC_NX20P3481)) { - /* Unmask Fast Role Swap detect interrupt */ - mask &= ~NX20P3481_INT1_FRS_DET; - } - rv = write_reg(port, NX20P348X_INTERRUPT1_MASK_REG, mask); - if (rv) - return rv; - - /* Clear any pending interrupts by reading interrupt registers */ - read_reg(port, NX20P348X_INTERRUPT1_REG, ®); - read_reg(port, NX20P348X_INTERRUPT2_REG, ®); - - /* Get device mode */ - rv = read_reg(port, NX20P348X_DEVICE_STATUS_REG, &mode); - if (rv) - return rv; - - if (IS_ENABLED(CONFIG_USBC_PPC_NX20P3481)) - mode &= NX20P3481_DEVICE_MODE_MASK; - else if (IS_ENABLED(CONFIG_USBC_PPC_NX20P3483)) - mode &= NX20P3483_DEVICE_MODE_MASK; - - /* Check if dead battery mode is active. */ - if (mode == NX20P348X_MODE_DEAD_BATTERY) { - /* - * If in dead battery mode, must enable HV SNK mode prior to - * exiting dead battery mode or VBUS path will get cut off and - * system will lose power. Before DB mode is exited, the device - * mode will not reflect the correct value and therefore the - * return value isn't useful here. - */ - nx20p348x_drv.vbus_sink_enable(port, 1); - - /* Exit dead battery mode. */ - rv = read_reg(port, NX20P348X_DEVICE_CONTROL_REG, ®); - if (rv) - return rv; - reg |= NX20P348X_CTRL_DB_EXIT; - rv = write_reg(port, NX20P348X_DEVICE_CONTROL_REG, reg); - if (rv) - return rv; - } - - /* - * Set VBUS over voltage threshold (OVLO). Note that while the PPC is in - * dead battery mode, OVLO is forced to 6.8V, so this setting must be - * done after dead battery mode is exited. - */ - nx20p348x_set_ovp_limit(port); - - /* Set the Vbus current limit after dead battery mode exit */ -#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT - initial_current_limit = CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT; -#else - initial_current_limit = TYPEC_RP_1A5; -#endif - nx20p348x_set_vbus_source_current_limit(port, initial_current_limit); - - /* Restore power-on reset value */ - rv = write_reg(port, NX20P348X_DEVICE_CONTROL_REG, 0); - if (rv) - return rv; - - return EC_SUCCESS; -} - -static void nx20p348x_handle_interrupt(int port) -{ - int reg; - int control_reg; - - /* - * Read interrupt 1 status register. Note, interrupt register is - * automatically cleared by reading. - */ - read_reg(port, NX20P348X_INTERRUPT1_REG, ®); - - /* Check for DBEXIT error */ - if (reg & NX20P348X_INT1_DBEXIT_ERR) { - int mask_reg; - - /* - * This failure is not expected. If for some reason, this - * keeps happening, then log an error and mask the interrupt to - * prevent interrupt floods. - */ - if (++db_exit_fail_count[port] >= - NX20P348X_DB_EXIT_FAIL_THRESHOLD) { - ppc_prints("failed to exit DB mode", port); - if (read_reg(port, NX20P348X_INTERRUPT1_MASK_REG, - &mask_reg)) { - mask_reg |= NX20P348X_INT1_DBEXIT_ERR; - write_reg(port, NX20P348X_INTERRUPT1_MASK_REG, - mask_reg); - } - } - read_reg(port, NX20P348X_DEVICE_CONTROL_REG, &control_reg); - control_reg |= NX20P348X_CTRL_DB_EXIT; - write_reg(port, NX20P348X_DEVICE_CONTROL_REG, control_reg); - /* - * If DB exit mode failed, then the OVP limit setting done in - * the init routine will not be successful. Set the OVP limit - * again here. - */ - nx20p348x_set_ovp_limit(port); - } - - /* Check for 5V OC interrupt */ - if (reg & NX20P348X_INT1_OC_5VSRC) { - ppc_prints("detected Vbus overcurrent!", port); - pd_handle_overcurrent(port); - } - - /* Check for Vbus reverse current protection */ - if (reg & NX20P348X_INT1_RCP_5VSRC) - ppc_prints("detected Vbus reverse current!", port); - - /* Check for Vbus short protection */ - if (reg & NX20P348X_INT1_SC_5VSRC) - ppc_prints("Vbus short detected!", port); - - /* Check for FRS detection */ - if (IS_ENABLED(CONFIG_USBC_PPC_NX20P3481) && - (reg & NX20P3481_INT1_FRS_DET)) { - /* - * TODO(b/113069469): Need to check for CC status and verifiy - * that a sink is attached to continue with FRS. If a sink is - * not attached, then this FRS detect is a false detect which is - * triggered when removing an external charger. If FRS was - * detected by the PPC, then it has automatically enabled the - * 5V SRC mode and this must be undone for a proper detach. - */ - /* Check CC status */ - - /* - * False detect, disable SRC mode which was enabled by - * NX20P3481. - */ - ppc_prints("FRS false detect, disabling SRC mode!", port); - nx20p3481_vbus_source_enable(port, 0); - } - - /* - * Read interrupt 2 status register. Note, interrupt register is - * automatically cleared by reading. - */ - /* - * TODO (b/75272421): Not sure if any of these interrupts - * will be used. Might want to use EN_ERR which tracks when both - * SNK_EN and SRC_EN are set. However, since for the Analogix TCPC - * these values aren't controlled by the EC directly, not sure what - * action if any can be taken. - */ - read_reg(port, NX20P348X_INTERRUPT2_REG, ®); -} - -static void nx20p348x_irq_deferred(void) -{ - int i; - uint32_t pending = atomic_clear(&irq_pending); - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - if (BIT(i) & pending) - nx20p348x_handle_interrupt(i); -} -DECLARE_DEFERRED(nx20p348x_irq_deferred); - -void nx20p348x_interrupt(int port) -{ - atomic_or(&irq_pending, BIT(port)); - hook_call_deferred(&nx20p348x_irq_deferred_data, 0); -} - -#ifdef CONFIG_CMD_PPC_DUMP -static int nx20p348x_dump(int port) -{ - int reg_addr; - int reg; - int rv; - - ccprintf("Port %d NX20P348X registers\n", port); - for (reg_addr = NX20P348X_DEVICE_ID_REG; reg_addr <= - NX20P348X_DEVICE_CONTROL_REG; reg_addr++) { - rv = read_reg(port, reg_addr, ®); - if (rv) { - ccprintf("nx20p: Failed to read register 0x%x\n", - reg_addr); - return rv; - } - ccprintf("[0x%02x]: 0x%02x\n", reg_addr, reg); - - /* Flush every call otherwise buffer may get full */ - cflush(); - } - - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ - -/* - * TODO (b/112697473): The NX20P348x PPCs do not support vbus detection or vconn - * generation. However, if a different PPC does support these features and needs - * these config options, then these functions do need to exist. The - * configuration for what each PPC supports should be converted to bits within - * a flags variable that is part of the ppc_config_t struct. - */ -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC -static int nx20p348x_is_vbus_present(int port) -{ - return EC_ERROR_UNIMPLEMENTED; -} -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ - -#ifdef CONFIG_USBC_PPC_POLARITY -static int nx20p348x_set_polarity(int port, int polarity) -{ - return EC_ERROR_UNIMPLEMENTED; -} -#endif - -#ifdef CONFIG_USBC_PPC_VCONN -static int nx20p348x_set_vconn(int port, int enable) -{ - return EC_ERROR_UNIMPLEMENTED; -} -#endif - -const struct ppc_drv nx20p348x_drv = { - .init = &nx20p348x_init, - .is_sourcing_vbus = &nx20p348x_is_sourcing_vbus, -#ifdef CONFIG_USBC_PPC_NX20P3481 - .vbus_sink_enable = &nx20p3481_vbus_sink_enable, - .vbus_source_enable = &nx20p3481_vbus_source_enable, -#endif /* CONFIG_USBC_PPC_NX20P3481 */ -#ifdef CONFIG_USBC_PPC_NX20P3483 - .vbus_sink_enable = &nx20p3483_vbus_sink_enable, - .vbus_source_enable = &nx20p3483_vbus_source_enable, -#endif /* CONFIG_USBC_PPC_NX20P3483 */ -#ifdef CONFIG_CMD_PPC_DUMP - .reg_dump = &nx20p348x_dump, -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ - .set_vbus_source_current_limit = - &nx20p348x_set_vbus_source_current_limit, - .discharge_vbus = &nx20p348x_discharge_vbus, -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC - .is_vbus_present = &nx20p348x_is_vbus_present, -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ -#ifdef CONFIG_USBC_PPC_POLARITY - .set_polarity = &nx20p348x_set_polarity, -#endif /* defined(CONFIG_USBC_PPC_POLARITY) */ -#ifdef CONFIG_USBC_PPC_VCONN - .set_vconn = &nx20p348x_set_vconn, -#endif /* defined(CONFIG_USBC_PPC_VCONN) */ -}; diff --git a/driver/ppc/nx20p348x.h b/driver/ppc/nx20p348x.h deleted file mode 100644 index 54c6dc40b5..0000000000 --- a/driver/ppc/nx20p348x.h +++ /dev/null @@ -1,132 +0,0 @@ -/* 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. - */ - -/* NX20P348x Type-C Power Path Controller */ - -#ifndef __CROS_EC_NX20P348X_H -#define __CROS_EC_NX20P348X_H - -#define NX20P3483_ADDR0_FLAGS 0x70 -#define NX20P3483_ADDR1_FLAGS 0x71 -#define NX20P3483_ADDR2_FLAGS 0x72 -#define NX20P3483_ADDR3_FLAGS 0x73 - -#define NX20P3481_ADDR0_FLAGS 0x74 -#define NX20P3481_ADDR1_FLAGS 0x75 -#define NX20P3481_ADDR2_FLAGS 0x76 -#define NX20P3481_ADDR3_FLAGS 0x77 - -/* - * This PPC hard-codes the over voltage protect of Vbus at 6.8V in dead-battery - * mode. If we ever are every going to drop the PD rail, we need to first ensure - * that Vbus is negotiated to below 6.8V otherwise we can lock out Vbus. - */ -#define NX20P348X_SAFE_RESET_VBUS_MV 5000 - -/* NX20P348x register addresses */ -#define NX20P348X_DEVICE_ID_REG 0x00 -#define NX20P348X_DEVICE_STATUS_REG 0x01 -#define NX20P348X_SWITCH_CONTROL_REG 0x02 -#define NX20P348X_SWITCH_STATUS_REG 0x03 -#define NX20P348X_INTERRUPT1_REG 0x04 -#define NX20P348X_INTERRUPT2_REG 0x05 -#define NX20P348X_INTERRUPT1_MASK_REG 0x06 -#define NX20P348X_INTERRUPT2_MASK_REG 0x07 -#define NX20P348X_OVLO_THRESHOLD_REG 0x08 -#define NX20P348X_HV_SRC_OCP_THRESHOLD_REG 0x09 -#define NX20P348X_5V_SRC_OCP_THRESHOLD_REG 0x0A -#define NX20P348X_DEVICE_CONTROL_REG 0x0B - -/* Device Control Register (0x0B) */ -#define NX20P3483_CTRL_FRS_AT BIT(3) -#define NX20P348X_CTRL_DB_EXIT BIT(2) -#define NX20P348X_CTRL_VBUSDIS_EN BIT(1) -#define NX20P348X_CTRL_LDO_SD BIT(0) - -/* Device Status Modes (0x01) */ -#define NX20P3481_DEVICE_MODE_MASK 0x3 -#define NX20P3483_DEVICE_MODE_MASK 0x7 -#define NX20P348X_MODE_DEAD_BATTERY 0 -/* After dead battery, mode values are different between 3481 and 3483 */ -#define NX20P3481_MODE_NORMAL 1 -#define NX20P3481_MODE_FRS 2 -#define NX20P3481_MODE_STANDBY 3 - -#define NX20P3483_MODE_HV_SNK 1 -#define NX20P3483_MODE_5V_SRC 2 -#define NX20P3483_MODE_HV_SRC 3 -#define NX20P3483_MODE_STANDBY 4 - -/* Switch Control Register (0x02) */ -#define NX20P3481_SWITCH_CONTROL_5VSRC BIT(2) -#define NX20P3481_SWITCH_CONTROL_HVSRC BIT(1) -#define NX20P3481_SWITCH_CONTROL_HVSNK BIT(0) - -/* Switch Status Register (0x03) */ -#define NX20P348X_SWITCH_STATUS_5VSRC BIT(2) -#define NX20P348X_SWITCH_STATUS_HVSRC BIT(1) -#define NX20P348X_SWITCH_STATUS_HVSNK BIT(0) -#define NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC 25 -#define NX20P348X_SWITCH_STATUS_MASK 0x7 - -/* Internal 5V VBUS Switch Current Limit Settings (min) */ -#define NX20P348X_ILIM_MASK 0xF -#define NX20P348X_ILIM_0_400 0 -#define NX20P348X_ILIM_0_600 1 -#define NX20P348X_ILIM_0_800 2 -#define NX20P348X_ILIM_1_000 3 -#define NX20P348X_ILIM_1_200 4 -#define NX20P348X_ILIM_1_400 5 -#define NX20P348X_ILIM_1_600 6 -#define NX20P348X_ILIM_1_800 7 -#define NX20P348X_ILIM_2_000 8 -#define NX20P348X_ILIM_2_200 9 -#define NX20P348X_ILIM_2_400 10 -#define NX20P348X_ILIM_2_600 11 -#define NX20P348X_ILIM_2_800 12 -#define NX20P348X_ILIM_3_000 13 -#define NX20P348X_ILIM_3_200 14 -#define NX20P348X_ILIM_3_400 15 - -/* HV VBUS over voltage threshold settings V_mV*/ -#define NX20P348X_OVLO_THRESHOLD_MASK 0x7 -#define NX20P348X_OVLO_06_0 0 -#define NX20P348X_OVLO_06_8 1 -#define NX20P348X_OVLO_10_0 2 -#define NX20P348X_OVLO_11_5 3 -#define NX20P348X_OVLO_14_0 4 -#define NX20P348X_OVLO_17_0 5 -#define NX20P348X_OVLO_23_0 6 - -/* Interrupt 1 Register Bits (0x04) */ -#define NX20P348X_INT1_DBEXIT_ERR BIT(7) -#define NX20P3481_INT1_FRS_DET BIT(6) -#define NX20P348X_INT1_OV_5VSRC BIT(4) -#define NX20P348X_INT1_RCP_5VSRC BIT(3) -#define NX20P348X_INT1_SC_5VSRC BIT(2) -#define NX20P348X_INT1_OC_5VSRC BIT(1) -#define NX20P348X_INT1_OTP BIT(0) - -/* Interrupt 2 Register Bits (0x05) */ -#define NX20P348X_INT2_EN_ERR BIT(7) -#define NX20P348X_INT2_RCP_HVSNK BIT(6) -#define NX20P348X_INT2_SC_HVSNK BIT(5) -#define NX20P348X_INT2_OV_HVSNK BIT(4) -#define NX20P348X_INT2_RCP_HVSRC BIT(3) -#define NX20P348X_INT2_SC_HVSRC BIT(2) -#define NX20P348X_INT2_OC_HVSRC BIT(1) -#define NX20P348X_INT2_OV_HVSRC BIT(0) - -struct ppc_drv; -extern const struct ppc_drv nx20p348x_drv; - -/** - * Interrupt Handler for the NX20P348x. - * - * @param port: The Type-C port which triggered the interrupt. - */ -void nx20p348x_interrupt(int port); - -#endif /* defined(__CROS_EC_NX20P348X_H) */ diff --git a/driver/ppc/rt1718s.c b/driver/ppc/rt1718s.c deleted file mode 100644 index 96cb789cd0..0000000000 --- a/driver/ppc/rt1718s.c +++ /dev/null @@ -1,237 +0,0 @@ -/* Copyright 2021 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. - */ - -/* Richtek RT1718S USB-C Power Path Controller */ -#include "atomic.h" -#include "common.h" -#include "config.h" -#include "console.h" -#include "driver/ppc/rt1718s.h" -#include "driver/tcpm/tcpci.h" -#include "usbc_ppc.h" -#include "util.h" - - -#define RT1718S_FLAGS_SOURCE_ENABLED BIT(0) -static atomic_t flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) - -static int read_reg(uint8_t port, int reg, int *val) -{ - if (reg > 0xFF) { - return i2c_read_offset16( - ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, val, 1); - } else { - return i2c_read8( - ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, val); - } -} - -static int write_reg(uint8_t port, int reg, int val) -{ - if (reg > 0xFF) { - return i2c_write_offset16( - ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, val, 1); - } else { - return i2c_write8( - ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, val); - } -} - -static int update_bits(int port, int reg, int mask, int val) -{ - int reg_val; - - if (mask == 0xFF) - return write_reg(port, reg, val); - - RETURN_ERROR(read_reg(port, reg, ®_val)); - - reg_val &= (~mask); - reg_val |= (mask & val); - return write_reg(port, reg, reg_val); -} - -static int rt1718s_is_sourcing_vbus(int port) -{ - return (flags[port] & RT1718S_FLAGS_SOURCE_ENABLED); -} - -static int rt1718s_vbus_source_enable(int port, int enable) -{ - atomic_t prev_flag; - - if (enable) - prev_flag = atomic_or(&flags[port], - RT1718S_FLAGS_SOURCE_ENABLED); - else - prev_flag = atomic_clear_bits(&flags[port], - RT1718S_FLAGS_SOURCE_ENABLED); - - /* Return if status doesn't change */ - if (!!(prev_flag & RT1718S_FLAGS_SOURCE_ENABLED) == !!enable) - return EC_SUCCESS; - - RETURN_ERROR(tcpm_set_src_ctrl(port, enable)); - -#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 rt1718s_vbus_sink_enable(int port, int enable) -{ - return tcpm_set_snk_ctrl(port, enable); -} - -static int rt1718s_discharge_vbus(int port, int enable) -{ - return update_bits(port, - TCPC_REG_POWER_CTRL, - TCPC_REG_POWER_CTRL_FORCE_DISCHARGE, - enable ? 0xFF : 0x00); -} - -#ifdef CONFIG_CMD_PPC_DUMP -static int rt1718s_dump(int port) -{ - for (int i = 0; i <= 0xEF; i++) { - int val = 0; - int rt = read_reg(port, i, &val); - - if (i % 16 == 0) - CPRINTF("%02X: ", i); - if (rt) - CPRINTF("-- "); - else - CPRINTF("%02X ", val); - if (i % 16 == 15) - CPRINTF("\n"); - } - for (int i = 0xF200; i <= 0xF2CF; i++) { - int val = 0; - int rt = read_reg(port, i, &val); - - if (i % 16 == 0) - CPRINTF("%04X: ", i); - if (rt) - CPRINTF("-- "); - else - CPRINTF("%02X ", val); - if (i % 16 == 15) - CPRINTF("\n"); - } - - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ - -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC -static int rt1718s_is_vbus_present(int port) -{ - __maybe_unused static atomic_t vbus_prev[CONFIG_USB_PD_PORT_MAX_COUNT]; - int status, vbus; - - if (read_reg(port, TCPC_REG_POWER_STATUS, &status)) - return 0; - - vbus = !!(status & TCPC_REG_POWER_STATUS_VBUS_PRES); - -#ifdef CONFIG_USB_CHARGER - if (!!(vbus_prev[port] != vbus)) - usb_charger_vbus_change(port, vbus); - - if (vbus) - atomic_or(&vbus_prev[port], 1); - else - atomic_clear(&vbus_prev[port]); -#endif - - return vbus; -} -#endif - -static int rt1718s_init(int port) -{ - atomic_clear(&flags[port]); - - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) - /* Set Rx frs unmasked */ - RETURN_ERROR(update_bits(port, RT1718S_RT_MASK1, - RT1718S_RT_MASK1_M_RX_FRS, - 0xFF)); - - return EC_SUCCESS; -} - -#ifdef CONFIG_USBC_PPC_POLARITY -static int rt1718s_set_polarity(int port, int polarity) -{ - return tcpci_tcpm_set_polarity(port, polarity); -} -#endif - -#ifdef CONFIG_USB_PD_FRS_PPC -static int rt1718s_set_frs_enable(int port, int enable) -{ - /* - * Use write instead of update to save 2 i2c read. - * Assume other bits are at their reset value. - */ - int frs_ctrl2 = 0x10, vbus_ctrl_en = 0x3F; - - if (enable) { - frs_ctrl2 |= RT1718S_FRS_CTRL2_RX_FRS_EN; - frs_ctrl2 |= RT1718S_FRS_CTRL2_VBUS_FRS_EN; - - vbus_ctrl_en |= RT1718S_VBUS_CTRL_EN_GPIO2_VBUS_PATH_EN; - vbus_ctrl_en |= RT1718S_VBUS_CTRL_EN_GPIO1_VBUS_PATH_EN; - } - - RETURN_ERROR(write_reg(port, RT1718S_FRS_CTRL2, frs_ctrl2)); - RETURN_ERROR(write_reg(port, RT1718S_VBUS_CTRL_EN, vbus_ctrl_en)); - return EC_SUCCESS; -} -#endif - -const struct ppc_drv rt1718s_ppc_drv = { - .init = &rt1718s_init, - .is_sourcing_vbus = &rt1718s_is_sourcing_vbus, - .vbus_sink_enable = &rt1718s_vbus_sink_enable, - .vbus_source_enable = &rt1718s_vbus_source_enable, -#ifdef CONFIG_CMD_PPC_DUMP - .reg_dump = &rt1718s_dump, -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ - -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC - .is_vbus_present = &rt1718s_is_vbus_present, -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ - .discharge_vbus = &rt1718s_discharge_vbus, -#ifdef CONFIG_USBC_PPC_POLARITY - .set_polarity = &rt1718s_set_polarity, -#endif -#ifdef CONFIG_USBC_PPC_VCONN - .set_vconn = &tcpci_tcpm_set_vconn, -#endif -#ifdef CONFIG_USB_PD_FRS_PPC - .set_frs_enable = rt1718s_set_frs_enable, -#endif -}; diff --git a/driver/ppc/rt1718s.h b/driver/ppc/rt1718s.h deleted file mode 100644 index f692bf3dd4..0000000000 --- a/driver/ppc/rt1718s.h +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright 2021 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. - */ - -/* Richtek RT1718S Type-C Power Path Controller */ - -#ifndef __CROS_EC_PPC_RT1718S_H -#define __CROS_EC_PPC_RT1718S_H - -#include "driver/tcpm/rt1718s.h" -#include "usbc_ppc.h" - -extern const struct ppc_drv rt1718s_ppc_drv; - -#endif /* defined(__CROS_EC_PPC_RT1718S_H) */ - diff --git a/driver/ppc/sn5s330.c b/driver/ppc/sn5s330.c deleted file mode 100644 index 4abf85cf50..0000000000 --- a/driver/ppc/sn5s330.c +++ /dev/null @@ -1,752 +0,0 @@ -/* 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 "sn5s330.h" -#include "hooks.h" -#include "i2c.h" -#include "system.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_pd_tcpm.h" -#include "usb_pd.h" -#include "usbc_ppc.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */ -static int source_enabled[CONFIG_USB_PD_PORT_MAX_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_flags, - 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_flags, - reg, - regval); -} - -static int set_flags(const int port, const int addr, const int flags_to_set) -{ - int val, rv; - - rv = read_reg(port, addr, &val); - if (rv) - return rv; - - val |= flags_to_set; - - return write_reg(port, addr, val); -} - - -static int clr_flags(const int port, const int addr, const int flags_to_clear) -{ - int val, rv; - - rv = read_reg(port, addr, &val); - if (rv) - return rv; - - val &= ~flags_to_clear; - - return write_reg(port, addr, val); -} - -#ifdef CONFIG_CMD_PPC_DUMP -static int sn5s330_dump(int port) -{ - int i; - int data; - const int i2c_port = ppc_chips[port].i2c_port; - const uint16_t i2c_addr_flags = ppc_chips[port].i2c_addr_flags; - - /* 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_flags, i, &data); - ccprintf("FUNC_SET%d [%02Xh] = 0x%02x\n", - i - SN5S330_FUNC_SET1 + 1, - i, - data); - } - - cflush(); - - for (i = SN5S330_INT_STATUS_REG1; i <= SN5S330_INT_STATUS_REG4; i++) { - i2c_read8(i2c_port, i2c_addr_flags, i, &data); - ccprintf("INT_STATUS_REG%d [%02Xh] = 0x%02x\n", - i - SN5S330_INT_STATUS_REG1 + 1, - i, - data); - } - - cflush(); - - for (i = SN5S330_INT_TRIP_RISE_REG1; i <= SN5S330_INT_TRIP_RISE_REG3; - i++) { - i2c_read8(i2c_port, i2c_addr_flags, i, &data); - ccprintf("INT_TRIP_RISE_REG%d [%02Xh] = 0x%02x\n", - i - SN5S330_INT_TRIP_RISE_REG1 + 1, - i, - data); - } - - cflush(); - - for (i = SN5S330_INT_TRIP_FALL_REG1; i <= SN5S330_INT_TRIP_FALL_REG3; - i++) { - i2c_read8(i2c_port, i2c_addr_flags, i, &data); - ccprintf("INT_TRIP_FALL_REG%d [%02Xh] = 0x%02x\n", - i - SN5S330_INT_TRIP_FALL_REG1 + 1, - i, - data); - } - - cflush(); - - for (i = SN5S330_INT_MASK_RISE_REG1; i <= SN5S330_INT_MASK_RISE_REG3; - i++) { - i2c_read8(i2c_port, i2c_addr_flags, i, &data); - ccprintf("INT_MASK_RISE_REG%d [%02Xh] = 0x%02x\n", - i - SN5S330_INT_MASK_RISE_REG1 + 1, - i, - data); - } - - cflush(); - - for (i = SN5S330_INT_MASK_FALL_REG1; i <= SN5S330_INT_MASK_FALL_REG3; - i++) { - i2c_read8(i2c_port, i2c_addr_flags, i, &data); - ccprintf("INT_MASK_FALL_REG%d [%02Xh] = 0x%02x\n", - i - SN5S330_INT_MASK_FALL_REG1 + 1, - i, - data); - } - - cflush(); - - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ - -static int sn5s330_pp_fet_enable(uint8_t port, enum sn5s330_pp_idx pp, - int enable) -{ - int status; - int pp_bit; - - if (pp == SN5S330_PP1) - pp_bit = SN5S330_PP1_EN; - else if (pp == SN5S330_PP2) - pp_bit = SN5S330_PP2_EN; - else - return EC_ERROR_INVAL; - - status = enable ? set_flags(port, SN5S330_FUNC_SET3, pp_bit) - : clr_flags(port, SN5S330_FUNC_SET3, pp_bit); - - if (status) { - ppc_prints("Failed to set FUNC_SET3!", port); - return status; - } - - if (pp == SN5S330_PP1) - source_enabled[port] = enable; - - return EC_SUCCESS; -} - -static int sn5s330_init(int port) -{ - int regval; - int status; - int retries; - int reg; - const int i2c_port = ppc_chips[port].i2c_port; - const uint16_t i2c_addr_flags = ppc_chips[port].i2c_addr_flags; - -#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT - /* Set the sourcing current limit value. */ - switch (CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) { - case TYPEC_RP_3A0: - /* Set current limit to ~3A. */ - regval = SN5S330_ILIM_3_06; - break; - - case TYPEC_RP_1A5: - default: - /* Set current limit to ~1.5A. */ - regval = SN5S330_ILIM_1_62; - break; - } -#else /* !defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) */ - /* Default SRC current limit to ~1.5A. */ - regval = SN5S330_ILIM_1_62; -#endif /* defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) */ - - /* - * 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_flags, - SN5S330_FUNC_SET1, regval); - if (status) { - ppc_prints("Failed to set FUNC_SET1! Retrying..", - port); - retries++; - msleep(1); - } else { - break; - } - } while (retries < 10); - - /* Set Vbus OVP threshold to ~22.325V. */ - regval = 0x37; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET5, regval); - if (status) { - ppc_prints("Failed to set FUNC_SET5!", port); - return status; - } - - /* Set Vbus UVP threshold to ~2.75V. */ - status = i2c_read8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET6, ®val); - if (status) { - ppc_prints("Failed to read FUNC_SET6!", port); - return status; - } - regval &= ~0x3F; - regval |= 1; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET6, regval); - if (status) { - ppc_prints("Failed to write FUNC_SET6!", port); - return status; - } - - /* Enable SBU Fets and set PP2 current limit to ~3A. */ - regval = SN5S330_SBU_EN | 0x8; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET2, regval); - if (status) { - ppc_prints("Failed to set FUNC_SET2!", port); - return status; - } - - /* - * Indicate we are using PP2 configuration 2 and enable OVP comparator - * for CC lines. - * - * Also, turn off under-voltage protection for incoming Vbus as it would - * prevent us from enabling SNK path before we hibernate the ec. We - * need to enable the SNK path so USB power will assert ACOK and wake - * the EC up went inserting USB power. We always turn off under-voltage - * protection because the battery charger will boost the voltage up - * to the needed battery voltage either way (and it will have its own - * low voltage protection). - */ - regval = SN5S330_OVP_EN_CC | SN5S330_PP2_CONFIG | SN5S330_CONFIG_UVP; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET9, regval); - if (status) { - ppc_prints("Failed to set FUNC_SET9!", port); - return status; - } - - /* - * Set analog current limit delay to 200 us for PP1, - * set 1000 us for PP2 for compatibility. - */ - regval = (PPX_ILIM_DEGLITCH_0_US_200 << 3) | - PPX_ILIM_DEGLITCH_0_US_1000; - status = i2c_write8(i2c_port, i2c_addr_flags, SN5S330_FUNC_SET11, - regval); - if (status) { - ppc_prints("Failed to set FUNC_SET11", port); - return status; - } - -#ifdef CONFIG_USBC_PPC_VCONN - /* - * Set the deglitch timeout on the Vconn current limit to 640us. This - * improves compatibility with some USB C -> HDMI devices versus the - * reset default (20 us). - */ - regval = 0; - status = i2c_read8(i2c_port, i2c_addr_flags, - SN5S330_FUNC_SET8, ®val); - if (status) { - ppc_prints("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_flags, - SN5S330_FUNC_SET8, regval); - if (status) { - ppc_prints("Failed to set FUNC_SET8!", port); - return status; - } -#endif /* CONFIG_USBC_PPC_VCONN */ - - /* - * Turn off dead battery resistors, turn on CC FETs, and set the higher - * of the two VCONN current limits (min 0.6A). Many VCONN accessories - * trip the default current limit of min 0.35A. - */ - status = set_flags(port, SN5S330_FUNC_SET4, - SN5S330_CC_EN | SN5S330_VCONN_ILIM_SEL); - if (status) { - ppc_prints("Failed to set FUNC_SET4!", port); - return status; - } - - /* Set ideal diode mode for both PP1 and PP2. */ - status = set_flags(port, SN5S330_FUNC_SET3, - SN5S330_SET_RCP_MODE_PP1 | SN5S330_SET_RCP_MODE_PP2); - if (status) { - ppc_prints("Failed to set FUNC_SET3!", port); - return status; - } - - /* Turn off PP1 FET. */ - status = sn5s330_pp_fet_enable(port, SN5S330_PP1, 0); - if (status) { - ppc_prints("Failed to turn off PP1 FET!", port); - } - - /* - * Don't proceed with the rest of initialization if we're sysjumping. - * We would have already done this before. - */ - if (system_jumped_late()) - return EC_SUCCESS; - - /* - * Clear the digital reset bit, and mask off and clear vSafe0V - * interrupts. Leave the dead battery mode bit unchanged since it - * is checked below. - */ - regval = SN5S330_DIG_RES | SN5S330_VSAFE0V_MASK; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_STATUS_REG4, regval); - if (status) { - ppc_prints("Failed to write INT_STATUS_REG4!", port); - return status; - } - - /* - * Before turning on the PP2 FET, mask off all unwanted interrupts and - * then clear all pending interrupts. - * - * TODO(aaboagye): Unmask fast-role swap events once fast-role swap is - * implemented in the PD stack. - */ - - /* Enable PP1 overcurrent interrupts. */ - regval = ~SN5S330_ILIM_PP1_MASK; - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_RISE_REG1, regval); - if (status) { - ppc_prints("Failed to write INT_MASK_RISE1!", port); - return status; - } - - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_FALL_REG1, 0xFF); - if (status) { - ppc_prints("Failed to write INT_MASK_FALL1!", port); - return status; - } - - /* Enable VCONN overcurrent and CC1/CC2 overvoltage interrupts. */ - regval = ~(SN5S330_VCONN_ILIM | SN5S330_CC1_CON | SN5S330_CC2_CON); - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_RISE_REG2, regval); - if (status) { - ppc_prints("Failed to write INT_MASK_RISE2!", port); - return status; - } - - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_FALL_REG2, 0xFF); - if (status) { - ppc_prints("Failed to write INT_MASK_FALL2!", port); - return status; - } - -#if defined(CONFIG_USB_PD_VBUS_DETECT_PPC) && defined(CONFIG_USB_CHARGER) - /* If PPC is being used to detect VBUS, enable VBUS interrupts. */ - regval = ~SN5S330_VBUS_GOOD_MASK; -#else - regval = 0xFF; -#endif /* CONFIG_USB_PD_VBUS_DETECT_PPC && CONFIG_USB_CHARGER */ - - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_RISE_REG3, regval); - if (status) { - ppc_prints("Failed to write INT_MASK_RISE3!", port); - return status; - } - - status = i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_MASK_FALL_REG3, regval); - if (status) { - ppc_prints("Failed to write INT_MASK_FALL3!", port); - return status; - } - - /* Now clear any pending interrupts. */ - for (reg = SN5S330_INT_TRIP_RISE_REG1; - reg <= SN5S330_INT_TRIP_FALL_REG3; - reg++) { - status = i2c_write8(i2c_port, i2c_addr_flags, - reg, 0xFF); - if (status) { - CPRINTS("ppc p%d: Failed to write reg 0x%2x!", - port, reg); - return status; - } - } - - - /* - * 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_flags, - SN5S330_INT_STATUS_REG4, ®val); - if (status) { - ppc_prints("Failed to read INT_STATUS_REG4!", port); - return status; - } - - if (regval & SN5S330_DB_BOOT) { - /* - * Clear the bit by writing 1 and keep vSafe0V_MASK - * unchanged. - */ - i2c_write8(i2c_port, i2c_addr_flags, - SN5S330_INT_STATUS_REG4, regval); - - /* Turn on PP2 FET. */ - status = sn5s330_pp_fet_enable(port, SN5S330_PP2, 1); - if (status) { - ppc_prints("Failed to turn on PP2 FET!", port); - return status; - } - } - - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC -static int sn5s330_is_vbus_present(int port) -{ - int regval; - int rv; - - rv = read_reg(port, SN5S330_INT_STATUS_REG3, ®val); - if (rv) { - ppc_err_prints("VBUS present error", port, rv); - return 0; - } - - return !!(regval & SN5S330_VBUS_GOOD); -} -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ - -static int sn5s330_is_sourcing_vbus(int port) -{ - return source_enabled[port]; -} - -#ifdef CONFIG_USBC_PPC_POLARITY -static int sn5s330_set_polarity(int port, int polarity) -{ - if (polarity) - /* CC2 active. */ - return set_flags(port, SN5S330_FUNC_SET4, SN5S330_CC_POLARITY); - else - /* CC1 active. */ - return clr_flags(port, SN5S330_FUNC_SET4, SN5S330_CC_POLARITY); -} -#endif - -static int sn5s330_set_vbus_source_current_limit(int port, - enum tcpc_rp_value rp) -{ - int regval; - int status; - - status = read_reg(port, SN5S330_FUNC_SET1, ®val); - if (status) - return status; - - /* - * Note that we chose the lowest current limit setting that is just - * above indicated Rp value. This is because these are minimum values - * and we must be able to provide the current that we advertise. - */ - regval &= ~0x1F; /* The current limit settings are 4:0. */ - switch (rp) { - case TYPEC_RP_3A0: - regval |= SN5S330_ILIM_3_06; - break; - - case TYPEC_RP_1A5: - regval |= SN5S330_ILIM_1_62; - break; - - case TYPEC_RP_USB: - default: - regval |= SN5S330_ILIM_0_63; - break; - }; - - status = write_reg(port, SN5S330_FUNC_SET1, regval); - - return status; -} - -static int sn5s330_discharge_vbus(int port, int enable) -{ - int status = enable ? set_flags(port, SN5S330_FUNC_SET3, - SN5S330_VBUS_DISCH_EN) - : clr_flags(port, SN5S330_FUNC_SET3, - SN5S330_VBUS_DISCH_EN); - - if (status) { - CPRINTS("ppc p%d: Failed to %s vbus discharge", - port, enable ? "enable" : "disable"); - return status; - } - - return EC_SUCCESS; -} - -static int sn5s330_enter_low_power_mode(int port) -{ - int rv; - - /* Turn off both SRC and SNK FETs */ - rv = clr_flags(port, SN5S330_FUNC_SET3, - SN5S330_PP1_EN | SN5S330_PP2_EN); - - if (rv) { - ppc_err_prints("Could not disable both FETS", port, rv); - return rv; - } - - /* Turn off Vconn power */ - rv = clr_flags(port, SN5S330_FUNC_SET4, SN5S330_VCONN_EN); - - if (rv) { - ppc_err_prints("Could not disable Vconn", port, rv); - return rv; - } - - /* Turn off SBU path */ - rv = clr_flags(port, SN5S330_FUNC_SET2, SN5S330_SBU_EN); - - if (rv) { - ppc_err_prints("Could not disable SBU path", port, rv); - return rv; - } - - /* - * Turn off the Over Voltage Protection circuits. Needs to happen after - * FETs are disabled, otherwise OVP can automatically turned back on. - * Since FETs are off, any over voltage does not make it to the board - * side of the PPC. - */ - rv = clr_flags(port, SN5S330_FUNC_SET9, - SN5S330_FORCE_OVP_EN_SBU | SN5S330_FORCE_ON_VBUS_OVP | - SN5S330_FORCE_ON_VBUS_UVP); - - if (rv) { - ppc_err_prints("Could not disable OVP circuit", port, rv); - return rv; - } - - return EC_SUCCESS; -} - -#ifdef CONFIG_USBC_PPC_VCONN -static int sn5s330_set_vconn(int port, int enable) -{ - int regval; - int status; - - status = read_reg(port, SN5S330_FUNC_SET4, ®val); - if (status) - return status; - - if (enable) - regval |= SN5S330_VCONN_EN; - else - regval &= ~SN5S330_VCONN_EN; - - return write_reg(port, SN5S330_FUNC_SET4, regval); -} -#endif - -static int sn5s330_vbus_sink_enable(int port, int enable) -{ - return sn5s330_pp_fet_enable(port, SN5S330_PP2, !!enable); -} - -static int sn5s330_vbus_source_enable(int port, int enable) -{ - return sn5s330_pp_fet_enable(port, SN5S330_PP1, !!enable); -} - -#ifdef CONFIG_USBC_PPC_SBU -static int sn5s330_set_sbu(int port, int enable) -{ - int rv; - - if (enable) - rv = set_flags(port, SN5S330_FUNC_SET2, SN5S330_SBU_EN); - else - rv = clr_flags(port, SN5S330_FUNC_SET2, SN5S330_SBU_EN); - - return rv; -} -#endif /* CONFIG_USBC_PPC_SBU */ - -static void sn5s330_handle_interrupt(int port) -{ - int attempt = 0; - - /* - * SN5S330's /INT pin is level, so process interrupts until it - * deasserts if the chip has a dedicated interrupt pin. - */ -#ifdef CONFIG_USBC_PPC_DEDICATED_INT - while (ppc_get_alert_status(port)) -#endif - { - int rise = 0; - int fall = 0; - - attempt++; - - if (attempt > 1) - ppc_prints("Could not clear interrupts on first " - "try, retrying", port); - - read_reg(port, SN5S330_INT_TRIP_RISE_REG1, &rise); - read_reg(port, SN5S330_INT_TRIP_FALL_REG1, &fall); - - /* Notify the system about the overcurrent event. */ - if (rise & SN5S330_ILIM_PP1_MASK) - pd_handle_overcurrent(port); - - /* Clear the interrupt sources. */ - write_reg(port, SN5S330_INT_TRIP_RISE_REG1, rise); - write_reg(port, SN5S330_INT_TRIP_FALL_REG1, fall); - - read_reg(port, SN5S330_INT_TRIP_RISE_REG2, &rise); - read_reg(port, SN5S330_INT_TRIP_FALL_REG2, &fall); - - /* - * VCONN may be latched off due to an overcurrent. Indicate - * when the VCONN overcurrent happens. - */ - if (rise & SN5S330_VCONN_ILIM) - ppc_prints("VCONN OC!", port); - - /* Notify the system about the CC overvoltage event. */ - if (rise & SN5S330_CC1_CON || rise & SN5S330_CC2_CON) { - ppc_prints("CC OV!", port); - pd_handle_cc_overvoltage(port); - } - - /* Clear the interrupt sources. */ - write_reg(port, SN5S330_INT_TRIP_RISE_REG2, rise); - write_reg(port, SN5S330_INT_TRIP_FALL_REG2, fall); - -#if defined(CONFIG_USB_PD_VBUS_DETECT_PPC) && defined(CONFIG_USB_CHARGER) - read_reg(port, SN5S330_INT_TRIP_RISE_REG3, &rise); - read_reg(port, SN5S330_INT_TRIP_FALL_REG3, &fall); - - /* Inform other modules about VBUS level */ - if (rise & SN5S330_VBUS_GOOD_MASK - || fall & SN5S330_VBUS_GOOD_MASK) - usb_charger_vbus_change(port, - sn5s330_is_vbus_present(port)); - - /* Clear the interrupt sources. */ - write_reg(port, SN5S330_INT_TRIP_RISE_REG3, rise); - write_reg(port, SN5S330_INT_TRIP_FALL_REG3, fall); -#endif /* CONFIG_USB_PD_VBUS_DETECT_PPC && CONFIG_USB_CHARGER */ - - } -} - -static void sn5s330_irq_deferred(void) -{ - int i; - uint32_t pending = atomic_clear(&irq_pending); - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - if (BIT(i) & pending) - sn5s330_handle_interrupt(i); -} -DECLARE_DEFERRED(sn5s330_irq_deferred); - -void sn5s330_interrupt(int port) -{ - atomic_or(&irq_pending, BIT(port)); - hook_call_deferred(&sn5s330_irq_deferred_data, 0); -} - -const struct ppc_drv sn5s330_drv = { - .init = &sn5s330_init, - .is_sourcing_vbus = &sn5s330_is_sourcing_vbus, - .vbus_sink_enable = &sn5s330_vbus_sink_enable, - .vbus_source_enable = &sn5s330_vbus_source_enable, - .set_vbus_source_current_limit = &sn5s330_set_vbus_source_current_limit, - .discharge_vbus = &sn5s330_discharge_vbus, - .enter_low_power_mode = &sn5s330_enter_low_power_mode, -#ifdef CONFIG_CMD_PPC_DUMP - .reg_dump = &sn5s330_dump, -#endif -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC - .is_vbus_present = &sn5s330_is_vbus_present, -#endif -#ifdef CONFIG_USBC_PPC_POLARITY - .set_polarity = &sn5s330_set_polarity, -#endif -#ifdef CONFIG_USBC_PPC_SBU - .set_sbu = &sn5s330_set_sbu, -#endif /* defined(CONFIG_USBC_PPC_SBU) */ -#ifdef CONFIG_USBC_PPC_VCONN - .set_vconn = &sn5s330_set_vconn, -#endif -}; diff --git a/driver/ppc/sn5s330.h b/driver/ppc/sn5s330.h deleted file mode 100644 index f94a3e8e10..0000000000 --- a/driver/ppc/sn5s330.h +++ /dev/null @@ -1,161 +0,0 @@ -/* 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" - -#include "driver/ppc/sn5s330_public.h" - -struct sn5s330_config { - uint8_t i2c_port; - uint8_t i2c_addr_flags; -}; - -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_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 BIT(4) - -/* FUNC_SET_3 */ -#define SN5S330_PP1_EN BIT(0) -#define SN5S330_PP2_EN BIT(1) -#define SN5S330_VBUS_DISCH_EN BIT(2) -#define SN5S330_SET_RCP_MODE_PP1 BIT(5) -#define SN5S330_SET_RCP_MODE_PP2 BIT(6) - -/* FUNC_SET_4 */ -#define SN5S330_VCONN_EN BIT(0) -#define SN5S330_CC_POLARITY BIT(1) -#define SN5S330_CC_EN BIT(4) -#define SN5S330_VCONN_ILIM_SEL BIT(5) - -/* FUNC_SET_8 */ -#define SN5S330_VCONN_DEGLITCH_MASK (3 << 6) -#define SN5S330_VCONN_DEGLITCH_63_US (0 << 6) -#define SN5S330_VCONN_DEGLITCH_125_US BIT(6) -#define SN5S330_VCONN_DEGLITCH_640_US (2 << 6) -#define SN5S330_VCONN_DEGLITCH_1280_US (3 << 6) - -/* FUNC_SET_9 */ -#define SN5S330_FORCE_OVP_EN_SBU BIT(1) -#define SN5S330_PP2_CONFIG BIT(2) -#define SN5S330_OVP_EN_CC BIT(4) -#define SN5S330_CONFIG_UVP BIT(5) -#define SN5S330_FORCE_ON_VBUS_OVP BIT(6) -#define SN5S330_FORCE_ON_VBUS_UVP BIT(7) - -/* INT_STATUS_REG3 */ -#define SN5S330_VBUS_GOOD BIT(0) - -/* INT_STATUS_REG4 */ -#define SN5S330_DIG_RES BIT(0) -#define SN5S330_DB_BOOT BIT(1) -#define SN5S330_VSAFE0V_STAT BIT(2) -#define SN5S330_VSAFE0V_MASK BIT(3) - -/* - * INT_MASK_RISE/FALL_EDGE_1 - * - * The ILIM_PP1 bit indicates an overcurrent condition when sourcing on power - * path 1. For rising edge registers, this indicates an overcurrent has - * occured; similarly for falling edge, it means the overcurrent condition is no - * longer present. - */ -#define SN5S330_ILIM_PP1_MASK BIT(4) - -/* - * INT_MASK_RISE/FALL_EDGE2 - * - * The VCONN_ILIM bit indicates an overcurrent condition on VCONN. By default, - * VCONN will be latched off. - */ -#define SN5S330_VCONN_ILIM (1 << 1) - -/* - * INT_MASK_RISE/FALL_EDGE2 - * - * The OV_CC1_CON/OV_CC2_CON bit indicates an over-voltage occurred on - * C_CC1/C_CC2. - */ -#define SN5S330_CC1_CON (1 << 2) -#define SN5S330_CC2_CON (1 << 3) - -/* - * INT_MASK_RISE/FALL_EDGE_3 - * - * The VBUS_GOOD bit indicates VBUS has increased beyond a 4.0V threshold. - * For rising edge registers, this indicates VBUS has risen above 4.0V. - * For falling edge registers, this indicates VBUS has fallen below 4.0V. - */ -#define SN5S330_VBUS_GOOD_MASK BIT(0) - -#endif /* defined(__CROS_EC_SN5S330_H) */ diff --git a/driver/ppc/syv682x.c b/driver/ppc/syv682x.c deleted file mode 100644 index 0e49abdf6a..0000000000 --- a/driver/ppc/syv682x.c +++ /dev/null @@ -1,837 +0,0 @@ -/* 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 "atomic.h" -#include "common.h" -#include "config.h" -#include "console.h" -#include "syv682x.h" -#include "hooks.h" -#include "i2c.h" -#include "system.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_pd_tcpm.h" -#include "usbc_ppc.h" -#include "usb_pd.h" -#include "util.h" - -#define SYV682X_FLAGS_SOURCE_ENABLED BIT(0) -#define SYV682X_FLAGS_SINK_ENABLED BIT(1) -/* 0 -> CC1, 1 -> CC2 */ -#define SYV682X_FLAGS_CC_POLARITY BIT(2) -#define SYV682X_FLAGS_VBUS_PRESENT BIT(3) -#define SYV682X_FLAGS_TSD BIT(4) -#define SYV682X_FLAGS_OVP BIT(5) -#define SYV682X_FLAGS_5V_OC BIT(6) -#define SYV682X_FLAGS_FRS BIT(7) -#define SYV682X_FLAGS_VCONN_OCP BIT(8) - -static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */ -static uint32_t flags[CONFIG_USB_PD_PORT_MAX_COUNT]; -/* Running count of sink ocp events */ -static uint32_t sink_ocp_count[CONFIG_USB_PD_PORT_MAX_COUNT]; -static timestamp_t vbus_oc_timer[CONFIG_USB_PD_PORT_MAX_COUNT]; -static timestamp_t vconn_oc_timer[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define SYV682X_VBUS_DET_THRESH_MV 4000 -/* Longest time that can be programmed in DSG_TIME field */ -#define SYV682X_MAX_VBUS_DISCHARGE_TIME_MS 400 -/* - * Delay between checks when polling the interrupt registers. Must be longer - * than the HW deglitch on OC (10ms) - */ -#define INTERRUPT_DELAY_MS 15 -/* Deglitch in ms of sourcing overcurrent detection */ -#define SOURCE_OC_DEGLITCH_MS 100 -#define VCONN_OC_DEGLITCH_MS 100 -/* Max. number of OC events allowed before disabling port */ -#define OCP_COUNT_LIMIT 3 - -#if INTERRUPT_DELAY_MS <= SYV682X_HW_OC_DEGLITCH_MS -#error "INTERRUPT_DELAY_MS should be greater than SYV682X_HW_OC_DEGLITCH_MS" -#endif - -#if SOURCE_OC_DEGLITCH_MS < INTERRUPT_DELAY_MS -#error "SOURCE_OC_DEGLITCH_MS should be at least INTERRUPT_DELAY_MS" -#endif - -/* When FRS is enabled, the VCONN line isn't passed through to the TCPC */ -#if defined(CONFIG_USB_PD_FRS_PPC) && defined(CONFIG_USBC_VCONN) && \ - !defined(CONFIG_USBC_PPC_VCONN) -#error "if FRS is enabled on the SYV682X, VCONN must be supplied by the PPC " -"instead of the TCPC" -#endif - -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -static int syv682x_vbus_sink_enable(int port, int enable); - -static int syv682x_init(int port); - -static void syv682x_interrupt_delayed(int port, int delay); - -static int read_reg(uint8_t port, int reg, int *regval) -{ - return i2c_read8(ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, - regval); -} - -#ifdef CONFIG_USBC_PPC_SYV682C -__overridable int syv682x_board_is_syv682c(int port) -{ - return true; -} -#endif - -/* - * During channel transition or discharge, the SYV682X silently ignores I2C - * writes. Poll the BUSY bit until the SYV682A is ready. - */ -static int syv682x_wait_for_ready(int port, int reg) -{ - int regval; - int rv; - timestamp_t deadline; - -#ifdef CONFIG_USBC_PPC_SYV682C - /* On SYV682C, busy bit is not applied to CONTROL_4 */ - if (syv682x_board_is_syv682c(port) && reg == SYV682X_CONTROL_4_REG) - return EC_SUCCESS; -#endif - - deadline.val = get_time().val - + (SYV682X_MAX_VBUS_DISCHARGE_TIME_MS * MSEC); - - do { - rv = read_reg(port, SYV682X_CONTROL_3_REG, ®val); - if (rv) - return rv; - - if (!(regval & SYV682X_BUSY)) - break; - - if (timestamp_expired(deadline, NULL)) { - ppc_prints("busy timeout", port); - return EC_ERROR_TIMEOUT; - } - - msleep(1); - } while (1); - - return EC_SUCCESS; -} - -static int write_reg(uint8_t port, int reg, int regval) -{ - int rv; - - rv = syv682x_wait_for_ready(port, reg); - if (rv) - return rv; - - return i2c_write8(ppc_chips[port].i2c_port, - ppc_chips[port].i2c_addr_flags, - reg, - regval); -} - -static int syv682x_is_sourcing_vbus(int port) -{ - return !!(flags[port] & SYV682X_FLAGS_SOURCE_ENABLED); -} - -static int syv682x_discharge_vbus(int port, int enable) -{ -#ifndef CONFIG_USBC_PPC_SYV682X_SMART_DISCHARGE - int regval; - int rv; - - rv = read_reg(port, SYV682X_CONTROL_2_REG, ®val); - 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); -#else - /* - * Smart discharge mode is enabled, nothing to do - */ - return EC_SUCCESS; -#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, ®val); - 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. - * - * De-assert the FRS GPIO, which will be asserted if we got to - * be a source via an FRS. - */ - regval |= SYV682X_CONTROL_1_PWR_ENB; - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) - gpio_set_level(ppc_chips[port].frs_en, 0); - } - - rv = write_reg(port, SYV682X_CONTROL_1_REG, regval); - if (rv) - return rv; - - if (enable) { - atomic_or(&flags[port], SYV682X_FLAGS_SOURCE_ENABLED); - atomic_clear_bits(&flags[port], SYV682X_FLAGS_SINK_ENABLED); - } else { - atomic_clear_bits(&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; -} - -/* Filter interrupts with rising edge trigger */ -static bool syv682x_interrupt_filter(int port, int regval, int regmask, - int flagmask) -{ - if (regval & regmask) { - if (!(flags[port] & flagmask)) { - atomic_or(&flags[port], flagmask); - return true; - } - } else { - atomic_clear_bits(&flags[port], flagmask); - } - return false; -} - -/* - * Two status registers can trigger the ALERT_L pin, STATUS and CONTROL_4 - * These registers are clear on read if the condition has been cleared. - * The ALERT_L pin will not de-assert if the alert condition has not been - * cleared. Since they are clear on read, we should check the alerts whenever we - * read these registers to avoid race conditions. - */ -static void syv682x_handle_status_interrupt(int port, int regval) -{ - /* - * An FRS will automatically disable sinking immediately, and enable the - * source path if VBUS is <5V. The FRS GPIO must remain asserted until - * VBUS falls below 5V. SYV682X_FLAGS_FRS signals that the SRC state was - * entered via an FRS. - * - * Note the FRS Alert will remain asserted until VBUS has fallen below - * 5V or the frs_en gpio is de-asserted. So use the rising edge trigger. - */ - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) { - if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_FRS, - SYV682X_FLAGS_FRS)) { - atomic_or(&flags[port], SYV682X_FLAGS_SOURCE_ENABLED); - atomic_clear_bits(&flags[port], - SYV682X_FLAGS_SINK_ENABLED); - if (!IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) - pd_got_frs_signal(port); - } - } - - /* - * 5V OC is actually notifying that it is current limiting - * to 3.3A. If this happens for a long time, we will trip TSD - * which will disable the channel. We should disable the sourcing path - * before that happens for safety reasons. - * - * On first check, set the flag and set the timer. This also clears the - * flag if the OC is gone. - */ - if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_OC_5V, - SYV682X_FLAGS_5V_OC)) { - vbus_oc_timer[port].val = - get_time().val + SOURCE_OC_DEGLITCH_MS * MSEC; - } else if ((regval & SYV682X_STATUS_OC_5V) && - (get_time().val > vbus_oc_timer[port].val)) { - vbus_oc_timer[port].val = UINT64_MAX; - atomic_clear_bits(&flags[port], SYV682X_FLAGS_5V_OC); - syv682x_vbus_source_enable(port, 0); - pd_handle_overcurrent(port); - } - - /* - * No PD handling for VBUS OVP or TSD events. - * For TSD, this means we are in danger of burning the device so turn - * everything off and leave it off. The power paths will be - * automatically disabled. - * In the case of OVP, the channels will be - * disabled but don't unset the sink flag, since a sink OCP can - * inadvertently cause an OVP, and we'd want to re-enable the sink - * path in that situation. - */ - if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_TSD, - SYV682X_FLAGS_TSD)) { - ppc_prints("TSD!", port); - atomic_clear_bits(&flags[port], - SYV682X_FLAGS_SOURCE_ENABLED | - SYV682X_FLAGS_SINK_ENABLED); - } - if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_OVP, - SYV682X_FLAGS_OVP)) { - ppc_prints("VBUS OVP!", port); - atomic_clear_bits(&flags[port], SYV682X_FLAGS_SOURCE_ENABLED); - } - - /* - * HV OC is a hard limit that will disable the sink path (automatically - * removing this alert condition), so try re-enabling if we hit an OCP. - * If we get multiple OCPs, don't re-enable. The OCP counter is reset on - * the sink path being explicitly disabled or on a PPC init. - */ - if (regval & SYV682X_STATUS_OC_HV) { - ppc_prints("Sink OCP!", port); - atomic_add(&sink_ocp_count[port], 1); - if ((sink_ocp_count[port] < OCP_COUNT_LIMIT) && - (flags[port] & SYV682X_FLAGS_SINK_ENABLED)) { - syv682x_vbus_sink_enable(port, 1); - } else { - ppc_prints("Disable sink", port); - atomic_clear_bits(&flags[port], - SYV682X_FLAGS_SINK_ENABLED); - } - } -} - -static int syv682x_handle_control_4_interrupt(int port, int regval) -{ - /* - * VCONN OC is actually notifying that it is current limiting - * to 600mA. If this happens for a long time, we will trip TSD - * which will disable the channel. We should disable the sourcing path - * before that happens for safety reasons. - * - * On first check, set the flag and set the timer. This also clears the - * flag if the OC is gone. - */ - if (syv682x_interrupt_filter(port, regval, SYV682X_CONTROL_4_VCONN_OCP, - SYV682X_FLAGS_VCONN_OCP)) { - vconn_oc_timer[port].val = - get_time().val + VCONN_OC_DEGLITCH_MS * MSEC; - } else if ((regval & SYV682X_CONTROL_4_VCONN_OCP) && - (get_time().val > vconn_oc_timer[port].val)) { - vconn_oc_timer[port].val = UINT64_MAX; - atomic_clear_bits(&flags[port], SYV682X_FLAGS_VCONN_OCP); - - /* Disable VCONN */ - regval &= - ~(SYV682X_CONTROL_4_VCONN2 | SYV682X_CONTROL_4_VCONN1); - write_reg(port, SYV682X_CONTROL_4_REG, regval); - - ppc_prints("VCONN OC!", port); - } - - /* - * On VBAT OVP, CC/VCONN are cut. Re-enable before sending the hard - * reset using a PPC re-init. We could reconfigure CC based on flags, - * but these will be updated anyway due to a hard reset so just re-init - * for simplicity. If this happens return an error since this isn't - * recoverable. - */ - if (regval & SYV682X_CONTROL_4_VBAT_OVP) { - ppc_prints("VBAT or CC OVP!", port); - syv682x_init(port); - pd_handle_cc_overvoltage(port); - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -static int syv682x_vbus_sink_enable(int port, int enable) -{ - int regval; - int rv; - - if (!enable) { - atomic_clear(&sink_ocp_count[port]); - atomic_clear_bits(&flags[port], SYV682X_FLAGS_SINK_ENABLED); - /* - * We're currently a source, so nothing more to do - */ - if (syv682x_is_sourcing_vbus(port)) - return EC_SUCCESS; - } else if (sink_ocp_count[port] > OCP_COUNT_LIMIT) { - /* - * Don't re-enable the channel until an explicit sink disable - * resets the ocp counter. - */ - return EC_ERROR_UNKNOWN; - } - - /* - * 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, ®val); - 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); - /* Set sink current limit to the configured value */ - regval |= CONFIG_SYV682X_HV_ILIM << SYV682X_HV_ILIM_BIT_SHIFT; - atomic_clear_bits(&flags[port], SYV682X_FLAGS_SOURCE_ENABLED); - atomic_or(&flags[port], SYV682X_FLAGS_SINK_ENABLED); - } else { - /* - * No need to change the voltage path or channel direction. But, - * turn both paths off because we are currently a sink. - */ - 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; - - if (read_reg(port, SYV682X_STATUS_REG, &val)) - return vbus; - /* - * The status register interrupt bits are clear on read, check - * register value to see if there are interrupts to avoid race - * conditions with the interrupt handler - */ - syv682x_handle_status_interrupt(port, val); - - /* - * 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) - atomic_or(&flags[port], SYV682X_FLAGS_VBUS_PRESENT); - else - atomic_clear_bits(&flags[port], SYV682X_FLAGS_VBUS_PRESENT); -#endif - - return vbus; -} -#endif - -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, ®val); - if (rv) - return rv; - - /* We need buffer room for all current values. */ - switch (rp) { - case TYPEC_RP_3A0: - limit = SYV682X_5V_ILIM_3_30; - break; - - case TYPEC_RP_1A5: - limit = SYV682X_5V_ILIM_1_75; - break; - - case TYPEC_RP_USB: - default: - /* 1.25 A is lowest current limit setting for SVY682 */ - limit = SYV682X_5V_ILIM_1_25; - break; - }; - - regval &= ~SYV682X_5V_ILIM_MASK; - regval |= (limit << SYV682X_5V_ILIM_BIT_SHIFT); - return write_reg(port, SYV682X_CONTROL_1_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) - atomic_or(&flags[port], SYV682X_FLAGS_CC_POLARITY); - else - atomic_clear_bits(&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, ®val); - if (rv) - return rv; - /* - * The control4 register interrupt bits are clear on read, check - * register value to see if there are interrupts to avoid race - * conditions with the interrupt handler - */ - rv = syv682x_handle_control_4_interrupt(port, regval); - if (rv) - return rv; - - regval &= ~(SYV682X_CONTROL_4_VCONN2 | SYV682X_CONTROL_4_VCONN1); - if (enable) { - regval |= flags[port] & SYV682X_FLAGS_CC_POLARITY ? - SYV682X_CONTROL_4_VCONN1 : - SYV682X_CONTROL_4_VCONN2; - } - - 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_flags = ppc_chips[port].i2c_addr_flags; - - for (reg_addr = SYV682X_STATUS_REG; reg_addr <= SYV682X_CONTROL_4_REG; - reg_addr++) { - rv = i2c_read8(i2c_port, i2c_addr_flags, 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 void syv682x_handle_interrupt(int port) -{ - int control4; - int status; - - /* Both interrupt registers are clear on read */ - read_reg(port, SYV682X_CONTROL_4_REG, &control4); - syv682x_handle_control_4_interrupt(port, control4); - - read_reg(port, SYV682X_STATUS_REG, &status); - syv682x_handle_status_interrupt(port, status); - - /* - * Since ALERT_L is level-triggered, check the alert status and repeat - * until all interrupts are cleared. The SYV682B and later have a 10ms - * deglitch on OC, so make sure not to check the status register again - * for at least 10ms to give it time to re-trigger. This will not spam - * indefinitely on OCP, but may on OVP, RVS, or TSD. - */ - - if (status & SYV682X_STATUS_INT_MASK || - control4 & SYV682X_CONTROL_4_INT_MASK) { - syv682x_interrupt_delayed(port, INTERRUPT_DELAY_MS); - } -} - -static void syv682x_irq_deferred(void) -{ - int i; - uint32_t pending = atomic_clear(&irq_pending); - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - if (BIT(i) & pending) - syv682x_handle_interrupt(i); -} -DECLARE_DEFERRED(syv682x_irq_deferred); - -static void syv682x_interrupt_delayed(int port, int delay) -{ - atomic_or(&irq_pending, BIT(port)); - hook_call_deferred(&syv682x_irq_deferred_data, delay * MSEC); -} - -void syv682x_interrupt(int port) -{ - /* FRS timings require <15ms response to an FRS event */ - syv682x_interrupt_delayed(port, 0); -} - -/* - * The frs_en signal can be driven from the TCPC as well (preferred). - * In that case, no PPC configuration needs to be done to enable FRS - */ -#ifdef CONFIG_USB_PD_FRS_PPC -static int syv682x_set_frs_enable(int port, int enable) -{ - int regval; - - read_reg(port, SYV682X_CONTROL_4_REG, ®val); - syv682x_handle_control_4_interrupt(port, regval); - - if (enable) { - /* - * The CC line is the FRS trigger, and VCONN should - * be ignored. The SYV682 uses the CCx_BPS fields to - * determine if CC1 or CC2 is CC and should be used for - * FRS. This CCx is also connected through to the TCPC. - * The other CCx signal (VCONN) is isolated from the - * TCPC with this write (VCONN must be provided by PPC). - * - * It is not a valid state to have both or neither - * CC_BPS bits set and the CC_FRS enabled, exactly 1 - * should be set. - */ - regval &= ~(SYV682X_CONTROL_4_CC1_BPS | - SYV682X_CONTROL_4_CC2_BPS); - regval |= flags[port] & SYV682X_FLAGS_CC_POLARITY ? - SYV682X_CONTROL_4_CC2_BPS : - SYV682X_CONTROL_4_CC1_BPS; - /* set GPIO after configuring */ - write_reg(port, SYV682X_CONTROL_4_REG, regval); - gpio_set_level(ppc_chips[port].frs_en, 1); - } else { - /* - * Reconnect CC lines to TCPC. Since the FRS GPIO needs to be - * asserted until VBUS falls below 5V during an FRS, if - * SYV682X_FLAGS_FRS is set then don't deassert it, instead - * disable when sourcing is disabled. - */ - regval |= SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS; - write_reg(port, SYV682X_CONTROL_4_REG, regval); - if (!(flags[port] & SYV682X_FLAGS_FRS)) - gpio_set_level(ppc_chips[port].frs_en, 0); - } - return EC_SUCCESS; -} -#endif /*CONFIG_USB_PD_FRS_PPC*/ - -#ifndef CONFIG_USBC_PPC_SYV682X_SMART_DISCHARGE -static int syv682x_dev_is_connected(int port, enum ppc_device_role dev) -{ - /* - * (b:160548079) We disable the smart discharge(SDSG), so we should - * turn off the discharge FET if a source is connected. - */ - if (dev == PPC_DEV_SRC) - return syv682x_discharge_vbus(port, 0); - else if (dev == PPC_DEV_DISCONNECTED) - return syv682x_discharge_vbus(port, 1); - - return EC_SUCCESS; -} -#endif - -static bool syv682x_is_sink(uint8_t control_1) -{ - /* - * The SYV682 integrates power paths: 5V and HV (high voltage). - * The SYV682 can source either 5V or HV, but only sinks on the HV path. - * - * PD analyzer without a device connected confirms the SYV682 acts as - * a source under these conditions: - * HV_DR && !CH_SEL: source 5V - * HV_DR && CH_SEL: source 15V - * !HV_DR && !CH_SEL: source 5V - * - * The SYV682 is only a sink when !HV_DR && CH_SEL - */ - if (!(control_1 & SYV682X_CONTROL_1_PWR_ENB) - && !(control_1 & SYV682X_CONTROL_1_HV_DR) - && (control_1 & SYV682X_CONTROL_1_CH_SEL)) - return true; - - return false; -} - -static int syv682x_init(int port) -{ - int rv; - int regval; - int status, control_1; - enum tcpc_rp_value initial_current_limit; - - rv = read_reg(port, SYV682X_STATUS_REG, &status); - if (rv) - return rv; - - rv = read_reg(port, SYV682X_CONTROL_1_REG, &control_1); - if (rv) - return rv; - atomic_clear(&sink_ocp_count[port]); - - /* - * Disable FRS prior to configuring the power paths - */ - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) - gpio_set_level(ppc_chips[port].frs_en, 0); - - if (!syv682x_is_sink(control_1) - || (status & SYV682X_STATUS_VSAFE_0V)) { - /* - * Disable both power paths, - * set HV_ILIM to 3.3A, - * set 5V_ILIM to 3.3A, - * set HV direction to sink, - * select HV channel. - */ - regval = SYV682X_CONTROL_1_PWR_ENB | - (CONFIG_SYV682X_HV_ILIM << SYV682X_HV_ILIM_BIT_SHIFT) | - /* !SYV682X_CONTROL_1_HV_DR */ - SYV682X_CONTROL_1_CH_SEL; - rv = write_reg(port, SYV682X_CONTROL_1_REG, regval); - if (rv) - return rv; - } else { - /* Dead battery mode, or an existing PD contract is in place */ - rv = syv682x_vbus_sink_enable(port, 1); - if (rv) - return rv; - } - -#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT - initial_current_limit = CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT; -#else - initial_current_limit = CONFIG_USB_PD_PULLUP; -#endif - rv = syv682x_set_vbus_source_current_limit(port, initial_current_limit); - if (rv) - return rv; - - /* - * Set Control Reg 2 to defaults except 50ms smart discharge time. - * Note: On SYV682A/B, enable smart discharge would block i2c - * transactions for 50ms (discharge time) and this - * prevents us from disabling Vconn when stop sourcing Vbus and has - * tVconnOff (35ms) timeout. - * On SYV682C, we are allowed to access CONTROL4 while the i2c busy. - */ - regval = (SYV682X_OC_DELAY_10MS << SYV682X_OC_DELAY_SHIFT) - | (SYV682X_DSG_RON_200_OHM << SYV682X_DSG_RON_SHIFT) - | (SYV682X_DSG_TIME_50MS << SYV682X_DSG_TIME_SHIFT); - - if (IS_ENABLED(CONFIG_USBC_PPC_SYV682X_SMART_DISCHARGE)) - regval |= SYV682X_CONTROL_2_SDSG; - - rv = write_reg(port, SYV682X_CONTROL_2_REG, regval); - if (rv) - return rv; - - /* - * Always set the over voltage setting to the maximum to support - * sinking from a 20V PD charger. The common PPC code doesn't provide - * any hooks for indicating what the currently negotiated voltage is. - * - * Mask Alerts due to Reverse Voltage. - */ - regval = (SYV682X_OVP_23_7 << SYV682X_OVP_BIT_SHIFT) | SYV682X_RVS_MASK; - rv = write_reg(port, SYV682X_CONTROL_3_REG, regval); - if (rv) - return rv; - - /* - * Remove Rd and connect CC1/CC2 lines to TCPC - * Disable Vconn - * Enable CC detection of Fast Role Swap (FRS) - */ - regval = SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS; - 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_FRS_PPC - .set_frs_enable = &syv682x_set_frs_enable, -#endif -#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, -#ifndef CONFIG_USBC_PPC_SYV682X_SMART_DISCHARGE - .dev_is_connected = &syv682x_dev_is_connected, -#endif /* defined(CONFIG_USBC_PPC_SYV682X_SMART_DISCHARGE) */ -#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 deleted file mode 100644 index d9416f47f1..0000000000 --- a/driver/ppc/syv682x.h +++ /dev/null @@ -1,112 +0,0 @@ -/* 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 - -#include "common.h" -#include "driver/ppc/syv682x_public.h" - -/* Source OC deglitch implemented in HW for SYV682B */ -#define SYV682X_HW_OC_DEGLITCH_MS 10 - -/* 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_OC_HV BIT(7) -#define SYV682X_STATUS_RVS BIT(6) -#define SYV682X_STATUS_OC_5V BIT(5) -#define SYV682X_STATUS_OVP BIT(4) -#define SYV682X_STATUS_FRS BIT(3) -#define SYV682X_STATUS_TSD BIT(2) -#define SYV682X_STATUS_VSAFE_5V BIT(1) -#define SYV682X_STATUS_VSAFE_0V BIT(0) -#define SYV682X_STATUS_INT_MASK 0xfc - -/* 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_5V_ILIM_MASK 0x18 -#define SYV682X_5V_ILIM_BIT_SHIFT 3 -#define SYV682X_5V_ILIM_1_25 0 -#define SYV682X_5V_ILIM_1_75 1 -#define SYV682X_5V_ILIM_2_25 2 -#define SYV682X_5V_ILIM_3_30 3 - -#define SYV682X_HV_ILIM_MASK 0x60 -#define SYV682X_HV_ILIM_BIT_SHIFT 5 -#define SYV682X_HV_ILIM_1_25 0 -#define SYV682X_HV_ILIM_1_75 1 -#define SYV682X_HV_ILIM_3_30 2 -#define SYV682X_HV_ILIM_5_50 3 - -/* Control Register 2 */ -#define SYV682X_OC_DELAY_MASK GENMASK(7, 6) -#define SYV682X_OC_DELAY_SHIFT 6 -#define SYV682X_OC_DELAY_1MS 0 -#define SYV682X_OC_DELAY_10MS 1 -#define SYV682X_OC_DELAY_50MS 2 -#define SYV682X_OC_DELAY_100MS 3 -#define SYV682X_DSG_TIME_MASK GENMASK(5, 4) -#define SYV682X_DSG_TIME_SHIFT 4 -#define SYV682X_DSG_TIME_50MS 0 -#define SYV682X_DSG_TIME_100MS 1 -#define SYV682X_DSG_TIME_200MS 2 -#define SYV682X_DSG_TIME_400MS 3 -#define SYV682X_DSG_RON_MASK GENMASK(3, 2) -#define SYV682X_DSG_RON_SHIFT 2 -#define SYV682X_DSG_RON_200_OHM 0 -#define SYV682X_DSG_RON_400_OHM 1 -#define SYV682X_DSG_RON_800_OHM 2 -#define SYV682X_DSG_RON_1600_OHM 3 -#define SYV682X_CONTROL_2_SDSG BIT(1) -#define SYV682X_CONTROL_2_FDSG BIT(0) - -/* Control Register 3 */ -#define SYV682X_BUSY BIT(7) -#define SYV682X_RVS_MASK BIT(3) -#define SYV682X_RST_REG BIT(0) -#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) -#define SYV682X_CONTROL_4_INT_MASK 0x0c - -/* - * syv682x_board_is_syv682c - * - * b:160548079 This is a function to workaround that some board revisions - * might have different SYV682 sub-version. - * - * @param port the query port - * @return 1 if the PPC is SYV682C else 0 - */ -__override_proto int syv682x_board_is_syv682c(int port); - -#endif /* defined(__CROS_EC_SYV682X_H) */ |