/* Copyright 2015 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 "charger.h" #include "common.h" #include "console.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "system.h" #include "task.h" #include "timer.h" #include "util.h" #include "usb_pd.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_EXTERNAL | PDO_FIXED_DATA_SWAP) /* Voltage indexes for the PDOs */ enum volt_idx { PDO_IDX_5V = 0, PDO_IDX_12V = 1, PDO_IDX_20V = 2, PDO_IDX_COUNT }; const uint32_t pd_src_pdo[] = { [PDO_IDX_5V] = PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS), [PDO_IDX_12V] = PDO_FIXED(12000, 3000, PDO_FIXED_FLAGS), [PDO_IDX_20V] = PDO_FIXED(20000, 3000, PDO_FIXED_FLAGS), }; const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); BUILD_ASSERT(ARRAY_SIZE(pd_src_pdo) == PDO_IDX_COUNT); /* 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) { } int pd_is_valid_input_voltage(int mv) { /* Any voltage less than the max is allowed */ return 1; } int pd_check_requested_voltage(uint32_t rdo) { int max_ma = rdo & 0x3FF; int op_ma = (rdo >> 10) & 0x3FF; int idx = rdo >> 28; uint32_t pdo; uint32_t pdo_ma; if (!idx || idx > pd_src_pdo_cnt) return EC_ERROR_INVAL; /* Invalid index */ /* check current ... */ pdo = pd_src_pdo[idx - 1]; pdo_ma = (pdo & 0x3ff); if (op_ma > pdo_ma) return EC_ERROR_INVAL; /* too much op current */ if (max_ma > pdo_ma) return EC_ERROR_INVAL; /* too much max current */ CPRINTF("Requested %d V %d mA (for %d/%d mA)\n", ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, ((rdo >> 10) & 0x3ff) * 10, (rdo & 0x3ff) * 10); return EC_SUCCESS; } void pd_transition_voltage(int idx) { /* PDO index are starting from 1 */ switch (idx - 1) { case PDO_IDX_20V: #if 0 gpio_set_level(GPIO_PPVAR_VBUS_EN, 0); gpio_set_level(GPIO_PP20000_EN, 1); break; #endif /* 20V transition is broken, putting 12V instead: fall-through */ case PDO_IDX_12V: gpio_set_level(GPIO_PP12000_EN, 1); gpio_set_level(GPIO_PP20000_EN, 0); gpio_set_level(GPIO_PPVAR_VBUS_EN, 1); break; case PDO_IDX_5V: default: gpio_set_level(GPIO_PP12000_EN, 0); gpio_set_level(GPIO_PP20000_EN, 0); gpio_set_level(GPIO_PPVAR_VBUS_EN, 1); } } int pd_set_power_supply_ready(int port) { /* provide VBUS */ gpio_set_level(GPIO_PPVAR_VBUS_EN, 1); return EC_SUCCESS; /* we are ready */ } void pd_power_supply_reset(int port) { /* Kill VBUS */ gpio_set_level(GPIO_PPVAR_VBUS_EN, 0); gpio_set_level(GPIO_PP12000_EN, 0); gpio_set_level(GPIO_PP20000_EN, 0); } int pd_snk_is_vbus_provided(int port) { return 0; } int pd_board_checks(void) { return EC_SUCCESS; } int pd_check_power_swap(int port) { /* We are source-only */ return 0; } int pd_check_data_swap(int port, int data_role) { /* Always allow data swap */ return 1; } void pd_execute_data_swap(int port, int data_role) { /* Do nothing */ } void pd_check_pr_role(int port, int pr_role, int flags) { } void pd_check_dr_role(int port, int dr_role, int flags) { } int pd_alt_mode(int port, uint16_t svid) { 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; } /* ----------------- Vendor Defined Messages ------------------ */ /* TODO () The VDM section needs to be updated for honeybuns */ const struct svdm_response svdm_rsp = { .identity = NULL, .svids = NULL, .modes = NULL, }; 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; /* make sure we have some payload */ if (cnt == 0) return 0; switch (cmd) { case VDO_CMD_VERSION: /* guarantee last byte of payload is null character */ *(payload + cnt - 1) = 0; CPRINTF("version: %s\n", (char *)(payload+1)); break; case VDO_CMD_READ_INFO: case VDO_CMD_SEND_INFO: /* if last word is present, it contains lots of info */ if (cnt == 7) { dev_id = VDO_INFO_HW_DEV_ID(payload[6]); CPRINTF("DevId:%d.%d SW:%d RW:%d\n", HW_DEV_ID_MAJ(dev_id), HW_DEV_ID_MIN(dev_id), VDO_INFO_SW_DBG_VER(payload[6]), VDO_INFO_IS_RW(payload[6])); } /* copy hash */ if (cnt >= 6) pd_dev_store_rw_hash(port, dev_id, payload + 1, SYSTEM_IMAGE_UNKNOWN); break; } return 0; } static int svdm_enter_dp_mode(int port, uint32_t mode_caps) { /* Only enter mode if device is DFP_D capable */ if (mode_caps & MODE_DP_SNK) { CPRINTF("Entering mode w/ vdo = %08x\n", mode_caps); return 0; } return -1; } static int dp_on; static int svdm_dp_status(int port, uint32_t *payload) { payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_STATUS); payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */ 0, /* HPD level ... not applicable */ 0, /* exit DP? ... no */ 0, /* usb mode? ... no */ 0, /* multi-function ... no */ dp_on, 0, /* power low? ... no */ dp_on); return 2; }; static int svdm_dp_config(int port, uint32_t *payload) { board_set_usb_mux(port, TYPEC_MUX_DP, USB_SWITCH_CONNECT, pd_get_polarity(port)); dp_on = 1; payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_CONFIG); payload[1] = VDO_DP_CFG(MODE_DP_PIN_E, /* pin mode */ 1, /* DPv1.3 signaling */ 2); /* UFP connected */ return 2; }; static int svdm_dp_attention(int port, uint32_t *payload) { return 1; /* ack */ } static void svdm_exit_dp_mode(int port) { CPRINTF("Exiting mode\n"); /* return to safe config */ } const struct svdm_amode_fx supported_modes[] = { { .svid = USB_SID_DISPLAYPORT, .enter = &svdm_enter_dp_mode, .status = &svdm_dp_status, .config = &svdm_dp_config, .attention = &svdm_dp_attention, .exit = &svdm_exit_dp_mode, }, }; const int supported_modes_cnt = ARRAY_SIZE(supported_modes);