blob: f17802c82ac5868eaf659055a214d7ca444627a8 [file] [log] [blame]
/* Copyright 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "adc.h"
#include "board.h"
#include "common.h"
#include "console.h"
#include "ec_commands.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_bb.h"
#include "usb_api.h"
#include "usb_pd.h"
#include "version.h"
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
#define PDO_FIXED_FLAGS PDO_FIXED_COMM_CAP
/* Source PDOs */
const uint32_t pd_src_pdo[] = {};
const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
/* Fake PDOs : we just want our pre-defined voltages */
const uint32_t pd_snk_pdo[] = {
PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
};
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
/* Holds valid object position (opos) for entered mode */
static int alt_mode[PD_AMODE_COUNT];
void pd_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage)
{
/* No battery, nothing to do */
return;
}
int pd_is_valid_input_voltage(int mv)
{
/* Any voltage less than the max is allowed */
return 1;
}
void pd_transition_voltage(int idx)
{
/* No operation: sink only */
}
int pd_set_power_supply_ready(int port)
{
return EC_SUCCESS;
}
void pd_power_supply_reset(int port)
{
}
int pd_snk_is_vbus_provided(int port)
{
return 1;
}
int pd_board_checks(void)
{
return EC_SUCCESS;
}
int pd_check_power_swap(int port)
{
/* Always refuse power swap */
return 0;
}
int pd_check_data_swap(int port,
enum pd_data_role data_role)
{
/* Always refuse data swap */
return 0;
}
void pd_execute_data_swap(int port,
enum pd_data_role data_role)
{
/* Do nothing */
}
void pd_check_pr_role(int port,
enum pd_power_role pr_role,
int flags)
{
}
void pd_check_dr_role(int port,
enum pd_data_role dr_role,
int flags)
{
}
/* ----------------- Vendor Defined Messages ------------------ */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
1, /* data caps as USB device */
IDH_PTYPE_AMA, /* Alternate mode */
1, /* supports alt modes */
USB_VID_GOOGLE);
const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV);
const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS,
CONFIG_USB_PD_IDENTITY_SW_VERS,
0, 0, 0, 0, /* SS[TR][12] */
0, /* Vconn power */
0, /* Vconn power required */
1, /* Vbus power required */
AMA_USBSS_BBONLY /* USB SS support */);
static int svdm_response_identity(int port, uint32_t *payload)
{
payload[VDO_I(IDH)] = vdo_idh;
/* TODO(tbroch): Do we plan to obtain TID (test ID) for hoho */
payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
payload[VDO_I(PRODUCT)] = vdo_product;
payload[VDO_I(AMA)] = vdo_ama;
return VDO_I(AMA) + 1;
}
static int svdm_response_svids(int port, uint32_t *payload)
{
payload[1] = VDO_SVID(USB_SID_DISPLAYPORT, USB_VID_GOOGLE);
payload[2] = 0;
return 3;
}
#define OPOS_DP 1
#define OPOS_GFU 1
const uint32_t vdo_dp_modes[1] = {
VDO_MODE_DP(0, /* UFP pin cfg supported : none */
MODE_DP_PIN_E, /* DFP pin cfg supported */
1, /* no usb2.0 signalling in AMode */
CABLE_PLUG, /* its a plug */
MODE_DP_V13, /* DPv1.3 Support, no Gen2 */
MODE_DP_SNK) /* Its a sink only */
};
const uint32_t vdo_goog_modes[1] = {
VDO_MODE_GOOGLE(MODE_GOOGLE_FU)
};
static int svdm_response_modes(int port, uint32_t *payload)
{
if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) {
memcpy(payload + 1, vdo_dp_modes, sizeof(vdo_dp_modes));
return ARRAY_SIZE(vdo_dp_modes) + 1;
} else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) {
memcpy(payload + 1, vdo_goog_modes, sizeof(vdo_goog_modes));
return ARRAY_SIZE(vdo_goog_modes) + 1;
} else {
return 0; /* nak */
}
}
static int dp_status(int port, uint32_t *payload)
{
int opos = PD_VDO_OPOS(payload[0]);
int hpd = gpio_get_level(GPIO_DP_HPD);
if (opos != OPOS_DP)
return 0; /* nak */
payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */
(hpd == 1), /* HPD_HI|LOW */
0, /* request exit DP */
0, /* request exit USB */
0, /* MF pref */
gpio_get_level(GPIO_PD_SBU_ENABLE),
0, /* power low */
0x2);
return 2;
}
static int dp_config(int port, uint32_t *payload)
{
if (PD_DP_CFG_DPON(payload[1]))
gpio_set_level(GPIO_PD_SBU_ENABLE, 1);
return 1;
}
static int svdm_enter_mode(int port, uint32_t *payload)
{
int rv = 0; /* will generate a NAK */
/* SID & mode request is valid */
if ((PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) &&
(PD_VDO_OPOS(payload[0]) == OPOS_DP)) {
alt_mode[PD_AMODE_DISPLAYPORT] = OPOS_DP;
rv = 1;
pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 1, NULL);
} else if ((PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) &&
(PD_VDO_OPOS(payload[0]) == OPOS_GFU)) {
alt_mode[PD_AMODE_GOOGLE] = OPOS_GFU;
rv = 1;
}
if (rv)
/*
* If we failed initial mode entry we'll have enumerated the USB
* Billboard class. If so we should disconnect.
*/
usb_disconnect();
return rv;
}
int pd_alt_mode(int port, enum tcpm_transmit_type type, uint16_t svid)
{
if (type != TCPC_TX_SOP)
return 0;
if (svid == USB_SID_DISPLAYPORT)
return alt_mode[PD_AMODE_DISPLAYPORT];
else if (svid == USB_VID_GOOGLE)
return alt_mode[PD_AMODE_GOOGLE];
return 0;
}
static int svdm_exit_mode(int port, uint32_t *payload)
{
if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) {
gpio_set_level(GPIO_PD_SBU_ENABLE, 0);
alt_mode[PD_AMODE_DISPLAYPORT] = 0;
pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 0, NULL);
} else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) {
alt_mode[PD_AMODE_GOOGLE] = 0;
} else {
CPRINTF("Unknown exit mode req:0x%08x\n", payload[0]);
}
return 1; /* Must return ACK */
}
static struct amode_fx dp_fx = {
.status = &dp_status,
.config = &dp_config,
};
const struct svdm_response svdm_rsp = {
.identity = &svdm_response_identity,
.svids = &svdm_response_svids,
.modes = &svdm_response_modes,
.enter_mode = &svdm_enter_mode,
.amode = &dp_fx,
.exit_mode = &svdm_exit_mode,
};
int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
int rsize;
if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE ||
!alt_mode[PD_AMODE_GOOGLE])
return 0;
*rpayload = payload;
rsize = pd_custom_flash_vdm(port, cnt, payload);
if (!rsize) {
int cmd = PD_VDO_CMD(payload[0]);
switch (cmd) {
case VDO_CMD_GET_LOG:
rsize = pd_vdm_get_log_entry(payload);
break;
default:
/* Unknown : do not answer */
return 0;
}
}
/* respond (positively) to the request */
payload[0] |= VDO_SRC_RESPONDER;
return rsize;
}