Files
OpenCellular/common/usb_pd_policy.c
Todd Broch cefb58066d pd: Add DisplayPort status and configure SVDMs.
Per revisements to the DisplayPort Alternate mode specification there
are two additional SVDMs for DPout support: status & configure.

This CL adds those SVDMs and calls them (status then config) after
finding a device that supports DP Alternate mode.

Future CLs will use these SVDMs to complete providing HPD over CC
support.

BRANCH=none
BUG=chrome-os-partner:31192,chrome-os-partner:31193
TEST=manual, plug hoho/dingdong into samus and see:

1. Additional DP status [16] & DP configure [17]
2. Drives DPout properly

Change-Id: I52b373085ddc330e4afb1d1883d2621bc2e4ee95
Signed-off-by: Todd Broch <tbroch@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/223260
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
2014-10-21 22:44:45 +00:00

341 lines
7.9 KiB
C

/* 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"
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#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) {
CPRINTF("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))
CPRINTF("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;
if (!pe[port].svid_cnt)
return 0;
payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
return 1;
}
static int dfp_consume_modes(int port, int cnt, uint32_t *payload)
{
int idx = pe[port].svid_idx;
pe[port].svids[idx].mode_cnt = cnt - 1;
if (pe[port].svids[idx].mode_cnt < 0) {
CPRINTF("PE ERR: no modes provided for SVID\n");
} else {
memcpy(pe[port].svids[pe[port].svid_idx].mode_vdo, &payload[1],
sizeof(uint32_t) * pe[port].svids[idx].mode_cnt);
}
pe[port].svid_idx++;
return (pe[port].svid_idx < pe[port].svid_cnt);
}
/* TODO(tbroch) this function likely needs to move up the stack to where system
* policy decisions are made. */
static int dfp_enter_mode(int port, uint32_t *payload)
{
int i, j, done;
struct svdm_amode_data *modep = &pe[port].amode;
pe[port].amode.index = -1; /* Error condition */
for (i = 0, done = 0; !done && (i < supported_modes_cnt); i++) {
for (j = 0; j < pe[port].svid_cnt; j++) {
if (pe[port].svids[j].svid != supported_modes[i].svid)
continue;
pe[port].amode.fx = &supported_modes[i];
pe[port].amode.mode_caps =
pe[port].svids[j].mode_vdo[0];
pe[port].amode.index = 0;
done = 1;
break;
}
}
if (modep->index == -1)
return 0;
modep->fx->enter(port, modep->mode_caps);
payload[0] = VDO(modep->fx->svid, 1,
CMD_ENTER_MODE |
VDO_OPOS((modep->index + 1)));
return 1;
}
int pd_exit_mode(int port, uint32_t *payload)
{
struct svdm_amode_data *modep = &pe[port].amode;
modep->fx->exit(port);
payload[0] = VDO(modep->fx->svid, 1,
CMD_EXIT_MODE |
VDO_OPOS((modep->index + 1)));
return 1;
}
static void dump_pe(int port)
{
int i, j;
if (pe[port].svid_cnt < 1) {
ccprintf("No SVIDS discovered yet.\n");
return;
}
for (i = 0; i < pe[port].svid_cnt; i++) {
ccprintf("SVID[%d]: %04x MODES:", i, pe[port].svids[i].svid);
for (j = 0; j < pe[port].svids[j].mode_cnt; j++)
ccprintf(" [%d] %08x", j + 1,
pe[port].svids[i].mode_vdo[j]);
ccprintf("\n");
}
if (pe[port].amode.index == -1) {
ccprintf("No mode chosen yet.\n");
return;
}
ccprintf("MODE[%d]: svid:%04x caps:%08x\n", pe[port].amode.index + 1,
pe[port].amode.fx->svid, pe[port].amode.mode_caps);
}
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 (*func)(int port, uint32_t *payload) = NULL;
int rsize = 1; /* VDM header at a minimum */
CPRINTF("SVDM/%d [%d] %08x", cnt, cmd, payload[0]);
for (i = 1; i < cnt; i++)
CPRINTF(" %08x", payload[i]);
CPRINTF("\n");
payload[0] &= ~VDO_CMDT_MASK;
*rpayload = payload;
if (cmd_type == CMDT_INIT) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
func = svdm_rsp.identity;
break;
case CMD_DISCOVER_SVID:
func = svdm_rsp.svids;
break;
case CMD_DISCOVER_MODES:
func = svdm_rsp.modes;
break;
case CMD_ENTER_MODE:
func = svdm_rsp.enter_mode;
break;
case CMD_DP_STATUS:
func = svdm_rsp.amode->status;
break;
case CMD_DP_CONFIG:
func = svdm_rsp.amode->config;
break;
case CMD_EXIT_MODE:
func = svdm_rsp.exit_mode;
break;
}
if (func)
rsize = func(port, payload);
else /* not supported : NACK it */
rsize = 0;
if (rsize >= 1)
payload[0] |= VDO_CMDT(CMDT_RSP_ACK);
else if (!rsize) {
payload[0] |= VDO_CMDT(CMDT_RSP_NAK);
rsize = 1;
} 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, cnt, payload))
rsize = dfp_discover_modes(port, payload);
else
rsize = dfp_enter_mode(port, payload);
break;
case CMD_ENTER_MODE:
if (pe[port].amode.index != -1)
rsize = pe[port].amode.fx->status(port,
payload);
break;
case CMD_DP_STATUS:
rsize = pe[port].amode.fx->config(port, payload);
break;
case CMD_DP_CONFIG:
rsize = 0;
break;
case CMD_EXIT_MODE:
rsize = pd_exit_mode(port, payload);
break;
default:
rsize = 0;
}
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 */
CPRINTF("PE ERR: received BUSY for Enter mode\n");
rsize = 0;
break;
case CMD_EXIT_MODE:
rsize = 0;
break;
default:
rsize = 0;
}
} else if (cmd_type == CMDT_RSP_NAK) {
/* nothing to do */
rsize = 0;
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
} else {
CPRINTF("PE ERR: unknown cmd type %d\n", cmd);
}
CPRINTS("DONE");
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_mode(int port, uint32_t *payload)
{
return 0;
}
#endif /* !CONFIG_USB_PD_ALT_MODE_DFP */