blob: 98605fe08b1d6026079876cf9e41b5a2d7498f9d [file] [log] [blame]
/* 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 "driver/ppc/aoz1380.h"
#include "hooks.h"
#include "system.h"
#include "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)
{
int rv;
bool is_sinking, is_sourcing;
flags[port] = 0;
rv = tcpm_get_snk_ctrl(port, &is_sinking);
if (rv == EC_SUCCESS && is_sinking)
AOZ1380_SET_FLAG(port, AOZ1380_FLAGS_SINK_ENABLED);
rv = tcpm_get_src_ctrl(port, &is_sourcing);
if (rv == EC_SUCCESS && is_sourcing)
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,
};