blob: bd20ecc1f1ecbbd9fb9761d3f047e3b22d63cdfe [file] [log] [blame]
/* 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 "common.h"
#include "config.h"
#include "console.h"
#include "driver/ppc/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)
/* 0 -> CC1, 1 -> CC2 */
#define SYV682X_FLAGS_CC_POLARITY BIT(1)
#define SYV682X_FLAGS_VBUS_PRESENT BIT(2)
#define SYV682X_FLAGS_OCP BIT(3)
#define SYV682X_FLAGS_OVP BIT(4)
#define SYV682X_FLAGS_5V_OC BIT(5)
#define SYV682X_FLAGS_RVS BIT(6)
#define SYV682X_FLAGS_VCONN_OCP BIT(7)
static uint32_t irq_pending; /* Bitmask of ports signaling an interrupt. */
static uint8_t flags[CONFIG_USB_PD_PORT_MAX_COUNT];
static timestamp_t 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 */
#define INTERRUPT_DELAY_MS 10
/* Deglitch in ms of sourcing overcurrent detection */
#define SOURCE_OC_DEGLITCH_MS 100
#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_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);
}
/*
* During channel transition or discharge, the SYV682A silently ignores I2C
* writes. Poll the BUSY bit until the SYV682A is ready.
*/
static int syv682x_wait_for_ready(int port)
{
int regval;
int rv;
timestamp_t deadline;
deadline.val = get_time().val
+ (SYV682X_MAX_VBUS_DISCHARGE_TIME_MS * MSEC);
do {
rv = read_reg(port, SYV682X_CONTROL_3_REG, &regval);
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);
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)
{
int regval;
int rv;
rv = read_reg(port, SYV682X_CONTROL_2_REG, &regval);
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);
}
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, &regval);
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.
*/
regval |= SYV682X_CONTROL_1_PWR_ENB;
}
rv = write_reg(port, SYV682X_CONTROL_1_REG, regval);
if (rv)
return rv;
if (enable)
flags[port] |= SYV682X_FLAGS_SOURCE_ENABLED;
else
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)) {
flags[port] |= flagmask;
return true;
}
} else {
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 enable the source path */
if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC) &&
(regval & SYV682X_STATUS_FRS)) {
flags[port] |= SYV682X_FLAGS_SOURCE_ENABLED;
/*
* Workaround for bug in SYV692.
*
* The SYV682X has an FRS trigger but it is broken in some
* versions of the part. The old parts require VBUS to fall to
* generate the interrupt, it needs to be generated on CC alone.
*
* The workaround is to use to the TCPC trigger if
* available. When the part is fixed, the TCPC trigger is no
* longer needed. If the TCPC doesn't have a trigger, FRS timing
* may be violated for slow-vbus discharge cases.
*/
if (!IS_ENABLED(CONFIG_USB_PD_FRS_TCPC))
pd_got_frs_signal(port);
}
/* These conditions automatically turn off VBUS sourcing */
if (regval & (SYV682X_STATUS_OVP | SYV682X_STATUS_TSD))
flags[port] &= ~SYV682X_FLAGS_SOURCE_ENABLED;
/*
* 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)) {
oc_timer[port].val =
get_time().val + SOURCE_OC_DEGLITCH_MS * MSEC;
} else if ((regval & SYV682X_STATUS_OC_5V) &&
(get_time().val > oc_timer[port].val)) {
oc_timer[port].val = UINT64_MAX;
flags[port] &= ~SYV682X_FLAGS_5V_OC;
syv682x_vbus_source_enable(port, 0);
pd_handle_overcurrent(port);
}
if (syv682x_interrupt_filter(port, regval,
SYV682X_STATUS_OC_HV | SYV682X_STATUS_TSD,
SYV682X_FLAGS_OCP))
pd_handle_overcurrent(port);
/* No PD handler for VBUS OVP/RVS events */
if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_OVP,
SYV682X_FLAGS_OVP)) {
ppc_prints("VBUS OVP!", port);
}
if (syv682x_interrupt_filter(port, regval, SYV682X_STATUS_RVS,
SYV682X_FLAGS_RVS)) {
ppc_prints("VBUS Reverse Voltage!", port);
}
}
static void syv682x_handle_control_4_interrupt(int port, int regval)
{
if (syv682x_interrupt_filter(port, regval, SYV682X_CONTROL_4_VCONN_OCP,
SYV682X_FLAGS_VCONN_OCP)) {
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 (regval & SYV682X_CONTROL_4_VBAT_OVP) {
ppc_prints("VBAT OVP!", port);
syv682x_init(port);
pd_handle_cc_overvoltage(port);
}
}
static int syv682x_vbus_sink_enable(int port, int enable)
{
int regval;
int rv;
if (!enable && syv682x_is_sourcing_vbus(port)) {
/*
* We're currently a source, so nothing more to do
*/
return EC_SUCCESS;
}
/*
* 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, &regval);
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;
flags[port] &= ~SYV682X_FLAGS_SOURCE_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)
flags[port] |= SYV682X_FLAGS_VBUS_PRESENT;
else
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, &regval);
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)
flags[port] |= SYV682X_FLAGS_CC_POLARITY;
else
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, &regval);
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
*/
syv682x_handle_control_4_interrupt(port, regval);
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. This will not spam indefinitely on
* OCP, but may on OVP, RVS, or TSD
*/
if (IS_ENABLED(CONFIG_USBC_PPC_DEDICATED_INT) &&
ppc_get_alert_status(port)) {
syv682x_interrupt_delayed(port, INTERRUPT_DELAY_MS);
} else {
read_reg(port, SYV682X_CONTROL_4_REG, &control4);
read_reg(port, SYV682X_STATUS_REG, &status);
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 status;
int regval;
read_reg(port, SYV682X_CONTROL_4_REG, &regval);
syv682x_handle_control_4_interrupt(port, regval);
if (enable) {
read_reg(port, SYV682X_STATUS_REG, &status);
syv682x_handle_status_interrupt(port, status);
/*
* Workaround for a bug in SYV682A
*
* The bug is that VBUS needs to be below VBAT when CC is pulled
* low to trigger FRS. This is fine when charging at 5V usually,
* but often not when charging at higher voltages. At higher
* voltages, the CC trigger needs to be disabled for the broken
* parts.
*
* TODO (b/161372139): When this is fixed in SYV682B, always use
* the CC trigger.
*/
if (status & SYV682X_STATUS_VSAFE_5V) {
/* Inverted register, clear to enable CC detection */
regval &= ~SYV682X_CONTROL_4_CC_FRS;
/*
* 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;
} else {
regval |= SYV682X_CONTROL_4_CC_FRS;
}
} else {
/*
* Disabling FRS is part of the disconnect sequence, reconnect
* CC lines to TCPC.
*/
regval |= SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS;
}
write_reg(port, SYV682X_CONTROL_4_REG, regval);
gpio_set_level(ppc_chips[port].frs_en, enable);
return EC_SUCCESS;
}
#endif /*CONFIG_USB_PD_FRS_PPC*/
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;
}
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;
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.
* Note: do not enable smart discharge since it would block
* i2c transactions for 50ms (discharge time) and this prevents
* us from disabling Vconn when stop sourcing Vbus and has tVconnOff
* (35ms) timeout.
*/
regval = (SYV682X_OC_DELAY_10MS << SYV682X_OC_DELAY_SHIFT)
| (SYV682X_DSG_TIME_200MS << SYV682X_DSG_TIME_SHIFT)
| (SYV682X_DSG_RON_200_OHM << SYV682X_DSG_RON_SHIFT);
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.
*/
regval = (SYV682X_OVP_23_7 << SYV682X_OVP_BIT_SHIFT);
rv = write_reg(port, SYV682X_CONTROL_3_REG, regval);
if (rv)
return rv;
/*
* Remove Rd and connect CC1/CC2 lines to TCPC
* Disable Vconn
* Disable CC detection of Fast Role Swap (FRS)
*/
regval = SYV682X_CONTROL_4_CC1_BPS | SYV682X_CONTROL_4_CC2_BPS |
SYV682X_CONTROL_4_CC_FRS;
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,
.dev_is_connected = &syv682x_dev_is_connected,
#ifdef CONFIG_USBC_PPC_POLARITY
.set_polarity = &syv682x_set_polarity,
#endif
#ifdef CONFIG_USBC_PPC_VCONN
.set_vconn = &syv682x_set_vconn,
#endif
};