| /* 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; |
| } |