diff --git a/board/fruitpie/board.h b/board/fruitpie/board.h index 0129872ae8..d20606ebbb 100644 --- a/board/fruitpie/board.h +++ b/board/fruitpie/board.h @@ -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 diff --git a/board/fruitpie/usb_pd_policy.c b/board/fruitpie/usb_pd_policy.c index b5d51540a7..1e1de216a0 100644 --- a/board/fruitpie/usb_pd_policy.c +++ b/board/fruitpie/usb_pd_policy.c @@ -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++; + } +} diff --git a/board/hoho/board.h b/board/hoho/board.h index 1d330fa4dd..aa53c70849 100644 --- a/board/hoho/board.h +++ b/board/hoho/board.h @@ -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 diff --git a/board/hoho/usb_pd_policy.c b/board/hoho/usb_pd_policy.c index 57099a960d..5523b2a95c 100644 --- a/board/hoho/usb_pd_policy.c +++ b/board/hoho/usb_pd_policy.c @@ -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); +} diff --git a/board/samus_pd/usb_pd_policy.c b/board/samus_pd/usb_pd_policy.c index 6689866463..2aff0210a9 100644 --- a/board/samus_pd/usb_pd_policy.c +++ b/board/samus_pd/usb_pd_policy.c @@ -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) diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c index 017cd95268..78b5430194 100644 --- a/board/zinger/usb_pd_policy.c +++ b/board/zinger/usb_pd_policy.c @@ -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); +} diff --git a/common/build.mk b/common/build.mk index 189a44a5c1..7627310345 100644 --- a/common/build.mk +++ b/common/build.mk @@ -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 diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c new file mode 100644 index 0000000000..2042b474a7 --- /dev/null +++ b/common/usb_pd_policy.c @@ -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 = 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, + " 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 */ diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index a5a272ab2d..1e7641ed56 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -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", diff --git a/include/config.h b/include/config.h index e6cb68c132..3183e3b267 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/usb_pd.h b/include/usb_pd.h index d2d6aae8c0..8824eb3b33 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -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 /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 diff --git a/test/usb_pd.c b/test/usb_pd.c index 208836fe5b..cc36b968cf 100644 --- a/test/usb_pd.c +++ b/test/usb_pd.c @@ -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)