pd: VDM Alternate mode support.

Successfully communicate SVDM for discovery (identity, svids, modes)
and enter mode.

Still need to:
- Add same functionality on when power role is sink too.
- determine what connected events would require exit mode.
- do proper cleanup on disconnect.
- implement real display port 'enter' mode for samus_pd
- test & cleanup

Additionally the USB Billboard class functionality needs to be added
but will likely do that in a separate CL.

BRANCH=none
BUG=chrome-os-partner:28342
TEST=manual,

From fruitpie,
    [Image: RO, fruitpie_v1.1.2263-d79140d-dirty 2014-09-29 17:44:15 tbroch@brisket.mtv.corp.google.com]
    [0.000383 Inits done]
    C0 st2
    Console is enabled; type HELP for help.
    > [0.250551 USB PD initialized]
    pd dualrole source
    C0 st8
    > [8.366335 PD TMOUT RX 1/1]
    RX ERR (-1)
    [8.478308 PD TMOUT RX 1/1]
    RX ERR (-1)
    [8.590280 PD TMOUT RX 1/1]
    RX ERR (-1)
    C0 st9
    Switch to 5000 V 3000 mA (for 3000/3000 mA)
    C0 st10
    C0 st11
    C0 st12
    8.867593] SVDM/4 [1] ff008081 340018d1 00000000 17000008
    8.867906] DONE
    8.871006] SVDM/2 [2] ff008082 ff010000
    8.871224] DONE
    8.875092] SVDM/7 [3] ff018083 00100081 00000000 00000000 00000000 00000000 00000000
    Entering mode w/ vdo = 00100081
    8.875492] DONE
    8.878435] SVDM/1 [4] ff018144
    8.878612] DONE

    > pe 0 dump
    SVID[0]: ff01 [0] 00100081 [1] 00000000 [2] 00000000 [3] 00000000 [4] 00000000 [5] 00000000
    MODE[0]: svid:ff01 mode:1 caps:00100081

From hoho,
    [Image: RO, hoho_v1.1.2263-d79140d-dirty 2014-09-29 17:54:59 tbroch@brisket.mtv.corp.google.com]
    [0.000375 Inits done]
    C0 st2
    Console is enabled; type HELP for help.
    > [0.250542 USB PD initialized]
    C0 st3
    [0.264637 PD TMOUT RX 1/1]
    RX ERR (-1)
    Request [1] 5V 3000mA
    C0 st4
    C0 st5
    C0 st6
    0.487451] SVDM/1 [1] ff008001
    0.487628] DONE
    0.491190] SVDM/1 [2] ff008002
    0.491346] DONE
    0.494510] SVDM/1 [3] ff018003
    0.494667] DONE
    0.498777] SVDM/1 [4] ff018104
    0.498934] DONE

Change-Id: I5e2b7802c66b8aaad97e5120dca7a02820086bc1
Signed-off-by: Todd Broch <tbroch@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/219513
Reviewed-by: Alec Berg <alecaberg@chromium.org>
This commit is contained in:
Todd Broch
2014-09-17 21:39:15 -07:00
committed by chrome-internal-fetch
parent 0938563284
commit a194bede19
12 changed files with 822 additions and 46 deletions

View File

@@ -22,6 +22,8 @@
#define CONFIG_USB_MS_BUFFER_SIZE SPI_FLASH_MAX_WRITE_SIZE
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_USB_PD_ALT_MODE
#define CONFIG_USB_PD_ALT_MODE_DFP
#define CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_INTERNAL_COMP
#define CONFIG_USBC_SS_MUX

View File

@@ -136,7 +136,14 @@ int pd_board_checks(void)
}
/* ----------------- Vendor Defined Messages ------------------ */
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
const struct svdm_response svdm_rsp = {
.identity = NULL,
.svids = NULL,
.modes = NULL,
};
static int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
int cmd = PD_VDO_CMD(payload[0]);
uint16_t dev_id = 0;
@@ -171,3 +178,50 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
return 0;
}
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
if (PD_VDO_SVDM(payload[0]))
return pd_svdm(port, cnt, payload, rpayload);
else
return pd_custom_vdm(port, cnt, payload, rpayload);
}
static void svdm_enter_dp_mode(uint32_t mode_caps)
{
ccprintf("Entering mode w/ vdo = %08x\n", mode_caps);
}
static void svdm_exit_dp_mode(void)
{
ccprintf("Exiting mode\n");
/* return to safe config */
}
static struct svdm_amode_data supported_modes[] = {
{
.svid = 0xff01,
.enter = &svdm_enter_dp_mode,
.exit = &svdm_exit_dp_mode,
},
};
void pd_dfp_choose_modes(struct pd_policy *pe)
{
int i, j;
struct svdm_amode_data *modep;
pe->amode_cnt = sizeof(supported_modes) / sizeof(struct
svdm_amode_data);
pe->amodes = modep = supported_modes;
for (i = 0; i < pe->amode_cnt; i++) {
for (j = 0; j < pe->svid_cnt; j++) {
if (pe->svids[j].svid == modep->svid) {
/* TODO(tbroch) need more elaborate mode
resolution */
modep->mode_caps = &pe->svids[j].mode_vdo[0];
modep->amode = dfp_amode1;
break;
}
}
modep++;
}
}

View File

@@ -26,8 +26,12 @@
#define CONFIG_SPI_MASTER_PORT 2
#define CONFIG_SPI_CS_GPIO GPIO_PD_MCDP_SPI_CS_L
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_ALT_MODE
#define CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_USB_PD_INTERNAL_COMP
#define CONFIG_USB_PD_IDENTITY_HW_VERS 1
#define CONFIG_USB_PD_IDENTITY_SW_VERS 1
#undef CONFIG_WATCHDOG_HELP
#undef CONFIG_LID_SWITCH
#undef CONFIG_TASK_PROFILING

View File

@@ -14,6 +14,7 @@
#include "timer.h"
#include "util.h"
#include "usb_pd.h"
#include "version.h"
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
@@ -23,7 +24,7 @@ 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, 0),
PDO_FIXED(5000, 500, 0),
};
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
@@ -88,3 +89,119 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
/* ----------------- Vendor Defined Messages ------------------ */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
0, /* data caps as USB device */
IDH_PTYPE_AMA, /* Alternate mode */
1, /* supports alt modes */
USB_VID_GOOGLE);
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 */
0 /* 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(AMA)] = vdo_ama;
return 4;
}
static int svdm_response_svids(int port, uint32_t *payload)
{
payload[1] = VDO_SVID(USB_SID_DISPLAYPORT, 0);
return 2;
}
const uint32_t vdo_dp_mode[1] = {
VDO_MODE_DP(MODE_DP_PIN_E, /* sink pin cfg */
0, /* no src pin cfg */
1, /* no usb2.0 signalling in AMode */
CABLE_PLUG, /* its a plug */
0, /* no GEN2 usb */
0, /* no dp 1.3 support */
MODE_DP_SNK) /* Its a sink only */
};
static int svdm_response_modes(int port, uint32_t *payload)
{
if (PD_VDO_VID(payload[0]) != USB_SID_DISPLAYPORT) {
/* TODO(tbroch) USB billboard enabled here then */
return 1; /* will generate a NAK */
}
memset(payload + 1, 0, sizeof(uint32_t) * PDO_MODES);
payload[1] = vdo_dp_mode[0];
/* TODO(tbroch) does spec have mechanism for identifying valid modes
* returned for svid? */
return PDO_MAX_OBJECTS;
}
static int svdm_enter_mode(int port, uint32_t *payload)
{
/* SID & mode request is valid */
if ((PD_VDO_VID(payload[0]) != USB_SID_DISPLAYPORT) ||
(PD_VDO_OPOS(payload[0]) != 1))
return 1; /* will generate a NAK */
gpio_set_level(GPIO_PD_SBU_ENABLE, 1);
payload[1] = 0;
return 1;
}
static int svdm_exit_mode(int port, uint32_t *payload)
{
/* SID & mode request is valid */
if ((PD_VDO_VID(payload[0]) != USB_SID_DISPLAYPORT) ||
(PD_VDO_OPOS(payload[0]) != 1))
return 1; /* will generate a NAK */
gpio_set_level(GPIO_PD_SBU_ENABLE, 0);
payload[1] = 0;
return 1;
}
const struct svdm_response svdm_rsp = {
.identity = &svdm_response_identity,
.svids = &svdm_response_svids,
.modes = &svdm_response_modes,
.enter_mode = &svdm_enter_mode,
.exit_mode = &svdm_exit_mode,
};
static int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
int cmd = PD_VDO_CMD(payload[0]);
int rsize = 1;
ccprintf("%T] VDM/%d [%d] %08x\n", cnt, cmd, payload[0]);
*rpayload = payload;
switch (cmd) {
case VDO_CMD_VERSION:
memcpy(payload + 1, &version_data.version, 24);
rsize = 7;
break;
default:
/* Unknown : do not answer */
return 0;
}
ccprintf("%T] DONE\n");
/* respond (positively) to the request */
payload[0] |= VDO_SRC_RESPONDER;
return rsize;
}
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
if (PD_VDO_SVDM(payload[0]))
return pd_svdm(port, cnt, payload, rpayload);
else
return pd_custom_vdm(port, cnt, payload, rpayload);
}

View File

@@ -158,7 +158,8 @@ static void pd_send_host_event(void)
}
/* ----------------- Vendor Defined Messages ------------------ */
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
static int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
int cmd = PD_VDO_CMD(payload[0]);
uint16_t dev_id = 0;
@@ -199,6 +200,14 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
return 0;
}
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
if (PD_VDO_SVDM(payload[0]))
return pd_svdm(port, cnt, payload, rpayload);
else
return pd_custom_vdm(port, cnt, payload, rpayload);
}
/****************************************************************************/
/* Console commands */
static int command_ec_int(int argc, char **argv)

View File

@@ -396,7 +396,8 @@ uint32_t *pd_get_info(void)
return info_data;
}
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
static int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
static int flash_offset;
int cmd = PD_VDO_CMD(payload[0]);
@@ -463,3 +464,11 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
return rsize;
}
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
if (PD_VDO_SVDM(payload[0]))
return pd_svdm(port, cnt, payload, rpayload);
else
return pd_custom_vdm(port, cnt, payload, rpayload);
}

View File

@@ -71,7 +71,7 @@ common-$(CONFIG_SW_CRC)+=crc.o
common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o throttle_ap.o
common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o
common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o
common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o
common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o
common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o
common-$(CONFIG_WIRELESS)+=wireless.o
common-$(HAS_TASK_CHIPSET)+=chipset.o

294
common/usb_pd_policy.c Normal file
View File

@@ -0,0 +1,294 @@
/* Copyright (c) 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 "atomic.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "host_command.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_pd.h"
#include "usb_pd_config.h"
#include "version.h"
#ifdef CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
struct pd_policy pe[PD_PORT_COUNT];
static void pe_init(int port)
{
memset(pe, 0, sizeof(struct pd_policy) * PD_PORT_COUNT);
}
static void dfp_consume_identity(int port, uint32_t *payload)
{
int ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]);
pe_init(port);
switch (ptype) {
case IDH_PTYPE_AMA:
/* TODO(tbroch) do I disable VBUS here if power contract
* requested it
*/
if (!PD_VDO_AMA_VBUS_REQ(payload[VDO_I(AMA)]))
pd_power_supply_reset(port);
break;
/* TODO(crosbug.com/p/30645) provide vconn support here */
default:
break;
}
}
static int dfp_discover_svids(int port, uint32_t *payload)
{
payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID);
return 1;
}
static void dfp_consume_svids(int port, uint32_t *payload)
{
int i;
uint32_t *ptr = payload + 1;
uint16_t svid0, svid1;
for (i = pe[port].svid_cnt; i < pe[port].svid_cnt + 12; i += 2) {
if (i == SVID_DISCOVERY_MAX) {
ccprintf("ERR: too many svids discovered\n");
break;
}
svid0 = PD_VDO_SVID_SVID0(*ptr);
if (!svid0)
break;
pe[port].svids[i].svid = svid0;
pe[port].svid_cnt++;
svid1 = PD_VDO_SVID_SVID1(*ptr);
if (!svid1)
break;
pe[port].svids[i + 1].svid = svid1;
pe[port].svid_cnt++;
ptr++;
}
/* TODO(tbroch) need to re-issue discover svids if > 12 */
if (i && ((i % 12) == 0))
ccprintf("TODO: need to re-issue discover svids > 12\n");
}
static int dfp_discover_modes(int port, uint32_t *payload)
{
uint16_t svid = pe[port].svids[pe[port].svid_idx].svid;
payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
return 1;
}
static int dfp_consume_modes(int port, uint32_t *payload)
{
memcpy(pe[port].svids[pe[port].svid_idx].mode_vdo, &payload[1],
sizeof(uint32_t) * PDO_MODES);
pe[port].svid_idx++;
return (pe[port].svid_idx < pe[port].svid_cnt);
}
static int dfp_enter_modes(int port, uint32_t *payload)
{
struct svdm_amode_data *modep;
if (pe[port].amodes == NULL)
pd_dfp_choose_modes(&pe[port]);
modep = &pe[port].amodes[pe[port].amode_idx];
if (modep->amode == dfp_amode_none)
return 0;
modep->enter(*modep->mode_caps);
payload[0] = VDO(modep->svid, 1,
CMD_ENTER_MODE |
VDO_OPOS(modep->amode));
pe[port].amode_idx++;
return 1;
}
int pd_exit_modes(int port, uint32_t *payload)
{
struct svdm_amode_data *modep;
if (pe[port].amodes == NULL)
return 0;
modep = &pe[port].amodes[pe[port].amode_idx];
if (modep->amode == dfp_amode_none)
return 1;
modep->exit();
payload[0] = VDO(modep->svid, 1,
CMD_EXIT_MODE |
VDO_OPOS(modep->amode));
pe[port].amode_idx++;
return 1;
}
static void dump_pe(int port)
{
int i, j;
struct svdm_amode_data *modep = pe[port].amodes;
for (i = 0; i < pe[port].svid_cnt; i++) {
ccprintf("SVID[%d]: %04x", i, pe[port].svids[i].svid);
for (j = 0; j < (PDO_MAX_OBJECTS - 1); j++)
ccprintf(" [%d] %08x", j,
pe[port].svids[i].mode_vdo[j]);
ccprintf("\n");
}
for (i = 0; i < pe[port].amode_cnt; i++) {
ccprintf("MODE[%d]: svid:%04x mode:%d caps:%08x\n", i,
modep->svid, modep->amode, *modep->mode_caps);
modep++;
}
}
static int command_pe(int argc, char **argv)
{
int port;
char *e;
if (argc < 3)
return EC_ERROR_PARAM_COUNT;
/* command: pe <port> <subcmd> <args> */
port = strtoi(argv[1], &e, 10);
if (*e || port >= PD_PORT_COUNT)
return EC_ERROR_PARAM2;
if (!strncasecmp(argv[2], "dump", 4))
dump_pe(port);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(pe, command_pe,
"<port> dump",
"USB PE",
NULL);
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
int i;
int cmd = PD_VDO_CMD(payload[0]);
int cmd_type = PD_VDO_CMDT(payload[0]);
int rsize = 1; /* VDM header at a minimum */
ccprintf("%T] SVDM/%d [%d] %08x", cnt, cmd, payload[0]);
for (i = 1; i < cnt; i++)
ccprintf(" %08x", payload[i]);
ccprintf("\n");
payload[0] &= ~VDO_CMDT_MASK;
*rpayload = payload;
if (cmd_type == CMDT_INIT) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
rsize = svdm_rsp.identity(port, payload);
break;
case CMD_DISCOVER_SVID:
rsize = svdm_rsp.svids(port, payload);
break;
case CMD_DISCOVER_MODES:
rsize = svdm_rsp.modes(port, payload);
break;
case CMD_ENTER_MODE:
rsize = svdm_rsp.enter_mode(port, payload);
break;
case CMD_EXIT_MODE:
rsize = svdm_rsp.exit_mode(port, payload);
break;
}
if (rsize > 1)
payload[0] |= VDO_CMDT(CMDT_RSP_ACK);
else if (rsize == 1)
payload[0] |= VDO_CMDT(CMDT_RSP_NAK);
else {
payload[0] |= VDO_CMDT(CMDT_RSP_BUSY);
rsize = 1;
}
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
} else if (cmd_type == CMDT_RSP_ACK) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
dfp_consume_identity(port, payload);
rsize = dfp_discover_svids(port, payload);
break;
case CMD_DISCOVER_SVID:
dfp_consume_svids(port, payload);
rsize = dfp_discover_modes(port, payload);
break;
case CMD_DISCOVER_MODES:
if (dfp_consume_modes(port, payload))
rsize = dfp_discover_modes(port, payload);
else
rsize = dfp_enter_modes(port, payload);
break;
case CMD_ENTER_MODE:
rsize = dfp_enter_modes(port, payload);
case CMD_EXIT_MODE:
rsize = pd_exit_modes(port, payload);
break;
}
payload[0] &= ~VDO_CMDT(0);
payload[0] |= VDO_CMDT(CMDT_INIT);
} else if (cmd_type == CMDT_RSP_BUSY) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
case CMD_DISCOVER_SVID:
case CMD_DISCOVER_MODES:
/* resend if its discovery */
payload[0] &= ~VDO_CMDT(0);
payload[0] |= VDO_CMDT(CMDT_INIT);
rsize = 1;
break;
case CMD_ENTER_MODE:
/* Error */
ccprintf("PE ERR: received BUSY for Enter mode\n");
rsize = 0;
break;
case CMD_EXIT_MODE:
rsize = 0;
break;
}
} else if (cmd_type == CMDT_RSP_NAK) {
/* nothing to do */
rsize = 0;
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
} else {
ccprintf("PE ERR: unknown cmd type %d\n", cmd);
}
ccprintf("%T] DONE\n");
return rsize;
}
#else
int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
return 0;
}
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifndef CONFIG_USB_PD_CUSTOM_VDM
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
return 0;
}
#endif /* !CONFIG_USB_PD_CUSTOM_VDM */
#ifndef CONFIG_USB_PD_ALT_MODE_DFP
int pd_exit_modes(int port, uint32_t *payload)
{
return 0;
}
#endif /* !CONFIG_USB_PD_ALT_MODE_DFP */

View File

@@ -151,6 +151,7 @@ static const uint8_t dec4b5b[] = {
#define CC_RA(cc) (cc < PD_SRC_RD_THRESHOLD)
#define CC_RD(cc) ((cc > PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
#define GET_POLARITY(cc1, cc2) (CC_RD(cc2) || CC_RA(cc1))
#define IS_CABLE(cc1, cc2) (CC_RD(cc1) || CC_RD(cc2))
/* PD counter definitions */
#define PD_MESSAGE_ID_COUNT 7
@@ -619,29 +620,23 @@ static void bist_mode_2_rx(int port)
static void handle_vdm_request(int port, int cnt, uint32_t *payload)
{
uint16_t vid = PD_VDO_VID(payload[0]);
#ifdef CONFIG_USB_PD_CUSTOM_VDM
int rlen;
int rlen = 0;
uint32_t *rdata;
#endif
if (vid == USB_VID_GOOGLE) {
if (pd[port].vdm_state == VDM_STATE_BUSY)
pd[port].vdm_state = VDM_STATE_DONE;
#ifdef CONFIG_USB_PD_CUSTOM_VDM
rlen = pd_custom_vdm(port, cnt, payload, &rdata);
if (rlen > 0) {
uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF,
pd[port].role, pd[port].msg_id,
rlen);
send_validate_message(port, header, rlen, rdata);
}
#endif
if (pd[port].vdm_state == VDM_STATE_BUSY)
pd[port].vdm_state = VDM_STATE_DONE;
rlen = pd_vdm(port, cnt, payload, &rdata);
if (rlen > 0) {
uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF,
pd[port].role, pd[port].msg_id,
rlen);
send_validate_message(port, header, rlen, rdata);
return;
}
if (debug_level >= 1)
CPRINTF("Unhandled VDM VID %04x CMD %04x\n",
vid, payload[0] & 0xFFFF);
PD_VDO_VID(payload[0]), payload[0] & 0xFFFF);
}
static void execute_hard_reset(int port)
@@ -1027,7 +1022,7 @@ void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
return;
}
pd[port].vdo_data[0] = VDO(vid, cmd);
pd[port].vdo_data[0] = VDO(vid, (vid == USB_SID_PD) ? 1 : 0, cmd);
pd[port].vdo_count = count + 1;
for (i = 1; i < count + 1; i++)
pd[port].vdo_data[i] = data[i-1];
@@ -1319,20 +1314,23 @@ void pd_task(void)
}
break;
case PD_STATE_SRC_READY:
#ifdef CONFIG_USB_PD_SIMPLE_DFP
/*
* For simple devices that don't support alternate
* mode and are only sources (ie power adapters),
* send custom VDM with info about this device
* once power contract has been negotiated.
*/
if (pd[port].last_state != pd[port].task_state) {
/* Send google VDM to read info */
#ifdef CONFIG_USB_PD_SIMPLE_DFP
/*
* For simple devices that don't support
* alternate mode and are only sources (ie power
* adapters), send custom VDM with info about
* this device once power contract has been
* negotiated.
*/
pd_send_vdm(port, USB_VID_GOOGLE,
VDO_CMD_SEND_INFO,
pd_get_info(), 6);
}
#else
pd_send_vdm(port, USB_SID_PD,
CMD_DISCOVER_IDENT, NULL, 0);
#endif
}
if (!pd[port].ping_enabled) {
timeout = PD_T_SOURCE_ACTIVITY;
@@ -1443,6 +1441,7 @@ void pd_task(void)
PD_STATE_HARD_RESET);
break;
case PD_STATE_HARD_RESET:
pd_exit_modes(port, payload);
send_hard_reset(port);
/* reset our own state machine */
execute_hard_reset(port);
@@ -1797,8 +1796,10 @@ static int command_pd(int argc, char **argv)
} else if (!strncasecmp(argv[2], "state", 5)) {
const char * const state_names[] = {
"DISABLED", "SUSPENDED",
#ifdef CONFIG_USB_PD_DUAL_ROLE
"SNK_DISCONNECTED", "SNK_DISCOVERY", "SNK_REQUESTED",
"SNK_TRANSITION", "SNK_READY",
#endif /* CONFIG_USB_PD_DUAL_ROLE */
"SRC_DISCONNECTED", "SRC_DISCOVERY", "SRC_NEGOCIATE",
"SRC_ACCEPTED", "SRC_TRANSITION", "SRC_READY",
"SOFT_RESET", "HARD_RESET", "BIST",

View File

@@ -1001,6 +1001,12 @@
/* Default state of PD communication enabled flag */
#define CONFIG_USB_PD_COMM_ENABLED 1
/* Support for USB PD alternate mode */
#undef CONFIG_USB_PD_ALT_MODE
/* Support for USB PD alternate mode of Downward Facing Port */
#undef CONFIG_USB_PD_ALT_MODE_DFP
/* Respond to custom vendor-defined messages over PD */
#undef CONFIG_USB_PD_CUSTOM_VDM
@@ -1013,6 +1019,10 @@
/* Check whether PD is the sole power source before flash erase operation */
#undef CONFIG_USB_PD_FLASH_ERASE_CHECK
/* HW & SW version for alternate mode discover identity response (4bits each) */
#undef CONFIG_USB_PD_IDENTITY_HW_ID
#undef CONFIG_USB_PD_IDENTITY_SW_ID
/* USB PD MCU slave address for host commands */
#define CONFIG_USB_PD_I2C_SLAVE_ADDR 0x3c

View File

@@ -22,6 +22,7 @@ enum pd_errors {
/* --- PD data message helpers --- */
#define PDO_MAX_OBJECTS 7
#define PDO_MODES (PDO_MAX_OBJECTS - 1)
/* PDO : Power Data Object */
/*
@@ -102,22 +103,106 @@ enum pd_errors {
#define BDO(mode, cnt) ((mode) | ((cnt) & 0xFFFF))
/* VDO : Vendor Defined Message Object */
#define VDO(vid, custom) (((vid) << 16) | ((custom) & 0xFFFF))
/* TODO(tbroch) is there a finite number for these in the spec */
#define SVID_DISCOVERY_MAX 16
/* function table for alternate mode capable responders */
struct svdm_response {
int (*identity)(int port, uint32_t *payload);
int (*svids)(int port, uint32_t *payload);
int (*modes)(int port, uint32_t *payload);
int (*enter_mode)(int port, uint32_t *payload);
int (*exit_mode)(int port, uint32_t *payload);
};
/* defined in <board>/usb_pd_policy.c */
extern const struct svdm_response svdm_rsp;
/* Each SVID is allowed to have 6 modes */
enum dfp_amode {
dfp_amode_none = 0,
dfp_amode1,
dfp_amode2,
dfp_amode3,
dfp_amode4,
dfp_amode5,
dfp_amode6,
};
struct svdm_svid_data {
uint16_t svid;
uint32_t mode_vdo[PDO_MODES];
};
struct svdm_amode_data {
uint16_t svid;
enum dfp_amode amode;
uint32_t *mode_caps;
void (*enter)(uint32_t mode_caps);
void (*exit)(void);
};
/* Policy structure for driving alternate mode */
struct pd_policy {
/* index of svid currently being operated on */
int svid_idx;
/* count of svids discovered */
int svid_cnt;
/* supported svids & corresponding vdo mode data */
struct svdm_svid_data svids[SVID_DISCOVERY_MAX];
/* index of amode currently being operated on */
int amode_idx;
/* count of amodes discovered */
int amode_cnt;
/* supported amodes */
struct svdm_amode_data *amodes;
};
/*
* VDO : Vendor Defined Message Object
* VDM object is minimum of VDM header + 6 additional data objects.
*/
/*
* VDM header
* ----------
* <31:16> :: SVID
* <15> :: VDM type ( 1b == structured, 0b == unstructured )
* <14:13> :: Structured VDM version
* <12:11> :: reserved
* <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
* <7:6> :: command type (SVDM only?)
* <5> :: reserved (SVDM), command type (UVDM)
* <4:0> :: command
*/
#define VDO_MAX_SIZE 7
#define VDO(vid, type, custom) \
(((vid) << 16) | \
((type) << 15) | \
((custom) & 0x7FFF))
#define VDO_ACK (0 << 6)
#define VDO_NAK (1 << 6)
#define VDO_PENDING (2 << 6)
#define VDO_SVDM_TYPE (1 << 15)
#define VDO_SVDM_VERS(x) (x << 13)
#define VDO_OPOS(x) (x << 8)
#define VDO_CMDT(x) (x << 6)
#define VDO_CMDT_MASK VDO_CMDT(0x3)
#define CMDT_INIT 0
#define CMDT_RSP_NAK 1
#define CMDT_RSP_ACK 2
#define CMDT_RSP_BUSY 3
/* reserved for SVDM ... for Google UVDM */
#define VDO_SRC_INITIATOR (0 << 5)
#define VDO_SRC_RESPONDER (1 << 5)
#define VDO_CMD_DISCOVER_VID (1 << 0)
#define VDO_CMD_DISCOVER_ALT (2 << 0)
#define VDO_CMD_AUTHENTICATE (3 << 0)
#define VDO_CMD_ENTER_ALT (4 << 0)
#define VDO_CMD_EXIT_ALT (5 << 0)
#define CMD_DISCOVER_IDENT 1
#define CMD_DISCOVER_SVID 2
#define CMD_DISCOVER_MODES 3
#define CMD_ENTER_MODE 4
#define CMD_EXIT_MODE 5
#define CMD_ATTENTION 6
#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f))
/* ChromeOS specific commands */
@@ -131,8 +216,164 @@ enum pd_errors {
#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10)
#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11)
#define PD_VDO_VID(vdo) ((vdo) >> 16)
#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
#define PD_VDO_VID(vdo) ((vdo) >> 16)
#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1)
#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7)
#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3)
/*
* SVDM Identity request -> response
*
* Request is simply properly formatted SVDM header
*
* Response is 4 data objects:
* [0] :: SVDM header
* [1] :: Identitiy header
* [2] :: Cert Stat VDO
* [3] :: (Product | Cable | AMA) VDO
*
*/
#define VDO_INDEX_HDR 0
#define VDO_INDEX_IDH 1
#define VDO_INDEX_CSTAT 2
#define VDO_INDEX_CABLE 3
#define VDO_INDEX_AMA 3
#define VDO_I(name) VDO_INDEX_##name
/*
* SVDM Identity Header
* --------------------
* <31> :: data capable as a USB host
* <30> :: data capable as a USB device
* <29:27> :: product type
* <26> :: modal operation supported (1b == yes)
* <25:16> :: SBZ
* <15:0> :: USB-IF assigned VID for this cable vendor
*/
#define IDH_PTYPE_UNDEF 0
#define IDH_PTYPE_HUB 1
#define IDH_PTYPE_PERIPH 2
#define IDH_PTYPE_ACABLE 4
#define IDH_PTYPE_PCABLE 5
#define IDH_PTYPE_AMA 6
#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
| (is_modal) << 26 | ((vid) & 0xffff))
#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7)
#define PD_IDH_VID(vdo) ((vdo) & 0xffff)
/*
* Cert Stat VDO
* -------------
* <31:20> : SBZ
* <19:0> : USB-IF assigned TID for this cable
*/
#define VDO_CSTAT(tid) ((tid) & 0xfffff)
#define PD_CSTAT_TID(vdo) ((vdo) & 0xfffff)
/*
* Cable VDO
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
* <23:20> :: SBZ
* <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
* <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
* <16:13> :: cable latency (0001 == <10ns(~1m length))
* <12:11> :: cable termination type (11b == both ends active VCONN req)
* <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
* <9> :: SSTX2 Directionality support
* <8> :: SSRX1 Directionality support
* <7> :: SSRX2 Directionality support
* <6:5> :: Vbus current handling capability
* <4> :: Vbus through cable (0b == no, 1b == yes)
* <3> :: SOP" controller present? (0b == no, 1b == yes)
* <2:0> :: USB SS Signaling support
*/
#define CABLE_ATYPE 0
#define CABLE_BTYPE 1
#define CABLE_CTYPE 2
#define CABLE_PLUG 0
#define CABLE_RECEPTACLE 1
#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur, vps, sopp, usbss) \
(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \
| (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \
| (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \
| ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \
| ((usbss) & 0x7))
/*
* AMA VDO
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
* <23:12> :: SBZ
* <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
* <10> :: SSTX2 Directionality support
* <9> :: SSRX1 Directionality support
* <8> :: SSRX2 Directionality support
* <7:5> :: Vconn power
* <4> :: Vconn power required
* <3> :: Vbus power required
* <2:0> :: USB SS Signaling support
*/
#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \
| (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \
| ((vcpwr) & 0x3) << 5 | (vcr) << 4 | (vbr) << 3 \
| ((usbss) & 0x7))
#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1)
/*
* SVDM Discover SVIDs request -> response
*
* Request is properly formatted VDM Header with discover SVIDs command.
* Response is a set of SVIDs of all all supported SVIDs with all zero's to
* mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be
* repeated.
*/
#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff))
#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16)
#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff)
/*
* Mode Capabilities
*
* Number of VDOs supplied is SID dependent (but <= 6 VDOS?)
*/
#define VDO_MODE_CNT_DISPLAYPORT 1
/*
* DisplayPort modes capabilities
* -------------------------------
* <31:24> : SBZ
* <23:16> : sink pin assignment supported
* <15:8> : source pin assignment supported
* <7> : USB 2.0 signaling (0b=yes, 1b=no)
* <6> : Plug | Receptacle (0b == plug, 1b == receptacle)
* <5:3> : USB Gen 2 signaling for DP (000b=no, 001b=yes, rest=rsv)
* <2> : supports dp1.3
* <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both )
*/
#define VDO_MODE_DP(snkp, srcp, usb, gdr, usbdp, dp3, sdir) \
(((snkp) & 0xff) << 16 | ((srcp) & 0xff) << 8 \
| ((usb) & 1) << 7 | ((gdr) & 1) << 6 | ((usbdp) & 0x7) << 3 \
| ((dp3) & 1) << 2 | ((sdir) & 0x3))
#define MODE_DP_PIN_A 0x01
#define MODE_DP_PIN_B 0x02
#define MODE_DP_PIN_C 0x04
#define MODE_DP_PIN_D 0x08
#define MODE_DP_PIN_E 0x10
#define MODE_DP_SNK 0x1
#define MODE_DP_SRC 0x2
#define MODE_DP_BOTH 0x3
/*
* ChromeOS specific PD device Hardware IDs. Used to identify unique
@@ -158,6 +399,9 @@ enum pd_errors {
#define VDO_INFO_SW_DBG_VER(x) (((x) >> 1) & 0x7fff)
#define VDO_INFO_IS_RW(x) ((x) & 1)
/* USB-IF SIDs */
#define USB_SID_PD 0xff00 /* power delivery */
#define USB_SID_DISPLAYPORT 0xff01
/* USB Vendor ID assigned to Google Inc. */
#define USB_VID_GOOGLE 0x18d1
@@ -337,7 +581,7 @@ int pd_board_checks(void);
uint32_t *pd_get_info(void);
/**
* Handle Vendor Defined Message with our vendor ID.
* Handle Vendor Defined Messages
*
* @param port USB-C port number
* @param cnt number of data objects in the payload.
@@ -345,7 +589,34 @@ uint32_t *pd_get_info(void);
* @param rpayload pointer to the data to send back.
* @return if >0, number of VDOs to send back.
*/
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload);
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload);
/**
* Handle Structured Vendor Defined Messages
*
* @param port USB-C port number
* @param cnt number of data objects in the payload.
* @param payload payload data.
* @param rpayload pointer to the data to send back.
* @return if >0, number of VDOs to send back.
*/
int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload);
/**
* Choose appropriate alternate modes.
*
* @param pe pd_policy data structure
*/
void pd_dfp_choose_modes(struct pd_policy *pe);
/**
* Exit alternate mode
*
* @param port USB-C port number
* @param payload payload data.
* @return if >0, number of VDOs to send back.
*/
int pd_exit_modes(int port, uint32_t *payload);
/**
* Store Device ID & RW hash of device

View File

@@ -49,6 +49,11 @@ void pd_select_polarity(int port, int polarity)
pd_port[port].polarity = polarity;
}
int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
{
return 0;
}
/* Tests */
void inc_tx_id(int port)